Improved dropdown menu's handling

This commit is contained in:
djmaze 2021-09-14 10:06:38 +02:00
parent 5839bcf62d
commit 2cb73643ca
8 changed files with 37 additions and 52 deletions

View file

@ -234,7 +234,7 @@ ko.bindingHandlers.onEsc = {
ko.bindingHandlers.registerBootstrapDropdown = { ko.bindingHandlers.registerBootstrapDropdown = {
init: element => { init: element => {
rl.Dropdowns.register(element); rl.Dropdowns.register(element);
element.ddBtn = new BSN.Dropdown(element.querySelector('[data-toggle="dropdown"]')); element.ddBtn = new BSN.Dropdown(element.querySelector('.dropdown-toggle'));
} }
}; };

View file

@ -74,19 +74,12 @@ export class AbstractSystemDropDownUserView extends AbstractViewRight {
this.allowContacts && showScreenPopup(ContactsPopupView); this.allowContacts && showScreenPopup(ContactsPopupView);
} }
layoutDesktop() toggleLayout()
{ {
doc.cookie = 'rllayout=desktop'; const mobile = !ThemeStore.isMobile();
ThemeStore.isMobile(false); doc.cookie = 'rllayout=' + (mobile ? 'mobile' : 'desktop');
leftPanelDisabled(false); ThemeStore.isMobile(mobile);
// location.reload(); leftPanelDisabled(mobile);
}
layoutMobile()
{
doc.cookie = 'rllayout=mobile';
ThemeStore.isMobile(true);
leftPanelDisabled(true);
// location.reload(); // location.reload();
} }

View file

@ -12,7 +12,7 @@
</a> </a>
<!-- ko if: !newMoveToFolder --> <!-- ko if: !newMoveToFolder -->
<div class="btn-group dropdown colored-toggle hide-mobile" data-bind="visible: allowMessageListActions, registerBootstrapDropdown: true, openDropdownTrigger: moveDropdownTrigger"> <div class="btn-group dropdown colored-toggle hide-mobile" data-bind="visible: allowMessageListActions, registerBootstrapDropdown: true, openDropdownTrigger: moveDropdownTrigger">
<a id="move-dropdown-id" href="#" tabindex="-1" class="btn dropdown-toggle buttonMove" data-toggle="dropdown" data-bind="command: moveCommand" data-i18n="[title]GLOBAL/MOVE_TO"> <a id="move-dropdown-id" href="#" tabindex="-1" class="btn dropdown-toggle buttonMove" data-bind="command: moveCommand" data-i18n="[title]GLOBAL/MOVE_TO">
<i class="icon-copy visible-on-ctrl-btn"></i> <i class="icon-copy visible-on-ctrl-btn"></i>
<i class="fontastic hidden-on-ctrl-btn">📁</i> <i class="fontastic hidden-on-ctrl-btn">📁</i>
<span class="caret"></span> <span class="caret"></span>
@ -46,7 +46,7 @@
data-bind="command: deleteCommand" data-i18n="[title]GLOBAL/DELETE">🗑</a> data-bind="command: deleteCommand" data-i18n="[title]GLOBAL/DELETE">🗑</a>
</div> </div>
<div class="btn-group dropdown colored-toggle" data-bind="visible: allowComposer || allowMessageListActions || allowDangerousActions, registerBootstrapDropdown: true, openDropdownTrigger: moreDropdownTrigger"> <div class="btn-group dropdown colored-toggle" data-bind="visible: allowComposer || allowMessageListActions || allowDangerousActions, registerBootstrapDropdown: true, openDropdownTrigger: moreDropdownTrigger">
<a id="more-list-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1" data-toggle="dropdown" data-i18n="[title]GLOBAL/MORE"></a> <a id="more-list-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1" data-i18n="[title]GLOBAL/MORE"></a>
<ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="more-list-dropdown-id"> <ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="more-list-dropdown-id">
<div data-bind="visible: allowMessageListActions"> <div data-bind="visible: allowMessageListActions">
<li class="e-item" role="presentation" data-bind="click: listUnsetSeen, css: {'disabled': !hasCheckedOrSelectedLines()}"> <li class="e-item" role="presentation" data-bind="click: listUnsetSeen, css: {'disabled': !hasCheckedOrSelectedLines()}">
@ -101,7 +101,7 @@
</ul> </ul>
</div> </div>
<div class="btn-group dropdown sortFolder" data-bind="visible: sortSupported() && mobileCheckedStateHide(), registerBootstrapDropdown: true, openDropdownTrigger: sortDropdownTrigger"> <div class="btn-group dropdown sortFolder" data-bind="visible: sortSupported() && mobileCheckedStateHide(), registerBootstrapDropdown: true, openDropdownTrigger: sortDropdownTrigger">
<a id="sort-list-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1" data-toggle="dropdown" data-i18n="[title]MESSAGE_LIST/SORT" data-bind="text: sortText"></a> <a id="sort-list-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1" data-i18n="[title]MESSAGE_LIST/SORT" data-bind="text: sortText"></a>
<ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="sort-list-dropdown-id"> <ul class="dropdown-menu g-ui-menu" role="menu" aria-labelledby="sort-list-dropdown-id">
<li class="e-item" role="presentation" data-sort="DATE" data-bind="click: changeSort"> <li class="e-item" role="presentation" data-sort="DATE" data-bind="click: changeSort">
<a class="e-link menuitem" href="#" tabindex="-1"> <a class="e-link menuitem" href="#" tabindex="-1">

