Compare commits
No commits in common. "5467cc6e29cf6c78aa2d5c1fb100824f6691de1c" and "58081348ab7f3cba6925f19a24b5cf4e2896df78" have entirely different histories.
5467cc6e29
...
58081348ab
15
CHANGELOG.md
15
CHANGELOG.md
@ -1,20 +1,5 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
## v1.0.2 (2026-05-17)
|
|
||||||
|
|
||||||
### 新特性
|
|
||||||
- **AutoForm**: 新增 `inline` 模式,支持紧凑的单行表单布局,并增强了与 `DataTable` 的联动能力(数据变化自动刷新)。
|
|
||||||
- **Resizer**:
|
|
||||||
- 支持 `$bind` 指令,实现尺寸的双向绑定。
|
|
||||||
- 新增 `resizing` 和 `change` 事件,便于监听实时与最终缩放结果。
|
|
||||||
|
|
||||||
### 优化
|
|
||||||
- **UI**:
|
|
||||||
- `Modal`, `Dialog`, `Toast` 默认主题色由 `body` 改为 `primary`,提升视觉引导。
|
|
||||||
- `Toast` 增加 `rounded` 圆角样式。
|
|
||||||
- 统一所有表单控件(input/select/textarea)在 `AutoForm` 中的设置默认为 `{}`。
|
|
||||||
- **FastList**: 重构虚拟滚动逻辑为独立的 `VirtualScroll` 模块,提升代码复用性与可维护性。
|
|
||||||
|
|
||||||
## v1.0.1 (2026-05-14)
|
## v1.0.1 (2026-05-14)
|
||||||
- **Perf**: 优化 `FastList` 渲染逻辑,消除 Layout Thrashing,提升复杂列表(GroupedList/Tree)滚动性能达 10 倍以上。
|
- **Perf**: 优化 `FastList` 渲染逻辑,消除 Layout Thrashing,提升复杂列表(GroupedList/Tree)滚动性能达 10 倍以上。
|
||||||
- **Refactor**: 采用 `requestAnimationFrame` 优化初始化高度计算,减少闪烁。
|
- **Refactor**: 采用 `requestAnimationFrame` 优化初始化高度计算,减少闪烁。
|
||||||
|
|||||||
235
dist/base.js
vendored
235
dist/base.js
vendored
@ -107,7 +107,7 @@ Component.register("Modal", (container) => {
|
|||||||
`
|
`
|
||||||
<div class="modal fade" data-bs-backdrop="static">
|
<div class="modal fade" data-bs-backdrop="static">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div $class="modal-content text-bg-\${this.state?.type || 'primary'}">
|
<div $class="modal-content text-bg-\${this.state?.type || 'body'}">
|
||||||
<div slot-id="header" class="modal-header">
|
<div slot-id="header" class="modal-header">
|
||||||
<h6 class="modal-title" $text="this.state?.title"></h6>
|
<h6 class="modal-title" $text="this.state?.title"></h6>
|
||||||
<button type="button" class="btn btn-link ms-2 bi bi-x-lg link-reset" style="color:inherit" data-bs-dismiss="modal"></button>
|
<button type="button" class="btn btn-link ms-2 bi bi-x-lg link-reset" style="color:inherit" data-bs-dismiss="modal"></button>
|
||||||
@ -124,7 +124,7 @@ Component.register("Dialog", Component.getSetupFunction("Modal"), Util.makeDom(
|
|||||||
`
|
`
|
||||||
<div class="modal fade" data-bs-backdrop="static">
|
<div class="modal fade" data-bs-backdrop="static">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div $class="modal-content text-bg-\${this.state?.type || 'primary'}">
|
<div $class="modal-content text-bg-\${this.state?.type || 'body'}">
|
||||||
<div $if="this.state?.title" class="modal-header" $text="this.state?.title"></div>
|
<div $if="this.state?.title" class="modal-header" $text="this.state?.title"></div>
|
||||||
<div slot-id="body" class="modal-body"><div $html="this.state?.message"></div></div>
|
<div slot-id="body" class="modal-body"><div $html="this.state?.message"></div></div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@ -181,7 +181,7 @@ Component.register("Toast", (container) => {
|
|||||||
/*html*/
|
/*html*/
|
||||||
`
|
`
|
||||||
<div class="toast align-items-center border-0 m-1">
|
<div class="toast align-items-center border-0 m-1">
|
||||||
<div $class="toast-body rounded p-3 text-bg-\${this.state?.type}">
|
<div $class="toast-body p-3 text-bg-\${this.state?.type}">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<span style="white-space:pre-wrap" class="fs-6" $text="this.state?.message"></span>
|
<span style="white-space:pre-wrap" class="fs-6" $text="this.state?.message"></span>
|
||||||
@ -202,7 +202,7 @@ Component.register("Toast", (container) => {
|
|||||||
UI$1.toast = function(message, options = {}) {
|
UI$1.toast = function(message, options = {}) {
|
||||||
const delay = options.delay ?? 5e3;
|
const delay = options.delay ?? 5e3;
|
||||||
const t = document.createElement("Toast");
|
const t = document.createElement("Toast");
|
||||||
t.state = { delay, left: delay ? delay / 1e3 : void 0, type: options.type || "primary", message, buttons: options.buttons || [] };
|
t.state = { delay, left: delay ? delay / 1e3 : void 0, type: options.type || "body", message, buttons: options.buttons || [] };
|
||||||
$(`[toast-container="${options.container || "default"}"]`).appendChild(t);
|
$(`[toast-container="${options.container || "default"}"]`).appendChild(t);
|
||||||
Promise.resolve().then(() => t.show());
|
Promise.resolve().then(() => t.show());
|
||||||
};
|
};
|
||||||
@ -212,23 +212,10 @@ UI$1.toastConfirm = function(message, options = {}) {
|
|||||||
Component.register("AutoForm", (container) => {
|
Component.register("AutoForm", (container) => {
|
||||||
if (!container.state.schema) container.state.schema = [];
|
if (!container.state.schema) container.state.schema = [];
|
||||||
container.vertical = container.hasAttribute("vertical");
|
container.vertical = container.hasAttribute("vertical");
|
||||||
container.inline = container.hasAttribute("inline");
|
|
||||||
container.request = { method: "POST" };
|
container.request = { method: "POST" };
|
||||||
container.response = {};
|
container.response = {};
|
||||||
container.result = null;
|
container.result = null;
|
||||||
const setupData = () => {
|
|
||||||
if (!container.data || !container.data.__watch) {
|
|
||||||
container.data = NewState(container.data || {});
|
container.data = NewState(container.data || {});
|
||||||
}
|
|
||||||
container.data.__watch("*", () => {
|
|
||||||
if (container.inline) {
|
|
||||||
const dt = container.closest("DataTable");
|
|
||||||
if (dt && dt.refresh) dt.refresh();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
if (container.data) setupData();
|
|
||||||
else requestAnimationFrame(setupData);
|
|
||||||
container.form = $(container, "form");
|
container.form = $(container, "form");
|
||||||
container.submit = (opt = {}) => {
|
container.submit = (opt = {}) => {
|
||||||
var _a, _b;
|
var _a, _b;
|
||||||
@ -253,29 +240,27 @@ Component.register("AutoForm", (container) => {
|
|||||||
}, Util.makeDom(
|
}, Util.makeDom(
|
||||||
/*html*/
|
/*html*/
|
||||||
`
|
`
|
||||||
<div $class="'auto-form-root' + (this.inline ? ' auto-form-inline min-h-100 w-100' : '')">
|
<div>
|
||||||
<form $class="'h-100 w-100 d-flex ' + (this.inline ? 'align-items-center' : (this.vertical ? 'flex-column' : 'auto-grid-form'))" $onsubmit="event.stopPropagation();event.preventDefault();this.submit()">
|
<form $class="align-items-center \${this.vertical?'':'auto-grid-form'}" $onsubmit="event.stopPropagation();event.preventDefault();this.submit()">
|
||||||
<div $each="this.state.schema || []" style="display:contents">
|
<div $each="this.state.schema || []" style="display:contents">
|
||||||
<label $if="!this.inline" $name="item.name" class="col-form-label text-muted" $text="item.label"></label>
|
<label $name="item.name" class="col-form-label text-muted" $text="item.label"></label>
|
||||||
<div control-wrapper $class="this.inline ? 'flex-grow-1 h-100 d-flex align-items-center' : 'mb-3'">
|
<div control-wrapper class="mb-3">
|
||||||
<input $if="['text', 'password', 'email', 'number', 'date', 'datetime', 'file'].includes(item.type)" $name="item.name" class="form-control" $type="item.type" $.="item.setting || {}" $bind="this.data[item.name]" $class="item.type === 'number' ? 'text-end' : ''">
|
<input $if="['text', 'password', 'email', 'number', 'date', 'datetime', 'file'].includes(item.type)" $name="item.name" class="form-control" $type="item.type" $.="item.setting" $bind="this.data[item.name]">
|
||||||
<select $if="item.type === 'select'" $name="item.name" class="form-select" $.="item.setting || {}" $bind="this.data[item.name]">
|
<select $if="item.type === 'select'" $name="item.name" class="form-select" $.="item.setting" $bind="this.data[item.name]">
|
||||||
<option value="" $if="item.placeholder" $text="item.placeholder" disabled></option>
|
<option value="" $if="item.placeholder" $text="item.placeholder" disabled selected></option>
|
||||||
<option $each="item.options" $value="item.value || item" $text="item.label || item"></option>
|
<option $each="item.options" $value="item.value || item" $text="item.label || item"></option>
|
||||||
</select>
|
</select>
|
||||||
<div $if="['checkbox', 'radio'].includes(item.type)" $class="this.inline ? 'h-100 d-flex align-items-center px-2 gap-3 justify-content-center w-100' : ''" style="white-space:nowrap">
|
<div $if="['checkbox', 'radio'].includes(item.type)" >
|
||||||
<label $each="item.options || [item.text||item.label||item.name]" as="option" $class="'form-check' + (item.vertical ? '' : ' form-check-inline') + ' mb-0 d-flex align-items-center p-0'">
|
<label $each="item.options || [item.text||item.label||item.name]" as="option" $class="form-check\${item.vertical ? '' : ' form-check-inline'}">
|
||||||
<input $name="item.name" class="form-check-input me-1" $type="item.type" $.="item.setting || {}" $value="item.options?option:'on'" $bind="this.data[item.name]">
|
<input $name="item.name" class="form-check-input" $type="item.type" $.="item.setting" $value="item.options?option:'on'" $bind="this.data[item.name]">
|
||||||
<span $if="!this.inline || (item.options && item.options.length > 0)" $text="option" class="form-check-label"></span>
|
<span $text="option" class="form-check-label"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div $if="item.type==='switch'" class="form-check form-switch fs-4 h-100 d-flex align-items-center px-2 m-0 justify-content-center w-100" style="padding-left:0">
|
<div $if="item.type==='switch'" class="form-check form-switch fs-4"><input $name="item.name" class="form-check-input my-0" type="checkbox" $bind="this.data[item.name]"></div>
|
||||||
<input $name="item.name" class="form-check-input m-0" type="checkbox" style="cursor:pointer" $bind="this.data[item.name]">
|
<textarea $if="item.type==='textarea'" $name="item.name" class="form-control" $.="item.setting" $bind="this.data[item.name]"></textarea>
|
||||||
</div>
|
|
||||||
<textarea $if="item.type==='textarea'" $name="item.name" class="form-control" $.="item.setting || {}" $bind="this.data[item.name]"></textarea>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div $if="!this.inline" class="d-flex justify-content-end align-items-baseline gap-3 mt-3" style="grid-column:1/-1">
|
<div class="d-flex justify-content-end align-items-baseline gap-3 mt-3" style="grid-column:1/-1">
|
||||||
<div slot-id="actions"></div>
|
<div slot-id="actions"></div>
|
||||||
<button type="submit" class="btn btn-primary" $text="this.submitlabel || '{#Submit#}'"></button>
|
<button type="submit" class="btn btn-primary" $text="this.submitlabel || '{#Submit#}'"></button>
|
||||||
</div>
|
</div>
|
||||||
@ -284,14 +269,7 @@ Component.register("AutoForm", (container) => {
|
|||||||
`
|
`
|
||||||
), Util.makeDom(
|
), Util.makeDom(
|
||||||
/*html*/
|
/*html*/
|
||||||
`<style>
|
`<style>@media (min-width: 576px) { .auto-grid-form {display: grid;grid-template-columns: max-content 1fr} .auto-grid-form .col-form-label {text-align: right; margin-bottom: 1rem;padding-right: 1rem;max-width: 200px} }</style>`
|
||||||
@media (min-width: 576px) { .auto-grid-form {display: grid;grid-template-columns: max-content 1fr} .auto-grid-form .col-form-label {text-align: right; margin-bottom: 1rem;padding-right: 1rem;max-width: 200px} }
|
|
||||||
.auto-form-inline { background-color: var(--bs-body-bg); }
|
|
||||||
.auto-form-inline.min-h-100 { min-height: 100%; height: auto; }
|
|
||||||
.auto-form-inline .form-control, .auto-form-inline .form-select { border-radius: 0; border: none; background-color: transparent !important; min-height: 100%; height: auto; width: 100%; padding: 0 8px; box-shadow: none; color: inherit; }
|
|
||||||
.auto-form-inline .form-select { background-position: right 4px center; background-size: 12px 10px; padding-right: 20px; }
|
|
||||||
.auto-form-inline textarea.form-control { padding: 8px; min-height: 100px; height: auto; }
|
|
||||||
</style>`
|
|
||||||
));
|
));
|
||||||
const _pendingAutoFormComponents = [];
|
const _pendingAutoFormComponents = [];
|
||||||
const AutoForm = {
|
const AutoForm = {
|
||||||
@ -305,7 +283,9 @@ const AutoForm = {
|
|||||||
var _a;
|
var _a;
|
||||||
const template = Component.getTemplate("AutoForm");
|
const template = Component.getTemplate("AutoForm");
|
||||||
if (template) {
|
if (template) {
|
||||||
(_a = $(template.content, "[control-wrapper]")) == null ? void 0 : _a.appendChild(Util.makeDom(`<${name} $if="item.type.toUpperCase() === '${name.toUpperCase()}'" $name="item.name" $.="item.setting || {}" $bind="this.data[item.name]"></${name}>`));
|
(_a = $(template.content, "[control-wrapper]")) == null ? void 0 : _a.appendChild(Util.makeDom(`<${name} $if="item.type.toUpperCase() === '${name.toUpperCase()}'" $name="item.name" $.="item.setting" $bind="this.data[item.name]"></${name}>`));
|
||||||
|
} else if (document.readyState !== "loading") {
|
||||||
|
console.error("AutoForm template not found during registration of", name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -357,62 +337,30 @@ Component.register("TagsInput", (container) => {
|
|||||||
`<style>TagsInput button:focus {background-color:var(--bs-btn-hover-bg);color:var(--bs-btn-hover-color)}</style>`
|
`<style>TagsInput button:focus {background-color:var(--bs-btn-hover-bg);color:var(--bs-btn-hover-color)}</style>`
|
||||||
));
|
));
|
||||||
AutoForm.register("TagsInput");
|
AutoForm.register("TagsInput");
|
||||||
const VirtualScroll = () => {
|
const FastListComponent = Component.register("FastList", (container) => {
|
||||||
const itemHeights = /* @__PURE__ */ new Map();
|
const itemHeights = /* @__PURE__ */ new Map();
|
||||||
const groupHeights = /* @__PURE__ */ new Map();
|
const groupHeights = /* @__PURE__ */ new Map();
|
||||||
let groupItemCount = 1;
|
let groupItemCount = 1;
|
||||||
|
container.state.renderedList = [];
|
||||||
const avg = Util.newAvg();
|
const avg = Util.newAvg();
|
||||||
let padTop = 0;
|
let containerPaddingTop = 0;
|
||||||
let rowGap = 0;
|
let containerRowGap = 0;
|
||||||
let topMargin = 0;
|
let topMargin = 0;
|
||||||
let itemMarginTop = null;
|
let itemMarginTop = null;
|
||||||
let itemMarginBottom = null;
|
let itemMarginBottom = null;
|
||||||
|
let listStartIndex = 0;
|
||||||
|
let visibleStartIndex = 0;
|
||||||
|
let visibleCount = 10;
|
||||||
let listInited = false;
|
let listInited = false;
|
||||||
return {
|
container.onItemUpdate = (index2, node) => {
|
||||||
// 核心1:重置与首屏预渲染
|
const absoluteIndex = index2 + listStartIndex;
|
||||||
reset: (list, container) => {
|
|
||||||
listInited = false;
|
|
||||||
itemHeights.clear();
|
|
||||||
groupHeights.clear();
|
|
||||||
avg.clear();
|
|
||||||
topMargin = 0;
|
|
||||||
itemMarginTop = null;
|
|
||||||
itemMarginBottom = null;
|
|
||||||
if (!(list == null ? void 0 : list.length)) return [];
|
|
||||||
const size = list.length;
|
|
||||||
groupItemCount = Math.ceil(Math.sqrt(size)) || 10;
|
|
||||||
const style = window.getComputedStyle(container);
|
|
||||||
padTop = parseFloat(style.paddingTop) || 0;
|
|
||||||
rowGap = parseFloat(style.rowGap) || 0;
|
|
||||||
return list.slice(0, Math.min(30, size));
|
|
||||||
},
|
|
||||||
// 核心2:无痛初始化缓存 (通过 rAF 调用)
|
|
||||||
init: (list, refreshCallback) => {
|
|
||||||
if (listInited) return;
|
|
||||||
const size = list.length;
|
|
||||||
const defaultHeight = avg.get() || 32;
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
if (!itemHeights.has(i)) itemHeights.set(i, defaultHeight);
|
|
||||||
}
|
|
||||||
for (let i = 0; i < size; i += groupItemCount) {
|
|
||||||
let sum = 0;
|
|
||||||
for (let j = i; j < Math.min(i + groupItemCount, size); j++) {
|
|
||||||
sum += itemHeights.get(j);
|
|
||||||
}
|
|
||||||
groupHeights.set(i, sum);
|
|
||||||
}
|
|
||||||
listInited = true;
|
|
||||||
refreshCallback();
|
|
||||||
},
|
|
||||||
// 核心3:原汁原味的极速高度更新
|
|
||||||
update: (absoluteIndex, node) => {
|
|
||||||
if (itemMarginTop === null) {
|
if (itemMarginTop === null) {
|
||||||
const style = window.getComputedStyle(node);
|
const style = window.getComputedStyle(node);
|
||||||
itemMarginTop = parseFloat(style.marginTop) || 0;
|
itemMarginTop = parseFloat(style.marginTop) || 0;
|
||||||
itemMarginBottom = parseFloat(style.marginBottom) || 0;
|
itemMarginBottom = parseFloat(style.marginBottom) || 0;
|
||||||
}
|
}
|
||||||
if (absoluteIndex === 0) topMargin = itemMarginTop;
|
if (absoluteIndex === 0) topMargin = itemMarginTop;
|
||||||
const newHeight = node.offsetHeight + itemMarginTop + itemMarginBottom + rowGap;
|
const newHeight = node.offsetHeight + itemMarginTop + itemMarginBottom + containerRowGap;
|
||||||
const oldHeight = itemHeights.get(absoluteIndex);
|
const oldHeight = itemHeights.get(absoluteIndex);
|
||||||
if (newHeight !== oldHeight) {
|
if (newHeight !== oldHeight) {
|
||||||
itemHeights.set(absoluteIndex, newHeight);
|
itemHeights.set(absoluteIndex, newHeight);
|
||||||
@ -421,37 +369,38 @@ const VirtualScroll = () => {
|
|||||||
const groupIndex = absoluteIndex - absoluteIndex % groupItemCount;
|
const groupIndex = absoluteIndex - absoluteIndex % groupItemCount;
|
||||||
if (groupHeights.has(groupIndex)) groupHeights.set(groupIndex, groupHeights.get(groupIndex) + offset);
|
if (groupHeights.has(groupIndex)) groupHeights.set(groupIndex, groupHeights.get(groupIndex) + offset);
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
// 核心4:原汁原味的状态机滚动计算
|
container.refresh = () => {
|
||||||
calc: (container, list) => {
|
if (!listInited) return;
|
||||||
if (!listInited || !list) return null;
|
visibleCount = Math.ceil((container.clientHeight || 100) / (avg.get() || 32));
|
||||||
const size = list.length;
|
let list = [];
|
||||||
const visibleCount = Math.ceil((container.clientHeight || 100) / (avg.get() || 32));
|
const fullList = container.state.list;
|
||||||
let prev = padTop + topMargin + rowGap;
|
const fullSize = fullList.length;
|
||||||
|
let i = 0;
|
||||||
|
let prev = containerPaddingTop + topMargin + containerRowGap;
|
||||||
let post = 0;
|
let post = 0;
|
||||||
let status = 0;
|
let starus = 0;
|
||||||
let listStartIndex = 0;
|
for (i = 0; i < fullSize; i++) {
|
||||||
let listEndIndex = 0;
|
if (starus === 0) {
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
if (status === 0) {
|
|
||||||
const gh = groupHeights.get(i);
|
const gh = groupHeights.get(i);
|
||||||
if (gh && prev + gh < container.scrollTop) {
|
if (gh && prev + gh < container.scrollTop) {
|
||||||
prev += gh;
|
prev += gh;
|
||||||
i += Math.min(groupItemCount, size - i) - 1;
|
i += Math.min(groupItemCount, fullSize - i) - 1;
|
||||||
} else {
|
} else {
|
||||||
const ih = itemHeights.get(i);
|
const ih = itemHeights.get(i);
|
||||||
if (prev + ih < container.scrollTop) {
|
if (prev + ih < container.scrollTop) {
|
||||||
prev += ih;
|
prev += ih;
|
||||||
} else {
|
} else {
|
||||||
status = 1;
|
starus = 1;
|
||||||
let visibleStartIndex = Math.max(0, i);
|
visibleStartIndex = Math.max(0, i);
|
||||||
listStartIndex = Math.max(0, visibleStartIndex - visibleCount);
|
listStartIndex = Math.max(0, visibleStartIndex - visibleCount);
|
||||||
listEndIndex = Math.min(listStartIndex + visibleCount * 3, size);
|
const listEndIndex = Math.min(listStartIndex + visibleCount * 3, fullSize);
|
||||||
i = listEndIndex - 1;
|
i = listEndIndex - 1;
|
||||||
|
list = fullList.slice(listStartIndex, listEndIndex);
|
||||||
for (let j = listStartIndex; j < visibleStartIndex; j++) prev -= itemHeights.get(j);
|
for (let j = listStartIndex; j < visibleStartIndex; j++) prev -= itemHeights.get(j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (status === 1) {
|
} else if (starus === 1) {
|
||||||
const gh = groupHeights.get(i);
|
const gh = groupHeights.get(i);
|
||||||
if (gh) {
|
if (gh) {
|
||||||
post += gh;
|
post += gh;
|
||||||
@ -461,36 +410,40 @@ const VirtualScroll = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
container.state.prevHeight = prev - containerPaddingTop - topMargin - containerRowGap;
|
||||||
prevHeight: prev - padTop - topMargin - rowGap,
|
container.state.postHeight = post;
|
||||||
postHeight: post,
|
container.state.renderedList = list;
|
||||||
renderedList: list.slice(listStartIndex, listEndIndex),
|
|
||||||
listStartIndex
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
const FastListComponent = Component.register("FastList", (container) => {
|
|
||||||
const vs = VirtualScroll();
|
|
||||||
container.state.renderedList = [];
|
|
||||||
container.onItemUpdate = (index2, node) => {
|
|
||||||
vs.update(index2 + (container.state._listStartIndex || 0), node);
|
|
||||||
};
|
|
||||||
container.refresh = () => {
|
|
||||||
const res = vs.calc(container, container.state.list);
|
|
||||||
if (res) {
|
|
||||||
container.state.prevHeight = res.prevHeight;
|
|
||||||
container.state.postHeight = res.postHeight;
|
|
||||||
container.state._listStartIndex = res.listStartIndex;
|
|
||||||
container.state.renderedList = res.renderedList;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
container.state.__watch("list", (list) => {
|
container.state.__watch("list", (list) => {
|
||||||
container.state._listStartIndex = 0;
|
listInited = false;
|
||||||
container.state.renderedList = vs.reset(list, container) || [];
|
itemHeights.clear();
|
||||||
|
groupHeights.clear();
|
||||||
|
avg.clear();
|
||||||
|
topMargin = 0;
|
||||||
|
itemMarginTop = null;
|
||||||
|
itemMarginBottom = null;
|
||||||
|
if (!(list == null ? void 0 : list.length)) return;
|
||||||
|
const listSize = list.length || 0;
|
||||||
|
groupItemCount = Math.ceil(Math.sqrt(listSize)) || 10;
|
||||||
|
const style = window.getComputedStyle(container);
|
||||||
|
containerPaddingTop = parseFloat(style.paddingTop) || 0;
|
||||||
|
containerRowGap = parseFloat(style.rowGap) || 0;
|
||||||
|
container.state.renderedList = list.slice(0, Math.min(30, listSize)) || [];
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (container.state.list !== list) return;
|
if (listInited || container.state.list !== list) return;
|
||||||
vs.init(list, container.refresh);
|
const defaultHeight = avg.get() || 32;
|
||||||
|
for (let i = 0; i < listSize; i++) {
|
||||||
|
if (!itemHeights.has(i)) itemHeights.set(i, defaultHeight);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < listSize; i += groupItemCount) {
|
||||||
|
let sum = 0;
|
||||||
|
for (let j = i; j < Math.min(i + groupItemCount, listSize); j++) {
|
||||||
|
sum += itemHeights.get(j);
|
||||||
|
}
|
||||||
|
groupHeights.set(i, sum);
|
||||||
|
}
|
||||||
|
listInited = true;
|
||||||
|
container.refresh();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, Util.makeDom(
|
}, Util.makeDom(
|
||||||
@ -754,32 +707,19 @@ if (typeof document !== "undefined") {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
Component.register("Resizer", (container) => {
|
Component.register("Resizer", (container) => {
|
||||||
container.isVertical = container.hasAttribute("vertical");
|
const isVertical = container.hasAttribute("vertical");
|
||||||
const min = parseInt(container.getAttribute("min")) || 10;
|
const min = parseInt(container.getAttribute("min")) || 10;
|
||||||
const max = parseInt(container.getAttribute("max")) || 1e3;
|
const max = parseInt(container.getAttribute("max")) || 1e3;
|
||||||
const target = container.target || container.previousElementSibling;
|
const target = container.target || container.previousElementSibling;
|
||||||
container.addEventListener("bind", (e) => {
|
|
||||||
if (e.detail !== void 0 && e.detail !== null) {
|
|
||||||
target.style[container.isVertical ? "height" : "width"] = e.detail + "px";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const getSize = (startSize, w, h) => {
|
const getSize = (startSize, w, h) => {
|
||||||
const newSize = startSize + (container.isVertical ? h : w);
|
const newSize = startSize + (isVertical ? h : w);
|
||||||
return newSize < min ? min : newSize > max ? max : newSize;
|
return newSize < min ? min : newSize > max ? max : newSize;
|
||||||
};
|
};
|
||||||
container.addEventListener("mousedown", (event) => {
|
container.addEventListener("mousedown", (event) => {
|
||||||
const startSize = container.isVertical ? target.offsetHeight : target.offsetWidth;
|
const startSize = isVertical ? target.offsetHeight : target.offsetWidth;
|
||||||
MouseMover.start(event, {
|
MouseMover.start(event, {
|
||||||
onmousemove: ({ w, h }) => {
|
onmousemove: ({ w, h }) => target.style[isVertical ? "height" : "width"] = getSize(startSize, w, h) + "px",
|
||||||
const newSize = getSize(startSize, w, h);
|
onmouseup: ({ w, h }) => container.dispatchEvent(new CustomEvent("resize", { detail: { oldSize: startSize, newSize: getSize(startSize, w, h) }, bubbles: false }))
|
||||||
target.style[container.isVertical ? "height" : "width"] = newSize + "px";
|
|
||||||
container.dispatchEvent(new CustomEvent("resizing", { detail: { oldSize: startSize, newSize }, bubbles: false }));
|
|
||||||
},
|
|
||||||
onmouseup: ({ w, h }) => {
|
|
||||||
const newSize = getSize(startSize, w, h);
|
|
||||||
container.dispatchEvent(new CustomEvent("resize", { detail: { oldSize: startSize, newSize }, bubbles: false }));
|
|
||||||
container.dispatchEvent(new CustomEvent("change", { detail: newSize, bubbles: false }));
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}, Util.makeDom(
|
}, Util.makeDom(
|
||||||
@ -815,6 +755,5 @@ export {
|
|||||||
HTTP,
|
HTTP,
|
||||||
MouseMover,
|
MouseMover,
|
||||||
State,
|
State,
|
||||||
UI$1 as UI,
|
UI$1 as UI
|
||||||
VirtualScroll
|
|
||||||
};
|
};
|
||||||
|
|||||||
2
dist/base.min.js
vendored
2
dist/base.min.js
vendored
File diff suppressed because one or more lines are too long
30
node_modules/.package-lock.json
generated
vendored
30
node_modules/.package-lock.json
generated
vendored
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@web/base",
|
"name": "@web/base",
|
||||||
"version": "1.0.2",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
@ -183,20 +183,6 @@
|
|||||||
"@esbuild/win32-x64": "0.21.5"
|
"@esbuild/win32-x64": "0.21.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/fsevents": {
|
|
||||||
"version": "2.3.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/nanoid": {
|
"node_modules/nanoid": {
|
||||||
"version": "3.3.12",
|
"version": "3.3.12",
|
||||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz",
|
||||||
@ -445,20 +431,6 @@
|
|||||||
"optional": true
|
"optional": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"node_modules/vite/node_modules/fsevents": {
|
|
||||||
"version": "2.3.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
|
||||||
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
6
node_modules/.vite/deps/_metadata.json
generated
vendored
6
node_modules/.vite/deps/_metadata.json
generated
vendored
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"hash": "a2c51734",
|
"hash": "f5c4792c",
|
||||||
"configHash": "a1c4c8e0",
|
"configHash": "a1c4c8e0",
|
||||||
"lockfileHash": "99ec7e3e",
|
"lockfileHash": "4618e5dd",
|
||||||
"browserHash": "7e396f40",
|
"browserHash": "7960c498",
|
||||||
"optimized": {},
|
"optimized": {},
|
||||||
"chunks": {}
|
"chunks": {}
|
||||||
}
|
}
|
||||||
3
node_modules/.vite/deps_temp_b6940abf/package.json
generated
vendored
Normal file
3
node_modules/.vite/deps_temp_b6940abf/package.json
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"type": "module"
|
||||||
|
}
|
||||||
22
node_modules/fsevents/LICENSE
generated
vendored
22
node_modules/fsevents/LICENSE
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
MIT License
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Copyright (C) 2010-2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
83
node_modules/fsevents/README.md
generated
vendored
83
node_modules/fsevents/README.md
generated
vendored
@ -1,83 +0,0 @@
|
|||||||
# fsevents [](https://nodei.co/npm/fsevents/)
|
|
||||||
|
|
||||||
Native access to MacOS FSEvents in [Node.js](https://nodejs.org/)
|
|
||||||
|
|
||||||
The FSEvents API in MacOS allows applications to register for notifications of
|
|
||||||
changes to a given directory tree. It is a very fast and lightweight alternative
|
|
||||||
to kqueue.
|
|
||||||
|
|
||||||
This is a low-level library. For a cross-platform file watching module that
|
|
||||||
uses fsevents, check out [Chokidar](https://github.com/paulmillr/chokidar).
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
Supports only **Node.js v8.16 and higher**.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install fsevents
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```js
|
|
||||||
const fsevents = require('fsevents');
|
|
||||||
const stop = fsevents.watch(__dirname, (path, flags, id) => {
|
|
||||||
const info = fsevents.getInfo(path, flags, id);
|
|
||||||
}); // To start observation
|
|
||||||
stop(); // To end observation
|
|
||||||
```
|
|
||||||
|
|
||||||
The callback passed as the second parameter to `.watch` get's called whenever the operating system detects a
|
|
||||||
a change in the file system. It takes three arguments:
|
|
||||||
|
|
||||||
###### `fsevents.watch(dirname: string, (path: string, flags: number, id: string) => void): () => Promise<undefined>`
|
|
||||||
|
|
||||||
* `path: string` - the item in the filesystem that have been changed
|
|
||||||
* `flags: number` - a numeric value describing what the change was
|
|
||||||
* `id: string` - an unique-id identifying this specific event
|
|
||||||
|
|
||||||
Returns closer callback which when called returns a Promise resolving when the watcher process has been shut down.
|
|
||||||
|
|
||||||
###### `fsevents.getInfo(path: string, flags: number, id: string): FsEventInfo`
|
|
||||||
|
|
||||||
The `getInfo` function takes the `path`, `flags` and `id` arguments and converts those parameters into a structure
|
|
||||||
that is easier to digest to determine what the change was.
|
|
||||||
|
|
||||||
The `FsEventsInfo` has the following shape:
|
|
||||||
|
|
||||||
```js
|
|
||||||
/**
|
|
||||||
* @typedef {'created'|'modified'|'deleted'|'moved'|'root-changed'|'cloned'|'unknown'} FsEventsEvent
|
|
||||||
* @typedef {'file'|'directory'|'symlink'} FsEventsType
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
"event": "created", // {FsEventsEvent}
|
|
||||||
"path": "file.txt",
|
|
||||||
"type": "file", // {FsEventsType}
|
|
||||||
"changes": {
|
|
||||||
"inode": true, // Had iNode Meta-Information changed
|
|
||||||
"finder": false, // Had Finder Meta-Data changed
|
|
||||||
"access": false, // Had access permissions changed
|
|
||||||
"xattrs": false // Had xAttributes changed
|
|
||||||
},
|
|
||||||
"flags": 0x100000000
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
- v2.3 supports Apple Silicon ARM CPUs
|
|
||||||
- v2 supports node 8.16+ and reduces package size massively
|
|
||||||
- v1.2.8 supports node 6+
|
|
||||||
- v1.2.7 supports node 4+
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
- I'm getting `EBADPLATFORM` `Unsupported platform for fsevents` error.
|
|
||||||
- It's fine, nothing is broken. fsevents is macos-only. Other platforms are skipped. If you want to hide this warning, report a bug to NPM bugtracker asking them to hide ebadplatform warnings by default.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The MIT License Copyright (C) 2010-2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller — see LICENSE file.
|
|
||||||
|
|
||||||
Visit our [GitHub page](https://github.com/fsevents/fsevents) and [NPM Page](https://npmjs.org/package/fsevents)
|
|
||||||
46
node_modules/fsevents/fsevents.d.ts
generated
vendored
46
node_modules/fsevents/fsevents.d.ts
generated
vendored
@ -1,46 +0,0 @@
|
|||||||
declare type Event = "created" | "cloned" | "modified" | "deleted" | "moved" | "root-changed" | "unknown";
|
|
||||||
declare type Type = "file" | "directory" | "symlink";
|
|
||||||
declare type FileChanges = {
|
|
||||||
inode: boolean;
|
|
||||||
finder: boolean;
|
|
||||||
access: boolean;
|
|
||||||
xattrs: boolean;
|
|
||||||
};
|
|
||||||
declare type Info = {
|
|
||||||
event: Event;
|
|
||||||
path: string;
|
|
||||||
type: Type;
|
|
||||||
changes: FileChanges;
|
|
||||||
flags: number;
|
|
||||||
};
|
|
||||||
declare type WatchHandler = (path: string, flags: number, id: string) => void;
|
|
||||||
export declare function watch(path: string, handler: WatchHandler): () => Promise<void>;
|
|
||||||
export declare function watch(path: string, since: number, handler: WatchHandler): () => Promise<void>;
|
|
||||||
export declare function getInfo(path: string, flags: number): Info;
|
|
||||||
export declare const constants: {
|
|
||||||
None: 0x00000000;
|
|
||||||
MustScanSubDirs: 0x00000001;
|
|
||||||
UserDropped: 0x00000002;
|
|
||||||
KernelDropped: 0x00000004;
|
|
||||||
EventIdsWrapped: 0x00000008;
|
|
||||||
HistoryDone: 0x00000010;
|
|
||||||
RootChanged: 0x00000020;
|
|
||||||
Mount: 0x00000040;
|
|
||||||
Unmount: 0x00000080;
|
|
||||||
ItemCreated: 0x00000100;
|
|
||||||
ItemRemoved: 0x00000200;
|
|
||||||
ItemInodeMetaMod: 0x00000400;
|
|
||||||
ItemRenamed: 0x00000800;
|
|
||||||
ItemModified: 0x00001000;
|
|
||||||
ItemFinderInfoMod: 0x00002000;
|
|
||||||
ItemChangeOwner: 0x00004000;
|
|
||||||
ItemXattrMod: 0x00008000;
|
|
||||||
ItemIsFile: 0x00010000;
|
|
||||||
ItemIsDir: 0x00020000;
|
|
||||||
ItemIsSymlink: 0x00040000;
|
|
||||||
ItemIsHardlink: 0x00100000;
|
|
||||||
ItemIsLastHardlink: 0x00200000;
|
|
||||||
OwnEvent: 0x00080000;
|
|
||||||
ItemCloned: 0x00400000;
|
|
||||||
};
|
|
||||||
export {};
|
|
||||||
82
node_modules/fsevents/fsevents.js
generated
vendored
82
node_modules/fsevents/fsevents.js
generated
vendored
@ -1,82 +0,0 @@
|
|||||||
/*
|
|
||||||
** © 2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller
|
|
||||||
** Licensed under MIT License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* jshint node:true */
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
if (process.platform !== "darwin") {
|
|
||||||
throw new Error(`Module 'fsevents' is not compatible with platform '${process.platform}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Native = require("./fsevents.node");
|
|
||||||
const events = Native.constants;
|
|
||||||
|
|
||||||
function watch(path, since, handler) {
|
|
||||||
if (typeof path !== "string") {
|
|
||||||
throw new TypeError(`fsevents argument 1 must be a string and not a ${typeof path}`);
|
|
||||||
}
|
|
||||||
if ("function" === typeof since && "undefined" === typeof handler) {
|
|
||||||
handler = since;
|
|
||||||
since = Native.flags.SinceNow;
|
|
||||||
}
|
|
||||||
if (typeof since !== "number") {
|
|
||||||
throw new TypeError(`fsevents argument 2 must be a number and not a ${typeof since}`);
|
|
||||||
}
|
|
||||||
if (typeof handler !== "function") {
|
|
||||||
throw new TypeError(`fsevents argument 3 must be a function and not a ${typeof handler}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let instance = Native.start(Native.global, path, since, handler);
|
|
||||||
if (!instance) throw new Error(`could not watch: ${path}`);
|
|
||||||
return () => {
|
|
||||||
const result = instance ? Promise.resolve(instance).then(Native.stop) : Promise.resolve(undefined);
|
|
||||||
instance = undefined;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getInfo(path, flags) {
|
|
||||||
return {
|
|
||||||
path,
|
|
||||||
flags,
|
|
||||||
event: getEventType(flags),
|
|
||||||
type: getFileType(flags),
|
|
||||||
changes: getFileChanges(flags),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFileType(flags) {
|
|
||||||
if (events.ItemIsFile & flags) return "file";
|
|
||||||
if (events.ItemIsDir & flags) return "directory";
|
|
||||||
if (events.ItemIsSymlink & flags) return "symlink";
|
|
||||||
}
|
|
||||||
function anyIsTrue(obj) {
|
|
||||||
for (let key in obj) {
|
|
||||||
if (obj[key]) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function getEventType(flags) {
|
|
||||||
if (events.ItemRemoved & flags) return "deleted";
|
|
||||||
if (events.ItemRenamed & flags) return "moved";
|
|
||||||
if (events.ItemCreated & flags) return "created";
|
|
||||||
if (events.ItemModified & flags) return "modified";
|
|
||||||
if (events.RootChanged & flags) return "root-changed";
|
|
||||||
if (events.ItemCloned & flags) return "cloned";
|
|
||||||
if (anyIsTrue(flags)) return "modified";
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
function getFileChanges(flags) {
|
|
||||||
return {
|
|
||||||
inode: !!(events.ItemInodeMetaMod & flags),
|
|
||||||
finder: !!(events.ItemFinderInfoMod & flags),
|
|
||||||
access: !!(events.ItemChangeOwner & flags),
|
|
||||||
xattrs: !!(events.ItemXattrMod & flags),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.watch = watch;
|
|
||||||
exports.getInfo = getInfo;
|
|
||||||
exports.constants = events;
|
|
||||||
BIN
node_modules/fsevents/fsevents.node
generated
vendored
BIN
node_modules/fsevents/fsevents.node
generated
vendored
Binary file not shown.
62
node_modules/fsevents/package.json
generated
vendored
62
node_modules/fsevents/package.json
generated
vendored
@ -1,62 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "fsevents",
|
|
||||||
"version": "2.3.2",
|
|
||||||
"description": "Native Access to MacOS FSEvents",
|
|
||||||
"main": "fsevents.js",
|
|
||||||
"types": "fsevents.d.ts",
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"files": [
|
|
||||||
"fsevents.d.ts",
|
|
||||||
"fsevents.js",
|
|
||||||
"fsevents.node"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"clean": "node-gyp clean && rm -f fsevents.node",
|
|
||||||
"build": "node-gyp clean && rm -f fsevents.node && node-gyp rebuild && node-gyp clean",
|
|
||||||
"test": "/bin/bash ./test.sh 2>/dev/null",
|
|
||||||
"prepublishOnly": "npm run build"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/fsevents/fsevents.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"fsevents",
|
|
||||||
"mac"
|
|
||||||
],
|
|
||||||
"contributors": [
|
|
||||||
{
|
|
||||||
"name": "Philipp Dunkel",
|
|
||||||
"email": "pip@pipobscure.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ben Noordhuis",
|
|
||||||
"email": "info@bnoordhuis.nl"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Elan Shankar",
|
|
||||||
"email": "elan.shanker@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Miroslav Bajtoš",
|
|
||||||
"email": "mbajtoss@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Paul Miller",
|
|
||||||
"url": "https://paulmillr.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/fsevents/fsevents/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/fsevents/fsevents",
|
|
||||||
"devDependencies": {
|
|
||||||
"node-gyp": "^6.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
22
node_modules/vite/node_modules/fsevents/LICENSE
generated
vendored
22
node_modules/vite/node_modules/fsevents/LICENSE
generated
vendored
@ -1,22 +0,0 @@
|
|||||||
MIT License
|
|
||||||
-----------
|
|
||||||
|
|
||||||
Copyright (C) 2010-2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
89
node_modules/vite/node_modules/fsevents/README.md
generated
vendored
89
node_modules/vite/node_modules/fsevents/README.md
generated
vendored
@ -1,89 +0,0 @@
|
|||||||
# fsevents
|
|
||||||
|
|
||||||
Native access to MacOS FSEvents in [Node.js](https://nodejs.org/)
|
|
||||||
|
|
||||||
The FSEvents API in MacOS allows applications to register for notifications of
|
|
||||||
changes to a given directory tree. It is a very fast and lightweight alternative
|
|
||||||
to kqueue.
|
|
||||||
|
|
||||||
This is a low-level library. For a cross-platform file watching module that
|
|
||||||
uses fsevents, check out [Chokidar](https://github.com/paulmillr/chokidar).
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm install fsevents
|
|
||||||
```
|
|
||||||
|
|
||||||
Supports only **Node.js v8.16 and higher**.
|
|
||||||
|
|
||||||
```js
|
|
||||||
const fsevents = require('fsevents');
|
|
||||||
|
|
||||||
// To start observation
|
|
||||||
const stop = fsevents.watch(__dirname, (path, flags, id) => {
|
|
||||||
const info = fsevents.getInfo(path, flags);
|
|
||||||
});
|
|
||||||
|
|
||||||
// To end observation
|
|
||||||
stop();
|
|
||||||
```
|
|
||||||
|
|
||||||
> **Important note:** The API behaviour is slightly different from typical JS APIs. The `stop` function **must** be
|
|
||||||
> retrieved and stored somewhere, even if you don't plan to stop the watcher. If you forget it, the garbage collector
|
|
||||||
> will eventually kick in, the watcher will be unregistered, and your callbacks won't be called anymore.
|
|
||||||
|
|
||||||
The callback passed as the second parameter to `.watch` get's called whenever the operating system detects a
|
|
||||||
a change in the file system. It takes three arguments:
|
|
||||||
|
|
||||||
###### `fsevents.watch(dirname: string, (path: string, flags: number, id: string) => void): () => Promise<undefined>`
|
|
||||||
|
|
||||||
* `path: string` - the item in the filesystem that have been changed
|
|
||||||
* `flags: number` - a numeric value describing what the change was
|
|
||||||
* `id: string` - an unique-id identifying this specific event
|
|
||||||
|
|
||||||
Returns closer callback which when called returns a Promise resolving when the watcher process has been shut down.
|
|
||||||
|
|
||||||
###### `fsevents.getInfo(path: string, flags: number, id: string): FsEventInfo`
|
|
||||||
|
|
||||||
The `getInfo` function takes the `path`, `flags` and `id` arguments and converts those parameters into a structure
|
|
||||||
that is easier to digest to determine what the change was.
|
|
||||||
|
|
||||||
The `FsEventsInfo` has the following shape:
|
|
||||||
|
|
||||||
```js
|
|
||||||
/**
|
|
||||||
* @typedef {'created'|'modified'|'deleted'|'moved'|'root-changed'|'cloned'|'unknown'} FsEventsEvent
|
|
||||||
* @typedef {'file'|'directory'|'symlink'} FsEventsType
|
|
||||||
*/
|
|
||||||
{
|
|
||||||
"event": "created", // {FsEventsEvent}
|
|
||||||
"path": "file.txt",
|
|
||||||
"type": "file", // {FsEventsType}
|
|
||||||
"changes": {
|
|
||||||
"inode": true, // Had iNode Meta-Information changed
|
|
||||||
"finder": false, // Had Finder Meta-Data changed
|
|
||||||
"access": false, // Had access permissions changed
|
|
||||||
"xattrs": false // Had xAttributes changed
|
|
||||||
},
|
|
||||||
"flags": 0x100000000
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Changelog
|
|
||||||
|
|
||||||
- v2.3 supports Apple Silicon ARM CPUs
|
|
||||||
- v2 supports node 8.16+ and reduces package size massively
|
|
||||||
- v1.2.8 supports node 6+
|
|
||||||
- v1.2.7 supports node 4+
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
- I'm getting `EBADPLATFORM` `Unsupported platform for fsevents` error.
|
|
||||||
- It's fine, nothing is broken. fsevents is macos-only. Other platforms are skipped. If you want to hide this warning, report a bug to NPM bugtracker asking them to hide ebadplatform warnings by default.
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
The MIT License Copyright (C) 2010-2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller — see LICENSE file.
|
|
||||||
|
|
||||||
Visit our [GitHub page](https://github.com/fsevents/fsevents) and [NPM Page](https://npmjs.org/package/fsevents)
|
|
||||||
46
node_modules/vite/node_modules/fsevents/fsevents.d.ts
generated
vendored
46
node_modules/vite/node_modules/fsevents/fsevents.d.ts
generated
vendored
@ -1,46 +0,0 @@
|
|||||||
declare type Event = "created" | "cloned" | "modified" | "deleted" | "moved" | "root-changed" | "unknown";
|
|
||||||
declare type Type = "file" | "directory" | "symlink";
|
|
||||||
declare type FileChanges = {
|
|
||||||
inode: boolean;
|
|
||||||
finder: boolean;
|
|
||||||
access: boolean;
|
|
||||||
xattrs: boolean;
|
|
||||||
};
|
|
||||||
declare type Info = {
|
|
||||||
event: Event;
|
|
||||||
path: string;
|
|
||||||
type: Type;
|
|
||||||
changes: FileChanges;
|
|
||||||
flags: number;
|
|
||||||
};
|
|
||||||
declare type WatchHandler = (path: string, flags: number, id: string) => void;
|
|
||||||
export declare function watch(path: string, handler: WatchHandler): () => Promise<void>;
|
|
||||||
export declare function watch(path: string, since: number, handler: WatchHandler): () => Promise<void>;
|
|
||||||
export declare function getInfo(path: string, flags: number): Info;
|
|
||||||
export declare const constants: {
|
|
||||||
None: 0x00000000;
|
|
||||||
MustScanSubDirs: 0x00000001;
|
|
||||||
UserDropped: 0x00000002;
|
|
||||||
KernelDropped: 0x00000004;
|
|
||||||
EventIdsWrapped: 0x00000008;
|
|
||||||
HistoryDone: 0x00000010;
|
|
||||||
RootChanged: 0x00000020;
|
|
||||||
Mount: 0x00000040;
|
|
||||||
Unmount: 0x00000080;
|
|
||||||
ItemCreated: 0x00000100;
|
|
||||||
ItemRemoved: 0x00000200;
|
|
||||||
ItemInodeMetaMod: 0x00000400;
|
|
||||||
ItemRenamed: 0x00000800;
|
|
||||||
ItemModified: 0x00001000;
|
|
||||||
ItemFinderInfoMod: 0x00002000;
|
|
||||||
ItemChangeOwner: 0x00004000;
|
|
||||||
ItemXattrMod: 0x00008000;
|
|
||||||
ItemIsFile: 0x00010000;
|
|
||||||
ItemIsDir: 0x00020000;
|
|
||||||
ItemIsSymlink: 0x00040000;
|
|
||||||
ItemIsHardlink: 0x00100000;
|
|
||||||
ItemIsLastHardlink: 0x00200000;
|
|
||||||
OwnEvent: 0x00080000;
|
|
||||||
ItemCloned: 0x00400000;
|
|
||||||
};
|
|
||||||
export {};
|
|
||||||
83
node_modules/vite/node_modules/fsevents/fsevents.js
generated
vendored
83
node_modules/vite/node_modules/fsevents/fsevents.js
generated
vendored
@ -1,83 +0,0 @@
|
|||||||
/*
|
|
||||||
** © 2020 by Philipp Dunkel, Ben Noordhuis, Elan Shankar, Paul Miller
|
|
||||||
** Licensed under MIT License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* jshint node:true */
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
if (process.platform !== "darwin") {
|
|
||||||
throw new Error(`Module 'fsevents' is not compatible with platform '${process.platform}'`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Native = require("./fsevents.node");
|
|
||||||
const events = Native.constants;
|
|
||||||
|
|
||||||
function watch(path, since, handler) {
|
|
||||||
if (typeof path !== "string") {
|
|
||||||
throw new TypeError(`fsevents argument 1 must be a string and not a ${typeof path}`);
|
|
||||||
}
|
|
||||||
if ("function" === typeof since && "undefined" === typeof handler) {
|
|
||||||
handler = since;
|
|
||||||
since = Native.flags.SinceNow;
|
|
||||||
}
|
|
||||||
if (typeof since !== "number") {
|
|
||||||
throw new TypeError(`fsevents argument 2 must be a number and not a ${typeof since}`);
|
|
||||||
}
|
|
||||||
if (typeof handler !== "function") {
|
|
||||||
throw new TypeError(`fsevents argument 3 must be a function and not a ${typeof handler}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
let instance = Native.start(Native.global, path, since, handler);
|
|
||||||
if (!instance) throw new Error(`could not watch: ${path}`);
|
|
||||||
return () => {
|
|
||||||
const result = instance ? Promise.resolve(instance).then(Native.stop) : Promise.resolve(undefined);
|
|
||||||
instance = undefined;
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getInfo(path, flags) {
|
|
||||||
return {
|
|
||||||
path,
|
|
||||||
flags,
|
|
||||||
event: getEventType(flags),
|
|
||||||
type: getFileType(flags),
|
|
||||||
changes: getFileChanges(flags),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function getFileType(flags) {
|
|
||||||
if (events.ItemIsFile & flags) return "file";
|
|
||||||
if (events.ItemIsDir & flags) return "directory";
|
|
||||||
if (events.MustScanSubDirs & flags) return "directory";
|
|
||||||
if (events.ItemIsSymlink & flags) return "symlink";
|
|
||||||
}
|
|
||||||
function anyIsTrue(obj) {
|
|
||||||
for (let key in obj) {
|
|
||||||
if (obj[key]) return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function getEventType(flags) {
|
|
||||||
if (events.ItemRemoved & flags) return "deleted";
|
|
||||||
if (events.ItemRenamed & flags) return "moved";
|
|
||||||
if (events.ItemCreated & flags) return "created";
|
|
||||||
if (events.ItemModified & flags) return "modified";
|
|
||||||
if (events.RootChanged & flags) return "root-changed";
|
|
||||||
if (events.ItemCloned & flags) return "cloned";
|
|
||||||
if (anyIsTrue(flags)) return "modified";
|
|
||||||
return "unknown";
|
|
||||||
}
|
|
||||||
function getFileChanges(flags) {
|
|
||||||
return {
|
|
||||||
inode: !!(events.ItemInodeMetaMod & flags),
|
|
||||||
finder: !!(events.ItemFinderInfoMod & flags),
|
|
||||||
access: !!(events.ItemChangeOwner & flags),
|
|
||||||
xattrs: !!(events.ItemXattrMod & flags),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
exports.watch = watch;
|
|
||||||
exports.getInfo = getInfo;
|
|
||||||
exports.constants = events;
|
|
||||||
BIN
node_modules/vite/node_modules/fsevents/fsevents.node
generated
vendored
BIN
node_modules/vite/node_modules/fsevents/fsevents.node
generated
vendored
Binary file not shown.
62
node_modules/vite/node_modules/fsevents/package.json
generated
vendored
62
node_modules/vite/node_modules/fsevents/package.json
generated
vendored
@ -1,62 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "fsevents",
|
|
||||||
"version": "2.3.3",
|
|
||||||
"description": "Native Access to MacOS FSEvents",
|
|
||||||
"main": "fsevents.js",
|
|
||||||
"types": "fsevents.d.ts",
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"files": [
|
|
||||||
"fsevents.d.ts",
|
|
||||||
"fsevents.js",
|
|
||||||
"fsevents.node"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"clean": "node-gyp clean && rm -f fsevents.node",
|
|
||||||
"build": "node-gyp clean && rm -f fsevents.node && node-gyp rebuild && node-gyp clean",
|
|
||||||
"test": "/bin/bash ./test.sh 2>/dev/null",
|
|
||||||
"prepublishOnly": "npm run build"
|
|
||||||
},
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/fsevents/fsevents.git"
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"fsevents",
|
|
||||||
"mac"
|
|
||||||
],
|
|
||||||
"contributors": [
|
|
||||||
{
|
|
||||||
"name": "Philipp Dunkel",
|
|
||||||
"email": "pip@pipobscure.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Ben Noordhuis",
|
|
||||||
"email": "info@bnoordhuis.nl"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Elan Shankar",
|
|
||||||
"email": "elan.shanker@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Miroslav Bajtoš",
|
|
||||||
"email": "mbajtoss@gmail.com"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Paul Miller",
|
|
||||||
"url": "https://paulmillr.com"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/fsevents/fsevents/issues"
|
|
||||||
},
|
|
||||||
"homepage": "https://github.com/fsevents/fsevents",
|
|
||||||
"devDependencies": {
|
|
||||||
"node-gyp": "^9.4.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "@web/base",
|
"name": "@web/base",
|
||||||
"version": "1.0.2",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "@web/base",
|
"name": "@web/base",
|
||||||
"version": "1.0.2",
|
"version": "1.0.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.40.0",
|
"@playwright/test": "^1.40.0",
|
||||||
"@rollup/plugin-terser": "^1.0.0",
|
"@rollup/plugin-terser": "^1.0.0",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@web/base",
|
"name": "@web/base",
|
||||||
"version": "1.0.2",
|
"version": "1.0.1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/base.js",
|
"main": "dist/base.js",
|
||||||
"module": "dist/base.js",
|
"module": "dist/base.js",
|
||||||
|
|||||||
64
src/form.js
64
src/form.js
@ -4,28 +4,10 @@ import { HTTP } from './http.js'
|
|||||||
Component.register('AutoForm', container => {
|
Component.register('AutoForm', container => {
|
||||||
if (!container.state.schema) container.state.schema = []
|
if (!container.state.schema) container.state.schema = []
|
||||||
container.vertical = container.hasAttribute('vertical')
|
container.vertical = container.hasAttribute('vertical')
|
||||||
container.inline = container.hasAttribute('inline')
|
|
||||||
container.request = { method: 'POST' }
|
container.request = { method: 'POST' }
|
||||||
container.response = {}
|
container.response = {}
|
||||||
container.result = null
|
container.result = null
|
||||||
|
|
||||||
// 初始化数据代理与联动逻辑
|
|
||||||
const setupData = () => {
|
|
||||||
if (!container.data || !container.data.__watch) {
|
|
||||||
container.data = NewState(container.data || {})
|
container.data = NewState(container.data || {})
|
||||||
}
|
|
||||||
container.data.__watch('*', () => {
|
|
||||||
if (container.inline) {
|
|
||||||
const dt = container.closest('DataTable')
|
|
||||||
if (dt && dt.refresh) dt.refresh()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果 data 已经存在(可能是同步绑定的),立即设置;否则延迟一帧等待属性绑定完成
|
|
||||||
if (container.data) setupData()
|
|
||||||
else requestAnimationFrame(setupData)
|
|
||||||
|
|
||||||
container.form = $(container, 'form')
|
container.form = $(container, 'form')
|
||||||
container.submit = (opt = {}) => {
|
container.submit = (opt = {}) => {
|
||||||
if (!container.form.reportValidity()) return globalThis.UI?.toast?.('{#verify failed#}', { type: 'danger' })
|
if (!container.form.reportValidity()) return globalThis.UI?.toast?.('{#verify failed#}', { type: 'danger' })
|
||||||
@ -46,42 +28,33 @@ Component.register('AutoForm', container => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, Util.makeDom(/*html*/`
|
}, Util.makeDom(/*html*/`
|
||||||
<div $class="'auto-form-root' + (this.inline ? ' auto-form-inline min-h-100 w-100' : '')">
|
<div>
|
||||||
<form $class="'h-100 w-100 d-flex ' + (this.inline ? 'align-items-center' : (this.vertical ? 'flex-column' : 'auto-grid-form'))" $onsubmit="event.stopPropagation();event.preventDefault();this.submit()">
|
<form $class="align-items-center \${this.vertical?'':'auto-grid-form'}" $onsubmit="event.stopPropagation();event.preventDefault();this.submit()">
|
||||||
<div $each="this.state.schema || []" style="display:contents">
|
<div $each="this.state.schema || []" style="display:contents">
|
||||||
<label $if="!this.inline" $name="item.name" class="col-form-label text-muted" $text="item.label"></label>
|
<label $name="item.name" class="col-form-label text-muted" $text="item.label"></label>
|
||||||
<div control-wrapper $class="this.inline ? 'flex-grow-1 h-100 d-flex align-items-center' : 'mb-3'">
|
<div control-wrapper class="mb-3">
|
||||||
<input $if="['text', 'password', 'email', 'number', 'date', 'datetime', 'file'].includes(item.type)" $name="item.name" class="form-control" $type="item.type" $.="item.setting || {}" $bind="this.data[item.name]" $class="item.type === 'number' ? 'text-end' : ''">
|
<input $if="['text', 'password', 'email', 'number', 'date', 'datetime', 'file'].includes(item.type)" $name="item.name" class="form-control" $type="item.type" $.="item.setting" $bind="this.data[item.name]">
|
||||||
<select $if="item.type === 'select'" $name="item.name" class="form-select" $.="item.setting || {}" $bind="this.data[item.name]">
|
<select $if="item.type === 'select'" $name="item.name" class="form-select" $.="item.setting" $bind="this.data[item.name]">
|
||||||
<option value="" $if="item.placeholder" $text="item.placeholder" disabled></option>
|
<option value="" $if="item.placeholder" $text="item.placeholder" disabled selected></option>
|
||||||
<option $each="item.options" $value="item.value || item" $text="item.label || item"></option>
|
<option $each="item.options" $value="item.value || item" $text="item.label || item"></option>
|
||||||
</select>
|
</select>
|
||||||
<div $if="['checkbox', 'radio'].includes(item.type)" $class="this.inline ? 'h-100 d-flex align-items-center px-2 gap-3 justify-content-center w-100' : ''" style="white-space:nowrap">
|
<div $if="['checkbox', 'radio'].includes(item.type)" >
|
||||||
<label $each="item.options || [item.text||item.label||item.name]" as="option" $class="'form-check' + (item.vertical ? '' : ' form-check-inline') + ' mb-0 d-flex align-items-center p-0'">
|
<label $each="item.options || [item.text||item.label||item.name]" as="option" $class="form-check\${item.vertical ? '' : ' form-check-inline'}">
|
||||||
<input $name="item.name" class="form-check-input me-1" $type="item.type" $.="item.setting || {}" $value="item.options?option:'on'" $bind="this.data[item.name]">
|
<input $name="item.name" class="form-check-input" $type="item.type" $.="item.setting" $value="item.options?option:'on'" $bind="this.data[item.name]">
|
||||||
<span $if="!this.inline || (item.options && item.options.length > 0)" $text="option" class="form-check-label"></span>
|
<span $text="option" class="form-check-label"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div $if="item.type==='switch'" class="form-check form-switch fs-4 h-100 d-flex align-items-center px-2 m-0 justify-content-center w-100" style="padding-left:0">
|
<div $if="item.type==='switch'" class="form-check form-switch fs-4"><input $name="item.name" class="form-check-input my-0" type="checkbox" $bind="this.data[item.name]"></div>
|
||||||
<input $name="item.name" class="form-check-input m-0" type="checkbox" style="cursor:pointer" $bind="this.data[item.name]">
|
<textarea $if="item.type==='textarea'" $name="item.name" class="form-control" $.="item.setting" $bind="this.data[item.name]"></textarea>
|
||||||
</div>
|
|
||||||
<textarea $if="item.type==='textarea'" $name="item.name" class="form-control" $.="item.setting || {}" $bind="this.data[item.name]"></textarea>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div $if="!this.inline" class="d-flex justify-content-end align-items-baseline gap-3 mt-3" style="grid-column:1/-1">
|
<div class="d-flex justify-content-end align-items-baseline gap-3 mt-3" style="grid-column:1/-1">
|
||||||
<div slot-id="actions"></div>
|
<div slot-id="actions"></div>
|
||||||
<button type="submit" class="btn btn-primary" $text="this.submitlabel || '{#Submit#}'"></button>
|
<button type="submit" class="btn btn-primary" $text="this.submitlabel || '{#Submit#}'"></button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
`), Util.makeDom(/*html*/`<style>
|
`), Util.makeDom(/*html*/`<style>@media (min-width: 576px) { .auto-grid-form {display: grid;grid-template-columns: max-content 1fr} .auto-grid-form .col-form-label {text-align: right; margin-bottom: 1rem;padding-right: 1rem;max-width: 200px} }</style>`))
|
||||||
@media (min-width: 576px) { .auto-grid-form {display: grid;grid-template-columns: max-content 1fr} .auto-grid-form .col-form-label {text-align: right; margin-bottom: 1rem;padding-right: 1rem;max-width: 200px} }
|
|
||||||
.auto-form-inline { background-color: var(--bs-body-bg); }
|
|
||||||
.auto-form-inline.min-h-100 { min-height: 100%; height: auto; }
|
|
||||||
.auto-form-inline .form-control, .auto-form-inline .form-select { border-radius: 0; border: none; background-color: transparent !important; min-height: 100%; height: auto; width: 100%; padding: 0 8px; box-shadow: none; color: inherit; }
|
|
||||||
.auto-form-inline .form-select { background-position: right 4px center; background-size: 12px 10px; padding-right: 20px; }
|
|
||||||
.auto-form-inline textarea.form-control { padding: 8px; min-height: 100px; height: auto; }
|
|
||||||
</style>`))
|
|
||||||
|
|
||||||
const _pendingAutoFormComponents = []
|
const _pendingAutoFormComponents = []
|
||||||
export const AutoForm = {
|
export const AutoForm = {
|
||||||
@ -94,7 +67,10 @@ export const AutoForm = {
|
|||||||
_addAutoFormComponent: name => {
|
_addAutoFormComponent: name => {
|
||||||
const template = Component.getTemplate('AutoForm')
|
const template = Component.getTemplate('AutoForm')
|
||||||
if (template) {
|
if (template) {
|
||||||
$(template.content, '[control-wrapper]')?.appendChild(Util.makeDom(`<${name} $if="item.type.toUpperCase() === '${name.toUpperCase()}'" $name="item.name" $.="item.setting || {}" $bind="this.data[item.name]"></${name}>`))
|
$(template.content, '[control-wrapper]')?.appendChild(Util.makeDom(`<${name} $if="item.type.toUpperCase() === '${name.toUpperCase()}'" $name="item.name" $.="item.setting" $bind="this.data[item.name]"></${name}>`))
|
||||||
|
} else if (document.readyState !== 'loading') {
|
||||||
|
// If template still missing after DOM ready, something is wrong
|
||||||
|
console.error('AutoForm template not found during registration of', name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,7 +80,7 @@ if (typeof document !== 'undefined') {
|
|||||||
_pendingAutoFormComponents.forEach(name => AutoForm._addAutoFormComponent(name))
|
_pendingAutoFormComponents.forEach(name => AutoForm._addAutoFormComponent(name))
|
||||||
_pendingAutoFormComponents.length = 0
|
_pendingAutoFormComponents.length = 0
|
||||||
}
|
}
|
||||||
if (document.readyState !== 'loading') setTimeout(initAutoForm, 100)
|
if (document.readyState !== 'loading') setTimeout(initAutoForm, 100) // Wait slightly for component templates to be added to body
|
||||||
else document.addEventListener('DOMContentLoaded', () => setTimeout(initAutoForm, 100), true)
|
else document.addEventListener('DOMContentLoaded', () => setTimeout(initAutoForm, 100), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -28,32 +28,19 @@ if (typeof document !== 'undefined') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Component.register('Resizer', container => {
|
Component.register('Resizer', container => {
|
||||||
container.isVertical = container.hasAttribute('vertical')
|
const isVertical = container.hasAttribute('vertical')
|
||||||
const min = parseInt(container.getAttribute('min')) || 10
|
const min = parseInt(container.getAttribute('min')) || 10
|
||||||
const max = parseInt(container.getAttribute('max')) || 1000
|
const max = parseInt(container.getAttribute('max')) || 1000
|
||||||
const target = container.target || container.previousElementSibling
|
const target = container.target || container.previousElementSibling
|
||||||
container.addEventListener('bind', e => {
|
|
||||||
if (e.detail !== undefined && e.detail !== null) {
|
|
||||||
target.style[container.isVertical ? 'height' : 'width'] = e.detail + 'px'
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const getSize = (startSize, w, h) => {
|
const getSize = (startSize, w, h) => {
|
||||||
const newSize = startSize + (container.isVertical ? h : w)
|
const newSize = startSize + (isVertical ? h : w)
|
||||||
return newSize < min ? min : newSize > max ? max : newSize
|
return newSize < min ? min : newSize > max ? max : newSize
|
||||||
}
|
}
|
||||||
container.addEventListener('mousedown', event => {
|
container.addEventListener('mousedown', event => {
|
||||||
const startSize = container.isVertical ? target.offsetHeight : target.offsetWidth
|
const startSize = isVertical ? target.offsetHeight : target.offsetWidth
|
||||||
MouseMover.start(event, {
|
MouseMover.start(event, {
|
||||||
onmousemove: ({ w, h }) => {
|
onmousemove: ({ w, h }) => target.style[isVertical ? 'height' : 'width'] = getSize(startSize, w, h) + 'px',
|
||||||
const newSize = getSize(startSize, w, h)
|
onmouseup: ({ w, h }) => container.dispatchEvent(new CustomEvent('resize', { detail: { oldSize: startSize, newSize: getSize(startSize, w, h) }, bubbles: false })),
|
||||||
target.style[container.isVertical ? 'height' : 'width'] = newSize + 'px'
|
|
||||||
container.dispatchEvent(new CustomEvent('resizing', { detail: { oldSize: startSize, newSize }, bubbles: false }))
|
|
||||||
},
|
|
||||||
onmouseup: ({ w, h }) => {
|
|
||||||
const newSize = getSize(startSize, w, h)
|
|
||||||
container.dispatchEvent(new CustomEvent('resize', { detail: { oldSize: startSize, newSize }, bubbles: false }))
|
|
||||||
container.dispatchEvent(new CustomEvent('change', { detail: newSize, bubbles: false }))
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}, Util.makeDom(/*html*/`
|
}, Util.makeDom(/*html*/`
|
||||||
|
|||||||
163
src/list.js
163
src/list.js
@ -1,63 +1,23 @@
|
|||||||
import { Component, NewState, Util, Hash } from '@web/state'
|
import { Component, NewState, Util, Hash } from '@web/state'
|
||||||
|
|
||||||
export const VirtualScroll = () => {
|
export const FastListComponent = Component.register('FastList', container => {
|
||||||
const itemHeights = new Map()
|
const itemHeights = new Map()
|
||||||
const groupHeights = new Map()
|
const groupHeights = new Map()
|
||||||
let groupItemCount = 1
|
let groupItemCount = 1
|
||||||
|
container.state.renderedList = []
|
||||||
const avg = Util.newAvg()
|
const avg = Util.newAvg()
|
||||||
|
let containerPaddingTop = 0
|
||||||
let padTop = 0
|
let containerRowGap = 0
|
||||||
let rowGap = 0
|
|
||||||
let topMargin = 0
|
let topMargin = 0
|
||||||
let itemMarginTop = null
|
let itemMarginTop = null
|
||||||
let itemMarginBottom = null
|
let itemMarginBottom = null
|
||||||
|
let listStartIndex = 0
|
||||||
|
let visibleStartIndex = 0
|
||||||
|
let visibleCount = 10
|
||||||
let listInited = false
|
let listInited = false
|
||||||
|
|
||||||
return {
|
container.onItemUpdate = (index, node) => {
|
||||||
// 核心1:重置与首屏预渲染
|
const absoluteIndex = index + listStartIndex
|
||||||
reset: (list, container) => {
|
|
||||||
listInited = false
|
|
||||||
itemHeights.clear()
|
|
||||||
groupHeights.clear()
|
|
||||||
avg.clear()
|
|
||||||
topMargin = 0
|
|
||||||
itemMarginTop = null
|
|
||||||
itemMarginBottom = null
|
|
||||||
|
|
||||||
if (!list?.length) return []
|
|
||||||
|
|
||||||
const size = list.length
|
|
||||||
groupItemCount = Math.ceil(Math.sqrt(size)) || 10
|
|
||||||
|
|
||||||
const style = window.getComputedStyle(container)
|
|
||||||
padTop = parseFloat(style.paddingTop) || 0
|
|
||||||
rowGap = parseFloat(style.rowGap) || 0
|
|
||||||
|
|
||||||
return list.slice(0, Math.min(30, size))
|
|
||||||
},
|
|
||||||
|
|
||||||
// 核心2:无痛初始化缓存 (通过 rAF 调用)
|
|
||||||
init: (list, refreshCallback) => {
|
|
||||||
if (listInited) return
|
|
||||||
const size = list.length
|
|
||||||
const defaultHeight = avg.get() || 32
|
|
||||||
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
if (!itemHeights.has(i)) itemHeights.set(i, defaultHeight)
|
|
||||||
}
|
|
||||||
for (let i = 0; i < size; i += groupItemCount) {
|
|
||||||
let sum = 0
|
|
||||||
for (let j = i; j < Math.min(i + groupItemCount, size); j++) {
|
|
||||||
sum += itemHeights.get(j)
|
|
||||||
}
|
|
||||||
groupHeights.set(i, sum)
|
|
||||||
}
|
|
||||||
listInited = true
|
|
||||||
refreshCallback()
|
|
||||||
},
|
|
||||||
|
|
||||||
// 核心3:原汁原味的极速高度更新
|
|
||||||
update: (absoluteIndex, node) => {
|
|
||||||
if (itemMarginTop === null) {
|
if (itemMarginTop === null) {
|
||||||
const style = window.getComputedStyle(node)
|
const style = window.getComputedStyle(node)
|
||||||
itemMarginTop = parseFloat(style.marginTop) || 0
|
itemMarginTop = parseFloat(style.marginTop) || 0
|
||||||
@ -65,9 +25,8 @@ export const VirtualScroll = () => {
|
|||||||
}
|
}
|
||||||
if (absoluteIndex === 0) topMargin = itemMarginTop
|
if (absoluteIndex === 0) topMargin = itemMarginTop
|
||||||
|
|
||||||
const newHeight = node.offsetHeight + itemMarginTop + itemMarginBottom + rowGap
|
const newHeight = node.offsetHeight + itemMarginTop + itemMarginBottom + containerRowGap
|
||||||
const oldHeight = itemHeights.get(absoluteIndex)
|
const oldHeight = itemHeights.get(absoluteIndex)
|
||||||
|
|
||||||
if (newHeight !== oldHeight) {
|
if (newHeight !== oldHeight) {
|
||||||
itemHeights.set(absoluteIndex, newHeight)
|
itemHeights.set(absoluteIndex, newHeight)
|
||||||
avg.add(newHeight)
|
avg.add(newHeight)
|
||||||
@ -75,41 +34,39 @@ export const VirtualScroll = () => {
|
|||||||
const groupIndex = absoluteIndex - (absoluteIndex % groupItemCount)
|
const groupIndex = absoluteIndex - (absoluteIndex % groupItemCount)
|
||||||
if (groupHeights.has(groupIndex)) groupHeights.set(groupIndex, groupHeights.get(groupIndex) + offset)
|
if (groupHeights.has(groupIndex)) groupHeights.set(groupIndex, groupHeights.get(groupIndex) + offset)
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// 核心4:原汁原味的状态机滚动计算
|
container.refresh = () => {
|
||||||
calc: (container, list) => {
|
if (!listInited) return
|
||||||
if (!listInited || !list) return null
|
visibleCount = Math.ceil((container.clientHeight || 100) / (avg.get() || 32))
|
||||||
|
let list = []
|
||||||
const size = list.length
|
const fullList = container.state.list
|
||||||
const visibleCount = Math.ceil((container.clientHeight || 100) / (avg.get() || 32))
|
const fullSize = fullList.length
|
||||||
|
let i = 0
|
||||||
let prev = padTop + topMargin + rowGap
|
let prev = containerPaddingTop + topMargin + containerRowGap
|
||||||
let post = 0
|
let post = 0
|
||||||
let status = 0
|
let starus = 0
|
||||||
let listStartIndex = 0
|
for (i = 0; i < fullSize; i++) {
|
||||||
let listEndIndex = 0
|
if (starus === 0) {
|
||||||
|
|
||||||
for (let i = 0; i < size; i++) {
|
|
||||||
if (status === 0) {
|
|
||||||
const gh = groupHeights.get(i)
|
const gh = groupHeights.get(i)
|
||||||
if (gh && prev + gh < container.scrollTop) {
|
if (gh && prev + gh < container.scrollTop) {
|
||||||
prev += gh
|
prev += gh
|
||||||
i += Math.min(groupItemCount, size - i) - 1
|
i += Math.min(groupItemCount, fullSize - i) - 1
|
||||||
} else {
|
} else {
|
||||||
const ih = itemHeights.get(i)
|
const ih = itemHeights.get(i)
|
||||||
if (prev + ih < container.scrollTop) {
|
if (prev + ih < container.scrollTop) {
|
||||||
prev += ih
|
prev += ih
|
||||||
} else {
|
} else {
|
||||||
status = 1
|
starus = 1
|
||||||
let visibleStartIndex = Math.max(0, i)
|
visibleStartIndex = Math.max(0, i)
|
||||||
listStartIndex = Math.max(0, visibleStartIndex - visibleCount)
|
listStartIndex = Math.max(0, visibleStartIndex - visibleCount)
|
||||||
listEndIndex = Math.min(listStartIndex + visibleCount * 3, size)
|
const listEndIndex = Math.min(listStartIndex + visibleCount * 3, fullSize)
|
||||||
i = listEndIndex - 1
|
i = listEndIndex - 1
|
||||||
|
list = fullList.slice(listStartIndex, listEndIndex)
|
||||||
for (let j = listStartIndex; j < visibleStartIndex; j++) prev -= itemHeights.get(j)
|
for (let j = listStartIndex; j < visibleStartIndex; j++) prev -= itemHeights.get(j)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (status === 1) {
|
} else if (starus === 1) {
|
||||||
const gh = groupHeights.get(i)
|
const gh = groupHeights.get(i)
|
||||||
if (gh) {
|
if (gh) {
|
||||||
post += gh
|
post += gh
|
||||||
@ -119,42 +76,42 @@ export const VirtualScroll = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
container.state.prevHeight = prev - containerPaddingTop - topMargin - containerRowGap
|
||||||
return {
|
container.state.postHeight = post
|
||||||
prevHeight: prev - padTop - topMargin - rowGap,
|
container.state.renderedList = list
|
||||||
postHeight: post,
|
|
||||||
renderedList: list.slice(listStartIndex, listEndIndex),
|
|
||||||
listStartIndex
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const FastListComponent = Component.register('FastList', container => {
|
|
||||||
const vs = VirtualScroll()
|
|
||||||
container.state.renderedList = []
|
|
||||||
|
|
||||||
container.onItemUpdate = (index, node) => {
|
|
||||||
vs.update(index + (container.state._listStartIndex || 0), node)
|
|
||||||
}
|
|
||||||
|
|
||||||
container.refresh = () => {
|
|
||||||
const res = vs.calc(container, container.state.list)
|
|
||||||
if (res) {
|
|
||||||
container.state.prevHeight = res.prevHeight
|
|
||||||
container.state.postHeight = res.postHeight
|
|
||||||
container.state._listStartIndex = res.listStartIndex
|
|
||||||
container.state.renderedList = res.renderedList
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
container.state.__watch('list', list => {
|
container.state.__watch('list', list => {
|
||||||
container.state._listStartIndex = 0
|
listInited = false
|
||||||
container.state.renderedList = vs.reset(list, container) || []
|
itemHeights.clear()
|
||||||
|
groupHeights.clear()
|
||||||
|
avg.clear()
|
||||||
|
topMargin = 0
|
||||||
|
itemMarginTop = null
|
||||||
|
itemMarginBottom = null
|
||||||
|
if (!list?.length) return
|
||||||
|
|
||||||
|
const listSize = list.length || 0
|
||||||
|
groupItemCount = Math.ceil(Math.sqrt(listSize)) || 10
|
||||||
|
const style = window.getComputedStyle(container)
|
||||||
|
containerPaddingTop = parseFloat(style.paddingTop) || 0
|
||||||
|
containerRowGap = parseFloat(style.rowGap) || 0
|
||||||
|
container.state.renderedList = list.slice(0, Math.min(30, listSize)) || []
|
||||||
|
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
if (container.state.list !== list) return
|
if (listInited || container.state.list !== list) return
|
||||||
vs.init(list, container.refresh)
|
const defaultHeight = avg.get() || 32
|
||||||
|
for (let i = 0; i < listSize; i++) {
|
||||||
|
if (!itemHeights.has(i)) itemHeights.set(i, defaultHeight)
|
||||||
|
}
|
||||||
|
for (let i = 0; i < listSize; i += groupItemCount) {
|
||||||
|
let sum = 0
|
||||||
|
for (let j = i; j < Math.min(i + groupItemCount, listSize); j++) {
|
||||||
|
sum += itemHeights.get(j)
|
||||||
|
}
|
||||||
|
groupHeights.set(i, sum)
|
||||||
|
}
|
||||||
|
listInited = true
|
||||||
|
container.refresh()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ Component.register('Modal', container => {
|
|||||||
}, Util.makeDom(/*html*/`
|
}, Util.makeDom(/*html*/`
|
||||||
<div class="modal fade" data-bs-backdrop="static">
|
<div class="modal fade" data-bs-backdrop="static">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div $class="modal-content text-bg-\${this.state?.type || 'primary'}">
|
<div $class="modal-content text-bg-\${this.state?.type || 'body'}">
|
||||||
<div slot-id="header" class="modal-header">
|
<div slot-id="header" class="modal-header">
|
||||||
<h6 class="modal-title" $text="this.state?.title"></h6>
|
<h6 class="modal-title" $text="this.state?.title"></h6>
|
||||||
<button type="button" class="btn btn-link ms-2 bi bi-x-lg link-reset" style="color:inherit" data-bs-dismiss="modal"></button>
|
<button type="button" class="btn btn-link ms-2 bi bi-x-lg link-reset" style="color:inherit" data-bs-dismiss="modal"></button>
|
||||||
@ -30,7 +30,7 @@ Component.register('Modal', container => {
|
|||||||
Component.register('Dialog', Component.getSetupFunction('Modal'), Util.makeDom(/*html*/`
|
Component.register('Dialog', Component.getSetupFunction('Modal'), Util.makeDom(/*html*/`
|
||||||
<div class="modal fade" data-bs-backdrop="static">
|
<div class="modal fade" data-bs-backdrop="static">
|
||||||
<div class="modal-dialog modal-dialog-centered">
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
<div $class="modal-content text-bg-\${this.state?.type || 'primary'}">
|
<div $class="modal-content text-bg-\${this.state?.type || 'body'}">
|
||||||
<div $if="this.state?.title" class="modal-header" $text="this.state?.title"></div>
|
<div $if="this.state?.title" class="modal-header" $text="this.state?.title"></div>
|
||||||
<div slot-id="body" class="modal-body"><div $html="this.state?.message"></div></div>
|
<div slot-id="body" class="modal-body"><div $html="this.state?.message"></div></div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
@ -84,7 +84,7 @@ Component.register('Toast', container => {
|
|||||||
})
|
})
|
||||||
}, Util.makeDom(/*html*/`
|
}, Util.makeDom(/*html*/`
|
||||||
<div class="toast align-items-center border-0 m-1">
|
<div class="toast align-items-center border-0 m-1">
|
||||||
<div $class="toast-body rounded p-3 text-bg-\${this.state?.type}">
|
<div $class="toast-body p-3 text-bg-\${this.state?.type}">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<span style="white-space:pre-wrap" class="fs-6" $text="this.state?.message"></span>
|
<span style="white-space:pre-wrap" class="fs-6" $text="this.state?.message"></span>
|
||||||
@ -102,7 +102,7 @@ Component.register('Toast', container => {
|
|||||||
UI.toast = function (message, options = {}) {
|
UI.toast = function (message, options = {}) {
|
||||||
const delay = options.delay ?? 5000
|
const delay = options.delay ?? 5000
|
||||||
const t = document.createElement('Toast')
|
const t = document.createElement('Toast')
|
||||||
t.state = { delay, left: delay ? delay / 1000 : undefined, type: options.type || 'primary', message, buttons: options.buttons || [] }
|
t.state = { delay, left: delay ? delay / 1000 : undefined, type: options.type || 'body', message, buttons: options.buttons || [] }
|
||||||
$(`[toast-container="${options.container || 'default'}"]`).appendChild(t)
|
$(`[toast-container="${options.container || 'default'}"]`).appendChild(t)
|
||||||
Promise.resolve().then(() => t.show())
|
Promise.resolve().then(() => t.show())
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user