luci-base: ui.js: improve dropdown behaviour
authorJo-Philipp Wich <jo@mein.io>
Tue, 28 May 2019 13:28:53 +0000 (15:28 +0200)
committerJo-Philipp Wich <jo@mein.io>
Sun, 7 Jul 2019 13:36:25 +0000 (15:36 +0200)
 - Do not artificially cutoff dropdown items, use all available space
 - Close open dropdown when clicking into the preview area

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

index 7d04d7807c247060f4f39d4fd0e1f13b41f0fbcd..f5300169368042a7ed6db9c5485b0b73e8b94ea5 100644 (file)
@@ -350,7 +350,7 @@ var UIDropdown = UIElement.extend({
                        select_placeholder: _('-- Please choose --'),
                        custom_placeholder: _('-- custom --'),
                        display_items:      3,
-                       dropdown_items:     5,
+                       dropdown_items:     -1,
                        create:             false,
                        create_query:       '.create-item-input',
                        create_template:    'script[type="item-template"]'
@@ -577,11 +577,32 @@ var UIDropdown = UIElement.extend({
                        ul.style.top = ul.style.bottom = '';
 
                        window.requestAnimationFrame(function() {
-                               var height = items * li[Math.max(0, li.length - 2)].offsetHeight;
+                               var itemHeight = li[Math.max(0, li.length - 2)].getBoundingClientRect().height,
+                                   fullHeight = 0,
+                                   spaceAbove = rect.top,
+                                   spaceBelow = window.innerHeight - rect.height - rect.top;
+
+                               for (var i = 0; i < (items == -1 ? li.length : items); i++)
+                                       fullHeight += li[i].getBoundingClientRect().height;
+
+                               if (fullHeight <= spaceBelow) {
+                                       ul.style.top = rect.height + 'px';
+                                       ul.style.maxHeight = spaceBelow + 'px';
+                               }
+                               else if (fullHeight <= spaceAbove) {
+                                       ul.style.bottom = rect.height + 'px';
+                                       ul.style.maxHeight = spaceAbove + 'px';
+                               }
+                               else if (spaceBelow >= spaceAbove) {
+                                       ul.style.top = rect.height + 'px';
+                                       ul.style.maxHeight = (spaceBelow - (spaceBelow % itemHeight)) + 'px';
+                               }
+                               else {
+                                       ul.style.bottom = rect.height + 'px';
+                                       ul.style.maxHeight = (spaceAbove - (spaceAbove % itemHeight)) + 'px';
+                               }
 
                                ul.scrollTop = sel ? Math.max(sel.offsetTop - sel.offsetHeight, 0) : 0;
-                               ul.style[((rect.top + rect.height + height) > window.innerHeight) ? 'bottom' : 'top'] = rect.height + 'px';
-                               ul.style.maxHeight = height + 'px';
                        });
                }
 
@@ -898,6 +919,8 @@ var UIDropdown = UIElement.extend({
                                this.toggleItem(sb, li);
                        else if (li && li.parentNode.classList.contains('preview'))
                                this.closeDropdown(sb);
+                       else if (matchesElem(ev.target, 'span.open, span.more'))
+                               this.closeDropdown(sb);
                }
 
                ev.preventDefault();
@@ -1090,7 +1113,7 @@ var UICombobox = UIDropdown.extend({
                this.super('__init__', [ value, choices, Object.assign({
                        select_placeholder: _('-- Please choose --'),
                        custom_placeholder: _('-- custom --'),
-                       dropdown_items: 5,
+                       dropdown_items: -1,
                        sort: true
                }, options, {
                        multi: false,