1 import {onSelect} from "../services/dom";
5 * Provides some simple logic to create simple dropdown menus.
11 this.container = this.$el;
12 this.menu = this.$refs.menu;
13 this.toggle = this.$refs.toggle;
14 this.moveMenu = this.$opts.moveMenu;
16 this.direction = (document.dir === 'rtl') ? 'right' : 'left';
17 this.body = document.body;
19 this.setupListeners();
25 this.menu.style.display = 'block';
26 this.menu.classList.add('anim', 'menuIn');
27 this.toggle.setAttribute('aria-expanded', 'true');
30 // Move to body to prevent being trapped within scrollable sections
31 this.rect = this.menu.getBoundingClientRect();
32 this.body.appendChild(this.menu);
33 this.menu.style.position = 'fixed';
34 if (this.direction === 'right') {
35 this.menu.style.right = `${(this.rect.right - this.rect.width)}px`;
37 this.menu.style.left = `${this.rect.left}px`;
39 this.menu.style.top = `${this.rect.top}px`;
40 this.menu.style.width = `${this.rect.width}px`;
43 // Set listener to hide on mouse leave or window click
44 this.menu.addEventListener('mouseleave', this.hide.bind(this));
45 window.addEventListener('click', event => {
46 if (!this.menu.contains(event.target)) {
51 // Focus on first input if existing
52 const input = this.menu.querySelector('input');
53 if (input !== null) input.focus();
57 const showEvent = new Event('show');
58 this.container.dispatchEvent(showEvent);
61 event.stopPropagation();
66 for (let dropdown of window.components.dropdown) {
72 this.menu.style.display = 'none';
73 this.menu.classList.remove('anim', 'menuIn');
74 this.toggle.setAttribute('aria-expanded', 'false');
76 this.menu.style.position = '';
77 this.menu.style[this.direction] = '';
78 this.menu.style.top = '';
79 this.menu.style.width = '';
80 this.container.appendChild(this.menu);
86 return Array.from(this.menu.querySelectorAll('[tabindex],[href],button,input:not([type=hidden])'));
90 const focusable = this.getFocusable();
91 const currentIndex = focusable.indexOf(document.activeElement);
92 let newIndex = currentIndex + 1;
93 if (newIndex >= focusable.length) {
97 focusable[newIndex].focus();
101 const focusable = this.getFocusable();
102 const currentIndex = focusable.indexOf(document.activeElement);
103 let newIndex = currentIndex - 1;
105 newIndex = focusable.length - 1;
108 focusable[newIndex].focus();
112 // Hide menu on option click
113 this.container.addEventListener('click', event => {
114 const possibleChildren = Array.from(this.menu.querySelectorAll('a'));
115 if (possibleChildren.includes(event.target)) {
120 onSelect(this.toggle, event => {
121 event.stopPropagation();
123 if (event instanceof KeyboardEvent) {
128 // Keyboard navigation
129 const keyboardNavigation = event => {
130 if (event.key === 'ArrowDown' || event.key === 'ArrowRight') {
132 event.preventDefault();
133 } else if (event.key === 'ArrowUp' || event.key === 'ArrowLeft') {
134 this.focusPrevious();
135 event.preventDefault();
136 } else if (event.key === 'Escape') {
139 event.stopPropagation();
142 this.container.addEventListener('keydown', keyboardNavigation);
144 this.menu.addEventListener('keydown', keyboardNavigation);
147 // Hide menu on enter press or escape
148 this.menu.addEventListener('keydown ', event => {
149 if (event.key === 'Enter') {
150 event.preventDefault();
151 event.stopPropagation();
159 export default DropDown;