luci-base: form.js: add column sorting to TableSections and GridSections 5696/head
authorJo-Philipp Wich <jo@mein.io>
Thu, 24 Feb 2022 15:48:00 +0000 (16:48 +0100)
committerJo-Philipp Wich <jo@mein.io>
Thu, 24 Feb 2022 22:45:18 +0000 (23:45 +0100)
Add ability to reorder TableSection and GridSection rows by clicking on
column headers.

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
modules/luci-base/htdocs/luci-static/resources/form.js

index 23cc0b1cb574778565391f9884763889f8fdb971..c17f8ff0ca967ad45cf985fd65e27de50d63c180 100644 (file)
@@ -2615,7 +2615,8 @@ var CBITableSection = CBITypedSection.extend(/** @lends LuCI.form.TableSection.p
                if (has_titles) {
                        var trEl = E('tr', {
                                'class': 'tr cbi-section-table-titles ' + anon_class,
-                               'data-title': (!this.anonymous || this.sectiontitle) ? _('Name') : null
+                               'data-title': (!this.anonymous || this.sectiontitle) ? _('Name') : null,
+                               'click': this.sortable ? ui.createHandlerFn(this, 'handleSort') : null
                        });
 
                        for (var i = 0, opt; i < max_cols && (opt = this.children[i]) != null; i++) {
@@ -2624,7 +2625,8 @@ var CBITableSection = CBITypedSection.extend(/** @lends LuCI.form.TableSection.p
 
                                trEl.appendChild(E('th', {
                                        'class': 'th cbi-section-table-cell',
-                                       'data-widget': opt.__name__
+                                       'data-widget': opt.__name__,
+                                       'data-sortable-row': this.sortable ? '' : null
                                }));
 
                                if (opt.width != null)
@@ -3034,6 +3036,68 @@ var CBITableSection = CBITypedSection.extend(/** @lends LuCI.form.TableSection.p
                        .catch(function() {});
        },
 
+       /** @private */
+       handleSort: function(ev) {
+               if (!ev.target.matches('th[data-sortable-row]'))
+                       return;
+
+               var th = ev.target,
+                   descending = (th.getAttribute('data-sort-direction') == 'desc'),
+                   config_name = this.uciconfig || this.map.config,
+                   index = 0,
+                   list = [];
+
+               ev.currentTarget.querySelectorAll('th').forEach(function(other_th, i) {
+                       if (other_th !== th)
+                               other_th.removeAttribute('data-sort-direction');
+                       else
+                               index = i;
+               });
+
+               ev.currentTarget.parentNode.querySelectorAll('tr.cbi-section-table-row').forEach(L.bind(function(tr, i) {
+                       var sid = tr.getAttribute('data-sid'),
+                           opt = tr.childNodes[index].getAttribute('data-name'),
+                           val = this.cfgvalue(sid, opt);
+
+                       tr.querySelectorAll('.flash').forEach(function(n) {
+                               n.classList.remove('flash')
+                       });
+
+                       list.push([
+                               ui.Table.prototype.deriveSortKey((val != null) ? val.trim() : ''),
+                               tr
+                       ]);
+               }, this));
+
+               list.sort(function(a, b) {
+                       if (a[0] < b[0])
+                               return descending ? 1 : -1;
+
+                       if (a[0] > b[0])
+                               return descending ? -1 : 1;
+
+                       return 0;
+               });
+
+               window.requestAnimationFrame(L.bind(function() {
+                       var ref_sid, cur_sid;
+
+                       for (var i = 0; i < list.length; i++) {
+                               list[i][1].childNodes[index].classList.add('flash');
+                               th.parentNode.parentNode.appendChild(list[i][1]);
+
+                               cur_sid = list[i][1].getAttribute('data-sid');
+
+                               if (ref_sid)
+                                       this.map.data.move(config_name, cur_sid, ref_sid, true);
+
+                               ref_sid = cur_sid;
+                       }
+
+                       th.setAttribute('data-sort-direction', descending ? 'asc' : 'desc');
+               }, this));
+       },
+
        /**
         * Add further options to the per-section instanced modal popup.
         *