X-Git-Url: http://source.bookstackapp.com/bookstack/blobdiff_plain/4310d34135243e91fdacd960a825f60f0231621e..3f5dc10cd4cf901b44b1cf8c9e2626bf0425d488:/resources/js/components/dropdown.js diff --git a/resources/js/components/dropdown.js b/resources/js/components/dropdown.js index 2625ff4de..b68f332b6 100644 --- a/resources/js/components/dropdown.js +++ b/resources/js/components/dropdown.js @@ -1,5 +1,6 @@ -import {onSelect} from "../services/dom"; -import {Component} from "./component"; +import {onSelect} from '../services/dom'; +import {KeyboardNavigationHandler} from '../services/keyboard-navigation'; +import {Component} from './component'; /** * Dropdown @@ -17,8 +18,9 @@ export class Dropdown extends Component { this.direction = (document.dir === 'rtl') ? 'right' : 'left'; this.body = document.body; this.showing = false; - this.setupListeners(); + this.hide = this.hide.bind(this); + this.setupListeners(); } show(event = null) { @@ -39,7 +41,11 @@ export class Dropdown extends Component { this.menu.style.position = 'fixed'; this.menu.style.width = `${menuOriginalRect.width}px`; this.menu.style.left = `${menuOriginalRect.left}px`; - heightOffset = dropUpwards ? (window.innerHeight - menuOriginalRect.top - toggleHeight / 2) : menuOriginalRect.top; + if (dropUpwards) { + heightOffset = (window.innerHeight - menuOriginalRect.top - toggleHeight / 2); + } else { + heightOffset = menuOriginalRect.top; + } } // Adjust menu to display upwards if near the bottom of the screen @@ -52,9 +58,9 @@ export class Dropdown extends Component { } // Set listener to hide on mouse leave or window click - this.menu.addEventListener('mouseleave', this.hide.bind(this)); - window.addEventListener('click', event => { - if (!this.menu.contains(event.target)) { + this.menu.addEventListener('mouseleave', this.hide); + window.addEventListener('click', clickEvent => { + if (!this.menu.contains(clickEvent.target)) { this.hide(); } }); @@ -74,7 +80,7 @@ export class Dropdown extends Component { } hideAll() { - for (let dropdown of window.$components.get('dropdown')) { + for (const dropdown of window.$components.get('dropdown')) { dropdown.hide(); } } @@ -97,78 +103,40 @@ export class Dropdown extends Component { this.showing = false; } - getFocusable() { - return Array.from(this.menu.querySelectorAll('[tabindex]:not([tabindex="-1"]),[href],button,input:not([type=hidden])')); - } - - focusNext() { - const focusable = this.getFocusable(); - const currentIndex = focusable.indexOf(document.activeElement); - let newIndex = currentIndex + 1; - if (newIndex >= focusable.length) { - newIndex = 0; - } - - focusable[newIndex].focus(); - } + setupListeners() { + const keyboardNavHandler = new KeyboardNavigationHandler(this.container, event => { + this.hide(); + this.toggle.focus(); + if (!this.bubbleEscapes) { + event.stopPropagation(); + } + }, event => { + if (event.target.nodeName === 'INPUT') { + event.preventDefault(); + event.stopPropagation(); + } + this.hide(); + }); - focusPrevious() { - const focusable = this.getFocusable(); - const currentIndex = focusable.indexOf(document.activeElement); - let newIndex = currentIndex - 1; - if (newIndex < 0) { - newIndex = focusable.length - 1; + if (this.moveMenu) { + keyboardNavHandler.shareHandlingToEl(this.menu); } - focusable[newIndex].focus(); - } - - setupListeners() { // Hide menu on option click this.container.addEventListener('click', event => { - const possibleChildren = Array.from(this.menu.querySelectorAll('a')); - if (possibleChildren.includes(event.target)) { - this.hide(); - } + const possibleChildren = Array.from(this.menu.querySelectorAll('a')); + if (possibleChildren.includes(event.target)) { + this.hide(); + } }); onSelect(this.toggle, event => { event.stopPropagation(); this.show(event); if (event instanceof KeyboardEvent) { - this.focusNext(); - } - }); - - // Keyboard navigation - const keyboardNavigation = event => { - if (event.key === 'ArrowDown' || event.key === 'ArrowRight') { - this.focusNext(); - event.preventDefault(); - } else if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') { - this.focusPrevious(); - event.preventDefault(); - } else if (event.key === 'Escape') { - this.hide(); - this.toggle.focus(); - if (!this.bubbleEscapes) { - event.stopPropagation(); - } - } - }; - this.container.addEventListener('keydown', keyboardNavigation); - if (this.moveMenu) { - this.menu.addEventListener('keydown', keyboardNavigation); - } - - // Hide menu on enter press or escape - this.menu.addEventListener('keydown ', event => { - if (event.key === 'Enter') { - event.preventDefault(); - event.stopPropagation(); - this.hide(); + keyboardNavHandler.focusNext(); } }); } -} \ No newline at end of file +}