Files
david/src/widgets/dropdown/Dropdown.js
T
2025-12-29 07:09:03 +02:00

181 lines
3.2 KiB
JavaScript

import './dropdown.css';
import { Ut } from '../../utils/Ut';
import { El } from '../../utils/dom';
import { Boot } from '../../core/Boot';
export class Dropdown {
#isOpen = false;
#hasRendered = false;
constructor(target, opts) {
this.target = target;
if (this.target.dxData('dropdown')) {
return;
}
this.target.dxData('dropdown', this);
Boot.addWidget(this);
this.opts = { ... { items: [] }, ... opts };
this.el = null;
this.ekey = Ut.nextKey();
this.#bindTargetEvents();
}
#bindTargetEvents() {
const that = this;
this.target.dxOn('focus.dropdown', e => {
e.stopPropagation();
Ut.trigger(that.opts.onFocus, this);
if (!that.#hasRendered) {
this.#render();
}
if (!that.#isOpen) {
that.show();
return;
}
that.hide();
});
}
#bindUiEvents() {
const that = this;
// Click on Item Handler
this.el.dxOn('click', 'li', (_, t) => {
that.hide();
Ut.trigger(that.opts.items[ t.dxIndex(that.items) ].handler);
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() );
let tm = null;
// Resize & Orientation Handler
window.dxOn(`resize.dropdown.${this.ekey} orientationchange.dropdown.${this.ekey}`, _ => {
if (tm) {
clearTimeout(tm);
tm = null;
}
tm = setTimeout(_ => that.#reposition(), 100);
});
}
#render() {
this.el = El('div', { className: 'sp-dropdown hide' });
if (this.opts.cls) {
this.el.classList.add(this.opts.cls);
}
const list = El('ul');
if (this.opts.items.length) {
this.el.append(list);
}
this.items = [];
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
))
);
});
document.body.append(this.el);
this.#bindUiEvents();
this.#hasRendered = true;
Ut.trigger(this.opts.onInit, this);
}
#reposition() {
const r = this.target.getBoundingClientRect();
const scrollX = window.scrollX || 0;
const scrollY = window.scrollY || 0;
this.el.style.left = Math.round(r.left + scrollX) + 'px';
this.el.style.top = Math.round(r.bottom + scrollY + 2) + 'px';
}
show() {
if (this.#isOpen) {
return;
}
this.#reposition();
this.el.classList.remove('hide');
this.el.classList.add('opened');
this.#isOpen = true;
Ut.trigger(this.opts.onShow, this);
}
hide() {
if (!this.#isOpen) {
return;
}
this.el.addEventListener('transitionend', _ => this.el.classList.add('hide'),
{ once: true }
);
this.el.classList.remove('opened');
this.#isOpen = false;
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);
}
}