add dropdown component
This commit is contained in:
@ -1,6 +1,7 @@
|
||||
import { createIcon, resolveIcon } from "./ui/icon";
|
||||
import { createCheckbox, resolveCheckbox } from "./ui/checkbox";
|
||||
import { setTooltip, resolveTooltip } from "./ui/tooltip";
|
||||
import Dropdown from "./ui/dropdown";
|
||||
|
||||
export {
|
||||
// icon
|
||||
@ -11,5 +12,7 @@ export {
|
||||
resolveCheckbox,
|
||||
// tooltip
|
||||
setTooltip,
|
||||
resolveTooltip
|
||||
resolveTooltip,
|
||||
// dropdown
|
||||
Dropdown
|
||||
}
|
||||
|
3
lib/ui/checkbox.d.ts
vendored
3
lib/ui/checkbox.d.ts
vendored
@ -1,11 +1,12 @@
|
||||
interface CheckboxOptions {
|
||||
type?: string;
|
||||
label?: string;
|
||||
label?: string | HTMLElement;
|
||||
checked?: boolean;
|
||||
isImage?: boolean;
|
||||
imageHeight?: Number;
|
||||
checkedNode?: HTMLElement;
|
||||
uncheckedNode?: HTMLElement;
|
||||
customerAttributes?: { [key: string]: string };
|
||||
onchange?: (this: HTMLInputElement, ev: Event) => any;
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
imageHeight?: Number;
|
||||
checkedNode?: HTMLElement;
|
||||
uncheckedNode?: HTMLElement;
|
||||
customerAttributes?: { [key: string]: string };
|
||||
onchange?: (this: HTMLInputElement, ev: Event) => any;
|
||||
}</pre>
|
||||
</p>
|
||||
@ -25,9 +26,9 @@
|
||||
<p>
|
||||
复选框图标的样式,可选值目前有 <code>fa-regular</code>、<code>fa-light</code>、<code>fa-solid</code>
|
||||
</p>
|
||||
<h3>label?: string</h3>
|
||||
<h3>label?: string | HTMLElement</h3>
|
||||
<p>
|
||||
复选框的标签文本
|
||||
复选框的标签文本,或者想要呈现的元素
|
||||
</p>
|
||||
<h3>checked?: boolean</h3>
|
||||
<p>
|
||||
@ -49,6 +50,14 @@
|
||||
<p>
|
||||
为图片复选框时的未选中时显示的元素
|
||||
</p>
|
||||
<h3>customerAttributes?: { [key: string]: string }</h3>
|
||||
<p>
|
||||
自定义属性,例如
|
||||
<pre>{
|
||||
'data-id': 'xxxxxx',
|
||||
'disabled': ''
|
||||
}</pre>
|
||||
</p>
|
||||
<h3>onchange?: (this: HTMLInputElement, ev: Event) => any</h3>
|
||||
<p>
|
||||
复选框改变时触发的事件
|
||||
|
@ -5,7 +5,9 @@ function fillCheckbox(container, type, label) {
|
||||
layer.className = 'check-box-inner';
|
||||
layer.appendChild(createIcon(type, 'check'));
|
||||
container.appendChild(layer);
|
||||
if (label != null && label.length > 0) {
|
||||
if (label instanceof HTMLElement) {
|
||||
container.appendChild(label);
|
||||
} else if (label != null && label.length > 0) {
|
||||
const span = document.createElement('span');
|
||||
span.innerText = label;
|
||||
container.appendChild(span);
|
||||
@ -21,6 +23,11 @@ function createCheckbox(opts) {
|
||||
if (opts.checked === true) {
|
||||
input.checked = true;
|
||||
}
|
||||
if (opts.customerAttributes != null) {
|
||||
for (let entry of Object.entries(opts.customerAttributes)) {
|
||||
input.setAttribute(entry[0], entry[1]);
|
||||
}
|
||||
}
|
||||
if (typeof opts.onchange === 'function') {
|
||||
input.addEventListener('change', opts.onchange);
|
||||
}
|
||||
@ -50,9 +57,7 @@ function createCheckbox(opts) {
|
||||
}
|
||||
|
||||
function resolveCheckbox(container, legacy) {
|
||||
if (container == null) {
|
||||
container = document.body;
|
||||
}
|
||||
container ??= document.body;
|
||||
if (legacy) {
|
||||
const checks = container.querySelectorAll('input[type="checkbox"]');
|
||||
for (let chk of checks) {
|
||||
|
32
lib/ui/dropdown.d.ts
vendored
Normal file
32
lib/ui/dropdown.d.ts
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
interface DropdownOptions {
|
||||
textkey?: string;
|
||||
valuekey?: string;
|
||||
htmlkey?: string;
|
||||
maxlength?: Number;
|
||||
multiselect?: boolean;
|
||||
selected?: any;
|
||||
selectedlist?: any[];
|
||||
disabled?: boolean;
|
||||
input?: boolean;
|
||||
search?: boolean;
|
||||
searchkeys?: string[];
|
||||
searchplaceholder?: string;
|
||||
tabindex?: Number;
|
||||
slidefixed?: boolean;
|
||||
parent?: HTMLElement
|
||||
}
|
||||
|
||||
interface Dropdown {
|
||||
create(): HTMLElement;
|
||||
get disabled(): boolean;
|
||||
set disabled(flag: boolean);
|
||||
readonly multiselect: boolean;
|
||||
readonly selected: any;
|
||||
}
|
||||
|
||||
declare var Dropdown: {
|
||||
prototype: Dropdown;
|
||||
new(options: DropdownOptions): Dropdown;
|
||||
};
|
||||
|
||||
export default Dropdown;
|
466
lib/ui/dropdown.js
Normal file
466
lib/ui/dropdown.js
Normal file
@ -0,0 +1,466 @@
|
||||
// import { r, global, contains, isPositive, nullOrEmpty } from "../utility";
|
||||
import { r } from "../utility/lgres";
|
||||
import { contains, nullOrEmpty } from "../utility/strings";
|
||||
import { global, isPositive } from "../utility";
|
||||
import { createCheckbox } from "./checkbox";
|
||||
import { createIcon } from "./icon"
|
||||
|
||||
const SymbolDropdown = Symbol.for('ui-dropdown');
|
||||
const DropdownTitleHeight = 26;
|
||||
const DropdownItemHeight = 30;
|
||||
|
||||
let dropdownGlobal = global[SymbolDropdown];
|
||||
|
||||
if (dropdownGlobal == null) {
|
||||
// init
|
||||
dropdownGlobal = {};
|
||||
Object.defineProperty(dropdownGlobal, 'clear', {
|
||||
writable: false,
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: function () {
|
||||
const panel = document.querySelector('.dropdown-wrapper .dropdown-panel.active');
|
||||
if (panel == null) {
|
||||
return;
|
||||
}
|
||||
panel.classList.remove('active');
|
||||
const dropId = panel.parentElement.dataset.dropId;
|
||||
if (dropId == null) {
|
||||
return;
|
||||
}
|
||||
const dropdown = this[dropId];
|
||||
if (dropdown != null && dropdown.multiselect && typeof dropdown.oncollapsed === 'function') {
|
||||
dropdown.oncollapsed();
|
||||
}
|
||||
}
|
||||
})
|
||||
global[SymbolDropdown] = dropdownGlobal;
|
||||
|
||||
document.addEventListener('mousedown', e => {
|
||||
let parent = e.target;
|
||||
while (parent != null) {
|
||||
if (parent.classList.contains('dropdown-panel')) {
|
||||
e.stopPropagation();
|
||||
return;
|
||||
}
|
||||
parent = parent.parentElement;
|
||||
}
|
||||
dropdownGlobal.clear();
|
||||
});
|
||||
}
|
||||
|
||||
class Dropdown {
|
||||
#options;
|
||||
|
||||
#wrapper;
|
||||
#container;
|
||||
#label;
|
||||
|
||||
#allChecked;
|
||||
#source;
|
||||
#lastSelected;
|
||||
#selected;
|
||||
#selectedList;
|
||||
|
||||
sourceFilter;
|
||||
onselectedlist;
|
||||
onselected;
|
||||
onexpanded;
|
||||
|
||||
constructor(options) {
|
||||
options ??= {};
|
||||
options.searchplaceholder ??= r('searchHolder', 'Search...');
|
||||
options.textkey ??= 'text';
|
||||
options.valuekey ??= 'value';
|
||||
options.htmlkey ??= 'html';
|
||||
options.maxlength ??= 500;
|
||||
this.#options = options;
|
||||
}
|
||||
|
||||
create() {
|
||||
const options = this.#options;
|
||||
|
||||
// wrapper
|
||||
const wrapper = document.createElement('div');
|
||||
const dropId = String(Math.random()).substring(2);
|
||||
wrapper.dataset.dropId = dropId;
|
||||
wrapper.className = 'dropdown-wrapper';
|
||||
dropdownGlobal[dropId] = this;
|
||||
this.#wrapper = wrapper;
|
||||
|
||||
// header
|
||||
const header = document.createElement('div');
|
||||
header.className = 'dropdown-header';
|
||||
header.addEventListener('click', () => {
|
||||
if (this.disabled) {
|
||||
return;
|
||||
}
|
||||
const active = this.#expanded;
|
||||
if (active && this.#label.hasFocus()) {
|
||||
return;
|
||||
}
|
||||
this.#dropdown(!active);
|
||||
if (!active && typeof this.onexpanded === 'function') {
|
||||
setTimeout(() => this.onexpanded(), 120);
|
||||
}
|
||||
});
|
||||
|
||||
// label or input
|
||||
let label;
|
||||
let searchkeys = options.searchkeys;
|
||||
if (!Array.isArray(searchkeys) || searchkeys.length === 0) {
|
||||
searchkeys = [options.textkey];
|
||||
}
|
||||
if (options.input) {
|
||||
label = document.createElement('input');
|
||||
label.className = 'dropdown-text';
|
||||
label.setAttribute('type', 'text');
|
||||
isPositive(options.maxlength) && label.setAttribute('maxlength', options.maxlength);
|
||||
isPositive(options.tabindex) && label.setAttribute('tabindex', options.tabindex);
|
||||
label.addEventListener('input', e => {
|
||||
const key = e.target.value.toLowerCase();
|
||||
let source = this.source;
|
||||
if (key.length > 0) {
|
||||
source = source.filter(it => {
|
||||
for (let k of searchkeys) {
|
||||
if (contains(it[k].toLowerCase(), key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
this.#filllist(source);
|
||||
this.#container.classList.add('active');
|
||||
});
|
||||
label.addEventListener('blur', e => this.select(e.target.value));
|
||||
label.addEventListener('mousedown', e => this.#expanded && e.stopPropagation());
|
||||
} else {
|
||||
isPositive(options.tabindex) && header.setAttribute('tabindex', options.tabindex);
|
||||
label = document.createElement('label');
|
||||
label.className = 'dropdown-text';
|
||||
}
|
||||
this.#label = label;
|
||||
if (options.multiselect) {
|
||||
if (Array.isArray(options.selectedlist)) {
|
||||
this.selectlist(options.selectedlist, true);
|
||||
} else {
|
||||
this.#allChecked = true;
|
||||
label.innerText = r('allItem', '( All )');
|
||||
}
|
||||
} else if (options.selected != null) {
|
||||
this.select(options.selected, true);
|
||||
}
|
||||
header.appendChild(label);
|
||||
const caret = document.createElement('label');
|
||||
caret.className = 'dropdown-caret';
|
||||
header.appendChild(caret);
|
||||
wrapper.appendChild(header);
|
||||
|
||||
this.disabled = options.disabled || false;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
get multiselect() { return this.#options.multiselect }
|
||||
|
||||
get disabled() { return this.#wrapper == null || this.#wrapper.querySelector('.dropdown-header.disabled') != null }
|
||||
|
||||
set disabled(flag) {
|
||||
if (this.#wrapper == null) {
|
||||
return;
|
||||
}
|
||||
if (flag) {
|
||||
this.#wrapper.querySelector('.dropdown-header').classList.add('disabled');
|
||||
} else {
|
||||
this.#wrapper.querySelector('.dropdown-header').classList.remove('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
get source() {
|
||||
let source = this.#source;
|
||||
if (source == null || !Array.isArray(source)) {
|
||||
if (typeof this.sourceFilter === 'function') {
|
||||
source = this.sourceFilter();
|
||||
}
|
||||
if (!Array.isArray(source)) {
|
||||
source = [];
|
||||
}
|
||||
this.#source = source;
|
||||
}
|
||||
return source;
|
||||
}
|
||||
|
||||
set source(list) {
|
||||
if (!Array.isArray(list)) {
|
||||
return;
|
||||
}
|
||||
this.#source = list;
|
||||
if (this.#expanded) {
|
||||
setTimeout(() => this.#dropdown(), 120);
|
||||
}
|
||||
}
|
||||
|
||||
get selected() { return this.#selected }
|
||||
|
||||
get selectedlist() { return this.#selectedList || [] }
|
||||
|
||||
select(selected, init) {
|
||||
if (this.#lastSelected === selected) {
|
||||
return false;
|
||||
}
|
||||
this.#lastSelected = selected;
|
||||
const valuekey = this.#options.valuekey;
|
||||
const textkey = this.#options.textkey;
|
||||
let item = this.source.find(it => it[valuekey] === selected);
|
||||
if (this.#options.input) {
|
||||
if (item == null) {
|
||||
item = {};
|
||||
item[valuekey] = selected;
|
||||
}
|
||||
this.#label.value = selected;
|
||||
} else {
|
||||
if (item == null) {
|
||||
this.#selected = null;
|
||||
this.#label.innerText = ' ';
|
||||
return false;
|
||||
}
|
||||
let text = item[textkey];
|
||||
if (nullOrEmpty(text)) {
|
||||
text = ' ';
|
||||
}
|
||||
this.#label.innerText = text;
|
||||
}
|
||||
this.#selected = item;
|
||||
if (!init && typeof this.onselected === 'function') {
|
||||
this.onselected(item);
|
||||
}
|
||||
}
|
||||
|
||||
selectlist(selectedlist, init) {
|
||||
const source = this.source;
|
||||
const valuekey = this.#options.valuekey;
|
||||
const textkey = this.#options.textkey;
|
||||
const itemlist = selectedlist.map(v => {
|
||||
let item = source.find(it => it[valuekey] === v);
|
||||
if (item == null) {
|
||||
item = {};
|
||||
item[valuekey] = v;
|
||||
item[textkey] = v;
|
||||
}
|
||||
return item;
|
||||
});
|
||||
const none = r('noneItem', '( None )');
|
||||
if (itemlist.length === 0) {
|
||||
this.#selectedList = null;
|
||||
this.#label.innerText = none;
|
||||
return false;
|
||||
}
|
||||
const text = itemlist.map(it => it[textkey]).join(', ');
|
||||
if (nullOrEmpty(text)) {
|
||||
text = none;
|
||||
}
|
||||
this.#selectedList = itemlist;
|
||||
this.#label.innerText = text;
|
||||
if (!init && typeof this.onselectedlist === 'function') {
|
||||
this.onselectedlist(itemlist);
|
||||
}
|
||||
}
|
||||
|
||||
get #expanded() { return this.#container != null && this.#container.style.visibility === 'visible' }
|
||||
|
||||
#dropdown(flag) {
|
||||
flag ??= true;
|
||||
const options = this.#options;
|
||||
const textkey = options.textkey;
|
||||
let panel = this.#container;
|
||||
if (panel == null) {
|
||||
panel = document.createElement('div');
|
||||
panel.className = 'dropdown-panel';
|
||||
// search box
|
||||
if (options.search) {
|
||||
let searchkeys = options.searchkeys;
|
||||
if (!Array.isArray(searchkeys) || searchkeys.length === 0) {
|
||||
searchkeys = [textkey];
|
||||
}
|
||||
const search = document.createElement('div');
|
||||
search.className = 'dropdown-search';
|
||||
const input = document.createElement('input');
|
||||
input.setAttribute('type', 'text');
|
||||
isPositive(options.tabindex) && input.setAttribute('tabindex', options.tabindex);
|
||||
!nullOrEmpty(options.searchplaceholder) && input.setAttribute('placeholder', options.searchplaceholder);
|
||||
input.addEventListener('input', e => {
|
||||
const key = e.target.value.toLowerCase();
|
||||
let source = this.source;
|
||||
if (key.length > 0) {
|
||||
source = source.filter(it => {
|
||||
for (let k of searchkeys) {
|
||||
if (contains(it[k].toLowerCase(), key)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
this.#filllist(source);
|
||||
})
|
||||
search.appendChild(input);
|
||||
search.appendChild(createIcon('fa-light', 'search'));
|
||||
panel.appendChild(search);
|
||||
}
|
||||
// list
|
||||
const list = document.createElement('ul');
|
||||
list.className = 'dropdown-list';
|
||||
if (!this.multiselect) {
|
||||
list.addEventListener('click', e => {
|
||||
let li = e.target;
|
||||
while (li.tagName !== 'LI') {
|
||||
li = li.parentElement;
|
||||
if (li == null) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
const value = li.dataset.value;
|
||||
if (this.select(value) !== false) {
|
||||
dropdownGlobal.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
panel.appendChild(list);
|
||||
this.#container = panel;
|
||||
this.#wrapper.appendChild(panel);
|
||||
this.#filllist(this.source);
|
||||
}
|
||||
if (flag) {
|
||||
if (!options.slidefixed) {
|
||||
let parent = options.parent ?? document.body;
|
||||
const height = panel.offsetHeight;
|
||||
if (this.#wrapper.offsetTop - parent.offsetTop + DropdownTitleHeight + height >= parent.offsetHeight) {
|
||||
panel.style.marginTop = -height - DropdownTitleHeight - 2;
|
||||
panel.classList.add('slide-up');
|
||||
} else {
|
||||
panel.style.marginTop = null;
|
||||
panel.classList.remove('slide-up');
|
||||
}
|
||||
}
|
||||
panel.classList.add('active');
|
||||
// search input
|
||||
// const inputSearch = panel.querySelector('.dropdown-search > input');
|
||||
// if (!nullOrEmpty(inputSearch.value)) {
|
||||
// const event = new InputEvent('type');
|
||||
// inputSearch.dispatchEvent(event);
|
||||
// }
|
||||
} else {
|
||||
panel.classList.remove('active');
|
||||
}
|
||||
}
|
||||
|
||||
#filllist(source) {
|
||||
const list = this.#container.querySelector('.dropdown-list');
|
||||
list.replaceChildren();
|
||||
const multiselect = this.multiselect;
|
||||
const allchecked = this.#allChecked;
|
||||
if (multiselect) {
|
||||
const liall = document.createElement('li');
|
||||
const boxall = createCheckbox({
|
||||
label: r('allItem', '( All )'),
|
||||
checked: allchecked,
|
||||
customerAttributes: { 'isall': '1' },
|
||||
onchange: e => this.#triggerselect(e.target)
|
||||
});
|
||||
liall.appendChild(boxall);
|
||||
list.appendChild(liall);
|
||||
}
|
||||
// TODO: virtual mode
|
||||
const valuekey = this.#options.valuekey;
|
||||
const textkey = this.#options.textkey;
|
||||
const htmlkey = this.#options.htmlkey;
|
||||
const selected = this.selected;
|
||||
const selectedlist = this.selectedlist;
|
||||
let scrolled;
|
||||
source.slice(0, 200).forEach((item, i) => {
|
||||
const val = item[valuekey];
|
||||
const li = document.createElement('li');
|
||||
li.dataset.value = val;
|
||||
li.setAttribute('title', item[textkey]);
|
||||
let label;
|
||||
const html = item[htmlkey];
|
||||
if (html instanceof HTMLElement) {
|
||||
label = html;
|
||||
}
|
||||
if (multiselect) {
|
||||
const selected = selectedlist.some(s => s[valuekey] === val);
|
||||
if (label == null) {
|
||||
label = document.createElement('span');
|
||||
label.innerText = item[textkey];
|
||||
}
|
||||
const box = createCheckbox({
|
||||
label,
|
||||
checked: allchecked || selected,
|
||||
customerAttributes: {
|
||||
'class': 'dataitem',
|
||||
'data-value': val
|
||||
},
|
||||
onchange: e => this.#triggerselect(e.target)
|
||||
});
|
||||
li.appendChild(box);
|
||||
} else {
|
||||
if (label == null) {
|
||||
li.innerText = item[textkey];
|
||||
} else {
|
||||
li.appendChild(label);
|
||||
}
|
||||
if (selected != null && selected[valuekey] === val) {
|
||||
scrolled = DropdownItemHeight * i;
|
||||
li.classList.add('selected');
|
||||
}
|
||||
}
|
||||
list.appendChild(li);
|
||||
});
|
||||
if (scrolled != null) {
|
||||
setTimeout(() => list.scrollTop = scrolled, 10);
|
||||
}
|
||||
}
|
||||
|
||||
#triggerselect(checkbox) {
|
||||
let list;
|
||||
const valuekey = this.#options.valuekey;
|
||||
const textkey = this.#options.textkey;
|
||||
if (checkbox.getAttribute('isall') === '1') {
|
||||
const allchecked = this.#allChecked = checkbox.checked;
|
||||
const boxes = this.#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;
|
||||
list = [];
|
||||
} else {
|
||||
const source = this.source;
|
||||
list = [...this.#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;
|
||||
list = this.source.filter(it => it[valuekey] !== val);
|
||||
} else {
|
||||
list = this.selectedlist.filter(it => it[valuekey] !== val);
|
||||
}
|
||||
}
|
||||
let text = this.#allChecked ? r('allItem', '( All )') : list.map(it => it[textkey]).join(', ');
|
||||
if (nullOrEmpty(text)) {
|
||||
text = r('noneItem', '( None )');
|
||||
}
|
||||
this.#selectedList = list;
|
||||
this.#label.innerText = text;
|
||||
if (typeof this.onselectedlist === 'function') {
|
||||
this.onselectedlist(itemlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Dropdown;
|
@ -49,9 +49,7 @@ function setTooltip(container, content) {
|
||||
}
|
||||
|
||||
function resolveTooltip(container) {
|
||||
if (container == null) {
|
||||
container = document.body;
|
||||
}
|
||||
container ??= document.body;
|
||||
const tips = container.querySelectorAll('[title]');
|
||||
for (let tip of tips) {
|
||||
const title = tip.getAttribute('title');
|
||||
|
@ -3,6 +3,12 @@ import { init, r, lang } from "./utility/lgres";
|
||||
import { get, post, upload } from "./utility/request";
|
||||
import { nullOrEmpty, contains, endsWith, padStart } from "./utility/strings";
|
||||
|
||||
let g = typeof globalThis !== 'undefined' ? globalThis : self;
|
||||
|
||||
function isPositive(n) {
|
||||
return !isNaN(n) && n > 0;
|
||||
}
|
||||
|
||||
export {
|
||||
// cookie
|
||||
getCookie,
|
||||
@ -20,5 +26,8 @@ export {
|
||||
nullOrEmpty,
|
||||
contains,
|
||||
endsWith,
|
||||
padStart
|
||||
padStart,
|
||||
// variables
|
||||
g as global,
|
||||
isPositive
|
||||
}
|
@ -65,7 +65,7 @@
|
||||
|
||||
lgres.init(document.body, {
|
||||
template: '/res.json',
|
||||
callback: (res) => document.title = res.r('title', 'Default Title')
|
||||
callback: res => document.title = res.r('title', 'Default Title')
|
||||
}).then(res => {
|
||||
document.querySelector('#header').innerText = res.r('header', 'My Header');
|
||||
const msg = lgres.lang.unknownError;
|
||||
|
@ -66,6 +66,7 @@ async function refreshLgres(template, lgres) {
|
||||
Object.defineProperty(lgres, 'r', {
|
||||
writable: false,
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
value: function (key, defaultValue) {
|
||||
return getLanguage(this, key, defaultValue);
|
||||
}
|
||||
@ -76,16 +77,11 @@ async function refreshLgres(template, lgres) {
|
||||
|
||||
function getLanguage(lgres, key, defaultValue) {
|
||||
let value = lgres[key];
|
||||
if (value == null) {
|
||||
value = defaultValue;
|
||||
}
|
||||
return value;
|
||||
return value ?? defaultValue;
|
||||
}
|
||||
|
||||
function applyLanguage(dom, result) {
|
||||
if (dom == null) {
|
||||
dom = document.body;
|
||||
}
|
||||
dom ??= document.body;
|
||||
for (let text of dom.querySelectorAll('[data-lgid]')) {
|
||||
const key = text.getAttribute('data-lgid');
|
||||
if (text.tagName === 'INPUT') {
|
||||
|
@ -103,7 +103,7 @@ request.post('api/query', { id: 101 })
|
||||
.then(result => console.log(result.data));
|
||||
|
||||
request.upload('api/upload', data, {
|
||||
progress: (ev) => {
|
||||
progress: ev => {
|
||||
console.log(`loaded: ${ev.loaded}, total: ${ev.total}`);
|
||||
}
|
||||
})
|
||||
|
@ -26,10 +26,7 @@ function padStart(s, num, char) {
|
||||
if (nullOrEmpty(s) || isNaN(num) || num <= s.length) {
|
||||
return s;
|
||||
}
|
||||
if (char == null) {
|
||||
char = ' ';
|
||||
}
|
||||
return char.repeat(num - s.length);
|
||||
return (char ?? ' ').repeat(num - s.length);
|
||||
}
|
||||
|
||||
export {
|
||||
|
Reference in New Issue
Block a user