luci-base: ui.js: improve ui.Dropdown event handling
Improve overall event and focus handling, avoid registering a global mouseover event listener, stop propagating escape keypress on closing dropdown and avoid `Element.blur()` to prevent de-focusing open modals. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
parent
c13ef9406c
commit
8db3e0e70f
1 changed files with 42 additions and 19 deletions
|
@ -1050,7 +1050,8 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
|
||||||
'class': 'cbi-dropdown',
|
'class': 'cbi-dropdown',
|
||||||
'multiple': this.options.multiple ? '' : null,
|
'multiple': this.options.multiple ? '' : null,
|
||||||
'optional': this.options.optional ? '' : null,
|
'optional': this.options.optional ? '' : null,
|
||||||
'disabled': this.options.disabled ? '' : null
|
'disabled': this.options.disabled ? '' : null,
|
||||||
|
'tabindex': -1
|
||||||
}, E('ul'));
|
}, E('ul'));
|
||||||
|
|
||||||
var keys = Object.keys(this.choices);
|
var keys = Object.keys(this.choices);
|
||||||
|
@ -1186,11 +1187,11 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sb.addEventListener('mouseover', this.handleMouseover.bind(this));
|
sb.addEventListener('mouseover', this.handleMouseover.bind(this));
|
||||||
|
sb.addEventListener('mouseout', this.handleMouseout.bind(this));
|
||||||
sb.addEventListener('focus', this.handleFocus.bind(this));
|
sb.addEventListener('focus', this.handleFocus.bind(this));
|
||||||
|
|
||||||
canary.addEventListener('focus', this.handleCanaryFocus.bind(this));
|
canary.addEventListener('focus', this.handleCanaryFocus.bind(this));
|
||||||
|
|
||||||
window.addEventListener('mouseover', this.setFocus);
|
|
||||||
window.addEventListener('click', this.closeAllDropdowns);
|
window.addEventListener('click', this.closeAllDropdowns);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1343,7 +1344,12 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
|
||||||
|
|
||||||
sb.lastElementChild.setAttribute('tabindex', 0);
|
sb.lastElementChild.setAttribute('tabindex', 0);
|
||||||
|
|
||||||
this.setFocus(sb, sel || li[0], true);
|
var focusFn = L.bind(function(el) {
|
||||||
|
this.setFocus(sb, el, true);
|
||||||
|
ul.removeEventListener('transitionend', focusFn);
|
||||||
|
}, this, sel || li[0]);
|
||||||
|
|
||||||
|
ul.addEventListener('transitionend', focusFn);
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
|
@ -1559,26 +1565,33 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
setFocus: function(sb, elem, scroll) {
|
setFocus: function(sb, elem, scroll) {
|
||||||
if (sb && sb.hasAttribute && sb.hasAttribute('locked-in'))
|
if (sb.hasAttribute('locked-in'))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (sb.target && findParent(sb.target, 'ul.dropdown'))
|
sb.querySelectorAll('.focus').forEach(function(e) {
|
||||||
return;
|
e.classList.remove('focus');
|
||||||
|
|
||||||
document.querySelectorAll('.focus').forEach(function(e) {
|
|
||||||
if (!matchesElem(e, 'input')) {
|
|
||||||
e.classList.remove('focus');
|
|
||||||
e.blur();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (elem) {
|
elem.classList.add('focus');
|
||||||
elem.focus();
|
|
||||||
elem.classList.add('focus');
|
|
||||||
|
|
||||||
if (scroll)
|
if (scroll)
|
||||||
elem.parentNode.scrollTop = elem.offsetTop - elem.parentNode.offsetTop;
|
elem.parentNode.scrollTop = elem.offsetTop - elem.parentNode.offsetTop;
|
||||||
}
|
|
||||||
|
elem.focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
/** @private */
|
||||||
|
handleMouseout: function(ev) {
|
||||||
|
var sb = ev.currentTarget;
|
||||||
|
|
||||||
|
if (!sb.hasAttribute('open'))
|
||||||
|
return;
|
||||||
|
|
||||||
|
sb.querySelectorAll('.focus').forEach(function(e) {
|
||||||
|
e.classList.remove('focus');
|
||||||
|
});
|
||||||
|
|
||||||
|
sb.querySelector('ul.dropdown').focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
|
@ -1758,7 +1771,8 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
|
||||||
|
|
||||||
/** @private */
|
/** @private */
|
||||||
handleKeydown: function(ev) {
|
handleKeydown: function(ev) {
|
||||||
var sb = ev.currentTarget;
|
var sb = ev.currentTarget,
|
||||||
|
ul = sb.querySelector('ul.dropdown');
|
||||||
|
|
||||||
if (matchesElem(ev.target, 'input'))
|
if (matchesElem(ev.target, 'input'))
|
||||||
return;
|
return;
|
||||||
|
@ -1779,6 +1793,7 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
|
||||||
switch (ev.keyCode) {
|
switch (ev.keyCode) {
|
||||||
case 27:
|
case 27:
|
||||||
this.closeDropdown(sb);
|
this.closeDropdown(sb);
|
||||||
|
ev.stopPropagation();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 13:
|
case 13:
|
||||||
|
@ -1802,6 +1817,10 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
|
||||||
this.setFocus(sb, active.previousElementSibling);
|
this.setFocus(sb, active.previousElementSibling);
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
|
else if (document.activeElement === ul) {
|
||||||
|
this.setFocus(sb, ul.lastElementChild);
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 40:
|
case 40:
|
||||||
|
@ -1809,6 +1828,10 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
|
||||||
this.setFocus(sb, active.nextElementSibling);
|
this.setFocus(sb, active.nextElementSibling);
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
}
|
}
|
||||||
|
else if (document.activeElement === ul) {
|
||||||
|
this.setFocus(sb, ul.firstElementChild);
|
||||||
|
ev.preventDefault();
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue