sync form work

This commit is contained in:
2023-04-10 17:30:17 +08:00
parent fecaf6f450
commit c38e486d7d
15 changed files with 860 additions and 150 deletions

5
lib/ui/grid.d.ts vendored
View File

@ -33,7 +33,7 @@ interface GridColumnDefinition {
caption?: string;
width?: Number;
align?: "left" | "center" | "right";
enabled?: boolean | string;
enabled?: boolean | string | ((item: GridItem | any) => boolean);
css?: { [key: string]: string };
styleFilter?: (item: GridItem | any) => { [key: string]: string };
textStyle?: { [key: string]: string };
@ -51,6 +51,7 @@ interface GridColumnDefinition {
dropOptions?: DropdownOptions;
source?: Array<any> | ((item: GridItem | any) => Array<any> | Promise<Array<GridSourceItem>>);
iconType?: string;
className?: string | ((item: GridItem | any) => string);
text?: string;
tooltip?: string;
onallchecked?: (this: Grid, col: GridColumnDefinition, flag: boolean) => void;
@ -73,6 +74,7 @@ interface Grid {
langs?: { all: string, ok: string, reset: string };
virtualCount?: Number;
rowHeight?: Number;
extraRows?: Number;
filterRowHeight?: Number;
height?: Number;
readonly?: boolean;
@ -80,6 +82,7 @@ interface Grid {
fullrowClick?: boolean;
allowHtml?: boolean;
holderDisabled?: boolean;
headerVisible?: boolean;
window?: Window
sortIndex?: Number;
sortDirection?: keyof GridColumnDirection;

View File

@ -172,19 +172,28 @@ class GridCheckboxColumn extends GridColumn {
}
class GridIconColumn extends GridColumn {
static create() { return createElement('span', 'icon') }
static create() { return createElement('span', 'col-icon') }
static setValue(element, val, item, col) {
let className = col.className;
if (typeof className === 'function') {
className = className.call(col, item);
}
if (className == null) {
element.className = 'col-icon';
} else {
element.className = `col-icon ${className}`;
}
let type = col.iconType;
if (typeof type === 'function') {
type = type(item);
type = type.call(col, item);
}
type ??= 'fa-regular';
if (element.dataset.type !== type || element.dataset.icon !== val) {
const icon = createIcon(type, val);
// const layer = element.children[0];
element.replaceChildren(icon);
!nullOrEmpty(col.tooltip) && setTooltip(icon, col.tooltip);
!nullOrEmpty(col.tooltip) && setTooltip(element, col.tooltip);
element.dataset.type = type;
element.dataset.icon = val;
}
@ -238,7 +247,8 @@ class Grid {
reset: r('reset', 'Reset')
};
virtualCount = 100;
rowHeight = 39;
rowHeight = 36;
extraRows = 0;
filterRowHeight = 30;
height;
readonly;
@ -246,6 +256,7 @@ class Grid {
fullrowClick = true;
allowHtml = false;
holderDisabled = false;
headerVisible = true;
window = global;
sortIndex = -1;
sortDirection = 1;
@ -416,7 +427,7 @@ class Grid {
}
scrollToIndex(index) {
const top = this.#scrollToTop(index * this.rowHeight, true);
const top = this.#scrollToTop(index * (this.rowHeight + 1), true);
this.#refs.body.scrollTop = top;
}
@ -431,13 +442,15 @@ class Grid {
// body.style.top = `${height}px`;
// top = height;
// }
const top = this.#refs.header.offsetHeight;
const top = this.headerVisible === false ? 0 : this.#refs.header.offsetHeight;
let height = this.height;
if (isNaN(height) || height <= 0) {
if (height === 0) {
height = this.#containerHeight;
} else if (isNaN(height) || height < 0) {
height = this.#el.offsetHeight - top;
}
const count = truncate((height - 1) / this.rowHeight) * (RedumCount * 2) + 1;
const count = truncate((height - 1) / (this.rowHeight + 1)) * (RedumCount * 2) + 1;
if (force || count !== this.#rowCount) {
this.#rowCount = count;
this.reload();
@ -446,7 +459,11 @@ class Grid {
}
reload() {
this.#containerHeight = this.#currentSource.length * this.rowHeight;
let length = this.#currentSource.length;
if (this.extraRows > 0) {
length += this.extraRows;
}
this.#containerHeight = length * (this.rowHeight + 1);
this.#refs.body.scrollTop = 0;
this.#refs.body.scrollLeft = 0;
this.#refs.bodyContent.style.top = '0px';
@ -555,6 +572,9 @@ class Grid {
#createHeader() {
const thead = createElement('table', 'grid-header');
if (this.headerVisible === false) {
thead.style.display = 'none';
}
const header = createElement('tr');
thead.appendChild(header);
const sizer = this.#refs.sizer;
@ -838,7 +858,9 @@ class Grid {
enabled = false;
} else {
enabled = col.enabled;
if (typeof enabled === 'string') {
if (typeof enabled === 'function') {
enabled = enabled.call(col, item);
} else if (typeof enabled === 'string') {
enabled = item[enabled];
}
}
@ -1023,7 +1045,7 @@ class Grid {
}
#scrollToTop(top, reload) {
const rowHeight = this.rowHeight;
const rowHeight = (this.rowHeight + 1);
top -= (top % (rowHeight * 2)) + (RedumCount * rowHeight);
if (top < 0) {
top = 0;
@ -1234,7 +1256,8 @@ class Grid {
return;
}
const key = col.key;
const test = typeof col.enabled === 'string';
const isFunction = typeof col.enabled === 'function';
const isString = typeof col.enabled === 'string';
if (typeof col.onallchecked === 'function') {
col.onallchecked.call(this, col, flag);
} else {
@ -1243,7 +1266,7 @@ class Grid {
if (item == null) {
continue;
}
const enabled = test ? item[col.enabled] : col.enabled;
const enabled = isFunction ? col.enabled(item) : isString ? item[col.enabled] : col.enabled;
if (enabled !== false) {
item[key] = flag;
row.__changed = true;
@ -1413,7 +1436,12 @@ class Grid {
if (item == null) {
return;
}
const enabled = typeof col.enabled === 'string' ? item[col.enabled] : col.enabled;
let enabled = col.enabled;
if (typeof enabled === 'function') {
enabled = enabled.call(col, item);
} else if (typeof enabled === 'string') {
enabled = item[enabled];
}
if (enabled !== false) {
item[col.key] = value;
row.__changed = true;

View File

@ -14,9 +14,15 @@
document.querySelector('#button-popup').addEventListener('click', () => {
const popup = ui.createPopup('title', 'content',
{ text: 'Ok', trigger: () => true });
document.body.appendChild(popup);
setTimeout(() => popup.style.opacity = 1);
{
text: 'Loading', trigger: p => {
p.loading = true;
setTimeout(() => p.loading = false, 1000);
return false;
}
},
{ text: 'OK' });
popup.show();
});
</script>
<style type="text/css">

View File

@ -1,45 +1,161 @@
import "../../css/popup.scss";
import { createElement } from "../functions";
import { r } from "../utility";
import { createIcon } from "./icon";
function createPopup(title, content, ...buttons) {
const mask = createElement('div', 'popup-mask');
const container = createElement('div', 'popup-container');
const close = () => {
mask.classList.add('popup-active');
mask.style.opacity = 0;
setTimeout(() => mask.remove(), 120);
};
container.append(
createElement('div', header => {
header.className = 'popup-header';
if (title instanceof HTMLElement) {
class Popup {
#mask;
#option;
constructor(opts = {}) {
this.#option = opts;
}
get container() { return this.#mask.querySelector('.popup-container') }
create() {
const mask = createElement('div', 'popup-mask');
const container = createElement('div', 'popup-container');
const close = () => {
mask.classList.add('popup-active');
mask.style.opacity = 0;
setTimeout(() => mask.remove(), 120);
};
let content = this.#option.content;
if (!(content instanceof HTMLElement)) {
content = createElement('div', d => d.innerText = content);
}
container.append(
createElement('div', header => {
header.className = 'popup-header';
let title = this.#option.title;
if (!(title instanceof HTMLElement)) {
title = createElement('div', t => t.innerText = title);
}
header.appendChild(title);
} else {
header.appendChild(createElement('div', t => t.innerText = title));
}
const cancel = createIcon('fa-regular', 'times');
cancel.addEventListener('click', () => close());
header.appendChild(cancel);
}),
createElement('div', 'popup-body', content),
createElement('div', 'popup-footer', ...buttons.map(b => {
const button = createElement('div', 'popup-button');
button.innerText = b.text;
if (typeof b.trigger === 'function') {
button.addEventListener('click', () => {
if (b.trigger(container) === false) {
return;
}
close();
const move = title.querySelector('.popup-move') ?? title;
move.addEventListener('mousedown', e => {
const x = e.clientX - container.offsetLeft;
const y = e.clientY - container.offsetTop;
const move = e => {
container.style.left = `${e.clientX - x}px`;
container.style.top = `${e.clientY - y}px`;
};
mask.addEventListener('mousemove', move, { passive: false });
mask.addEventListener('mouseup', () => {
mask.removeEventListener('mousemove', move, { passive: false });
});
});
}
}))
);
mask.appendChild(container);
return mask;
const cancel = createIcon('fa-regular', 'times');
cancel.addEventListener('click', () => close());
header.appendChild(cancel);
}),
createElement('div', 'popup-body', content, createElement('div', 'popup-loading',
createElement('div', null, createIcon('fa-regular', 'spinner-third'))
))
);
if (Array.isArray(this.#option.buttons)) {
container.appendChild(
createElement('div', 'popup-footer', ...this.#option.buttons.map(b => {
const button = createElement('div', 'popup-button');
button.innerText = b.text;
button.addEventListener('click', () => {
if (typeof b.trigger === 'function' && b.trigger(this) === false) {
return;
}
close();
});
return button;
}))
);
}
mask.appendChild(container);
this.#mask = mask;
return mask;
}
show(parent = document.body) {
if (parent == null) {
return;
}
let mask = this.#mask ?? this.create();
parent.appendChild(mask);
return new Promise(resolve => {
setTimeout(() => {
mask.style.opacity = 1
resolve();
}, 0);
});
}
get loading() { return this.#mask?.querySelector('.popup-body>.popup-loading')?.style?.visibility === 'visible' }
set loading(flag) {
let loading = this.#mask?.querySelector('.popup-body>.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;
}
}
}
export {
createPopup
export default Popup;
export function createPopup(title, content, ...buttons) {
const popup = new Popup({
title,
content,
buttons
});
return popup;
}
const iconTypes = {
'info': 'info-circle',
'information': 'info-circle',
'warn': 'exclamation-triangle',
'warning': 'exclamation-triangle',
'question': 'question-circle',
'error': 'times-circle'
}
export function showAlert(title, message, iconType = 'info', parent = document.body) {
return new Promise(resolve => {
const popup = new Popup({
title,
content: createElement('div', 'message-wrapper',
createIcon('fa-solid', iconTypes[iconType] ?? 'info-circle'),
createElement('span', span => span.innerText = message)
),
buttons: [
{ text: r('ok', 'OK'), trigger: resolve }
]
});
popup.show(parent);
});
}
export function showConfirm(title, message, buttons, iconType = 'question', parent = document.body) {
return new Promise(resolve => {
const popup = new Popup({
title,
content: createElement('div', 'message-wrapper',
createIcon('fa-solid', iconTypes[iconType] ?? 'question-circle'),
createElement('span', span => span.innerText = message)
),
buttons: buttons?.map(b => {
return { text: b.text, trigger: p => resolve(b.key, p) }
}) ??
[
{ text: r('yes', 'Yes'), trigger: p => resolve('yes', p) },
{ text: r('no', 'No'), trigger: p => resolve('no', p) }
]
});
popup.show(parent);
});
}