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;
}