(function(global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@apigo.cc/state"), require("@apigo.cc/bootstrap")) : typeof define === "function" && define.amd ? define(["exports", "@apigo.cc/state", "@apigo.cc/bootstrap"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.ApigoBase = {}, global.ApigoState));
})(this, function(exports2, state) {
"use strict";
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 = state.Component.register("API", (container) => {
container.request = state.NewState({ url: "", method: "GET", headers: {}, data: null, timeout: 1e4, responseType: "" });
container.response = state.NewState({ loading: false, ok: null, status: null, error: null, headers: {}, responseType: "", result: null });
container.result = state.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 = {};
state.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 }));
});
state.Util.copyFunction(container, container.modal, "show", "hide");
}, state.Util.makeDom(
/*html*/
`
`
));
state.Component.register("Dialog", state.Component.getSetupFunction("Modal"), state.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)));
};
state.Component.register("Toast", (container) => {
container.toast = new bootstrap.Toast(container, { autohide: container.state.delay > 0 });
state.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);
}
});
}, state.Util.makeDom(
/*html*/
`
`
), state.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 || [] };
state.$(`[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)));
};
state.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 : state.NewState(v || {});
}
});
container.data = container.data || {};
container._thisObj = container;
container.form = state.$(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 }));
});
};
}, state.Util.makeDom(
/*html*/
`
`
), state.Util.makeDom(
/*html*/
``
));
const _pendingAutoFormComponents = [];
const AutoForm = {
register: (name, typeName) => {
const entry = { name, typeName: typeName || name };
if (typeof document !== "undefined") {
if (document.readyState !== "loading" && state.Component.getTemplate("AutoForm")) AutoForm._addAutoFormComponent(entry);
else _pendingAutoFormComponents.push(entry);
}
},
_addAutoFormComponent: (entry) => {
const { name, typeName } = entry;
const template = state.Component.getTemplate("AutoForm");
if (template) {
let container = state.$(template.content, "[control-wrapper]");
if (!container) {
const nested = state.$(template.content, "template");
if (nested) container = state.$(nested.content, "[control-wrapper]");
}
if (container && !container.querySelector(name)) {
container.appendChild(state.Util.makeDom(`<${name} $if="item.type?.toLowerCase() === '${typeName.toLowerCase()}'" $name="item.name" $.="item.setting || {}" $bind="thisNode.closest('AutoForm').data[item.name]">${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);
}
state.Component.register("TagsInput", (container) => {
container.state = 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 : [];
}
});
}, state.Util.makeDom(
/*html*/
`
`
), state.Util.makeDom(
/*html*/
``
));
AutoForm.register("TagsInput");
state.Component.register("DatePicker", (container) => {
container.state = 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;
}
};
}, state.Util.makeDom(
/*html*/
`
-
`
));
state.Component.register("ColorPicker", (container) => {
container.state = 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 }));
};
}, state.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"];
state.Component.register("IconPicker", (container) => {
container.state = 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 = state.$(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));
}, state.Util.makeDom(
/*html*/
`
`
), state.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 = state.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 };
}
};
};
state.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 = state.NewState({});
const updateFlatList = () => {
state.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();
}, state.Util.makeDom(
/*html*/
`
`
));
state.Component.register("Nav", (container) => {
container.vertical = container.hasAttribute("vertical");
container.click = (item, noselect) => {
if (!item.noselect && !noselect) state.Hash.nav = item.name;
container.dispatchEvent(new CustomEvent("nav", { detail: { item }, bubbles: false }));
};
}, state.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 });
});
}
state.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 }));
}
});
});
}, state.Util.makeDom(
/*html*/
`
`
));
if (typeof window !== "undefined") {
window.addEventListener("beforeunload", (event) => {
if (state.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: state.State
};
if (typeof document !== "undefined") {
globalThis.ApigoBase = ApigoBase;
const doRefresh = () => state.RefreshState(document.documentElement);
if (document.readyState !== "loading") setTimeout(doRefresh, 1);
else document.addEventListener("DOMContentLoaded", () => setTimeout(doRefresh, 1), true);
}
Object.defineProperty(exports2, "State", {
enumerable: true,
get: () => state.State
});
exports2.APIComponent = APIComponent;
exports2.AutoForm = AutoForm;
exports2.HTTP = HTTP;
exports2.MouseMover = MouseMover;
exports2.UI = UI$1;
exports2.VirtualScroll = VirtualScroll;
Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
});