This commit is contained in:
2024-01-17 17:31:41 +08:00
parent 84190ed9f1
commit fb9e920c15
35 changed files with 3003 additions and 1304 deletions

View File

@ -82,17 +82,18 @@ function filterSource(searchkeys, textkey, key, source) {
}
export class Dropdown {
#options;
_var = {};
// _var.options;
#wrapper;
#container;
#label;
// _var.wrapper;
// _var.container;
// _var.label;
#allChecked;
#source;
#lastSelected;
#selected;
#selectedList;
// _var.allChecked;
// _var.source;
// _var.lastSelected;
// _var.selected;
// _var.selectedList;
sourceFilter;
onselectedlist;
@ -105,18 +106,18 @@ export class Dropdown {
options.valuekey ??= 'value';
options.htmlkey ??= 'html';
options.maxlength ??= 500;
this.#options = options;
this._var.options = options;
}
create() {
const options = this.#options;
const options = this._var.options;
// wrapper
const wrapper = createElement('div', 'ui-drop-wrapper');
const dropId = String(Math.random()).substring(2);
wrapper.dataset.dropId = dropId;
dropdownGlobal[dropId] = this;
this.#wrapper = wrapper;
this._var.wrapper = wrapper;
// header
const header = createElement('div', 'ui-drop-header');
@ -131,8 +132,8 @@ export class Dropdown {
if (up || down) {
const source = this.source;
const count = source.length;
const valuekey = this.#options.valuekey;
let index = source?.indexOf(this.#selected);
const valuekey = this._var.options.valuekey;
let index = source?.indexOf(this._var.selected);
if (isNaN(index) || index < -1) {
index = -1;
} else if (index >= count) {
@ -158,19 +159,19 @@ export class Dropdown {
this.select(target);
}
} else if (e.key === 'Tab') {
this.#dropdown(false);
this._dropdown(false);
}
});
header.addEventListener('click', () => {
if (this.disabled) {
return;
}
const active = this.#expanded;
const label = this.#label;
const active = this._expanded;
const label = this._var.label;
if (active && label.ownerDocument.activeElement === label) {
return;
}
this.#dropdown(!active);
this._dropdown(!active);
if (!active && typeof this.onexpanded === 'function') {
setTimeout(() => this.onexpanded(), 120);
}
@ -187,21 +188,21 @@ export class Dropdown {
label.addEventListener('input', e => {
const key = e.target.value.toLowerCase();
const source = filterSource(options.searchkeys, options.textkey, key, this.source);
this.#filllist(source);
this.#container.classList.add('active');
this._filllist(source);
this._var.container.classList.add('active');
});
label.addEventListener('blur', e => this.select(e.target.value));
label.addEventListener('mousedown', e => this.#expanded && e.stopPropagation());
label.addEventListener('mousedown', e => this._expanded && e.stopPropagation());
} else {
isPositive(options.tabIndex) && header.setAttribute('tabindex', options.tabIndex);
label = createElement('label', 'ui-drop-text');
}
this.#label = label;
this._var.label = label;
if (options.multiselect) {
if (Array.isArray(options.selectedlist)) {
this.selectlist(options.selectedlist, true);
} else {
this.#allChecked = true;
this._var.allChecked = true;
label.innerText = r('allItem', '( All )');
}
} else if (options.selected != null) {
@ -214,23 +215,23 @@ export class Dropdown {
return wrapper;
}
get multiselect() { return this.#options.multiselect }
get multiselect() { return this._var.options.multiselect }
get disabled() { return this.#wrapper == null || this.#wrapper.querySelector('.ui-drop-header.disabled') != null }
get disabled() { return this._var.wrapper == null || this._var.wrapper.querySelector('.ui-drop-header.disabled') != null }
set disabled(flag) {
if (this.#wrapper == null) {
if (this._var.wrapper == null) {
return;
}
if (flag) {
this.#wrapper.querySelector('.ui-drop-header').classList.add('disabled');
this._var.wrapper.querySelector('.ui-drop-header').classList.add('disabled');
} else {
this.#wrapper.querySelector('.ui-drop-header').classList.remove('disabled');
this._var.wrapper.querySelector('.ui-drop-header').classList.remove('disabled');
}
}
get source() {
let source = this.#source;
let source = this._var.source;
if (source == null || !Array.isArray(source)) {
if (typeof this.sourceFilter === 'function') {
source = this.sourceFilter();
@ -238,7 +239,7 @@ export class Dropdown {
if (!Array.isArray(source)) {
source = [];
}
this.#source = source;
this._var.source = source;
}
return source;
}
@ -247,59 +248,59 @@ export class Dropdown {
if (!Array.isArray(list)) {
return;
}
this.#source = list;
if (this.#expanded) {
setTimeout(() => this.#dropdown(), 120);
this._var.source = list;
if (this._expanded) {
setTimeout(() => this._dropdown(), 120);
}
}
get selected() { return this.#selected }
get selected() { return this._var.selected }
get selectedlist() { return this.#selectedList || [] }
get selectedlist() { return this._var.selectedList || [] }
select(selected, silence) {
if (this.#lastSelected === selected) {
if (this._var.lastSelected === selected) {
return false;
}
this.#lastSelected = selected;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
this._var.lastSelected = selected;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
let item = this.source.find(it => it[valuekey] === selected);
if (this.#options.input) {
if (this._var.options.input) {
if (item == null) {
item = { [valuekey]: selected };
}
this.#label.value = selected;
this._var.label.value = selected;
} else {
const expanded = this.#expanded;
const expanded = this._expanded;
if (expanded) {
this.#container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected'));
this._var.container.querySelectorAll('li[data-value].selected').forEach(li => li.classList.remove('selected'));
}
if (item == null) {
this.#selected = null;
this.#label.innerText = ' ';
this._var.selected = null;
this._var.label.innerText = ' ';
return false;
}
const html = item[htmlkey];
if (html instanceof HTMLElement) {
this.#label.replaceChildren(html.cloneNode(true));
this._var.label.replaceChildren(html.cloneNode(true));
} else {
let text = item[textkey];
if (nullOrEmpty(text)) {
text = ' ';
}
this.#label.innerText = text;
this._var.label.innerText = text;
}
if (expanded) {
const val = selected.replace(/"/g, '\\"');
const li = this.#container.querySelector(`li[data-value="${val}"]`);
const li = this._var.container.querySelector(`li[data-value="${val}"]`);
if (li != null) {
li.classList.add('selected');
}
}
}
this.#selected = item;
this._var.selected = item;
if (!silence && typeof this.onselected === 'function') {
this.onselected(item);
}
@ -307,9 +308,9 @@ export class Dropdown {
selectlist(selectedlist, silence) {
const source = this.source;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
const itemlist = selectedlist.map(v => {
let item = source.find(it => it[valuekey] === v);
if (item == null) {
@ -318,22 +319,22 @@ export class Dropdown {
return item;
});
if (itemlist.length === 0) {
this.#selectedList = null;
this.#label.innerText = none;
this._var.selectedList = null;
this._var.label.innerText = none;
return false;
}
selectItems(this.#label, itemlist, htmlkey, textkey);
this.#selectedList = itemlist;
selectItems(this._var.label, itemlist, htmlkey, textkey);
this._var.selectedList = itemlist;
if (!silence && typeof this.onselectedlist === 'function') {
this.onselectedlist(itemlist);
}
}
get #expanded() { return this.#container?.classList?.contains('active') }
get _expanded() { return this._var.container?.classList?.contains('active') }
#dropdown(flag = true) {
const options = this.#options;
let panel = this.#container;
_dropdown(flag = true) {
const options = this._var.options;
let panel = this._var.container;
if (panel == null) {
panel = createElement('div', 'ui-drop-box');
// search box
@ -346,7 +347,7 @@ export class Dropdown {
input.addEventListener('input', e => {
const key = e.target.value.toLowerCase();
const source = filterSource(options.searchkeys, options.textkey, key, this.source);
this.#filllist(source);
this._filllist(source);
})
search.append(input, createIcon('fa-light', 'search'));
panel.appendChild(search);
@ -369,8 +370,12 @@ export class Dropdown {
});
}
panel.appendChild(list);
this.#container = panel;
this.#wrapper.appendChild(panel);
this._var.container = panel;
if (options.holder instanceof HTMLElement) {
options.holder.appendChild(panel);
} else {
this._var.wrapper.appendChild(panel);
}
}
if (flag) {
let source = this.source;
@ -380,11 +385,11 @@ export class Dropdown {
source = filterSource(options.searchkeys, options.textkey, search.value, source);
}
}
this.#filllist(source);
this._filllist(source);
// slide direction
if (!options.slidefixed) {
let parent = options.parent ?? document.body;
let p = this.#wrapper;
let p = this._var.wrapper;
let top = p.offsetTop;
while ((p = p.parentElement) != null && p !== parent) {
top -= p.scrollTop;
@ -401,11 +406,11 @@ export class Dropdown {
}
}
#filllist(source) {
const list = this.#container.querySelector('.ui-drop-list');
_filllist(source) {
const list = this._var.container.querySelector('.ui-drop-list');
list.replaceChildren();
const multiselect = this.multiselect;
const allchecked = this.#allChecked;
const allchecked = this._var.allChecked;
if (multiselect) {
list.appendChild(
createElement('li', null,
@ -413,15 +418,15 @@ export class Dropdown {
label: r('allItem', '( All )'),
checked: allchecked,
customAttributes: { 'isall': '1' },
onchange: e => this.#triggerselect(e.target)
onchange: e => this._triggerselect(e.target)
})
)
);
}
// TODO: virtual mode
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
const selected = this.selected;
const selectedlist = this.selectedlist;
let scrolled;
@ -448,7 +453,7 @@ export class Dropdown {
'class': 'dataitem',
'data-value': val
},
onchange: e => this.#triggerselect(e.target)
onchange: e => this._triggerselect(e.target)
});
li.appendChild(box);
} else {
@ -469,43 +474,43 @@ export class Dropdown {
}
}
#triggerselect(checkbox) {
_triggerselect(checkbox) {
let list;
const valuekey = this.#options.valuekey;
const textkey = this.#options.textkey;
const htmlkey = this.#options.htmlkey;
const valuekey = this._var.options.valuekey;
const textkey = this._var.options.textkey;
const htmlkey = this._var.options.htmlkey;
if (checkbox.getAttribute('isall') === '1') {
const allchecked = this.#allChecked = checkbox.checked;
const boxes = this.#container.querySelectorAll('input.dataitem');
const allchecked = this._var.allChecked = checkbox.checked;
const boxes = this._var.container.querySelectorAll('input.dataitem');
boxes.forEach(box => box.checked = allchecked);
list = [];
} else if (checkbox.checked) {
if (this.#container.querySelectorAll('input.dataitem:not(:checked)').length === 0) {
this.#allChecked = true;
this.#container.querySelector('input[isall="1"]').checked = true;
if (this._var.container.querySelectorAll('input.dataitem:not(:checked)').length === 0) {
this._var.allChecked = true;
this._var.container.querySelector('input[isall="1"]').checked = true;
list = [];
} else {
const source = this.source;
list = [...this.#container.querySelectorAll('input.dataitem:checked')]
list = [...this._var.container.querySelectorAll('input.dataitem:checked')]
.map(c => source.find(it => it[valuekey] === c.dataset.value))
.filter(it => it != null);
}
} else {
const val = checkbox.dataset.value;
if (this.#allChecked) {
this.#allChecked = false;
this.#container.querySelector('input[isall="1"]').checked = false;
if (this._var.allChecked) {
this._var.allChecked = false;
this._var.container.querySelector('input[isall="1"]').checked = false;
list = this.source.filter(it => it[valuekey] !== val);
} else {
list = this.selectedlist.filter(it => it[valuekey] !== val);
}
}
if (this.#allChecked) {
this.#label.innerText = r('allItem', '( All )');
if (this._var.allChecked) {
this._var.label.innerText = r('allItem', '( All )');
} else {
selectItems(this.#label, list, htmlkey, textkey);
selectItems(this._var.label, list, htmlkey, textkey);
}
this.#selectedList = list;
this._var.selectedList = list;
if (typeof this.onselectedlist === 'function') {
this.onselectedlist(itemlist);
}