diff --git a/.vite/deps_temp_89ecc3b4/package.json b/.vite/deps_temp_89ecc3b4/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/.vite/deps_temp_89ecc3b4/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 812eb69..75d4317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,6 @@ # Changelog -## [1.0.17] - 2026-06-09 -### Changed -- **Non-ESM Architecture**: Refactored all source components to remove `import`/`export` and use IIFE wrappers, attaching directly to `globalThis`. -- **Synchronous Dependency Loading**: Redesigned the test suite to load dependencies synchronously in the ``, ensuring absolute timing stability and 100% production parity. -- **Vite Alignment**: Updated build configuration to prioritize IIFE/UMD output and removed ESM distribution artifacts. -- **Documentation**: Updated README with the new "Synchronous First" quick start guide and latest CDN versions. - -## [1.0.14] - 2026-06-08 +## [1.0.13] - 2026-06-08 ### Fixed - **State Integration**: Bumped `@apigo.cc/state` to `1.0.18` to restore the globally injected `$`/`$$` helpers from the original UMD era, fixing `TagsInput` element targeting. - **AutoForm Layout**: Fixed `horizontal` grid layout breakage caused by missing `AUTOFORM_STYLE` registration. diff --git a/README.md b/README.md index 43760f2..f7b329f 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,20 @@ # @apigo.cc/base - AI 逻辑操作说明书 (示例驱动) -本库是基于 `@apigo.cc/state` 增强的 UI 原子库,提供遵循 Bootstrap 5.3 规范的高阶业务组件。本包采用完全的全局注入模式。 - ---- - -## 全局变量与核心 API 清单 -引入 `base.js` 后,以下对象将直接挂载到 `window` / `globalThis` 上,可随时随地调用: - -| 全局变量名 | 用途说明 | -| :--- | :--- | -| **`HTTP`** | 异步请求工具 (`HTTP.get`, `HTTP.post`, `HTTP.request` 等)。 | -| **`UI`** | 交互组件触发器 (`UI.toast`, `UI.alert`, `UI.confirm`, `UI.showDialog`)。 | -| **`AutoForm`** | 表单组件扩展注册器 (`AutoForm.register`) 和自定义控件库 (`AutoForm.customTypes`)。 | -| **`MouseMover`** | 拖拽辅助工具 (`MouseMover.start`)。 | -| **`VirtualScroll`** / **`List`** | 虚拟滚动列表核心算法类。 | - -*(注意:底层状态库 `@apigo.cc/state` 暴露的 `NewState`, `Hash`, `LocalStorage`, `State`, `$`, `$$`, `Component` 以及危险的高级 API `_unsafeRefreshState` 也在全局可用。严禁随意调用 `_unsafeRefreshState`。)* +本库是基于 `@apigo.cc/state` 增强的 UI 原子库,提供遵循 Bootstrap 5.3 规范的高阶业务组件。 --- ## 1. 快速集成 (Quick Start) +### 同步 UMD 集成 (推荐:消灭异步时序风险,实现“秒开”渲染) 将脚本放置在 `` 中,确保地基在 DOM 解析前就绪: ```html - + - - - + + + ``` +### ESM 模块引入 (Legacy) +```javascript +const { HTTP, UI, AutoForm, State } = window.ApigoBase +``` + --- ## 2. 组件超能力示例 (Mega Examples) diff --git a/dist/base.js b/dist/base.js index 74d4b7a..54a45d9 100644 --- a/dist/base.js +++ b/dist/base.js @@ -1,22 +1,113 @@ -(function() { +(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"; - (function(global) { - const { Component, Util, $ } = global; - const UI = {}; - Component.register("Modal", (container) => { - container.modal = new bootstrap.Modal(container); - container.addEventListener("bind", (e) => { - e.detail ? container.modal.show() : container.modal.hide(); + 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); + }); }); - container.addEventListener("hide.bs.modal", () => { - var _a; - (_a = document.activeElement) == null ? void 0 : _a.blur(); - container.dispatchEvent(new CustomEvent("change", { bubbles: false, detail: false })); + }; + 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; }); - Util.copyFunction(container, container.modal, "show", "hide"); - }, Util.makeDom( - /*html*/ - ` + }); + }); + 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*/ + `