mirror of
https://github.com/kiddin9/op-packages.git
synced 2026-11-05 01:44:16 +08:00
🐤 Sync 2026-10-03 00:12:36
This commit is contained in:
1 parent
ce3df55b24
commit
cb1d978497
58 files changed
+1627
-1018
No files matched your search
@@ -8,6 +8,31 @@
|
||||
'require ui';
|
||||
'require validation';
|
||||
|
||||
function funcSed(originFunc, matrix = [], closure = []) {
|
||||
if (isEmpty(matrix))
|
||||
return originFunc;
|
||||
|
||||
const oriFuncStr = originFunc.toString();
|
||||
let funcStr = oriFuncStr;
|
||||
|
||||
for (const [regexp, replacer] of matrix) {
|
||||
if (!regexp)
|
||||
continue;
|
||||
if (regexp.test(funcStr))
|
||||
funcStr = funcStr.replace(regexp, replacer || '');
|
||||
else
|
||||
throw new Error(`funcSed: No matching items found: ${regexp}`);
|
||||
}
|
||||
|
||||
if (isEmpty(funcStr) || funcStr === oriFuncStr)
|
||||
return originFunc;
|
||||
|
||||
const funcArgs = funcStr.substring(funcStr.indexOf('(') +1, funcStr.indexOf(')')).split(',');
|
||||
const funcBody = funcStr.substring(funcStr.indexOf('{') +1, funcStr.lastIndexOf('}'));
|
||||
|
||||
return new Function(...closure, ...funcArgs, funcBody);
|
||||
}
|
||||
|
||||
/* Member */
|
||||
const rulesetdoc = [
|
||||
'data:text/html;base64,',
|
||||
@@ -511,25 +536,17 @@ const CBIDynamicList = form.DynamicList.extend({ // @less_25_12
|
||||
const CBIMultiValue = form.MultiValue.extend({ // @pr8758_merged
|
||||
__name__: 'CBI.MultiValue',
|
||||
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
const value = (cfgvalue != null) ? cfgvalue : this.default;
|
||||
const choices = this.transformChoices();
|
||||
renderWidget(/* ... */) {
|
||||
const sb = funcSed(
|
||||
form.MultiValue.prototype.renderWidget,
|
||||
[
|
||||
[new RegExp(/(widget\s*=\s*new)\s*ui\.Dropdown/), '$1 UIDropdown'],
|
||||
[new RegExp(/(multiple:\s*true)/), 'keep_order: this.keep_order, $1']
|
||||
],
|
||||
['UIDropdown']
|
||||
).call(this, UIDropdown, ...arguments);
|
||||
|
||||
const widget = new UIDropdown(L.toArray(value), choices, {
|
||||
id: this.cbid(section_id),
|
||||
sort: this.keylist,
|
||||
multiple: true,
|
||||
keep_order: this.keep_order,
|
||||
optional: this.optional || this.rmempty,
|
||||
select_placeholder: this.placeholder,
|
||||
create: this.create,
|
||||
display_items: this.display_size ?? this.size ?? 3,
|
||||
dropdown_items: this.dropdown_size ?? this.size ?? -1,
|
||||
validate: this.getValidator(section_id),
|
||||
disabled: (this.readonly != null) ? this.readonly : this.map.readonly
|
||||
});
|
||||
|
||||
return widget.render();
|
||||
return sb;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -827,354 +844,88 @@ const UIDynamicList = ui.DynamicList.extend({ // @less_25_12
|
||||
// keep selected order for multiple selection
|
||||
const UIDropdown = ui.Dropdown.extend({ // @pr8758_merged
|
||||
__init__(value, choices, options) {
|
||||
if (typeof(choices) != 'object')
|
||||
choices = {};
|
||||
|
||||
if (!Array.isArray(value))
|
||||
this.values = (value != null && value != '') ? [ value ] : [];
|
||||
else
|
||||
this.values = value;
|
||||
const result = ui.Dropdown.prototype.__init__.apply(this, arguments);
|
||||
|
||||
this.orderCounter = this.values.length;
|
||||
this.choices = choices;
|
||||
this.options = Object.assign({
|
||||
sort: true,
|
||||
multiple: Array.isArray(value),
|
||||
keep_order: false,
|
||||
optional: true,
|
||||
select_placeholder: _('-- Please choose --'),
|
||||
custom_placeholder: _('-- custom --'),
|
||||
display_items: 3,
|
||||
dropdown_items: -1,
|
||||
create: false,
|
||||
create_query: '.create-item-input',
|
||||
create_template: 'script[type="item-template"]'
|
||||
}, options);
|
||||
this.options.keep_order = options?.keep_order ?? false;
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
render() {
|
||||
const sb = E('div', {
|
||||
'id': this.options.id,
|
||||
'class': 'cbi-dropdown',
|
||||
'multiple': this.options.multiple ? '' : null,
|
||||
'keep_order': this.options.keep_order ? '' : null,
|
||||
'optional': this.options.optional ? '' : null,
|
||||
'disabled': this.options.disabled ? '' : null,
|
||||
'tabindex': -1
|
||||
}, E('ul'));
|
||||
|
||||
let keys = Object.keys(this.choices);
|
||||
|
||||
if (this.options.sort === true)
|
||||
keys.sort(L.naturalCompare);
|
||||
else if (Array.isArray(this.options.sort))
|
||||
keys = this.options.sort;
|
||||
|
||||
if (this.options.create)
|
||||
for (let i = 0; i < this.values.length; i++)
|
||||
if (!this.choices.hasOwnProperty(this.values[i]))
|
||||
keys.push(this.values[i]);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
let label = this.choices[keys[i]];
|
||||
|
||||
if (dom.elem(label))
|
||||
label = label.cloneNode(true);
|
||||
|
||||
sb.lastElementChild.appendChild(E('li', {
|
||||
'data-value': keys[i],
|
||||
'selected': (this.values.indexOf(keys[i]) > -1) ? '' : null
|
||||
}, [ label ?? keys[i] ]));
|
||||
}
|
||||
|
||||
if (this.options.create) {
|
||||
const createEl = E('input', {
|
||||
'type': 'text',
|
||||
'class': 'create-item-input',
|
||||
'readonly': this.options.readonly ? '' : null,
|
||||
'maxlength': this.options.maxlength,
|
||||
'placeholder': this.options.custom_placeholder ?? this.options.placeholder,
|
||||
'inputmode': 'text',
|
||||
'enterkeyhint': 'done'
|
||||
});
|
||||
|
||||
if (this.options.datatype || this.options.validate)
|
||||
UI.prototype.addValidator(createEl, this.options.datatype ?? 'string',
|
||||
true, this.options.validate, 'blur', 'keyup');
|
||||
|
||||
sb.lastElementChild.appendChild(E('li', { 'data-value': '-' }, createEl));
|
||||
}
|
||||
|
||||
if (this.options.create_markup)
|
||||
sb.appendChild(E('script', { type: 'item-template' },
|
||||
this.options.create_markup));
|
||||
|
||||
return this.bind(sb);
|
||||
},
|
||||
|
||||
bind(sb) {
|
||||
const o = this.options;
|
||||
|
||||
o.multiple = sb.hasAttribute('multiple');
|
||||
o.keep_order = sb.hasAttribute('keep_order');
|
||||
o.optional = sb.hasAttribute('optional');
|
||||
o.placeholder = sb.getAttribute('placeholder') ?? o.placeholder;
|
||||
o.display_items = parseInt(sb.getAttribute('display-items') ?? o.display_items);
|
||||
o.dropdown_items = parseInt(sb.getAttribute('dropdown-items') ?? o.dropdown_items);
|
||||
o.create_query = sb.getAttribute('item-create') ?? o.create_query;
|
||||
o.create_template = sb.getAttribute('item-template') ?? o.create_template;
|
||||
|
||||
const ul = sb.querySelector('ul');
|
||||
const more = sb.appendChild(E('span', { class: 'more', tabindex: -1 }, '···'));
|
||||
sb.appendChild(E('span', { class: 'open', tabindex: -1 }, '▾'));
|
||||
const canary = sb.appendChild(E('div'));
|
||||
const create = sb.querySelector(this.options.create_query);
|
||||
let ndisplay = this.options.display_items;
|
||||
let n = 0;
|
||||
|
||||
if (this.options.multiple) {
|
||||
let items = ul.querySelectorAll('li');
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
this.transformItem(sb, items[i]);
|
||||
|
||||
if (items[i].hasAttribute('selected')) {
|
||||
if (ndisplay-- > 0)
|
||||
items[i].setAttribute('display', n++);
|
||||
|
||||
if (this.options.keep_order) {
|
||||
const value = items[i].getAttribute('data-value');
|
||||
if (this.values.includes(value))
|
||||
items[i].setAttribute('data-order', this.values.indexOf(value) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (this.options.optional && !ul.querySelector('li[data-value=""]')) {
|
||||
const placeholder = E('li', { placeholder: '' },
|
||||
this.options.select_placeholder ?? this.options.placeholder);
|
||||
|
||||
ul.firstChild
|
||||
? ul.insertBefore(placeholder, ul.firstChild)
|
||||
: ul.appendChild(placeholder);
|
||||
}
|
||||
|
||||
let items = ul.querySelectorAll('li');
|
||||
const sel = sb.querySelectorAll('[selected]');
|
||||
|
||||
sel.forEach(s => {
|
||||
s.removeAttribute('selected');
|
||||
});
|
||||
|
||||
const s = sel[0] ?? items[0];
|
||||
if (s) {
|
||||
s.setAttribute('selected', '');
|
||||
s.setAttribute('display', n++);
|
||||
}
|
||||
|
||||
ndisplay--;
|
||||
}
|
||||
|
||||
this.saveValues(sb, ul);
|
||||
|
||||
ul.setAttribute('tabindex', -1);
|
||||
sb.setAttribute('tabindex', 0);
|
||||
|
||||
if (ndisplay < 0)
|
||||
sb.setAttribute('more', '')
|
||||
else
|
||||
sb.removeAttribute('more');
|
||||
|
||||
if (ndisplay == this.options.display_items)
|
||||
sb.setAttribute('empty', '')
|
||||
else
|
||||
sb.removeAttribute('empty');
|
||||
|
||||
dom.content(more, (ndisplay == this.options.display_items)
|
||||
? (this.options.select_placeholder ?? this.options.placeholder) : '···');
|
||||
|
||||
|
||||
sb.addEventListener('click', this.handleClick.bind(this));
|
||||
sb.addEventListener('keydown', this.handleKeydown.bind(this));
|
||||
sb.addEventListener('cbi-dropdown-close', this.handleDropdownClose.bind(this));
|
||||
sb.addEventListener('cbi-dropdown-select', this.handleDropdownSelect.bind(this));
|
||||
|
||||
if ('ontouchstart' in window) {
|
||||
sb.addEventListener('touchstart', ev => ev.stopPropagation());
|
||||
window.addEventListener('touchstart', this.closeAllDropdowns);
|
||||
}
|
||||
else {
|
||||
sb.addEventListener('focus', this.handleFocus.bind(this));
|
||||
|
||||
canary.addEventListener('focus', this.handleCanaryFocus.bind(this));
|
||||
|
||||
window.addEventListener('click', this.closeAllDropdowns);
|
||||
}
|
||||
|
||||
if (create) {
|
||||
create.addEventListener('keydown', this.handleCreateKeydown.bind(this));
|
||||
create.addEventListener('focus', this.handleCreateFocus.bind(this));
|
||||
create.addEventListener('blur', this.handleCreateBlur.bind(this));
|
||||
|
||||
const li = findParent(create, 'li');
|
||||
|
||||
li.setAttribute('unselectable', '');
|
||||
li.addEventListener('click', this.handleCreateClick.bind(this));
|
||||
}
|
||||
|
||||
this.node = sb;
|
||||
|
||||
this.setUpdateEvents(sb, 'cbi-dropdown-open', 'cbi-dropdown-close');
|
||||
this.setChangeEvents(sb, 'cbi-dropdown-change', 'cbi-dropdown-close');
|
||||
|
||||
dom.bindClassInstance(sb, this);
|
||||
render(/* ... */) {
|
||||
const sb = funcSed(
|
||||
ui.Dropdown.prototype.render,
|
||||
[
|
||||
[new RegExp(/('multiple':[^\n]*this\.options\.multiple)/), `'keep_order': this.options.keep_order ? '' : null, $1`]
|
||||
],
|
||||
['dom']
|
||||
).call(this, dom, ...arguments);
|
||||
|
||||
return sb;
|
||||
},
|
||||
|
||||
toggleItem(sb, li, force_state) {
|
||||
const ul = li.parentNode;
|
||||
bind(/* ... */) {
|
||||
const sb = funcSed(
|
||||
ui.Dropdown.prototype.bind,
|
||||
[
|
||||
[new RegExp(/(o\.multiple\s*=)/), `o.keep_order = sb.hasAttribute('keep_order'); $1`],
|
||||
[
|
||||
new RegExp(/(if\s*\(items\[i\]\.hasAttribute\('selected'\)\s*&&\s*ndisplay--\s*>\s*0\)[\n\s]*items\[i\]\.setAttribute\('display',\s*n\+\+\);)/),
|
||||
`$1
|
||||
if (this.options.keep_order) {
|
||||
const value = items[i].getAttribute('data-value');
|
||||
if (items[i].hasAttribute('selected') && this.values.includes(value))
|
||||
items[i].setAttribute('data-order', this.values.indexOf(value));
|
||||
}`
|
||||
]
|
||||
],
|
||||
['dom']
|
||||
).call(this, dom, ...arguments);
|
||||
|
||||
if (li.hasAttribute('unselectable'))
|
||||
return;
|
||||
|
||||
if (this.options.multiple) {
|
||||
const cbox = li.querySelector('input[type="checkbox"]');
|
||||
const items = li.parentNode.querySelectorAll('li');
|
||||
const label = sb.querySelector('ul.preview');
|
||||
let sel = li.parentNode.querySelectorAll('[selected]').length;
|
||||
const more = sb.querySelector('.more');
|
||||
let ndisplay = this.options.display_items;
|
||||
let n = 0;
|
||||
|
||||
if (li.hasAttribute('selected')) {
|
||||
if (force_state !== true) {
|
||||
if (sel > 1 || this.options.optional) {
|
||||
li.removeAttribute('selected');
|
||||
cbox.checked = cbox.disabled = false;
|
||||
sel--;
|
||||
|
||||
if (this.options.keep_order)
|
||||
li.removeAttribute('data-order');
|
||||
}
|
||||
else {
|
||||
cbox.disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (force_state !== false) {
|
||||
li.setAttribute('selected', '');
|
||||
cbox.checked = true;
|
||||
cbox.disabled = false;
|
||||
sel++;
|
||||
|
||||
if (this.options.keep_order)
|
||||
li.setAttribute('data-order', ++this.orderCounter);
|
||||
}
|
||||
}
|
||||
|
||||
while (label && label.firstElementChild)
|
||||
label.removeChild(label.firstElementChild);
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
items[i].removeAttribute('display');
|
||||
if (items[i].hasAttribute('selected')) {
|
||||
if (ndisplay-- > 0) {
|
||||
items[i].setAttribute('display', n++);
|
||||
if (label)
|
||||
label.appendChild(items[i].cloneNode(true));
|
||||
}
|
||||
const c = items[i].querySelector('input[type="checkbox"]');
|
||||
if (c)
|
||||
c.disabled = (sel == 1 && !this.options.optional);
|
||||
}
|
||||
}
|
||||
|
||||
if (ndisplay < 0)
|
||||
sb.setAttribute('more', '');
|
||||
else
|
||||
sb.removeAttribute('more');
|
||||
|
||||
if (ndisplay === this.options.display_items)
|
||||
sb.setAttribute('empty', '');
|
||||
else
|
||||
sb.removeAttribute('empty');
|
||||
|
||||
dom.content(more, (ndisplay === this.options.display_items)
|
||||
? (this.options.select_placeholder ?? this.options.placeholder) : '···');
|
||||
}
|
||||
else {
|
||||
let sel = li.parentNode.querySelector('[selected]');
|
||||
if (sel) {
|
||||
sel.removeAttribute('display');
|
||||
sel.removeAttribute('selected');
|
||||
}
|
||||
|
||||
li.setAttribute('display', 0);
|
||||
li.setAttribute('selected', '');
|
||||
|
||||
this.closeDropdown(sb);
|
||||
}
|
||||
|
||||
this.saveValues(sb, ul);
|
||||
return sb;
|
||||
},
|
||||
|
||||
saveValues(sb, ul) {
|
||||
const sel = Array.from(ul.querySelectorAll('li[selected]'));
|
||||
const div = sb.lastElementChild;
|
||||
const name = this.options.name;
|
||||
let strval = '';
|
||||
const values = [];
|
||||
toggleItem(/* ... */) {
|
||||
const result = funcSed(
|
||||
ui.Dropdown.prototype.toggleItem,
|
||||
[
|
||||
[
|
||||
new RegExp(/(li\.removeAttribute\('selected'\))/),
|
||||
`if (this.options.keep_order)
|
||||
li.removeAttribute('data-order');
|
||||
$1`
|
||||
],
|
||||
[
|
||||
new RegExp(/(li\.setAttribute\('selected',\s*''\))/),
|
||||
`if (this.options.keep_order)
|
||||
li.setAttribute('data-order', this.orderCounter++);
|
||||
$1`
|
||||
]
|
||||
],
|
||||
['dom']
|
||||
).call(this, dom, ...arguments);
|
||||
|
||||
if (this.options.keep_order) {
|
||||
sel.sort((a, b) =>
|
||||
(+a.getAttribute('data-order') || 0) -
|
||||
(+b.getAttribute('data-order') || 0)
|
||||
);
|
||||
}
|
||||
return result;
|
||||
},
|
||||
|
||||
while (div.lastElementChild)
|
||||
div.removeChild(div.lastElementChild);
|
||||
saveValues(/* ... */) {
|
||||
const result = funcSed(
|
||||
ui.Dropdown.prototype.saveValues,
|
||||
[
|
||||
[
|
||||
new RegExp(/(const sel\s*=)([^;\n]+);/),
|
||||
`$1 Array.from($2);
|
||||
if (this.options.keep_order) {
|
||||
sel.sort((a, b) =>
|
||||
(+a.getAttribute('data-order') || 0) -
|
||||
(+b.getAttribute('data-order') || 0)
|
||||
);
|
||||
}`
|
||||
]
|
||||
]
|
||||
).call(this, ...arguments);
|
||||
|
||||
sel.forEach(s => {
|
||||
if (s.hasAttribute('placeholder'))
|
||||
return;
|
||||
|
||||
const v = {
|
||||
text: s.innerText,
|
||||
value: s.hasAttribute('data-value') ? s.getAttribute('data-value') : s.innerText,
|
||||
element: s
|
||||
};
|
||||
|
||||
div.appendChild(E('input', {
|
||||
type: 'hidden',
|
||||
name: name,
|
||||
value: v.value
|
||||
}));
|
||||
|
||||
values.push(v);
|
||||
|
||||
strval += strval.length ? ` ${v.value}` : v.value;
|
||||
});
|
||||
|
||||
const detail = {
|
||||
instance: this,
|
||||
element: sb
|
||||
};
|
||||
|
||||
if (this.options.multiple)
|
||||
detail.values = values;
|
||||
else
|
||||
detail.value = values.length ? values[0] : null;
|
||||
|
||||
sb.value = strval;
|
||||
|
||||
sb.dispatchEvent(new CustomEvent('cbi-dropdown-change', {
|
||||
bubbles: true,
|
||||
detail: detail
|
||||
}));
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user