add: getText compatibility.

add: `AssetSelector` and `TemplateSelector`.
add: `popup-selector` style class.
add: `ui.resolvePopup` function.
add: `switch` in checkbox.
add: `GridColumn.filterTemplate` supports.
add: add `action` callback in `createIcon`.
change: replace `setTimeout(..., 0)` with `requestAnimationFrame`.
change: Popup result structure adjustment ({ result: any, popup: Popup }).
change: complete add work order flow.
change: reduce Popup title height.
fix: Grid column sort in number.
This commit is contained in:
2024-06-21 17:28:11 +08:00
parent 1a7aa1ab66
commit 5baf00de64
34 changed files with 1772 additions and 365 deletions

View File

@@ -1,9 +1,10 @@
import "./css/popup.scss";
import { r } from "../utility/lgres";
import { r as lang } from "../utility/lgres";
import { nullOrEmpty } from "../utility/strings";
import { global } from "../utility";
import { createElement } from "../functions";
import { createIcon, changeIcon } from "./icon";
import { requestAnimationFrame } from "../ui";
const ResizeMods = {
right: 1,
@@ -51,6 +52,30 @@ export class Popup {
get container() { return this._var.mask.querySelector('.ui-popup-container') }
get title() { return this._var.option.title }
set title(title) {
const element = this._var.mask?.querySelector('.ui-popup-container .ui-popup-header .ui-popup-header-title');
if (element != null) {
element.innerText = title;
}
this._var.option.title = title;
}
get loading() { return this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
set loading(flag) {
let loading = this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading');
if (loading == null) {
return;
}
if (flag === false) {
loading.style.visibility = 'hidden';
loading.style.opacity = 0;
} else {
loading.style.visibility = 'visible';
loading.style.opacity = 1;
}
}
get rect() {
const container = this.container;
if (container == null) {
@@ -105,25 +130,41 @@ export class Popup {
}
}
close(animation = true) {
close(result = null, animation = true) {
const option = this._var.option;
const mask = this._var.mask;
const doClose = () => {
if (option.persistent) {
mask.style.display = 'none';
} else {
mask.remove();
this._var.mask = null;
}
}
if (animation) {
mask.classList.add('ui-popup-active');
mask.style.opacity = 0;
setTimeout(() => { mask.remove(); }, 120);
setTimeout(() => { doClose(); }, 120);
} else {
mask.remove();
doClose();
}
if (typeof this._var.option.onMasking === 'function') {
this._var.option.onMasking.call(this, false);
if (typeof option.onMasking === 'function') {
option.onMasking.call(this, false);
}
if (typeof this._var.option.resolve === 'function') {
this._var.option.resolve();
if (typeof option.resolve === 'function') {
option.resolve.call(this, {
result,
popup: this
});
}
}
/**
* 创建 Popup 面板
* @returns {HTMLDivElement} 返回遮罩元素(顶层元素)
*/
create() {
const mask = createElement('div', 'ui-popup-mask');
const mask = createElement('div', 'ui-popup-mask ui-popup-active');
const option = this._var.option;
if (option.mask === false) {
mask.classList.add('ui-popup-transparent');
@@ -286,14 +327,14 @@ export class Popup {
if (typeof result?.then === 'function') {
result.then(r => {
if (r !== false) {
this.close();
this.close(r);
}
}).catch(reason => console.warn(reason));
} else if (result !== false) {
this.close();
this.close(result);
}
} else {
this.close();
this.close(b.key ?? i);
}
});
return button;
@@ -357,24 +398,33 @@ export class Popup {
return mask;
}
show(parent = document.body) {
show(parent = document.body, hidden = false) {
if (parent == null) {
return;
}
let mask = this._var.mask ?? this.create();
// const exists = [...parent.children].filter(e => e.classList.contains('ui-popup-mask'));
const exists = parent.querySelectorAll('.ui-popup-mask');
let zindex = 0;
for (let ex of exists) {
let z = parseInt(ex.style.zIndex);
if (!isNaN(z) && z > zindex) {
zindex = z;
let mask = this._var.mask;
if (mask == null) {
mask = this._var.mask = this.create();
}
if (mask.parentElement == null) {
// const exists = [...parent.children].filter(e => e.classList.contains('ui-popup-mask'));
const exists = parent.querySelectorAll('.ui-popup-mask');
let zindex = 0;
for (let ex of exists) {
let z = parseInt(global.getComputedStyle(ex).zIndex);
if (!isNaN(z) && z > zindex) {
zindex = z;
}
}
if (zindex > 0) {
mask.style.zIndex = String(zindex + 1);
}
parent.appendChild(mask);
if (hidden === true) {
mask.style.display = 'none';
return Promise.resolve(mask);
}
}
if (zindex > 0) {
mask.style.zIndex = String(zindex + 1);
}
parent.appendChild(mask);
if (this._var.option.mask === false) {
// calculator position
const container = this.container;
@@ -382,29 +432,16 @@ export class Popup {
container.style.top = String((parent.offsetHeight - container.offsetHeight) / 2) + 'px';
}
return new Promise(resolve => {
setTimeout(() => {
mask.style.display = '';
requestAnimationFrame(() => {
mask.classList.remove('ui-popup-active');
mask.style.opacity = 1;
this.container.focus();
resolve(mask);
}, 0);
setTimeout(() => resolve(mask), 120);
});
});
}
get loading() { return this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading')?.style?.visibility === 'visible' }
set loading(flag) {
let loading = this._var.mask?.querySelector('.ui-popup-body>.ui-popup-loading');
if (loading == null) {
return;
}
if (flag === false) {
loading.style.visibility = 'hidden';
loading.style.opacity = 0;
} else {
loading.style.visibility = 'visible';
loading.style.opacity = 1;
}
}
_resize(mod, e) {
if (e.buttons !== 1) {
return;
@@ -500,6 +537,43 @@ export function createPopup(title, content, ...buttons) {
return popup;
}
/**
* 解析对话框元素
* @param {HTMLElement | string} wrapper - 解析该 `.dialog` 元素
* @param {Function} [callback] - 关闭对话框时的回调
* @param {boolean} [removable] - 是否可移除
* @param {number} [zIndex] - 对话框默认 `z-index`
* @returns {Popup} 返回弹出框字典
*/
export function resolvePopup(wrapper, callback, removable, zIndex) {
if (typeof wrapper === 'string') {
wrapper = document.querySelector(wrapper);
}
if (wrapper == null) {
return null;
}
if (!wrapper.classList.contains('dialog')) {
return null;
}
const title = wrapper.querySelector('.dialog-title>.title')?.innerText;
const content = wrapper.querySelector('.dialog-title+div');
const buttons = [...wrapper.querySelectorAll('.dialog-func>input[type="button"]')].reverse().map(b => ({
tabIndex: b.tabIndex,
text: b.value,
trigger: b.onclick == null ? null : (popup => (b.onclick.call(popup), false))
}));
const popup = new Popup({
title,
content,
persistent: !removable,
resolve: typeof callback === 'function' ? (result => callback(result)) : null,
zIndex: wrapper.zIndex ?? zIndex,
buttons
});
popup.show(document.body, true);
return popup;
}
const iconTypes = {
'info': 'info-circle',
'information': 'info-circle',
@@ -510,6 +584,7 @@ const iconTypes = {
}
export function showAlert(title, message, iconType = 'info', parent = document.body) {
const r = typeof GetTextByKey === 'function' ? GetTextByKey : lang;
return new Promise(resolve => {
const popup = new Popup({
title,
@@ -519,7 +594,7 @@ export function showAlert(title, message, iconType = 'info', parent = document.b
),
resolve,
buttons: [
{ text: r('ok', 'OK'), trigger: resolve }
{ text: r('ok', 'OK') }
]
});
popup.show(parent).then(mask => {
@@ -530,6 +605,7 @@ export function showAlert(title, message, iconType = 'info', parent = document.b
}
export function showConfirm(title, content, buttons, iconType = 'question', parent = document.body) {
const r = typeof GetTextByKey === 'function' ? GetTextByKey : lang;
return new Promise(resolve => {
const wrapper = createElement('div', 'message-wrapper');
if (!nullOrEmpty(iconType)) {
@@ -542,34 +618,23 @@ export function showConfirm(title, content, buttons, iconType = 'question', pare
title,
content: wrapper,
resolve,
buttons: buttons?.map(b => {
buttons: buttons?.map((b, i) => {
return {
text: b.text,
trigger: p => {
let result;
if (typeof b.trigger === 'function') {
result = b.trigger(p, b);
if (typeof result?.then === 'function') {
return result.then(r => {
r !== false && resolve(r);
return r;
});
}
result !== false && resolve(result);
} else {
result = {
key: b.key,
popup: p
};
resolve(result);
result = b.key ?? i;
}
return result;
}
};
}) ??
[
{ text: r('yes', 'Yes'), trigger: p => resolve({ key: 'yes', popup: p }) },
{ text: r('no', 'No'), trigger: p => resolve({ key: 'no', popup: p }) }
{ key: 'yes', text: r('yes', 'Yes') },
{ key: 'no', text: r('no', 'No') }
]
});
popup.show(parent).then(mask => {