github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/app/components/popover-menu.js (about) 1 import Component from '@ember/component'; 2 import { action } from '@ember/object'; 3 import { scheduleOnce } from '@ember/runloop'; 4 import { classNames } from '@ember-decorators/component'; 5 import classic from 'ember-classic-decorator'; 6 7 const TAB = 9; 8 const ARROW_DOWN = 40; 9 const FOCUSABLE = [ 10 'a:not([disabled])', 11 'button:not([disabled])', 12 'input:not([disabled]):not([type="hidden"])', 13 'textarea:not([disabled])', 14 '[tabindex]:not([disabled]):not([tabindex="-1"])', 15 ].join(', '); 16 17 @classic 18 @classNames('popover') 19 export default class PopoverMenu extends Component { 20 triggerClass = ''; 21 isOpen = false; 22 isDisabled = false; 23 label = ''; 24 25 dropdown = null; 26 27 capture(dropdown) { 28 // It's not a good idea to grab a dropdown reference like this, but it's necessary 29 // in order to invoke dropdown.actions.close in traverseList as well as 30 // dropdown.actions.reposition when the label or selection length changes. 31 this.set('dropdown', dropdown); 32 } 33 34 didReceiveAttrs() { 35 super.didReceiveAttrs(); 36 const dropdown = this.dropdown; 37 if (this.isOpen && dropdown) { 38 scheduleOnce('afterRender', this, this.repositionDropdown); 39 } 40 } 41 42 repositionDropdown() { 43 this.dropdown.actions.reposition(); 44 } 45 46 @action 47 openOnArrowDown(dropdown, e) { 48 if (!this.isOpen && e.keyCode === ARROW_DOWN) { 49 dropdown.actions.open(e); 50 e.preventDefault(); 51 } else if (this.isOpen && (e.keyCode === TAB || e.keyCode === ARROW_DOWN)) { 52 const optionsId = this.element 53 .querySelector('.popover-trigger') 54 .getAttribute('aria-owns'); 55 const popoverContentEl = document.querySelector(`#${optionsId}`); 56 const firstFocusableElement = popoverContentEl.querySelector(FOCUSABLE); 57 58 if (firstFocusableElement) { 59 firstFocusableElement.focus(); 60 e.preventDefault(); 61 } 62 } 63 } 64 }