View file

@ -52,7 +52,7 @@
<a class="btn btn-thin-2 btn-transparent buttonForward fontastic" <a class="btn btn-thin-2 btn-transparent buttonForward fontastic"
data-bind="visible: 'forward' === lastReplyAction() && allowComposer, command: forwardCommand" data-i18n="[title]MESSAGE/BUTTON_FORWARD">→</a> data-bind="visible: 'forward' === lastReplyAction() && allowComposer, command: forwardCommand" data-i18n="[title]MESSAGE/BUTTON_FORWARD">→</a>
<div class="btn-group" data-bind="registerBootstrapDropdown: true, visible: allowComposer || allowMessageListActions || allowMessageActions" style="display: inline-block"> <div class="btn-group" data-bind="registerBootstrapDropdown: true, visible: allowComposer || allowMessageListActions || allowMessageActions" style="display: inline-block">
<a class="btn btn-thin-2 btn-transparent dropdown-toggle" id="more-view-dropdown-id" href="#" tabindex="-1" data-toggle="dropdown" style="margin-left: -4px; margin-right: 2px"> <a class="btn btn-thin-2 btn-transparent dropdown-toggle" id="more-view-dropdown-id" href="#" tabindex="-1" style="margin-left: -4px; margin-right: 2px">
<span data-bind="visible: allowComposer"></span> <span data-bind="visible: allowComposer"></span>
<span data-bind="visible: !allowComposer" class="fontastic"></span> <span data-bind="visible: !allowComposer" class="fontastic"></span>
</a> </a>

View file

@ -27,7 +27,7 @@
<div class="e-cell e-value"> <div class="e-cell e-value">
<div class="dropdown pull-left" data-bind="registerBootstrapDropdown: true, openDropdownTrigger: identitiesDropdownTrigger"> <div class="dropdown pull-left" data-bind="registerBootstrapDropdown: true, openDropdownTrigger: identitiesDropdownTrigger">
<a class="dropdown-toggle e-identity" href="#" tabindex="-1" <a class="dropdown-toggle e-identity" href="#" tabindex="-1"
id="identity-label-id" role="button" data-toggle="dropdown" id="identity-label-id" role="button"
data-bind="text: currentIdentityView, dropdownCloser: true, css: {'multiply': 1 < identitiesOptions().length }"> data-bind="text: currentIdentityView, dropdownCloser: true, css: {'multiply': 1 < identitiesOptions().length }">
</a> </a>
<!-- ko if: 1 < identitiesOptions().length --> <!-- ko if: 1 < identitiesOptions().length -->
@ -40,7 +40,7 @@
</div> </div>
<div class="pull-right"> <div class="pull-right">
<div class="btn-group dropdown colored-toggle pull-right" style="margin-right: 4px;" data-bind="registerBootstrapDropdown: true"> <div class="btn-group dropdown colored-toggle pull-right" style="margin-right: 4px;" data-bind="registerBootstrapDropdown: true">
<a class="btn dropdown-toggle fontastic" data-toggle="dropdown"></a> <a class="btn dropdown-toggle fontastic"></a>
<ul class="dropdown-menu right-edge g-ui-menu" role="menu"> <ul class="dropdown-menu right-edge g-ui-menu" role="menu">
<li class="e-item" data-bind="click: function () { requestReadReceipt(!requestReadReceipt()); }"> <li class="e-item" data-bind="click: function () { requestReadReceipt(!requestReadReceipt()); }">
<a class="e-link"> <a class="e-link">
@ -127,7 +127,7 @@
<div class="e-cell e-value"> <div class="e-cell e-value">
<div> <div>
<div class="pull-left"> <div class="pull-left">
<div class="btn-group" data-toggle="buttons-radio"> <div class="btn-group">
<button type="button" class="btn" data-bind="click: function () { attachmentsPlace(false); }, <button type="button" class="btn" data-bind="click: function () { attachmentsPlace(false); },
css: { 'active': !attachmentsPlace() }"> css: { 'active': !attachmentsPlace() }">
<i class="icon-file-text"></i> <i class="icon-file-text"></i>

