From 4eb092760e2b5a2dc87da4cad8c532bd3ca41ea1 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 31 Jan 2020 11:50:16 +0530 Subject: [PATCH] fix: MultiSelectDialog UX - Made results area header sticky - Added persistence. Co-Authored-By: Shivam Mishra --- .../js/frappe/form/multi_select_dialog.js | 42 +++++++++++++------ frappe/public/less/list.less | 8 ++++ 2 files changed, 38 insertions(+), 12 deletions(-) diff --git a/frappe/public/js/frappe/form/multi_select_dialog.js b/frappe/public/js/frappe/form/multi_select_dialog.js index 2621448af9..cdd385a6ea 100644 --- a/frappe/public/js/frappe/form/multi_select_dialog.js +++ b/frappe/public/js/frappe/form/multi_select_dialog.js @@ -163,6 +163,7 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ }, get_checked_values: function() { + // Return name of checked value. return this.$results.find('.list-item-container').map(function() { if ($(this).find('.list-row-check:checkbox:checked').length > 0 ) { return $(this).attr('data-item-name'); @@ -170,6 +171,12 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ }).get(); }, + get_checked_items: function() { + // Return checked items with all the column values. + let checked_values = this.get_checked_values(); + return this.results.filter(res => checked_values.includes(res.name)); + }, + make_list_row: function(result={}) { var me = this; // Make a head row by default (if result not passed) @@ -206,18 +213,17 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ ${contents} `); - head ? $row.addClass('list-item--head') : $row = $(`
`).append($row); return $row; }, - render_result_list: function(results, more = 0) { + render_result_list: function(results, more = 0, empty=true) { var me = this; var more_btn = me.dialog.fields_dict.more_btn.$wrapper; // Make empty result set if filter is set - if (!frappe.flags.auto_scroll) { + if (!frappe.flags.auto_scroll && empty) { this.empty_list(); } more_btn.hide(); @@ -225,9 +231,13 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ if (results.length === 0) return; if (more) more_btn.show(); - results.forEach((result) => { - me.$results.append(me.make_list_row(result)); - }); + let checked = this.get_checked_values(); + + results + .filter(result => !checked.includes(result.name)) + .forEach(result => { + me.$results.append(me.make_list_row(result)); + }); if (frappe.flags.auto_scroll) { this.$results.animate({scrollTop: me.$results.prop('scrollHeight')}, 500); @@ -235,7 +245,14 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ }, empty_list: function() { + let checked = this.get_checked_items().map(item => { + return { + ...item, + checked: true + } + }); this.$results.find('.list-item-container').remove(); + this.render_result_list(checked, 0, false); }, get_results: function() { @@ -278,7 +295,8 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ no_spinner: true, args: args, callback: function(r) { - let results = [], more = 0; + let more = 0; + me.results = []; if (r.values.length) { if (r.values.length > me.page_length) { r.values.pop(); @@ -290,22 +308,22 @@ frappe.ui.form.MultiSelectDialog = Class.extend({ } result.checked = 0; result.parsed_date = Date.parse(result["Date"]); - results.push(result); + me.results.push(result); }); - results.map( (result) => { + me.results.map( (result) => { result["Date"] = frappe.format(result["Date"], {"fieldtype":"Date"}); }) - results.sort((a, b) => { + me.results.sort((a, b) => { return a.parsed_date - b.parsed_date; }); // Preselect oldest entry if (me.start < 1 && r.values.length === 1) { - results[0].checked = 1; + me.results[0].checked = 1; } } - me.render_result_list(results, more); + me.render_result_list(me.results, more); } }); }, diff --git a/frappe/public/less/list.less b/frappe/public/less/list.less index c22184406c..3294e24bf3 100644 --- a/frappe/public/less/list.less +++ b/frappe/public/less/list.less @@ -525,6 +525,14 @@ input.list-check-all, input.list-row-checkbox { // list view +.modal-body { + .list-item--head { + position: sticky !important; + z-index: 500; + top: 0; + } +} + .list-items { width: 100%; }