diff --git a/frappe/public/js/frappe/list/list_view.js b/frappe/public/js/frappe/list/list_view.js
index e450b8c1e6..a578312e34 100644
--- a/frappe/public/js/frappe/list/list_view.js
+++ b/frappe/public/js/frappe/list/list_view.js
@@ -634,7 +634,10 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
${frappe.utils.icon("heart", "sm", "like-icon")}
- ${__(subject_field.label)}
+
+ ${__(subject_field.label)}
+
`;
const $columns = this.columns
.map((col) => {
@@ -645,15 +648,21 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
frappe.model.is_numeric_field(col.df) ? "text-right" : "",
].join(" ");
- return `
-
- ${
- col.type === "Subject"
- ? subject_html
- : `
- ${__((col.df && col.df.label) || col.type)}`
- }
-
+ let html = "";
+ if (col.type === "Subject") {
+ html = subject_html;
+ } else {
+ const fieldname = col.df?.fieldname;
+ const attrs = fieldname
+ ? ` data-sort-by="${fieldname}"
+ title="${__("Click to sort by {0}", [col.df?.label])}"`
+ : "";
+ html = `
+ ${__(col.df?.label || col.type)}
+ `;
+ }
+
+ return `${html}
`;
})
.join("");
@@ -1061,6 +1070,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
setup_events() {
this.setup_filterable();
+ this.setup_sort_by();
this.setup_list_click();
this.setup_drag_click();
this.setup_tag_event();
@@ -1202,6 +1212,20 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
});
}
+ setup_sort_by() {
+ this.$result.on("click", "[data-sort-by]", (e) => {
+ const sort_by = e.currentTarget.getAttribute("data-sort-by");
+ if (!sort_by) return;
+ let sort_order = "asc"; // always start with asc
+ if (this.sort_by === sort_by) {
+ // unless it's the same field, then toggle
+ sort_order = this.sort_order === "asc" ? "desc" : "asc";
+ }
+ this.sort_selector.set_value(sort_by, sort_order);
+ this.on_sort_change(sort_by, sort_order);
+ });
+ }
+
setup_list_click() {
this.$result.on("click", ".list-row, .image-view-header, .file-header", (e) => {
const $target = $(e.target);
diff --git a/frappe/public/js/frappe/ui/sort_selector.js b/frappe/public/js/frappe/ui/sort_selector.js
index eb59bec8c7..88fc32d8a3 100644
--- a/frappe/public/js/frappe/ui/sort_selector.js
+++ b/frappe/public/js/frappe/ui/sort_selector.js
@@ -23,26 +23,35 @@ frappe.ui.SortSelector = class SortSelector {
// order
this.wrapper.find(".btn-order").on("click", function () {
- let btn = $(this);
const order = $(this).attr("data-value") === "desc" ? "asc" : "desc";
- const title =
- $(this).attr("data-value") === "desc" ? __("ascending") : __("descending");
-
- btn.attr("data-value", order);
- btn.attr("title", title);
- me.sort_order = order;
- const icon_name = order === "asc" ? "sort-ascending" : "sort-descending";
- btn.find(".sort-order").html(frappe.utils.icon(icon_name, "sm"));
+ me.set_value(me.sort_by, order);
(me.onchange || me.change)(me.sort_by, me.sort_order);
});
// select field
this.wrapper.find(".dropdown-menu a.option").on("click", function () {
- me.sort_by = $(this).attr("data-value");
- me.wrapper.find(".dropdown-text").html($(this).html());
+ me.set_value($(this).attr("data-value"), me.sort_order);
(me.onchange || me.change)(me.sort_by, me.sort_order);
});
}
+ set_value(sort_by, sort_order) {
+ const $btn = this.wrapper.find(".btn-order");
+ const $icon = $btn.find(".sort-order");
+ const $text = this.wrapper.find(".dropdown-text");
+
+ if (this.sort_by !== sort_by) {
+ this.sort_by = sort_by;
+ $text.html(__(this.get_label(sort_by)));
+ }
+ if (this.sort_order !== sort_order) {
+ this.sort_order = sort_order;
+ const title = sort_order === "desc" ? __("ascending") : __("descending");
+ const icon_name = sort_order === "asc" ? "sort-ascending" : "sort-descending";
+ $btn.attr("data-value", sort_order);
+ $btn.attr("title", title);
+ $icon.html(frappe.utils.icon(icon_name, "sm"));
+ }
+ }
prepare_args() {
var me = this;
if (!this.args) {
diff --git a/frappe/public/scss/desk/list.scss b/frappe/public/scss/desk/list.scss
index d464e6dec1..f65e65741b 100644
--- a/frappe/public/scss/desk/list.scss
+++ b/frappe/public/scss/desk/list.scss
@@ -165,6 +165,11 @@
a {
color: var(--text-light);
}
+
+ & > [data-sort-by]:hover {
+ cursor: pointer;
+ text-decoration: underline;
+ }
}
$level-margin-right: 8px;