View file

@ -15,7 +15,7 @@
<a class="btn button-delete fontastic" data-bind="command: deleteCommand">🗑</a> <a class="btn button-delete fontastic" data-bind="command: deleteCommand">🗑</a>
<div class="btn-group dropdown colored-toggle" data-bind="registerBootstrapDropdown: true, visible: !contacts.importing() && !contacts.syncing()"> <div class="btn-group dropdown colored-toggle" data-bind="registerBootstrapDropdown: true, visible: !contacts.importing() && !contacts.syncing()">
<a id="contacts-more-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1" data-toggle="dropdown"></a> <a id="contacts-more-dropdown-id" class="btn dropdown-toggle fontastic" href="#" tabindex="-1"></a>
<ul class="dropdown-menu g-ui-menu right-edge" role="menu" aria-labelledby="contacts-more-dropdown-id"> <ul class="dropdown-menu g-ui-menu right-edge" role="menu" aria-labelledby="contacts-more-dropdown-id">
<li class="e-item" role="presentation"> <li class="e-item" role="presentation">
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="initDom: importUploaderButton"> <a class="e-link menuitem" href="#" tabindex="-1" data-bind="initDom: importUploaderButton">
@ -85,7 +85,7 @@
</button> </button>
</div> </div>
<div class="btn-group dropdown colored-toggle button-add-prop" data-bind="visible: !emptySelection(), registerBootstrapDropdown: true"> <div class="btn-group dropdown colored-toggle button-add-prop" data-bind="visible: !emptySelection(), registerBootstrapDropdown: true">
<a id="button-add-prop-dropdown-id" href="#" tabindex="-1" class="btn dropdown-toggle" data-toggle="dropdown"> <a id="button-add-prop-dropdown-id" href="#" tabindex="-1" class="btn dropdown-toggle">
<span data-i18n="CONTACTS/ADD_MENU_LABEL"></span> <span data-i18n="CONTACTS/ADD_MENU_LABEL"></span>
<span class="caret"></span> <span class="caret"></span>
</a> </a>

View file

@ -8,7 +8,7 @@
</div> </div>
<div class="accountPlace hide-mobile" data-bind="text: emailTitle()"></div> <div class="accountPlace hide-mobile" data-bind="text: emailTitle()"></div>
<div class="btn-group dropdown colored-toggle" data-bind="registerBootstrapDropdown: true, openDropdownTrigger: accountMenuDropdownTrigger"> <div class="btn-group dropdown colored-toggle" data-bind="registerBootstrapDropdown: true, openDropdownTrigger: accountMenuDropdownTrigger">
<a id="top-system-dropdown-id" href="#" tabindex="-1" class="btn single btn-block dropdown-toggle" data-toggle="dropdown"> <a id="top-system-dropdown-id" href="#" tabindex="-1" class="btn single btn-block dropdown-toggle">
<i class="fontastic" data-bind="css: {'icon-spinner': accountsLoading()}">👤</i> <i class="fontastic" data-bind="css: {'icon-spinner': accountsLoading()}">👤</i>
<!-- <!--
<b data-bind="text: accountsUnreadCount, visible: 100 > accountsUnreadCount() && 0 < accountsUnreadCount()"></b> <b data-bind="text: accountsUnreadCount, visible: 100 > accountsUnreadCount() && 0 < accountsUnreadCount()"></b>
@ -42,8 +42,8 @@
<span data-i18n="TOP_TOOLBAR/BUTTON_ADD_ACCOUNT"></span> <span data-i18n="TOP_TOOLBAR/BUTTON_ADD_ACCOUNT"></span>
</a> </a>
</li> </li>
<li class="e-item" role="presentation" data-bind="visible: allowSettings"> <li class="e-item" role="presentation" data-bind="visible: allowSettings && allowContacts">
<a class="e-link menuitem" href="#" tabindex="-1" data-bind="visible: allowContacts, click: contactsClick"> <a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: contactsClick">
<i class="fontastic">📇</i> <i class="fontastic">📇</i>
<span data-i18n="GLOBAL/CONTACTS"></span> <span data-i18n="GLOBAL/CONTACTS"></span>
</a> </a>
@ -61,13 +61,11 @@
</a> </a>
</li> </li>
<li class="e-item dividerbar" role="presentation"> <li class="e-item dividerbar" role="presentation">
<a class="e-link menuitem show-mobile" href="#" tabindex="-1" data-bind="click: layoutDesktop"> <a class="e-link menuitem" href="#" tabindex="-1" data-bind="click: toggleLayout">
<i class="fontastic">💻</i> <i class="fontastic show-mobile">💻</i>
<span data-i18n="MOBILE/BUTTON_DESKTOP_VERSION"></span> <span class="show-mobile" data-i18n="MOBILE/BUTTON_DESKTOP_VERSION"></span>
</a> <i class="fontastic hide-mobile">📱</i>
<a class="e-link menuitem hide-mobile" href="#" tabindex="-1" data-bind="click: layoutMobile"> <span class="hide-mobile" data-i18n="MOBILE/BUTTON_MOBILE_VERSION"></span>
<i class="fontastic">📱</i>
<span data-i18n="MOBILE/BUTTON_MOBILE_VERSION"></span>
</a> </a>
</li> </li>
<li class="e-item" role="presentation"> <li class="e-item" role="presentation">

