ui/lib/shared/addon/components/hover-dropdown/component.js

208 lines
4.9 KiB
JavaScript

import Component from '@ember/component';
import layout from './template';
import { cancel, later } from '@ember/runloop';
import { oneWay } from '@ember/object/computed';
import { get, set } from '@ember/object';
import calculatePosition from 'shared/utils/calculate-position';
import { inject as service } from '@ember/service'
import $ from 'jquery';
import { isEmpty } from '@ember/utils';
export default Component.extend({
router: service(),
layout,
delay: 200,
renderInPlace: true,
publicDropdownApi: null,
dropdownCloseDelay: 150,
ddCloseDelayTimer: null,
openDelay: oneWay('delay'),
closeDelay: oneWay('delay'),
init() {
this._super(...arguments);
this.router.on('routeWillChange', (/* transition */) => {
if (!isEmpty(this.publicDropdownApi) && !isEmpty(get(this, 'publicDropdownApi.actions.close'))) {
this.publicDropdownApi.actions.close();
set(this, 'publicDropdownApi', null);
}
});
},
actions: {
registerAPI(dd) {
this.registerAPI(dd);
},
open(forContent, dropdown) {
if (get(this, 'closeTimer') || this.isTransitioning()) {
cancel(get(this, 'closeTimer'));
set(this, 'closeTimer', null);
} else {
let openFn = () => {
set(this, 'openTimer', null);
this.openDropdown.call(this, forContent, dropdown);
};
let openDelay = get(this, 'openDelay');
if (openDelay) {
set(this, 'openTimer', later(openFn, openDelay));
} else {
openFn();
}
}
},
close(forContent, dropdown) {
if (this.openTimer || this.isTransitioning()) {
cancel(this.openTimer);
set(this, 'openTimer', null);
} else {
let closeFn = () => {
set(this, 'closeTimer', null);
this.closeDropdown.call(this, forContent, dropdown);
};
let closeDelay = get(this, 'closeDelay');
if (closeDelay) {
set(this, 'closeTimer', later(closeFn, closeDelay));
} else {
closeFn();
}
}
},
prevent() {
return false;
},
calculatePosition,
},
openDropdown(forContent, dropdown) {
if ( forContent ) {
if (this.onBeforeOpen) {
this.onBeforeOpen(dropdown);
}
}
dropdown.actions.open();
if ( forContent ) {
if (this.onOpen) {
this.onOpen(dropdown);
}
}
},
closeDropdown(forContent, dropdown, skipFocus = true) {
if ( forContent ) {
if (this.onBeforeClose) {
this.onBeforeClose(dropdown);
}
}
// signature - event, skipfocus
dropdown.actions.close(null, skipFocus);
if ( forContent ) {
if (this.onClose) {
this.onClose(dropdown);
}
}
},
isTransitioning() {
if (this.router._routerMicrolib && this.router._routerMicrolib.activeTransition && this.router._routerMicrolib.activeTransition.isTransition) {
return true
}
return false;
},
registerAPI(publicDropdown) {
set(this, 'publicDropdownApi', publicDropdown);
},
focusIn() {
if (this.ddCloseDelayTimer) {
clearTimeout(this.ddCloseDelayTimer);
}
},
focusOut() {
if (this.publicDropdownApi && this.publicDropdownApi.isOpen) {
set(this, 'ddCloseDelayTimer', setTimeout(() => {
if (this.isDestroyed || this.isDestroying) {
return;
}
set(this, 'ddCloseDelayTimer', null);
this.closeDropdown(null, this.publicDropdownApi);
}, this.dropdownCloseDelay));
}
},
keyUp(e) {
const code = e.keyCode;
const { publicDropdownApi } = this;
if (!publicDropdownApi) {
return;
}
let tabList = $(`#ember-basic-dropdown-content-${ publicDropdownApi.uniqueId } > LI > a:first-child`);
let currentFocusIndex = tabList.index(e.target);
switch (code) {
case 27: {
if (publicDropdownApi && publicDropdownApi.isOpen) {
this.closeDropdown(null, publicDropdownApi, false);
}
break;
}
case 38: {
// up
let nextIndex = currentFocusIndex - 1;
if (nextIndex >= tabList.length) {
tabList.eq(tabList.length).focus();
} else {
tabList.eq(nextIndex).focus();
}
break;
}
case 40: {
// down
if (publicDropdownApi && publicDropdownApi.isOpen) {
let nextIndex = currentFocusIndex + 1;
if (nextIndex >= tabList.length) {
tabList.eq(0).focus();
} else {
tabList.eq(nextIndex).focus();
}
} else if (publicDropdownApi && !publicDropdownApi.isOpen) {
this.openDropdown(null, publicDropdownApi);
later(() => {
$(`#ember-basic-dropdown-content-${ publicDropdownApi.uniqueId } > LI > a:first-child`).first().focus();
}, 10)
}
break;
}
default:
}
},
});