65 lines
1.8 KiB
JavaScript
65 lines
1.8 KiB
JavaScript
import Component from '@ember/component';
|
|
import { action } from '@ember/object';
|
|
import { run } from '@ember/runloop';
|
|
import { classNames } from '@ember-decorators/component';
|
|
import classic from 'ember-classic-decorator';
|
|
|
|
const TAB = 9;
|
|
const ARROW_DOWN = 40;
|
|
const FOCUSABLE = [
|
|
'a:not([disabled])',
|
|
'button:not([disabled])',
|
|
'input:not([disabled]):not([type="hidden"])',
|
|
'textarea:not([disabled])',
|
|
'[tabindex]:not([disabled]):not([tabindex="-1"])',
|
|
].join(', ');
|
|
|
|
@classic
|
|
@classNames('popover')
|
|
export default class PopoverMenu extends Component {
|
|
triggerClass = '';
|
|
isOpen = false;
|
|
isDisabled = false;
|
|
label = '';
|
|
|
|
dropdown = null;
|
|
|
|
capture(dropdown) {
|
|
// It's not a good idea to grab a dropdown reference like this, but it's necessary
|
|
// in order to invoke dropdown.actions.close in traverseList as well as
|
|
// dropdown.actions.reposition when the label or selection length changes.
|
|
this.set('dropdown', dropdown);
|
|
}
|
|
|
|
didReceiveAttrs() {
|
|
super.didReceiveAttrs();
|
|
const dropdown = this.dropdown;
|
|
if (this.isOpen && dropdown) {
|
|
run.scheduleOnce('afterRender', this, this.repositionDropdown);
|
|
}
|
|
}
|
|
|
|
repositionDropdown() {
|
|
this.dropdown.actions.reposition();
|
|
}
|
|
|
|
@action
|
|
openOnArrowDown(dropdown, e) {
|
|
if (!this.isOpen && e.keyCode === ARROW_DOWN) {
|
|
dropdown.actions.open(e);
|
|
e.preventDefault();
|
|
} else if (this.isOpen && (e.keyCode === TAB || e.keyCode === ARROW_DOWN)) {
|
|
const optionsId = this.element
|
|
.querySelector('.popover-trigger')
|
|
.getAttribute('aria-owns');
|
|
const popoverContentEl = document.querySelector(`#${optionsId}`);
|
|
const firstFocusableElement = popoverContentEl.querySelector(FOCUSABLE);
|
|
|
|
if (firstFocusableElement) {
|
|
firstFocusableElement.focus();
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
}
|