sync
This commit is contained in:
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user