feat: move tags from sidebar to nav
This commit is contained in:
parent
5974be9534
commit
bfff0178bd
8 changed files with 155 additions and 145 deletions
|
|
@ -10,6 +10,7 @@
|
|||
"disable_auto_refresh",
|
||||
"disable_sidebar_stats",
|
||||
"disable_automatic_recency_filters",
|
||||
"show_tags",
|
||||
"column_break_oany",
|
||||
"disable_comment_count",
|
||||
"disable_scrolling",
|
||||
|
|
@ -81,11 +82,17 @@
|
|||
{
|
||||
"fieldname": "section_break_evqq",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "show_tags",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show Tags"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"links": [],
|
||||
"modified": "2025-08-25 15:54:18.886680",
|
||||
"modified": "2025-12-12 14:26:20.920434",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "List View Settings",
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class ListViewSettings(Document):
|
|||
disable_scrolling: DF.Check
|
||||
disable_sidebar_stats: DF.Check
|
||||
fields: DF.Code | None
|
||||
show_tags: DF.Check
|
||||
# end: auto-generated types
|
||||
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -289,10 +289,6 @@ frappe.views.BaseList = class BaseList {
|
|||
});
|
||||
}
|
||||
|
||||
toggle_side_bar(show) {
|
||||
frappe.app.sidebar.toggle_sidebar();
|
||||
}
|
||||
|
||||
show_or_hide_sidebar() {
|
||||
let show_sidebar = JSON.parse(localStorage.show_sidebar || "true");
|
||||
$(document.body).toggleClass("no-list-sidebar", !show_sidebar);
|
||||
|
|
@ -701,7 +697,7 @@ class FilterArea {
|
|||
this.user_setting_fields =
|
||||
frappe.get_user_settings(this.list_view.doctype)?.group_by_fields || [];
|
||||
|
||||
if (["assigned_to", "owner"].some((v) => this.user_setting_fields.includes(v))) {
|
||||
if (["assigned_to", "owner", "tags"].some((v) => this.user_setting_fields.includes(v))) {
|
||||
this.render_non_standard_fields_filter();
|
||||
}
|
||||
}
|
||||
|
|
@ -823,6 +819,8 @@ class FilterArea {
|
|||
label = __("Assigned To");
|
||||
} else if (fieldname === "owner") {
|
||||
label = __("Created By");
|
||||
} else if (fieldname === "tags") {
|
||||
label = __("Tags");
|
||||
}
|
||||
|
||||
return `<div class="group-by-field list-link form-group frappe-control input-max-width">
|
||||
|
|
@ -848,6 +846,10 @@ class FilterArea {
|
|||
filtes_to_add.push("assigned_to");
|
||||
}
|
||||
|
||||
if (this.user_setting_fields.includes("tags")) {
|
||||
filtes_to_add.push("tags");
|
||||
}
|
||||
|
||||
let html = filtes_to_add.map(get_item_html).join("");
|
||||
this.list_view.page.page_form.find(".standard-filter-section").append(html);
|
||||
this.setup_non_standard_items_dropdown();
|
||||
|
|
@ -863,11 +865,21 @@ class FilterArea {
|
|||
this.set_dropdown_loading_state($dropdown);
|
||||
let fieldname = $(e.currentTarget).find("a").attr("data-fieldname");
|
||||
let fieldtype = $(e.currentTarget).find("a").attr("data-fieldtype");
|
||||
|
||||
if (fieldname == "tags") {
|
||||
$dropdown.addClass("list-stats-dropdown");
|
||||
this.get_stats($dropdown);
|
||||
return;
|
||||
}
|
||||
this.get_group_by_count(fieldname).then((field_count_list) => {
|
||||
if (field_count_list.length) {
|
||||
let applied_filter = this.list_view.get_filter_value(
|
||||
fieldname == "assigned_to" ? "_assign" : fieldname
|
||||
);
|
||||
if (fieldname == "assigned_to") {
|
||||
fieldname = "_assign";
|
||||
}
|
||||
if (fieldname == "tags") {
|
||||
fieldname = "_user_tags";
|
||||
}
|
||||
let applied_filter = this.list_view.get_filter_value(fieldname);
|
||||
this.render_dropdown_items(
|
||||
field_count_list,
|
||||
fieldtype,
|
||||
|
|
@ -896,7 +908,13 @@ class FilterArea {
|
|||
typeof $target.data("value") === "string"
|
||||
? decodeURIComponent($target.data("value").trim())
|
||||
: $target.data("value");
|
||||
fieldname = fieldname === "assigned_to" ? "_assign" : fieldname;
|
||||
|
||||
if (fieldname == "assigned_to") {
|
||||
fieldname = "_assign";
|
||||
}
|
||||
if (fieldname == "tags") {
|
||||
fieldname = "_user_tags";
|
||||
}
|
||||
|
||||
return this.list_view.filter_area.remove(fieldname).then(() => {
|
||||
if (is_selected) return;
|
||||
|
|
@ -905,20 +923,6 @@ class FilterArea {
|
|||
});
|
||||
}
|
||||
|
||||
apply_filter(fieldname, value) {
|
||||
let operator = "=";
|
||||
if (value === "") {
|
||||
operator = "is";
|
||||
value = "not set";
|
||||
}
|
||||
if (fieldname === "_assign") {
|
||||
operator = "like";
|
||||
value = `%${value}%`;
|
||||
}
|
||||
|
||||
return this.list_view.filter_area.add(this.list_view.doctype, fieldname, operator, value);
|
||||
}
|
||||
|
||||
render_dropdown_items(fields, fieldtype, $dropdown, applied_filter) {
|
||||
let standard_html = `
|
||||
<div class="dropdown-search mb-1">
|
||||
|
|
@ -945,18 +949,6 @@ class FilterArea {
|
|||
$dropdown.html(dropdown_html);
|
||||
}
|
||||
|
||||
setup_search($dropdown) {
|
||||
frappe.utils.setup_search($dropdown, ".group-by-item", ".group-by-value", "data-name");
|
||||
}
|
||||
|
||||
set_empty_state($dropdown) {
|
||||
$dropdown.html(
|
||||
`<div class="empty-state group-by-empty">
|
||||
${__("No filters found")}
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
|
||||
get_dropdown_html(field, fieldtype, applied = false) {
|
||||
let label;
|
||||
if (field.name == null) {
|
||||
|
|
@ -985,18 +977,61 @@ class FilterArea {
|
|||
</div>`;
|
||||
}
|
||||
|
||||
set_dropdown_loading_state($dropdown) {
|
||||
$dropdown.html(`<li>
|
||||
<div class="empty-state group-by-loading">
|
||||
${__("Loading...")}
|
||||
</div>
|
||||
</li>`);
|
||||
get_stats($dropdown) {
|
||||
let me = this;
|
||||
|
||||
frappe.call({
|
||||
method: "frappe.desk.reportview.get_sidebar_stats",
|
||||
type: "GET",
|
||||
args: {
|
||||
stats: ["_user_tags"],
|
||||
doctype: me.list_view.doctype,
|
||||
// wait for list filter area to be generated before getting filters, or fallback to default filters
|
||||
filters:
|
||||
(me.list_view.filter_area
|
||||
? me.list_view.get_filters_for_args()
|
||||
: me.default_filters) || [],
|
||||
},
|
||||
callback: function (r) {
|
||||
let stats = (r.message.stats || {})["_user_tags"] || [];
|
||||
me.render_stat(stats, $dropdown);
|
||||
frappe.utils.setup_search($dropdown, ".stat-link", ".stat-label");
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
render_stat(stats, $dropdown) {
|
||||
let args = {
|
||||
stats: stats,
|
||||
label: __("Tags"),
|
||||
applied_filter: this.list_view.get_filter_value("_user_tags"),
|
||||
};
|
||||
|
||||
let tag_list = $(frappe.render_template("list_sidebar_stat", args)).on(
|
||||
"click",
|
||||
".stat-link",
|
||||
(e) => {
|
||||
let fieldname = $(e.currentTarget).attr("data-field");
|
||||
let label = $(e.currentTarget).attr("data-label");
|
||||
let condition = "like";
|
||||
let existing = this.list_view.filter_area.filter_list.get_filter(fieldname);
|
||||
if (existing) {
|
||||
existing.remove();
|
||||
}
|
||||
if (label == "No Tags") {
|
||||
label = "not set";
|
||||
condition = "is";
|
||||
}
|
||||
this.list_view.filter_area.add(this.doctype, fieldname, condition, label);
|
||||
}
|
||||
);
|
||||
|
||||
$dropdown.html(tag_list);
|
||||
}
|
||||
|
||||
get_group_by_count(field) {
|
||||
let current_filters = this.list_view.get_filters_for_args();
|
||||
|
||||
// remove filter of the current field
|
||||
current_filters = current_filters.filter(
|
||||
(f_arr) => !f_arr.includes(field === "assigned_to" ? "_assign" : field)
|
||||
);
|
||||
|
|
@ -1020,6 +1055,40 @@ class FilterArea {
|
|||
});
|
||||
}
|
||||
|
||||
apply_filter(fieldname, value) {
|
||||
let operator = "=";
|
||||
if (value === "") {
|
||||
operator = "is";
|
||||
value = "not set";
|
||||
}
|
||||
if (fieldname === "_assign") {
|
||||
operator = "like";
|
||||
value = `%${value}%`;
|
||||
}
|
||||
|
||||
return this.list_view.filter_area.add(this.list_view.doctype, fieldname, operator, value);
|
||||
}
|
||||
|
||||
set_dropdown_loading_state($dropdown) {
|
||||
$dropdown.html(`<li>
|
||||
<div class="empty-state group-by-loading">
|
||||
${__("Loading...")}
|
||||
</div>
|
||||
</li>`);
|
||||
}
|
||||
|
||||
setup_search($dropdown) {
|
||||
frappe.utils.setup_search($dropdown, ".group-by-item", ".group-by-value", "data-name");
|
||||
}
|
||||
|
||||
set_empty_state($dropdown) {
|
||||
$dropdown.html(
|
||||
`<div class="empty-state group-by-empty">
|
||||
${__("No filters found")}
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
|
||||
remove_filters(filters) {
|
||||
filters.map((f) => {
|
||||
this.remove(f[1]);
|
||||
|
|
|
|||
|
|
@ -28,44 +28,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-section tags-section">
|
||||
<div class="sidebar-label">
|
||||
<svg class="es-icon es-line icon-xs" aria-hidden="true">
|
||||
<use class="" href="#es-line-right-chevron"></use>
|
||||
</svg>
|
||||
<span>{{ __("Tags") }}</span>
|
||||
</div>
|
||||
|
||||
<div class="list-tags hide">
|
||||
<div class="list-stats list-link">
|
||||
<a
|
||||
class="btn btn-default btn-sm list-sidebar-button"
|
||||
data-toggle="dropdown"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
href="#"
|
||||
>
|
||||
<span>{{ __("Tags") }}</span>
|
||||
<span>
|
||||
<svg class="icon icon-xs">
|
||||
<use href="#icon-select"></use>
|
||||
</svg>
|
||||
</span>
|
||||
</a>
|
||||
<ul class="dropdown-menu list-stats-dropdown" role="menu">
|
||||
<div class="dropdown-search">
|
||||
<input type="text" placeholder={{__("Search") }} data-element="search" class="form-control input-xs">
|
||||
</div>
|
||||
<div class="stat-result">
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="sidebar-action show-tags">
|
||||
<a class="list-tag-preview">{{ __("Show Tags") }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-section save-filter-section">
|
||||
<div class="sidebar-label">
|
||||
<svg class="es-icon es-line icon-xs" aria-hidden="true">
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
|
||||
{% if (stats.length) { %}
|
||||
<div class="dropdown-search mb-1">
|
||||
<input type="text"
|
||||
placeholder="${__("Search")}"
|
||||
data-element="search"
|
||||
class="dropdown-search-input form-control input-xs"
|
||||
>
|
||||
</div>
|
||||
{% } %}
|
||||
{% if (!stats.length) { %}
|
||||
<li class="stat-no-records text-muted">{{ __("No records tagged.") }}</li>
|
||||
{% } else {
|
||||
|
|
@ -7,8 +15,14 @@
|
|||
var stat_count = stats[i][1];
|
||||
%}
|
||||
<li>
|
||||
<a class="stat-link dropdown-item" data-label="{{ stat_label %}" data-field="_user_tags" href="#" onclick="return false;">
|
||||
<span class="stat-label">{{ __(stat_label) }}</span>
|
||||
<a class="stat-link dropdown-item flex justify-between group-by-item" data-label="{{ stat_label }}" data-value="{{ stat_label }}" data-field="_user_tags" href="#" onclick="return false;">
|
||||
|
||||
<span class="stat-label">
|
||||
{% if (applied_filter == stat_label) { %}
|
||||
<span class="applied"> {{ frappe.utils.icon("tick", "xs") }} </span>
|
||||
{% } %}
|
||||
{{ __(stat_label) }}
|
||||
</span>
|
||||
<span>{{ stat_count }}</span>
|
||||
</a>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -329,6 +329,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
|
||||
refresh_columns(meta, list_view_settings) {
|
||||
this.meta = meta;
|
||||
this.tags_shown = list_view_settings?.show_tags;
|
||||
this.list_view_settings = list_view_settings;
|
||||
|
||||
this.setup_columns();
|
||||
|
|
@ -727,7 +728,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
let classes = [
|
||||
"list-row-col ellipsis",
|
||||
col.type == "Subject" ? "list-subject level" : "hidden-xs",
|
||||
col.type == "Tag" ? "tag-col hide" : "",
|
||||
col.type == "Tag" ? `tag-col ${!this.tags_shown ? "hide" : ""} ` : "",
|
||||
frappe.model.is_numeric_field(col.df) ? "text-right" : "",
|
||||
col.df?.fieldname,
|
||||
].join(" ");
|
||||
|
|
@ -1338,7 +1339,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
this.setup_sort_by();
|
||||
this.setup_list_click();
|
||||
this.setup_drag_click();
|
||||
this.setup_tag_event();
|
||||
this.setup_tag_visibility();
|
||||
this.setup_new_doc_event();
|
||||
this.setup_check_events();
|
||||
this.setup_like();
|
||||
|
|
@ -1679,13 +1680,8 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
});
|
||||
}
|
||||
|
||||
setup_tag_event() {
|
||||
this.tags_shown = false;
|
||||
this.list_sidebar &&
|
||||
this.list_sidebar.parent.on("click", ".list-tag-preview", () => {
|
||||
this.tags_shown = !this.tags_shown;
|
||||
this.toggle_tags();
|
||||
});
|
||||
setup_tag_visibility() {
|
||||
this.tags_shown = this.list_view_settings?.show_tags;
|
||||
}
|
||||
|
||||
setup_realtime_updates() {
|
||||
|
|
@ -1853,12 +1849,6 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
this.toggle_actions_menu_button(this.$checks.length > 0);
|
||||
}
|
||||
|
||||
toggle_tags() {
|
||||
this.$result.find(".tag-col").toggleClass("hide");
|
||||
const preview_label = this.tags_shown ? __("Hide Tags") : __("Show Tags");
|
||||
this.list_sidebar.parent.find(".list-tag-preview").text(preview_label);
|
||||
}
|
||||
|
||||
get_checked_items(only_docnames) {
|
||||
const docnames = Array.from(this.$checks || []).map((check) =>
|
||||
cstr(unescape($(check).data().name))
|
||||
|
|
@ -1973,14 +1963,6 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
});
|
||||
}
|
||||
|
||||
items.push({
|
||||
label: __("Toggle Sidebar", null, "Button in list view menu"),
|
||||
action: () => this.toggle_side_bar(),
|
||||
condition: () => !this.page.disable_sidebar_toggle,
|
||||
standard: true,
|
||||
shortcut: "Ctrl+G",
|
||||
});
|
||||
|
||||
if (frappe.user.has_role("System Manager") && frappe.boot.developer_mode) {
|
||||
// edit doctype
|
||||
items.push({
|
||||
|
|
@ -2059,6 +2041,10 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
|
|||
label: "Created By",
|
||||
fieldname: "owner",
|
||||
},
|
||||
{
|
||||
label: "Tags",
|
||||
fieldname: "tags",
|
||||
},
|
||||
];
|
||||
fields = fields.concat(default_fields_dict);
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ frappe.views.ListViewSelect = class ListViewSelect {
|
|||
if (frappe.get_route().length > 3) {
|
||||
default_action = {
|
||||
label: __("Report Builder"),
|
||||
action: () => this.set_route("report"),
|
||||
action: () => frappe.set_route("report"),
|
||||
};
|
||||
}
|
||||
this.setup_dropdown_in_sidebar("Report", reports, default_action);
|
||||
|
|
@ -145,39 +145,21 @@ frappe.views.ListViewSelect = class ListViewSelect {
|
|||
}
|
||||
|
||||
setup_dropdown_in_sidebar(view, items, default_action) {
|
||||
if (!this.sidebar) return;
|
||||
const views_wrapper = this.sidebar.sidebar.find(".views-section");
|
||||
views_wrapper.find(".sidebar-label").html(__(view));
|
||||
const $dropdown = views_wrapper.find(".views-dropdown");
|
||||
|
||||
let placeholder = __("Select {0}", [__(view)]);
|
||||
let html = ``;
|
||||
|
||||
if (!items || !items.length) {
|
||||
html = `<div class="empty-state">
|
||||
${__("No {0} Found", [__(view)])}
|
||||
</div>`;
|
||||
} else {
|
||||
const page_name = this.get_page_name();
|
||||
if (items && items.length) {
|
||||
items.map((item) => {
|
||||
if (item.name.toLowerCase() == page_name.toLowerCase()) {
|
||||
placeholder = item.name;
|
||||
} else {
|
||||
html += `<li><a class="dropdown-item" href="${item.route}">${item.name}</a></li>`;
|
||||
}
|
||||
this.page.add_inner_button(
|
||||
item.name,
|
||||
() => location.replace(item.route),
|
||||
placeholder
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
views_wrapper.find(".selected-view").html(placeholder);
|
||||
|
||||
if (default_action) {
|
||||
views_wrapper.find(".sidebar-action a").html(default_action.label);
|
||||
views_wrapper.find(".sidebar-action a").click(() => default_action.action());
|
||||
if (default_action && Object.keys(default_action).length) {
|
||||
this.page.add_inner_button(default_action.label, default_action.action, placeholder);
|
||||
}
|
||||
|
||||
$dropdown.html(html);
|
||||
|
||||
views_wrapper.removeClass("hide");
|
||||
}
|
||||
|
||||
setup_kanban_switcher(kanbans) {
|
||||
|
|
|
|||
|
|
@ -77,12 +77,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
|
|||
super.setup_page();
|
||||
}
|
||||
|
||||
toggle_side_bar() {
|
||||
super.toggle_side_bar();
|
||||
// refresh datatable when sidebar is toggled to accomodate extra space
|
||||
this.render(true);
|
||||
}
|
||||
|
||||
setup_result_area() {
|
||||
super.setup_result_area();
|
||||
this.setup_charts_area();
|
||||
|
|
@ -1572,11 +1566,6 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
|
|||
label: __("Toggle Chart"),
|
||||
action: () => this.toggle_charts(),
|
||||
},
|
||||
{
|
||||
label: __("Toggle Sidebar"),
|
||||
action: () => this.toggle_side_bar(),
|
||||
shortcut: "Ctrl+K",
|
||||
},
|
||||
{
|
||||
label: __("Pick Columns"),
|
||||
action: () => {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue