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 || "body", 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.request = { method: "POST" }; container.response = {}; container.result = null; container.data = NewState(container.data || {}); 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]">`)); } else if (document.readyState !== "loading") { console.error("AutoForm template not found during registration of", 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 FastListComponent = Component.register("FastList", (container) => { const itemHeights = /* @__PURE__ */ new Map(); const groupHeights = /* @__PURE__ */ new Map(); let groupItemCount = 1; container.state.renderedList = []; const avg = Util.newAvg(); let containerPaddingTop = 0; let containerRowGap = 0; let topMargin = 0; let itemMarginTop = null; let itemMarginBottom = null; let listStartIndex = 0; let visibleStartIndex = 0; let visibleCount = 10; let listInited = false; container.onItemUpdate = (index2, node) => { const absoluteIndex = index2 + listStartIndex; if (itemMarginTop === null) { const style = window.getComputedStyle(node); itemMarginTop = parseFloat(style.marginTop) || 0; itemMarginBottom = parseFloat(style.marginBottom) || 0; } if (absoluteIndex === 0) topMargin = itemMarginTop; const newHeight = node.offsetHeight + itemMarginTop + itemMarginBottom + containerRowGap; const oldHeight = itemHeights.get(absoluteIndex); if (newHeight !== oldHeight) { itemHeights.set(absoluteIndex, newHeight); avg.add(newHeight); const offset = newHeight - (oldHeight || 0); const groupIndex = absoluteIndex - absoluteIndex % groupItemCount; if (groupHeights.has(groupIndex)) groupHeights.set(groupIndex, groupHeights.get(groupIndex) + offset); } }; container.refresh = () => { if (!listInited) return; visibleCount = Math.ceil((container.clientHeight || 100) / (avg.get() || 32)); let list = []; const fullList = container.state.list; const fullSize = fullList.length; let i = 0; let prev = containerPaddingTop + topMargin + containerRowGap; let post = 0; let starus = 0; for (i = 0; i < fullSize; i++) { if (starus === 0) { const gh = groupHeights.get(i); if (gh && prev + gh < container.scrollTop) { prev += gh; i += Math.min(groupItemCount, fullSize - i) - 1; } else { const ih = itemHeights.get(i); if (prev + ih < container.scrollTop) { prev += ih; } else { starus = 1; visibleStartIndex = Math.max(0, i); listStartIndex = Math.max(0, visibleStartIndex - visibleCount); const listEndIndex = Math.min(listStartIndex + visibleCount * 3, fullSize); i = listEndIndex - 1; list = fullList.slice(listStartIndex, listEndIndex); for (let j = listStartIndex; j < visibleStartIndex; j++) prev -= itemHeights.get(j); } } } else if (starus === 1) { const gh = groupHeights.get(i); if (gh) { post += gh; i += groupItemCount - 1; } else { post += itemHeights.get(i); } } } container.state.prevHeight = prev - containerPaddingTop - topMargin - containerRowGap; container.state.postHeight = post; container.state.renderedList = list; }; container.state.__watch("list", (list) => { listInited = false; 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(() => { if (listInited || container.state.list !== list) return; 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( /*html*/ `
` )); Component.attachSelectFeature = (container, eventName = "itemclick", selectActionName = "selectItem", activeActionName = "itemActiveTag", selectedItemKey = "selectedItem", idfieldKey = "idfield", selectableAttr = "auto-select") => { const selectable = selectableAttr && container.hasAttribute(selectableAttr); container[selectActionName] = (item, index2) => { if (selectable) container.state[selectedItemKey] = container.state[selectedItemKey] === item[container[idfieldKey]] ? null : item[container[idfieldKey]]; container.dispatchEvent(new CustomEvent(eventName, { bubbles: false, detail: !selectable || container.state[selectedItemKey] ? { item, index: index2 } : { item: {}, index: null } })); }; container[activeActionName] = (item) => container.state[selectedItemKey] !== null && container.state[selectedItemKey] === item[container[idfieldKey]] ? "active" : ""; }; Component.register("List", (container) => { Component.attachSelectFeature(container); const defaultSets = { idfield: "id", labelfield: "label", summaryfield: "summary" }; container.state.__watch("list", (oldList) => { Util.updateDefaults(container, defaultSets); }); }, Util.makeDom( /*html*/ `
\${item[this.labelfield]}
` )); Component.register("GroupedList", (container) => { Component.getSetupFunction("List")(container); container._selectItem = container.selectItem; container.selectItem = (item, index2) => { container.state.selectedGroup = null; container._selectItem(item, index2); }; Component.attachSelectFeature(container, "groupclick", "selectGroup", "groupActiveTag", "selectedGroup", "groupidfield", container.hasAttribute("auto-select-group") || container.hasAttribute("auto-select") && "group-selectable"); container._selectGroup = container.selectGroup; container.selectGroup = (item, index2) => { container.state.selectedItem = null; container._selectGroup(item, index2); }; const defaultSets = { typefield: "type", groupidfield: "id", grouplabelfield: "label", groupsummaryfield: "summary", groupfield: "group" }; container.state.__watch("list", (oldList) => { Util.updateDefaults(container, defaultSets); const newList = []; (container.state.groups || []).forEach((group) => { newList.push({ [container.typefield]: "group", ...group }); if (oldList) oldList.forEach((item) => { if (item[container.groupfield] === group[container.groupidfield]) { newList.push({ [container.typefield]: "item", ...item }); } }); }); return newList; }); }, Util.makeDom( /*html*/ `
` )); Component.register("FastGroupedList", (container) => { Component.getSetupFunction("List")(container); Component.getSetupFunction("GroupedList")(container); }, Util.makeDom( /*html*/ `
\${item[this.grouplabelfield]}
\${item[this.labelfield]}
` )); Component.register("Tree", (container) => { const defaultSets = { parentfield: "parent", groupicon: "folder", itemicon: "file" }; container.state.__watch("list", () => { Util.updateDefaults(container, defaultSets); const childrenMap = {}; (container.state.list || []).forEach((item) => { var _a; return (childrenMap[_a = item[container.parentfield] || ""] ?? (childrenMap[_a] = [])).push(item); }); const newList = []; const traverse = (list, level) => list.forEach((item) => { var _a; const id = item[container.idfield]; const hasChildren = !!((_a = childrenMap[id]) == null ? void 0 : _a.length); newList.push({ ...item, _level: level, _hasChildren: hasChildren }); if (hasChildren) traverse(childrenMap[id], level + 1); }); traverse(childrenMap[""] || [], 0); return newList; }); }, Util.makeDom( /*html*/ `
` )); const FastTreeComponent = Component.register("FastTree", (container) => { Component.getSetupFunction("List")(container); Component.getSetupFunction("Tree")(container); }, Util.makeDom( /*html*/ `
` )); Component.register("CollapseTree", (container) => { Component.attachSelectFeature(container); const defaultSets = { idfield: "id", parentfield: "parent", labelfield: "label", summaryfield: "summary" }; container.collapsed = NewState({}); container.state.__watch("list", () => { Util.updateDefaults(container, defaultSets); const childrenMap = {}; (container.state.list || []).forEach((item) => { var _a; return (childrenMap[_a = item[container.parentfield] || ""] ?? (childrenMap[_a] = [])).push(item); }); const newList = []; const traverse = (list, level, parents) => list.forEach((item) => { var _a; const id = item[container.idfield]; const hasChildren = !!((_a = childrenMap[id]) == null ? void 0 : _a.length); newList.push({ ...item, _level: level, _hasChildren: hasChildren, _parents: parents }); if (hasChildren) traverse(childrenMap[id], level + 1, [...parents, id]); }); traverse(childrenMap[""] || [], 0, []); return newList; }); }, 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; } }; globalThis.MouseMover = MouseMover; 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) => { const isVertical = container.hasAttribute("vertical"); const min = parseInt(container.getAttribute("min")) || 10; const max = parseInt(container.getAttribute("max")) || 1e3; const target = container.target || container.previousElementSibling; const getSize = (startSize, w, h) => { const newSize = startSize + (isVertical ? h : w); return newSize < min ? min : newSize > max ? max : newSize; }; container.addEventListener("mousedown", (event) => { const startSize = isVertical ? target.offsetHeight : target.offsetWidth; MouseMover.start(event, { onmousemove: ({ w, h }) => target.style[isVertical ? "height" : "width"] = getSize(startSize, w, h) + "px", onmouseup: ({ w, h }) => container.dispatchEvent(new CustomEvent("resize", { detail: { oldSize: startSize, newSize: getSize(startSize, w, h) }, 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(); }); } globalThis.HTTP = HTTP; globalThis.UI = UI$1; globalThis.AutoForm = AutoForm; globalThis.MouseMover = MouseMover; if (typeof document !== "undefined") { const doRefresh = () => { console.log("Base project triggering RefreshState"); RefreshState(document.documentElement); }; if (document.readyState !== "loading") doRefresh(); else document.addEventListener("DOMContentLoaded", doRefresh, true); } export { APIComponent, AutoForm, FastListComponent, FastTreeComponent, HTTP, MouseMover, State, UI$1 as UI };