From bedd4ae225b207978317733a4e3d3a372e4ce8ca Mon Sep 17 00:00:00 2001 From: natxocc Date: Wed, 15 Apr 2026 14:57:21 +0200 Subject: [PATCH] 1.2.12 Fix computed problem --- dist/sigpro.esm.js | 140 ++++++----- dist/sigpro.esm.min.js | 2 +- dist/sigpro.js | 140 ++++++----- dist/sigpro.min.js | 2 +- docs/_sidebar.md | 5 +- docs/examples.md | 92 +------- docs/index.html | 97 +++++--- docs/sigpro.js | 140 ++++++----- docs/ui/quick.md | 148 ------------ package.json | 1 + sigpro.js | 22 +- sigworkPro.js | 510 ----------------------------------------- 12 files changed, 327 insertions(+), 972 deletions(-) delete mode 100644 docs/ui/quick.md delete mode 100644 sigworkPro.js diff --git a/dist/sigpro.esm.js b/dist/sigpro.esm.js index 5c3d07c..de287a8 100644 --- a/dist/sigpro.esm.js +++ b/dist/sigpro.esm.js @@ -108,11 +108,11 @@ var trackUpdate = (subs, trigger = false) => { if (!trigger && activeEffect && !activeEffect._disposed) { subs.add(activeEffect); (activeEffect._deps ||= new Set).add(subs); - } else if (trigger) { + } else if (trigger && subs.size > 0) { let hasQueue = false; - subs.forEach((e) => { + for (const e of subs) { if (e === activeEffect || e._disposed) - return; + continue; if (e._isComputed) { e._dirty = true; if (e._subs) @@ -121,29 +121,29 @@ var trackUpdate = (subs, trigger = false) => { effectQueue.add(e); hasQueue = true; } - }); + } if (hasQueue && !isFlushing && batchDepth === 0) queueMicrotask(flush); } }; -var $ = (val, key = null) => { +var $ = (val2, key = null) => { const subs = new Set; - if (isFunc(val)) { - let cache, dirty = true; + if (isFunc(val2)) { + let cache; const computed = () => { - if (dirty) { + if (computed._dirty) { const prev = activeEffect; activeEffect = computed; try { - const next = val(); + const next = val2(); if (!Object.is(cache, next)) { cache = next; - dirty = false; trackUpdate(subs, true); } } finally { activeEffect = prev; } + computed._dirty = false; } trackUpdate(subs); return cache; @@ -153,44 +153,35 @@ var $ = (val, key = null) => { computed._dirty = true; computed._deps = null; computed._disposed = false; - computed.markDirty = () => { - dirty = true; - }; - computed.stop = () => { - computed._disposed = true; - if (computed._deps) { - computed._deps.forEach((depSet) => depSet.delete(computed)); - computed._deps.clear(); - } - subs.clear(); - }; + computed.stop = () => {}; if (activeOwner) onUnmount(computed.stop); return computed; } if (key) try { - val = JSON.parse(localStorage.getItem(key)) ?? val; + val2 = JSON.parse(localStorage.getItem(key)) ?? val2; } catch (e) {} return (...args) => { if (args.length) { - const next = isFunc(args[0]) ? args[0](val) : args[0]; - if (!Object.is(val, next)) { - val = next; + const next = isFunc(args[0]) ? args[0](val2) : args[0]; + if (!Object.is(val2, next)) { + val2 = next; if (key) - localStorage.setItem(key, JSON.stringify(val)); + localStorage.setItem(key, JSON.stringify(val2)); trackUpdate(subs, true); } } trackUpdate(subs); - return val; + return val2; }; }; var $$ = (target) => { if (!isObj(target)) return target; - if (proxyCache.has(target)) - return proxyCache.get(target); + let proxy = proxyCache.get(target); + if (proxy) + return proxy; const subsMap = new Map; const getSubs = (k) => { let s = subsMap.get(k); @@ -198,28 +189,30 @@ var $$ = (target) => { subsMap.set(k, s = new Set); return s; }; - const proxy = new Proxy(target, { - get(t, k) { - trackUpdate(getSubs(k)); - return $$(t[k]); + proxy = new Proxy(target, { + get(t, k, receiver) { + if (typeof k !== "symbol") + trackUpdate(getSubs(k)); + return $$(Reflect.get(t, k, receiver)); }, - set(t, k, v) { - const isNew = !(k in t); - if (!Object.is(t[k], v)) { - t[k] = v; + set(t, k, v, receiver) { + const isNew = !Reflect.has(t, k); + const oldV = Reflect.get(t, k, receiver); + const result = Reflect.set(t, k, v, receiver); + if (result && !Object.is(oldV, v)) { trackUpdate(getSubs(k), true); if (isNew) trackUpdate(getSubs(ITER), true); } - return true; + return result; }, deleteProperty(t, k) { - const res = Reflect.deleteProperty(t, k); - if (res) { + const result = Reflect.deleteProperty(t, k); + if (result) { trackUpdate(getSubs(k), true); trackUpdate(getSubs(ITER), true); } - return res; + return result; }, ownKeys(t) { trackUpdate(getSubs(ITER)); @@ -254,17 +247,17 @@ var cleanupNode = (node) => { }; var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on"); -var validateAttr = (key, val) => { - if (val == null || val === false) +var validateAttr = (key, val2) => { + if (val2 == null || val2 === false) return null; if (isDangerousAttr(key)) { - const sVal = String(val); + const sVal = String(val2); if (DANGEROUS_PROTOCOL.test(sVal)) { console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`); return "#"; } } - return val; + return val2; }; var Tag = (tag, props = {}, children = []) => { if (props instanceof Node || isArr(props) || !isObj(props)) { @@ -296,7 +289,7 @@ var Tag = (tag, props = {}, children = []) => { isArr(node) ? node.forEach(attach) : attach(node); return node; } - const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag); + const isSVG = /^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(tag); const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : doc.createElement(tag); el._cleanups = new Set; for (let k in props) { @@ -307,6 +300,11 @@ var Tag = (tag, props = {}, children = []) => { isFunc(v) ? v(el) : v.current = el; continue; } + if (isSVG && k.startsWith("xlink:")) { + const ns = "http://www.w3.org/1999/xlink"; + val == null ? el.removeAttributeNS(ns, k.slice(6)) : el.setAttributeNS(ns, k.slice(6), val); + continue; + } if (k.startsWith("on")) { const ev = k.slice(2).toLowerCase(); el.addEventListener(ev, v); @@ -315,15 +313,15 @@ var Tag = (tag, props = {}, children = []) => { onUnmount(off); } else if (isFunc(v)) { const effect = createEffect(() => { - const val = validateAttr(k, v()); + const val2 = validateAttr(k, v()); if (k === "class") - el.className = val || ""; - else if (val == null) + el.className = val2 || ""; + else if (val2 == null) el.removeAttribute(k); else if (k in el && !isSVG) - el[k] = val; + el[k] = val2; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, val2 === true ? "" : val2); }); effect(); el._cleanups.add(() => dispose(effect)); @@ -333,12 +331,12 @@ var Tag = (tag, props = {}, children = []) => { el.addEventListener(evType, (ev) => v(ev.target[k])); } } else { - const val = validateAttr(k, v); - if (val != null) { + const val2 = validateAttr(k, v); + if (val2 != null) { if (k in el && !isSVG) - el[k] = val; + el[k] = val2; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, val2 === true ? "" : val2); } } } @@ -506,6 +504,35 @@ Router.params = $({}); Router.to = (p) => window.location.hash = p.replace(/^#?\/?/, "#/"); Router.back = () => window.history.back(); Router.path = () => window.location.hash.replace(/^#/, "") || "/"; +var Anim = (show, render, { enter, leave } = {}) => { + const wrap = Tag("div", { style: "display:contents" }); + let view = null; + const wait = (el, cb) => { + let done = false; + const finish = () => !done && (done = true, cb()); + if (!el) + return finish(); + "transitionend animationend".split(" ").map((e) => el.addEventListener(e, finish, { once: true })); + setTimeout(finish, 500); + }; + Watch(show, (on) => { + if (on && !view) { + const el = (view = Render(render)).container.firstChild; + wrap.appendChild(view.container); + if (enter && el) { + el.classList.add(enter); + el.clientTop; + el.classList.add(enter + "-active"); + wait(el, () => el.classList.remove(enter, enter + "-active")); + } + } else if (!on && view) { + const el = view.container.firstChild; + const del = () => (view?.destroy(), view = null); + leave && el ? (el.classList.add(leave), wait(el, del)) : del(); + } + }); + return onUnmount(() => view?.destroy()), wrap; +}; var Mount = (comp, target) => { const t = typeof target === "string" ? doc.querySelector(target) : target; if (!t) @@ -517,7 +544,7 @@ var Mount = (comp, target) => { MOUNTED_NODES.set(t, inst); return inst; }; -var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Batch }); +var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Anim, Batch }); if (typeof window !== "undefined") { Object.assign(window, SigPro); "div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((t) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c)); @@ -533,6 +560,7 @@ export { If, For, Batch, + Anim, $$, $ }; diff --git a/dist/sigpro.esm.min.js b/dist/sigpro.esm.min.js index 042c3cc..76a12ca 100644 --- a/dist/sigpro.esm.min.js +++ b/dist/sigpro.esm.min.js @@ -1 +1 @@ -var m=(e)=>typeof e==="function",P=(e)=>e&&typeof e==="object",S=Array.isArray,E=typeof document<"u"?document:null,U=(e)=>e?._isRuntime?e.container:e instanceof Node?e:E.createTextNode(e==null?"":String(e)),d=null,_=null,b=!1,C=0,T=new Set,M=new WeakMap,j=Symbol("iter"),B=new WeakMap,g=(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((c)=>c()),t._cleanups.clear();if(t._children)t._children.forEach((c)=>s.push(c)),t._children.clear();if(t._deps)t._deps.forEach((c)=>c.delete(t)),t._deps.clear()}},k=(e)=>{if(_)(_._mounts||=[]).push(e)},N=(e)=>{if(_)(_._cleanups||=new Set).add(e)},z=(e)=>{let s=d;d=null;try{return e()}finally{d=s}},x=(e,s=!1)=>{let t=()=>{if(t._disposed)return;if(t._deps)t._deps.forEach((n)=>n.delete(t));if(t._cleanups)t._cleanups.forEach((n)=>n()),t._cleanups.clear();let c=d,r=_;d=_=t;try{return t._result=e()}catch(n){console.error("[SigPro]",n)}finally{d=c,_=r}};if(t._deps=t._cleanups=t._children=null,t._disposed=!1,t._isComputed=s,t._depth=d?d._depth+1:0,t._mounts=[],t._parent=_,_)(_._children||=new Set).add(t);return t},W=()=>{if(b)return;b=!0;let e=Array.from(T).sort((s,t)=>s._depth-t._depth);T.clear();for(let s of e)if(!s._disposed)s();b=!1},F=(e)=>{C++;try{return e()}finally{if(C--,C===0&&T.size>0&&!b)W()}},w=(e,s=!1)=>{if(!s&&d&&!d._disposed)e.add(d),(d._deps||=new Set).add(e);else if(s){let t=!1;if(e.forEach((c)=>{if(c===d||c._disposed)return;if(c._isComputed){if(c._dirty=!0,c._subs)w(c._subs,!0)}else T.add(c),t=!0}),t&&!b&&C===0)queueMicrotask(W)}},L=(e,s=null)=>{let t=new Set;if(m(e)){let c,r=!0,n=()=>{if(r){let i=d;d=n;try{let o=e();if(!Object.is(c,o))c=o,r=!1,w(t,!0)}finally{d=i}}return w(t),c};if(n._isComputed=!0,n._subs=t,n._dirty=!0,n._deps=null,n._disposed=!1,n.markDirty=()=>{r=!0},n.stop=()=>{if(n._disposed=!0,n._deps)n._deps.forEach((i)=>i.delete(n)),n._deps.clear();t.clear()},_)N(n.stop);return n}if(s)try{e=JSON.parse(localStorage.getItem(s))??e}catch(c){}return(...c)=>{if(c.length){let r=m(c[0])?c[0](e):c[0];if(!Object.is(e,r)){if(e=r,s)localStorage.setItem(s,JSON.stringify(e));w(t,!0)}}return w(t),e}},V=(e)=>{if(!P(e))return e;if(M.has(e))return M.get(e);let s=new Map,t=(r)=>{let n=s.get(r);if(!n)s.set(r,n=new Set);return n},c=new Proxy(e,{get(r,n){return w(t(n)),V(r[n])},set(r,n,i){let o=!(n in r);if(!Object.is(r[n],i)){if(r[n]=i,w(t(n),!0),o)w(t(j),!0)}return!0},deleteProperty(r,n){let i=Reflect.deleteProperty(r,n);if(i)w(t(n),!0),w(t(j),!0);return i},ownKeys(r){return w(t(j)),Reflect.ownKeys(r)}});return M.set(e,c),c},A=(e,s)=>{if(s===void 0){let c=x(e);return c(),()=>g(c)}let t=x(()=>{let c=Array.isArray(e)?e.map((r)=>r()):e();z(()=>s(c))});return t(),()=>g(t)},$=(e)=>{if(e._cleanups)e._cleanups.forEach((s)=>s()),e._cleanups.clear();if(e._ownerEffect)g(e._ownerEffect);if(e.childNodes)e.childNodes.forEach($)},G=/^\s*(javascript|data|vbscript):/i,J=(e)=>e==="src"||e==="href"||e.startsWith("on"),D=(e,s)=>{if(s==null||s===!1)return null;if(J(e)){let t=String(s);if(G.test(t))return console.warn(`[SigPro] Bloqueado protocolo peligroso en ${e}`),"#"}return s},R=(e,s={},t=[])=>{if(s instanceof Node||S(s)||!P(s))t=s,s={};if(m(e)){let i={_mounts:[],_cleanups:new Set},o=x(()=>{let l=e(s,{children:t,emit:(h,...u)=>s[`on${h[0].toUpperCase()}${h.slice(1)}`]?.(...u)});return o._result=l,l});o();let a=o._result;if(a==null)return null;let f=a instanceof Node||S(a)&&a.every((l)=>l instanceof Node)?a:E.createTextNode(String(a)),p=(l)=>{if(P(l)&&!l._isRuntime)l._mounts=o._mounts||[],l._cleanups=o._cleanups||new Set,l._ownerEffect=o};return S(f)?f.forEach(p):p(f),f}let c=/^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(e),r=c?E.createElementNS("http://www.w3.org/2000/svg",e):E.createElement(e);r._cleanups=new Set;for(let i in s){if(!s.hasOwnProperty(i))continue;let o=s[i];if(i==="ref"){m(o)?o(r):o.current=r;continue}if(i.startsWith("on")){let a=i.slice(2).toLowerCase();r.addEventListener(a,o);let f=()=>r.removeEventListener(a,o);r._cleanups.add(f),N(f)}else if(m(o)){let a=x(()=>{let f=D(i,o());if(i==="class")r.className=f||"";else if(f==null)r.removeAttribute(i);else if(i in r&&!c)r[i]=f;else r.setAttribute(i,f===!0?"":f)});if(a(),r._cleanups.add(()=>g(a)),N(()=>g(a)),/^(INPUT|TEXTAREA|SELECT)$/.test(r.tagName)&&(i==="value"||i==="checked")){let f=i==="checked"?"change":"input";r.addEventListener(f,(p)=>o(p.target[i]))}}else{let a=D(i,o);if(a!=null)if(i in r&&!c)r[i]=a;else r.setAttribute(i,a===!0?"":a)}}let n=(i)=>{if(S(i))return i.forEach(n);if(m(i)){let o=E.createTextNode("");r.appendChild(o);let a=[],f=x(()=>{let p=i(),l=(S(p)?p:[p]).map(U);a.forEach((u)=>{if(u._isRuntime)u.destroy();else $(u);if(u.parentNode)u.remove()});let h=o;for(let u=l.length-1;u>=0;u--){let y=l[u];if(y.parentNode!==h.parentNode)h.parentNode?.insertBefore(y,h);if(y._mounts)y._mounts.forEach((q)=>q());h=y}a=l});f(),r._cleanups.add(()=>g(f)),N(()=>g(f))}else{let o=U(i);if(r.appendChild(o),o._mounts)o._mounts.forEach((a)=>a())}};return n(t),r},O=(e)=>{let s=new Set,t=[],c=_,r=d,n=E.createElement("div");n.style.display="contents",n.setAttribute("role","presentation"),_={_cleanups:s,_mounts:t},d=null;let i=(o)=>{if(!o)return;if(o._isRuntime)s.add(o.destroy),n.appendChild(o.container);else if(S(o))o.forEach(i);else n.appendChild(o instanceof Node?o:E.createTextNode(String(o==null?"":o)))};try{i(e({onCleanup:(o)=>s.add(o)}))}finally{_=c,d=r}return t.forEach((o)=>o()),{_isRuntime:!0,container:n,destroy:()=>{s.forEach((o)=>o()),$(n),n.remove()}}},K=(e,s,t=null)=>{let c=E.createTextNode(""),r=R("div",{style:"display:contents"},[c]),n=null;return A(()=>!!(m(e)?e():e),(i)=>{if(n)n.destroy(),n=null;let o=i?s:t;if(o)n=O(()=>m(o)?o():o),r.insertBefore(n.container,c)}),N(()=>n?.destroy()),r},Q=(e,s,t)=>{let c=E.createTextNode(""),r=R("div",{style:"display:contents"},[c]),n=new Map;return A(()=>(m(e)?e():e)||[],(i)=>{let o=new Map,a=[],f=i||[];for(let l=0;ls(h,l));else n.delete(u);o.set(u,y),a.push(y)}n.forEach((l)=>l.destroy());let p=c;for(let l=a.length-1;l>=0;l--){let u=a[l].container;if(u.nextSibling!==p)r.insertBefore(u,p);p=u}n=o}),r},v=(e)=>{let s=()=>window.location.hash.slice(1)||"/",t=L(s()),c=()=>t(s());window.addEventListener("hashchange",c),N(()=>window.removeEventListener("hashchange",c));let r=R("div",{class:"router-hook"}),n=null;return A([t],()=>{let i=t(),o=e.find((a)=>{let f=a.path.split("/").filter(Boolean),p=i.split("/").filter(Boolean);return f.length===p.length&&f.every((l,h)=>l[0]===":"||l===p[h])})||e.find((a)=>a.path==="*");if(o){n?.destroy();let a={};o.path.split("/").filter(Boolean).forEach((f,p)=>{if(f[0]===":")a[f.slice(1)]=i.split("/").filter(Boolean)[p]}),v.params(a),n=O(()=>m(o.component)?o.component(a):o.component),r.replaceChildren(n.container)}}),r};v.params=L({});v.to=(e)=>window.location.hash=e.replace(/^#?\/?/,"#/");v.back=()=>window.history.back();v.path=()=>window.location.hash.replace(/^#/,"")||"/";var H=(e,s)=>{let t=typeof s==="string"?E.querySelector(s):s;if(!t)return;if(B.has(t))B.get(t).destroy();let c=O(m(e)?e:()=>e);return t.replaceChildren(c.container),B.set(t,c),c},I=Object.freeze({$:L,$$:V,Watch:A,Tag:R,Render:O,If:K,For:Q,Router:v,Mount:H,onMount:k,onUnmount:N,Batch:F});if(typeof window<"u")Object.assign(window,I),"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((e)=>window[e[0].toUpperCase()+e.slice(1)]=(s,t)=>I.Tag(e,s,t));export{N as onUnmount,k as onMount,A as Watch,R as Tag,v as Router,O as Render,H as Mount,K as If,Q as For,F as Batch,V as $$,L as $}; +var m=(e)=>typeof e==="function",j=(e)=>e&&typeof e==="object",N=Array.isArray,E=typeof document<"u"?document:null,$=(e)=>e?._isRuntime?e.container:e instanceof Node?e:E.createTextNode(e==null?"":String(e)),p=null,_=null,x=!1,A=0,T=new Set,U=new WeakMap,L=Symbol("iter"),M=new WeakMap,v=(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()}},q=(e)=>{if(_)(_._mounts||=[]).push(e)},g=(e)=>{if(_)(_._cleanups||=new Set).add(e)},z=(e)=>{let s=p;p=null;try{return e()}finally{p=s}},C=(e,s=!1)=>{let t=()=>{if(t._disposed)return;if(t._deps)t._deps.forEach((i)=>i.delete(t));if(t._cleanups)t._cleanups.forEach((i)=>i()),t._cleanups.clear();let r=p,n=_;p=_=t;try{return t._result=e()}catch(i){console.error("[SigPro]",i)}finally{p=r,_=n}};if(t._deps=t._cleanups=t._children=null,t._disposed=!1,t._isComputed=s,t._depth=p?p._depth+1:0,t._mounts=[],t._parent=_,_)(_._children||=new Set).add(t);return t},D=()=>{if(x)return;x=!0;let e=Array.from(T).sort((s,t)=>s._depth-t._depth);T.clear();for(let s of e)if(!s._disposed)s();x=!1},F=(e)=>{A++;try{return e()}finally{if(A--,A===0&&T.size>0&&!x)D()}},w=(e,s=!1)=>{if(!s&&p&&!p._disposed)e.add(p),(p._deps||=new Set).add(e);else if(s&&e.size>0){let t=!1;for(let r of e){if(r===p||r._disposed)continue;if(r._isComputed){if(r._dirty=!0,r._subs)w(r._subs,!0)}else T.add(r),t=!0}if(t&&!x&&A===0)queueMicrotask(D)}},B=(e,s=null)=>{let t=new Set;if(m(e)){let r,n=()=>{if(n._dirty){let i=p;p=n;try{let c=e();if(!Object.is(r,c))r=c,w(t,!0)}finally{p=i}n._dirty=!1}return w(t),r};if(n._isComputed=!0,n._subs=t,n._dirty=!0,n._deps=null,n._disposed=!1,n.stop=()=>{},_)g(n.stop);return n}if(s)try{e=JSON.parse(localStorage.getItem(s))??e}catch(r){}return(...r)=>{if(r.length){let n=m(r[0])?r[0](e):r[0];if(!Object.is(e,n)){if(e=n,s)localStorage.setItem(s,JSON.stringify(e));w(t,!0)}}return w(t),e}},V=(e)=>{if(!j(e))return e;let s=U.get(e);if(s)return s;let t=new Map,r=(n)=>{let i=t.get(n);if(!i)t.set(n,i=new Set);return i};return s=new Proxy(e,{get(n,i,c){if(typeof i!=="symbol")w(r(i));return V(Reflect.get(n,i,c))},set(n,i,c,o){let l=!Reflect.has(n,i),a=Reflect.get(n,i,o),d=Reflect.set(n,i,c,o);if(d&&!Object.is(a,c)){if(w(r(i),!0),l)w(r(L),!0)}return d},deleteProperty(n,i){let c=Reflect.deleteProperty(n,i);if(c)w(r(i),!0),w(r(L),!0);return c},ownKeys(n){return w(r(L)),Reflect.ownKeys(n)}}),U.set(e,s),s},O=(e,s)=>{if(s===void 0){let r=C(e);return r(),()=>v(r)}let t=C(()=>{let r=Array.isArray(e)?e.map((n)=>n()):e();z(()=>s(r))});return t(),()=>v(t)},P=(e)=>{if(e._cleanups)e._cleanups.forEach((s)=>s()),e._cleanups.clear();if(e._ownerEffect)v(e._ownerEffect);if(e.childNodes)e.childNodes.forEach(P)},G=/^\s*(javascript|data|vbscript):/i,J=(e)=>e==="src"||e==="href"||e.startsWith("on"),I=(e,s)=>{if(s==null||s===!1)return null;if(J(e)){let t=String(s);if(G.test(t))return console.warn(`[SigPro] Bloqueado protocolo peligroso en ${e}`),"#"}return s},R=(e,s={},t=[])=>{if(s instanceof Node||N(s)||!j(s))t=s,s={};if(m(e)){let c={_mounts:[],_cleanups:new Set},o=C(()=>{let f=e(s,{children:t,emit:(h,...u)=>s[`on${h[0].toUpperCase()}${h.slice(1)}`]?.(...u)});return o._result=f,f});o();let l=o._result;if(l==null)return null;let a=l instanceof Node||N(l)&&l.every((f)=>f instanceof Node)?l:E.createTextNode(String(l)),d=(f)=>{if(j(f)&&!f._isRuntime)f._mounts=o._mounts||[],f._cleanups=o._cleanups||new Set,f._ownerEffect=o};return N(a)?a.forEach(d):d(a),a}let r=/^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(e),n=r?E.createElementNS("http://www.w3.org/2000/svg",e):E.createElement(e);n._cleanups=new Set;for(let c in s){if(!s.hasOwnProperty(c))continue;let o=s[c];if(c==="ref"){m(o)?o(n):o.current=n;continue}if(r&&c.startsWith("xlink:")){val==null?n.removeAttributeNS("http://www.w3.org/1999/xlink",c.slice(6)):n.setAttributeNS("http://www.w3.org/1999/xlink",c.slice(6),val);continue}if(c.startsWith("on")){let l=c.slice(2).toLowerCase();n.addEventListener(l,o);let a=()=>n.removeEventListener(l,o);n._cleanups.add(a),g(a)}else if(m(o)){let l=C(()=>{let a=I(c,o());if(c==="class")n.className=a||"";else if(a==null)n.removeAttribute(c);else if(c in n&&!r)n[c]=a;else n.setAttribute(c,a===!0?"":a)});if(l(),n._cleanups.add(()=>v(l)),g(()=>v(l)),/^(INPUT|TEXTAREA|SELECT)$/.test(n.tagName)&&(c==="value"||c==="checked")){let a=c==="checked"?"change":"input";n.addEventListener(a,(d)=>o(d.target[c]))}}else{let l=I(c,o);if(l!=null)if(c in n&&!r)n[c]=l;else n.setAttribute(c,l===!0?"":l)}}let i=(c)=>{if(N(c))return c.forEach(i);if(m(c)){let o=E.createTextNode("");n.appendChild(o);let l=[],a=C(()=>{let d=c(),f=(N(d)?d:[d]).map($);l.forEach((u)=>{if(u._isRuntime)u.destroy();else P(u);if(u.parentNode)u.remove()});let h=o;for(let u=f.length-1;u>=0;u--){let y=f[u];if(y.parentNode!==h.parentNode)h.parentNode?.insertBefore(y,h);if(y._mounts)y._mounts.forEach((k)=>k());h=y}l=f});a(),n._cleanups.add(()=>v(a)),g(()=>v(a))}else{let o=$(c);if(n.appendChild(o),o._mounts)o._mounts.forEach((l)=>l())}};return i(t),n},S=(e)=>{let s=new Set,t=[],r=_,n=p,i=E.createElement("div");i.style.display="contents",i.setAttribute("role","presentation"),_={_cleanups:s,_mounts:t},p=null;let c=(o)=>{if(!o)return;if(o._isRuntime)s.add(o.destroy),i.appendChild(o.container);else if(N(o))o.forEach(c);else i.appendChild(o instanceof Node?o:E.createTextNode(String(o==null?"":o)))};try{c(e({onCleanup:(o)=>s.add(o)}))}finally{_=r,p=n}return t.forEach((o)=>o()),{_isRuntime:!0,container:i,destroy:()=>{s.forEach((o)=>o()),P(i),i.remove()}}},K=(e,s,t=null)=>{let r=E.createTextNode(""),n=R("div",{style:"display:contents"},[r]),i=null;return O(()=>!!(m(e)?e():e),(c)=>{if(i)i.destroy(),i=null;let o=c?s:t;if(o)i=S(()=>m(o)?o():o),n.insertBefore(i.container,r)}),g(()=>i?.destroy()),n},Q=(e,s,t)=>{let r=E.createTextNode(""),n=R("div",{style:"display:contents"},[r]),i=new Map;return O(()=>(m(e)?e():e)||[],(c)=>{let o=new Map,l=[],a=c||[];for(let f=0;fs(h,f));else i.delete(u);o.set(u,y),l.push(y)}i.forEach((f)=>f.destroy());let d=r;for(let f=l.length-1;f>=0;f--){let u=l[f].container;if(u.nextSibling!==d)n.insertBefore(u,d);d=u}i=o}),n},b=(e)=>{let s=()=>window.location.hash.slice(1)||"/",t=B(s()),r=()=>t(s());window.addEventListener("hashchange",r),g(()=>window.removeEventListener("hashchange",r));let n=R("div",{class:"router-hook"}),i=null;return O([t],()=>{let c=t(),o=e.find((l)=>{let a=l.path.split("/").filter(Boolean),d=c.split("/").filter(Boolean);return a.length===d.length&&a.every((f,h)=>f[0]===":"||f===d[h])})||e.find((l)=>l.path==="*");if(o){i?.destroy();let l={};o.path.split("/").filter(Boolean).forEach((a,d)=>{if(a[0]===":")l[a.slice(1)]=c.split("/").filter(Boolean)[d]}),b.params(l),i=S(()=>m(o.component)?o.component(l):o.component),n.replaceChildren(i.container)}}),n};b.params=B({});b.to=(e)=>window.location.hash=e.replace(/^#?\/?/,"#/");b.back=()=>window.history.back();b.path=()=>window.location.hash.replace(/^#/,"")||"/";var H=(e,s,{enter:t,leave:r}={})=>{let n=R("div",{style:"display:contents"}),i=null,c=(o,l)=>{let a=!1,d=()=>!a&&(a=!0,l());if(!o)return d();"transitionend animationend".split(" ").map((f)=>o.addEventListener(f,d,{once:!0})),setTimeout(d,500)};return O(e,(o)=>{if(o&&!i){let l=(i=S(s)).container.firstChild;if(n.appendChild(i.container),t&&l)l.classList.add(t),l.clientTop,l.classList.add(t+"-active"),c(l,()=>l.classList.remove(t,t+"-active"))}else if(!o&&i){let l=i.container.firstChild,a=()=>(i?.destroy(),i=null);r&&l?(l.classList.add(r),c(l,a)):a()}}),g(()=>i?.destroy()),n},X=(e,s)=>{let t=typeof s==="string"?E.querySelector(s):s;if(!t)return;if(M.has(t))M.get(t).destroy();let r=S(m(e)?e:()=>e);return t.replaceChildren(r.container),M.set(t,r),r},W=Object.freeze({$:B,$$:V,Watch:O,Tag:R,Render:S,If:K,For:Q,Router:b,Mount:X,onMount:q,onUnmount:g,Anim:H,Batch:F});if(typeof window<"u")Object.assign(window,W),"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((e)=>window[e[0].toUpperCase()+e.slice(1)]=(s,t)=>W.Tag(e,s,t));export{g as onUnmount,q as onMount,O as Watch,R as Tag,b as Router,S as Render,X as Mount,K as If,Q as For,F as Batch,H as Anim,V as $$,B as $}; diff --git a/dist/sigpro.js b/dist/sigpro.js index 2eaf487..78ba313 100644 --- a/dist/sigpro.js +++ b/dist/sigpro.js @@ -40,6 +40,7 @@ If: () => If, For: () => For, Batch: () => Batch, + Anim: () => Anim, $$: () => $$, $: () => $ }); @@ -154,11 +155,11 @@ if (!trigger && activeEffect && !activeEffect._disposed) { subs.add(activeEffect); (activeEffect._deps ||= new Set).add(subs); - } else if (trigger) { + } else if (trigger && subs.size > 0) { let hasQueue = false; - subs.forEach((e) => { + for (const e of subs) { if (e === activeEffect || e._disposed) - return; + continue; if (e._isComputed) { e._dirty = true; if (e._subs) @@ -167,29 +168,29 @@ effectQueue.add(e); hasQueue = true; } - }); + } if (hasQueue && !isFlushing && batchDepth === 0) queueMicrotask(flush); } }; - var $ = (val, key = null) => { + var $ = (val2, key = null) => { const subs = new Set; - if (isFunc(val)) { - let cache, dirty = true; + if (isFunc(val2)) { + let cache; const computed = () => { - if (dirty) { + if (computed._dirty) { const prev = activeEffect; activeEffect = computed; try { - const next = val(); + const next = val2(); if (!Object.is(cache, next)) { cache = next; - dirty = false; trackUpdate(subs, true); } } finally { activeEffect = prev; } + computed._dirty = false; } trackUpdate(subs); return cache; @@ -199,44 +200,35 @@ computed._dirty = true; computed._deps = null; computed._disposed = false; - computed.markDirty = () => { - dirty = true; - }; - computed.stop = () => { - computed._disposed = true; - if (computed._deps) { - computed._deps.forEach((depSet) => depSet.delete(computed)); - computed._deps.clear(); - } - subs.clear(); - }; + computed.stop = () => {}; if (activeOwner) onUnmount(computed.stop); return computed; } if (key) try { - val = JSON.parse(localStorage.getItem(key)) ?? val; + val2 = JSON.parse(localStorage.getItem(key)) ?? val2; } catch (e) {} return (...args) => { if (args.length) { - const next = isFunc(args[0]) ? args[0](val) : args[0]; - if (!Object.is(val, next)) { - val = next; + const next = isFunc(args[0]) ? args[0](val2) : args[0]; + if (!Object.is(val2, next)) { + val2 = next; if (key) - localStorage.setItem(key, JSON.stringify(val)); + localStorage.setItem(key, JSON.stringify(val2)); trackUpdate(subs, true); } } trackUpdate(subs); - return val; + return val2; }; }; var $$ = (target) => { if (!isObj(target)) return target; - if (proxyCache.has(target)) - return proxyCache.get(target); + let proxy = proxyCache.get(target); + if (proxy) + return proxy; const subsMap = new Map; const getSubs = (k) => { let s = subsMap.get(k); @@ -244,28 +236,30 @@ subsMap.set(k, s = new Set); return s; }; - const proxy = new Proxy(target, { - get(t, k) { - trackUpdate(getSubs(k)); - return $$(t[k]); + proxy = new Proxy(target, { + get(t, k, receiver) { + if (typeof k !== "symbol") + trackUpdate(getSubs(k)); + return $$(Reflect.get(t, k, receiver)); }, - set(t, k, v) { - const isNew = !(k in t); - if (!Object.is(t[k], v)) { - t[k] = v; + set(t, k, v, receiver) { + const isNew = !Reflect.has(t, k); + const oldV = Reflect.get(t, k, receiver); + const result = Reflect.set(t, k, v, receiver); + if (result && !Object.is(oldV, v)) { trackUpdate(getSubs(k), true); if (isNew) trackUpdate(getSubs(ITER), true); } - return true; + return result; }, deleteProperty(t, k) { - const res = Reflect.deleteProperty(t, k); - if (res) { + const result = Reflect.deleteProperty(t, k); + if (result) { trackUpdate(getSubs(k), true); trackUpdate(getSubs(ITER), true); } - return res; + return result; }, ownKeys(t) { trackUpdate(getSubs(ITER)); @@ -300,17 +294,17 @@ }; var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on"); - var validateAttr = (key, val) => { - if (val == null || val === false) + var validateAttr = (key, val2) => { + if (val2 == null || val2 === false) return null; if (isDangerousAttr(key)) { - const sVal = String(val); + const sVal = String(val2); if (DANGEROUS_PROTOCOL.test(sVal)) { console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`); return "#"; } } - return val; + return val2; }; var Tag = (tag, props = {}, children = []) => { if (props instanceof Node || isArr(props) || !isObj(props)) { @@ -342,7 +336,7 @@ isArr(node) ? node.forEach(attach) : attach(node); return node; } - const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag); + const isSVG = /^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(tag); const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : doc.createElement(tag); el._cleanups = new Set; for (let k in props) { @@ -353,6 +347,11 @@ isFunc(v) ? v(el) : v.current = el; continue; } + if (isSVG && k.startsWith("xlink:")) { + const ns = "http://www.w3.org/1999/xlink"; + val == null ? el.removeAttributeNS(ns, k.slice(6)) : el.setAttributeNS(ns, k.slice(6), val); + continue; + } if (k.startsWith("on")) { const ev = k.slice(2).toLowerCase(); el.addEventListener(ev, v); @@ -361,15 +360,15 @@ onUnmount(off); } else if (isFunc(v)) { const effect = createEffect(() => { - const val = validateAttr(k, v()); + const val2 = validateAttr(k, v()); if (k === "class") - el.className = val || ""; - else if (val == null) + el.className = val2 || ""; + else if (val2 == null) el.removeAttribute(k); else if (k in el && !isSVG) - el[k] = val; + el[k] = val2; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, val2 === true ? "" : val2); }); effect(); el._cleanups.add(() => dispose(effect)); @@ -379,12 +378,12 @@ el.addEventListener(evType, (ev) => v(ev.target[k])); } } else { - const val = validateAttr(k, v); - if (val != null) { + const val2 = validateAttr(k, v); + if (val2 != null) { if (k in el && !isSVG) - el[k] = val; + el[k] = val2; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, val2 === true ? "" : val2); } } } @@ -552,6 +551,35 @@ Router.to = (p) => window.location.hash = p.replace(/^#?\/?/, "#/"); Router.back = () => window.history.back(); Router.path = () => window.location.hash.replace(/^#/, "") || "/"; + var Anim = (show, render, { enter, leave } = {}) => { + const wrap = Tag("div", { style: "display:contents" }); + let view = null; + const wait = (el, cb) => { + let done = false; + const finish = () => !done && (done = true, cb()); + if (!el) + return finish(); + "transitionend animationend".split(" ").map((e) => el.addEventListener(e, finish, { once: true })); + setTimeout(finish, 500); + }; + Watch(show, (on) => { + if (on && !view) { + const el = (view = Render(render)).container.firstChild; + wrap.appendChild(view.container); + if (enter && el) { + el.classList.add(enter); + el.clientTop; + el.classList.add(enter + "-active"); + wait(el, () => el.classList.remove(enter, enter + "-active")); + } + } else if (!on && view) { + const el = view.container.firstChild; + const del = () => (view?.destroy(), view = null); + leave && el ? (el.classList.add(leave), wait(el, del)) : del(); + } + }); + return onUnmount(() => view?.destroy()), wrap; + }; var Mount = (comp, target) => { const t = typeof target === "string" ? doc.querySelector(target) : target; if (!t) @@ -563,7 +591,7 @@ MOUNTED_NODES.set(t, inst); return inst; }; - var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Batch }); + var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Anim, Batch }); if (typeof window !== "undefined") { Object.assign(window, SigPro); "div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((t) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c)); diff --git a/dist/sigpro.min.js b/dist/sigpro.min.js index c16bf71..3f3db69 100644 --- a/dist/sigpro.min.js +++ b/dist/sigpro.min.js @@ -1 +1 @@ -(()=>{var{defineProperty:j,getOwnPropertyNames:H,getOwnPropertyDescriptor:X}=Object,Y=Object.prototype.hasOwnProperty;var I=new WeakMap,Z=(e)=>{var n=I.get(e),t;if(n)return n;if(n=j({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")H(e).map((o)=>!Y.call(n,o)&&j(n,o,{get:()=>e[o],enumerable:!(t=X(e,o))||t.enumerable}));return I.set(e,n),n};var ee=(e,n)=>{for(var t in n)j(e,t,{get:n[t],enumerable:!0,configurable:!0,set:(o)=>n[t]=()=>o})};var oe={};ee(oe,{onUnmount:()=>g,onMount:()=>k,Watch:()=>C,Tag:()=>T,Router:()=>S,Render:()=>b,Mount:()=>K,If:()=>G,For:()=>J,Batch:()=>F,$$:()=>U,$:()=>M});var m=(e)=>typeof e==="function",$=(e)=>e&&typeof e==="object",v=Array.isArray,E=typeof document<"u"?document:null,W=(e)=>e?._isRuntime?e.container:e instanceof Node?e:E.createTextNode(e==null?"":String(e)),d=null,_=null,x=!1,A=0,R=new Set,B=new WeakMap,P=Symbol("iter"),L=new WeakMap,N=(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((o)=>o()),t._cleanups.clear();if(t._children)t._children.forEach((o)=>n.push(o)),t._children.clear();if(t._deps)t._deps.forEach((o)=>o.delete(t)),t._deps.clear()}},k=(e)=>{if(_)(_._mounts||=[]).push(e)},g=(e)=>{if(_)(_._cleanups||=new Set).add(e)},te=(e)=>{let n=d;d=null;try{return e()}finally{d=n}},O=(e,n=!1)=>{let t=()=>{if(t._disposed)return;if(t._deps)t._deps.forEach((s)=>s.delete(t));if(t._cleanups)t._cleanups.forEach((s)=>s()),t._cleanups.clear();let o=d,c=_;d=_=t;try{return t._result=e()}catch(s){console.error("[SigPro]",s)}finally{d=o,_=c}};if(t._deps=t._cleanups=t._children=null,t._disposed=!1,t._isComputed=n,t._depth=d?d._depth+1:0,t._mounts=[],t._parent=_,_)(_._children||=new Set).add(t);return t},z=()=>{if(x)return;x=!0;let e=Array.from(R).sort((n,t)=>n._depth-t._depth);R.clear();for(let n of e)if(!n._disposed)n();x=!1},F=(e)=>{A++;try{return e()}finally{if(A--,A===0&&R.size>0&&!x)z()}},w=(e,n=!1)=>{if(!n&&d&&!d._disposed)e.add(d),(d._deps||=new Set).add(e);else if(n){let t=!1;if(e.forEach((o)=>{if(o===d||o._disposed)return;if(o._isComputed){if(o._dirty=!0,o._subs)w(o._subs,!0)}else R.add(o),t=!0}),t&&!x&&A===0)queueMicrotask(z)}},M=(e,n=null)=>{let t=new Set;if(m(e)){let o,c=!0,s=()=>{if(c){let i=d;d=s;try{let r=e();if(!Object.is(o,r))o=r,c=!1,w(t,!0)}finally{d=i}}return w(t),o};if(s._isComputed=!0,s._subs=t,s._dirty=!0,s._deps=null,s._disposed=!1,s.markDirty=()=>{c=!0},s.stop=()=>{if(s._disposed=!0,s._deps)s._deps.forEach((i)=>i.delete(s)),s._deps.clear();t.clear()},_)g(s.stop);return s}if(n)try{e=JSON.parse(localStorage.getItem(n))??e}catch(o){}return(...o)=>{if(o.length){let c=m(o[0])?o[0](e):o[0];if(!Object.is(e,c)){if(e=c,n)localStorage.setItem(n,JSON.stringify(e));w(t,!0)}}return w(t),e}},U=(e)=>{if(!$(e))return e;if(B.has(e))return B.get(e);let n=new Map,t=(c)=>{let s=n.get(c);if(!s)n.set(c,s=new Set);return s},o=new Proxy(e,{get(c,s){return w(t(s)),U(c[s])},set(c,s,i){let r=!(s in c);if(!Object.is(c[s],i)){if(c[s]=i,w(t(s),!0),r)w(t(P),!0)}return!0},deleteProperty(c,s){let i=Reflect.deleteProperty(c,s);if(i)w(t(s),!0),w(t(P),!0);return i},ownKeys(c){return w(t(P)),Reflect.ownKeys(c)}});return B.set(e,o),o},C=(e,n)=>{if(n===void 0){let o=O(e);return o(),()=>N(o)}let t=O(()=>{let o=Array.isArray(e)?e.map((c)=>c()):e();te(()=>n(o))});return t(),()=>N(t)},D=(e)=>{if(e._cleanups)e._cleanups.forEach((n)=>n()),e._cleanups.clear();if(e._ownerEffect)N(e._ownerEffect);if(e.childNodes)e.childNodes.forEach(D)},ne=/^\s*(javascript|data|vbscript):/i,se=(e)=>e==="src"||e==="href"||e.startsWith("on"),V=(e,n)=>{if(n==null||n===!1)return null;if(se(e)){let t=String(n);if(ne.test(t))return console.warn(`[SigPro] Bloqueado protocolo peligroso en ${e}`),"#"}return n},T=(e,n={},t=[])=>{if(n instanceof Node||v(n)||!$(n))t=n,n={};if(m(e)){let i={_mounts:[],_cleanups:new Set},r=O(()=>{let l=e(n,{children:t,emit:(h,...u)=>n[`on${h[0].toUpperCase()}${h.slice(1)}`]?.(...u)});return r._result=l,l});r();let a=r._result;if(a==null)return null;let f=a instanceof Node||v(a)&&a.every((l)=>l instanceof Node)?a:E.createTextNode(String(a)),p=(l)=>{if($(l)&&!l._isRuntime)l._mounts=r._mounts||[],l._cleanups=r._cleanups||new Set,l._ownerEffect=r};return v(f)?f.forEach(p):p(f),f}let o=/^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(e),c=o?E.createElementNS("http://www.w3.org/2000/svg",e):E.createElement(e);c._cleanups=new Set;for(let i in n){if(!n.hasOwnProperty(i))continue;let r=n[i];if(i==="ref"){m(r)?r(c):r.current=c;continue}if(i.startsWith("on")){let a=i.slice(2).toLowerCase();c.addEventListener(a,r);let f=()=>c.removeEventListener(a,r);c._cleanups.add(f),g(f)}else if(m(r)){let a=O(()=>{let f=V(i,r());if(i==="class")c.className=f||"";else if(f==null)c.removeAttribute(i);else if(i in c&&!o)c[i]=f;else c.setAttribute(i,f===!0?"":f)});if(a(),c._cleanups.add(()=>N(a)),g(()=>N(a)),/^(INPUT|TEXTAREA|SELECT)$/.test(c.tagName)&&(i==="value"||i==="checked")){let f=i==="checked"?"change":"input";c.addEventListener(f,(p)=>r(p.target[i]))}}else{let a=V(i,r);if(a!=null)if(i in c&&!o)c[i]=a;else c.setAttribute(i,a===!0?"":a)}}let s=(i)=>{if(v(i))return i.forEach(s);if(m(i)){let r=E.createTextNode("");c.appendChild(r);let a=[],f=O(()=>{let p=i(),l=(v(p)?p:[p]).map(W);a.forEach((u)=>{if(u._isRuntime)u.destroy();else D(u);if(u.parentNode)u.remove()});let h=r;for(let u=l.length-1;u>=0;u--){let y=l[u];if(y.parentNode!==h.parentNode)h.parentNode?.insertBefore(y,h);if(y._mounts)y._mounts.forEach((Q)=>Q());h=y}a=l});f(),c._cleanups.add(()=>N(f)),g(()=>N(f))}else{let r=W(i);if(c.appendChild(r),r._mounts)r._mounts.forEach((a)=>a())}};return s(t),c},b=(e)=>{let n=new Set,t=[],o=_,c=d,s=E.createElement("div");s.style.display="contents",s.setAttribute("role","presentation"),_={_cleanups:n,_mounts:t},d=null;let i=(r)=>{if(!r)return;if(r._isRuntime)n.add(r.destroy),s.appendChild(r.container);else if(v(r))r.forEach(i);else s.appendChild(r instanceof Node?r:E.createTextNode(String(r==null?"":r)))};try{i(e({onCleanup:(r)=>n.add(r)}))}finally{_=o,d=c}return t.forEach((r)=>r()),{_isRuntime:!0,container:s,destroy:()=>{n.forEach((r)=>r()),D(s),s.remove()}}},G=(e,n,t=null)=>{let o=E.createTextNode(""),c=T("div",{style:"display:contents"},[o]),s=null;return C(()=>!!(m(e)?e():e),(i)=>{if(s)s.destroy(),s=null;let r=i?n:t;if(r)s=b(()=>m(r)?r():r),c.insertBefore(s.container,o)}),g(()=>s?.destroy()),c},J=(e,n,t)=>{let o=E.createTextNode(""),c=T("div",{style:"display:contents"},[o]),s=new Map;return C(()=>(m(e)?e():e)||[],(i)=>{let r=new Map,a=[],f=i||[];for(let l=0;ln(h,l));else s.delete(u);r.set(u,y),a.push(y)}s.forEach((l)=>l.destroy());let p=o;for(let l=a.length-1;l>=0;l--){let u=a[l].container;if(u.nextSibling!==p)c.insertBefore(u,p);p=u}s=r}),c},S=(e)=>{let n=()=>window.location.hash.slice(1)||"/",t=M(n()),o=()=>t(n());window.addEventListener("hashchange",o),g(()=>window.removeEventListener("hashchange",o));let c=T("div",{class:"router-hook"}),s=null;return C([t],()=>{let i=t(),r=e.find((a)=>{let f=a.path.split("/").filter(Boolean),p=i.split("/").filter(Boolean);return f.length===p.length&&f.every((l,h)=>l[0]===":"||l===p[h])})||e.find((a)=>a.path==="*");if(r){s?.destroy();let a={};r.path.split("/").filter(Boolean).forEach((f,p)=>{if(f[0]===":")a[f.slice(1)]=i.split("/").filter(Boolean)[p]}),S.params(a),s=b(()=>m(r.component)?r.component(a):r.component),c.replaceChildren(s.container)}}),c};S.params=M({});S.to=(e)=>window.location.hash=e.replace(/^#?\/?/,"#/");S.back=()=>window.history.back();S.path=()=>window.location.hash.replace(/^#/,"")||"/";var K=(e,n)=>{let t=typeof n==="string"?E.querySelector(n):n;if(!t)return;if(L.has(t))L.get(t).destroy();let o=b(m(e)?e:()=>e);return t.replaceChildren(o.container),L.set(t,o),o},q=Object.freeze({$:M,$$:U,Watch:C,Tag:T,Render:b,If:G,For:J,Router:S,Mount:K,onMount:k,onUnmount:g,Batch:F});if(typeof window<"u")Object.assign(window,q),"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((e)=>window[e[0].toUpperCase()+e.slice(1)]=(n,t)=>q.Tag(e,n,t));})(); +(()=>{var{defineProperty:M,getOwnPropertyNames:X,getOwnPropertyDescriptor:Y}=Object,Z=Object.prototype.hasOwnProperty;var I=new WeakMap,ee=(e)=>{var n=I.get(e),t;if(n)return n;if(n=M({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function")X(e).map((o)=>!Z.call(n,o)&&M(n,o,{get:()=>e[o],enumerable:!(t=Y(e,o))||t.enumerable}));return I.set(e,n),n};var te=(e,n)=>{for(var t in n)M(e,t,{get:n[t],enumerable:!0,configurable:!0,set:(o)=>n[t]=()=>o})};var re={};te(re,{onUnmount:()=>g,onMount:()=>q,Watch:()=>x,Tag:()=>C,Router:()=>S,Render:()=>N,Mount:()=>Q,If:()=>G,For:()=>J,Batch:()=>F,Anim:()=>K,$$:()=>$,$:()=>L});var m=(e)=>typeof e==="function",P=(e)=>e&&typeof e==="object",b=Array.isArray,E=typeof document<"u"?document:null,W=(e)=>e?._isRuntime?e.container:e instanceof Node?e:E.createTextNode(e==null?"":String(e)),p=null,_=null,O=!1,A=0,T=new Set,D=new WeakMap,j=Symbol("iter"),B=new WeakMap,v=(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((o)=>o()),t._cleanups.clear();if(t._children)t._children.forEach((o)=>n.push(o)),t._children.clear();if(t._deps)t._deps.forEach((o)=>o.delete(t)),t._deps.clear()}},q=(e)=>{if(_)(_._mounts||=[]).push(e)},g=(e)=>{if(_)(_._cleanups||=new Set).add(e)},ne=(e)=>{let n=p;p=null;try{return e()}finally{p=n}},R=(e,n=!1)=>{let t=()=>{if(t._disposed)return;if(t._deps)t._deps.forEach((i)=>i.delete(t));if(t._cleanups)t._cleanups.forEach((i)=>i()),t._cleanups.clear();let o=p,s=_;p=_=t;try{return t._result=e()}catch(i){console.error("[SigPro]",i)}finally{p=o,_=s}};if(t._deps=t._cleanups=t._children=null,t._disposed=!1,t._isComputed=n,t._depth=p?p._depth+1:0,t._mounts=[],t._parent=_,_)(_._children||=new Set).add(t);return t},z=()=>{if(O)return;O=!0;let e=Array.from(T).sort((n,t)=>n._depth-t._depth);T.clear();for(let n of e)if(!n._disposed)n();O=!1},F=(e)=>{A++;try{return e()}finally{if(A--,A===0&&T.size>0&&!O)z()}},w=(e,n=!1)=>{if(!n&&p&&!p._disposed)e.add(p),(p._deps||=new Set).add(e);else if(n&&e.size>0){let t=!1;for(let o of e){if(o===p||o._disposed)continue;if(o._isComputed){if(o._dirty=!0,o._subs)w(o._subs,!0)}else T.add(o),t=!0}if(t&&!O&&A===0)queueMicrotask(z)}},L=(e,n=null)=>{let t=new Set;if(m(e)){let o,s=()=>{if(s._dirty){let i=p;p=s;try{let c=e();if(!Object.is(o,c))o=c,w(t,!0)}finally{p=i}s._dirty=!1}return w(t),o};if(s._isComputed=!0,s._subs=t,s._dirty=!0,s._deps=null,s._disposed=!1,s.stop=()=>{},_)g(s.stop);return s}if(n)try{e=JSON.parse(localStorage.getItem(n))??e}catch(o){}return(...o)=>{if(o.length){let s=m(o[0])?o[0](e):o[0];if(!Object.is(e,s)){if(e=s,n)localStorage.setItem(n,JSON.stringify(e));w(t,!0)}}return w(t),e}},$=(e)=>{if(!P(e))return e;let n=D.get(e);if(n)return n;let t=new Map,o=(s)=>{let i=t.get(s);if(!i)t.set(s,i=new Set);return i};return n=new Proxy(e,{get(s,i,c){if(typeof i!=="symbol")w(o(i));return $(Reflect.get(s,i,c))},set(s,i,c,r){let l=!Reflect.has(s,i),a=Reflect.get(s,i,r),d=Reflect.set(s,i,c,r);if(d&&!Object.is(a,c)){if(w(o(i),!0),l)w(o(j),!0)}return d},deleteProperty(s,i){let c=Reflect.deleteProperty(s,i);if(c)w(o(i),!0),w(o(j),!0);return c},ownKeys(s){return w(o(j)),Reflect.ownKeys(s)}}),D.set(e,n),n},x=(e,n)=>{if(n===void 0){let o=R(e);return o(),()=>v(o)}let t=R(()=>{let o=Array.isArray(e)?e.map((s)=>s()):e();ne(()=>n(o))});return t(),()=>v(t)},U=(e)=>{if(e._cleanups)e._cleanups.forEach((n)=>n()),e._cleanups.clear();if(e._ownerEffect)v(e._ownerEffect);if(e.childNodes)e.childNodes.forEach(U)},se=/^\s*(javascript|data|vbscript):/i,oe=(e)=>e==="src"||e==="href"||e.startsWith("on"),V=(e,n)=>{if(n==null||n===!1)return null;if(oe(e)){let t=String(n);if(se.test(t))return console.warn(`[SigPro] Bloqueado protocolo peligroso en ${e}`),"#"}return n},C=(e,n={},t=[])=>{if(n instanceof Node||b(n)||!P(n))t=n,n={};if(m(e)){let c={_mounts:[],_cleanups:new Set},r=R(()=>{let f=e(n,{children:t,emit:(h,...u)=>n[`on${h[0].toUpperCase()}${h.slice(1)}`]?.(...u)});return r._result=f,f});r();let l=r._result;if(l==null)return null;let a=l instanceof Node||b(l)&&l.every((f)=>f instanceof Node)?l:E.createTextNode(String(l)),d=(f)=>{if(P(f)&&!f._isRuntime)f._mounts=r._mounts||[],f._cleanups=r._cleanups||new Set,f._ownerEffect=r};return b(a)?a.forEach(d):d(a),a}let o=/^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(e),s=o?E.createElementNS("http://www.w3.org/2000/svg",e):E.createElement(e);s._cleanups=new Set;for(let c in n){if(!n.hasOwnProperty(c))continue;let r=n[c];if(c==="ref"){m(r)?r(s):r.current=s;continue}if(o&&c.startsWith("xlink:")){val==null?s.removeAttributeNS("http://www.w3.org/1999/xlink",c.slice(6)):s.setAttributeNS("http://www.w3.org/1999/xlink",c.slice(6),val);continue}if(c.startsWith("on")){let l=c.slice(2).toLowerCase();s.addEventListener(l,r);let a=()=>s.removeEventListener(l,r);s._cleanups.add(a),g(a)}else if(m(r)){let l=R(()=>{let a=V(c,r());if(c==="class")s.className=a||"";else if(a==null)s.removeAttribute(c);else if(c in s&&!o)s[c]=a;else s.setAttribute(c,a===!0?"":a)});if(l(),s._cleanups.add(()=>v(l)),g(()=>v(l)),/^(INPUT|TEXTAREA|SELECT)$/.test(s.tagName)&&(c==="value"||c==="checked")){let a=c==="checked"?"change":"input";s.addEventListener(a,(d)=>r(d.target[c]))}}else{let l=V(c,r);if(l!=null)if(c in s&&!o)s[c]=l;else s.setAttribute(c,l===!0?"":l)}}let i=(c)=>{if(b(c))return c.forEach(i);if(m(c)){let r=E.createTextNode("");s.appendChild(r);let l=[],a=R(()=>{let d=c(),f=(b(d)?d:[d]).map(W);l.forEach((u)=>{if(u._isRuntime)u.destroy();else U(u);if(u.parentNode)u.remove()});let h=r;for(let u=f.length-1;u>=0;u--){let y=f[u];if(y.parentNode!==h.parentNode)h.parentNode?.insertBefore(y,h);if(y._mounts)y._mounts.forEach((H)=>H());h=y}l=f});a(),s._cleanups.add(()=>v(a)),g(()=>v(a))}else{let r=W(c);if(s.appendChild(r),r._mounts)r._mounts.forEach((l)=>l())}};return i(t),s},N=(e)=>{let n=new Set,t=[],o=_,s=p,i=E.createElement("div");i.style.display="contents",i.setAttribute("role","presentation"),_={_cleanups:n,_mounts:t},p=null;let c=(r)=>{if(!r)return;if(r._isRuntime)n.add(r.destroy),i.appendChild(r.container);else if(b(r))r.forEach(c);else i.appendChild(r instanceof Node?r:E.createTextNode(String(r==null?"":r)))};try{c(e({onCleanup:(r)=>n.add(r)}))}finally{_=o,p=s}return t.forEach((r)=>r()),{_isRuntime:!0,container:i,destroy:()=>{n.forEach((r)=>r()),U(i),i.remove()}}},G=(e,n,t=null)=>{let o=E.createTextNode(""),s=C("div",{style:"display:contents"},[o]),i=null;return x(()=>!!(m(e)?e():e),(c)=>{if(i)i.destroy(),i=null;let r=c?n:t;if(r)i=N(()=>m(r)?r():r),s.insertBefore(i.container,o)}),g(()=>i?.destroy()),s},J=(e,n,t)=>{let o=E.createTextNode(""),s=C("div",{style:"display:contents"},[o]),i=new Map;return x(()=>(m(e)?e():e)||[],(c)=>{let r=new Map,l=[],a=c||[];for(let f=0;fn(h,f));else i.delete(u);r.set(u,y),l.push(y)}i.forEach((f)=>f.destroy());let d=o;for(let f=l.length-1;f>=0;f--){let u=l[f].container;if(u.nextSibling!==d)s.insertBefore(u,d);d=u}i=r}),s},S=(e)=>{let n=()=>window.location.hash.slice(1)||"/",t=L(n()),o=()=>t(n());window.addEventListener("hashchange",o),g(()=>window.removeEventListener("hashchange",o));let s=C("div",{class:"router-hook"}),i=null;return x([t],()=>{let c=t(),r=e.find((l)=>{let a=l.path.split("/").filter(Boolean),d=c.split("/").filter(Boolean);return a.length===d.length&&a.every((f,h)=>f[0]===":"||f===d[h])})||e.find((l)=>l.path==="*");if(r){i?.destroy();let l={};r.path.split("/").filter(Boolean).forEach((a,d)=>{if(a[0]===":")l[a.slice(1)]=c.split("/").filter(Boolean)[d]}),S.params(l),i=N(()=>m(r.component)?r.component(l):r.component),s.replaceChildren(i.container)}}),s};S.params=L({});S.to=(e)=>window.location.hash=e.replace(/^#?\/?/,"#/");S.back=()=>window.history.back();S.path=()=>window.location.hash.replace(/^#/,"")||"/";var K=(e,n,{enter:t,leave:o}={})=>{let s=C("div",{style:"display:contents"}),i=null,c=(r,l)=>{let a=!1,d=()=>!a&&(a=!0,l());if(!r)return d();"transitionend animationend".split(" ").map((f)=>r.addEventListener(f,d,{once:!0})),setTimeout(d,500)};return x(e,(r)=>{if(r&&!i){let l=(i=N(n)).container.firstChild;if(s.appendChild(i.container),t&&l)l.classList.add(t),l.clientTop,l.classList.add(t+"-active"),c(l,()=>l.classList.remove(t,t+"-active"))}else if(!r&&i){let l=i.container.firstChild,a=()=>(i?.destroy(),i=null);o&&l?(l.classList.add(o),c(l,a)):a()}}),g(()=>i?.destroy()),s},Q=(e,n)=>{let t=typeof n==="string"?E.querySelector(n):n;if(!t)return;if(B.has(t))B.get(t).destroy();let o=N(m(e)?e:()=>e);return t.replaceChildren(o.container),B.set(t,o),o},k=Object.freeze({$:L,$$:$,Watch:x,Tag:C,Render:N,If:G,For:J,Router:S,Mount:Q,onMount:q,onUnmount:g,Anim:K,Batch:F});if(typeof window<"u")Object.assign(window,k),"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((e)=>window[e[0].toUpperCase()+e.slice(1)]=(n,t)=>k.Tag(e,n,t));})(); diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 7bfee7e..8361aa2 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -16,7 +16,4 @@ * [Tag](api/html.md) * [Tags](api/tags.md) * [Global Store](api/global.md) - * [JSX Style](api/jsx.md) - -* **UI Components** - * [Quick Start](ui/quick.md) \ No newline at end of file + * [JSX Style](api/jsx.md) \ No newline at end of file diff --git a/docs/examples.md b/docs/examples.md index 472359e..69eaf4e 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -134,94 +134,4 @@ const PersistDemo = () => { ]); }; Mount(PersistDemo, '#demo-persist'); -``` - - \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/index.html b/docs/index.html index 8567411..59b1379 100644 --- a/docs/index.html +++ b/docs/index.html @@ -1,38 +1,65 @@ - + - - - SigPro Docs - - - - - - - - - - -
+ + + SigPro Docs + + - + - - - - - \ No newline at end of file + + + + + +
+ + + + + + + + diff --git a/docs/sigpro.js b/docs/sigpro.js index 2eaf487..78ba313 100644 --- a/docs/sigpro.js +++ b/docs/sigpro.js @@ -40,6 +40,7 @@ If: () => If, For: () => For, Batch: () => Batch, + Anim: () => Anim, $$: () => $$, $: () => $ }); @@ -154,11 +155,11 @@ if (!trigger && activeEffect && !activeEffect._disposed) { subs.add(activeEffect); (activeEffect._deps ||= new Set).add(subs); - } else if (trigger) { + } else if (trigger && subs.size > 0) { let hasQueue = false; - subs.forEach((e) => { + for (const e of subs) { if (e === activeEffect || e._disposed) - return; + continue; if (e._isComputed) { e._dirty = true; if (e._subs) @@ -167,29 +168,29 @@ effectQueue.add(e); hasQueue = true; } - }); + } if (hasQueue && !isFlushing && batchDepth === 0) queueMicrotask(flush); } }; - var $ = (val, key = null) => { + var $ = (val2, key = null) => { const subs = new Set; - if (isFunc(val)) { - let cache, dirty = true; + if (isFunc(val2)) { + let cache; const computed = () => { - if (dirty) { + if (computed._dirty) { const prev = activeEffect; activeEffect = computed; try { - const next = val(); + const next = val2(); if (!Object.is(cache, next)) { cache = next; - dirty = false; trackUpdate(subs, true); } } finally { activeEffect = prev; } + computed._dirty = false; } trackUpdate(subs); return cache; @@ -199,44 +200,35 @@ computed._dirty = true; computed._deps = null; computed._disposed = false; - computed.markDirty = () => { - dirty = true; - }; - computed.stop = () => { - computed._disposed = true; - if (computed._deps) { - computed._deps.forEach((depSet) => depSet.delete(computed)); - computed._deps.clear(); - } - subs.clear(); - }; + computed.stop = () => {}; if (activeOwner) onUnmount(computed.stop); return computed; } if (key) try { - val = JSON.parse(localStorage.getItem(key)) ?? val; + val2 = JSON.parse(localStorage.getItem(key)) ?? val2; } catch (e) {} return (...args) => { if (args.length) { - const next = isFunc(args[0]) ? args[0](val) : args[0]; - if (!Object.is(val, next)) { - val = next; + const next = isFunc(args[0]) ? args[0](val2) : args[0]; + if (!Object.is(val2, next)) { + val2 = next; if (key) - localStorage.setItem(key, JSON.stringify(val)); + localStorage.setItem(key, JSON.stringify(val2)); trackUpdate(subs, true); } } trackUpdate(subs); - return val; + return val2; }; }; var $$ = (target) => { if (!isObj(target)) return target; - if (proxyCache.has(target)) - return proxyCache.get(target); + let proxy = proxyCache.get(target); + if (proxy) + return proxy; const subsMap = new Map; const getSubs = (k) => { let s = subsMap.get(k); @@ -244,28 +236,30 @@ subsMap.set(k, s = new Set); return s; }; - const proxy = new Proxy(target, { - get(t, k) { - trackUpdate(getSubs(k)); - return $$(t[k]); + proxy = new Proxy(target, { + get(t, k, receiver) { + if (typeof k !== "symbol") + trackUpdate(getSubs(k)); + return $$(Reflect.get(t, k, receiver)); }, - set(t, k, v) { - const isNew = !(k in t); - if (!Object.is(t[k], v)) { - t[k] = v; + set(t, k, v, receiver) { + const isNew = !Reflect.has(t, k); + const oldV = Reflect.get(t, k, receiver); + const result = Reflect.set(t, k, v, receiver); + if (result && !Object.is(oldV, v)) { trackUpdate(getSubs(k), true); if (isNew) trackUpdate(getSubs(ITER), true); } - return true; + return result; }, deleteProperty(t, k) { - const res = Reflect.deleteProperty(t, k); - if (res) { + const result = Reflect.deleteProperty(t, k); + if (result) { trackUpdate(getSubs(k), true); trackUpdate(getSubs(ITER), true); } - return res; + return result; }, ownKeys(t) { trackUpdate(getSubs(ITER)); @@ -300,17 +294,17 @@ }; var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on"); - var validateAttr = (key, val) => { - if (val == null || val === false) + var validateAttr = (key, val2) => { + if (val2 == null || val2 === false) return null; if (isDangerousAttr(key)) { - const sVal = String(val); + const sVal = String(val2); if (DANGEROUS_PROTOCOL.test(sVal)) { console.warn(`[SigPro] Bloqueado protocolo peligroso en ${key}`); return "#"; } } - return val; + return val2; }; var Tag = (tag, props = {}, children = []) => { if (props instanceof Node || isArr(props) || !isObj(props)) { @@ -342,7 +336,7 @@ isArr(node) ? node.forEach(attach) : attach(node); return node; } - const isSVG = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use)$/.test(tag); + const isSVG = /^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(tag); const el = isSVG ? doc.createElementNS("http://www.w3.org/2000/svg", tag) : doc.createElement(tag); el._cleanups = new Set; for (let k in props) { @@ -353,6 +347,11 @@ isFunc(v) ? v(el) : v.current = el; continue; } + if (isSVG && k.startsWith("xlink:")) { + const ns = "http://www.w3.org/1999/xlink"; + val == null ? el.removeAttributeNS(ns, k.slice(6)) : el.setAttributeNS(ns, k.slice(6), val); + continue; + } if (k.startsWith("on")) { const ev = k.slice(2).toLowerCase(); el.addEventListener(ev, v); @@ -361,15 +360,15 @@ onUnmount(off); } else if (isFunc(v)) { const effect = createEffect(() => { - const val = validateAttr(k, v()); + const val2 = validateAttr(k, v()); if (k === "class") - el.className = val || ""; - else if (val == null) + el.className = val2 || ""; + else if (val2 == null) el.removeAttribute(k); else if (k in el && !isSVG) - el[k] = val; + el[k] = val2; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, val2 === true ? "" : val2); }); effect(); el._cleanups.add(() => dispose(effect)); @@ -379,12 +378,12 @@ el.addEventListener(evType, (ev) => v(ev.target[k])); } } else { - const val = validateAttr(k, v); - if (val != null) { + const val2 = validateAttr(k, v); + if (val2 != null) { if (k in el && !isSVG) - el[k] = val; + el[k] = val2; else - el.setAttribute(k, val === true ? "" : val); + el.setAttribute(k, val2 === true ? "" : val2); } } } @@ -552,6 +551,35 @@ Router.to = (p) => window.location.hash = p.replace(/^#?\/?/, "#/"); Router.back = () => window.history.back(); Router.path = () => window.location.hash.replace(/^#/, "") || "/"; + var Anim = (show, render, { enter, leave } = {}) => { + const wrap = Tag("div", { style: "display:contents" }); + let view = null; + const wait = (el, cb) => { + let done = false; + const finish = () => !done && (done = true, cb()); + if (!el) + return finish(); + "transitionend animationend".split(" ").map((e) => el.addEventListener(e, finish, { once: true })); + setTimeout(finish, 500); + }; + Watch(show, (on) => { + if (on && !view) { + const el = (view = Render(render)).container.firstChild; + wrap.appendChild(view.container); + if (enter && el) { + el.classList.add(enter); + el.clientTop; + el.classList.add(enter + "-active"); + wait(el, () => el.classList.remove(enter, enter + "-active")); + } + } else if (!on && view) { + const el = view.container.firstChild; + const del = () => (view?.destroy(), view = null); + leave && el ? (el.classList.add(leave), wait(el, del)) : del(); + } + }); + return onUnmount(() => view?.destroy()), wrap; + }; var Mount = (comp, target) => { const t = typeof target === "string" ? doc.querySelector(target) : target; if (!t) @@ -563,7 +591,7 @@ MOUNTED_NODES.set(t, inst); return inst; }; - var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Batch }); + var SigPro = Object.freeze({ $, $$, Watch, Tag, Render, If, For, Router, Mount, onMount, onUnmount, Anim, Batch }); if (typeof window !== "undefined") { Object.assign(window, SigPro); "div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer ul ol li a em strong pre code form label input textarea select button img svg".split(" ").forEach((t) => window[t[0].toUpperCase() + t.slice(1)] = (p, c) => SigPro.Tag(t, p, c)); diff --git a/docs/ui/quick.md b/docs/ui/quick.md deleted file mode 100644 index b35a26e..0000000 --- a/docs/ui/quick.md +++ /dev/null @@ -1,148 +0,0 @@ -# UI Components `(WIP)` - -> **Status: Work In Progress.** -> SigPro UI is a complete component environment built with SigPro, Tailwind CSS and DaisyUI. It provides smart components that handle their own internal logic, reactivity, and professional styling. - -
-
- -
-

📢 Official Documentation

-

- All official documentation, interactive examples, and usage guides are available at: -

- -

- * Documentation is actively being updated as components are released. -

-
-
-
- ---- - -## 1. What are UI Components? - -Unlike **Tag Helpers** (which are just functional mirrors of HTML tags), SigPro UI Components are smart abstractions: - -* **Stateful**: They manage complex internal states (like date ranges, search filtering, or API lifecycles). -* **Reactive**: Attributes prefixed with `$` are automatically tracked via `Watch`. -* **Self-Sane**: They automatically use `._cleanups` to destroy observers or event listeners when removed from the DOM. -* **Themed**: Fully compatible with DaisyUI v5 theme system and Tailwind v4 utility classes. - ---- - -## 2. Prerequisites - -
-
- -
-

To ensure all components render correctly, your project must have:

-
    -
  • Tailwind CSS v4+
  • -
  • DaisyUI v5+
  • -
-
-
-
- ---- - -## 3. The UI Registry (Available Now) - -| Category | Components | -| :--- | :--- | -| **Forms & Inputs** | `Button`, `Input`, `Select`, `Autocomplete`, `Datepicker`, `Colorpicker`, `CheckBox`, `Radio`, `Range`, `Rating`, `Swap` | -| **Feedback** | `Alert`, `Toast`, `Modal`, `Loading`, `Badge`, `Tooltip`, `Indicator` | -| **Navigation** | `Navbar`, `Menu`, `Drawer`, `Tabs`, `Accordion`, `Dropdown` | -| **Data & Layout** | `Request`, `Response`, `List`, `Stack`, `Timeline`, `Stat`, `Fieldset`, `Fab` | - ---- - -## 4. Examples with "Superpowers" - -### A. The Declarative API Flow (`Request` & `Response`) -Instead of manually managing `loading` and `error` flags, use these together to handle data fetching elegantly. - -```javascript -// 1. Define the request (it tracks dependencies automatically) -const userProfile = Request( - () => `https://api.example.com/user/${userId()}` -); - -// 2. Render the UI based on the request state -Div({ class: "p-4" }, [ - Response(userProfile, (data) => - Div([ - H1(data.name), - P(data.email) - ]) - ) -]); -``` - -### B. Smart Inputs & Autocomplete -SigPro UI inputs handle labels, icons, password toggles, and validation states out of the box using DaisyUI v5 classes. - -```javascript -const searchQuery = $(""); - -Autocomplete({ - label: "Find a Country", - placeholder: "Start typing...", - options: ["Spain", "France", "Germany", "Italy", "Portugal"], - $value: searchQuery, - onSelect: (val) => console.log("Selected:", val) -}); -``` - -### C. The Reactive Datepicker -Handles single dates or ranges with a clean, reactive interface that automatically syncs with your signals. - -```javascript -const myDate = $(""); // or { start: "", end: "" } for range - -Datepicker({ - label: "Select Expiry Date", - $value: myDate, - range: false -}); -``` - -### D. Imperative Toasts & Modals -Trigger complex UI elements from your logic. These components use `Mount` internally to ensure they are properly cleaned up from memory after they close. - -```javascript -// Show a notification (Self-destroying after 3s) -Toast("Settings saved successfully!", "alert-success", 3000); - -// Control a modal with a simple signal -const isModalOpen = $(false); - -Modal({ - $open: isModalOpen, - title: "Delete Account", - buttons: [ - Button({ class: "btn-error", onclick: doDelete }, "Confirm") - ] -}, "This action cannot be undone."); -``` - ---- - -## 5. Internationalization (i18n) - -The UI library comes with a built-in locale system. - -```javascript -// Set the global UI language -Locale("en"); - -// Access translated strings (Returns a signal that tracks the current locale) -const t = tt("confirm"); -``` \ No newline at end of file diff --git a/package.json b/package.json index 8163fd1..70a06df 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "files": [ "index.js", + "sigpro.js", "dist", "sigpro", "vite", diff --git a/sigpro.js b/sigpro.js index 2a22849..67f1eb2 100644 --- a/sigpro.js +++ b/sigpro.js @@ -1,4 +1,4 @@ -// sigpro +// sigpro 1.2.12 const isFunc = f => typeof f === "function" const isObj = o => o && typeof o === "object" const isArr = Array.isArray @@ -123,19 +123,21 @@ const trackUpdate = (subs, trigger = false) => { const $ = (val, key = null) => { const subs = new Set() if (isFunc(val)) { - let cache, dirty = true + let cache const computed = () => { - if (dirty) { + if (computed._dirty) { const prev = activeEffect activeEffect = computed try { const next = val() if (!Object.is(cache, next)) { cache = next - dirty = false trackUpdate(subs, true) } - } finally { activeEffect = prev } + } finally { + activeEffect = prev + } + computed._dirty = false } trackUpdate(subs) return cache @@ -145,15 +147,7 @@ const $ = (val, key = null) => { computed._dirty = true computed._deps = null computed._disposed = false - computed.markDirty = () => { dirty = true } - computed.stop = () => { - computed._disposed = true - if (computed._deps) { - computed._deps.forEach(depSet => depSet.delete(computed)) - computed._deps.clear() - } - subs.clear() - } + computed.stop = () => { } if (activeOwner) onUnmount(computed.stop) return computed } diff --git a/sigworkPro.js b/sigworkPro.js deleted file mode 100644 index 6e03630..0000000 --- a/sigworkPro.js +++ /dev/null @@ -1,510 +0,0 @@ -const isFunction = v => typeof v === 'function'; -const isNode = v => v instanceof Node; -const doc = typeof document !== "undefined" ? document : null; - -let activeEffect = null; -const pendingEffects = new Set(); -let flushScheduled = false; -const nodeDisposers = new WeakMap(); - -const registerNodeCleanup = (node, disposer) => { - if (!nodeDisposers.has(node)) nodeDisposers.set(node, []); - nodeDisposers.get(node).push(disposer); -}; - -const flushEffects = () => { - if (pendingEffects.size === 0) return; - const all = Array.from(pendingEffects); - pendingEffects.clear(); - all.sort((a, b) => a.depth - b.depth); - for (let i = 0; i < all.length; i++) { - const e = all[i]; - if (!e.disposed) e.execute(); - } - flushScheduled = false; -}; - -const scheduleFlush = () => { - if (!flushScheduled) { - flushScheduled = true; - queueMicrotask(flushEffects); - } -}; - -const disposeEffectTree = effect => { - if (effect.disposed) return; - effect.disposed = true; - const stack = [effect]; - while (stack.length) { - const cur = stack.pop(); - if (cur.cleanups) { - for (const fn of cur.cleanups) fn(); - cur.cleanups.clear(); - } - if (cur.dependencies) { - for (const depSet of cur.dependencies) depSet.delete(cur); - cur.dependencies.clear(); - } - if (cur.children) { - for (const child of cur.children) stack.push(child); - cur.children.clear(); - } - } -}; - -const createEffect = (fn) => { - const effect = { - execute: null, - dependencies: new Set(), - cleanups: new Set(), - children: new Set(), - depth: activeEffect ? activeEffect.depth + 1 : 0, - disposed: false, - owner: activeEffect - }; - effect.execute = () => { - if (effect.disposed) return; - if (effect.dependencies) { - for (const depSet of effect.dependencies) depSet.delete(effect); - effect.dependencies.clear(); - } - if (effect.cleanups) { - for (const fn of effect.cleanups) fn(); - effect.cleanups.clear(); - } - const prev = activeEffect; - activeEffect = effect; - try { - const cleanup = fn(); - if (isFunction(cleanup)) effect.cleanups.add(cleanup); - } finally { - activeEffect = prev; - } - }; - if (activeEffect) activeEffect.children.add(effect); - effect.execute(); - return () => disposeEffectTree(effect); -}; - -export const Watch = createEffect; - -const getComponentContext = () => { - let eff = activeEffect; - while (eff) { - if (eff.componentContext) return eff.componentContext; - eff = eff.owner; - } - return null; -}; - -export const removeNode = node => { - if (!node) return; - if (node.childNodes) { - [...node.childNodes].forEach(removeNode); - } - const disposers = nodeDisposers.get(node); - if (disposers) { - disposers.forEach(d => d()); - nodeDisposers.delete(node); - } - if (node._raf) cancelAnimationFrame(node._raf); - if (node._stop) node._stop(); - const ctx = node.componentContext; - if (ctx) { - ctx.unmount.forEach(fn => fn()); - ctx.unmount = []; - } - if (node.leaveTransition) { - node.leaveTransition(() => node.remove()); - } else { - node.remove?.(); - } -}; - -const track = subs => { - if (activeEffect && !activeEffect.disposed) { - subs.add(activeEffect); - activeEffect.dependencies.add(subs); - } -}; - -const trigger = subs => { - if (!subs) return; - for (const eff of subs) { - if (eff !== activeEffect && !eff.disposed) pendingEffects.add(eff); - } - scheduleFlush(); -}; - -export const $ = (initialValue, storageKey) => { - if (isFunction(initialValue)) { - let dirty = true, cached; - const s = $(); - const stop = Watch(() => { - const v = initialValue(); - if (!Object.is(v, cached)) { cached = v; dirty = false; s(v); } - }); - const ctx = getComponentContext(); - if (ctx) ctx.unmount.push(stop); - const signal = newVal => { - if (newVal === undefined) { - if (dirty) { cached = initialValue(); dirty = false; s(cached); } - return s(); - } - return s(newVal); - }; - return signal; - } - const subs = new Set(); - let value = initialValue; - if (storageKey) { - try { - const item = localStorage.getItem(storageKey); - if (item !== null) value = JSON.parse(item); - } catch (e) {} - } - const signal = newVal => { - if (newVal === undefined) { - track(subs); - return value; - } - const next = isFunction(newVal) ? newVal(value) : newVal; - if (!Object.is(next, value)) { - value = next; - if (storageKey) { - try { localStorage.setItem(storageKey, JSON.stringify(value)); } catch (e) {} - } - trigger(subs); - } - return value; - }; - if (storageKey) { - const sync = Watch(() => { signal(value); }); - const ctx = getComponentContext(); - if (ctx) ctx.unmount.push(sync); - } - return signal; -}; - -export const computed = fn => { - let dirty = true; - let cache; - const subs = new Set(); - const evaluate = () => { - if (!dirty) return cache; - const prev = activeEffect; - activeEffect = null; - try { - cache = fn(); - } finally { - activeEffect = prev; - } - dirty = false; - trigger(subs); - return cache; - }; - const signal = () => { - track(subs); - return evaluate(); - }; - const effect = createEffect(() => { - fn(); - dirty = true; - trigger(subs); - }); - return signal; -}; - -export const set = (signal, path, value) => { - if (value === undefined) { - signal(isFunction(path) ? path(signal()) : path); - } else { - const keys = path.split('.'); - const last = keys.pop(); - const obj = keys.reduce((o, k) => ({ ...o, [k]: { ...o[k] } }), { ...signal() }); - obj[last] = value; - signal(obj); - } -}; - -export const watch = (source, callback) => { - let first = true, oldVal; - return Watch(() => { - const newVal = isFunction(source) ? source() : source; - if (!first) untrack(() => callback(newVal, oldVal)); - else first = false; - oldVal = newVal; - }); -}; - -export const untrack = fn => { - const prev = activeEffect; - activeEffect = null; - try { return fn(); } finally { activeEffect = prev; } -}; - -export const onMount = fn => { - const ctx = getComponentContext(); - if (ctx) ctx.mount.push(fn); -}; - -export const onUnmount = fn => { - const ctx = getComponentContext(); - if (ctx) ctx.unmount.push(fn); -}; - -const DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; -const isDangerousAttr = key => key === 'src' || key === 'href' || key.startsWith('on'); - -const validateAttr = (key, val) => { - if (val == null || val === false) return null; - if (isDangerousAttr(key)) { - const sVal = String(val); - if (DANGEROUS_PROTOCOL.test(sVal)) { - console.warn(`[SP] Bloqueado protocolo peligroso en ${key}`); - return '#'; - } - } - return val; -}; - -const setProperty = (el, key, val, isSVG) => { - val = validateAttr(key, val); - if (key === 'class' || key === 'className') { - el.className = val || ''; - } else if (key === 'style' && typeof val === 'object') { - Object.assign(el.style, val); - } else if (key in el && !isSVG) { - el[key] = val; - } else { - if (isSVG) { - if (key.startsWith('xlink:')) { - if (val == null || val === false) el.removeAttributeNS('http://www.w3.org/1999/xlink', key.slice(6)); - else el.setAttributeNS('http://www.w3.org/1999/xlink', key, val); - } else if (key === 'xmlns' || key.startsWith('xmlns:')) { - if (val == null || val === false) el.removeAttributeNS('http://www.w3.org/2000/xmlns/', key); - else el.setAttributeNS('http://www.w3.org/2000/xmlns/', key, val); - } else { - if (val == null || val === false) el.removeAttribute(key); - else if (val === true) el.setAttribute(key, ''); - else el.setAttribute(key, val); - } - } else { - if (val == null || val === false) el.removeAttribute(key); - else if (val === true) el.setAttribute(key, ''); - else el.setAttribute(key, val); - } - } -}; - -const appendChildNode = (parent, child) => { - if (child == null) return; - if (isFunction(child)) { - const anchor = doc.createTextNode(''); - parent.appendChild(anchor); - let currentNodes = []; - const stop = Watch(() => { - const raw = child(); - if (raw?._isFor) { - raw._reconcile(parent); - currentNodes = raw(); - } else { - const next = (Array.isArray(raw) ? raw : [raw]) - .flat(Infinity) - .filter(v => v != null) - .map(v => isNode(v) ? v : doc.createTextNode(String(v))); - for (const n of currentNodes) { - if (!next.includes(n)) removeNode(n); - } - let ref = anchor; - for (let i = next.length - 1; i >= 0; i--) { - const n = next[i]; - if (n.parentNode !== parent) { - parent.insertBefore(n, ref); - } else if (n.nextSibling !== next[i + 1]) { - parent.insertBefore(n, ref); - } - if (n.componentContext) n.componentContext.mount.forEach(f => f()); - ref = n; - } - currentNodes = next; - } - }); - registerNodeCleanup(anchor, stop); - } else if (isNode(child)) { - parent.appendChild(child); - } else { - parent.appendChild(doc.createTextNode(String(child))); - } -}; - -const SVG_TAGS = /^(svg|path|circle|rect|line|polyline|polygon|g|defs|text|tspan|use|image|ellipse|foreignObject|linearGradient|radialGradient|stop|pattern|mask|clipPath|filter|feColorMatrix|feBlend|feGaussianBlur|animate|animateTransform|set|metadata|desc|title|symbol|marker|view)$/i; - -export const Tag = (tag, props = {}, ...children) => { - children = children.flat(Infinity); - if (isFunction(tag)) { - const ctx = { mount: [], unmount: [], provisions: {} }; - let rendered; - const stop = Watch(() => { - if (activeEffect) activeEffect.componentContext = ctx; - rendered = tag(props, { children, emit: (ev, ...args) => { - const h = props[`on${ev[0].toUpperCase()}${ev.slice(1)}`]; - if (isFunction(h)) h(...args); - }}); - if (activeEffect) activeEffect.componentContext = null; - }); - if (isNode(rendered)) { - rendered.componentContext = ctx; - rendered._stop = stop; - queueMicrotask(() => ctx.mount.forEach(fn => fn())); - } else if (Array.isArray(rendered)) { - rendered.forEach(node => { - if (isNode(node)) { - node.componentContext = ctx; - node._stop = stop; - } - }); - queueMicrotask(() => ctx.mount.forEach(fn => fn())); - } - return rendered; - } - const isSVG = SVG_TAGS.test(tag); - const el = isSVG ? doc.createElementNS('http://www.w3.org/2000/svg', tag) : doc.createElement(tag); - for (const [k, v] of Object.entries(props)) { - if (k.startsWith('on')) { - const ev = k.slice(2).toLowerCase(); - el.addEventListener(ev, v); - onUnmount(() => el.removeEventListener(ev, v)); - } else if (isFunction(v)) { - const stopAttr = Watch(() => setProperty(el, k, v(), isSVG)); - registerNodeCleanup(el, stopAttr); - if (/^(INPUT|TEXTAREA|SELECT)$/.test(el.tagName) && (k === 'value' || k === 'checked')) { - const evType = k === 'checked' ? 'change' : 'input'; - const handler = e => v(e.target[k]); - el.addEventListener(evType, handler); - onUnmount(() => el.removeEventListener(evType, handler)); - } - } else { - setProperty(el, k, v, isSVG); - } - } - for (const child of children) appendChildNode(el, child); - return el; -}; - -export const If = (cond, thenFn, elseFn = null, hooks = {}) => { - let lastResult = null; - let nodes = []; - let exitPromise = null; - return () => { - const condition = !!(isFunction(cond) ? cond() : cond); - if (condition === lastResult) return nodes.length === 1 ? nodes[0] : nodes; - if (nodes.length) { - if (hooks.leave) { - if (exitPromise && exitPromise.cancel) exitPromise.cancel(); - const anim = hooks.leave(nodes); - exitPromise = anim; - if (anim && anim.finished) { - anim.finished.then(() => nodes.forEach(n => removeNode(n))); - } else { - nodes.forEach(n => removeNode(n)); - } - } else { - nodes.forEach(n => removeNode(n)); - } - } - lastResult = condition; - const newNodes = condition ? thenFn() : elseFn?.() ?? []; - const newArr = (Array.isArray(newNodes) ? newNodes : [newNodes]).flat(Infinity).filter(v => v != null); - nodes = newArr; - if (nodes.length && hooks.enter) hooks.enter(nodes); - return nodes.length === 1 ? nodes[0] : nodes; - }; -}; - -export const For = ({ each, key, children }) => { - let cache = new Map(); - let order = []; - const reconcile = (parent) => { - const items = isFunction(each) ? each() : each || []; - const newMap = new Map(); - const newOrder = []; - const getKey = (item, i) => key ? (isFunction(key) ? key(item, i) : item[key]) : i; - for (let i = 0; i < items.length; i++) { - const item = items[i]; - const k = getKey(item, i); - let node = cache.get(k); - if (!node) node = Tag(children[0], { item, index: i }); - newMap.set(k, node); - newOrder.push(node); - cache.delete(k); - } - if (parent) { - let ref = null; - for (let i = newOrder.length - 1; i >= 0; i--) { - const node = newOrder[i]; - if (node.nextSibling !== newOrder[i + 1]) { - parent.insertBefore(node, ref); - } - ref = node; - } - } - for (const node of cache.values()) removeNode(node); - cache = newMap; - order = newOrder; - }; - const renderFn = () => order; - renderFn._isFor = true; - renderFn._reconcile = reconcile; - return renderFn; -}; - -export const Router = ({ routes }) => { - const outlet = Tag('div', { class: 'router-outlet' }); - const getHash = () => window.location.hash.slice(1) || '/'; - const path = $(getHash()); - const handler = () => { path(getHash()); }; - window.addEventListener('hashchange', handler); - registerNodeCleanup(outlet, () => window.removeEventListener('hashchange', handler)); - Watch(() => { - const current = path(); - const matched = routes.find(r => { - const rSeg = r.path.split('/').filter(Boolean); - const pSeg = current.split('/').filter(Boolean); - return rSeg.length === pSeg.length && rSeg.every((s, i) => s[0] === ':' || s === pSeg[i]); - }) || routes.find(r => r.path === '*'); - if (matched) { - while (outlet.firstChild) removeNode(outlet.firstChild); - const params = {}; - matched.path.split('/').filter(Boolean).forEach((s, i) => { - if (s[0] === ':') params[s.slice(1)] = current.split('/').filter(Boolean)[i]; - }); - Router.params(params); - const view = Tag(matched.component, { params }); - outlet.appendChild(view); - } - }); - return outlet; -}; - -Router.params = $({}); - -export const navigate = path => { window.location.hash = path; }; -export const currentPath = () => window.location.hash.slice(1) || '/'; - -export const createApp = (Root, rootProps = {}) => selector => { - const target = typeof selector === 'string' ? doc.querySelector(selector) : selector; - if (target.appUnmount) target.appUnmount(); - const app = Tag(Root, rootProps); - target.appendChild(app); - if (app.componentContext) app.componentContext.mount.forEach(f => f()); - target.appUnmount = () => removeNode(app); - return target.appUnmount; -}; - -'div span p a button input form label ul li ol header footer main section article nav aside h1 h2 h3 h4 h5 h6 img svg path circle rect line polyline polygon g defs text tspan use image ellipse foreignObject linearGradient radialGradient stop pattern mask clipPath filter feColorMatrix feBlend feGaussianBlur animate animateTransform set metadata desc title symbol marker view br hr pre code strong em table tr td th thead tbody tfoot select option textarea iframe video audio canvas' -.split(' ').forEach(tag => { - globalThis[tag[0].toUpperCase() + tag.slice(1)] = (props, ...children) => Tag(tag, props, ...children); -}); - -export default { $, set, Watch, watch, computed, untrack, Tag, If, For, Router, createApp, removeNode, navigate, currentPath, onMount, onUnmount }; \ No newline at end of file