import { Component, NewState, Util, $, Hash, RefreshState } from "@web/state"; const HTTP = { get: ({ url, ...opt }) => HTTP.request({ url, method: "GET", ...opt }), post: ({ url, data, ...opt }) => HTTP.request({ url, method: "POST", data, ...opt }), put: ({ url, data, ...opt }) => HTTP.request({ url, method: "PUT", data, ...opt }), delete: ({ url, ...opt }) => HTTP.request({ url, method: "DELETE", ...opt }), head: ({ url, ...opt }) => HTTP.request({ url, method: "HEAD", ...opt }), request: async ({ url, method = "POST", data = void 0, headers = {}, responseType, timeout = 1e4 }) => { var _a; method = method.toUpperCase(); const options = { method, signal: (_a = AbortSignal.timeout) == null ? void 0 : _a.call(AbortSignal, timeout) }; if (data !== void 0 && method !== "GET" && method !== "HEAD") { if (data instanceof HTMLFormElement) data = new FormData(data); if (data && typeof data === "object" && !(data instanceof FormData) && !(data instanceof ArrayBuffer || ArrayBuffer.isView(data)) && Object.values(data).some((v) => v instanceof File || v instanceof Blob || v instanceof FileList || Array.isArray(v) && v.some((i) => i instanceof File || i instanceof Blob))) { const fd = new FormData(); for (const [k, v] of Object.entries(data)) { if (v instanceof FileList || Array.isArray(v)) Array.from(v).forEach((item) => fd.append(k, item)); else if (v !== void 0 && v !== null) fd.append(k, v); } data = fd; } if (data instanceof FormData) { delete headers["Content-Type"]; } else if (typeof data !== "string" && !(data instanceof ArrayBuffer || ArrayBuffer.isView(data))) { data = JSON.stringify(data); if (!headers["Content-Type"]) headers["Content-Type"] = "application/json"; } options.body = data; } if (Object.keys(headers).length) options.headers = headers; const response = { error: null, ok: null, status: 0, headers: {}, responseType: "", result: null }; try { const resp = await fetch(url, options); Object.assign(response, { ok: resp.ok, status: resp.status, headers: Object.fromEntries(resp.headers.entries()) }); if (!responseType) { const contentType = resp.headers.get("Content-Type") || ""; if (contentType.includes("application/json")) responseType = "json"; else if (/image|video|audio|pdf|zip|octet-stream/.test(contentType)) responseType = "binary"; else responseType = "text"; response.responseType = responseType; } if (response.ok === false) response.error = (response.statusText || "HTTP " + response.status + " error") + " for " + url; if (responseType === "json") response.result = await resp.json(); else response.result = responseType === "binary" ? await resp.arrayBuffer() : await resp.text(); } catch (err) { Object.assign(response, { error: err.message || String(err), ok: false }); } return response; } }; const APIComponent = Component.register("API", (container) => { container.request = NewState({ url: "", method: "GET", headers: {}, data: null, timeout: 1e4, responseType: "" }); container.response = NewState({ loading: false, ok: null, status: null, error: null, headers: {}, responseType: "", result: null }); container.result = NewState(); container.do = (opt = {}) => { return new Promise((resolve, reject) => { const req = { ...container.request, ...opt }; if (!req.url) throw new Error(".url is required"); req.headers = { ...container.request.headers, ...opt.headers }; container.response.loading = true; HTTP.request(req).then((resp) => { Object.keys(resp).forEach((k) => { if (k !== "result") container.response[k] = resp[k]; }); if (resp.result && typeof resp.result === "object" && container.result && typeof container.result === "object") { Object.assign(container.result, resp.result); } else { container.result = resp.result; } container.response.loading = false; if (resp.ok === false) throw new Error(resp.error); if (typeof resp.result === "object" && resp.result.error) throw new Error(resp.result.error); container.dispatchEvent(new CustomEvent("response", { detail: resp, bubbles: false })); resolve(resp); }).catch((err) => { var _a; if (!opt.noui && ((_a = globalThis.UI) == null ? void 0 : _a.toast)) UI.toast(err.message, { type: "danger" }); container.dispatchEvent(new CustomEvent("error", { detail: err, bubbles: true })); reject(err); }); }); }; let _autoTimer = null; container.request.__watch(null, () => { if (!container.hasAttribute("auto") || !container.request.url) return; if (_autoTimer) return; _autoTimer = Promise.resolve().then(() => { container.do(); _autoTimer = null; }); }); }); const UI$1 = {}; Component.register("Modal", (container) => { container.modal = new bootstrap.Modal(container); container.addEventListener("bind", (e) => { e.detail ? container.modal.show() : container.modal.hide(); }); container.addEventListener("hide.bs.modal", () => { var _a; (_a = document.activeElement) == null ? void 0 : _a.blur(); container.dispatchEvent(new CustomEvent("change", { bubbles: false, detail: false })); }); Util.copyFunction(container, container.modal, "show", "hide"); }, Util.makeDom( /*html*/ ` ` )); Component.register("Dialog", Component.getSetupFunction("Modal"), Util.makeDom( /*html*/ ` ` )); let _dialogCount = 0; UI$1.showDialog = function({ title = "", message = "", buttons = ["{#Close#}"], type = "body" }) { const d = document.body.appendChild(document.createElement("Dialog")); d.style.zIndex = 2e3 + ++_dialogCount; Promise.resolve().then(() => { Object.assign(d.state, { message, title, type, buttons }); d.show(); }); return new Promise((resolve) => { d.addEventListener("change", (e) => { _dialogCount--; resolve(d.result || 0); d.remove(); }); }); }; UI$1.alert = function(message, options = {}) { return UI$1.showDialog({ message, ...options }); }; UI$1.confirm = function(message, options = {}) { return new Promise((resolve) => UI$1.showDialog({ message, buttons: ["{#Cancel#}", "{#Confirm#}"], ...options }).then((index2) => resolve(index2 >= 2)).catch(() => resolve(false))); }; Component.register("Toast", (container) => { container.toast = new bootstrap.Toast(container, { autohide: container.state.delay > 0 }); Util.copyFunction(container, container.toast, "show", "hide"); container.addEventListener("show.bs.toast", () => { if (container.state.delay > 0) { let timer; const startTimer = () => { container.state.left = container.state.delay / 1e3; timer = setInterval(() => { if (!container.isConnected || --container.state.left <= 0) clearInterval(timer); }, 1e3); }; startTimer(); container.addEventListener("mouseenter", () => { clearInterval(timer); container.state.left = void 0; }); container.addEventListener("mouseleave", startTimer); } }); }, Util.makeDom( /*html*/ `
` ), Util.makeDom( /*html*/ `
` )); UI$1.toast = function(message, options = {}) { const delay = options.delay ?? 5e3; const t = document.createElement("Toast"); t.state = { delay, left: delay ? delay / 1e3 : void 0, type: options.type || "primary", message, buttons: options.buttons || [] }; $(`[toast-container="${options.container || "default"}"]`).appendChild(t); Promise.resolve().then(() => t.show()); }; UI$1.toastConfirm = function(message, options = {}) { return new Promise((resolve) => UI$1.toast(message, { buttons: ["{#Confirm#}"], ...options }).then((index2) => resolve(index2 === 1)).catch(() => resolve(false))); }; Component.register("AutoForm", (container) => { if (!container.state.schema) container.state.schema = []; container.vertical = container.hasAttribute("vertical"); container.inline = container.hasAttribute("inline"); container.request = { method: "POST" }; container.response = {}; container.result = null; const setupData = () => { if (!container.data || !container.data.__watch) { 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.submit = (opt = {}) => { var _a, _b; if (!container.form.reportValidity()) return (_b = (_a = globalThis.UI) == null ? void 0 : _a.toast) == null ? void 0 : _b.call(_a, "{#verify failed#}", { type: "danger" }); if (!container.dispatchEvent(new CustomEvent("submit", { detail: container.data, cancelable: true, bubbles: false }))) return; const req = { ...container.request, data: container.data, noui: true, ...opt }; let task = null; if (container.api) task = container.api.do(req); else if (container.request.url) task = HTTP.request(req); else return console.warn("{#please config .api or .request.url to auto submit#}"); task.then((resp) => { container.response = resp; container.result = resp.result; if (typeof resp.result === "object" && resp.result.error) throw new Error(resp.result.error); container.dispatchEvent(new CustomEvent("response", { detail: resp, bubbles: false })); }).catch((err) => { var _a2; if ((_a2 = globalThis.UI) == null ? void 0 : _a2.toast) UI.toast(err.message, { type: "danger" }); container.dispatchEvent(new CustomEvent("error", { detail: err, bubbles: true })); }); }; }, Util.makeDom( /*html*/ `
` ), Util.makeDom( /*html*/ `` )); const _pendingAutoFormComponents = []; const AutoForm = { register: (name) => { if (typeof document !== "undefined") { if (document.readyState !== "loading" && Component.getTemplate("AutoForm")) AutoForm._addAutoFormComponent(name); else _pendingAutoFormComponents.push(name); } }, _addAutoFormComponent: (name) => { var _a; const template = Component.getTemplate("AutoForm"); 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]">`)); } } }; if (typeof document !== "undefined") { const initAutoForm = () => { _pendingAutoFormComponents.forEach((name) => AutoForm._addAutoFormComponent(name)); _pendingAutoFormComponents.length = 0; }; if (document.readyState !== "loading") setTimeout(initAutoForm, 100); else document.addEventListener("DOMContentLoaded", () => setTimeout(initAutoForm, 100), true); } Component.register("TagsInput", (container) => { container.addEventListener("bind", (e) => { container.state.tags = e.detail || []; }); }, Util.makeDom( /*html*/ `
` ), Util.makeDom( /*html*/ `` )); AutoForm.register("TagsInput"); const VirtualScroll = () => { const itemHeights = /* @__PURE__ */ new Map(); const groupHeights = /* @__PURE__ */ new Map(); let groupItemCount = 1; const avg = Util.newAvg(); let padTop = 0, rowGap = 0, topMargin = 0, itemMarginTop = null, itemMarginBottom = null, listInited = false; return { 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)); }, init: (list, refreshCallback) => { if (listInited) return; const size = list.length, 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) groupHeights.set(i, Math.min(groupItemCount, size - i) * defaultHeight); listInited = true; refreshCallback(); }, update: (absoluteIndex, node) => { if (itemMarginTop === null) { const style = window.getComputedStyle(node); itemMarginTop = parseFloat(style.marginTop) || 0; itemMarginBottom = parseFloat(style.marginBottom) || 0; } if (absoluteIndex === 0 && !topMargin) topMargin = itemMarginTop; const newHeight = node.offsetHeight + itemMarginTop + itemMarginBottom + rowGap; const oldHeight = itemHeights.get(absoluteIndex); if (newHeight !== oldHeight) { itemHeights.set(absoluteIndex, newHeight); avg.add(newHeight); const offset = newHeight - (oldHeight || 0), groupIndex = absoluteIndex - absoluteIndex % groupItemCount; if (groupHeights.has(groupIndex)) groupHeights.set(groupIndex, groupHeights.get(groupIndex) + offset); } }, calc: (container, list) => { if (!listInited || !list) return null; const size = list.length; let visibleCount = Math.max(10, Math.ceil((container.clientHeight || 100) / (avg.get() || 32))); let prev = padTop + topMargin + rowGap, post = 0, status = 0, listStartIndex = 0, listEndIndex = 0; let renderedList = []; const scrollTop = container.scrollTop; for (let i = 0; i < size; i++) { if (status === 0) { const gh = groupHeights.get(i); if (gh && prev + gh <= scrollTop && i + groupItemCount < size) { prev += gh; i += groupItemCount - 1; } else { const ih = itemHeights.get(i); if (prev + ih <= scrollTop && i < size - 1) { prev += ih; } else { status = 1; let visibleStartIndex = Math.max(0, i); listStartIndex = Math.max(0, visibleStartIndex - visibleCount); listEndIndex = Math.min(listStartIndex + visibleCount * 3, size); i = listEndIndex - 1; renderedList = list.slice(listStartIndex, listEndIndex); for (let j = listStartIndex; j < visibleStartIndex; j++) prev -= itemHeights.get(j); } } } else if (status === 1) { const gh = groupHeights.get(i); if (gh) { post += gh; i += groupItemCount - 1; } else { post += itemHeights.get(i); } } } const finalPrevHeight = Math.max(0, prev - padTop - topMargin - rowGap - (listStartIndex > 0 ? rowGap : 0)); const finalPostHeight = post > 0 ? Math.max(0, post - 2 * rowGap) : 0; return { prevHeight: finalPrevHeight, postHeight: finalPostHeight, renderedList, listStartIndex }; } }; }; Component.register("List", (container) => { container.mode = container.getAttribute("mode") || "normal"; container.fast = container.hasAttribute("fast"); container.collapsible = container.hasAttribute("collapsible"); const defaultSets = { idfield: "id", labelfield: "label", summaryfield: "summary", groupidfield: "id", grouplabelfield: "label", groupsummaryfield: "summary", groupfield: "group", parentfield: "parent", groupicon: "folder", itemicon: "file" }; container.collapsed = NewState({}); const updateFlatList = () => { Util.updateDefaults(container, defaultSets); const list = container.state.list || [], flatList = []; if (container.mode === "group") { const itemMap = {}; list.forEach((item) => { var _a; return (itemMap[_a = item[container.groupfield]] ?? (itemMap[_a] = [])).push(item); }); (container.state.groups || []).forEach((group) => { flatList.push({ type: "group", ...group }); const items = itemMap[group[container.groupidfield]]; if (items) items.forEach((item) => flatList.push({ type: "item", ...item })); }); } else if (container.mode === "tree") { const childrenMap = {}; list.forEach((item) => { var _a; return (childrenMap[_a = item[container.parentfield] || ""] ?? (childrenMap[_a] = [])).push(item); }); const traverse = (items, level, parents) => items.forEach((item) => { var _a; const id = item[container.idfield], hasChildren = !!((_a = childrenMap[id]) == null ? void 0 : _a.length); const isCollapsed = container.collapsed[id]; flatList.push({ type: "item", ...item, _level: level, _hasChildren: hasChildren, _parents: parents }); if (hasChildren && !isCollapsed) traverse(childrenMap[id], level + 1, [...parents, id]); }); traverse(childrenMap[""] || [], 0, []); } else list.forEach((item) => flatList.push({ type: "item", ...item })); container.state._flatList = flatList; }; container.state.__watch("list", updateFlatList); const vs = container.fast ? VirtualScroll() : null; container.state._renderedList = []; container.refresh = () => { if (!container.fast) return; const res = vs.calc(container, container.state._flatList); if (res) { container.state.prevHeight = res.prevHeight; container.state.postHeight = res.postHeight; container.state._listStartIndex = res.listStartIndex; container.state._renderedList = res.renderedList; } }; container.onItemUpdate = (index2, node) => { if (container.fast) vs.update(index2 + (container.state._listStartIndex || 0), node); }; container.state.__watch("_flatList", (flatList) => { if (container.fast) { container.state._listStartIndex = 0; container.state._renderedList = vs.reset(flatList, container) || []; setTimeout(() => { if (container.state._flatList === flatList) vs.init(flatList, container.refresh); }); } else container.state._renderedList = flatList; }); container.selectItem = (item, index2) => { if (container.hasAttribute("auto-select")) container.state.selectedItem = container.state.selectedItem === item[container.idfield] ? null : item[container.idfield]; container.dispatchEvent(new CustomEvent("itemclick", { bubbles: false, detail: { item, index: index2 + (container.fast ? container.state._listStartIndex || 0 : 0) } })); }; container.selectGroup = (item, index2) => { if (container.hasAttribute("auto-select-group")) container.state.selectedGroup = container.state.selectedGroup === item[container.groupidfield] ? null : item[container.groupidfield]; container.dispatchEvent(new CustomEvent("groupclick", { bubbles: false, detail: { item, index: index2 } })); }; container.toggleCollapse = (item) => { if (container.collapsible && item._hasChildren) { container.collapsed[item[container.idfield]] = !container.collapsed[item[container.idfield]]; updateFlatList(); } }; updateFlatList(); }, Util.makeDom( /*html*/ `
` )); Component.register("Nav", (container) => { container.click = (item, noselect) => { if (!item.noselect && !noselect) Hash.nav = item.name; container.dispatchEvent(new CustomEvent("nav", { detail: { item }, bubbles: false })); }; }, Util.makeDom( /*html*/ ` ` )); let _mouseMoverMoving = false; let _mouseMoverPos = {}; let _mouseMoverEvents = {}; const MouseMover = { start: (event, { onmousemove, onmouseup }) => { _mouseMoverPos = { x: event.clientX, y: event.clientY, w: 0, h: 0 }; _mouseMoverEvents = { onmousemove, onmouseup }; _mouseMoverMoving = true; } }; if (typeof document !== "undefined") { document.addEventListener("mouseup", (event) => { var _a; if (!_mouseMoverMoving) return; _mouseMoverMoving = false; (_a = _mouseMoverEvents.onmouseup) == null ? void 0 : _a.call(_mouseMoverEvents, { event, ..._mouseMoverPos }); }); document.addEventListener("mousemove", (event) => { var _a; if (!_mouseMoverMoving) return; _mouseMoverPos.w = event.clientX - _mouseMoverPos.x; _mouseMoverPos.h = event.clientY - _mouseMoverPos.y; (_a = _mouseMoverEvents.onmousemove) == null ? void 0 : _a.call(_mouseMoverEvents, { event, ..._mouseMoverPos }); }); } Component.register("Resizer", (container) => { container.isVertical = container.hasAttribute("vertical"); const min = parseInt(container.getAttribute("min")) || 10; const max = parseInt(container.getAttribute("max")) || 1e3; 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 newSize = startSize + (container.isVertical ? h : w); return newSize < min ? min : newSize > max ? max : newSize; }; container.addEventListener("mousedown", (event) => { const startSize = container.isVertical ? target.offsetHeight : target.offsetWidth; MouseMover.start(event, { onmousemove: ({ w, h }) => { const newSize = getSize(startSize, w, h); 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*/ `
` )); const State = NewState({ exitBlocks: 0 }); globalThis.State = State; if (typeof window !== "undefined") { window.addEventListener("beforeunload", (event) => { if (State.exitBlocks > 0) event.preventDefault(); }); } const htmlNode = document.documentElement; if (!htmlNode.hasAttribute("$data-bs-theme") && !htmlNode.hasAttribute("data-bs-theme")) { htmlNode.setAttribute("$data-bs-theme", "LocalStorage.darkMode?'dark':'light'"); } globalThis.HTTP = HTTP; globalThis.UI = UI$1; globalThis.AutoForm = AutoForm; globalThis.MouseMover = MouseMover; if (typeof document !== "undefined") { const doRefresh = () => RefreshState(document.documentElement); if (document.readyState !== "loading") doRefresh(); else document.addEventListener("DOMContentLoaded", doRefresh, true); } export { APIComponent, AutoForm, HTTP, MouseMover, State, UI$1 as UI, VirtualScroll };