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