import { Component, NewState, Util, $, Hash, State, RefreshState } from "@apigo.cc/state"; import { State as State2 } from "@apigo.cc/state"; import "@apigo.cc/bootstrap"; 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.nobutton = container.hasAttribute("nobutton"); container.request = { method: "POST" }; container.response = {}; container.result = null; Object.defineProperty(container, "data", { get: () => container.state.data, set: (v) => { container.state.data = (v == null ? void 0 : v.__isProxy) ? v : NewState(v || {}); } }); container.data = container.data || {}; container._thisObj = container; 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, typeName) => { const entry = { name, typeName: typeName || name }; if (typeof document !== "undefined") { if (document.readyState !== "loading" && Component.getTemplate("AutoForm")) AutoForm._addAutoFormComponent(entry); else _pendingAutoFormComponents.push(entry); } }, _addAutoFormComponent: (entry) => { const { name, typeName } = entry; const template = Component.getTemplate("AutoForm"); if (template) { let container = $(template.content, "[control-wrapper]"); if (!container) { const nested = $(template.content, "template"); if (nested) container = $(nested.content, "[control-wrapper]"); } if (container && !container.querySelector(name)) { container.appendChild(Util.makeDom(`<${name} $if="item.type?.toLowerCase() === '${typeName.toLowerCase()}'" $name="item.name" $.="item.setting || {}" $bind="thisNode.closest('AutoForm').data[item.name]">`)); } } } }; if (typeof document !== "undefined") { const initAutoForm = () => { _pendingAutoFormComponents.forEach((entry) => AutoForm._addAutoFormComponent(entry)); _pendingAutoFormComponents.length = 0; }; if (document.readyState !== "loading") setTimeout(initAutoForm, 100); else document.addEventListener("DOMContentLoaded", () => setTimeout(initAutoForm, 100), true); } Component.register("TagsInput", (container) => { container.state = NewState({ tags: [] }); container.addEventListener("bind", (e) => { container.state.tags = Array.isArray(e.detail) ? e.detail : []; }); Object.defineProperty(container, "value", { get: () => container.state.tags, set: (v) => { container.state.tags = Array.isArray(v) ? v : []; } }); }, Util.makeDom( /*html*/ `
` ), Util.makeDom( /*html*/ `` )); AutoForm.register("TagsInput"); Component.register("DatePicker", (container) => { container.state = NewState({ start: "", end: "" }); container.addEventListener("bind", (e) => { var _a, _b, _c; container.state.start = e.detail || ""; const form = container.closest("AutoForm"); const name = container.getAttribute("name"); const item = (_b = (_a = form == null ? void 0 : form.state) == null ? void 0 : _a.schema) == null ? void 0 : _b.find((i) => i.name === name); const rangeEnd = ((_c = item == null ? void 0 : item.setting) == null ? void 0 : _c.rangeEnd) || container.rangeEnd; if (form && rangeEnd) { container.state.end = form.data[rangeEnd] || ""; } }); Object.defineProperty(container, "isRange", { get: () => { var _a, _b, _c; const form = container.closest("AutoForm"); const name = container.getAttribute("name"); const item = (_b = (_a = form == null ? void 0 : form.state) == null ? void 0 : _a.schema) == null ? void 0 : _b.find((i) => i.name === name); return !!(((_c = item == null ? void 0 : item.setting) == null ? void 0 : _c.rangeEnd) || container.rangeEnd); } }); Object.defineProperty(container, "value", { get: () => container.state.start, set: (v) => { container.state.start = v || ""; } }); container.updateStart = (val) => { container.state.start = val; container.dispatchEvent(new CustomEvent("change", { bubbles: true, detail: val })); }; container.updateEnd = (val) => { var _a, _b, _c; container.state.end = val; const form = container.closest("AutoForm"); const name = container.getAttribute("name"); const item = (_b = (_a = form == null ? void 0 : form.state) == null ? void 0 : _a.schema) == null ? void 0 : _b.find((i) => i.name === name); const rangeEnd = ((_c = item == null ? void 0 : item.setting) == null ? void 0 : _c.rangeEnd) || container.rangeEnd; if (form && rangeEnd) { form.data[rangeEnd] = val; } }; }, Util.makeDom( /*html*/ `
` )); Component.register("ColorPicker", (container) => { container.state = NewState({ value: "#000000" }); container.addEventListener("bind", (e) => { container.state.value = e.detail || "#000000"; }); Object.defineProperty(container, "value", { get: () => container.state.value, set: (v) => { container.state.value = v || "#000000"; } }); container.updateValue = (val) => { container.state.value = val; container.dispatchEvent(new CustomEvent("change", { bubbles: true, detail: val })); }; }, Util.makeDom( /*html*/ `
` )); const BOOTSTRAP_ICONS = ["alarm", "archive", "arrow-left", "arrow-right", "bag", "bank", "basket", "bell", "bookmark", "box", "briefcase", "calendar", "camera", "cart", "chat", "check", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "clock", "cloud", "code", "collection", "command", "cpu", "credit-card", "cup", "dash", "database", "display", "door-closed", "download", "droplet", "earbuds", "edit", "egg", "eject", "envelope", "eraser", "eye", "file", "filter", "flag", "folder", "gear", "gem", "gift", "graph-up", "grid", "hammer", "hand-thumbs-up", "heart", "house", "image", "inbox", "info-circle", "journal", "key", "laptop", "layers", "layout-text-sidebar-reverse", "lightbulb", "link", "list", "lock", "map", "mic", "moon", "mouse", "music-note", "newspaper", "palette", "paperclip", "pause", "pencil", "person", "phone", "pie-chart", "play", "plus", "printer", "puzzle", "question-circle", "reception-4", "record", "reply", "rss", "save", "search", "send", "server", "share", "shield", "shop", "shuffle", "skip-end", "skip-start", "slash", "sliders", "smartphone", "speaker", "speedometer", "spellcheck", "square", "star", "stickies", "stop", "stopwatch", "suit-heart", "sun", "table", "tag", "tags", "telephone", "terminal", "text-paragraph", "thermometer", "three-dots", "ticket", "tools", "trash", "trophy", "truck", "tv", "umbrella", "unlock", "upload", "vector-pen", "wallet", "watch", "wifi", "window", "wrench", "x", "zoom-in", "zoom-out", "activity", "at", "award", "backspace", "badge-3d", "badge-4k", "badge-8k", "badge-ad", "badge-ar", "badge-cc", "badge-hd", "badge-tm", "badge-vo", "badge-vr", "badge-wc", "bar-chart", "battery", "bicycle", "binoculars", "blockquote-left", "blockquote-right", "book", "bookshelf", "bootstrap", "border-all", "border-bottom", "border-center", "border-inner", "border-left", "border-middle", "border-outer", "border-right", "border-style", "border-top", "border-width", "bounding-box", "box-arrow-down", "box-arrow-in-down", "box-arrow-in-left", "box-arrow-in-right", "box-arrow-in-up", "box-arrow-left", "box-arrow-right", "box-arrow-up", "box-seam", "brightness-alt-high", "brightness-alt-low", "brightness-high", "brightness-low", "broadcast", "brush", "bucket", "bug", "building", "bullseye", "calculator", "calendar-check", "calendar-date", "calendar-day", "calendar-event", "calendar-minus", "calendar-month", "calendar-plus", "calendar-range", "calendar-week", "calendar-x", "calendar2", "calendar3", "calendar4", "camera-reels", "camera-video", "capslock", "card-checklist", "card-heading", "card-image", "card-list", "card-text", "caret-down", "caret-left", "caret-right", "caret-up", "cart-check", "cart-dash", "cart-plus", "cart-x", "cash", "cash-stack", "cast", "chat-dots", "chat-left", "chat-quote", "chat-right", "chat-square", "chat-text", "check-all", "check-circle", "check-square", "circle", "clipboard", "cloud-arrow-down", "cloud-arrow-up", "cloud-check", "cloud-download", "cloud-fog", "cloud-hail", "cloud-lightning", "cloud-minus", "cloud-moon", "cloud-plus", "cloud-rain", "cloud-slash", "cloud-snow", "cloud-sun", "cloud-upload", "clouds", "cloudy", "code-slash", "code-square", "collection-play", "columns", "columns-gap", "compass", "cone", "cone-striped", "controller", "credit-card-2-back", "credit-card-2-front", "crop", "cup-straw", "cursor", "dash-circle", "dash-square", "diagram-2", "diagram-3", "diamond", "dice-1", "dice-2", "dice-3", "dice-4", "dice-5", "dice-6", "disc", "discord", "distribute-horizontal", "distribute-vertical", "door-open", "dot", "droplet-half", "easel", "egg-fried", "emoji-angry", "emoji-dizzy", "emoji-expressionless", "emoji-frown", "emoji-heart-eyes", "emoji-laughing", "emoji-neutral", "emoji-smile", "emoji-sunglasses", "emoji-wink", "envelope-open", "exclamation", "exclamation-circle", "exclamation-diamond", "exclamation-octagon", "exclamation-square", "exclamation-triangle", "eye-slash", "eyedropper", "facebook", "file-arrow-down", "file-arrow-up", "file-binary", "file-break", "file-check", "file-code", "file-diff", "file-earmark", "file-excel", "file-image", "file-lock", "file-medical", "file-minus", "file-music", "file-pdf", "file-person", "file-play", "file-plus", "file-post", "file-ppt", "file-richtext", "file-slides", "file-spreadsheet", "file-text", "file-word", "file-zip", "files", "film", "filter-circle", "filter-left", "filter-right", "filter-square", "fingerprint", "flower1", "flower2", "flower3", "folder-check", "folder-minus", "folder-plus", "folder-symlink", "folder-x", "folder2-open", "fonts", "forward", "front", "fullscreen", "fullscreen-exit", "funnel", "gear-wide", "gender-female", "gender-male", "gender-trans", "geo", "geo-alt", "github", "globe", "google", "graph-down", "grid-1x2", "grid-3x2", "grid-3x3", "grip-horizontal", "grip-vertical", "hand-index", "hand-thumbs-down", "handbag", "hash", "headphones", "headset", "heart-half", "heptagon", "hourglass", "hourglass-bottom", "hourglass-split", "hourglass-top", "house-door", "hr", "hurricane", "image-alt", "images", "infinity", "input-cursor", "instagram", "intersect", "journal-album", "journal-arrow-down", "journal-arrow-up", "journal-bookmark", "journal-check", "journal-code", "journal-medical", "journal-minus", "journal-plus", "journal-richtext", "journal-text", "journal-x", "journals", "justify", "kanban", "keyboard", "ladder", "lamp", "layers-half", "layout-sidebar", "layout-split", "layout-three-columns", "life-preserver", "lightbulb-off", "lightning", "lightning-charge", "link-45deg", "linkedin", "list-check", "list-nested", "list-ol", "list-stars", "list-task", "list-ul", "mailbox", "markdown", "mask", "mastodon", "megaphone", "menu-app", "menu-button", "messenger", "mic-mute", "minecart", "minecart-loaded", "moisture", "mouse2", "mouse3", "music-note-beamed", "music-note-list", "music-player", "node-minus", "node-plus", "nut", "octagon", "option", "outlet", "paint-bucket", "patch-check", "patch-exclamation", "patch-minus", "patch-plus", "patch-question", "pause-btn", "pause-circle", "peace", "pen", "pencil-square", "pentagon", "person-badge", "person-bounding-box", "person-circle", "person-lines-fill", "phone-landscape", "phone-vibrate", "pie-chart-fill", "pin", "pin-angle", "pin-fill", "pin-map", "pip", "play-btn", "play-circle", "plug", "plus-circle", "plus-square", "power", "question", "question-diamond", "question-square", "rainbow", "receipt", "receipt-cutoff", "reception-0", "reception-1", "reception-2", "reception-3", "record-btn", "record-circle", "record2", "recycle", "reddit", "reply-all", "router", "rulers", "safe", "save2", "sd-card", "segmented-nav", "shield-check", "shield-exclamation", "shield-lock", "shield-shaded", "shield-slash", "shift", "signpost", "signpost-2", "signpost-split", "sim", "skip-backward", "skip-forward", "slack", "slash-circle", "slash-square", "smartwatch", "snow", "snow2", "snow3", "sort-alpha-down", "sort-alpha-up", "sort-numeric-down", "sort-numeric-up", "soundwave", "speedometer2", "square-half", "stack", "star-half", "stars", "stop-btn", "stop-circle", "suit-club", "suit-diamond", "suit-spade", "sunglasses", "sunrise", "sunset", "symmetry-horizontal", "symmetry-vertical", "tablet", "tablet-landscape", "telegram", "telephone-forward", "telephone-inbound", "telephone-outbound", "telephone-plus", "telephone-x", "text-center", "text-indent-left", "text-indent-right", "text-left", "text-right", "thermometer-half", "thermometer-high", "thermometer-low", "thermometer-snow", "thermometer-sun", "three-dots-vertical", "toggle-off", "toggle-on", "toggle2-off", "toggle2-on", "tornado", "translate", "trash2", "tree", "truck-flatbed", "tsunami", "type", "type-bold", "type-h1", "type-h2", "type-h3", "type-italic", "type-strikethrough", "type-underline", "ui-checks", "ui-checks-grid", "ui-radios", "ui-radios-grid", "union", "upc", "upc-scan", "view-list", "view-stacked", "vinyl", "voicemail", "volume-down", "volume-mute", "volume-off", "volume-up", "vr", "wallet2", "water", "whatsapp", "wifi-1", "wifi-2", "wifi-off", "wind", "window-dock", "window-sidebar", "x-circle", "x-diamond", "x-octagon", "x-square", "youtube"]; Component.register("IconPicker", (container) => { container.state = NewState({ value: "", search: "", open: false }); container.addEventListener("bind", (e) => { container.state.value = e.detail || ""; }); Object.defineProperty(container, "value", { get: () => container.state.value, set: (v) => { container.state.value = v || ""; } }); Object.defineProperty(container, "filteredIcons", { get: () => { var _a; const s = ((_a = container.state.search) == null ? void 0 : _a.toLowerCase()) || ""; return BOOTSTRAP_ICONS.filter((i) => i.includes(s)); } }); container.selectIcon = (icon) => { container.state.value = icon; container.state.open = false; container.dispatchEvent(new CustomEvent("change", { bubbles: true, detail: icon })); }; container.toggle = () => { container.state.open = !container.state.open; if (container.state.open) { setTimeout(() => { var _a; (_a = $(container, "input")) == null ? void 0 : _a.focus(); }, 10); } }; const onGlobalClick = (e) => { if (!container.contains(e.target)) { container.state.open = false; } }; window.addEventListener("click", onGlobalClick); container.addEventListener("remove", () => window.removeEventListener("click", onGlobalClick)); }, Util.makeDom( /*html*/ ` ` ), Util.makeDom( /*html*/ `` )); AutoForm.register("DatePicker"); AutoForm.register("ColorPicker"); AutoForm.register("IconPicker"); const VirtualScroll = (options = {}) => { 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; const providedItemHeight = options.itemHeight || null; 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; const visibleCount = Math.max(10, Math.ceil((container.clientHeight || 100) / (providedItemHeight || 32))); return list.slice(0, Math.min(visibleCount * 3, size)); }, init: (list, refreshCallback) => { if (listInited) return; const size = list.length; let defaultHeight = providedItemHeight || avg.get() || 32; if (size > 0 && typeof list[0] === "object" && list[0] !== null && list[0]._itemHeight) { defaultHeight = list[0]._itemHeight; } avg.add(defaultHeight); if (itemMarginTop === null) { itemMarginTop = 0; itemMarginBottom = 0; } for (let i = 0; i < size; i++) { if (!itemHeights.has(i)) { const ih = typeof list[i] === "object" && list[i] !== null && list[i]._itemHeight ? list[i]._itemHeight : defaultHeight; itemHeights.set(i, ih); } } for (let i = 0; i < size; i += groupItemCount) groupHeights.set(i, Math.min(groupItemCount, size - i) * defaultHeight); listInited = true; refreshCallback(); }, update: (absoluteIndex, node) => { if (node.offsetHeight === 0) return; 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; const avgVal = Math.max(16, avg.get() || 32); let visibleCount = Math.max(10, Math.ceil((container.clientHeight || 100) / avgVal)); let prev = padTop + topMargin + rowGap, post = 0, status = 0, listStartIndex = 0, listEndIndex = 0; let renderedList = []; const scrollTop = container.scrollTop; let loopCount = 0; for (let i = 0; i < size; i++) { if (++loopCount > size * 2) { throw new Error(`VirtualScroll.calc infinite loop detected at i=${i}, status=${status}, size=${size}, groupItemCount=${groupItemCount}`); } 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 padTopEl = container.fast ? container.querySelector(".vs-pad-top") : null; const padBottomEl = container.fast ? container.querySelector(".vs-pad-bottom") : null; 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 = []; let refreshing = false; container.refresh = () => { if (!container.fast || refreshing) return; refreshing = true; try { const res = vs.calc(container, container.state._flatList); if (res) { if (padTopEl) padTopEl.style.height = `${res.prevHeight}px`; if (padBottomEl) padBottomEl.style.height = `${res.postHeight}px`; if (container.state._listStartIndex !== res.listStartIndex) { container.state._listStartIndex = res.listStartIndex; } const cur = container.state._renderedList || []; if (cur.length !== res.renderedList.length || cur[0] !== res.renderedList[0] || cur[cur.length - 1] !== res.renderedList[res.renderedList.length - 1]) { container.state._renderedList = res.renderedList; } } } finally { setTimeout(() => { refreshing = false; }, 0); } }; container.onItemUpdate = (index2, node) => { if (container.fast) vs.update(index2 + (container.state._listStartIndex || 0), node); }; container.state.__watch("_flatList", (flatList) => { if (container.fast) { if (padTopEl) padTopEl.style.height = "0px"; if (padBottomEl) padBottomEl.style.height = "0px"; 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.vertical = container.hasAttribute("vertical"); 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*/ `
` )); 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; globalThis.VirtualScroll = VirtualScroll; const ApigoBase = { HTTP, UI: UI$1, AutoForm, MouseMover, VirtualScroll, State }; if (typeof document !== "undefined") { globalThis.ApigoBase = ApigoBase; const doRefresh = () => RefreshState(document.documentElement); if (document.readyState !== "loading") setTimeout(doRefresh, 1); else document.addEventListener("DOMContentLoaded", () => setTimeout(doRefresh, 1), true); } export { APIComponent, AutoForm, HTTP, MouseMover, State2 as State, UI$1 as UI, VirtualScroll };