View file

@ -8,10 +8,10 @@
const setFocus = element => element.focus ? element.focus() : element.setActive(); const setFocus = element => element.focus ? element.focus() : element.setActive();
this.BSN = { this.BSN = {
Dropdown: function(element) { Dropdown: function(toggleBtn) {
let menu, menuItems = []; let menu, menuItems = [];
const self = this, const self = this,
parent = element.parentNode, parent = toggleBtn.parentNode,
preventEmptyAnchor = e => { preventEmptyAnchor = e => {
const t = e.target, href = t.href || (t.parentNode && t.parentNode.href); const t = e.target, href = t.href || (t.parentNode && t.parentNode.href);
(href && href.slice(-1) === '#') && e.preventDefault(); (href && href.slice(-1) === '#') && e.preventDefault();
@ -19,30 +19,24 @@
open = bool => { open = bool => {
menu && menu.classList.toggle('show', bool); menu && menu.classList.toggle('show', bool);
parent.classList.toggle('show', bool); parent.classList.toggle('show', bool);
element.setAttribute('aria-expanded', bool); toggleBtn.setAttribute('aria-expanded', bool);
element.open = bool; toggleBtn.open = bool;
if (bool) { if (bool) {
element.removeEventListener('click',clickHandler); toggleBtn.removeEventListener('click',clickHandler);
} else { } else {
setTimeout(() => element.addEventListener('click',clickHandler), 1); setTimeout(() => toggleBtn.addEventListener('click',clickHandler), 1);
} }
}, },
toggleEvents = () => { toggleEvents = () => {
let action = (element.open ? 'add' : 'remove') + 'EventListener'; let action = (toggleBtn.open ? 'add' : 'remove') + 'EventListener';
doc[action]('click',dismissHandler); doc[action]('click',dismissHandler);
doc[action]('keydown',preventScroll); doc[action]('keydown',preventScroll);
doc[action]('keyup',keyHandler); doc[action]('keyup',keyHandler);
doc[action]('focus',dismissHandler); doc[action]('focus',dismissHandler);
}, },
dismissHandler = e => { dismissHandler = e => {
let eventTarget = e.target, let eventTarget = e.target;
inside = menu.contains(eventTarget), if ((!menu.contains(eventTarget) && !toggleBtn.contains(eventTarget)) || e.type !== 'focus') {
hasData = eventTarget && (
(eventTarget.getAttribute && eventTarget.getAttribute('data-toggle'))
|| (eventTarget.parentNode && eventTarget.parentNode.getAttribute('data-toggle')));
if (!(hasData && inside)
&& !(e.type === 'focus' && (inside || eventTarget === element))
) {
self.hide(); self.hide();
preventEmptyAnchor(e); preventEmptyAnchor(e);
} }
@ -57,7 +51,7 @@
self.toggle(); self.toggle();
} else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') { } else if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
let activeItem = doc.activeElement, let activeItem = doc.activeElement,
isMenuButton = activeItem === element, isMenuButton = activeItem === toggleBtn,
idx = isMenuButton ? 0 : menuItems.indexOf(activeItem); idx = isMenuButton ? 0 : menuItems.indexOf(activeItem);
if (parent.contains(activeItem)) { if (parent.contains(activeItem)) {
if (!isMenuButton) { if (!isMenuButton) {
@ -76,18 +70,18 @@
!('tabindex' in menu) && menu.setAttribute('tabindex', '0'); !('tabindex' in menu) && menu.setAttribute('tabindex', '0');
open(true); open(true);
setTimeout(() => { setTimeout(() => {
setFocus( menu.getElementsByTagName('INPUT')[0] || element ); setFocus( menu.getElementsByTagName('INPUT')[0] || toggleBtn );
toggleEvents(); toggleEvents();
},1); },1);
}; };
self.hide = () => { self.hide = () => {
open(false); open(false);
toggleEvents(); toggleEvents();
setFocus(element); setFocus(toggleBtn);
}; };
self.toggle = () => element.open ? self.hide() : self.show(); self.toggle = () => toggleBtn.open ? self.hide() : self.show();
open(false); open(false);
element.Dropdown = self; toggleBtn.Dropdown = self;
} }
}; };