(function(global, factory) {
  typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@apigo.cc/state")) : typeof define === "function" && define.amd ? define(["exports", "@apigo.cc/state"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.ApigoBase = 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;
      }
      for (const name of HTTP.keepHeaders) {
        if (HTTP._keepHeadersCache[name] && !headers[name]) headers[name] = HTTP._keepHeadersCache[name];
      }
      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()) });
        for (const name of HTTP.keepHeaders) {
          const val = resp.headers.get(name);
          if (val) {
            HTTP._keepHeadersCache[name] = val;
            try {
              localStorage.setItem("_http_keep_" + name, val);
            } catch (e) {
            }
          }
        }
        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;
    }
  };
  HTTP.keepHeaders = ["Session-Id", "Device-Id"];
  HTTP._keepHeadersCache = {};
  for (const name of HTTP.keepHeaders) {
    try {
      const val = localStorage.getItem("_http_keep_" + name);
      if (val !== null) HTTP._keepHeadersCache[name] = val;
    } catch (e) {
    }
  }
  globalThis.HTTP = HTTP;
  const UI = {};
  globalThis.Component.register("Toast", (container) => {
    container.toast = new bootstrap.Toast(container, { autohide: container.state.delay > 0 });
    globalThis.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);
      }
    });
  }, globalThis.Util.makeDom(
    /*html*/
    `
<div class="toast align-items-center border-0 m-1">
  <div $class="toast-body rounded p-3 text-bg-\${this.state?.type}">
    <div class="d-flex align-items-center">
      <div class="flex-grow-1">
        <span style="white-space:pre-wrap" class="fs-6" $text="this.state?.message"></span>
        <template $if="this.state?.left !== undefined">
          <span class="small text-dim ms-2" $text="\${this.state?.left}s"></span>
        </template>
      </div>
      <button type="button" class="btn btn-link ms-3 bi bi-x-lg link-reset" style="color:inherit" data-bs-dismiss="toast"></button>
    </div>
    <div class="d-flex justify-content-end gap-3">
      <template $each="this.state?.buttons || ['{#Close#}']">
        <button type="button" $class="btn btn-sm btn-\${this.state?.type} mt-2" data-bs-dismiss="toast" $onclick="this.result=index+1" $text="item"></button>
      </template>
    </div>
  </div>
</div>
`
  ), globalThis.Util.makeDom(
    /*html*/
    `<div toast-container="default" class="position-fixed bottom-0 end-0 overflow-auto" style="z-index:3000;max-height:80%"></div>`
  ));
  UI.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 || [] };
    globalThis.$(`[toast-container="${options.container || "default"}"]`).appendChild(t);
    Promise.resolve().then(() => t.show());
  };
  UI.toastConfirm = function(message, options = {}) {
    return new Promise((resolve) => UI.toast(message, { buttons: ["{#Confirm#}"], ...options }).then((index2) => resolve(index2 === 1)).catch(() => resolve(false)));
  };
  globalThis.UI = UI;
  const AUTOFORM_BLUEPRINT = globalThis.Util.makeDom(
    /*html*/
    `
<div class="auto-form-root">
  <form $class="\${this.inline ? 'd-flex flex-wrap align-items-center gap-3' : (this.vertical ? 'd-flex flex-column' : (this.horizontal ? 'auto-grid-form forced-horizontal' : 'auto-grid-form'))}" $onsubmit="event.stopPropagation();event.preventDefault();this.submit()">
  
  <template $each="this.state.schema || []">
    <div style="display:contents" $if="this.checkIf(item)">
        <label $name="item.name" $class="\${this.inline ? 'mb-0 text-muted text-nowrap' : 'col-form-label text-muted'}" $text="item.label"></label>
        <div control-wrapper $class="\${this.inline ? 'd-flex align-items-center' : 'mb-3 d-flex align-items-center'}">
          <input $if="['text', 'password', 'email', 'number', 'date', 'datetime', 'file'].includes(item.type)" $name="item.name" $type="item.type" $.="item.setting || {}" $bind="this.data[item.name]" $class="form-control \${item.type === 'number' ? 'text-end' : ''}">
          
          <select $if="item.type === 'select'" $name="item.name" $.="item.setting || {}" $bind="this.data[item.name]" class="form-select">
              <option value="" $if="item.placeholder" $text="item.placeholder" disabled selected></option>
              <option $each="item.options" as="opt" $value="opt.value !== undefined ? opt.value : opt" $text="opt.label || opt"></option>
          </select>
          
          <div $if="['checkbox', 'radio'].includes(item.type)" class="d-flex align-items-center flex-wrap gap-3 h-100" style="padding: 0 0.75rem; min-height: calc(2.25rem + 2px);">
              <label $each="item.options || [item.text||item.label||item.name]" as="opt" class="form-check mb-0 d-flex align-items-center" style="padding-left:0; cursor:pointer;">
                  <input $name="item.name" class="form-check-input m-0 me-2" style="float:none;" $type="item.type" $.="item.setting || {}" $value="item.options ? (opt.value !== undefined ? opt.value : opt) : 'on'" $bind="this.data[item.name]">
                  <span $if="!this.inline || (item.options && item.options.length > 0)" $text="opt.label || opt" class="form-check-label"></span>
              </label>
          </div>

          <div $if="item.type === 'switch'" class="form-check form-switch fs-5 d-flex align-items-center m-0" style="padding: 0 0.75rem; min-height: calc(2.25rem + 2px); display:flex !important;">
              <input $name="item.name" class="form-check-input m-0" style="float:none; cursor:pointer" type="checkbox" $bind="this.data[item.name]">
          </div>
          
          <textarea $if="item.type === 'textarea'" $name="item.name" class="form-control" $.="item.setting || {}" $bind="this.data[item.name]"></textarea>
        </div>
    </div>
  </template>

  <div $if="!this.nobutton" $class="\${this.inline ? '' : 'd-flex justify-content-end align-items-baseline gap-3 mt-2'}" $style="\${this.inline ? '' : 'grid-column:1/-1'}">
      <div slot-id="actions"></div>
      <button type="submit" class="btn btn-primary" $text="this.submitlabel || '{#Submit#}'"></button>
  </div>
  </form>
</div>
`
  );
  const AUTOFORM_STYLE = globalThis.Util.makeDom(
    /*html*/
    `<style>
        .auto-grid-form { display: block; }
        .auto-grid-form .col-form-label { text-align: left; margin-bottom: 0.25rem; padding-top: 0; }

        @media (min-width: 576px) { 
                .auto-grid-form { display: grid; grid-template-columns: max-content 1fr; gap: 0 1.5rem; } 
                .auto-grid-form .col-form-label { text-align: right; margin-bottom: 1rem; padding-right: 0; max-width: 200px; padding-top: calc(0.375rem + 1px); } 
        .auto-grid-form [control-wrapper] { min-height: calc(2.25rem + 2px); }
        }

        .auto-grid-form.forced-horizontal { display: grid !important; grid-template-columns: max-content 1fr !important; gap: 0 1.5rem !important; }
        .auto-grid-form.forced-horizontal .col-form-label { text-align: right !important; margin-bottom: 1rem !important; padding-right: 0 !important; max-width: 200px !important; padding-top: calc(0.375rem + 1px) !important; }
    .auto-grid-form.forced-horizontal [control-wrapper] { min-height: calc(2.25rem + 2px) !important; }

    .auto-form-root .form-check-input { width: 1.2em; height: 1.2em; }
    .auto-form-root .form-switch .form-check-input { width: 2em; }
</style>`
  );
  globalThis.Component.register("AutoForm", (container) => {
    if (!container.state.schema) container.state.schema = [];
    const ensureProxy = (v) => v && typeof v === "object" && !v.__isProxy ? globalThis.NewState(v) : v;
    container.state.__watch("data", (v) => container.data = ensureProxy(v));
    container.data = ensureProxy(container.state.data || {});
    container.vertical = container.hasAttribute("vertical");
    container.horizontal = container.hasAttribute("horizontal");
    container.inline = container.hasAttribute("inline");
    container.nobutton = container.hasAttribute("nobutton");
    container.request = { method: "POST" };
    container.response = {};
    container.result = null;
    container.form = globalThis.$(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 = globalThis.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) globalThis.UI.toast(err.message, { type: "danger" });
        container.dispatchEvent(new CustomEvent("error", { detail: err, bubbles: true }));
      });
    };
    container.checkIf = (item) => {
      if (!item.if) return true;
      try {
        const fn = new Function("Hash", "LocalStorage", "State", "item", "data", "return " + item.if);
        return fn.call(container, globalThis.Hash, globalThis.LocalStorage, globalThis.State, item, container.data);
      } catch (e) {
        return false;
      }
    };
  }, AUTOFORM_BLUEPRINT, AUTOFORM_STYLE);
  const findAnchorInBlueprint = (root) => {
    let f = root.querySelector("[control-wrapper]");
    if (f) return f;
    for (const t of root.querySelectorAll("template")) {
      f = findAnchorInBlueprint(t.content);
      if (f) return f;
    }
    return null;
  };
  const AutoForm = {
    customTypes: [],
    register: (name, typeName) => {
      const type = typeName || name;
      if (!AutoForm.customTypes.find((t) => t.name === name)) {
        AutoForm.customTypes.push({ name, typeName: type });
        AutoForm._addAutoFormComponent(name, type);
      }
    },
    _addAutoFormComponent: (name, type) => {
      const wrapper = findAnchorInBlueprint(AUTOFORM_BLUEPRINT);
      if (wrapper) {
        const node = globalThis.Util.makeDom(`<${name} $if="item.type?.toLowerCase() === '${type.toLowerCase()}'" $name="item.name" $.="item.setting || {}" $bind="this.data[item.name]" class="w-100"></${name}>`);
        wrapper.appendChild(node);
      }
    }
  };
  globalThis.AutoForm = AutoForm;
  const VirtualScroll = (options = {}) => {
    const itemHeights = /* @__PURE__ */ new Map();
    const groupHeights = /* @__PURE__ */ new Map();
    let groupItemCount = 1;
    const avg = globalThis.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 infinite loop");
          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 };
      }
    };
  };
  globalThis.VirtualScroll = VirtualScroll;
  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 });
    });
  }
  globalThis.MouseMover = MouseMover;
  const APIComponent = globalThis.Component.register("API", (container) => {
    const presetRequest = container.request && typeof container.request === "object" ? container.request : {};
    const presetResponse = container.response && typeof container.response === "object" ? container.response : {};
    const presetResult = container.result && typeof container.result === "object" ? container.result : {};
    container.request = globalThis.NewState({
      url: "",
      method: "GET",
      headers: {},
      data: null,
      timeout: 1e4,
      responseType: "",
      ...presetRequest,
      headers: { ...presetRequest.headers || {} }
    });
    container.response = globalThis.NewState({
      loading: false,
      ok: null,
      status: null,
      error: null,
      headers: {},
      responseType: "",
      result: null,
      ...presetResponse,
      headers: { ...presetResponse.headers || {} }
    });
    container.result = globalThis.NewState(presetResult);
    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;
        globalThis.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)) globalThis.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;
      });
    });
  });
  globalThis.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 }));
    });
    globalThis.Util.copyFunction(container, container.modal, "show", "hide");
  }, globalThis.Util.makeDom(
    /*html*/
    `
<div class="modal fade" data-bs-backdrop="static">
  <div class="modal-dialog modal-dialog-centered">
    <div $class="modal-content border-\${this.state?.type || 'primary'} border-2 shadow-lg">
      <div slot-id="header" class="modal-header py-2 px-3 bg-light">
        <h6 $class="modal-title fw-bold text-\${this.state?.type || 'primary'}" $text="this.state?.title"></h6>
        <button type="button" class="btn btn-link ms-2 bi bi-x-lg link-reset p-0" style="color:inherit; text-decoration:none" data-bs-dismiss="modal"></button>
      </div>
      <div slot-id="body" class="modal-body p-3"></div>
      <div slot-id="footer" class="modal-footer py-2 px-3 bg-light"></div>
    </div>
  </div>
</div>
`
  ));
  globalThis.Component.register("Dialog", globalThis.Component.getSetupFunction("Modal"), globalThis.Util.makeDom(
    /*html*/
    `
<div class="modal fade" data-bs-backdrop="static">
  <div class="modal-dialog modal-dialog-centered">
    <div $class="modal-content border-\${this.state?.type || 'primary'} border-2 shadow-lg">
      <template $if="this.state?.title">
        <div $class="modal-header py-2 px-3 bg-light fw-bold text-\${this.state?.type || 'primary'}" $text="this.state?.title"></div>
      </template>
      <div class="modal-body p-4 text-center">
        <div $text="this.state?.message" class="fs-5 text-secondary"></div>
      </div>
      <div class="modal-footer py-2 px-3 bg-light border-0 d-flex justify-content-center gap-3">
        <template $each="this.state?.buttons">
          <button type="button" $class="btn btn-\${this.state?.type} px-4" data-bs-dismiss="modal" $onclick="this.result=index+1" $text="item"></button>
        </template>
      </div>
    </div>
  </div>
</div>
`
  ));
  let _dialogCount = 0;
  globalThis.UI.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();
      });
    });
  };
  globalThis.UI.alert = function(message, options = {}) {
    return globalThis.UI.showDialog({ message, ...options });
  };
  globalThis.UI.confirm = function(message, options = {}) {
    return new Promise((resolve) => globalThis.UI.showDialog({ message, buttons: ["{#Cancel#}", "{#Confirm#}"], ...options }).then((index2) => resolve(index2 >= 2)).catch(() => resolve(false)));
  };
  globalThis.Component.register("TagsInput", (container) => {
    container._thisObj = container;
    container.addEventListener("bind", (e) => {
      container.state.tags = Array.isArray(e.detail) ? e.detail : [];
    });
  }, globalThis.Util.makeDom(
    /*html*/
    `
<div class="form-control d-flex flex-wrap gap-1 align-items-center" style="min-height:38px;cursor:text">
  <template $each="this.state.tags">
    <button type="button" class="btn btn-sm btn-outline-primary rounded-pill py-0 px-2" $onkeydown='${globalThis.Util.getFunctionBody(function(event) {
      if (["Backspace", "Delete"].includes(event.key)) {
        event.preventDefault();
        this.state.tags.splice(index, 1);
        this.state.tags = this.state.tags;
        this.dispatchEvent(new CustomEvent("change", { bubbles: false, detail: this.state.tags }));
        Promise.resolve().then(() => {
          const buttons = globalThis.$$(this, "button");
          (buttons.length > 0 ? buttons[index > 0 ? index - 1 : 0] : globalThis.$(this, "input")).focus();
        });
      }
    })}' $text="item"></button>
  </template>
  <input type="text" class="border-0 shadow-none py-0 px-2 flex-grow-1 bg-transparent" placeholder="{#new tag name#}" style="min-width:100px;width:0;outline:none" $onchange="event.stopPropagation()" $oninput="event.stopPropagation()" $onkeydown='${globalThis.Util.getFunctionBody(function(event) {
      if (event.isComposing) return;
      if (["Enter", ",", " "].includes(event.key)) {
        event.preventDefault();
        const v = thisNode.value.trim();
        if (v && !this.state.tags.includes(v)) {
          this.state.tags.push(v);
          this.state.tags = this.state.tags;
          this.dispatchEvent(new CustomEvent("change", { bubbles: false, detail: this.state.tags }));
        }
        thisNode.value = "";
      }
    })}'>
</div>
`
  ), globalThis.Util.makeDom(
    /*html*/
    `<style>TagsInput button:focus {background-color:var(--bs-btn-hover-bg);color:var(--bs-btn-hover-color)}</style>`
  ));
  if (globalThis.AutoForm) {
    globalThis.AutoForm.register("TagsInput");
  }
  globalThis.Component.register("DatePicker", (container) => {
    container._thisObj = container;
    container.state = globalThis.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;
      }
    };
  }, globalThis.Util.makeDom(
    /*html*/
    `
<div class="d-flex align-items-center gap-1 w-100">
  <input type="date" class="form-control h-100" $bind="this.state.start" $onchange="this.updateStart(thisNode.value)">
  <template $if="this.isRange">
    <span class="text-muted mx-1">-</span>
    <input type="date" class="form-control h-100" $bind="this.state.end" $onchange="this.updateEnd(thisNode.value)">
  </template>
</div>
`
  ));
  if (globalThis.AutoForm) {
    globalThis.AutoForm.register("DatePicker");
  }
  globalThis.Component.register("ColorPicker", (container) => {
    container._thisObj = container;
    container.state = globalThis.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 }));
    };
  }, globalThis.Util.makeDom(
    /*html*/
    `
<div class="d-flex align-items-center gap-2 w-100 h-100">
  <input type="color" class="form-control form-control-color flex-shrink-0" style="width: 3rem; padding: 0.25rem" $bind="this.state.value" $onchange="this.updateValue(thisNode.value)">
  <input type="text" class="form-control" $bind="this.state.value" $onchange="this.updateValue(thisNode.value)">
</div>
`
  ));
  if (globalThis.AutoForm) {
    globalThis.AutoForm.register("ColorPicker");
  }
  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"];
  globalThis.Component.register("IconPicker", (container) => {
    container._thisObj = container;
    container.state = globalThis.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 = globalThis.$(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));
  }, globalThis.Util.makeDom(
    /*html*/
    `
<div class="dropdown w-100 h-100">
  <button class="btn btn-outline-secondary dropdown-toggle w-100 h-100 d-flex align-items-center justify-content-between px-2" type="button" $onclick="this.toggle()">
    <span class="d-flex align-items-center overflow-hidden">
        <i $if="this.state.value" $class="bi bi-\${this.state.value} me-2"></i>
        <span class="text-truncate" $text="this.state.value || '{#Select Icon#}'"></span>
    </span>
  </button>
  <div $class="dropdown-menu p-2 shadow \${this.state.open ? 'show' : ''}" style="width: 300px; max-height: 350px; overflow-y: hidden; display: flex; flex-direction: column; z-index: 1050">
    <input type="text" class="form-control form-control-sm mb-2" placeholder="Search icons..." $bind="this.state.search" $onclick="event.stopPropagation()">
    <div class="d-flex flex-wrap gap-1 overflow-auto p-1" style="flex: 1" $onclick="event.stopPropagation()">
        <template $each="this.filteredIcons" as="icon">
            <button type="button" class="btn btn-sm btn-outline-light text-dark p-2 d-flex align-items-center justify-content-center" $onclick="this.selectIcon(icon)" $title="icon" style="width: 40px; height: 40px">
                <i $class="bi bi-\${icon}"></i>
            </button>
        </template>
        <div $if="this.filteredIcons.length === 0" class="text-muted p-2 w-100 text-center">No icons found</div>
    </div>
  </div>
</div>
`
  ), globalThis.Util.makeDom(
    /*html*/
    `<style>
    IconPicker { display: block; height: 100%; }
    IconPicker .dropdown-menu { left: 0; }
    IconPicker .btn-outline-light:hover { background-color: var(--bs-primary-bg-subtle); border-color: var(--bs-primary); }
</style>`
  ));
  if (globalThis.AutoForm) {
    globalThis.AutoForm.register("IconPicker");
  }
  globalThis.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 = globalThis.NewState({});
    container.state.renderedList = [];
    container.addEventListener("bind", (e) => {
      container.state.selectedItem = e.detail;
    });
    const matchString = (val, query) => {
      if (query === void 0 || query === null || query === "") return true;
      return String(val || "").toLowerCase().includes(String(query).toLowerCase());
    };
    const matchesItem = (item, filter, filterFunc) => {
      if (filterFunc && !filterFunc(item)) return false;
      if (filter === 0) filter = "";
      if (filter !== void 0 && filter !== null && filter !== "") {
        if (typeof filter === "object") {
          for (const [key, qVal] of Object.entries(filter)) {
            if (qVal !== void 0 && qVal !== null && qVal !== "") {
              if (!matchString(item[key], qVal)) return false;
            }
          }
        } else {
          const q = String(filter).trim();
          if (q) {
            const labelMatches = matchString(item[container.labelfield], q);
            const summaryMatches = matchString(item[container.summaryfield], q);
            if (!labelMatches && !summaryMatches) return false;
          }
        }
      }
      return true;
    };
    const updateFlatList = () => {
      var _a;
      globalThis.Util.updateDefaults(container, defaultSets);
      let list = container.state.list || [];
      let filter = container.state.filter;
      let order = container.state.order;
      const filterFunc = container._filterFunc;
      const orderFunc = container._orderFunc;
      if (filter === 0) filter = "";
      if (order === 0) order = "";
      if (orderFunc) {
        list = [...list].sort(orderFunc);
      } else if (order) {
        if (typeof order === "string") {
          const parts = order.trim().split(/\s+/);
          const field = parts[0];
          const isDesc = ((_a = parts[1]) == null ? void 0 : _a.toLowerCase()) === "desc" || field.startsWith("-");
          const cleanField = field.startsWith("-") ? field.slice(1) : field;
          list = [...list].sort((a, b) => {
            const valA = a[cleanField];
            const valB = b[cleanField];
            if (valA === void 0 || valA === null) return isDesc ? 1 : -1;
            if (valB === void 0 || valB === null) return isDesc ? -1 : 1;
            if (typeof valA === "string" && typeof valB === "string") {
              return isDesc ? valB.localeCompare(valA) : valA.localeCompare(valB);
            }
            return isDesc ? valB > valA ? 1 : -1 : valA > valB ? 1 : -1;
          });
        }
      }
      const flatList = [];
      if (container.mode === "group") {
        const filteredList = list.filter((item) => matchesItem(item, filter, filterFunc));
        const itemMap = {};
        filteredList.forEach((item) => {
          var _a2;
          return (itemMap[_a2 = item[container.groupfield]] ?? (itemMap[_a2] = [])).push(item);
        });
        (container.state.groups || []).forEach((group) => {
          const items = itemMap[group[container.groupidfield]];
          let groupMatches = false;
          if (filter !== void 0 && filter !== null && filter !== "") {
            if (typeof filter === "object") {
              groupMatches = true;
              for (const [key, qVal] of Object.entries(filter)) {
                if (qVal !== void 0 && qVal !== null && qVal !== "") {
                  if (!matchString(group[key], qVal)) {
                    groupMatches = false;
                    break;
                  }
                }
              }
            } else {
              const q = String(filter).trim();
              if (q) {
                groupMatches = matchString(group[container.grouplabelfield], q) || matchString(group[container.groupsummaryfield], q);
              }
            }
          }
          if (items && items.length > 0 || groupMatches || !filter && !filterFunc) {
            flatList.push({ type: "group", ...group });
            if (items) items.forEach((item) => flatList.push({ type: "item", ...item }));
          }
        });
      } else if (container.mode === "tree") {
        const itemMap = {};
        list.forEach((item) => {
          itemMap[item[container.idfield]] = item;
        });
        const matches = /* @__PURE__ */ new Set();
        list.forEach((item) => {
          if (matchesItem(item, filter, filterFunc)) {
            matches.add(item[container.idfield]);
          }
        });
        const keep = new Set(matches);
        if (filter || filterFunc) {
          matches.forEach((id) => {
            let curr = itemMap[id];
            while (curr) {
              const parentId = curr[container.parentfield];
              if (!parentId) break;
              if (keep.has(parentId)) break;
              keep.add(parentId);
              curr = itemMap[parentId];
            }
          });
        }
        const childrenMap = {};
        list.forEach((item) => {
          if (!filter && !filterFunc || keep.has(item[container.idfield])) {
            const parentId = item[container.parentfield] || "";
            (childrenMap[parentId] ?? (childrenMap[parentId] = [])).push(item);
          }
        });
        const traverse = (items, level, parents) => items.forEach((item) => {
          var _a2;
          const id = item[container.idfield];
          const hasChildren = !!((_a2 = childrenMap[id]) == null ? void 0 : _a2.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 {
        const filteredList = list.filter((item) => matchesItem(item, filter, filterFunc));
        filteredList.forEach((item) => flatList.push({ type: "item", ...item }));
      }
      container.state.flatList = flatList;
    };
    container.state.__watch("list", updateFlatList);
    container.state.__watch("filter", updateFlatList);
    container.state.__watch("order", updateFlatList);
    const vs = container.fast ? globalThis.VirtualScroll() : null;
    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`;
          container.state.listStartIndex = res.listStartIndex;
          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("change", { detail: container.state.selectedItem, bubbles: false }));
      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();
      }
    };
    Object.defineProperty(container, "filterFunc", {
      get: () => container._filterFunc,
      set: (v) => {
        container._filterFunc = v;
        updateFlatList();
      }
    });
    Object.defineProperty(container, "orderFunc", {
      get: () => container._orderFunc,
      set: (v) => {
        container._orderFunc = v;
        updateFlatList();
      }
    });
    updateFlatList();
  }, globalThis.Util.makeDom(
    /*html*/
    `
<div class="list-group overflow-auto" onscroll="this.refresh()" style="overflow-anchor:none">
        <div class="vs-pad-top flex-shrink-0" style="height:0px;"></div>
        <template slot-id="item" $each="this.state.renderedList">
                <div $onupdate="this.onItemUpdate(index, thisNode)" $class="list-group-item d-inline-flex align-items-center pe-2 \${item.type==='group'?'bg-body-tertiary fw-bold ps-2':'list-group-item-action ' + (this.mode==='group'?'ps-4':'ps-2')} \${item.type==='group'?(this.state?.selectedGroup===item[this.groupidfield]?'active':''):(this.state?.selectedItem===item[this.idfield]?'active':'')}" $onclick="item.type==='group'?this.selectGroup(item,index):this.selectItem(item,index)">
                        <template $if="item.type === 'group'">
                                <template $if="this.groupicon">
                                        <span $class="bi bi-\${this.groupicon} text-body"></span>
                                </template>
                                <div class="flex-shrink-0 px-1" $text="\${item[this.grouplabelfield]}"></div>
                                <div class="text-muted small flex-fill text-end" $text="\${item[this.groupsummaryfield]}"></div>
                                <div slot-id="group-actions"></div>
                        </template>
                        <template $if="item.type === 'item'">
                                <template $if="this.mode === 'tree'">
                                        <div $style="width:\${item._level * 16 + (this.collapsible ? 16 : 0)}px; cursor:\${this.collapsible ? 'pointer' : 'default'}" class="text-end text-muted flex-shrink-0" $onclick="event.stopPropagation(); this.toggleCollapse(item)">
                                                <template $if="this.collapsible && item._hasChildren">
                                                        <i $class="bi \${this.collapsed[item[this.idfield]] ? 'bi-caret-right-fill' : 'bi-caret-down-fill'}"></i>
                                                </template>
                                        </div>
                                </template>
                                <template $if="this.mode === 'tree'">
                                        <span $class="text-muted bi bi-\${item._hasChildren ? this.groupicon : this.itemicon}"></span>
                                </template>
                                <template $if="this.mode !== 'tree' && this.itemicon">
                                        <span $class="bi bi-\${this.itemicon} text-body"></span>
                                </template>
                                <div class="flex-shrink-0 px-1" $text="\${item[this.labelfield]}"></div>
                                <div class="text-muted fs-7 small ms-2 flex-fill text-truncate text-end" $text="\${item[this.summaryfield]}"></div>
                                <div slot-id="item-actions"></div>
                        </template>
                </div>
        </template>
        <div class="vs-pad-bottom flex-shrink-0" style="height:0px;"></div>
</div>
`
  ));
  globalThis.Component.register("Nav", (container) => {
    container.vertical = container.hasAttribute("vertical");
    container.state.openName = container.state.openName || null;
    container.state.value = container.state.value || null;
    container.addEventListener("bind", (e) => {
      container.state.value = e.detail;
    });
    container.click = (item, noselect) => {
      if (!item.noselect && !noselect) {
        container.state.value = item.name;
        container.dispatchEvent(new CustomEvent("change", { detail: item.name, bubbles: false }));
      }
      container.dispatchEvent(new CustomEvent("nav", { detail: { item }, bubbles: false }));
    };
    container.clickSubitem = (item) => {
      container.state.value = item.name;
      container.dispatchEvent(new CustomEvent("change", { detail: item.name, bubbles: false }));
      container.dispatchEvent(new CustomEvent("nav", { detail: { item }, bubbles: false }));
    };
    container.toggleGroup = (item) => {
      container.state.openName = container.state.openName === item.name ? null : item.name;
      container.dispatchEvent(new CustomEvent("nav", { detail: { item, open: container.state.openName === item.name }, bubbles: false }));
    };
  }, globalThis.Util.makeDom(
    /*html*/
    `
<div $class="\${this.vertical ? 'd-flex flex-column border-end h-100 align-self-stretch overflow-visible' : 'navbar navbar-expand border-bottom'} bg-body-secondary px-2 \${this.vertical ? 'py-3' : 'py-1'} align-items-center \${this.vertical ? 'align-items-start' : ''}" $style="this.vertical ? 'min-height:0;' : ''">
	<div class="d-flex align-items-center gap-2">
        <template $if="this.state?.brand?.image">
                <img $src="this.state.brand.image" $class="\${this.vertical ? 'mb-2' : 'mx-1'}" style="height:30px;width:auto;max-width:300px">
        </template>
        <template $if="this.state?.brand?.icon">
                <i $class="bi bi-\${this.state.brand.icon} \${this.vertical ? 'mb-2' : 'mx-1'}"></i>
        </template>
        <template $if="this.state?.brand?.label">
                <span $class="\${this.vertical ? 'mb-2 fw-bold' : 'mx-1'}" style="transform: translateY(3px);" $text="this.state.brand.label"></span>
        </template>
	</div>
	<div $class="d-flex \${this.vertical ? 'w-100 flex-fill mt-2 flex-column' : 'ms-2 align-items-center flex-fill'}" $style="this.vertical ? 'min-height:0; overflow-y:auto; overflow-x:visible;' : ''">
		<template $each="this.state?.list || []">
			<div $class="\${this.vertical ? 'nav nav-pills flex-column w-100 position-relative' : 'navbar-nav flex-row align-items-center'} \${!this.vertical && item.type==='fill' ? 'flex-fill' : ''}" style="transform: translateY(3px);">
			<template $if="item.type==='label'">
				<div $class="\${this.vertical ? 'small text-uppercase text-body-secondary fw-semibold px-2 py-1 mt-2' : 'navbar-text small text-uppercase text-body-secondary fw-semibold px-2'} \${item.class || ''}" $text="item.label"></div>
			</template>
			<template $if="item.type==='button'">
				<button $class="nav-link \${this.state.value===item.name?'active':''} \${this.vertical ? 'w-100 text-start py-1 px-2' : ''} \${item.class || ''}" $onclick="this.click(item)">
						<i $class="bi bi-\${item.icon} me-2"></i><span $class="\${this.vertical ? 'text-truncate' : 'd-none d-' + (this.state?.list?.length>5?'lg':'md') + '-inline'}" $text="item.label"></span>
				</button>
				</template>
				<template $if="item.type==='dropdown'">
					<template $if="this.vertical">
						<div class="w-100">
							<button type="button" $class="nav-link w-100 text-start py-1 px-2 d-flex align-items-center justify-content-between \${item.class || ''}" $onclick="this.toggleGroup(item)">
								<span class="d-inline-flex align-items-center">
									<i $class="bi bi-\${item.icon} me-2"></i><span class="text-truncate" $text="item.label"></span>
								</span>
								<i $class="bi \${this.state.openName===item.name?'bi-chevron-down':'bi-chevron-right'} small ms-2"></i>
							</button>
							<template $if="this.state.openName===item.name">
								<div class="d-flex flex-column w-100 ps-2 mt-1">
									<template $each="item.list" as="subitem">
										<template $if="subitem.type==='label'">
											<div $class="small text-uppercase text-body-secondary fw-semibold px-2 py-1 \${subitem.class || ''}" $text="subitem.label"></div>
										</template>
										<template $if="subitem.type==='button'">
											<button $class="nav-link w-100 text-start py-1 px-2 \${subitem.class || ''}" $onclick="this.clickSubitem(subitem)">
												<i $class="bi bi-\${subitem.icon} me-2 d-inline-block" style="width: 16px;"></i><span $text="subitem.label"></span>
											</button>
										</template>
										<template $if="subitem.type==='switch'">
											<div $class="d-flex align-items-center px-2 py-2 \${subitem.class || ''}">
												<i $class="bi bi-\${subitem.icon} me-2 d-inline-block" style="width: 16px;"></i><span $text="subitem.label"></span><div class="flex-fill"></div>
												<div class="form-switch ms-2"><input class="form-check-input" type="checkbox" $bind="subitem.bind[subitem.name]"></div>
											</div>
										</template>
									</template>
								</div>
							</template>
						</div>
					</template>
					<template $if="!this.vertical">
						<div class="dropdown">
							<button type="button" $class="nav-link dropdown-toggle \${item.class || ''}" data-bs-toggle="dropdown">
								<i $class="bi bi-\${item.icon} me-2"></i><span $class="'d-none d-' + (this.state?.list?.length>5?'lg':'md') + '-inline'" $text="item.label"></span>
							</button>
							<div class="dropdown-menu dropdown-menu-end p-2 bg-body-secondary shadow" $style="'width: ' + (item.width || 250) + 'px;'">
								<template $each="item.list" as="subitem">
									<template $if="subitem.type==='label'">
										<div $class="dropdown-header \${subitem.class || ''}" $text="subitem.label"></div>
									</template>
									<template $if="subitem.type==='button'">
										<button $class="dropdown-item \${subitem.class || ''}" $onclick="this.clickSubitem(subitem)">
											<i $class="bi bi-\${subitem.icon} me-2 d-inline-block" style="width: 16px;"></i><span $text="subitem.label"></span>
										</button>
									</template>
									<template $if="subitem.type==='switch'">
										<div $class="d-flex align-items-center px-3 py-2 \${subitem.class || ''}">
											<i $class="bi bi-\${subitem.icon} me-2 d-inline-block" style="width: 16px;"></i><span $text="subitem.label"></span><div class="flex-fill"></div>
												<div class="form-switch ms-2"><input class="form-check-input" type="checkbox" $bind="subitem.bind[subitem.name]"></div>
										</div>
									</template>
								</template>
							</div>
						</div>
					</template>
				</template>
			</div>
		</template>
	</div>
</div>
`
  ));
  globalThis.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;
      globalThis.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 }));
        }
      });
    });
  }, globalThis.Util.makeDom(
    /*html*/
    `
<div $class="border-\${this.isVertical?'top':'start'} flex-shrink-0" $style="\${this.isVertical?'height':'width'}:3px;\${!this.isVertical?'height':'width'}:100%;cursor:\${this.isVertical?'row-resize':'col-resize'}"></div>
`
  ));
  Object.defineProperty(exports2, "State", {
    enumerable: true,
    get: () => state.State
  });
  exports2.APIComponent = APIComponent;
  exports2.AutoForm = AutoForm;
  exports2.HTTP = HTTP;
  exports2.MouseMover = MouseMover;
  exports2.UI = UI;
  exports2.VirtualScroll = VirtualScroll;
  Object.defineProperty(exports2, Symbol.toStringTag, { value: "Module" });
});
