diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js index 907066d79a..497e651b73 100644 --- a/frappe/public/js/frappe/list/list_view.js +++ b/frappe/public/js/frappe/list/list_view.js @@ -1092,7 +1092,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { } } - get_tags_html(user_tags, limit, colored = false) { + get_tags_html(user_tags, limit = null, colored = false) { let get_tag_html = (tag) => { let color = "", style = ""; @@ -1105,11 +1105,12 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList { return `
${tag}
`; } }; - return user_tags - .split(",") - .slice(1, limit + 1) - .map(get_tag_html) - .join(""); + user_tags = (user_tags || "").split(","); + if (limit !== null) { + // if there is a limit apply it + user_tags = user_tags.slice(0, limit); + } + return user_tags.map(get_tag_html).join(""); } get_meta_html(doc) { diff --git a/frappe/public/js/frappe/ui/tag_editor.js b/frappe/public/js/frappe/ui/tag_editor.js index 391a336820..860eab9a87 100644 --- a/frappe/public/js/frappe/ui/tag_editor.js +++ b/frappe/public/js/frappe/ui/tag_editor.js @@ -18,6 +18,12 @@ frappe.ui.TagEditor = class TagEditor { this.initialized = true; this.refresh(this.user_tags); } + update_user_tags(tags_string) { + this.user_tags = tags_string; + frappe.model.set_value(this.frm.doctype, this.frm.docname, "_user_tags", this.user_tags); + this.on_change && this.on_change(this.user_tags); + frappe.tags.utils.fetch_tags(); + } setup_tags() { var me = this; @@ -40,9 +46,7 @@ frappe.ui.TagEditor = class TagEditor { callback: function (r) { var user_tags = me.user_tags ? me.user_tags.split(",") : []; user_tags.push(tag); - me.user_tags = user_tags.join(","); - me.on_change && me.on_change(me.user_tags); - frappe.tags.utils.fetch_tags(); + me.update_user_tags(user_tags.join(",")); }, }); } @@ -55,9 +59,7 @@ frappe.ui.TagEditor = class TagEditor { callback: function (r) { var user_tags = me.user_tags.split(","); user_tags.splice(user_tags.indexOf(tag), 1); - me.user_tags = user_tags.join(","); - me.on_change && me.on_change(me.user_tags); - frappe.tags.utils.fetch_tags(); + me.update_user_tags(user_tags.join(",")); }, }); } diff --git a/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js b/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js index 8c661290f6..b8a7660a76 100644 --- a/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js +++ b/frappe/public/js/frappe/views/kanban/kanban_board.bundle.js @@ -589,8 +589,6 @@ frappe.provide("frappe.views"); group: "cards", animation: 150, dataIdAttr: "data-name", - forceFallback: true, - fallbackTolerance: 20, onStart: function () { wrapper.find(".kanban-card.add-card").fadeOut(200, function () { wrapper.find(".kanban-cards").height("100vh"); @@ -599,7 +597,6 @@ frappe.provide("frappe.views"); onEnd: function (e) { wrapper.find(".kanban-card.add-card").fadeIn(100); wrapper.find(".kanban-cards").height("auto"); - // update order const args = { name: decodeURIComponent($(e.item).attr("data-name")), from_colname: $(e.from) @@ -611,7 +608,6 @@ frappe.provide("frappe.views"); }; store.dispatch("update_order_for_single_card", args); }, - onAdd: function () {}, }); } @@ -756,11 +752,26 @@ frappe.provide("frappe.views"); } function get_tags_html(card) { - return card.tags - ? `
- ${cur_list.get_tags_html(card.tags, 3, true)} -
` - : ""; + if (!card.tags) return ""; + const tags_array = card.tags.split(","); + const limit = 3; // cap. at 3 tags + const visible_tags = tags_array.slice(0, limit).join(","); + const hidden_tags = tags_array.slice(limit).join(","); + const hidden_tags_html = cur_list.get_tags_html(hidden_tags, null, true); + const hidden_count = tags_array.length - limit; + let html = `
+ ${cur_list.get_tags_html(visible_tags, null, true)}`; + + if (hidden_count > 0) { + html += ` + + +${hidden_count} + ${hidden_tags_html} + `; + } + + html += `
`; + return html; } function render_card_meta() { diff --git a/frappe/public/scss/desk/kanban.scss b/frappe/public/scss/desk/kanban.scss index 2e4678a089..251e824c8d 100644 --- a/frappe/public/scss/desk/kanban.scss +++ b/frappe/public/scss/desk/kanban.scss @@ -186,10 +186,23 @@ .kanban-title-area { margin-bottom: 12px; - max-width: 90%; + width: 100%; + max-width: 100%; font-size: var(--text-md); font-weight: 500; + .kanban-card-title, + .kanban-card-doc { + white-space: normal !important; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + line-clamp: 2; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-word; + } + .kanban-card-doc { .text-muted div { display: inline; @@ -330,6 +343,62 @@ width: 100%; line-height: 400px; } + + .kanban-tags .more-tags { + cursor: pointer; + + .hidden-tags { + position: absolute; + + bottom: 70px; + + left: 50%; + transform: translateX(-50%); + + background: var(--card-bg); + border: 1px solid var(--border-color); + padding: 10px; + border-radius: var(--border-radius-md); + box-shadow: var(--shadow-lg); + + display: flex; + flex-wrap: wrap; + justify-content: center; + + width: calc(100% - 24px); + max-width: 220px; + + gap: 6px; + opacity: 0; + pointer-events: none; + z-index: 1000; + + .tag-pill { + margin: 0 !important; + white-space: nowrap; + } + } + + &:hover .hidden-tags { + opacity: 1; + transform: translateX(-50%) translateY(-5px); + pointer-events: auto; + } + } + + .kanban-card-wrapper:hover { + z-index: 1001; + } + + .kanban-card-wrapper:first-child { + .hidden-tags { + bottom: auto; + } + + &:hover .hidden-tags { + transform: translateX(-50%) translateY(5px); + } + } } body[data-route*="Kanban"] { diff --git a/frappe/public/scss/desk/tags.scss b/frappe/public/scss/desk/tags.scss index 895206f140..a14da7c3bb 100644 --- a/frappe/public/scss/desk/tags.scss +++ b/frappe/public/scss/desk/tags.scss @@ -1,10 +1,10 @@ .tag-pill { background-color: var(--gray-200); border-radius: var(--border-radius); - display: inline-block; - padding: 3px 10px; + display: inline-flex; + padding: 4px 10px; + align-items: center; color: var(--text-muted); - height: 24px; width: 60px; vertical-align: middle; }