updates
This commit is contained in:
+123
-121
@@ -2,178 +2,180 @@ import './dropdown.css';
|
||||
|
||||
import { Ut } from '../../utils/Ut';
|
||||
import { El } from '../../utils/dom';
|
||||
|
||||
////////////////////
|
||||
// DopDown Menu
|
||||
////////////////////
|
||||
import { Boot } from '../../core/Boot';
|
||||
|
||||
export class Dropdown {
|
||||
|
||||
/**
|
||||
* Dropdown Constructor
|
||||
*/
|
||||
constructor(target, opts) {
|
||||
#isOpen = false;
|
||||
#hasRendered = false;
|
||||
|
||||
this.target = target;
|
||||
constructor(target, opts) {
|
||||
|
||||
if (this.target.dxData('dropdown')) {
|
||||
return;
|
||||
}
|
||||
this.target = target;
|
||||
|
||||
this.target.dxData('dropdown', this);
|
||||
if (this.target.dxData('dropdown')) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.opts = { ... { items: [] }, ... opts };
|
||||
this.target.dxData('dropdown', this);
|
||||
|
||||
Boot.addWidget(this);
|
||||
|
||||
this.isOpened = false;
|
||||
this.isBusy = false;
|
||||
this.opts = { ... { items: [] }, ... opts };
|
||||
|
||||
this.create();
|
||||
this.el = null;
|
||||
|
||||
this.setEvents();
|
||||
}
|
||||
this.ekey = Ut.nextKey();
|
||||
|
||||
/**
|
||||
* Dropdown, Create UI
|
||||
*/
|
||||
create() {
|
||||
this.#bindTargetEvents();
|
||||
}
|
||||
|
||||
this.ui = El('div', { className: 'sp-dropdown hide' });
|
||||
#bindTargetEvents() {
|
||||
|
||||
if (this.opts.cls) {
|
||||
this.ui.classList.add(this.opts.cls);
|
||||
}
|
||||
const that = this;
|
||||
|
||||
const list = El('ul');
|
||||
this.target.dxOn('focus.dropdown', e => {
|
||||
|
||||
if (this.opts.items.length) {
|
||||
this.ui.append(list);
|
||||
}
|
||||
e.stopPropagation();
|
||||
|
||||
this.items = [];
|
||||
Ut.trigger(that.opts.onFocus, this);
|
||||
|
||||
this.opts.items.forEach(item => {
|
||||
this.items.push(
|
||||
list.appendChild( El('li', { className: 'color-'+ (item.color || 'black'), 'data-id': item.id },
|
||||
(item.icon ? El('i', { className: 'fa fa-'+ item.icon }) : null),
|
||||
item.name
|
||||
))
|
||||
);
|
||||
});
|
||||
if (!that.#hasRendered) {
|
||||
this.#render();
|
||||
}
|
||||
|
||||
this.target.after(this.ui);
|
||||
if (!that.#isOpen) {
|
||||
that.show();
|
||||
return;
|
||||
}
|
||||
|
||||
that.hide();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
Ut.trigger(this.opts.onInit, this);
|
||||
}
|
||||
#bindUiEvents() {
|
||||
|
||||
/**
|
||||
* Dropdown, Set UI Position
|
||||
*/
|
||||
setPosition() {
|
||||
const that = this;
|
||||
|
||||
// Click on Item Handler
|
||||
this.el.dxOn('click', 'li', (_, t) => {
|
||||
|
||||
let left = Math.round(this.target.offsetLeft);
|
||||
that.hide();
|
||||
|
||||
if ((this.ui.clientWidth + left) > window.innerWidth) {
|
||||
left = 0;
|
||||
}
|
||||
Ut.trigger(that.opts.items[ t.dxIndex(that.items) ].handler);
|
||||
|
||||
this.ui.style.top = (Math.round(this.target.offsetTop) + this.target.clientHeight + 2) + 'px';
|
||||
this.ui.style.left = left + 'px';
|
||||
}
|
||||
Ut.trigger(that.opts.onChange, t.dataset.id, this);
|
||||
});
|
||||
|
||||
// PointerDown Handler
|
||||
document.dxOn('pointerdown.dropdown', e => !this.target.contains(e.target) && !this.el.contains(e.target) && this.hide() );
|
||||
|
||||
/**
|
||||
* Dropdown, Show UI
|
||||
*/
|
||||
show() {
|
||||
let tm = null;
|
||||
|
||||
if (this.isBusy || this.isOpened) {
|
||||
return;
|
||||
}
|
||||
// Resize & Orientation Handler
|
||||
window.dxOn(`resize.dropdown.${this.ekey} orientationchange.dropdown.${this.ekey}`, _ => {
|
||||
|
||||
Ut.trigger(this.opts.onShow, this);
|
||||
if (tm) {
|
||||
clearTimeout(tm);
|
||||
tm = null;
|
||||
}
|
||||
|
||||
this.setPosition();
|
||||
tm = setTimeout(_ => that.#reposition(), 100);
|
||||
});
|
||||
}
|
||||
|
||||
this.ui.classList.remove('hide');
|
||||
this.ui.classList.add('opened');
|
||||
this.target.classList.add('dropdown-opened');
|
||||
#render() {
|
||||
|
||||
this.isOpened = true;
|
||||
}
|
||||
this.el = El('div', { className: 'sp-dropdown hide' });
|
||||
|
||||
/**
|
||||
* Dropdown, Hide UI
|
||||
*/
|
||||
hide() {
|
||||
if (this.opts.cls) {
|
||||
this.el.classList.add(this.opts.cls);
|
||||
}
|
||||
|
||||
if (!this.isOpened) {
|
||||
return;
|
||||
}
|
||||
const list = El('ul');
|
||||
|
||||
Ut.trigger(this.opts.onHide, this);
|
||||
if (this.opts.items.length) {
|
||||
this.el.append(list);
|
||||
}
|
||||
|
||||
let that = this;
|
||||
this.items = [];
|
||||
|
||||
this.ui.addEventListener('transitionend', _ => !this.ui.classList.contains('opened') && this.ui.classList.add('hide'), { once: true });
|
||||
this.opts.items.forEach(item => {
|
||||
this.items.push(
|
||||
list.appendChild( El('li', { className: 'color-'+ (item.color || 'black'), 'data-id': item.id },
|
||||
(item.icon ? El('i', { className: 'fa fa-'+ item.icon }) : null),
|
||||
item.name
|
||||
))
|
||||
);
|
||||
});
|
||||
|
||||
this.ui.classList.remove('opened');
|
||||
this.target.classList.remove('dropdown-opened');
|
||||
document.body.append(this.el);
|
||||
|
||||
this.isOpened = false;
|
||||
};
|
||||
this.#bindUiEvents();
|
||||
|
||||
/**
|
||||
* Dropdown, Set Evensts
|
||||
*/
|
||||
setEvents() {
|
||||
this.#hasRendered = true;
|
||||
|
||||
let that = this;
|
||||
Ut.trigger(this.opts.onInit, this);
|
||||
}
|
||||
|
||||
document.body.dxOn('click.dropdown', _ => that.hide());
|
||||
#reposition() {
|
||||
|
||||
this.target.dxOn('click', e => {
|
||||
const r = this.target.getBoundingClientRect();
|
||||
|
||||
e.stopPropagation();
|
||||
const scrollX = window.scrollX || 0;
|
||||
const scrollY = window.scrollY || 0;
|
||||
|
||||
if (!that.ui.classList.contains('opened')) {
|
||||
that.show();
|
||||
}
|
||||
else {
|
||||
that.hide();
|
||||
}
|
||||
});
|
||||
this.el.style.left = Math.round(r.left + scrollX) + 'px';
|
||||
this.el.style.top = Math.round(r.bottom + scrollY + 2) + 'px';
|
||||
}
|
||||
|
||||
this.ui.dxOn('click', 'li', (_, t) => {
|
||||
Ut.trigger(that.opts.items[ t.dxIndex(that.items) ].handler);
|
||||
Ut.trigger(that.opts.onChange, t.dataset.id, this);
|
||||
});
|
||||
show() {
|
||||
|
||||
let tm = null;
|
||||
if (this.#isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.dxOn('resize.dropdown' + this.ekey + ' orientationchange.dropdown' + this.ekey, _ => {
|
||||
this.#reposition();
|
||||
|
||||
if (tm) {
|
||||
clearTimeout(tm);
|
||||
tm = null;
|
||||
}
|
||||
this.el.classList.remove('hide');
|
||||
this.el.classList.add('opened');
|
||||
|
||||
tm = setTimeout(_ => that.setPosition(), 100);
|
||||
});
|
||||
}
|
||||
this.#isOpen = true;
|
||||
|
||||
/**
|
||||
* Dropdown, Destroy
|
||||
*/
|
||||
destroy() {
|
||||
Ut.trigger(this.opts.onShow, this);
|
||||
}
|
||||
|
||||
this.ui.remove();
|
||||
hide() {
|
||||
|
||||
if (!this.#isOpen) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.body.dxOff('click.dropdown');
|
||||
window.dxOff('resize.dropdown' + this.ekey + ' orientationchange.dropdown' + this.ekey);
|
||||
this.el.addEventListener('transitionend', _ => this.el.classList.add('hide'),
|
||||
{ once: true }
|
||||
);
|
||||
|
||||
this.target.dxRemoveData('dropdown');
|
||||
this.el.classList.remove('opened');
|
||||
|
||||
Ut.trigger(this.events.destroy, this);
|
||||
this.#isOpen = false;
|
||||
|
||||
return this.target;
|
||||
}
|
||||
Ut.trigger(this.opts.onHide, this);
|
||||
};
|
||||
|
||||
destroy() {
|
||||
|
||||
if (this.#hasRendered) {
|
||||
|
||||
this.el.remove();
|
||||
|
||||
document.dxOff(`pointerdown.dropdown.${this.ekey}`);
|
||||
window.dxOff(`resize.dropdown.${this.ekey} orientationchange.dropdown.${this.ekey}`);
|
||||
}
|
||||
|
||||
this.target.dxOff(`focus.dropdown.${this.ekey}`);
|
||||
this.target.dxRemoveData('dropdown');
|
||||
|
||||
Ut.trigger(this.opts.onDestroy, this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user