luci-base, themes: dropdown behaviour improvements
authorJo-Philipp Wich <jo@mein.io>
Thu, 8 Nov 2018 12:02:24 +0000 (13:02 +0100)
committerJo-Philipp Wich <jo@mein.io>
Wed, 14 Nov 2018 19:46:04 +0000 (20:46 +0100)
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
modules/luci-base/htdocs/luci-static/resources/cbi.js
themes/luci-theme-bootstrap/htdocs/luci-static/bootstrap/cascade.css
themes/luci-theme-openwrt/htdocs/luci-static/openwrt.org/cascade.css

index 9c8341cc54a4a0ac62aa5f51225a3da005aec7fa..b3ba8259f91a87cf6ef6d912e29bada79ab9c17d 100644 (file)
@@ -1436,6 +1436,14 @@ if (window.NodeList && !NodeList.prototype.forEach) {
        };
 }
 
+if (!window.requestAnimationFrame) {
+       window.requestAnimationFrame = function(f) {
+               window.setTimeout(function() {
+                       f(new Date().getTime())
+               }, 1000/30);
+       };
+}
+
 
 var dummyElem, domParser;
 
@@ -1556,11 +1564,10 @@ CBIDropdown = {
                var st = window.getComputedStyle(sb, null),
                    ul = sb.querySelector('ul'),
                    li = ul.querySelectorAll('li'),
+                   fl = findParent(sb, '.cbi-value-field'),
                    sel = ul.querySelector('[selected]'),
                    rect = sb.getBoundingClientRect(),
-                   h = sb.clientHeight - parseFloat(st.paddingTop) - parseFloat(st.paddingBottom),
-                   mh = this.dropdown_items * h,
-                   eh = Math.min(mh, li.length * h);
+                   items = Math.min(this.dropdown_items, li.length);
 
                document.querySelectorAll('.cbi-dropdown[open]').forEach(function(s) {
                        s.dispatchEvent(new CustomEvent('cbi-dropdown-close', {}));
@@ -1568,22 +1575,54 @@ CBIDropdown = {
 
                sb.setAttribute('open', '');
 
+               var pv = ul.cloneNode(true);
+                   pv.classList.add('preview');
+
+               if (fl)
+                       fl.classList.add('cbi-dropdown-open');
+
                if ('ontouchstart' in window) {
-                       var scroll = document.documentElement.scrollTop,
-                           vpWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
-                           vpHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0);
+                       var vpWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
+                           vpHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0),
+                           scrollFrom = window.pageYOffset,
+                           scrollTo = scrollFrom + rect.top - vpHeight * 0.5,
+                           start = null;
 
-                       ul.style.top = h + 'px';
+                       ul.style.top = sb.offsetHeight + 'px';
                        ul.style.left = -rect.left + 'px';
                        ul.style.right = (rect.right - vpWidth) + 'px';
+                       ul.style.maxHeight = (vpHeight * 0.5) + 'px';
+                       ul.style.WebkitOverflowScrolling = 'touch';
+
+                       var scrollStep = function(timestamp) {
+                               if (!start) {
+                                       start = timestamp;
+                                       ul.scrollTop = sel ? Math.max(sel.offsetTop - sel.offsetHeight, 0) : 0;
+                               }
+
+                               var duration = Math.max(timestamp - start, 1);
+                               if (duration < 100) {
+                                       document.body.scrollTop = scrollFrom + (scrollTo - scrollFrom) * (duration / 100);
+                                       window.requestAnimationFrame(scrollStep);
+                               }
+                               else {
+                                       document.body.scrollTop = scrollTo;
+                               }
+                       };
 
-                       window.scrollTo(0, (scroll + rect.top - vpHeight * 0.6));
+                       window.requestAnimationFrame(scrollStep);
                }
                else {
-                       ul.style.maxHeight = mh + 'px';
-                       ul.scrollTop = sel ? Math.max(sel.offsetTop - sel.offsetHeight, 0) : 0;
+                       ul.style.maxHeight = '1px';
                        ul.style.top = ul.style.bottom = '';
-                       ul.style[((rect.top + rect.height + eh) > window.innerHeight) ? 'bottom' : 'top'] = rect.height + 'px';
+
+                       window.requestAnimationFrame(function() {
+                               var height = items * li[Math.max(0, li.length - 2)].offsetHeight;
+
+                               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';
+                       });
                }
 
                ul.querySelectorAll('[selected] input[type="checkbox"]').forEach(function(c) {
@@ -1592,10 +1631,6 @@ CBIDropdown = {
 
                ul.classList.add('dropdown');
 
-               var pv = ul.cloneNode(true);
-                   pv.classList.remove('dropdown');
-                   pv.classList.add('preview');
-
                sb.insertBefore(pv, ul.nextElementSibling);
 
                li.forEach(function(l) {
@@ -1613,7 +1648,8 @@ CBIDropdown = {
 
                var pv = sb.querySelector('ul.preview'),
                    ul = sb.querySelector('ul.dropdown'),
-                   li = ul.querySelectorAll('li');
+                   li = ul.querySelectorAll('li'),
+                   fl = findParent(sb, '.cbi-value-field');
 
                li.forEach(function(l) { l.removeAttribute('tabindex'); });
                sb.lastElementChild.removeAttribute('tabindex');
@@ -1623,6 +1659,10 @@ CBIDropdown = {
                sb.style.width = sb.style.height = '';
 
                ul.classList.remove('dropdown');
+               ul.style.top = ul.style.bottom = ul.style.maxHeight = '';
+
+               if (fl)
+                       fl.classList.remove('cbi-dropdown-open');
 
                if (!no_focus)
                        this.setFocus(sb, sb);
@@ -1817,14 +1857,13 @@ CBIDropdown = {
 
        createItems: function(sb, value) {
                var sbox = this,
-                   val = (value || '').trim().split(/\s+/),
+                   val = (value || '').trim(),
                    ul = sb.querySelector('ul');
 
                if (!sbox.multi)
-                       val.length = Math.min(val.length, 1);
-
-               if (val.length === 1 && val[0].length === 0)
-                       val.length = 0;
+                       val = val.length ? [ val ] : [];
+               else
+                       val = val.length ? val.split(/\s+/) : [];
 
                val.forEach(function(item) {
                        var new_item = null;
@@ -1881,6 +1920,8 @@ CBIDropdown = {
                        var li = findParent(ev.target, 'li');
                        if (li && li.parentNode.classList.contains('dropdown'))
                                this.toggleItem(sb, li);
+                       else if (li && li.parentNode.classList.contains('preview'))
+                               this.closeDropdown(sb);
                }
 
                ev.preventDefault();
index 22c49fdde1a0538cbc6ec026656a815ec6c3600f..e64744994e950ea727d63b77caf406610585cc86 100644 (file)
@@ -60,7 +60,6 @@ blockquote:after {
 }
 
 html {
-       overflow-y: scroll;
        font-size: 100%;
        -webkit-text-size-adjust: 100%;
        -ms-text-size-adjust: 100%;
@@ -1457,6 +1456,7 @@ footer {
        max-width: none;
        min-width: 100%;
        width: auto;
+       transition: max-height .125s ease-in;
 }
 
 .cbi-dropdown > ul > li[display],
index f24493c993f12277895c8b9c1ff2ff517a143b46..a88d678f6dbfdb2bc615e23ba87edb77f5e70450 100644 (file)
@@ -491,16 +491,16 @@ select:hover {
 
 input[type=text],
 input[type=password] {
-       padding: 0 3px;
+       padding: .25em;
 }
 
 select,
 input[type=text],
-input[type=password] {
+input[type=password],
+.cbi-dropdown {
        width: 20em;
-       font-size: inherit;
-       line-height: 13pt;
-       height: 14pt;
+       font-size: 10pt;
+       height: 22px;
 }
 
 select[multiple] {
@@ -521,7 +521,6 @@ input.cbi-input-password + img {
        width: 100%;
 }
 
-.td [data-dynlist] > input,
 .td input.cbi-input-password {
        width: calc(100% - 20px);
 }
@@ -1050,6 +1049,7 @@ ul.cbi-tabmenu li.cbi-tab {
        font-weight: bold;
        text-shadow: 1px 1px 0px #fff;
        display: none;
+       min-height: 22px;
 }
 
 .cbi-dropdown > ul > li {
@@ -1062,7 +1062,7 @@ ul.cbi-tabmenu li.cbi-tab {
        flex-grow: 1;
        align-items: center;
        align-self: center;
-       min-height: 20px;
+       min-height: 22px;
 }
 
 .cbi-dropdown > ul > li .hide-open { display: initial; }
@@ -1092,10 +1092,6 @@ ul.cbi-tabmenu li.cbi-tab {
        margin: 0;
 }
 
-.cbi-dropdown > ul > li input[type="text"] {
-       height: 20px;
-}
-
 .cbi-dropdown[open] {
        position: relative;
 }
@@ -1110,6 +1106,7 @@ ul.cbi-tabmenu li.cbi-tab {
        max-width: none;
        min-width: 100%;
        width: auto;
+       transition: max-height .125s ease-in;
 }
 
 .cbi-dropdown > ul > li[display],
@@ -1125,7 +1122,6 @@ ul.cbi-tabmenu li.cbi-tab {
 }
 
 .cbi-dropdown[empty] > ul > li,
-.cbi-dropdown[optional][open] > ul.dropdown > li[placeholder],
 .cbi-dropdown[multiple][open] > ul.dropdown > li > form {
        display: block;
 }
@@ -1213,9 +1209,9 @@ select + .cbi-button {
        padding: 0 6px;
        vertical-align: top;
        display: inline-block;
-       height: 14pt;
+       height: 22px;
        font-size: 10pt;
-       line-height: 12pt;
+       line-height: 20px;
 }
 
 .cbi-tooltip-container {
@@ -1240,6 +1236,7 @@ select + .cbi-button {
        left: auto;
        opacity: 1;
        transition: opacity .25s ease-in;
+       white-space: normal;
 }
 
 .zonebadge .cbi-tooltip {
@@ -1652,6 +1649,10 @@ select + .cbi-button {
                display: inline-block;
        }
 
+       .td.cbi-dropdown-open {
+               overflow: visible;
+       }
+
        .td select {
                word-wrap: normal;
        }
@@ -1734,26 +1735,41 @@ select + .cbi-button {
                font-size: 12pt;
        }
 
-       input, textarea, select {
+       input, textarea, select, .cbi-button {
                font-size: 12pt !important;
-               line-height: 1.4em;
+               line-height: 30px;
        }
 
        select, input[type="text"], input[type="password"] {
                width: 100%;
-               height: 1.4em;
+               height: 30px;
        }
 
-       input.cbi-input-password {
+       input[type="text"] + .cbi-button,
+       input[type="password"] + .cbi-button,
+       select + .cbi-button {
+               height: 30px;
+               line-height: 28px;
+       }
+
+       input.cbi-input-password,
+       [data-dynlist] > .add-item > input {
                width: calc(100% - 20px);
        }
 
        .cbi-dynlist,
        .cbi-dropdown {
                min-width: 100%;
+               height: auto;
                display: flex;
        }
 
+       .cbi-dropdown > .more,
+       .cbi-dropdown > ul > li,
+       .cbi-dropdown > ul > li[placeholder] {
+               min-height: 30px;
+       }
+
        .btn, .cbi-button {
                font-size: 9pt !important;
                line-height: 13pt;