From a0701422f5dcb684cf4470529bbd0f5a12d85531 Mon Sep 17 00:00:00 2001 From: natxocc Date: Sat, 9 May 2026 16:06:54 +0200 Subject: [PATCH] Megacompact f1 --- Readme.md | 2 +- dist/sigpro.esm.js | 595 ++++++++++------------ dist/sigpro.esm.min.js | 2 +- dist/sigpro.js | 596 ++++++++++------------- dist/sigpro.min.js | 2 +- dist/sigpro.plus.js | 447 ----------------- dist/sigpro.utils.js | 396 +++++++++++++++ docs/router.md | 4 +- docs/sigpro.js | 596 ++++++++++------------- package.json | 8 +- src/build_umd.js | 2 +- src/sigpro.js | 623 +++++++++--------------- src/{sigpro.plus.js => sigpro.utils.js} | 0 13 files changed, 1430 insertions(+), 1843 deletions(-) delete mode 100644 dist/sigpro.plus.js create mode 100644 dist/sigpro.utils.js rename src/{sigpro.plus.js => sigpro.utils.js} (100%) diff --git a/Readme.md b/Readme.md index 0365b92..baa2aba 100644 --- a/Readme.md +++ b/Readme.md @@ -3,7 +3,7 @@ Blazing fast, zero-overhead, vanilla JS renderer with atomic reactivity. # `SigPro` [![npm version](https://img.shields.io/npm/v/sigpro.svg)](https://www.npmjs.com/package/sigpro) -![js size](https://img.shields.io/badge/js_size-2.8_kB_brotli-blue) +![js size](https://img.shields.io/badge/js_size-2.7_kB_brotli-blue) [![license](https://img.shields.io/npm/l/sigpro)](https://github.com/natxocc/sigpro/blob/main/LICENSE) [**Explore the Docs →**](https://sigpro.natxocc.com/#/) diff --git a/dist/sigpro.esm.js b/dist/sigpro.esm.js index 7b9cd80..0f833a1 100644 --- a/dist/sigpro.esm.js +++ b/dist/sigpro.esm.js @@ -1,443 +1,384 @@ // src/sigpro.js -var isFunc = (f) => typeof f === "function"; -var isObj = (o) => o && typeof o === "object"; +var isFunc = (f) => typeof f == "function"; +var isObj = (o) => o && typeof o == "object"; var isArr = Array.isArray; -var doc = typeof document !== "undefined" ? document : null; -var ensureNode = (n) => n?._isRuntime ? n.container : n instanceof Node ? n : doc.createTextNode(n == null ? "" : String(n)); -var activeEffect = null; -var activeOwner = null; -var isFlushing = false; -var batchDepth = 0; -var effectQueue = new Set; -var MOUNTED_NODES = new WeakMap; +var doc = typeof document < "u" ? document : null; +var txt = (s) => doc.createTextNode(s == null ? "" : String(s)); +var toNd = (n) => n?._rt ? n._cnt : n instanceof Node ? n : txt(n); +var Fragment = (p) => p.children; +var val = (v) => isFunc(v) ? v() : v; +var aEff = null; +var aOwn = null; +var isFlushing = 0; +var bDepth = 0; +var eQ = new Set; +var MOUNTED = new WeakMap; var SVG_NS = "http://www.w3.org/2000/svg"; -var XLINK_NS = "http://www.w3.org/1999/xlink"; +var XLINK = "http://www.w3.org/1999/xlink"; var SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")); -var dispose = (eff) => { - if (!eff || eff._disposed) +var DANG_ATTR = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]); +var clr = (s) => { + if (s) { + s.forEach((f) => f()); + s.clear(); + } +}; +var dispose = (e) => { + if (!e || e._x) return; - eff._disposed = true; - const stack = [eff]; - while (stack.length) { - const e = stack.pop(); - if (e._cleanups) { - e._cleanups.forEach((fn) => fn()); - e._cleanups.clear(); + e._x = 1; + let st = [e], c; + while (c = st.pop()) { + clr(c._c); + if (c._ch) { + c._ch.forEach((x) => st.push(x)); + c._ch.clear(); } - if (e._children) { - e._children.forEach((child) => stack.push(child)); - e._children.clear(); - } - if (e._deps) { - e._deps.forEach((depSet) => depSet.delete(e)); - e._deps.clear(); + if (c._d) { + c._d.forEach((d) => d.delete(c)); + c._d.clear(); } } }; -var onUnmount = (fn) => { - if (activeOwner) - (activeOwner._cleanups ||= new Set).add(fn); -}; -var untrack = (fn) => { - const p = activeEffect; - activeEffect = null; +var onUnmount = (f) => aOwn && (aOwn._c ||= new Set).add(f); +var untrack = (f) => { + let p = aEff; + aEff = null; try { - return fn(); + return f(); } finally { - activeEffect = p; + aEff = p; } }; -var createEffect = (fn, isComputed = false) => { - const effect = () => { - if (effect._disposed) +var createEffect = (f, isC = 0) => { + const e = () => { + if (e._x) return; - if (effect._deps) - effect._deps.forEach((s) => s.delete(effect)); - if (effect._cleanups) { - effect._cleanups.forEach((c) => c()); - effect._cleanups.clear(); - } - const prevEffect = activeEffect; - const prevOwner = activeOwner; - activeEffect = activeOwner = effect; + if (e._d) + e._d.forEach((s) => s.delete(e)); + clr(e._c); + let pE = aEff, pO = aOwn; + aEff = aOwn = e; try { - return effect._result = fn(); - } catch (e) { - console.error("[SigPro]", e); + return e._res = f(); + } catch (err) { + console.error("[SigPro]", err); } finally { - activeEffect = prevEffect; - activeOwner = prevOwner; + aEff = pE; + aOwn = pO; } }; - effect._deps = effect._cleanups = effect._children = null; - effect._disposed = false; - effect._isComputed = isComputed; - effect._depth = activeEffect ? activeEffect._depth + 1 : 0; - effect._mounts = []; - effect._parent = activeOwner; - if (activeOwner) - (activeOwner._children ||= new Set).add(effect); - return effect; + e._d = e._c = e._ch = null; + e._x = 0; + e._iC = isC; + e._dp = aEff ? aEff._dp + 1 : 0; + e._m = []; + e._p = aOwn; + if (aOwn) + (aOwn._ch ||= new Set).add(e); + return e; }; var flush = () => { if (isFlushing) return; - isFlushing = true; - const sorted = Array.from(effectQueue).sort((a, b) => a._depth - b._depth); - effectQueue.clear(); - for (const e of sorted) - if (!e._disposed) + isFlushing = 1; + let q = [...eQ].sort((a, b) => a._dp - b._dp); + eQ.clear(); + for (let e of q) + if (!e._x) e(); - isFlushing = false; + isFlushing = 0; }; -var batch = (fn) => { - batchDepth++; +var batch = (f) => { + bDepth++; try { - return fn(); + return f(); } finally { - batchDepth--; - if (batchDepth === 0 && effectQueue.size > 0 && !isFlushing) + if (!--bDepth && eQ.size && !isFlushing) flush(); } }; -var trackUpdate = (subs, trigger = false) => { - if (!trigger && activeEffect && !activeEffect._disposed) { - subs.add(activeEffect); - (activeEffect._deps ||= new Set).add(subs); - } else if (trigger && subs.size > 0) { - let hasQueue = false; - for (const e of subs) { - if (e === activeEffect || e._disposed) +var trkUpd = (s, trg = 0) => { + if (!trg && aEff && !aEff._x) { + s.add(aEff); + (aEff._d ||= new Set).add(s); + } else if (trg && s.size) { + let q = 0; + for (let e of s) { + if (e === aEff || e._x) continue; - if (e._isComputed) { - e._dirty = true; - if (e._subs) - trackUpdate(e._subs, true); + if (e._iC) { + e._dt = 1; + if (e._sb) + trkUpd(e._sb, 1); } else { - effectQueue.add(e); - hasQueue = true; + eQ.add(e); + q = 1; } } - if (hasQueue && !isFlushing && batchDepth === 0) + if (q && !isFlushing && !bDepth) queueMicrotask(flush); } }; -var $ = (val, key = null) => { - const subs = new Set; - if (isFunc(val)) { - let cache; - const computed = () => { - if (computed._dirty) { - const prev = activeEffect; - activeEffect = computed; +var $ = (v, k = null) => { + let s = new Set; + if (isFunc(v)) { + let c, cp = () => { + if (cp._dt) { + let p = aEff; + aEff = cp; try { - const next = val(); - if (!Object.is(cache, next)) { - cache = next; - trackUpdate(subs, true); + let n = v(); + if (!Object.is(c, n)) { + c = n; + trkUpd(s, 1); } } finally { - activeEffect = prev; + aEff = p; } - computed._dirty = false; + cp._dt = 0; } - trackUpdate(subs); - return cache; + trkUpd(s); + return c; }; - computed._isComputed = true; - computed._subs = subs; - computed._dirty = true; - computed._deps = null; - computed._disposed = false; - return computed; + cp._iC = cp._dt = 1; + cp._sb = s; + cp._d = null; + cp._x = 0; + return cp; } - if (key) + if (k) try { - val = JSON.parse(localStorage.getItem(key)) ?? val; + v = JSON.parse(localStorage.getItem(k)) ?? v; } catch (e) {} - return (...args) => { - if (args.length) { - const next = isFunc(args[0]) ? args[0](val) : args[0]; - if (!Object.is(val, next)) { - val = next; - if (key) - localStorage.setItem(key, JSON.stringify(val)); - trackUpdate(subs, true); + return (...a) => { + if (a.length) { + let n = isFunc(a[0]) ? a[0](v) : a[0]; + if (!Object.is(v, n)) { + v = n; + if (k) + localStorage.setItem(k, JSON.stringify(v)); + trkUpd(s, 1); } } - trackUpdate(subs); - return val; + trkUpd(s); + return v; }; }; -var watch = (sources, cb) => { - if (cb === undefined) { - const effect2 = createEffect(sources); - effect2(); - return () => dispose(effect2); - } - const effect = createEffect(() => { - const vals = isArr(sources) ? sources.map((s) => s()) : sources(); - untrack(() => cb(vals)); - }); - effect(); - return () => dispose(effect); +var watch = (src, cb) => { + let e = createEffect(cb ? () => { + let v = isArr(src) ? src.map((s) => s()) : src(); + untrack(() => cb(v)); + } : src); + e(); + return () => dispose(e); }; -var cleanupNode = (node) => { - if (!node) +var clnNd = (n) => { + if (!n) return; - if (node._cleanups) { - node._cleanups.forEach((fn) => fn()); - node._cleanups.clear(); - } - if (node._ownerEffect) - dispose(node._ownerEffect); - if (node.childNodes) - node.childNodes.forEach((n) => cleanupNode(n)); + clr(n._c); + if (n._oE) + dispose(n._oE); + if (n.childNodes) + n.childNodes.forEach(clnNd); }; -var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; -var DANGEROUS_URI_ATTRS = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]); -var isDangerousAttr = (key) => DANGEROUS_URI_ATTRS.has(key) || key.startsWith("on"); -var validateAttr = (key, val) => { - if (val == null || val === false) - return null; - if (isDangerousAttr(key)) { - const sVal = String(val); - if (DANGEROUS_PROTOCOL.test(sVal)) - return "#"; - } - return val; -}; -var h = (tag, props = {}, children = []) => { - if (props instanceof Node || isArr(props) || !isObj(props)) { - children = props; - props = {}; +var valAtt = (k, v) => v == null || v === false ? null : (DANG_ATTR.has(k) || k.startsWith("on")) && /^\s*(javascript|data|vbscript):/i.test(String(v)) ? "#" : v; +var h = (tag, prp = {}, ch = []) => { + if (prp instanceof Node || isArr(prp) || !isObj(prp)) { + ch = prp; + prp = {}; } if (isFunc(tag)) { - const effect = createEffect(() => { - const result2 = tag(props, { - children, - emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args) - }); - effect._result = result2; - return result2; - }); - effect(); - const result = effect._result; - if (result == null) + let e = createEffect(() => e._res = tag(prp, { children: ch, emit: (ev, ...a) => prp[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...a) })); + e(); + if (e._res == null) return null; - const node = result instanceof Node || isArr(result) && result.every((n) => n instanceof Node) ? result : doc.createTextNode(String(result)); - const attach = (n) => { - if (isObj(n) && !n._isRuntime) { - n._mounts = effect._mounts || []; - n._cleanups = effect._cleanups || new Set; - n._ownerEffect = effect; + let nd = e._res instanceof Node || isArr(e._res) && e._res.every((n) => n instanceof Node) ? e._res : txt(e._res); + let att = (n) => { + if (isObj(n) && !n._rt) { + n._m = e._m || []; + n._c = e._c || new Set; + n._oE = e; } }; - isArr(node) ? node.forEach(attach) : attach(node); - return node; + isArr(nd) ? nd.forEach(att) : att(nd); + return nd; } - const isSVG = SVG_TAGS.has(tag); - const el = isSVG ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag); - el._cleanups = new Set; - for (const k of Object.keys(props)) { - let v = props[k]; + let isS = SVG_TAGS.has(tag), el = isS ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag); + el._c = new Set; + for (let k in prp) { + let v = prp[k]; if (k === "ref") { isFunc(v) ? v(el) : v.current = el; continue; } - if (isSVG && k.startsWith("xlink:")) { - const cleanVal = validateAttr(k.slice(6), v); - cleanVal == null ? el.removeAttributeNS(XLINK_NS, k.slice(6)) : el.setAttributeNS(XLINK_NS, k.slice(6), cleanVal); + if (isS && k.startsWith("xlink:")) { + let cv = valAtt(k.slice(6), v); + cv == null ? el.removeAttributeNS(XLINK, k.slice(6)) : el.setAttributeNS(XLINK, k.slice(6), cv); continue; } if (k.startsWith("on")) { - const ev = k.slice(2).toLowerCase(); + let ev = k.slice(2).toLowerCase(); el.addEventListener(ev, v); - const off = () => el.removeEventListener(ev, v); - el._cleanups.add(off); + let off = () => el.removeEventListener(ev, v); + el._c.add(off); onUnmount(off); } else if (isFunc(v)) { - const effect = createEffect(() => { - const val = validateAttr(k, v()); + let e = createEffect(() => { + let r = valAtt(k, v()); if (k === "class") - el.className = val || ""; - else if (val == null) + el.className = r || ""; + else if (r == null) el.removeAttribute(k); - else if (k === "style" && typeof val === "string") - el.setAttribute("style", val); - else if (k in el && !isSVG) - el[k] = val; + else if (k === "style" && typeof r == "string") + el.setAttribute("style", r); + else if (k in el && !isS) + el[k] = r; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, r === true ? "" : r); }); - effect(); - el._cleanups.add(() => dispose(effect)); - onUnmount(() => dispose(effect)); + e(); + el._c.add(() => dispose(e)); + onUnmount(() => dispose(e)); if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) { - const evType = k === "checked" ? "change" : "input"; - el.addEventListener(evType, (ev) => v(ev.target[k])); + el.addEventListener(k === "checked" ? "change" : "input", (ev) => v(ev.target[k])); } } else { - const val = validateAttr(k, v); - if (val != null) { - if (k === "style" && typeof val === "string") - el.setAttribute("style", val); - else if (k in el && !isSVG) - el[k] = val; + let r = valAtt(k, v); + if (r != null) { + if (k === "style" && typeof r == "string") + el.setAttribute("style", r); + else if (k in el && !isS) + el[k] = r; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, r === true ? "" : r); } } } - const append = (c) => { + const app = (c) => { if (isArr(c)) - return c.forEach(append); + return c.forEach(app); if (isFunc(c)) { - const anchor = doc.createTextNode(""); - el.appendChild(anchor); - let currentNodes = []; - const effect = createEffect(() => { - const res = c(); - const next = (isArr(res) ? res : [res]).map(ensureNode); - currentNodes.forEach((n) => { - if (n._isRuntime) - n.destroy(); - else - cleanupNode(n); + let anc = txt(""), cur = []; + el.appendChild(anc); + let e = createEffect(() => { + let r = c(), nxt = (isArr(r) ? r : [r]).map(toNd), ref = anc; + cur.forEach((n) => { + n._rt ? n._del() : clnNd(n); if (n.parentNode) n.remove(); }); - let ref = anchor; - for (let i = next.length - 1;i >= 0; i--) { - const node = next[i]; - if (node.parentNode !== ref.parentNode) - ref.parentNode?.insertBefore(node, ref); - if (node._mounts) - node._mounts.forEach((fn) => fn()); - ref = node; + for (let i = nxt.length - 1;i >= 0; i--) { + let nd = nxt[i]; + if (nd.parentNode !== ref.parentNode) + ref.parentNode?.insertBefore(nd, ref); + if (nd._m) + nd._m.forEach((f) => f()); + ref = nd; } - currentNodes = next; + cur = nxt; }); - effect(); - el._cleanups.add(() => dispose(effect)); - onUnmount(() => dispose(effect)); + e(); + el._c.add(() => dispose(e)); + onUnmount(() => dispose(e)); } else { - const node = ensureNode(c); - el.appendChild(node); - if (node._mounts) - node._mounts.forEach((fn) => fn()); + let nd = toNd(c); + el.appendChild(nd); + if (nd._m) + nd._m.forEach((f) => f()); } }; - append(children); + app(ch); return el; }; -var render = (renderFn) => { - const cleanups = new Set; - const previousOwner = activeOwner; - const previousEffect = activeEffect; - const container = doc.createElement("div"); - container.style.display = "contents"; - container.setAttribute("role", "presentation"); - activeOwner = { _cleanups: cleanups }; - activeEffect = null; - const processResult = (result) => { - if (!result) +var render = (rFn) => { + let c = new Set, pO = aOwn, pE = aEff, cnt = doc.createElement("div"); + cnt.style.display = "contents"; + cnt.setAttribute("role", "presentation"); + aOwn = { _c: c }; + aEff = null; + const pRes = (r) => { + if (!r) return; - if (result._isRuntime) { - cleanups.add(result.destroy); - container.appendChild(result.container); - } else if (isArr(result)) { - result.forEach(processResult); - } else { - container.appendChild(result instanceof Node ? result : doc.createTextNode(String(result == null ? "" : result))); - } + if (r._rt) { + c.add(r._del); + cnt.appendChild(r._cnt); + } else if (isArr(r)) + r.forEach(pRes); + else + cnt.appendChild(r instanceof Node ? r : txt(r)); }; try { - processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) })); + pRes(rFn({ onCleanup: (f) => c.add(f) })); } finally { - activeOwner = previousOwner; - activeEffect = previousEffect; + aOwn = pO; + aEff = pE; } - return { - _isRuntime: true, - container, - destroy: () => { - cleanups.forEach((fn) => fn()); - cleanupNode(container); - container.remove(); - } - }; + return { _rt: 1, _cnt: cnt, _del: () => { + clr(c); + clnNd(cnt); + cnt.remove(); + } }; }; -var when = (cond, SIP, NOP = null) => { - const anchor = doc.createTextNode(""); - const root = h("div", { style: "display:contents" }, [anchor]); - let currentView = null; - watch(() => !!(isFunc(cond) ? cond() : cond), (show) => { - if (currentView) { - currentView.destroy(); - currentView = null; +var when = (c, Y, N = null) => { + let anc = txt(""), rt = h("div", { style: "display:contents" }, [anc]), v; + watch(() => !!val(c), (s) => { + if (v) { + v._del(); + v = null; } - const content = show ? SIP : NOP; - if (content) { - currentView = render(() => isFunc(content) ? content() : content); - root.insertBefore(currentView.container, anchor); + let ct = s ? Y : N; + if (ct) { + v = render(() => val(ct)); + rt.insertBefore(v._cnt, anc); } }); - onUnmount(() => currentView?.destroy()); - return root; + onUnmount(() => v?._del()); + return rt; }; -var each = (src, itemFn, keyField) => { - const anchor = doc.createTextNode(""); - const root = h("div", { style: "display:contents" }, [anchor]); - let cache = new Map; - watch(() => (isFunc(src) ? src() : src) || [], (items) => { - const nextCache = new Map; - const nextOrder = []; - const newItems = items || []; - for (let i = 0;i < newItems.length; i++) { - const item = newItems[i]; - const key = keyField ? item?.[keyField] ?? i : item?.id ?? i; - let view = cache.get(key); - if (!view) - view = render(() => itemFn(item, i)); +var each = (s, fn, kF) => { + let anc = txt(""), rt = h("div", { style: "display:contents" }, [anc]), cch = new Map; + watch(() => val(s) || [], (it) => { + let nCc = new Map, nOd = []; + for (let i = 0, l = (it || []).length;i < l; i++) { + let t = it[i], k = kF ? t?.[kF] ?? i : t?.id ?? i, v = cch.get(k); + if (!v) + v = render(() => fn(t, i)); else - cache.delete(key); - nextCache.set(key, view); - nextOrder.push(view); + cch.delete(k); + nCc.set(k, v); + nOd.push(v); } - cache.forEach((view) => view.destroy()); - let lastRef = anchor; - for (let i = nextOrder.length - 1;i >= 0; i--) { - const view = nextOrder[i]; - const node = view.container; - if (node.nextSibling !== lastRef) - root.insertBefore(node, lastRef); - lastRef = node; + cch.forEach((v) => v._del()); + let ref = anc; + for (let i = nOd.length - 1;i >= 0; i--) { + let nd = nOd[i]._cnt; + if (nd.nextSibling !== ref) + rt.insertBefore(nd, ref); + ref = nd; } - cache = nextCache; + cch = nCc; }); - return root; + return rt; }; -var Fragment = (props) => props.children; -var mount = (comp, target) => { - const t = typeof target === "string" ? doc.querySelector(target) : target; +var mount = (c, tgt) => { + let t = typeof tgt == "string" ? doc.querySelector(tgt) : tgt; if (!t) return; - if (MOUNTED_NODES.has(t)) - MOUNTED_NODES.get(t).destroy(); - const inst = render(isFunc(comp) ? comp : () => comp); - t.replaceChildren(inst.container); - MOUNTED_NODES.set(t, inst); - return inst; + if (MOUNTED.has(t)) + MOUNTED.get(t)._del(); + let i = render(isFunc(c) ? c : () => c); + t.replaceChildren(i._cnt); + MOUNTED.set(t, i); + return i; }; -if (typeof window !== "undefined") { - "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((tag) => { - window[tag] = (props, children) => h(tag, props, children); - }); +if (typeof window < "u") { + "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((t) => window[t] = (p, c) => h(t, p, c)); } export { when, watch, + val, render, onUnmount, mount, diff --git a/dist/sigpro.esm.min.js b/dist/sigpro.esm.min.js index 5db79c7..0be7fa0 100644 --- a/dist/sigpro.esm.min.js +++ b/dist/sigpro.esm.min.js @@ -1 +1 @@ -var y=(e)=>typeof e==="function",U=(e)=>e&&typeof e==="object",E=Array.isArray,w=typeof document<"u"?document:null,j=(e)=>e?._isRuntime?e.container:e instanceof Node?e:w.createTextNode(e==null?"":String(e)),u=null,_=null,b=!1,v=0,x=new Set,O=new WeakMap,V="http://www.w3.org/2000/svg",L="http://www.w3.org/1999/xlink",M=new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")),N=(e)=>{if(!e||e._disposed)return;e._disposed=!0;let s=[e];while(s.length){let t=s.pop();if(t._cleanups)t._cleanups.forEach((r)=>r()),t._cleanups.clear();if(t._children)t._children.forEach((r)=>s.push(r)),t._children.clear();if(t._deps)t._deps.forEach((r)=>r.delete(t)),t._deps.clear()}},A=(e)=>{if(_)(_._cleanups||=new Set).add(e)},P=(e)=>{let s=u;u=null;try{return e()}finally{u=s}},g=(e,s=!1)=>{let t=()=>{if(t._disposed)return;if(t._deps)t._deps.forEach((l)=>l.delete(t));if(t._cleanups)t._cleanups.forEach((l)=>l()),t._cleanups.clear();let r=u,o=_;u=_=t;try{return t._result=e()}catch(l){console.error("[SigPro]",l)}finally{u=r,_=o}};if(t._deps=t._cleanups=t._children=null,t._disposed=!1,t._isComputed=s,t._depth=u?u._depth+1:0,t._mounts=[],t._parent=_,_)(_._children||=new Set).add(t);return t},D=()=>{if(b)return;b=!0;let e=Array.from(x).sort((s,t)=>s._depth-t._depth);x.clear();for(let s of e)if(!s._disposed)s();b=!1},B=(e)=>{v++;try{return e()}finally{if(v--,v===0&&x.size>0&&!b)D()}},S=(e,s=!1)=>{if(!s&&u&&!u._disposed)e.add(u),(u._deps||=new Set).add(e);else if(s&&e.size>0){let t=!1;for(let r of e){if(r===u||r._disposed)continue;if(r._isComputed){if(r._dirty=!0,r._subs)S(r._subs,!0)}else x.add(r),t=!0}if(t&&!b&&v===0)queueMicrotask(D)}},z=(e,s=null)=>{let t=new Set;if(y(e)){let r,o=()=>{if(o._dirty){let l=u;u=o;try{let n=e();if(!Object.is(r,n))r=n,S(t,!0)}finally{u=l}o._dirty=!1}return S(t),r};return o._isComputed=!0,o._subs=t,o._dirty=!0,o._deps=null,o._disposed=!1,o}if(s)try{e=JSON.parse(localStorage.getItem(s))??e}catch(r){}return(...r)=>{if(r.length){let o=y(r[0])?r[0](e):r[0];if(!Object.is(e,o)){if(e=o,s)localStorage.setItem(s,JSON.stringify(e));S(t,!0)}}return S(t),e}},G=(e,s)=>{if(s===void 0){let r=g(e);return r(),()=>N(r)}let t=g(()=>{let r=E(e)?e.map((o)=>o()):e();P(()=>s(r))});return t(),()=>N(t)},C=(e)=>{if(!e)return;if(e._cleanups)e._cleanups.forEach((s)=>s()),e._cleanups.clear();if(e._ownerEffect)N(e._ownerEffect);if(e.childNodes)e.childNodes.forEach((s)=>C(s))},W=/^\s*(javascript|data|vbscript):/i,$=new Set(["src","href","formaction","action","background","code","archive"]),q=(e)=>$.has(e)||e.startsWith("on"),T=(e,s)=>{if(s==null||s===!1)return null;if(q(e)){let t=String(s);if(W.test(t))return"#"}return s},R=(e,s={},t=[])=>{if(s instanceof Node||E(s)||!U(s))t=s,s={};if(y(e)){let n=g(()=>{let f=e(s,{children:t,emit:(d,...h)=>s[`on${d[0].toUpperCase()}${d.slice(1)}`]?.(...h)});return n._result=f,f});n();let c=n._result;if(c==null)return null;let i=c instanceof Node||E(c)&&c.every((f)=>f instanceof Node)?c:w.createTextNode(String(c)),a=(f)=>{if(U(f)&&!f._isRuntime)f._mounts=n._mounts||[],f._cleanups=n._cleanups||new Set,f._ownerEffect=n};return E(i)?i.forEach(a):a(i),i}let r=M.has(e),o=r?w.createElementNS(V,e):w.createElement(e);o._cleanups=new Set;for(let n of Object.keys(s)){let c=s[n];if(n==="ref"){y(c)?c(o):c.current=o;continue}if(r&&n.startsWith("xlink:")){let i=T(n.slice(6),c);i==null?o.removeAttributeNS(L,n.slice(6)):o.setAttributeNS(L,n.slice(6),i);continue}if(n.startsWith("on")){let i=n.slice(2).toLowerCase();o.addEventListener(i,c);let a=()=>o.removeEventListener(i,c);o._cleanups.add(a),A(a)}else if(y(c)){let i=g(()=>{let a=T(n,c());if(n==="class")o.className=a||"";else if(a==null)o.removeAttribute(n);else if(n==="style"&&typeof a==="string")o.setAttribute("style",a);else if(n in o&&!r)o[n]=a;else o.setAttribute(n,a===!0?"":a)});if(i(),o._cleanups.add(()=>N(i)),A(()=>N(i)),/^(INPUT|TEXTAREA|SELECT)$/.test(o.tagName)&&(n==="value"||n==="checked")){let a=n==="checked"?"change":"input";o.addEventListener(a,(f)=>c(f.target[n]))}}else{let i=T(n,c);if(i!=null)if(n==="style"&&typeof i==="string")o.setAttribute("style",i);else if(n in o&&!r)o[n]=i;else o.setAttribute(n,i===!0?"":i)}}let l=(n)=>{if(E(n))return n.forEach(l);if(y(n)){let c=w.createTextNode("");o.appendChild(c);let i=[],a=g(()=>{let f=n(),d=(E(f)?f:[f]).map(j);i.forEach((p)=>{if(p._isRuntime)p.destroy();else C(p);if(p.parentNode)p.remove()});let h=c;for(let p=d.length-1;p>=0;p--){let m=d[p];if(m.parentNode!==h.parentNode)h.parentNode?.insertBefore(m,h);if(m._mounts)m._mounts.forEach((I)=>I());h=m}i=d});a(),o._cleanups.add(()=>N(a)),A(()=>N(a))}else{let c=j(n);if(o.appendChild(c),c._mounts)c._mounts.forEach((i)=>i())}};return l(t),o},k=(e)=>{let s=new Set,t=_,r=u,o=w.createElement("div");o.style.display="contents",o.setAttribute("role","presentation"),_={_cleanups:s},u=null;let l=(n)=>{if(!n)return;if(n._isRuntime)s.add(n.destroy),o.appendChild(n.container);else if(E(n))n.forEach(l);else o.appendChild(n instanceof Node?n:w.createTextNode(String(n==null?"":n)))};try{l(e({onCleanup:(n)=>s.add(n)}))}finally{_=t,u=r}return{_isRuntime:!0,container:o,destroy:()=>{s.forEach((n)=>n()),C(o),o.remove()}}},F=(e,s,t=null)=>{let r=w.createTextNode(""),o=R("div",{style:"display:contents"},[r]),l=null;return G(()=>!!(y(e)?e():e),(n)=>{if(l)l.destroy(),l=null;let c=n?s:t;if(c)l=k(()=>y(c)?c():c),o.insertBefore(l.container,r)}),A(()=>l?.destroy()),o},J=(e,s,t)=>{let r=w.createTextNode(""),o=R("div",{style:"display:contents"},[r]),l=new Map;return G(()=>(y(e)?e():e)||[],(n)=>{let c=new Map,i=[],a=n||[];for(let d=0;ds(h,d));else l.delete(p);c.set(p,m),i.push(m)}l.forEach((d)=>d.destroy());let f=r;for(let d=i.length-1;d>=0;d--){let p=i[d].container;if(p.nextSibling!==f)o.insertBefore(p,f);f=p}l=c}),o},Q=(e)=>e.children,X=(e,s)=>{let t=typeof s==="string"?w.querySelector(s):s;if(!t)return;if(O.has(t))O.get(t).destroy();let r=k(y(e)?e:()=>e);return t.replaceChildren(r.container),O.set(t,r),r};if(typeof window<"u")"a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((e)=>{window[e]=(s,t)=>R(e,s,t)});export{F as when,G as watch,k as render,A as onUnmount,X as mount,U as isObj,y as isFunc,E as isArr,R as h,J as each,B as batch,Q as Fragment,z as $}; +var y=(e)=>typeof e=="function",U=(e)=>e&&typeof e=="object",b=Array.isArray,N=typeof document<"u"?document:null,E=(e)=>N.createTextNode(e==null?"":String(e)),G=(e)=>e?._rt?e._cnt:e instanceof Node?e:E(e),F=(e)=>e.children,T=(e)=>y(e)?e():e,d=null,_=null,S=0,j=0,A=new Set,k=new WeakMap,D="http://www.w3.org/2000/svg",I="http://www.w3.org/1999/xlink",P=new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")),R=new Set(["src","href","formaction","action","background","code","archive"]),C=(e)=>{if(e)e.forEach((r)=>r()),e.clear()},g=(e)=>{if(!e||e._x)return;e._x=1;let r=[e],l;while(l=r.pop()){if(C(l._c),l._ch)l._ch.forEach((i)=>r.push(i)),l._ch.clear();if(l._d)l._d.forEach((i)=>i.delete(l)),l._d.clear()}},x=(e)=>_&&(_._c||=new Set).add(e),z=(e)=>{let r=d;d=null;try{return e()}finally{d=r}},v=(e,r=0)=>{let l=()=>{if(l._x)return;if(l._d)l._d.forEach((a)=>a.delete(l));C(l._c);let i=d,n=_;d=_=l;try{return l._res=e()}catch(a){console.error("[SigPro]",a)}finally{d=i,_=n}};if(l._d=l._c=l._ch=null,l._x=0,l._iC=r,l._dp=d?d._dp+1:0,l._m=[],l._p=_,_)(_._ch||=new Set).add(l);return l},W=()=>{if(S)return;S=1;let e=[...A].sort((r,l)=>r._dp-l._dp);A.clear();for(let r of e)if(!r._x)r();S=0},J=(e)=>{j++;try{return e()}finally{if(!--j&&A.size&&!S)W()}},w=(e,r=0)=>{if(!r&&d&&!d._x)e.add(d),(d._d||=new Set).add(e);else if(r&&e.size){let l=0;for(let i of e){if(i===d||i._x)continue;if(i._iC){if(i._dt=1,i._sb)w(i._sb,1)}else A.add(i),l=1}if(l&&!S&&!j)queueMicrotask(W)}},V=(e,r=null)=>{let l=new Set;if(y(e)){let i,n=()=>{if(n._dt){let a=d;d=n;try{let t=e();if(!Object.is(i,t))i=t,w(l,1)}finally{d=a}n._dt=0}return w(l),i};return n._iC=n._dt=1,n._sb=l,n._d=null,n._x=0,n}if(r)try{e=JSON.parse(localStorage.getItem(r))??e}catch(i){}return(...i)=>{if(i.length){let n=y(i[0])?i[0](e):i[0];if(!Object.is(e,n)){if(e=n,r)localStorage.setItem(r,JSON.stringify(e));w(l,1)}}return w(l),e}},$=(e,r)=>{let l=v(r?()=>{let i=b(e)?e.map((n)=>n()):e();z(()=>r(i))}:e);return l(),()=>g(l)},L=(e)=>{if(!e)return;if(C(e._c),e._oE)g(e._oE);if(e.childNodes)e.childNodes.forEach(L)},O=(e,r)=>r==null||r===!1?null:(R.has(e)||e.startsWith("on"))&&/^\s*(javascript|data|vbscript):/i.test(String(r))?"#":r,q=(e,r={},l=[])=>{if(r instanceof Node||b(r)||!U(r))l=r,r={};if(y(e)){let t=v(()=>t._res=e(r,{children:l,emit:(o,...f)=>r[`on${o[0].toUpperCase()}${o.slice(1)}`]?.(...f)}));if(t(),t._res==null)return null;let s=t._res instanceof Node||b(t._res)&&t._res.every((o)=>o instanceof Node)?t._res:E(t._res),c=(o)=>{if(U(o)&&!o._rt)o._m=t._m||[],o._c=t._c||new Set,o._oE=t};return b(s)?s.forEach(c):c(s),s}let i=P.has(e),n=i?N.createElementNS(D,e):N.createElement(e);n._c=new Set;for(let t in r){let s=r[t];if(t==="ref"){y(s)?s(n):s.current=n;continue}if(i&&t.startsWith("xlink:")){let c=O(t.slice(6),s);c==null?n.removeAttributeNS(I,t.slice(6)):n.setAttributeNS(I,t.slice(6),c);continue}if(t.startsWith("on")){let c=t.slice(2).toLowerCase();n.addEventListener(c,s);let o=()=>n.removeEventListener(c,s);n._c.add(o),x(o)}else if(y(s)){let c=v(()=>{let o=O(t,s());if(t==="class")n.className=o||"";else if(o==null)n.removeAttribute(t);else if(t==="style"&&typeof o=="string")n.setAttribute("style",o);else if(t in n&&!i)n[t]=o;else n.setAttribute(t,o===!0?"":o)});if(c(),n._c.add(()=>g(c)),x(()=>g(c)),/^(INPUT|TEXTAREA|SELECT)$/.test(n.tagName)&&(t==="value"||t==="checked"))n.addEventListener(t==="checked"?"change":"input",(o)=>s(o.target[t]))}else{let c=O(t,s);if(c!=null)if(t==="style"&&typeof c=="string")n.setAttribute("style",c);else if(t in n&&!i)n[t]=c;else n.setAttribute(t,c===!0?"":c)}}let a=(t)=>{if(b(t))return t.forEach(a);if(y(t)){let s=E(""),c=[];n.appendChild(s);let o=v(()=>{let f=t(),p=(b(f)?f:[f]).map(G),m=s;c.forEach((u)=>{if(u._rt?u._del():L(u),u.parentNode)u.remove()});for(let u=p.length-1;u>=0;u--){let h=p[u];if(h.parentNode!==m.parentNode)m.parentNode?.insertBefore(h,m);if(h._m)h._m.forEach((B)=>B());m=h}c=p});o(),n._c.add(()=>g(o)),x(()=>g(o))}else{let s=G(t);if(n.appendChild(s),s._m)s._m.forEach((c)=>c())}};return a(l),n},M=(e)=>{let r=new Set,l=_,i=d,n=N.createElement("div");n.style.display="contents",n.setAttribute("role","presentation"),_={_c:r},d=null;let a=(t)=>{if(!t)return;if(t._rt)r.add(t._del),n.appendChild(t._cnt);else if(b(t))t.forEach(a);else n.appendChild(t instanceof Node?t:E(t))};try{a(e({onCleanup:(t)=>r.add(t)}))}finally{_=l,d=i}return{_rt:1,_cnt:n,_del:()=>{C(r),L(n),n.remove()}}},X=(e,r,l=null)=>{let i=E(""),n=q("div",{style:"display:contents"},[i]),a;return $(()=>!!T(e),(t)=>{if(a)a._del(),a=null;let s=t?r:l;if(s)a=M(()=>T(s)),n.insertBefore(a._cnt,i)}),x(()=>a?._del()),n},K=(e,r,l)=>{let i=E(""),n=q("div",{style:"display:contents"},[i]),a=new Map;return $(()=>T(e)||[],(t)=>{let s=new Map,c=[];for(let f=0,p=(t||[]).length;fr(m,f));else a.delete(u);s.set(u,h),c.push(h)}a.forEach((f)=>f._del());let o=i;for(let f=c.length-1;f>=0;f--){let p=c[f]._cnt;if(p.nextSibling!==o)n.insertBefore(p,o);o=p}a=s}),n},Q=(e,r)=>{let l=typeof r=="string"?N.querySelector(r):r;if(!l)return;if(k.has(l))k.get(l)._del();let i=M(y(e)?e:()=>e);return l.replaceChildren(i._cnt),k.set(l,i),i};if(typeof window<"u")"a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((e)=>window[e]=(r,l)=>q(e,r,l));export{X as when,$ as watch,T as val,M as render,x as onUnmount,Q as mount,U as isObj,y as isFunc,b as isArr,q as h,K as each,J as batch,F as Fragment,V as $}; diff --git a/dist/sigpro.js b/dist/sigpro.js index 528c2e9..778bbd9 100644 --- a/dist/sigpro.js +++ b/dist/sigpro.js @@ -1,443 +1,383 @@ (() => { // src/sigpro.js - var isFunc = (f) => typeof f === "function"; - var isObj = (o) => o && typeof o === "object"; + var isFunc = (f) => typeof f == "function"; + var isObj = (o) => o && typeof o == "object"; var isArr = Array.isArray; - var doc = typeof document !== "undefined" ? document : null; - var ensureNode = (n) => n?._isRuntime ? n.container : n instanceof Node ? n : doc.createTextNode(n == null ? "" : String(n)); - var activeEffect = null; - var activeOwner = null; - var isFlushing = false; - var batchDepth = 0; - var effectQueue = new Set; - var MOUNTED_NODES = new WeakMap; + var doc = typeof document < "u" ? document : null; + var txt = (s) => doc.createTextNode(s == null ? "" : String(s)); + var toNd = (n) => n?._rt ? n._cnt : n instanceof Node ? n : txt(n); + var Fragment = (p) => p.children; + var val = (v) => isFunc(v) ? v() : v; + var aEff = null; + var aOwn = null; + var isFlushing = 0; + var bDepth = 0; + var eQ = new Set; + var MOUNTED = new WeakMap; var SVG_NS = "http://www.w3.org/2000/svg"; - var XLINK_NS = "http://www.w3.org/1999/xlink"; + var XLINK = "http://www.w3.org/1999/xlink"; var SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")); - var dispose = (eff) => { - if (!eff || eff._disposed) + var DANG_ATTR = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]); + var clr = (s) => { + if (s) { + s.forEach((f) => f()); + s.clear(); + } + }; + var dispose = (e) => { + if (!e || e._x) return; - eff._disposed = true; - const stack = [eff]; - while (stack.length) { - const e = stack.pop(); - if (e._cleanups) { - e._cleanups.forEach((fn) => fn()); - e._cleanups.clear(); + e._x = 1; + let st = [e], c; + while (c = st.pop()) { + clr(c._c); + if (c._ch) { + c._ch.forEach((x) => st.push(x)); + c._ch.clear(); } - if (e._children) { - e._children.forEach((child) => stack.push(child)); - e._children.clear(); - } - if (e._deps) { - e._deps.forEach((depSet) => depSet.delete(e)); - e._deps.clear(); + if (c._d) { + c._d.forEach((d) => d.delete(c)); + c._d.clear(); } } }; - var onUnmount = (fn) => { - if (activeOwner) - (activeOwner._cleanups ||= new Set).add(fn); - }; - var untrack = (fn) => { - const p = activeEffect; - activeEffect = null; + var onUnmount = (f) => aOwn && (aOwn._c ||= new Set).add(f); + var untrack = (f) => { + let p = aEff; + aEff = null; try { - return fn(); + return f(); } finally { - activeEffect = p; + aEff = p; } }; - var createEffect = (fn, isComputed = false) => { - const effect = () => { - if (effect._disposed) + var createEffect = (f, isC = 0) => { + const e = () => { + if (e._x) return; - if (effect._deps) - effect._deps.forEach((s) => s.delete(effect)); - if (effect._cleanups) { - effect._cleanups.forEach((c) => c()); - effect._cleanups.clear(); - } - const prevEffect = activeEffect; - const prevOwner = activeOwner; - activeEffect = activeOwner = effect; + if (e._d) + e._d.forEach((s) => s.delete(e)); + clr(e._c); + let pE = aEff, pO = aOwn; + aEff = aOwn = e; try { - return effect._result = fn(); - } catch (e) { - console.error("[SigPro]", e); + return e._res = f(); + } catch (err) { + console.error("[SigPro]", err); } finally { - activeEffect = prevEffect; - activeOwner = prevOwner; + aEff = pE; + aOwn = pO; } }; - effect._deps = effect._cleanups = effect._children = null; - effect._disposed = false; - effect._isComputed = isComputed; - effect._depth = activeEffect ? activeEffect._depth + 1 : 0; - effect._mounts = []; - effect._parent = activeOwner; - if (activeOwner) - (activeOwner._children ||= new Set).add(effect); - return effect; + e._d = e._c = e._ch = null; + e._x = 0; + e._iC = isC; + e._dp = aEff ? aEff._dp + 1 : 0; + e._m = []; + e._p = aOwn; + if (aOwn) + (aOwn._ch ||= new Set).add(e); + return e; }; var flush = () => { if (isFlushing) return; - isFlushing = true; - const sorted = Array.from(effectQueue).sort((a, b) => a._depth - b._depth); - effectQueue.clear(); - for (const e of sorted) - if (!e._disposed) + isFlushing = 1; + let q = [...eQ].sort((a, b) => a._dp - b._dp); + eQ.clear(); + for (let e of q) + if (!e._x) e(); - isFlushing = false; + isFlushing = 0; }; - var batch = (fn) => { - batchDepth++; + var batch = (f) => { + bDepth++; try { - return fn(); + return f(); } finally { - batchDepth--; - if (batchDepth === 0 && effectQueue.size > 0 && !isFlushing) + if (!--bDepth && eQ.size && !isFlushing) flush(); } }; - var trackUpdate = (subs, trigger = false) => { - if (!trigger && activeEffect && !activeEffect._disposed) { - subs.add(activeEffect); - (activeEffect._deps ||= new Set).add(subs); - } else if (trigger && subs.size > 0) { - let hasQueue = false; - for (const e of subs) { - if (e === activeEffect || e._disposed) + var trkUpd = (s, trg = 0) => { + if (!trg && aEff && !aEff._x) { + s.add(aEff); + (aEff._d ||= new Set).add(s); + } else if (trg && s.size) { + let q = 0; + for (let e of s) { + if (e === aEff || e._x) continue; - if (e._isComputed) { - e._dirty = true; - if (e._subs) - trackUpdate(e._subs, true); + if (e._iC) { + e._dt = 1; + if (e._sb) + trkUpd(e._sb, 1); } else { - effectQueue.add(e); - hasQueue = true; + eQ.add(e); + q = 1; } } - if (hasQueue && !isFlushing && batchDepth === 0) + if (q && !isFlushing && !bDepth) queueMicrotask(flush); } }; - var $ = (val, key = null) => { - const subs = new Set; - if (isFunc(val)) { - let cache; - const computed = () => { - if (computed._dirty) { - const prev = activeEffect; - activeEffect = computed; + var $ = (v, k = null) => { + let s = new Set; + if (isFunc(v)) { + let c, cp = () => { + if (cp._dt) { + let p = aEff; + aEff = cp; try { - const next = val(); - if (!Object.is(cache, next)) { - cache = next; - trackUpdate(subs, true); + let n = v(); + if (!Object.is(c, n)) { + c = n; + trkUpd(s, 1); } } finally { - activeEffect = prev; + aEff = p; } - computed._dirty = false; + cp._dt = 0; } - trackUpdate(subs); - return cache; + trkUpd(s); + return c; }; - computed._isComputed = true; - computed._subs = subs; - computed._dirty = true; - computed._deps = null; - computed._disposed = false; - return computed; + cp._iC = cp._dt = 1; + cp._sb = s; + cp._d = null; + cp._x = 0; + return cp; } - if (key) + if (k) try { - val = JSON.parse(localStorage.getItem(key)) ?? val; + v = JSON.parse(localStorage.getItem(k)) ?? v; } catch (e) {} - return (...args) => { - if (args.length) { - const next = isFunc(args[0]) ? args[0](val) : args[0]; - if (!Object.is(val, next)) { - val = next; - if (key) - localStorage.setItem(key, JSON.stringify(val)); - trackUpdate(subs, true); + return (...a) => { + if (a.length) { + let n = isFunc(a[0]) ? a[0](v) : a[0]; + if (!Object.is(v, n)) { + v = n; + if (k) + localStorage.setItem(k, JSON.stringify(v)); + trkUpd(s, 1); } } - trackUpdate(subs); - return val; + trkUpd(s); + return v; }; }; - var watch = (sources, cb) => { - if (cb === undefined) { - const effect2 = createEffect(sources); - effect2(); - return () => dispose(effect2); - } - const effect = createEffect(() => { - const vals = isArr(sources) ? sources.map((s) => s()) : sources(); - untrack(() => cb(vals)); - }); - effect(); - return () => dispose(effect); + var watch = (src, cb) => { + let e = createEffect(cb ? () => { + let v = isArr(src) ? src.map((s) => s()) : src(); + untrack(() => cb(v)); + } : src); + e(); + return () => dispose(e); }; - var cleanupNode = (node) => { - if (!node) + var clnNd = (n) => { + if (!n) return; - if (node._cleanups) { - node._cleanups.forEach((fn) => fn()); - node._cleanups.clear(); - } - if (node._ownerEffect) - dispose(node._ownerEffect); - if (node.childNodes) - node.childNodes.forEach((n) => cleanupNode(n)); + clr(n._c); + if (n._oE) + dispose(n._oE); + if (n.childNodes) + n.childNodes.forEach(clnNd); }; - var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; - var DANGEROUS_URI_ATTRS = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]); - var isDangerousAttr = (key) => DANGEROUS_URI_ATTRS.has(key) || key.startsWith("on"); - var validateAttr = (key, val) => { - if (val == null || val === false) - return null; - if (isDangerousAttr(key)) { - const sVal = String(val); - if (DANGEROUS_PROTOCOL.test(sVal)) - return "#"; - } - return val; - }; - var h = (tag, props = {}, children = []) => { - if (props instanceof Node || isArr(props) || !isObj(props)) { - children = props; - props = {}; + var valAtt = (k, v) => v == null || v === false ? null : (DANG_ATTR.has(k) || k.startsWith("on")) && /^\s*(javascript|data|vbscript):/i.test(String(v)) ? "#" : v; + var h = (tag, prp = {}, ch = []) => { + if (prp instanceof Node || isArr(prp) || !isObj(prp)) { + ch = prp; + prp = {}; } if (isFunc(tag)) { - const effect = createEffect(() => { - const result2 = tag(props, { - children, - emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args) - }); - effect._result = result2; - return result2; - }); - effect(); - const result = effect._result; - if (result == null) + let e = createEffect(() => e._res = tag(prp, { children: ch, emit: (ev, ...a) => prp[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...a) })); + e(); + if (e._res == null) return null; - const node = result instanceof Node || isArr(result) && result.every((n) => n instanceof Node) ? result : doc.createTextNode(String(result)); - const attach = (n) => { - if (isObj(n) && !n._isRuntime) { - n._mounts = effect._mounts || []; - n._cleanups = effect._cleanups || new Set; - n._ownerEffect = effect; + let nd = e._res instanceof Node || isArr(e._res) && e._res.every((n) => n instanceof Node) ? e._res : txt(e._res); + let att = (n) => { + if (isObj(n) && !n._rt) { + n._m = e._m || []; + n._c = e._c || new Set; + n._oE = e; } }; - isArr(node) ? node.forEach(attach) : attach(node); - return node; + isArr(nd) ? nd.forEach(att) : att(nd); + return nd; } - const isSVG = SVG_TAGS.has(tag); - const el = isSVG ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag); - el._cleanups = new Set; - for (const k of Object.keys(props)) { - let v = props[k]; + let isS = SVG_TAGS.has(tag), el = isS ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag); + el._c = new Set; + for (let k in prp) { + let v = prp[k]; if (k === "ref") { isFunc(v) ? v(el) : v.current = el; continue; } - if (isSVG && k.startsWith("xlink:")) { - const cleanVal = validateAttr(k.slice(6), v); - cleanVal == null ? el.removeAttributeNS(XLINK_NS, k.slice(6)) : el.setAttributeNS(XLINK_NS, k.slice(6), cleanVal); + if (isS && k.startsWith("xlink:")) { + let cv = valAtt(k.slice(6), v); + cv == null ? el.removeAttributeNS(XLINK, k.slice(6)) : el.setAttributeNS(XLINK, k.slice(6), cv); continue; } if (k.startsWith("on")) { - const ev = k.slice(2).toLowerCase(); + let ev = k.slice(2).toLowerCase(); el.addEventListener(ev, v); - const off = () => el.removeEventListener(ev, v); - el._cleanups.add(off); + let off = () => el.removeEventListener(ev, v); + el._c.add(off); onUnmount(off); } else if (isFunc(v)) { - const effect = createEffect(() => { - const val = validateAttr(k, v()); + let e = createEffect(() => { + let r = valAtt(k, v()); if (k === "class") - el.className = val || ""; - else if (val == null) + el.className = r || ""; + else if (r == null) el.removeAttribute(k); - else if (k === "style" && typeof val === "string") - el.setAttribute("style", val); - else if (k in el && !isSVG) - el[k] = val; + else if (k === "style" && typeof r == "string") + el.setAttribute("style", r); + else if (k in el && !isS) + el[k] = r; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, r === true ? "" : r); }); - effect(); - el._cleanups.add(() => dispose(effect)); - onUnmount(() => dispose(effect)); + e(); + el._c.add(() => dispose(e)); + onUnmount(() => dispose(e)); if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) { - const evType = k === "checked" ? "change" : "input"; - el.addEventListener(evType, (ev) => v(ev.target[k])); + el.addEventListener(k === "checked" ? "change" : "input", (ev) => v(ev.target[k])); } } else { - const val = validateAttr(k, v); - if (val != null) { - if (k === "style" && typeof val === "string") - el.setAttribute("style", val); - else if (k in el && !isSVG) - el[k] = val; + let r = valAtt(k, v); + if (r != null) { + if (k === "style" && typeof r == "string") + el.setAttribute("style", r); + else if (k in el && !isS) + el[k] = r; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, r === true ? "" : r); } } } - const append = (c) => { + const app = (c) => { if (isArr(c)) - return c.forEach(append); + return c.forEach(app); if (isFunc(c)) { - const anchor = doc.createTextNode(""); - el.appendChild(anchor); - let currentNodes = []; - const effect = createEffect(() => { - const res = c(); - const next = (isArr(res) ? res : [res]).map(ensureNode); - currentNodes.forEach((n) => { - if (n._isRuntime) - n.destroy(); - else - cleanupNode(n); + let anc = txt(""), cur = []; + el.appendChild(anc); + let e = createEffect(() => { + let r = c(), nxt = (isArr(r) ? r : [r]).map(toNd), ref = anc; + cur.forEach((n) => { + n._rt ? n._del() : clnNd(n); if (n.parentNode) n.remove(); }); - let ref = anchor; - for (let i = next.length - 1;i >= 0; i--) { - const node = next[i]; - if (node.parentNode !== ref.parentNode) - ref.parentNode?.insertBefore(node, ref); - if (node._mounts) - node._mounts.forEach((fn) => fn()); - ref = node; + for (let i = nxt.length - 1;i >= 0; i--) { + let nd = nxt[i]; + if (nd.parentNode !== ref.parentNode) + ref.parentNode?.insertBefore(nd, ref); + if (nd._m) + nd._m.forEach((f) => f()); + ref = nd; } - currentNodes = next; + cur = nxt; }); - effect(); - el._cleanups.add(() => dispose(effect)); - onUnmount(() => dispose(effect)); + e(); + el._c.add(() => dispose(e)); + onUnmount(() => dispose(e)); } else { - const node = ensureNode(c); - el.appendChild(node); - if (node._mounts) - node._mounts.forEach((fn) => fn()); + let nd = toNd(c); + el.appendChild(nd); + if (nd._m) + nd._m.forEach((f) => f()); } }; - append(children); + app(ch); return el; }; - var render = (renderFn) => { - const cleanups = new Set; - const previousOwner = activeOwner; - const previousEffect = activeEffect; - const container = doc.createElement("div"); - container.style.display = "contents"; - container.setAttribute("role", "presentation"); - activeOwner = { _cleanups: cleanups }; - activeEffect = null; - const processResult = (result) => { - if (!result) + var render = (rFn) => { + let c = new Set, pO = aOwn, pE = aEff, cnt = doc.createElement("div"); + cnt.style.display = "contents"; + cnt.setAttribute("role", "presentation"); + aOwn = { _c: c }; + aEff = null; + const pRes = (r) => { + if (!r) return; - if (result._isRuntime) { - cleanups.add(result.destroy); - container.appendChild(result.container); - } else if (isArr(result)) { - result.forEach(processResult); - } else { - container.appendChild(result instanceof Node ? result : doc.createTextNode(String(result == null ? "" : result))); - } + if (r._rt) { + c.add(r._del); + cnt.appendChild(r._cnt); + } else if (isArr(r)) + r.forEach(pRes); + else + cnt.appendChild(r instanceof Node ? r : txt(r)); }; try { - processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) })); + pRes(rFn({ onCleanup: (f) => c.add(f) })); } finally { - activeOwner = previousOwner; - activeEffect = previousEffect; + aOwn = pO; + aEff = pE; } - return { - _isRuntime: true, - container, - destroy: () => { - cleanups.forEach((fn) => fn()); - cleanupNode(container); - container.remove(); - } - }; + return { _rt: 1, _cnt: cnt, _del: () => { + clr(c); + clnNd(cnt); + cnt.remove(); + } }; }; - var when = (cond, SIP, NOP = null) => { - const anchor = doc.createTextNode(""); - const root = h("div", { style: "display:contents" }, [anchor]); - let currentView = null; - watch(() => !!(isFunc(cond) ? cond() : cond), (show) => { - if (currentView) { - currentView.destroy(); - currentView = null; + var when = (c, Y, N = null) => { + let anc = txt(""), rt = h("div", { style: "display:contents" }, [anc]), v; + watch(() => !!val(c), (s) => { + if (v) { + v._del(); + v = null; } - const content = show ? SIP : NOP; - if (content) { - currentView = render(() => isFunc(content) ? content() : content); - root.insertBefore(currentView.container, anchor); + let ct = s ? Y : N; + if (ct) { + v = render(() => val(ct)); + rt.insertBefore(v._cnt, anc); } }); - onUnmount(() => currentView?.destroy()); - return root; + onUnmount(() => v?._del()); + return rt; }; - var each = (src, itemFn, keyField) => { - const anchor = doc.createTextNode(""); - const root = h("div", { style: "display:contents" }, [anchor]); - let cache = new Map; - watch(() => (isFunc(src) ? src() : src) || [], (items) => { - const nextCache = new Map; - const nextOrder = []; - const newItems = items || []; - for (let i = 0;i < newItems.length; i++) { - const item = newItems[i]; - const key = keyField ? item?.[keyField] ?? i : item?.id ?? i; - let view = cache.get(key); - if (!view) - view = render(() => itemFn(item, i)); + var each = (s, fn, kF) => { + let anc = txt(""), rt = h("div", { style: "display:contents" }, [anc]), cch = new Map; + watch(() => val(s) || [], (it) => { + let nCc = new Map, nOd = []; + for (let i = 0, l = (it || []).length;i < l; i++) { + let t = it[i], k = kF ? t?.[kF] ?? i : t?.id ?? i, v = cch.get(k); + if (!v) + v = render(() => fn(t, i)); else - cache.delete(key); - nextCache.set(key, view); - nextOrder.push(view); + cch.delete(k); + nCc.set(k, v); + nOd.push(v); } - cache.forEach((view) => view.destroy()); - let lastRef = anchor; - for (let i = nextOrder.length - 1;i >= 0; i--) { - const view = nextOrder[i]; - const node = view.container; - if (node.nextSibling !== lastRef) - root.insertBefore(node, lastRef); - lastRef = node; + cch.forEach((v) => v._del()); + let ref = anc; + for (let i = nOd.length - 1;i >= 0; i--) { + let nd = nOd[i]._cnt; + if (nd.nextSibling !== ref) + rt.insertBefore(nd, ref); + ref = nd; } - cache = nextCache; + cch = nCc; }); - return root; + return rt; }; - var Fragment = (props) => props.children; - var mount = (comp, target) => { - const t = typeof target === "string" ? doc.querySelector(target) : target; + var mount = (c, tgt) => { + let t = typeof tgt == "string" ? doc.querySelector(tgt) : tgt; if (!t) return; - if (MOUNTED_NODES.has(t)) - MOUNTED_NODES.get(t).destroy(); - const inst = render(isFunc(comp) ? comp : () => comp); - t.replaceChildren(inst.container); - MOUNTED_NODES.set(t, inst); - return inst; + if (MOUNTED.has(t)) + MOUNTED.get(t)._del(); + let i = render(isFunc(c) ? c : () => c); + t.replaceChildren(i._cnt); + MOUNTED.set(t, i); + return i; }; - if (typeof window !== "undefined") { - "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((tag) => { - window[tag] = (props, children) => h(tag, props, children); - }); + if (typeof window < "u") { + "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((t) => window[t] = (p, c) => h(t, p, c)); } - // src/sigpro.plus.js + // src/sigpro.utils.js var router = (routes) => { const getHash = () => window.location.hash.slice(1) || "/"; const path = $(getHash()); diff --git a/dist/sigpro.min.js b/dist/sigpro.min.js index 782bca0..75edfe6 100644 --- a/dist/sigpro.min.js +++ b/dist/sigpro.min.js @@ -1 +1 @@ -(()=>{var h=(e)=>typeof e==="function",L=(e)=>e&&typeof e==="object",E=Array.isArray,y=typeof document<"u"?document:null,V=(e)=>e?._isRuntime?e.container:e instanceof Node?e:y.createTextNode(e==null?"":String(e)),u=null,m=null,O=!1,k=0,R=new Set,U=new WeakMap,Q="http://www.w3.org/2000/svg",D="http://www.w3.org/1999/xlink",X=new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")),b=(e)=>{if(!e||e._disposed)return;e._disposed=!0;let n=[e];while(n.length){let t=n.pop();if(t._cleanups)t._cleanups.forEach((r)=>r()),t._cleanups.clear();if(t._children)t._children.forEach((r)=>n.push(r)),t._children.clear();if(t._deps)t._deps.forEach((r)=>r.delete(t)),t._deps.clear()}},C=(e)=>{if(m)(m._cleanups||=new Set).add(e)},H=(e)=>{let n=u;u=null;try{return e()}finally{u=n}},A=(e,n=!1)=>{let t=()=>{if(t._disposed)return;if(t._deps)t._deps.forEach((a)=>a.delete(t));if(t._cleanups)t._cleanups.forEach((a)=>a()),t._cleanups.clear();let r=u,o=m;u=m=t;try{return t._result=e()}catch(a){console.error("[SigPro]",a)}finally{u=r,m=o}};if(t._deps=t._cleanups=t._children=null,t._disposed=!1,t._isComputed=n,t._depth=u?u._depth+1:0,t._mounts=[],t._parent=m,m)(m._children||=new Set).add(t);return t},G=()=>{if(O)return;O=!0;let e=Array.from(R).sort((n,t)=>n._depth-t._depth);R.clear();for(let n of e)if(!n._disposed)n();O=!1},I=(e)=>{k++;try{return e()}finally{if(k--,k===0&&R.size>0&&!O)G()}},x=(e,n=!1)=>{if(!n&&u&&!u._disposed)e.add(u),(u._deps||=new Set).add(e);else if(n&&e.size>0){let t=!1;for(let r of e){if(r===u||r._disposed)continue;if(r._isComputed){if(r._dirty=!0,r._subs)x(r._subs,!0)}else R.add(r),t=!0}if(t&&!O&&k===0)queueMicrotask(G)}},v=(e,n=null)=>{let t=new Set;if(h(e)){let r,o=()=>{if(o._dirty){let a=u;u=o;try{let s=e();if(!Object.is(r,s))r=s,x(t,!0)}finally{u=a}o._dirty=!1}return x(t),r};return o._isComputed=!0,o._subs=t,o._dirty=!0,o._deps=null,o._disposed=!1,o}if(n)try{e=JSON.parse(localStorage.getItem(n))??e}catch(r){}return(...r)=>{if(r.length){let o=h(r[0])?r[0](e):r[0];if(!Object.is(e,o)){if(e=o,n)localStorage.setItem(n,JSON.stringify(e));x(t,!0)}}return x(t),e}},N=(e,n)=>{if(n===void 0){let r=A(e);return r(),()=>b(r)}let t=A(()=>{let r=E(e)?e.map((o)=>o()):e();H(()=>n(r))});return t(),()=>b(t)},B=(e)=>{if(!e)return;if(e._cleanups)e._cleanups.forEach((n)=>n()),e._cleanups.clear();if(e._ownerEffect)b(e._ownerEffect);if(e.childNodes)e.childNodes.forEach((n)=>B(n))},K=/^\s*(javascript|data|vbscript):/i,Y=new Set(["src","href","formaction","action","background","code","archive"]),Z=(e)=>Y.has(e)||e.startsWith("on"),$=(e,n)=>{if(n==null||n===!1)return null;if(Z(e)){let t=String(n);if(K.test(t))return"#"}return n},g=(e,n={},t=[])=>{if(n instanceof Node||E(n)||!L(n))t=n,n={};if(h(e)){let s=A(()=>{let f=e(n,{children:t,emit:(d,..._)=>n[`on${d[0].toUpperCase()}${d.slice(1)}`]?.(..._)});return s._result=f,f});s();let c=s._result;if(c==null)return null;let i=c instanceof Node||E(c)&&c.every((f)=>f instanceof Node)?c:y.createTextNode(String(c)),l=(f)=>{if(L(f)&&!f._isRuntime)f._mounts=s._mounts||[],f._cleanups=s._cleanups||new Set,f._ownerEffect=s};return E(i)?i.forEach(l):l(i),i}let r=X.has(e),o=r?y.createElementNS(Q,e):y.createElement(e);o._cleanups=new Set;for(let s of Object.keys(n)){let c=n[s];if(s==="ref"){h(c)?c(o):c.current=o;continue}if(r&&s.startsWith("xlink:")){let i=$(s.slice(6),c);i==null?o.removeAttributeNS(D,s.slice(6)):o.setAttributeNS(D,s.slice(6),i);continue}if(s.startsWith("on")){let i=s.slice(2).toLowerCase();o.addEventListener(i,c);let l=()=>o.removeEventListener(i,c);o._cleanups.add(l),C(l)}else if(h(c)){let i=A(()=>{let l=$(s,c());if(s==="class")o.className=l||"";else if(l==null)o.removeAttribute(s);else if(s==="style"&&typeof l==="string")o.setAttribute("style",l);else if(s in o&&!r)o[s]=l;else o.setAttribute(s,l===!0?"":l)});if(i(),o._cleanups.add(()=>b(i)),C(()=>b(i)),/^(INPUT|TEXTAREA|SELECT)$/.test(o.tagName)&&(s==="value"||s==="checked")){let l=s==="checked"?"change":"input";o.addEventListener(l,(f)=>c(f.target[s]))}}else{let i=$(s,c);if(i!=null)if(s==="style"&&typeof i==="string")o.setAttribute("style",i);else if(s in o&&!r)o[s]=i;else o.setAttribute(s,i===!0?"":i)}}let a=(s)=>{if(E(s))return s.forEach(a);if(h(s)){let c=y.createTextNode("");o.appendChild(c);let i=[],l=A(()=>{let f=s(),d=(E(f)?f:[f]).map(V);i.forEach((p)=>{if(p._isRuntime)p.destroy();else B(p);if(p.parentNode)p.remove()});let _=c;for(let p=d.length-1;p>=0;p--){let w=d[p];if(w.parentNode!==_.parentNode)_.parentNode?.insertBefore(w,_);if(w._mounts)w._mounts.forEach((z)=>z());_=w}i=d});l(),o._cleanups.add(()=>b(l)),C(()=>b(l))}else{let c=V(s);if(o.appendChild(c),c._mounts)c._mounts.forEach((i)=>i())}};return a(t),o},T=(e)=>{let n=new Set,t=m,r=u,o=y.createElement("div");o.style.display="contents",o.setAttribute("role","presentation"),m={_cleanups:n},u=null;let a=(s)=>{if(!s)return;if(s._isRuntime)n.add(s.destroy),o.appendChild(s.container);else if(E(s))s.forEach(a);else o.appendChild(s instanceof Node?s:y.createTextNode(String(s==null?"":s)))};try{a(e({onCleanup:(s)=>n.add(s)}))}finally{m=t,u=r}return{_isRuntime:!0,container:o,destroy:()=>{n.forEach((s)=>s()),B(o),o.remove()}}},F=(e,n,t=null)=>{let r=y.createTextNode(""),o=g("div",{style:"display:contents"},[r]),a=null;return N(()=>!!(h(e)?e():e),(s)=>{if(a)a.destroy(),a=null;let c=s?n:t;if(c)a=T(()=>h(c)?c():c),o.insertBefore(a.container,r)}),C(()=>a?.destroy()),o},M=(e,n,t)=>{let r=y.createTextNode(""),o=g("div",{style:"display:contents"},[r]),a=new Map;return N(()=>(h(e)?e():e)||[],(s)=>{let c=new Map,i=[],l=s||[];for(let d=0;dn(_,d));else a.delete(p);c.set(p,w),i.push(w)}a.forEach((d)=>d.destroy());let f=r;for(let d=i.length-1;d>=0;d--){let p=i[d].container;if(p.nextSibling!==f)o.insertBefore(p,f);f=p}a=c}),o},P=(e)=>e.children,W=(e,n)=>{let t=typeof n==="string"?y.querySelector(n):n;if(!t)return;if(U.has(t))U.get(t).destroy();let r=T(h(e)?e:()=>e);return t.replaceChildren(r.container),U.set(t,r),r};if(typeof window<"u")"a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((e)=>{window[e]=(n,t)=>g(e,n,t)});var S=(e)=>{let n=()=>window.location.hash.slice(1)||"/",t=v(n()),r=()=>t(n());window.addEventListener("hashchange",r);let o=g("div",{class:"router-hook"}),a=null;return N([t],()=>{let s=t(),c=e.find((i)=>{let l=i.path.split("/").filter(Boolean),f=s.split("/").filter(Boolean);return l.length===f.length&&l.every((d,_)=>d[0]===":"||d===f[_])})||e.find((i)=>i.path==="*");if(c){a?.destroy();let i={};c.path.split("/").filter(Boolean).forEach((l,f)=>{if(l[0]===":")i[l.slice(1)]=s.split("/").filter(Boolean)[f]}),S.params(i),a=T(()=>h(c.component)?c.component(i):c.component),o.replaceChildren(a.container)}}),o.destroy=()=>{window.removeEventListener("hashchange",r),a?.destroy()},o};S.params=v({});S.to=(e)=>window.location.hash=e.replace(/^#?\/?/,"#/");S.back=()=>window.history.back();S.path=()=>window.location.hash.replace(/^#/,"")||"/";var ee=v("en"),j={},q=(e)=>{for(let n of Object.keys(e)){if(!j[n])j[n]={};Object.assign(j[n],e[n])}};var J=(e)=>{return()=>j[ee()]?.[e]??e};if(typeof window<"u")Object.assign(window,{$:v,watch:N,h:g,Fragment:P,when:F,each:M,router:S,addLang:q,t:J,mount:W,batch:I,isArr:E,isFunc:h,isObj:L});})(); +(()=>{var p=(e)=>typeof e=="function",T=(e)=>e&&typeof e=="object",y=Array.isArray,O=typeof document<"u"?document:null,S=(e)=>O.createTextNode(e==null?"":String(e)),I=(e)=>e?._rt?e._cnt:e instanceof Node?e:S(e),W=(e)=>e.children,M=(e)=>p(e)?e():e,f=null,_=null,A=0,U=0,j=new Set,q=new WeakMap,Q="http://www.w3.org/2000/svg",P="http://www.w3.org/1999/xlink",Y=new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")),Z=new Set(["src","href","formaction","action","background","code","archive"]),$=(e)=>{if(e)e.forEach((r)=>r()),e.clear()},E=(e)=>{if(!e||e._x)return;e._x=1;let r=[e],n;while(n=r.pop()){if($(n._c),n._ch)n._ch.forEach((i)=>r.push(i)),n._ch.clear();if(n._d)n._d.forEach((i)=>i.delete(n)),n._d.clear()}},C=(e)=>_&&(_._c||=new Set).add(e),ee=(e)=>{let r=f;f=null;try{return e()}finally{f=r}},L=(e,r=0)=>{let n=()=>{if(n._x)return;if(n._d)n._d.forEach((a)=>a.delete(n));$(n._c);let i=f,o=_;f=_=n;try{return n._res=e()}catch(a){console.error("[SigPro]",a)}finally{f=i,_=o}};if(n._d=n._c=n._ch=null,n._x=0,n._iC=r,n._dp=f?f._dp+1:0,n._m=[],n._p=_,_)(_._ch||=new Set).add(n);return n},D=()=>{if(A)return;A=1;let e=[...j].sort((r,n)=>r._dp-n._dp);j.clear();for(let r of e)if(!r._x)r();A=0},J=(e)=>{U++;try{return e()}finally{if(!--U&&j.size&&!A)D()}},v=(e,r=0)=>{if(!r&&f&&!f._x)e.add(f),(f._d||=new Set).add(e);else if(r&&e.size){let n=0;for(let i of e){if(i===f||i._x)continue;if(i._iC){if(i._dt=1,i._sb)v(i._sb,1)}else j.add(i),n=1}if(n&&!A&&!U)queueMicrotask(D)}},N=(e,r=null)=>{let n=new Set;if(p(e)){let i,o=()=>{if(o._dt){let a=f;f=o;try{let t=e();if(!Object.is(i,t))i=t,v(n,1)}finally{f=a}o._dt=0}return v(n),i};return o._iC=o._dt=1,o._sb=n,o._d=null,o._x=0,o}if(r)try{e=JSON.parse(localStorage.getItem(r))??e}catch(i){}return(...i)=>{if(i.length){let o=p(i[0])?i[0](e):i[0];if(!Object.is(e,o)){if(e=o,r)localStorage.setItem(r,JSON.stringify(e));v(n,1)}}return v(n),e}},x=(e,r)=>{let n=L(r?()=>{let i=y(e)?e.map((o)=>o()):e();ee(()=>r(i))}:e);return n(),()=>E(n)},G=(e)=>{if(!e)return;if($(e._c),e._oE)E(e._oE);if(e.childNodes)e.childNodes.forEach(G)},F=(e,r)=>r==null||r===!1?null:(Z.has(e)||e.startsWith("on"))&&/^\s*(javascript|data|vbscript):/i.test(String(r))?"#":r,b=(e,r={},n=[])=>{if(r instanceof Node||y(r)||!T(r))n=r,r={};if(p(e)){let t=L(()=>t._res=e(r,{children:n,emit:(l,...d)=>r[`on${l[0].toUpperCase()}${l.slice(1)}`]?.(...d)}));if(t(),t._res==null)return null;let c=t._res instanceof Node||y(t._res)&&t._res.every((l)=>l instanceof Node)?t._res:S(t._res),s=(l)=>{if(T(l)&&!l._rt)l._m=t._m||[],l._c=t._c||new Set,l._oE=t};return y(c)?c.forEach(s):s(c),c}let i=Y.has(e),o=i?O.createElementNS(Q,e):O.createElement(e);o._c=new Set;for(let t in r){let c=r[t];if(t==="ref"){p(c)?c(o):c.current=o;continue}if(i&&t.startsWith("xlink:")){let s=F(t.slice(6),c);s==null?o.removeAttributeNS(P,t.slice(6)):o.setAttributeNS(P,t.slice(6),s);continue}if(t.startsWith("on")){let s=t.slice(2).toLowerCase();o.addEventListener(s,c);let l=()=>o.removeEventListener(s,c);o._c.add(l),C(l)}else if(p(c)){let s=L(()=>{let l=F(t,c());if(t==="class")o.className=l||"";else if(l==null)o.removeAttribute(t);else if(t==="style"&&typeof l=="string")o.setAttribute("style",l);else if(t in o&&!i)o[t]=l;else o.setAttribute(t,l===!0?"":l)});if(s(),o._c.add(()=>E(s)),C(()=>E(s)),/^(INPUT|TEXTAREA|SELECT)$/.test(o.tagName)&&(t==="value"||t==="checked"))o.addEventListener(t==="checked"?"change":"input",(l)=>c(l.target[t]))}else{let s=F(t,c);if(s!=null)if(t==="style"&&typeof s=="string")o.setAttribute("style",s);else if(t in o&&!i)o[t]=s;else o.setAttribute(t,s===!0?"":s)}}let a=(t)=>{if(y(t))return t.forEach(a);if(p(t)){let c=S(""),s=[];o.appendChild(c);let l=L(()=>{let d=t(),h=(y(d)?d:[d]).map(I),m=c;s.forEach((u)=>{if(u._rt?u._del():G(u),u.parentNode)u.remove()});for(let u=h.length-1;u>=0;u--){let w=h[u];if(w.parentNode!==m.parentNode)m.parentNode?.insertBefore(w,m);if(w._m)w._m.forEach((K)=>K());m=w}s=h});l(),o._c.add(()=>E(l)),C(()=>E(l))}else{let c=I(t);if(o.appendChild(c),c._m)c._m.forEach((s)=>s())}};return a(n),o},k=(e)=>{let r=new Set,n=_,i=f,o=O.createElement("div");o.style.display="contents",o.setAttribute("role","presentation"),_={_c:r},f=null;let a=(t)=>{if(!t)return;if(t._rt)r.add(t._del),o.appendChild(t._cnt);else if(y(t))t.forEach(a);else o.appendChild(t instanceof Node?t:S(t))};try{a(e({onCleanup:(t)=>r.add(t)}))}finally{_=n,f=i}return{_rt:1,_cnt:o,_del:()=>{$(r),G(o),o.remove()}}},R=(e,r,n=null)=>{let i=S(""),o=b("div",{style:"display:contents"},[i]),a;return x(()=>!!M(e),(t)=>{if(a)a._del(),a=null;let c=t?r:n;if(c)a=k(()=>M(c)),o.insertBefore(a._cnt,i)}),C(()=>a?._del()),o},V=(e,r,n)=>{let i=S(""),o=b("div",{style:"display:contents"},[i]),a=new Map;return x(()=>M(e)||[],(t)=>{let c=new Map,s=[];for(let d=0,h=(t||[]).length;dr(m,d));else a.delete(u);c.set(u,w),s.push(w)}a.forEach((d)=>d._del());let l=i;for(let d=s.length-1;d>=0;d--){let h=s[d]._cnt;if(h.nextSibling!==l)o.insertBefore(h,l);l=h}a=c}),o},z=(e,r)=>{let n=typeof r=="string"?O.querySelector(r):r;if(!n)return;if(q.has(n))q.get(n)._del();let i=k(p(e)?e:()=>e);return n.replaceChildren(i._cnt),q.set(n,i),i};if(typeof window<"u")"a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((e)=>window[e]=(r,n)=>b(e,r,n));var g=(e)=>{let r=()=>window.location.hash.slice(1)||"/",n=N(r()),i=()=>n(r());window.addEventListener("hashchange",i);let o=b("div",{class:"router-hook"}),a=null;return x([n],()=>{let t=n(),c=e.find((s)=>{let l=s.path.split("/").filter(Boolean),d=t.split("/").filter(Boolean);return l.length===d.length&&l.every((h,m)=>h[0]===":"||h===d[m])})||e.find((s)=>s.path==="*");if(c){a?.destroy();let s={};c.path.split("/").filter(Boolean).forEach((l,d)=>{if(l[0]===":")s[l.slice(1)]=t.split("/").filter(Boolean)[d]}),g.params(s),a=k(()=>p(c.component)?c.component(s):c.component),o.replaceChildren(a.container)}}),o.destroy=()=>{window.removeEventListener("hashchange",i),a?.destroy()},o};g.params=N({});g.to=(e)=>window.location.hash=e.replace(/^#?\/?/,"#/");g.back=()=>window.history.back();g.path=()=>window.location.hash.replace(/^#/,"")||"/";var te=N("en"),B={},X=(e)=>{for(let r of Object.keys(e)){if(!B[r])B[r]={};Object.assign(B[r],e[r])}};var H=(e)=>{return()=>B[te()]?.[e]??e};if(typeof window<"u")Object.assign(window,{$:N,watch:x,h:b,Fragment:W,when:R,each:V,router:g,addLang:X,t:H,mount:z,batch:J,isArr:y,isFunc:p,isObj:T});})(); diff --git a/dist/sigpro.plus.js b/dist/sigpro.plus.js deleted file mode 100644 index 7c3c9b8..0000000 --- a/dist/sigpro.plus.js +++ /dev/null @@ -1,447 +0,0 @@ -// src/sigpro.js -var isFunc = (f) => typeof f === "function"; -var isObj = (o) => o && typeof o === "object"; -var isArr = Array.isArray; -var doc = typeof document !== "undefined" ? document : null; -var ensureNode = (n) => n?._isRuntime ? n.container : n instanceof Node ? n : doc.createTextNode(n == null ? "" : String(n)); -var activeEffect = null; -var activeOwner = null; -var isFlushing = false; -var batchDepth = 0; -var effectQueue = new Set; -var MOUNTED_NODES = new WeakMap; -var SVG_NS = "http://www.w3.org/2000/svg"; -var XLINK_NS = "http://www.w3.org/1999/xlink"; -var SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")); -var dispose = (eff) => { - if (!eff || eff._disposed) - return; - eff._disposed = true; - const stack = [eff]; - while (stack.length) { - const e = stack.pop(); - if (e._cleanups) { - e._cleanups.forEach((fn) => fn()); - e._cleanups.clear(); - } - if (e._children) { - e._children.forEach((child) => stack.push(child)); - e._children.clear(); - } - if (e._deps) { - e._deps.forEach((depSet) => depSet.delete(e)); - e._deps.clear(); - } - } -}; -var onUnmount = (fn) => { - if (activeOwner) - (activeOwner._cleanups ||= new Set).add(fn); -}; -var untrack = (fn) => { - const p = activeEffect; - activeEffect = null; - try { - return fn(); - } finally { - activeEffect = p; - } -}; -var createEffect = (fn, isComputed = false) => { - const effect = () => { - if (effect._disposed) - return; - if (effect._deps) - effect._deps.forEach((s) => s.delete(effect)); - if (effect._cleanups) { - effect._cleanups.forEach((c) => c()); - effect._cleanups.clear(); - } - const prevEffect = activeEffect; - const prevOwner = activeOwner; - activeEffect = activeOwner = effect; - try { - return effect._result = fn(); - } catch (e) { - console.error("[SigPro]", e); - } finally { - activeEffect = prevEffect; - activeOwner = prevOwner; - } - }; - effect._deps = effect._cleanups = effect._children = null; - effect._disposed = false; - effect._isComputed = isComputed; - effect._depth = activeEffect ? activeEffect._depth + 1 : 0; - effect._mounts = []; - effect._parent = activeOwner; - if (activeOwner) - (activeOwner._children ||= new Set).add(effect); - return effect; -}; -var flush = () => { - if (isFlushing) - return; - isFlushing = true; - const sorted = Array.from(effectQueue).sort((a, b) => a._depth - b._depth); - effectQueue.clear(); - for (const e of sorted) - if (!e._disposed) - e(); - isFlushing = false; -}; -var trackUpdate = (subs, trigger = false) => { - if (!trigger && activeEffect && !activeEffect._disposed) { - subs.add(activeEffect); - (activeEffect._deps ||= new Set).add(subs); - } else if (trigger && subs.size > 0) { - let hasQueue = false; - for (const e of subs) { - if (e === activeEffect || e._disposed) - continue; - if (e._isComputed) { - e._dirty = true; - if (e._subs) - trackUpdate(e._subs, true); - } else { - effectQueue.add(e); - hasQueue = true; - } - } - if (hasQueue && !isFlushing && batchDepth === 0) - queueMicrotask(flush); - } -}; -var $ = (val, key = null) => { - const subs = new Set; - if (isFunc(val)) { - let cache; - const computed = () => { - if (computed._dirty) { - const prev = activeEffect; - activeEffect = computed; - try { - const next = val(); - if (!Object.is(cache, next)) { - cache = next; - trackUpdate(subs, true); - } - } finally { - activeEffect = prev; - } - computed._dirty = false; - } - trackUpdate(subs); - return cache; - }; - computed._isComputed = true; - computed._subs = subs; - computed._dirty = true; - computed._deps = null; - computed._disposed = false; - return computed; - } - if (key) - try { - val = JSON.parse(localStorage.getItem(key)) ?? val; - } catch (e) {} - return (...args) => { - if (args.length) { - const next = isFunc(args[0]) ? args[0](val) : args[0]; - if (!Object.is(val, next)) { - val = next; - if (key) - localStorage.setItem(key, JSON.stringify(val)); - trackUpdate(subs, true); - } - } - trackUpdate(subs); - return val; - }; -}; -var watch = (sources, cb) => { - if (cb === undefined) { - const effect2 = createEffect(sources); - effect2(); - return () => dispose(effect2); - } - const effect = createEffect(() => { - const vals = isArr(sources) ? sources.map((s) => s()) : sources(); - untrack(() => cb(vals)); - }); - effect(); - return () => dispose(effect); -}; -var cleanupNode = (node) => { - if (!node) - return; - if (node._cleanups) { - node._cleanups.forEach((fn) => fn()); - node._cleanups.clear(); - } - if (node._ownerEffect) - dispose(node._ownerEffect); - if (node.childNodes) - node.childNodes.forEach((n) => cleanupNode(n)); -}; -var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; -var DANGEROUS_URI_ATTRS = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]); -var isDangerousAttr = (key) => DANGEROUS_URI_ATTRS.has(key) || key.startsWith("on"); -var validateAttr = (key, val) => { - if (val == null || val === false) - return null; - if (isDangerousAttr(key)) { - const sVal = String(val); - if (DANGEROUS_PROTOCOL.test(sVal)) - return "#"; - } - return val; -}; -var h = (tag, props = {}, children = []) => { - if (props instanceof Node || isArr(props) || !isObj(props)) { - children = props; - props = {}; - } - if (isFunc(tag)) { - const effect = createEffect(() => { - const result2 = tag(props, { - children, - emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args) - }); - effect._result = result2; - return result2; - }); - effect(); - const result = effect._result; - if (result == null) - return null; - const node = result instanceof Node || isArr(result) && result.every((n) => n instanceof Node) ? result : doc.createTextNode(String(result)); - const attach = (n) => { - if (isObj(n) && !n._isRuntime) { - n._mounts = effect._mounts || []; - n._cleanups = effect._cleanups || new Set; - n._ownerEffect = effect; - } - }; - isArr(node) ? node.forEach(attach) : attach(node); - return node; - } - const isSVG = SVG_TAGS.has(tag); - const el = isSVG ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag); - el._cleanups = new Set; - for (const k of Object.keys(props)) { - let v = props[k]; - if (k === "ref") { - isFunc(v) ? v(el) : v.current = el; - continue; - } - if (isSVG && k.startsWith("xlink:")) { - const cleanVal = validateAttr(k.slice(6), v); - cleanVal == null ? el.removeAttributeNS(XLINK_NS, k.slice(6)) : el.setAttributeNS(XLINK_NS, k.slice(6), cleanVal); - continue; - } - if (k.startsWith("on")) { - const ev = k.slice(2).toLowerCase(); - el.addEventListener(ev, v); - const off = () => el.removeEventListener(ev, v); - el._cleanups.add(off); - onUnmount(off); - } else if (isFunc(v)) { - const effect = createEffect(() => { - const val = validateAttr(k, v()); - if (k === "class") - el.className = val || ""; - else if (val == null) - el.removeAttribute(k); - else if (k === "style" && typeof val === "string") - el.setAttribute("style", val); - else if (k in el && !isSVG) - el[k] = val; - else - el.setAttribute(k, val === true ? "" : val); - }); - effect(); - el._cleanups.add(() => dispose(effect)); - onUnmount(() => dispose(effect)); - if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) { - const evType = k === "checked" ? "change" : "input"; - el.addEventListener(evType, (ev) => v(ev.target[k])); - } - } else { - const val = validateAttr(k, v); - if (val != null) { - if (k === "style" && typeof val === "string") - el.setAttribute("style", val); - else if (k in el && !isSVG) - el[k] = val; - else - el.setAttribute(k, val === true ? "" : val); - } - } - } - const append = (c) => { - if (isArr(c)) - return c.forEach(append); - if (isFunc(c)) { - const anchor = doc.createTextNode(""); - el.appendChild(anchor); - let currentNodes = []; - const effect = createEffect(() => { - const res = c(); - const next = (isArr(res) ? res : [res]).map(ensureNode); - currentNodes.forEach((n) => { - if (n._isRuntime) - n.destroy(); - else - cleanupNode(n); - if (n.parentNode) - n.remove(); - }); - let ref = anchor; - for (let i = next.length - 1;i >= 0; i--) { - const node = next[i]; - if (node.parentNode !== ref.parentNode) - ref.parentNode?.insertBefore(node, ref); - if (node._mounts) - node._mounts.forEach((fn) => fn()); - ref = node; - } - currentNodes = next; - }); - effect(); - el._cleanups.add(() => dispose(effect)); - onUnmount(() => dispose(effect)); - } else { - const node = ensureNode(c); - el.appendChild(node); - if (node._mounts) - node._mounts.forEach((fn) => fn()); - } - }; - append(children); - return el; -}; -var render = (renderFn) => { - const cleanups = new Set; - const previousOwner = activeOwner; - const previousEffect = activeEffect; - const container = doc.createElement("div"); - container.style.display = "contents"; - container.setAttribute("role", "presentation"); - activeOwner = { _cleanups: cleanups }; - activeEffect = null; - const processResult = (result) => { - if (!result) - return; - if (result._isRuntime) { - cleanups.add(result.destroy); - container.appendChild(result.container); - } else if (isArr(result)) { - result.forEach(processResult); - } else { - container.appendChild(result instanceof Node ? result : doc.createTextNode(String(result == null ? "" : result))); - } - }; - try { - processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) })); - } finally { - activeOwner = previousOwner; - activeEffect = previousEffect; - } - return { - _isRuntime: true, - container, - destroy: () => { - cleanups.forEach((fn) => fn()); - cleanupNode(container); - container.remove(); - } - }; -}; -if (typeof window !== "undefined") { - "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((tag) => { - window[tag] = (props, children) => h(tag, props, children); - }); -} - -// src/sigpro.plus.js -var router = (routes) => { - const getHash = () => window.location.hash.slice(1) || "/"; - const path = $(getHash()); - const handler = () => path(getHash()); - window.addEventListener("hashchange", handler); - const hook = h("div", { class: "router-hook" }); - let currentView = null; - watch([path], () => { - const cur = path(); - const route = routes.find((r) => { - const p1 = r.path.split("/").filter(Boolean); - const p2 = cur.split("/").filter(Boolean); - return p1.length === p2.length && p1.every((p, i) => p[0] === ":" || p === p2[i]); - }) || routes.find((r) => r.path === "*"); - if (route) { - currentView?.destroy(); - const params = {}; - route.path.split("/").filter(Boolean).forEach((p, i) => { - if (p[0] === ":") - params[p.slice(1)] = cur.split("/").filter(Boolean)[i]; - }); - router.params(params); - currentView = render(() => isFunc(route.component) ? route.component(params) : route.component); - hook.replaceChildren(currentView.container); - } - }); - hook.destroy = () => { - window.removeEventListener("hashchange", handler); - currentView?.destroy(); - }; - return hook; -}; -router.params = $({}); -router.to = (p) => window.location.hash = p.replace(/^#?\/?/, "#/"); -router.back = () => window.history.back(); -router.path = () => window.location.hash.replace(/^#/, "") || "/"; -var db = async (url, data = {}, loading = null) => { - if (loading) - loading(true); - try { - const res = await fetch(url, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data), - credentials: "include" - }); - if (!res.ok) { - const errorText = await res.text(); - throw new Error(`Error ${res.status}: ${errorText}`); - } - return await res.json(); - } finally { - if (loading) - loading(false); - } -}; -var currentLocale = $("en"); -var translations = {}; -var addLang = (obj) => { - for (const locale of Object.keys(obj)) { - if (!translations[locale]) - translations[locale] = {}; - Object.assign(translations[locale], obj[locale]); - } -}; -var setLocale = (locale) => { - if (locale && translations[locale]) { - currentLocale(locale); - } -}; -var t = (key) => { - return () => translations[currentLocale()]?.[key] ?? key; -}; -export { - t, - setLocale, - router, - db, - addLang -}; diff --git a/dist/sigpro.utils.js b/dist/sigpro.utils.js new file mode 100644 index 0000000..b218a01 --- /dev/null +++ b/dist/sigpro.utils.js @@ -0,0 +1,396 @@ +// src/sigpro.js +var isFunc = (f) => typeof f == "function"; +var isObj = (o) => o && typeof o == "object"; +var isArr = Array.isArray; +var doc = typeof document < "u" ? document : null; +var txt = (s) => doc.createTextNode(s == null ? "" : String(s)); +var toNd = (n) => n?._rt ? n._cnt : n instanceof Node ? n : txt(n); +var aEff = null; +var aOwn = null; +var isFlushing = 0; +var bDepth = 0; +var eQ = new Set; +var MOUNTED = new WeakMap; +var SVG_NS = "http://www.w3.org/2000/svg"; +var XLINK = "http://www.w3.org/1999/xlink"; +var SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")); +var DANG_ATTR = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]); +var clr = (s) => { + if (s) { + s.forEach((f) => f()); + s.clear(); + } +}; +var dispose = (e) => { + if (!e || e._x) + return; + e._x = 1; + let st = [e], c; + while (c = st.pop()) { + clr(c._c); + if (c._ch) { + c._ch.forEach((x) => st.push(x)); + c._ch.clear(); + } + if (c._d) { + c._d.forEach((d) => d.delete(c)); + c._d.clear(); + } + } +}; +var onUnmount = (f) => aOwn && (aOwn._c ||= new Set).add(f); +var untrack = (f) => { + let p = aEff; + aEff = null; + try { + return f(); + } finally { + aEff = p; + } +}; +var createEffect = (f, isC = 0) => { + const e = () => { + if (e._x) + return; + if (e._d) + e._d.forEach((s) => s.delete(e)); + clr(e._c); + let pE = aEff, pO = aOwn; + aEff = aOwn = e; + try { + return e._res = f(); + } catch (err) { + console.error("[SigPro]", err); + } finally { + aEff = pE; + aOwn = pO; + } + }; + e._d = e._c = e._ch = null; + e._x = 0; + e._iC = isC; + e._dp = aEff ? aEff._dp + 1 : 0; + e._m = []; + e._p = aOwn; + if (aOwn) + (aOwn._ch ||= new Set).add(e); + return e; +}; +var flush = () => { + if (isFlushing) + return; + isFlushing = 1; + let q = [...eQ].sort((a, b) => a._dp - b._dp); + eQ.clear(); + for (let e of q) + if (!e._x) + e(); + isFlushing = 0; +}; +var trkUpd = (s, trg = 0) => { + if (!trg && aEff && !aEff._x) { + s.add(aEff); + (aEff._d ||= new Set).add(s); + } else if (trg && s.size) { + let q = 0; + for (let e of s) { + if (e === aEff || e._x) + continue; + if (e._iC) { + e._dt = 1; + if (e._sb) + trkUpd(e._sb, 1); + } else { + eQ.add(e); + q = 1; + } + } + if (q && !isFlushing && !bDepth) + queueMicrotask(flush); + } +}; +var $ = (v, k = null) => { + let s = new Set; + if (isFunc(v)) { + let c, cp = () => { + if (cp._dt) { + let p = aEff; + aEff = cp; + try { + let n = v(); + if (!Object.is(c, n)) { + c = n; + trkUpd(s, 1); + } + } finally { + aEff = p; + } + cp._dt = 0; + } + trkUpd(s); + return c; + }; + cp._iC = cp._dt = 1; + cp._sb = s; + cp._d = null; + cp._x = 0; + return cp; + } + if (k) + try { + v = JSON.parse(localStorage.getItem(k)) ?? v; + } catch (e) {} + return (...a) => { + if (a.length) { + let n = isFunc(a[0]) ? a[0](v) : a[0]; + if (!Object.is(v, n)) { + v = n; + if (k) + localStorage.setItem(k, JSON.stringify(v)); + trkUpd(s, 1); + } + } + trkUpd(s); + return v; + }; +}; +var watch = (src, cb) => { + let e = createEffect(cb ? () => { + let v = isArr(src) ? src.map((s) => s()) : src(); + untrack(() => cb(v)); + } : src); + e(); + return () => dispose(e); +}; +var clnNd = (n) => { + if (!n) + return; + clr(n._c); + if (n._oE) + dispose(n._oE); + if (n.childNodes) + n.childNodes.forEach(clnNd); +}; +var valAtt = (k, v) => v == null || v === false ? null : (DANG_ATTR.has(k) || k.startsWith("on")) && /^\s*(javascript|data|vbscript):/i.test(String(v)) ? "#" : v; +var h = (tag, prp = {}, ch = []) => { + if (prp instanceof Node || isArr(prp) || !isObj(prp)) { + ch = prp; + prp = {}; + } + if (isFunc(tag)) { + let e = createEffect(() => e._res = tag(prp, { children: ch, emit: (ev, ...a) => prp[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...a) })); + e(); + if (e._res == null) + return null; + let nd = e._res instanceof Node || isArr(e._res) && e._res.every((n) => n instanceof Node) ? e._res : txt(e._res); + let att = (n) => { + if (isObj(n) && !n._rt) { + n._m = e._m || []; + n._c = e._c || new Set; + n._oE = e; + } + }; + isArr(nd) ? nd.forEach(att) : att(nd); + return nd; + } + let isS = SVG_TAGS.has(tag), el = isS ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag); + el._c = new Set; + for (let k in prp) { + let v = prp[k]; + if (k === "ref") { + isFunc(v) ? v(el) : v.current = el; + continue; + } + if (isS && k.startsWith("xlink:")) { + let cv = valAtt(k.slice(6), v); + cv == null ? el.removeAttributeNS(XLINK, k.slice(6)) : el.setAttributeNS(XLINK, k.slice(6), cv); + continue; + } + if (k.startsWith("on")) { + let ev = k.slice(2).toLowerCase(); + el.addEventListener(ev, v); + let off = () => el.removeEventListener(ev, v); + el._c.add(off); + onUnmount(off); + } else if (isFunc(v)) { + let e = createEffect(() => { + let r = valAtt(k, v()); + if (k === "class") + el.className = r || ""; + else if (r == null) + el.removeAttribute(k); + else if (k === "style" && typeof r == "string") + el.setAttribute("style", r); + else if (k in el && !isS) + el[k] = r; + else + el.setAttribute(k, r === true ? "" : r); + }); + e(); + el._c.add(() => dispose(e)); + onUnmount(() => dispose(e)); + if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) { + el.addEventListener(k === "checked" ? "change" : "input", (ev) => v(ev.target[k])); + } + } else { + let r = valAtt(k, v); + if (r != null) { + if (k === "style" && typeof r == "string") + el.setAttribute("style", r); + else if (k in el && !isS) + el[k] = r; + else + el.setAttribute(k, r === true ? "" : r); + } + } + } + const app = (c) => { + if (isArr(c)) + return c.forEach(app); + if (isFunc(c)) { + let anc = txt(""), cur = []; + el.appendChild(anc); + let e = createEffect(() => { + let r = c(), nxt = (isArr(r) ? r : [r]).map(toNd), ref = anc; + cur.forEach((n) => { + n._rt ? n._del() : clnNd(n); + if (n.parentNode) + n.remove(); + }); + for (let i = nxt.length - 1;i >= 0; i--) { + let nd = nxt[i]; + if (nd.parentNode !== ref.parentNode) + ref.parentNode?.insertBefore(nd, ref); + if (nd._m) + nd._m.forEach((f) => f()); + ref = nd; + } + cur = nxt; + }); + e(); + el._c.add(() => dispose(e)); + onUnmount(() => dispose(e)); + } else { + let nd = toNd(c); + el.appendChild(nd); + if (nd._m) + nd._m.forEach((f) => f()); + } + }; + app(ch); + return el; +}; +var render = (rFn) => { + let c = new Set, pO = aOwn, pE = aEff, cnt = doc.createElement("div"); + cnt.style.display = "contents"; + cnt.setAttribute("role", "presentation"); + aOwn = { _c: c }; + aEff = null; + const pRes = (r) => { + if (!r) + return; + if (r._rt) { + c.add(r._del); + cnt.appendChild(r._cnt); + } else if (isArr(r)) + r.forEach(pRes); + else + cnt.appendChild(r instanceof Node ? r : txt(r)); + }; + try { + pRes(rFn({ onCleanup: (f) => c.add(f) })); + } finally { + aOwn = pO; + aEff = pE; + } + return { _rt: 1, _cnt: cnt, _del: () => { + clr(c); + clnNd(cnt); + cnt.remove(); + } }; +}; +if (typeof window < "u") { + "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((t) => window[t] = (p, c) => h(t, p, c)); +} + +// src/sigpro.utils.js +var router = (routes) => { + const getHash = () => window.location.hash.slice(1) || "/"; + const path = $(getHash()); + const handler = () => path(getHash()); + window.addEventListener("hashchange", handler); + const hook = h("div", { class: "router-hook" }); + let currentView = null; + watch([path], () => { + const cur = path(); + const route = routes.find((r) => { + const p1 = r.path.split("/").filter(Boolean); + const p2 = cur.split("/").filter(Boolean); + return p1.length === p2.length && p1.every((p, i) => p[0] === ":" || p === p2[i]); + }) || routes.find((r) => r.path === "*"); + if (route) { + currentView?.destroy(); + const params = {}; + route.path.split("/").filter(Boolean).forEach((p, i) => { + if (p[0] === ":") + params[p.slice(1)] = cur.split("/").filter(Boolean)[i]; + }); + router.params(params); + currentView = render(() => isFunc(route.component) ? route.component(params) : route.component); + hook.replaceChildren(currentView.container); + } + }); + hook.destroy = () => { + window.removeEventListener("hashchange", handler); + currentView?.destroy(); + }; + return hook; +}; +router.params = $({}); +router.to = (p) => window.location.hash = p.replace(/^#?\/?/, "#/"); +router.back = () => window.history.back(); +router.path = () => window.location.hash.replace(/^#/, "") || "/"; +var db = async (url, data = {}, loading = null) => { + if (loading) + loading(true); + try { + const res = await fetch(url, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(data), + credentials: "include" + }); + if (!res.ok) { + const errorText = await res.text(); + throw new Error(`Error ${res.status}: ${errorText}`); + } + return await res.json(); + } finally { + if (loading) + loading(false); + } +}; +var currentLocale = $("en"); +var translations = {}; +var addLang = (obj) => { + for (const locale of Object.keys(obj)) { + if (!translations[locale]) + translations[locale] = {}; + Object.assign(translations[locale], obj[locale]); + } +}; +var setLocale = (locale) => { + if (locale && translations[locale]) { + currentLocale(locale); + } +}; +var t = (key) => { + return () => translations[currentLocale()]?.[key] ?? key; +}; +export { + t, + setLocale, + router, + db, + addLang +}; diff --git a/docs/router.md b/docs/router.md index 862bc64..0191fa7 100644 --- a/docs/router.md +++ b/docs/router.md @@ -17,7 +17,7 @@ router(routes: Route[]): HTMLElement **Returns:** A `div` element (with class `"router-hook"`) that acts as the router outlet. The router automatically destroys the previous view and mounts the matched component when the hash changes. -> **Availability:** `router` and its helper methods (`router.to`, `router.back`, `router.path`, `router.params`) are exported from the SigPro module. In **ESM** you must import them (`import { router } from 'sigpro/plus'`). In the **IIFE** classic script, they are automatically available on `window`. The examples below assume the functions are already in scope. +> **Availability:** `router` and its helper methods (`router.to`, `router.back`, `router.path`, `router.params`) are exported from the SigPro module. In **ESM** you must import them (`import { router } from 'sigpro/utils'`). In the **IIFE** classic script, they are automatically available on `window`. The examples below assume the functions are already in scope. --- @@ -243,7 +243,7 @@ Thanks to **SigPro's synchronous initialization**, you no longer need to wrap yo ```javascript // src/main.js import { mount } from 'sigpro'; -import { router } from 'sigpro/plus'; +import { router } from 'sigpro/utils'; import { routes } from 'virtual:sigpro-routes'; // The Core already has Router ready diff --git a/docs/sigpro.js b/docs/sigpro.js index 528c2e9..778bbd9 100644 --- a/docs/sigpro.js +++ b/docs/sigpro.js @@ -1,443 +1,383 @@ (() => { // src/sigpro.js - var isFunc = (f) => typeof f === "function"; - var isObj = (o) => o && typeof o === "object"; + var isFunc = (f) => typeof f == "function"; + var isObj = (o) => o && typeof o == "object"; var isArr = Array.isArray; - var doc = typeof document !== "undefined" ? document : null; - var ensureNode = (n) => n?._isRuntime ? n.container : n instanceof Node ? n : doc.createTextNode(n == null ? "" : String(n)); - var activeEffect = null; - var activeOwner = null; - var isFlushing = false; - var batchDepth = 0; - var effectQueue = new Set; - var MOUNTED_NODES = new WeakMap; + var doc = typeof document < "u" ? document : null; + var txt = (s) => doc.createTextNode(s == null ? "" : String(s)); + var toNd = (n) => n?._rt ? n._cnt : n instanceof Node ? n : txt(n); + var Fragment = (p) => p.children; + var val = (v) => isFunc(v) ? v() : v; + var aEff = null; + var aOwn = null; + var isFlushing = 0; + var bDepth = 0; + var eQ = new Set; + var MOUNTED = new WeakMap; var SVG_NS = "http://www.w3.org/2000/svg"; - var XLINK_NS = "http://www.w3.org/1999/xlink"; + var XLINK = "http://www.w3.org/1999/xlink"; var SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")); - var dispose = (eff) => { - if (!eff || eff._disposed) + var DANG_ATTR = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]); + var clr = (s) => { + if (s) { + s.forEach((f) => f()); + s.clear(); + } + }; + var dispose = (e) => { + if (!e || e._x) return; - eff._disposed = true; - const stack = [eff]; - while (stack.length) { - const e = stack.pop(); - if (e._cleanups) { - e._cleanups.forEach((fn) => fn()); - e._cleanups.clear(); + e._x = 1; + let st = [e], c; + while (c = st.pop()) { + clr(c._c); + if (c._ch) { + c._ch.forEach((x) => st.push(x)); + c._ch.clear(); } - if (e._children) { - e._children.forEach((child) => stack.push(child)); - e._children.clear(); - } - if (e._deps) { - e._deps.forEach((depSet) => depSet.delete(e)); - e._deps.clear(); + if (c._d) { + c._d.forEach((d) => d.delete(c)); + c._d.clear(); } } }; - var onUnmount = (fn) => { - if (activeOwner) - (activeOwner._cleanups ||= new Set).add(fn); - }; - var untrack = (fn) => { - const p = activeEffect; - activeEffect = null; + var onUnmount = (f) => aOwn && (aOwn._c ||= new Set).add(f); + var untrack = (f) => { + let p = aEff; + aEff = null; try { - return fn(); + return f(); } finally { - activeEffect = p; + aEff = p; } }; - var createEffect = (fn, isComputed = false) => { - const effect = () => { - if (effect._disposed) + var createEffect = (f, isC = 0) => { + const e = () => { + if (e._x) return; - if (effect._deps) - effect._deps.forEach((s) => s.delete(effect)); - if (effect._cleanups) { - effect._cleanups.forEach((c) => c()); - effect._cleanups.clear(); - } - const prevEffect = activeEffect; - const prevOwner = activeOwner; - activeEffect = activeOwner = effect; + if (e._d) + e._d.forEach((s) => s.delete(e)); + clr(e._c); + let pE = aEff, pO = aOwn; + aEff = aOwn = e; try { - return effect._result = fn(); - } catch (e) { - console.error("[SigPro]", e); + return e._res = f(); + } catch (err) { + console.error("[SigPro]", err); } finally { - activeEffect = prevEffect; - activeOwner = prevOwner; + aEff = pE; + aOwn = pO; } }; - effect._deps = effect._cleanups = effect._children = null; - effect._disposed = false; - effect._isComputed = isComputed; - effect._depth = activeEffect ? activeEffect._depth + 1 : 0; - effect._mounts = []; - effect._parent = activeOwner; - if (activeOwner) - (activeOwner._children ||= new Set).add(effect); - return effect; + e._d = e._c = e._ch = null; + e._x = 0; + e._iC = isC; + e._dp = aEff ? aEff._dp + 1 : 0; + e._m = []; + e._p = aOwn; + if (aOwn) + (aOwn._ch ||= new Set).add(e); + return e; }; var flush = () => { if (isFlushing) return; - isFlushing = true; - const sorted = Array.from(effectQueue).sort((a, b) => a._depth - b._depth); - effectQueue.clear(); - for (const e of sorted) - if (!e._disposed) + isFlushing = 1; + let q = [...eQ].sort((a, b) => a._dp - b._dp); + eQ.clear(); + for (let e of q) + if (!e._x) e(); - isFlushing = false; + isFlushing = 0; }; - var batch = (fn) => { - batchDepth++; + var batch = (f) => { + bDepth++; try { - return fn(); + return f(); } finally { - batchDepth--; - if (batchDepth === 0 && effectQueue.size > 0 && !isFlushing) + if (!--bDepth && eQ.size && !isFlushing) flush(); } }; - var trackUpdate = (subs, trigger = false) => { - if (!trigger && activeEffect && !activeEffect._disposed) { - subs.add(activeEffect); - (activeEffect._deps ||= new Set).add(subs); - } else if (trigger && subs.size > 0) { - let hasQueue = false; - for (const e of subs) { - if (e === activeEffect || e._disposed) + var trkUpd = (s, trg = 0) => { + if (!trg && aEff && !aEff._x) { + s.add(aEff); + (aEff._d ||= new Set).add(s); + } else if (trg && s.size) { + let q = 0; + for (let e of s) { + if (e === aEff || e._x) continue; - if (e._isComputed) { - e._dirty = true; - if (e._subs) - trackUpdate(e._subs, true); + if (e._iC) { + e._dt = 1; + if (e._sb) + trkUpd(e._sb, 1); } else { - effectQueue.add(e); - hasQueue = true; + eQ.add(e); + q = 1; } } - if (hasQueue && !isFlushing && batchDepth === 0) + if (q && !isFlushing && !bDepth) queueMicrotask(flush); } }; - var $ = (val, key = null) => { - const subs = new Set; - if (isFunc(val)) { - let cache; - const computed = () => { - if (computed._dirty) { - const prev = activeEffect; - activeEffect = computed; + var $ = (v, k = null) => { + let s = new Set; + if (isFunc(v)) { + let c, cp = () => { + if (cp._dt) { + let p = aEff; + aEff = cp; try { - const next = val(); - if (!Object.is(cache, next)) { - cache = next; - trackUpdate(subs, true); + let n = v(); + if (!Object.is(c, n)) { + c = n; + trkUpd(s, 1); } } finally { - activeEffect = prev; + aEff = p; } - computed._dirty = false; + cp._dt = 0; } - trackUpdate(subs); - return cache; + trkUpd(s); + return c; }; - computed._isComputed = true; - computed._subs = subs; - computed._dirty = true; - computed._deps = null; - computed._disposed = false; - return computed; + cp._iC = cp._dt = 1; + cp._sb = s; + cp._d = null; + cp._x = 0; + return cp; } - if (key) + if (k) try { - val = JSON.parse(localStorage.getItem(key)) ?? val; + v = JSON.parse(localStorage.getItem(k)) ?? v; } catch (e) {} - return (...args) => { - if (args.length) { - const next = isFunc(args[0]) ? args[0](val) : args[0]; - if (!Object.is(val, next)) { - val = next; - if (key) - localStorage.setItem(key, JSON.stringify(val)); - trackUpdate(subs, true); + return (...a) => { + if (a.length) { + let n = isFunc(a[0]) ? a[0](v) : a[0]; + if (!Object.is(v, n)) { + v = n; + if (k) + localStorage.setItem(k, JSON.stringify(v)); + trkUpd(s, 1); } } - trackUpdate(subs); - return val; + trkUpd(s); + return v; }; }; - var watch = (sources, cb) => { - if (cb === undefined) { - const effect2 = createEffect(sources); - effect2(); - return () => dispose(effect2); - } - const effect = createEffect(() => { - const vals = isArr(sources) ? sources.map((s) => s()) : sources(); - untrack(() => cb(vals)); - }); - effect(); - return () => dispose(effect); + var watch = (src, cb) => { + let e = createEffect(cb ? () => { + let v = isArr(src) ? src.map((s) => s()) : src(); + untrack(() => cb(v)); + } : src); + e(); + return () => dispose(e); }; - var cleanupNode = (node) => { - if (!node) + var clnNd = (n) => { + if (!n) return; - if (node._cleanups) { - node._cleanups.forEach((fn) => fn()); - node._cleanups.clear(); - } - if (node._ownerEffect) - dispose(node._ownerEffect); - if (node.childNodes) - node.childNodes.forEach((n) => cleanupNode(n)); + clr(n._c); + if (n._oE) + dispose(n._oE); + if (n.childNodes) + n.childNodes.forEach(clnNd); }; - var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; - var DANGEROUS_URI_ATTRS = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]); - var isDangerousAttr = (key) => DANGEROUS_URI_ATTRS.has(key) || key.startsWith("on"); - var validateAttr = (key, val) => { - if (val == null || val === false) - return null; - if (isDangerousAttr(key)) { - const sVal = String(val); - if (DANGEROUS_PROTOCOL.test(sVal)) - return "#"; - } - return val; - }; - var h = (tag, props = {}, children = []) => { - if (props instanceof Node || isArr(props) || !isObj(props)) { - children = props; - props = {}; + var valAtt = (k, v) => v == null || v === false ? null : (DANG_ATTR.has(k) || k.startsWith("on")) && /^\s*(javascript|data|vbscript):/i.test(String(v)) ? "#" : v; + var h = (tag, prp = {}, ch = []) => { + if (prp instanceof Node || isArr(prp) || !isObj(prp)) { + ch = prp; + prp = {}; } if (isFunc(tag)) { - const effect = createEffect(() => { - const result2 = tag(props, { - children, - emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args) - }); - effect._result = result2; - return result2; - }); - effect(); - const result = effect._result; - if (result == null) + let e = createEffect(() => e._res = tag(prp, { children: ch, emit: (ev, ...a) => prp[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...a) })); + e(); + if (e._res == null) return null; - const node = result instanceof Node || isArr(result) && result.every((n) => n instanceof Node) ? result : doc.createTextNode(String(result)); - const attach = (n) => { - if (isObj(n) && !n._isRuntime) { - n._mounts = effect._mounts || []; - n._cleanups = effect._cleanups || new Set; - n._ownerEffect = effect; + let nd = e._res instanceof Node || isArr(e._res) && e._res.every((n) => n instanceof Node) ? e._res : txt(e._res); + let att = (n) => { + if (isObj(n) && !n._rt) { + n._m = e._m || []; + n._c = e._c || new Set; + n._oE = e; } }; - isArr(node) ? node.forEach(attach) : attach(node); - return node; + isArr(nd) ? nd.forEach(att) : att(nd); + return nd; } - const isSVG = SVG_TAGS.has(tag); - const el = isSVG ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag); - el._cleanups = new Set; - for (const k of Object.keys(props)) { - let v = props[k]; + let isS = SVG_TAGS.has(tag), el = isS ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag); + el._c = new Set; + for (let k in prp) { + let v = prp[k]; if (k === "ref") { isFunc(v) ? v(el) : v.current = el; continue; } - if (isSVG && k.startsWith("xlink:")) { - const cleanVal = validateAttr(k.slice(6), v); - cleanVal == null ? el.removeAttributeNS(XLINK_NS, k.slice(6)) : el.setAttributeNS(XLINK_NS, k.slice(6), cleanVal); + if (isS && k.startsWith("xlink:")) { + let cv = valAtt(k.slice(6), v); + cv == null ? el.removeAttributeNS(XLINK, k.slice(6)) : el.setAttributeNS(XLINK, k.slice(6), cv); continue; } if (k.startsWith("on")) { - const ev = k.slice(2).toLowerCase(); + let ev = k.slice(2).toLowerCase(); el.addEventListener(ev, v); - const off = () => el.removeEventListener(ev, v); - el._cleanups.add(off); + let off = () => el.removeEventListener(ev, v); + el._c.add(off); onUnmount(off); } else if (isFunc(v)) { - const effect = createEffect(() => { - const val = validateAttr(k, v()); + let e = createEffect(() => { + let r = valAtt(k, v()); if (k === "class") - el.className = val || ""; - else if (val == null) + el.className = r || ""; + else if (r == null) el.removeAttribute(k); - else if (k === "style" && typeof val === "string") - el.setAttribute("style", val); - else if (k in el && !isSVG) - el[k] = val; + else if (k === "style" && typeof r == "string") + el.setAttribute("style", r); + else if (k in el && !isS) + el[k] = r; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, r === true ? "" : r); }); - effect(); - el._cleanups.add(() => dispose(effect)); - onUnmount(() => dispose(effect)); + e(); + el._c.add(() => dispose(e)); + onUnmount(() => dispose(e)); if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) { - const evType = k === "checked" ? "change" : "input"; - el.addEventListener(evType, (ev) => v(ev.target[k])); + el.addEventListener(k === "checked" ? "change" : "input", (ev) => v(ev.target[k])); } } else { - const val = validateAttr(k, v); - if (val != null) { - if (k === "style" && typeof val === "string") - el.setAttribute("style", val); - else if (k in el && !isSVG) - el[k] = val; + let r = valAtt(k, v); + if (r != null) { + if (k === "style" && typeof r == "string") + el.setAttribute("style", r); + else if (k in el && !isS) + el[k] = r; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, r === true ? "" : r); } } } - const append = (c) => { + const app = (c) => { if (isArr(c)) - return c.forEach(append); + return c.forEach(app); if (isFunc(c)) { - const anchor = doc.createTextNode(""); - el.appendChild(anchor); - let currentNodes = []; - const effect = createEffect(() => { - const res = c(); - const next = (isArr(res) ? res : [res]).map(ensureNode); - currentNodes.forEach((n) => { - if (n._isRuntime) - n.destroy(); - else - cleanupNode(n); + let anc = txt(""), cur = []; + el.appendChild(anc); + let e = createEffect(() => { + let r = c(), nxt = (isArr(r) ? r : [r]).map(toNd), ref = anc; + cur.forEach((n) => { + n._rt ? n._del() : clnNd(n); if (n.parentNode) n.remove(); }); - let ref = anchor; - for (let i = next.length - 1;i >= 0; i--) { - const node = next[i]; - if (node.parentNode !== ref.parentNode) - ref.parentNode?.insertBefore(node, ref); - if (node._mounts) - node._mounts.forEach((fn) => fn()); - ref = node; + for (let i = nxt.length - 1;i >= 0; i--) { + let nd = nxt[i]; + if (nd.parentNode !== ref.parentNode) + ref.parentNode?.insertBefore(nd, ref); + if (nd._m) + nd._m.forEach((f) => f()); + ref = nd; } - currentNodes = next; + cur = nxt; }); - effect(); - el._cleanups.add(() => dispose(effect)); - onUnmount(() => dispose(effect)); + e(); + el._c.add(() => dispose(e)); + onUnmount(() => dispose(e)); } else { - const node = ensureNode(c); - el.appendChild(node); - if (node._mounts) - node._mounts.forEach((fn) => fn()); + let nd = toNd(c); + el.appendChild(nd); + if (nd._m) + nd._m.forEach((f) => f()); } }; - append(children); + app(ch); return el; }; - var render = (renderFn) => { - const cleanups = new Set; - const previousOwner = activeOwner; - const previousEffect = activeEffect; - const container = doc.createElement("div"); - container.style.display = "contents"; - container.setAttribute("role", "presentation"); - activeOwner = { _cleanups: cleanups }; - activeEffect = null; - const processResult = (result) => { - if (!result) + var render = (rFn) => { + let c = new Set, pO = aOwn, pE = aEff, cnt = doc.createElement("div"); + cnt.style.display = "contents"; + cnt.setAttribute("role", "presentation"); + aOwn = { _c: c }; + aEff = null; + const pRes = (r) => { + if (!r) return; - if (result._isRuntime) { - cleanups.add(result.destroy); - container.appendChild(result.container); - } else if (isArr(result)) { - result.forEach(processResult); - } else { - container.appendChild(result instanceof Node ? result : doc.createTextNode(String(result == null ? "" : result))); - } + if (r._rt) { + c.add(r._del); + cnt.appendChild(r._cnt); + } else if (isArr(r)) + r.forEach(pRes); + else + cnt.appendChild(r instanceof Node ? r : txt(r)); }; try { - processResult(renderFn({ onCleanup: (fn) => cleanups.add(fn) })); + pRes(rFn({ onCleanup: (f) => c.add(f) })); } finally { - activeOwner = previousOwner; - activeEffect = previousEffect; + aOwn = pO; + aEff = pE; } - return { - _isRuntime: true, - container, - destroy: () => { - cleanups.forEach((fn) => fn()); - cleanupNode(container); - container.remove(); - } - }; + return { _rt: 1, _cnt: cnt, _del: () => { + clr(c); + clnNd(cnt); + cnt.remove(); + } }; }; - var when = (cond, SIP, NOP = null) => { - const anchor = doc.createTextNode(""); - const root = h("div", { style: "display:contents" }, [anchor]); - let currentView = null; - watch(() => !!(isFunc(cond) ? cond() : cond), (show) => { - if (currentView) { - currentView.destroy(); - currentView = null; + var when = (c, Y, N = null) => { + let anc = txt(""), rt = h("div", { style: "display:contents" }, [anc]), v; + watch(() => !!val(c), (s) => { + if (v) { + v._del(); + v = null; } - const content = show ? SIP : NOP; - if (content) { - currentView = render(() => isFunc(content) ? content() : content); - root.insertBefore(currentView.container, anchor); + let ct = s ? Y : N; + if (ct) { + v = render(() => val(ct)); + rt.insertBefore(v._cnt, anc); } }); - onUnmount(() => currentView?.destroy()); - return root; + onUnmount(() => v?._del()); + return rt; }; - var each = (src, itemFn, keyField) => { - const anchor = doc.createTextNode(""); - const root = h("div", { style: "display:contents" }, [anchor]); - let cache = new Map; - watch(() => (isFunc(src) ? src() : src) || [], (items) => { - const nextCache = new Map; - const nextOrder = []; - const newItems = items || []; - for (let i = 0;i < newItems.length; i++) { - const item = newItems[i]; - const key = keyField ? item?.[keyField] ?? i : item?.id ?? i; - let view = cache.get(key); - if (!view) - view = render(() => itemFn(item, i)); + var each = (s, fn, kF) => { + let anc = txt(""), rt = h("div", { style: "display:contents" }, [anc]), cch = new Map; + watch(() => val(s) || [], (it) => { + let nCc = new Map, nOd = []; + for (let i = 0, l = (it || []).length;i < l; i++) { + let t = it[i], k = kF ? t?.[kF] ?? i : t?.id ?? i, v = cch.get(k); + if (!v) + v = render(() => fn(t, i)); else - cache.delete(key); - nextCache.set(key, view); - nextOrder.push(view); + cch.delete(k); + nCc.set(k, v); + nOd.push(v); } - cache.forEach((view) => view.destroy()); - let lastRef = anchor; - for (let i = nextOrder.length - 1;i >= 0; i--) { - const view = nextOrder[i]; - const node = view.container; - if (node.nextSibling !== lastRef) - root.insertBefore(node, lastRef); - lastRef = node; + cch.forEach((v) => v._del()); + let ref = anc; + for (let i = nOd.length - 1;i >= 0; i--) { + let nd = nOd[i]._cnt; + if (nd.nextSibling !== ref) + rt.insertBefore(nd, ref); + ref = nd; } - cache = nextCache; + cch = nCc; }); - return root; + return rt; }; - var Fragment = (props) => props.children; - var mount = (comp, target) => { - const t = typeof target === "string" ? doc.querySelector(target) : target; + var mount = (c, tgt) => { + let t = typeof tgt == "string" ? doc.querySelector(tgt) : tgt; if (!t) return; - if (MOUNTED_NODES.has(t)) - MOUNTED_NODES.get(t).destroy(); - const inst = render(isFunc(comp) ? comp : () => comp); - t.replaceChildren(inst.container); - MOUNTED_NODES.set(t, inst); - return inst; + if (MOUNTED.has(t)) + MOUNTED.get(t)._del(); + let i = render(isFunc(c) ? c : () => c); + t.replaceChildren(i._cnt); + MOUNTED.set(t, i); + return i; }; - if (typeof window !== "undefined") { - "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((tag) => { - window[tag] = (props, children) => h(tag, props, children); - }); + if (typeof window < "u") { + "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video".split(" ").forEach((t) => window[t] = (p, c) => h(t, p, c)); } - // src/sigpro.plus.js + // src/sigpro.utils.js var router = (routes) => { const getHash = () => window.location.hash.slice(1) || "/"; const path = $(getHash()); diff --git a/package.json b/package.json index 721084c..a344788 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,8 @@ "script": "./dist/sigpro.min.js", "types": "./sigpro.d.ts" }, - "./plus": { - "import": "./dist/sigpro.plus.js" + "./utils": { + "import": "./dist/sigpro.utils.js" }, "./vite": { "import": "./dist/sigpro.vite.js" @@ -49,10 +49,10 @@ "build:iife:min": "bun build ./src/build_umd.js --bundle --outfile=./dist/sigpro.min.js --format=iife --global-name=sp --minify", "build:esm": "bun build ./src/sigpro.js --bundle --outfile=./dist/sigpro.esm.js --format=esm", "build:esm:min": "bun build ./src/sigpro.js --bundle --outfile=./dist/sigpro.esm.min.js --format=esm --minify", - "build:plus": "bun build ./src/sigpro.plus.js --bundle --outfile=./dist/sigpro.plus.js --format=esm", + "build:utils": "bun build ./src/sigpro.utils.js --bundle --outfile=./dist/sigpro.utils.js --format=esm", "build:vite": "bun build ./src/sigpro.vite.js --bundle --outfile=./dist/sigpro.vite.js --format=esm --external:fs --external:path", "build:copy": "cp ./dist/sigpro.js ./docs/sigpro.js", - "build": "bun run build:iife && bun run build:iife:min && bun run build:esm && bun run build:esm:min && bun run build:plus && bun run build:vite && bun run build:copy", + "build": "bun run build:iife && bun run build:iife:min && bun run build:esm && bun run build:esm:min && bun run build:utils && bun run build:vite && bun run build:copy", "docs": "bun x serve docs" }, "keywords": [ diff --git a/src/build_umd.js b/src/build_umd.js index e8f95a4..2507b0e 100644 --- a/src/build_umd.js +++ b/src/build_umd.js @@ -1,5 +1,5 @@ import { $, watch, batch, h, Fragment, mount, when, each, isArr, isFunc, isObj } from "./sigpro.js" -import { router, addLang, t } from "./sigpro.plus.js" +import { router, addLang, t } from "./sigpro.utils.js" if (typeof window !== "undefined") { Object.assign(window, { $, watch, h, Fragment, when, each, router, addLang, t, mount, batch, isArr, isFunc, isObj }) diff --git a/src/sigpro.js b/src/sigpro.js index db6e922..3e4eeb1 100644 --- a/src/sigpro.js +++ b/src/sigpro.js @@ -1,433 +1,250 @@ -const isFunc = f => typeof f === "function" -const isObj = o => o && typeof o === "object" -const isArr = Array.isArray -const doc = typeof document !== "undefined" ? document : null -const ensureNode = n => n?._isRuntime ? n.container : (n instanceof Node ? n : doc.createTextNode(n == null ? "" : String(n))) +const isFunc = f => typeof f == "function"; +const isObj = o => o && typeof o == "object"; +const isArr = Array.isArray; +const doc = typeof document < "u" ? document : null; +const txt = s => doc.createTextNode(s == null ? "" : String(s)); +const toNd = n => n?._rt ? n._cnt : (n instanceof Node ? n : txt(n)); +const Fragment = p => p.children; +const val = v => isFunc(v) ? v() : v; -let activeEffect = null -let activeOwner = null -let isFlushing = false -let batchDepth = 0 -const effectQueue = new Set() -const MOUNTED_NODES = new WeakMap() +let aEff = null, aOwn = null, isFlushing = 0, bDepth = 0; +const eQ = new Set(), MOUNTED = new WeakMap(); -const SVG_NS = "http://www.w3.org/2000/svg" -const XLINK_NS = "http://www.w3.org/1999/xlink" -const SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")) +const SVG_NS = "http://www.w3.org/2000/svg", XLINK = "http://www.w3.org/1999/xlink"; +const SVG_TAGS = new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")); +const DANG_ATTR = new Set(["src","href","formaction","action","background","code","archive"]); -const dispose = eff => { - if (!eff || eff._disposed) return - eff._disposed = true - const stack = [eff] - while (stack.length) { - const e = stack.pop() - if (e._cleanups) { - e._cleanups.forEach(fn => fn()) - e._cleanups.clear() - } - if (e._children) { - e._children.forEach(child => stack.push(child)) - e._children.clear() - } - if (e._deps) { - e._deps.forEach(depSet => depSet.delete(e)) - e._deps.clear() - } +const clr = s => { if(s){ s.forEach(f => f()); s.clear(); } }; +const dispose = e => { + if (!e || e._x) return; + e._x = 1; + let st = [e], c; + while ((c = st.pop())) { + clr(c._c); + if (c._ch) { c._ch.forEach(x => st.push(x)); c._ch.clear(); } + if (c._d) { c._d.forEach(d => d.delete(c)); c._d.clear(); } } -} - -const onUnmount = fn => { - if (activeOwner) (activeOwner._cleanups ||= new Set()).add(fn) -} - -const untrack = fn => { - const p = activeEffect - activeEffect = null - try { return fn() } finally { activeEffect = p } -} - -const createEffect = (fn, isComputed = false) => { - const effect = () => { - if (effect._disposed) return - if (effect._deps) effect._deps.forEach(s => s.delete(effect)) - if (effect._cleanups) { - effect._cleanups.forEach(c => c()) - effect._cleanups.clear() - } - const prevEffect = activeEffect - const prevOwner = activeOwner - activeEffect = activeOwner = effect - try { - return effect._result = fn() - } catch (e) { - console.error("[SigPro]", e) - } finally { - activeEffect = prevEffect - activeOwner = prevOwner - } - } - effect._deps = effect._cleanups = effect._children = null - effect._disposed = false - effect._isComputed = isComputed - effect._depth = activeEffect ? activeEffect._depth + 1 : 0 - effect._mounts = [] - effect._parent = activeOwner - if (activeOwner) (activeOwner._children ||= new Set()).add(effect) - return effect -} - -const flush = () => { - if (isFlushing) return - isFlushing = true - const sorted = Array.from(effectQueue).sort((a, b) => a._depth - b._depth) - effectQueue.clear() - for (const e of sorted) if (!e._disposed) e() - isFlushing = false -} - -const batch = fn => { - batchDepth++ - try { - return fn() - } finally { - batchDepth-- - if (batchDepth === 0 && effectQueue.size > 0 && !isFlushing) flush() - } -} - -const trackUpdate = (subs, trigger = false) => { - if (!trigger && activeEffect && !activeEffect._disposed) { - subs.add(activeEffect); - (activeEffect._deps ||= new Set()).add(subs) - } else if (trigger && subs.size > 0) { - let hasQueue = false - for (const e of subs) { - if (e === activeEffect || e._disposed) continue - if (e._isComputed) { - e._dirty = true - if (e._subs) trackUpdate(e._subs, true) - } else { - effectQueue.add(e) - hasQueue = true - } - } - if (hasQueue && !isFlushing && batchDepth === 0) queueMicrotask(flush) - } -} - -const $ = (val, key = null) => { - const subs = new Set() - if (isFunc(val)) { - let cache - const computed = () => { - if (computed._dirty) { - const prev = activeEffect - activeEffect = computed - try { - const next = val() - if (!Object.is(cache, next)) { - cache = next - trackUpdate(subs, true) - } - } finally { - activeEffect = prev - } - computed._dirty = false - } - trackUpdate(subs) - return cache - } - computed._isComputed = true - computed._subs = subs - computed._dirty = true - computed._deps = null - computed._disposed = false - return computed - } - if (key) try { val = JSON.parse(localStorage.getItem(key)) ?? val } catch (e) { } - return (...args) => { - if (args.length) { - const next = isFunc(args[0]) ? args[0](val) : args[0] - if (!Object.is(val, next)) { - val = next - if (key) localStorage.setItem(key, JSON.stringify(val)) - trackUpdate(subs, true) - } - } - trackUpdate(subs) - return val - } -} - -const watch = (sources, cb) => { - if (cb === undefined) { - const effect = createEffect(sources) - effect() - return () => dispose(effect) - } - const effect = createEffect(() => { - const vals = isArr(sources) ? sources.map(s => s()) : sources() - untrack(() => cb(vals)) - }) - effect() - return () => dispose(effect) -} - -const cleanupNode = (node) => { - if (!node) return; - if (node._cleanups) { - node._cleanups.forEach(fn => fn()); - node._cleanups.clear(); - } - if (node._ownerEffect) dispose(node._ownerEffect); - if (node.childNodes) node.childNodes.forEach(n => cleanupNode(n)); }; -var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; -var DANGEROUS_URI_ATTRS = new Set(["src", "href", "formaction", "action", "background", "code", "archive"]); -var isDangerousAttr = (key) => DANGEROUS_URI_ATTRS.has(key) || key.startsWith("on"); +const onUnmount = f => aOwn && (aOwn._c ||= new Set()).add(f); +const untrack = f => { let p = aEff; aEff = null; try { return f() } finally { aEff = p } }; -const validateAttr = (key, val) => { - if (val == null || val === false) return null - if (isDangerousAttr(key)) { - const sVal = String(val) - if (DANGEROUS_PROTOCOL.test(sVal)) return '#' - } - return val -} +const createEffect = (f, isC = 0) => { + const e = () => { + if (e._x) return; + if (e._d) e._d.forEach(s => s.delete(e)); + clr(e._c); + let pE = aEff, pO = aOwn; + aEff = aOwn = e; + try { return e._res = f(); } + catch (err) { console.error("[SigPro]", err); } + finally { aEff = pE; aOwn = pO; } + }; + e._d = e._c = e._ch = null; + e._x = 0; e._iC = isC; + e._dp = aEff ? aEff._dp + 1 : 0; + e._m = []; e._p = aOwn; + if (aOwn) (aOwn._ch ||= new Set()).add(e); + return e; +}; -const h = (tag, props = {}, children = []) => { - if (props instanceof Node || isArr(props) || !isObj(props)) { - children = props - props = {} +const flush = () => { + if (isFlushing) return; + isFlushing = 1; + let q = [...eQ].sort((a, b) => a._dp - b._dp); + eQ.clear(); + for (let e of q) if (!e._x) e(); + isFlushing = 0; +}; + +const batch = f => { + bDepth++; + try { return f() } finally { if (!--bDepth && eQ.size && !isFlushing) flush() } +}; + +const trkUpd = (s, trg = 0) => { + if (!trg && aEff && !aEff._x) { + s.add(aEff); + (aEff._d ||= new Set()).add(s); + } else if (trg && s.size) { + let q = 0; + for (let e of s) { + if (e === aEff || e._x) continue; + if (e._iC) { e._dt = 1; if (e._sb) trkUpd(e._sb, 1); } + else { eQ.add(e); q = 1; } + } + if (q && !isFlushing && !bDepth) queueMicrotask(flush); } +}; + +const $ = (v, k = null) => { + let s = new Set(); + if (isFunc(v)) { + let c, cp = () => { + if (cp._dt) { + let p = aEff; aEff = cp; + try { let n = v(); if (!Object.is(c, n)) { c = n; trkUpd(s, 1); } } + finally { aEff = p; } + cp._dt = 0; + } + trkUpd(s); return c; + }; + cp._iC = cp._dt = 1; cp._sb = s; cp._d = null; cp._x = 0; + return cp; + } + if (k) try { v = JSON.parse(localStorage.getItem(k)) ?? v } catch(e){} + return (...a) => { + if (a.length) { + let n = isFunc(a[0]) ? a[0](v) : a[0]; + if (!Object.is(v, n)) { v = n; if (k) localStorage.setItem(k, JSON.stringify(v)); trkUpd(s, 1); } + } + trkUpd(s); return v; + }; +}; + +const watch = (src, cb) => { + let e = createEffect(cb ? () => { let v = isArr(src) ? src.map(s=>s()) : src(); untrack(() => cb(v)) } : src); + e(); return () => dispose(e); +}; + +const clnNd = n => { + if (!n) return; + clr(n._c); + if (n._oE) dispose(n._oE); + if (n.childNodes) n.childNodes.forEach(clnNd); +}; + +const valAtt = (k, v) => (v == null || v === false) ? null : + (DANG_ATTR.has(k) || k.startsWith("on")) && /^\s*(javascript|data|vbscript):/i.test(String(v)) ? '#' : v; + +const h = (tag, prp = {}, ch = []) => { + if (prp instanceof Node || isArr(prp) || !isObj(prp)) { ch = prp; prp = {}; } if (isFunc(tag)) { - const effect = createEffect(() => { - const result = tag(props, { - children, - emit: (ev, ...args) => props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...args) - }) - effect._result = result - return result - }) - effect() - - const result = effect._result - if (result == null) return null - - const node = (result instanceof Node || (isArr(result) && result.every(n => n instanceof Node))) - ? result - : doc.createTextNode(String(result)) - - const attach = n => { - if (isObj(n) && !n._isRuntime) { - n._mounts = effect._mounts || [] - n._cleanups = effect._cleanups || new Set() - n._ownerEffect = effect - } - } - - isArr(node) ? node.forEach(attach) : attach(node) - return node + let e = createEffect(() => e._res = tag(prp, { children: ch, emit: (ev, ...a) => prp[`on${ev[0].toUpperCase()}${ev.slice(1)}`]?.(...a) })); + e(); + if (e._res == null) return null; + let nd = e._res instanceof Node || (isArr(e._res) && e._res.every(n => n instanceof Node)) ? e._res : txt(e._res); + let att = n => { if (isObj(n) && !n._rt) { n._m = e._m || []; n._c = e._c || new Set(); n._oE = e; } }; + isArr(nd) ? nd.forEach(att) : att(nd); + return nd; } - const isSVG = SVG_TAGS.has(tag) - const el = isSVG ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag) - el._cleanups = new Set() + let isS = SVG_TAGS.has(tag), el = isS ? doc.createElementNS(SVG_NS, tag) : doc.createElement(tag); + el._c = new Set(); - for (const k of Object.keys(props)) { - let v = props[k] - if (k === "ref") { - isFunc(v) ? v(el) : (v.current = el) - continue - } - if (isSVG && k.startsWith("xlink:")) { - const cleanVal = validateAttr(k.slice(6), v) - cleanVal == null - ? el.removeAttributeNS(XLINK_NS, k.slice(6)) - : el.setAttributeNS(XLINK_NS, k.slice(6), cleanVal) - continue + for (let k in prp) { + let v = prp[k]; + if (k === "ref") { isFunc(v) ? v(el) : (v.current = el); continue; } + if (isS && k.startsWith("xlink:")) { + let cv = valAtt(k.slice(6), v); + cv == null ? el.removeAttributeNS(XLINK, k.slice(6)) : el.setAttributeNS(XLINK, k.slice(6), cv); + continue; } if (k.startsWith("on")) { - const ev = k.slice(2).toLowerCase() - el.addEventListener(ev, v) - const off = () => el.removeEventListener(ev, v) - el._cleanups.add(off) - onUnmount(off) + let ev = k.slice(2).toLowerCase(); el.addEventListener(ev, v); + let off = () => el.removeEventListener(ev, v); el._c.add(off); onUnmount(off); } else if (isFunc(v)) { - const effect = createEffect(() => { - const val = validateAttr(k, v()) - if (k === "class") el.className = val || "" - else if (val == null) el.removeAttribute(k) - else if (k === "style" && typeof val === "string") el.setAttribute("style", val) - else if (k in el && !isSVG) el[k] = val - else el.setAttribute(k, val === true ? "" : val) - }) - effect() - el._cleanups.add(() => dispose(effect)) - onUnmount(() => dispose(effect)) + let e = createEffect(() => { + let r = valAtt(k, v()); + if (k === "class") el.className = r || ""; + else if (r == null) el.removeAttribute(k); + else if (k === "style" && typeof r == "string") el.setAttribute("style", r); + else if (k in el && !isS) el[k] = r; + else el.setAttribute(k, r === true ? "" : r); + }); + e(); el._c.add(() => dispose(e)); onUnmount(() => dispose(e)); if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === "value" || k === "checked")) { - const evType = k === "checked" ? "change" : "input" - el.addEventListener(evType, ev => v(ev.target[k])) + el.addEventListener(k === "checked" ? "change" : "input", ev => v(ev.target[k])); } } else { - const val = validateAttr(k, v) - if (val != null) { - if (k === "style" && typeof val === "string") el.setAttribute("style", val) - else if (k in el && !isSVG) el[k] = val - else el.setAttribute(k, val === true ? "" : val) + let r = valAtt(k, v); + if (r != null) { + if (k === "style" && typeof r == "string") el.setAttribute("style", r); + else if (k in el && !isS) el[k] = r; + else el.setAttribute(k, r === true ? "" : r); } } } - const append = c => { - if (isArr(c)) return c.forEach(append) + const app = c => { + if (isArr(c)) return c.forEach(app); if (isFunc(c)) { - const anchor = doc.createTextNode("") - el.appendChild(anchor) - let currentNodes = [] - const effect = createEffect(() => { - const res = c() - const next = (isArr(res) ? res : [res]).map(ensureNode) - currentNodes.forEach(n => { - if (n._isRuntime) n.destroy() - else cleanupNode(n) - if (n.parentNode) n.remove() - }) - let ref = anchor - for (let i = next.length - 1; i >= 0; i--) { - const node = next[i] - if (node.parentNode !== ref.parentNode) ref.parentNode?.insertBefore(node, ref) - if (node._mounts) node._mounts.forEach(fn => fn()) - ref = node + let anc = txt(""), cur = []; el.appendChild(anc); + let e = createEffect(() => { + let r = c(), nxt = (isArr(r) ? r : [r]).map(toNd), ref = anc; + cur.forEach(n => { n._rt ? n._del() : clnNd(n); if (n.parentNode) n.remove(); }); + for (let i = nxt.length - 1; i >= 0; i--) { + let nd = nxt[i]; + if (nd.parentNode !== ref.parentNode) ref.parentNode?.insertBefore(nd, ref); + if (nd._m) nd._m.forEach(f => f()); ref = nd; } - currentNodes = next - }) - effect() - el._cleanups.add(() => dispose(effect)) - onUnmount(() => dispose(effect)) + cur = nxt; + }); + e(); el._c.add(() => dispose(e)); onUnmount(() => dispose(e)); } else { - const node = ensureNode(c) - el.appendChild(node) - if (node._mounts) node._mounts.forEach(fn => fn()) + let nd = toNd(c); el.appendChild(nd); + if (nd._m) nd._m.forEach(f => f()); } - } - append(children) - return el + }; + app(ch); return el; +}; + +const render = rFn => { + let c = new Set(), pO = aOwn, pE = aEff, cnt = doc.createElement("div"); + cnt.style.display = "contents"; cnt.setAttribute("role", "presentation"); + aOwn = { _c: c }; aEff = null; + const pRes = r => { + if (!r) return; + if (r._rt) { c.add(r._del); cnt.appendChild(r._cnt); } + else if (isArr(r)) r.forEach(pRes); + else cnt.appendChild(r instanceof Node ? r : txt(r)); + }; + try { pRes(rFn({ onCleanup: f => c.add(f) })); } finally { aOwn = pO; aEff = pE; } + return { _rt: 1, _cnt: cnt, _del: () => { clr(c); clnNd(cnt); cnt.remove(); } }; +}; + +const when = (c, Y, N = null) => { + let anc = txt(""), rt = h("div", { style: "display:contents" }, [anc]), v; + watch(() => !!val(c), s => { + if (v) { v._del(); v = null; } + let ct = s ? Y : N; + if (ct) { v = render(() => val(ct)); rt.insertBefore(v._cnt, anc); } + }); + onUnmount(() => v?._del()); return rt; +}; + +const each = (s, fn, kF) => { + let anc = txt(""), rt = h("div", { style: "display:contents" }, [anc]), cch = new Map(); + watch(() => val(s) || [], it => { + let nCc = new Map(), nOd = []; + for (let i = 0, l = (it||[]).length; i < l; i++) { + let t = it[i], k = kF ? (t?.[kF] ?? i) : (t?.id ?? i), v = cch.get(k); + if (!v) v = render(() => fn(t, i)); else cch.delete(k); + nCc.set(k, v); nOd.push(v); + } + cch.forEach(v => v._del()); + let ref = anc; + for (let i = nOd.length - 1; i >= 0; i--) { + let nd = nOd[i]._cnt; + if (nd.nextSibling !== ref) rt.insertBefore(nd, ref); ref = nd; + } + cch = nCc; + }); + return rt; +}; + +const mount = (c, tgt) => { + let t = typeof tgt == "string" ? doc.querySelector(tgt) : tgt; + if (!t) return; + if (MOUNTED.has(t)) MOUNTED.get(t)._del(); + let i = render(isFunc(c) ? c : () => c); + t.replaceChildren(i._cnt); MOUNTED.set(t, i); return i; +}; + +if (typeof window < "u") { + "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video" + .split(" ").forEach(t => window[t] = (p, c) => h(t, p, c)); } -const render = renderFn => { - const cleanups = new Set() - const previousOwner = activeOwner - const previousEffect = activeEffect - const container = doc.createElement("div") - container.style.display = "contents" - container.setAttribute("role", "presentation") - activeOwner = { _cleanups: cleanups } - activeEffect = null - - const processResult = result => { - if (!result) return - if (result._isRuntime) { - cleanups.add(result.destroy) - container.appendChild(result.container) - } else if (isArr(result)) { - result.forEach(processResult) - } else { - container.appendChild(result instanceof Node ? result : doc.createTextNode(String(result == null ? "" : result))) - } - } - - try { - processResult(renderFn({ onCleanup: fn => cleanups.add(fn) })) - } finally { - activeOwner = previousOwner - activeEffect = previousEffect - } - - return { - _isRuntime: true, - container, - destroy: () => { - cleanups.forEach(fn => fn()) - cleanupNode(container) - container.remove() - } - } -} - -const when = (cond, SIP, NOP = null) => { - const anchor = doc.createTextNode("") - const root = h("div", { style: "display:contents" }, [anchor]) - let currentView = null - - watch( - () => !!(isFunc(cond) ? cond() : cond), - show => { - if (currentView) { - currentView.destroy() - currentView = null - } - - const content = show ? SIP : NOP - if (content) { - currentView = render(() => isFunc(content) ? content() : content) - root.insertBefore(currentView.container, anchor) - } - } - ) - - onUnmount(() => currentView?.destroy()) - return root -} - -const each = (src, itemFn, keyField) => { - const anchor = doc.createTextNode("") - const root = h("div", { style: "display:contents" }, [anchor]) - let cache = new Map() - watch(() => (isFunc(src) ? src() : src) || [], items => { - const nextCache = new Map() - const nextOrder = [] - const newItems = items || [] - for (let i = 0; i < newItems.length; i++) { - const item = newItems[i] - const key = keyField ? (item?.[keyField] ?? i) : (item?.id ?? i) - let view = cache.get(key) - if (!view) view = render(() => itemFn(item, i)) - else cache.delete(key) - nextCache.set(key, view) - nextOrder.push(view) - } - cache.forEach(view => view.destroy()) - let lastRef = anchor - for (let i = nextOrder.length - 1; i >= 0; i--) { - const view = nextOrder[i] - const node = view.container - if (node.nextSibling !== lastRef) root.insertBefore(node, lastRef) - lastRef = node - } - cache = nextCache - }) - return root -} - -const Fragment = (props) => props.children; - -const mount = (comp, target) => { - const t = typeof target === "string" ? doc.querySelector(target) : target - if (!t) return - if (MOUNTED_NODES.has(t)) MOUNTED_NODES.get(t).destroy() - const inst = render(isFunc(comp) ? comp : () => comp) - t.replaceChildren(inst.container) - MOUNTED_NODES.set(t, inst) - return inst -} - -if (typeof window !== "undefined") { - "a abbr article aside audio b blockquote br button canvas caption cite code col colgroup datalist dd del details dfn dialog div dl dt em embed fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 header hr i iframe img input ins kbd label legend li main mark meter nav object ol optgroup option output p picture pre progress section select slot small source span strong sub summary sup svg table tbody td template textarea tfoot th thead time tr u ul video" - .split(" ") - .forEach(tag => { window[tag] = (props, children) => h(tag, props, children) }) -} - -export { $, watch, batch, h, Fragment, render, mount, when, each, onUnmount, isArr, isFunc, isObj } \ No newline at end of file +export { $, watch, batch, h, Fragment, render, mount, when, each, onUnmount, val, isArr, isFunc, isObj }; \ No newline at end of file diff --git a/src/sigpro.plus.js b/src/sigpro.utils.js similarity index 100% rename from src/sigpro.plus.js rename to src/sigpro.utils.js