From 482ff19adbcd7eff855d4180a61446db7f99a0c9 Mon Sep 17 00:00:00 2001 From: natxocc Date: Wed, 22 Apr 2026 21:39:07 +0200 Subject: [PATCH] Include fx, req, recover if in actual when --- dist/sigpro.esm.js | 141 ++++++++++++++++++++++++++---------- dist/sigpro.esm.min.js | 2 +- dist/sigpro.js | 141 ++++++++++++++++++++++++++---------- dist/sigpro.min.js | 2 +- docs/sigpro.js | 141 ++++++++++++++++++++++++++---------- sigpro.js | 157 ++++++++++++++++++++++++++++++----------- 6 files changed, 433 insertions(+), 151 deletions(-) diff --git a/dist/sigpro.esm.js b/dist/sigpro.esm.js index 5c27245..d6105a5 100644 --- a/dist/sigpro.esm.js +++ b/dist/sigpro.esm.js @@ -231,15 +231,24 @@ var watch = (sources, cb) => { effect(); return () => dispose(effect); }; -var cleanupNode = (node) => { +var cleanupNode = (node, skipLeave = false) => { + if (!node) + return; if (node._cleanups) { node._cleanups.forEach((fn) => fn()); node._cleanups.clear(); } if (node._ownerEffect) dispose(node._ownerEffect); + if (!skipLeave && node._sig_leave) { + return node._sig_leave(() => { + if (node.childNodes) + node.childNodes.forEach((n) => cleanupNode(n, true)); + node.remove(); + }); + } if (node.childNodes) - node.childNodes.forEach(cleanupNode); + node.childNodes.forEach((n) => cleanupNode(n, false)); }; var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on"); @@ -410,44 +419,61 @@ var render = (renderFn) => { destroy: () => { cleanups.forEach((fn) => fn()); cleanupNode(container); - container.remove(); + if (!container._sig_leave) + container.remove(); } }; }; -var when = (cond, render2, { enter, leave } = {}) => { - const wrap = h("div", { style: "display:contents" }); - let view = null; - const wait = (el, cb) => { - if (!el) - return cb(); - let done = false; - const finish = () => !done && (done = true, cb()); - el.addEventListener("transitionend", finish, { once: true }); - el.addEventListener("animationend", finish, { once: true }); - setTimeout(finish, 500); - }; - watch(cond, (on) => { - if (on && !view) { - const el = (view = render2(render2)).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 destroyView = () => (view.destroy(), view = null); - if (leave && el) { - el.classList.add(leave); - wait(el, destroyView); - } else { - destroyView(); - } +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; + } + const content = show ? SIP : NOP; + if (content) { + currentView = render(() => isFunc(content) ? content() : content); + root.insertBefore(currentView.container, anchor); } }); - return onUnmount(() => view?.destroy()), wrap; + onUnmount(() => currentView?.destroy()); + return root; +}; +var fx = ({ name, duration = 200, scale, slide, rotate, blur }, child) => { + const el = typeof child === "function" ? child() : child; + if (!(el instanceof Node)) + return el; + if (name) { + el.style.animation = `${name}-in ${duration}ms`; + el._sig_leave = (done) => { + el.style.animation = `${name}-out ${duration}ms`; + el.addEventListener("animationend", done, { once: true }); + }; + return el; + } + const hasTransform = scale || slide || rotate || blur; + el.style.transition = hasTransform ? `all ${duration}ms` : ""; + el.style.opacity = "0"; + if (scale) + el.style.transform = "scale(0.95)"; + if (slide) + el.style.transform = "translateY(-10px)"; + if (rotate) + el.style.transform = "rotate(-2deg)"; + if (blur) + el.style.filter = "blur(4px)"; + requestAnimationFrame(() => { + el.style.opacity = "1"; + el.style.transform = scale || slide || rotate || blur ? "" : "none"; + }); + el._sig_leave = (done) => { + el.style.opacity = "0"; + el.addEventListener("transitionend", done, { once: true }); + }; + return el; }; var each = (src, itemFn, keyFn) => { const anchor = doc.createTextNode(""); @@ -514,6 +540,47 @@ router.params = $({}); router.to = (p) => window.location.hash = p.replace(/^#?\/?/, "#/"); router.back = () => window.history.back(); router.path = () => window.location.hash.replace(/^#/, "") || "/"; +var req = ({ url, method = "GET", headers = {} }) => { + const loading = $(false); + const error = $(null); + const data = $(null); + let controller = null; + let timeoutId = null; + const run = async (body = null) => { + controller?.abort(); + clearTimeout(timeoutId); + controller = new AbortController; + timeoutId = setTimeout(() => controller.abort(), 1e4); + loading(true); + error(null); + try { + const isFormData = body instanceof FormData; + const res = await fetch(url, { + method, + headers: isFormData ? headers : { "Content-Type": "application/json", ...headers }, + body: isFormData ? body : body ? JSON.stringify(body) : undefined, + signal: controller.signal + }); + const text = await res.text(); + const json = text ? JSON.parse(text) : null; + if (!res.ok) + throw new Error(json?.message || res.statusText); + data(json); + return json; + } catch (e) { + if (e.name !== "AbortError") + error(e.message); + throw e; + } finally { + loading(false); + clearTimeout(timeoutId); + controller = null; + timeoutId = null; + } + }; + const abort = () => controller?.abort(); + return { run, abort, loading, error, data }; +}; var mount = (comp, target) => { const t = typeof target === "string" ? doc.querySelector(target) : target; if (!t) @@ -525,7 +592,7 @@ var mount = (comp, target) => { MOUNTED_NODES.set(t, inst); return inst; }; -var SigPro = Object.freeze({ $, $$, watch, h, when, each, router, mount, batch }); +var SigPro = Object.freeze({ $, $$, watch, h, when, each, fx, router, req, mount, batch }); if (typeof window !== "undefined") { Object.assign(window, SigPro); "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) => { @@ -536,8 +603,10 @@ export { when, watch, router, + req, mount, h, + fx, each, batch, $$, diff --git a/dist/sigpro.esm.min.js b/dist/sigpro.esm.min.js index 816de36..8f888fd 100644 --- a/dist/sigpro.esm.min.js +++ b/dist/sigpro.esm.min.js @@ -1 +1 @@ -var y=(e)=>typeof e==="function",j=(e)=>e&&typeof e==="object",b=Array.isArray,E=typeof document<"u"?document:null,M=(e)=>e?._isRuntime?e.container:e instanceof Node?e:E.createTextNode(e==null?"":String(e)),u=null,_=null,N=!1,O=0,A=new Set,k=new WeakMap,T=Symbol("iter"),L=new WeakMap,g=(e)=>{if(!e||e._disposed)return;e._disposed=!0;let s=[e];while(s.length){let n=s.pop();if(n._cleanups)n._cleanups.forEach((r)=>r()),n._cleanups.clear();if(n._children)n._children.forEach((r)=>s.push(r)),n._children.clear();if(n._deps)n._deps.forEach((r)=>r.delete(n)),n._deps.clear()}},v=(e)=>{if(_)(_._cleanups||=new Set).add(e)},W=(e)=>{let s=u;u=null;try{return e()}finally{u=s}},x=(e,s=!1)=>{let n=()=>{if(n._disposed)return;if(n._deps)n._deps.forEach((l)=>l.delete(n));if(n._cleanups)n._cleanups.forEach((l)=>l()),n._cleanups.clear();let r=u,o=_;u=_=n;try{return n._result=e()}catch(l){console.error("[SigPro]",l)}finally{u=r,_=o}};if(n._deps=n._cleanups=n._children=null,n._disposed=!1,n._isComputed=s,n._depth=u?u._depth+1:0,n._mounts=[],n._parent=_,_)(_._children||=new Set).add(n);return n},D=()=>{if(N)return;N=!0;let e=Array.from(A).sort((s,n)=>s._depth-n._depth);A.clear();for(let s of e)if(!s._disposed)s();N=!1},q=(e)=>{O++;try{return e()}finally{if(O--,O===0&&A.size>0&&!N)D()}},m=(e,s=!1)=>{if(!s&&u&&!u._disposed)e.add(u),(u._deps||=new Set).add(e);else if(s&&e.size>0){let n=!1;for(let r of e){if(r===u||r._disposed)continue;if(r._isComputed){if(r._dirty=!0,r._subs)m(r._subs,!0)}else A.add(r),n=!0}if(n&&!N&&O===0)queueMicrotask(D)}},P=(e,s=null)=>{let n=new Set;if(y(e)){let r,o=()=>{if(o._dirty){let l=u;u=o;try{let t=e();if(!Object.is(r,t))r=t,m(n,!0)}finally{u=l}o._dirty=!1}return m(n),r};if(o._isComputed=!0,o._subs=n,o._dirty=!0,o._deps=null,o._disposed=!1,o.stop=()=>{},_)v(o.stop);return 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));m(n,!0)}}return m(n),e}},I=(e)=>{if(!j(e))return e;let s=k.get(e);if(s)return s;let n=new Map,r=(l)=>{let t=n.get(l);if(!t)n.set(l,t=new Set);return t},o=new Proxy(e,{get(l,t,c){if(typeof t!=="symbol")m(r(t));return I(Reflect.get(l,t,c))},set(l,t,c,i){let a=Reflect.has(l,t),d=Reflect.get(l,t,i),f=Reflect.set(l,t,c,i);if(f&&!Object.is(d,c)){if(m(r(t),!0),!a)m(r(T),!0)}return f},deleteProperty(l,t){let c=Reflect.deleteProperty(l,t);if(c)m(r(t),!0),m(r(T),!0);return c},ownKeys(l){return m(r(T)),Reflect.ownKeys(l)}});return k.set(e,o),o},R=(e,s)=>{if(s===void 0){let r=x(e);return r(),()=>g(r)}let n=x(()=>{let r=Array.isArray(e)?e.map((o)=>o()):e();W(()=>s(r))});return n(),()=>g(n)},$=(e)=>{if(e._cleanups)e._cleanups.forEach((s)=>s()),e._cleanups.clear();if(e._ownerEffect)g(e._ownerEffect);if(e.childNodes)e.childNodes.forEach($)},z=/^\s*(javascript|data|vbscript):/i,K=(e)=>e==="src"||e==="href"||e.startsWith("on"),U=(e,s)=>{if(s==null||s===!1)return null;if(K(e)){let n=String(s);if(z.test(n))return console.warn(`[SigPro] Bloqueado protocolo peligroso en ${e}`),"#"}return s},C=(e,s={},n=[])=>{if(s instanceof Node||b(s)||!j(s))n=s,s={};if(y(e)){let t=x(()=>{let d=e(s,{children:n,emit:(f,...h)=>s[`on${f[0].toUpperCase()}${f.slice(1)}`]?.(...h)});return t._result=d,d});t();let c=t._result;if(c==null)return null;let i=c instanceof Node||b(c)&&c.every((d)=>d instanceof Node)?c:E.createTextNode(String(c)),a=(d)=>{if(j(d)&&!d._isRuntime)d._mounts=t._mounts||[],d._cleanups=t._cleanups||new Set,d._ownerEffect=t};return b(i)?i.forEach(a):a(i),i}let r=/^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(e),o=r?E.createElementNS("http://www.w3.org/2000/svg",e):E.createElement(e);o._cleanups=new Set;for(let t in s){if(!s.hasOwnProperty(t))continue;let c=s[t];if(t==="ref"){y(c)?c(o):c.current=o;continue}if(r&&t.startsWith("xlink:")){c==null?o.removeAttributeNS("http://www.w3.org/1999/xlink",t.slice(6)):o.setAttributeNS("http://www.w3.org/1999/xlink",t.slice(6),c);continue}if(t.startsWith("on")){let i=t.slice(2).toLowerCase();o.addEventListener(i,c);let a=()=>o.removeEventListener(i,c);o._cleanups.add(a),v(a)}else if(y(c)){let i=x(()=>{let a=U(t,c());if(t==="class")o.className=a||"";else if(a==null)o.removeAttribute(t);else if(t in o&&!r)o[t]=a;else o.setAttribute(t,a===!0?"":a)});if(i(),o._cleanups.add(()=>g(i)),v(()=>g(i)),/^(INPUT|TEXTAREA|SELECT)$/.test(o.tagName)&&(t==="value"||t==="checked")){let a=t==="checked"?"change":"input";o.addEventListener(a,(d)=>c(d.target[t]))}}else{let i=U(t,c);if(i!=null)if(t in o&&!r)o[t]=i;else o.setAttribute(t,i===!0?"":i)}}let l=(t)=>{if(b(t))return t.forEach(l);if(y(t)){let c=E.createTextNode("");o.appendChild(c);let i=[],a=x(()=>{let d=t(),f=(b(d)?d:[d]).map(M);i.forEach((p)=>{if(p._isRuntime)p.destroy();else $(p);if(p.parentNode)p.remove()});let h=c;for(let p=f.length-1;p>=0;p--){let w=f[p];if(w.parentNode!==h.parentNode)h.parentNode?.insertBefore(w,h);if(w._mounts)w._mounts.forEach((V)=>V());h=w}i=f});a(),o._cleanups.add(()=>g(a)),v(()=>g(a))}else{let c=M(t);if(o.appendChild(c),c._mounts)c._mounts.forEach((i)=>i())}};return l(n),o},B=(e)=>{let s=new Set,n=_,r=u,o=E.createElement("div");o.style.display="contents",o.setAttribute("role","presentation"),_={_cleanups:s},u=null;let l=(t)=>{if(!t)return;if(t._isRuntime)s.add(t.destroy),o.appendChild(t.container);else if(b(t))t.forEach(l);else o.appendChild(t instanceof Node?t:E.createTextNode(String(t==null?"":t)))};try{l(e({onCleanup:(t)=>s.add(t)}))}finally{_=n,u=r}return{_isRuntime:!0,container:o,destroy:()=>{s.forEach((t)=>t()),$(o),o.remove()}}},G=(e,s,{enter:n,leave:r}={})=>{let o=C("div",{style:"display:contents"}),l=null,t=(c,i)=>{if(!c)return i();let a=!1,d=()=>!a&&(a=!0,i());c.addEventListener("transitionend",d,{once:!0}),c.addEventListener("animationend",d,{once:!0}),setTimeout(d,500)};return R(e,(c)=>{if(c&&!l){let i=(l=s(s)).container.firstChild;if(o.appendChild(l.container),n&&i)i.classList.add(n),i.clientTop,i.classList.add(n+"-active"),t(i,()=>i.classList.remove(n,n+"-active"))}else if(!c&&l){let i=l.container.firstChild,a=()=>(l.destroy(),l=null);if(r&&i)i.classList.add(r),t(i,a);else a()}}),v(()=>l?.destroy()),o},J=(e,s,n)=>{let r=E.createTextNode(""),o=C("div",{style:"display:contents"},[r]),l=new Map;return R(()=>(y(e)?e():e)||[],(t)=>{let c=new Map,i=[],a=t||[];for(let f=0;fs(h,f));else l.delete(p);c.set(p,w),i.push(w)}l.forEach((f)=>f.destroy());let d=r;for(let f=i.length-1;f>=0;f--){let p=i[f].container;if(p.nextSibling!==d)o.insertBefore(p,d);d=p}l=c}),o},S=(e)=>{let s=()=>window.location.hash.slice(1)||"/",n=P(s()),r=()=>n(s());window.addEventListener("hashchange",r),v(()=>window.removeEventListener("hashchange",r));let o=C("div",{class:"router-hook"}),l=null;return R([n],()=>{let t=n(),c=e.find((i)=>{let a=i.path.split("/").filter(Boolean),d=t.split("/").filter(Boolean);return a.length===d.length&&a.every((f,h)=>f[0]===":"||f===d[h])})||e.find((i)=>i.path==="*");if(c){l?.destroy();let i={};c.path.split("/").filter(Boolean).forEach((a,d)=>{if(a[0]===":")i[a.slice(1)]=t.split("/").filter(Boolean)[d]}),S.params(i),l=B(()=>y(c.component)?c.component(i):c.component),o.replaceChildren(l.container)}}),o};S.params=P({});S.to=(e)=>window.location.hash=e.replace(/^#?\/?/,"#/");S.back=()=>window.history.back();S.path=()=>window.location.hash.replace(/^#/,"")||"/";var Q=(e,s)=>{let n=typeof s==="string"?E.querySelector(s):s;if(!n)return;if(L.has(n))L.get(n).destroy();let r=B(y(e)?e:()=>e);return n.replaceChildren(r.container),L.set(n,r),r},F=Object.freeze({$:P,$$:I,watch:R,h:C,when:G,each:J,router:S,mount:Q,batch:q});if(typeof window<"u")Object.assign(window,F),"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,n)=>C(e,s,n)});export{G as when,R as watch,S as router,Q as mount,C as h,J as each,q as batch,I as $$,P as $}; +var y=(e)=>typeof e==="function",B=(e)=>e&&typeof e==="object",b=Array.isArray,g=typeof document<"u"?document:null,k=(e)=>e?._isRuntime?e.container:e instanceof Node?e:g.createTextNode(e==null?"":String(e)),p=null,m=null,x=!1,A=0,C=new Set,D=new WeakMap,L=Symbol("iter"),P=new WeakMap,E=(e)=>{if(!e||e._disposed)return;e._disposed=!0;let r=[e];while(r.length){let n=r.pop();if(n._cleanups)n._cleanups.forEach((i)=>i()),n._cleanups.clear();if(n._children)n._children.forEach((i)=>r.push(i)),n._children.clear();if(n._deps)n._deps.forEach((i)=>i.delete(n)),n._deps.clear()}},v=(e)=>{if(m)(m._cleanups||=new Set).add(e)},V=(e)=>{let r=p;p=null;try{return e()}finally{p=r}},O=(e,r=!1)=>{let n=()=>{if(n._disposed)return;if(n._deps)n._deps.forEach((c)=>c.delete(n));if(n._cleanups)n._cleanups.forEach((c)=>c()),n._cleanups.clear();let i=p,o=m;p=m=n;try{return n._result=e()}catch(c){console.error("[SigPro]",c)}finally{p=i,m=o}};if(n._deps=n._cleanups=n._children=null,n._disposed=!1,n._isComputed=r,n._depth=p?p._depth+1:0,n._mounts=[],n._parent=m,m)(m._children||=new Set).add(n);return n},q=()=>{if(x)return;x=!0;let e=Array.from(C).sort((r,n)=>r._depth-n._depth);C.clear();for(let r of e)if(!r._disposed)r();x=!1},W=(e)=>{A++;try{return e()}finally{if(A--,A===0&&C.size>0&&!x)q()}},w=(e,r=!1)=>{if(!r&&p&&!p._disposed)e.add(p),(p._deps||=new Set).add(e);else if(r&&e.size>0){let n=!1;for(let i of e){if(i===p||i._disposed)continue;if(i._isComputed){if(i._dirty=!0,i._subs)w(i._subs,!0)}else C.add(i),n=!0}if(n&&!x&&A===0)queueMicrotask(q)}},N=(e,r=null)=>{let n=new Set;if(y(e)){let i,o=()=>{if(o._dirty){let c=p;p=o;try{let t=e();if(!Object.is(i,t))i=t,w(n,!0)}finally{p=c}o._dirty=!1}return w(n),i};if(o._isComputed=!0,o._subs=n,o._dirty=!0,o._deps=null,o._disposed=!1,o.stop=()=>{},m)v(o.stop);return o}if(r)try{e=JSON.parse(localStorage.getItem(r))??e}catch(i){}return(...i)=>{if(i.length){let o=y(i[0])?i[0](e):i[0];if(!Object.is(e,o)){if(e=o,r)localStorage.setItem(r,JSON.stringify(e));w(n,!0)}}return w(n),e}},I=(e)=>{if(!B(e))return e;let r=D.get(e);if(r)return r;let n=new Map,i=(c)=>{let t=n.get(c);if(!t)n.set(c,t=new Set);return t},o=new Proxy(e,{get(c,t,s){if(typeof t!=="symbol")w(i(t));return I(Reflect.get(c,t,s))},set(c,t,s,l){let a=Reflect.has(c,t),f=Reflect.get(c,t,l),u=Reflect.set(c,t,s,l);if(u&&!Object.is(f,s)){if(w(i(t),!0),!a)w(i(L),!0)}return u},deleteProperty(c,t){let s=Reflect.deleteProperty(c,t);if(s)w(i(t),!0),w(i(L),!0);return s},ownKeys(c){return w(i(L)),Reflect.ownKeys(c)}});return D.set(e,o),o},$=(e,r)=>{if(r===void 0){let i=O(e);return i(),()=>E(i)}let n=O(()=>{let i=Array.isArray(e)?e.map((o)=>o()):e();V(()=>r(i))});return n(),()=>E(n)},R=(e,r=!1)=>{if(!e)return;if(e._cleanups)e._cleanups.forEach((n)=>n()),e._cleanups.clear();if(e._ownerEffect)E(e._ownerEffect);if(!r&&e._sig_leave)return e._sig_leave(()=>{if(e.childNodes)e.childNodes.forEach((n)=>R(n,!0));e.remove()});if(e.childNodes)e.childNodes.forEach((n)=>R(n,!1))},F=/^\s*(javascript|data|vbscript):/i,J=(e)=>e==="src"||e==="href"||e.startsWith("on"),M=(e,r)=>{if(r==null||r===!1)return null;if(J(e)){let n=String(r);if(F.test(n))return console.warn(`[SigPro] Bloqueado protocolo peligroso en ${e}`),"#"}return r},T=(e,r={},n=[])=>{if(r instanceof Node||b(r)||!B(r))n=r,r={};if(y(e)){let t=O(()=>{let f=e(r,{children:n,emit:(u,...h)=>r[`on${u[0].toUpperCase()}${u.slice(1)}`]?.(...h)});return t._result=f,f});t();let s=t._result;if(s==null)return null;let l=s instanceof Node||b(s)&&s.every((f)=>f instanceof Node)?s:g.createTextNode(String(s)),a=(f)=>{if(B(f)&&!f._isRuntime)f._mounts=t._mounts||[],f._cleanups=t._cleanups||new Set,f._ownerEffect=t};return b(l)?l.forEach(a):a(l),l}let i=/^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(e),o=i?g.createElementNS("http://www.w3.org/2000/svg",e):g.createElement(e);o._cleanups=new Set;for(let t in r){if(!r.hasOwnProperty(t))continue;let s=r[t];if(t==="ref"){y(s)?s(o):s.current=o;continue}if(i&&t.startsWith("xlink:")){s==null?o.removeAttributeNS("http://www.w3.org/1999/xlink",t.slice(6)):o.setAttributeNS("http://www.w3.org/1999/xlink",t.slice(6),s);continue}if(t.startsWith("on")){let l=t.slice(2).toLowerCase();o.addEventListener(l,s);let a=()=>o.removeEventListener(l,s);o._cleanups.add(a),v(a)}else if(y(s)){let l=O(()=>{let a=M(t,s());if(t==="class")o.className=a||"";else if(a==null)o.removeAttribute(t);else if(t in o&&!i)o[t]=a;else o.setAttribute(t,a===!0?"":a)});if(l(),o._cleanups.add(()=>E(l)),v(()=>E(l)),/^(INPUT|TEXTAREA|SELECT)$/.test(o.tagName)&&(t==="value"||t==="checked")){let a=t==="checked"?"change":"input";o.addEventListener(a,(f)=>s(f.target[t]))}}else{let l=M(t,s);if(l!=null)if(t in o&&!i)o[t]=l;else o.setAttribute(t,l===!0?"":l)}}let c=(t)=>{if(b(t))return t.forEach(c);if(y(t)){let s=g.createTextNode("");o.appendChild(s);let l=[],a=O(()=>{let f=t(),u=(b(f)?f:[f]).map(k);l.forEach((d)=>{if(d._isRuntime)d.destroy();else R(d);if(d.parentNode)d.remove()});let h=s;for(let d=u.length-1;d>=0;d--){let _=u[d];if(_.parentNode!==h.parentNode)h.parentNode?.insertBefore(_,h);if(_._mounts)_._mounts.forEach((U)=>U());h=_}l=u});a(),o._cleanups.add(()=>E(a)),v(()=>E(a))}else{let s=k(t);if(o.appendChild(s),s._mounts)s._mounts.forEach((l)=>l())}};return c(n),o},j=(e)=>{let r=new Set,n=m,i=p,o=g.createElement("div");o.style.display="contents",o.setAttribute("role","presentation"),m={_cleanups:r},p=null;let c=(t)=>{if(!t)return;if(t._isRuntime)r.add(t.destroy),o.appendChild(t.container);else if(b(t))t.forEach(c);else o.appendChild(t instanceof Node?t:g.createTextNode(String(t==null?"":t)))};try{c(e({onCleanup:(t)=>r.add(t)}))}finally{m=n,p=i}return{_isRuntime:!0,container:o,destroy:()=>{if(r.forEach((t)=>t()),R(o),!o._sig_leave)o.remove()}}},z=(e,r,n=null)=>{let i=g.createTextNode(""),o=T("div",{style:"display:contents"},[i]),c=null;return $(()=>!!(y(e)?e():e),(t)=>{if(c)c.destroy(),c=null;let s=t?r:n;if(s)c=j(()=>y(s)?s():s),o.insertBefore(c.container,i)}),v(()=>c?.destroy()),o},G=({name:e,duration:r=200,scale:n,slide:i,rotate:o,blur:c},t)=>{let s=typeof t==="function"?t():t;if(!(s instanceof Node))return s;if(e)return s.style.animation=`${e}-in ${r}ms`,s._sig_leave=(a)=>{s.style.animation=`${e}-out ${r}ms`,s.addEventListener("animationend",a,{once:!0})},s;let l=n||i||o||c;if(s.style.transition=l?`all ${r}ms`:"",s.style.opacity="0",n)s.style.transform="scale(0.95)";if(i)s.style.transform="translateY(-10px)";if(o)s.style.transform="rotate(-2deg)";if(c)s.style.filter="blur(4px)";return requestAnimationFrame(()=>{s.style.opacity="1",s.style.transform=n||i||o||c?"":"none"}),s._sig_leave=(a)=>{s.style.opacity="0",s.addEventListener("transitionend",a,{once:!0})},s},K=(e,r,n)=>{let i=g.createTextNode(""),o=T("div",{style:"display:contents"},[i]),c=new Map;return $(()=>(y(e)?e():e)||[],(t)=>{let s=new Map,l=[],a=t||[];for(let u=0;ur(h,u));else c.delete(d);s.set(d,_),l.push(_)}c.forEach((u)=>u.destroy());let f=i;for(let u=l.length-1;u>=0;u--){let d=l[u].container;if(d.nextSibling!==f)o.insertBefore(d,f);f=d}c=s}),o},S=(e)=>{let r=()=>window.location.hash.slice(1)||"/",n=N(r()),i=()=>n(r());window.addEventListener("hashchange",i),v(()=>window.removeEventListener("hashchange",i));let o=T("div",{class:"router-hook"}),c=null;return $([n],()=>{let t=n(),s=e.find((l)=>{let a=l.path.split("/").filter(Boolean),f=t.split("/").filter(Boolean);return a.length===f.length&&a.every((u,h)=>u[0]===":"||u===f[h])})||e.find((l)=>l.path==="*");if(s){c?.destroy();let l={};s.path.split("/").filter(Boolean).forEach((a,f)=>{if(a[0]===":")l[a.slice(1)]=t.split("/").filter(Boolean)[f]}),S.params(l),c=j(()=>y(s.component)?s.component(l):s.component),o.replaceChildren(c.container)}}),o};S.params=N({});S.to=(e)=>window.location.hash=e.replace(/^#?\/?/,"#/");S.back=()=>window.history.back();S.path=()=>window.location.hash.replace(/^#/,"")||"/";var Q=({url:e,method:r="GET",headers:n={}})=>{let i=N(!1),o=N(null),c=N(null),t=null,s=null;return{run:async(f=null)=>{t?.abort(),clearTimeout(s),t=new AbortController,s=setTimeout(()=>t.abort(),1e4),i(!0),o(null);try{let u=f instanceof FormData,h=await fetch(e,{method:r,headers:u?n:{"Content-Type":"application/json",...n},body:u?f:f?JSON.stringify(f):void 0,signal:t.signal}),d=await h.text(),_=d?JSON.parse(d):null;if(!h.ok)throw Error(_?.message||h.statusText);return c(_),_}catch(u){if(u.name!=="AbortError")o(u.message);throw u}finally{i(!1),clearTimeout(s),t=null,s=null}},abort:()=>t?.abort(),loading:i,error:o,data:c}},H=(e,r)=>{let n=typeof r==="string"?g.querySelector(r):r;if(!n)return;if(P.has(n))P.get(n).destroy();let i=j(y(e)?e:()=>e);return n.replaceChildren(i.container),P.set(n,i),i},X=Object.freeze({$:N,$$:I,watch:$,h:T,when:z,each:K,fx:G,router:S,req:Q,mount:H,batch:W});if(typeof window<"u")Object.assign(window,X),"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)=>T(e,r,n)});export{z as when,$ as watch,S as router,Q as req,H as mount,T as h,G as fx,K as each,W as batch,I as $$,N as $}; diff --git a/dist/sigpro.js b/dist/sigpro.js index e33e608..5dcca77 100644 --- a/dist/sigpro.js +++ b/dist/sigpro.js @@ -43,8 +43,10 @@ when: () => when, watch: () => watch, router: () => router, + req: () => req, mount: () => mount, h: () => h, + fx: () => fx, each: () => each, batch: () => batch, $$: () => $$, @@ -284,15 +286,24 @@ effect(); return () => dispose(effect); }; - var cleanupNode = (node) => { + var cleanupNode = (node, skipLeave = false) => { + if (!node) + return; if (node._cleanups) { node._cleanups.forEach((fn) => fn()); node._cleanups.clear(); } if (node._ownerEffect) dispose(node._ownerEffect); + if (!skipLeave && node._sig_leave) { + return node._sig_leave(() => { + if (node.childNodes) + node.childNodes.forEach((n) => cleanupNode(n, true)); + node.remove(); + }); + } if (node.childNodes) - node.childNodes.forEach(cleanupNode); + node.childNodes.forEach((n) => cleanupNode(n, false)); }; var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on"); @@ -463,44 +474,61 @@ destroy: () => { cleanups.forEach((fn) => fn()); cleanupNode(container); - container.remove(); + if (!container._sig_leave) + container.remove(); } }; }; - var when = (cond, render2, { enter, leave } = {}) => { - const wrap = h("div", { style: "display:contents" }); - let view = null; - const wait = (el, cb) => { - if (!el) - return cb(); - let done = false; - const finish = () => !done && (done = true, cb()); - el.addEventListener("transitionend", finish, { once: true }); - el.addEventListener("animationend", finish, { once: true }); - setTimeout(finish, 500); - }; - watch(cond, (on) => { - if (on && !view) { - const el = (view = render2(render2)).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 destroyView = () => (view.destroy(), view = null); - if (leave && el) { - el.classList.add(leave); - wait(el, destroyView); - } else { - destroyView(); - } + 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; + } + const content = show ? SIP : NOP; + if (content) { + currentView = render(() => isFunc(content) ? content() : content); + root.insertBefore(currentView.container, anchor); } }); - return onUnmount(() => view?.destroy()), wrap; + onUnmount(() => currentView?.destroy()); + return root; + }; + var fx = ({ name, duration = 200, scale, slide, rotate, blur }, child) => { + const el = typeof child === "function" ? child() : child; + if (!(el instanceof Node)) + return el; + if (name) { + el.style.animation = `${name}-in ${duration}ms`; + el._sig_leave = (done) => { + el.style.animation = `${name}-out ${duration}ms`; + el.addEventListener("animationend", done, { once: true }); + }; + return el; + } + const hasTransform = scale || slide || rotate || blur; + el.style.transition = hasTransform ? `all ${duration}ms` : ""; + el.style.opacity = "0"; + if (scale) + el.style.transform = "scale(0.95)"; + if (slide) + el.style.transform = "translateY(-10px)"; + if (rotate) + el.style.transform = "rotate(-2deg)"; + if (blur) + el.style.filter = "blur(4px)"; + requestAnimationFrame(() => { + el.style.opacity = "1"; + el.style.transform = scale || slide || rotate || blur ? "" : "none"; + }); + el._sig_leave = (done) => { + el.style.opacity = "0"; + el.addEventListener("transitionend", done, { once: true }); + }; + return el; }; var each = (src, itemFn, keyFn) => { const anchor = doc.createTextNode(""); @@ -567,6 +595,47 @@ router.to = (p) => window.location.hash = p.replace(/^#?\/?/, "#/"); router.back = () => window.history.back(); router.path = () => window.location.hash.replace(/^#/, "") || "/"; + var req = ({ url, method = "GET", headers = {} }) => { + const loading = $(false); + const error = $(null); + const data = $(null); + let controller = null; + let timeoutId = null; + const run = async (body = null) => { + controller?.abort(); + clearTimeout(timeoutId); + controller = new AbortController; + timeoutId = setTimeout(() => controller.abort(), 1e4); + loading(true); + error(null); + try { + const isFormData = body instanceof FormData; + const res = await fetch(url, { + method, + headers: isFormData ? headers : { "Content-Type": "application/json", ...headers }, + body: isFormData ? body : body ? JSON.stringify(body) : undefined, + signal: controller.signal + }); + const text = await res.text(); + const json = text ? JSON.parse(text) : null; + if (!res.ok) + throw new Error(json?.message || res.statusText); + data(json); + return json; + } catch (e) { + if (e.name !== "AbortError") + error(e.message); + throw e; + } finally { + loading(false); + clearTimeout(timeoutId); + controller = null; + timeoutId = null; + } + }; + const abort = () => controller?.abort(); + return { run, abort, loading, error, data }; + }; var mount = (comp, target) => { const t = typeof target === "string" ? doc.querySelector(target) : target; if (!t) @@ -578,7 +647,7 @@ MOUNTED_NODES.set(t, inst); return inst; }; - var SigPro = Object.freeze({ $, $$, watch, h, when, each, router, mount, batch }); + var SigPro = Object.freeze({ $, $$, watch, h, when, each, fx, router, req, mount, batch }); if (typeof window !== "undefined") { Object.assign(window, SigPro); "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) => { diff --git a/dist/sigpro.min.js b/dist/sigpro.min.js index 392df87..f7d3f70 100644 --- a/dist/sigpro.min.js +++ b/dist/sigpro.min.js @@ -1 +1 @@ -(()=>{var{defineProperty:L,getOwnPropertyNames:Q,getOwnPropertyDescriptor:F}=Object,H=Object.prototype.hasOwnProperty;function X(e){return this[e]}var Y=(e)=>{var n=(U??=new WeakMap).get(e),t;if(n)return n;if(n=L({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function"){for(var o of Q(e))if(!H.call(n,o))L(n,o,{get:X.bind(e,o),enumerable:!(t=F(e,o))||t.enumerable})}return U.set(e,n),n},U;var Z=(e)=>e;function ee(e,n){this[e]=Z.bind(null,n)}var te=(e,n)=>{for(var t in n)L(e,t,{get:n[t],enumerable:!0,configurable:!0,set:ee.bind(n,t)})};var ie={};te(ie,{when:()=>z,watch:()=>O,router:()=>b,mount:()=>G,h:()=>N,each:()=>K,batch:()=>q,$$:()=>B,$:()=>T});var y=(e)=>typeof e==="function",$=(e)=>e&&typeof e==="object",v=Array.isArray,E=typeof document<"u"?document:null,D=(e)=>e?._isRuntime?e.container:e instanceof Node?e:E.createTextNode(e==null?"":String(e)),u=null,_=null,x=!1,A=0,R=new Set,I=new WeakMap,j=Symbol("iter"),P=new WeakMap,g=(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()}},S=(e)=>{if(_)(_._cleanups||=new Set).add(e)},ne=(e)=>{let n=u;u=null;try{return e()}finally{u=n}},C=(e,n=!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 o=u,r=_;u=_=t;try{return t._result=e()}catch(l){console.error("[SigPro]",l)}finally{u=o,_=r}};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=_,_)(_._children||=new Set).add(t);return t},W=()=>{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},q=(e)=>{A++;try{return e()}finally{if(A--,A===0&&R.size>0&&!x)W()}},m=(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 o of e){if(o===u||o._disposed)continue;if(o._isComputed){if(o._dirty=!0,o._subs)m(o._subs,!0)}else R.add(o),t=!0}if(t&&!x&&A===0)queueMicrotask(W)}},T=(e,n=null)=>{let t=new Set;if(y(e)){let o,r=()=>{if(r._dirty){let l=u;u=r;try{let s=e();if(!Object.is(o,s))o=s,m(t,!0)}finally{u=l}r._dirty=!1}return m(t),o};if(r._isComputed=!0,r._subs=t,r._dirty=!0,r._deps=null,r._disposed=!1,r.stop=()=>{},_)S(r.stop);return r}if(n)try{e=JSON.parse(localStorage.getItem(n))??e}catch(o){}return(...o)=>{if(o.length){let r=y(o[0])?o[0](e):o[0];if(!Object.is(e,r)){if(e=r,n)localStorage.setItem(n,JSON.stringify(e));m(t,!0)}}return m(t),e}},B=(e)=>{if(!$(e))return e;let n=I.get(e);if(n)return n;let t=new Map,o=(l)=>{let s=t.get(l);if(!s)t.set(l,s=new Set);return s},r=new Proxy(e,{get(l,s,c){if(typeof s!=="symbol")m(o(s));return B(Reflect.get(l,s,c))},set(l,s,c,i){let a=Reflect.has(l,s),d=Reflect.get(l,s,i),f=Reflect.set(l,s,c,i);if(f&&!Object.is(d,c)){if(m(o(s),!0),!a)m(o(j),!0)}return f},deleteProperty(l,s){let c=Reflect.deleteProperty(l,s);if(c)m(o(s),!0),m(o(j),!0);return c},ownKeys(l){return m(o(j)),Reflect.ownKeys(l)}});return I.set(e,r),r},O=(e,n)=>{if(n===void 0){let o=C(e);return o(),()=>g(o)}let t=C(()=>{let o=Array.isArray(e)?e.map((r)=>r()):e();ne(()=>n(o))});return t(),()=>g(t)},M=(e)=>{if(e._cleanups)e._cleanups.forEach((n)=>n()),e._cleanups.clear();if(e._ownerEffect)g(e._ownerEffect);if(e.childNodes)e.childNodes.forEach(M)},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},N=(e,n={},t=[])=>{if(n instanceof Node||v(n)||!$(n))t=n,n={};if(y(e)){let s=C(()=>{let d=e(n,{children:t,emit:(f,...h)=>n[`on${f[0].toUpperCase()}${f.slice(1)}`]?.(...h)});return s._result=d,d});s();let c=s._result;if(c==null)return null;let i=c instanceof Node||v(c)&&c.every((d)=>d instanceof Node)?c:E.createTextNode(String(c)),a=(d)=>{if($(d)&&!d._isRuntime)d._mounts=s._mounts||[],d._cleanups=s._cleanups||new Set,d._ownerEffect=s};return v(i)?i.forEach(a):a(i),i}let o=/^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(e),r=o?E.createElementNS("http://www.w3.org/2000/svg",e):E.createElement(e);r._cleanups=new Set;for(let s in n){if(!n.hasOwnProperty(s))continue;let c=n[s];if(s==="ref"){y(c)?c(r):c.current=r;continue}if(o&&s.startsWith("xlink:")){c==null?r.removeAttributeNS("http://www.w3.org/1999/xlink",s.slice(6)):r.setAttributeNS("http://www.w3.org/1999/xlink",s.slice(6),c);continue}if(s.startsWith("on")){let i=s.slice(2).toLowerCase();r.addEventListener(i,c);let a=()=>r.removeEventListener(i,c);r._cleanups.add(a),S(a)}else if(y(c)){let i=C(()=>{let a=V(s,c());if(s==="class")r.className=a||"";else if(a==null)r.removeAttribute(s);else if(s in r&&!o)r[s]=a;else r.setAttribute(s,a===!0?"":a)});if(i(),r._cleanups.add(()=>g(i)),S(()=>g(i)),/^(INPUT|TEXTAREA|SELECT)$/.test(r.tagName)&&(s==="value"||s==="checked")){let a=s==="checked"?"change":"input";r.addEventListener(a,(d)=>c(d.target[s]))}}else{let i=V(s,c);if(i!=null)if(s in r&&!o)r[s]=i;else r.setAttribute(s,i===!0?"":i)}}let l=(s)=>{if(v(s))return s.forEach(l);if(y(s)){let c=E.createTextNode("");r.appendChild(c);let i=[],a=C(()=>{let d=s(),f=(v(d)?d:[d]).map(D);i.forEach((p)=>{if(p._isRuntime)p.destroy();else M(p);if(p.parentNode)p.remove()});let h=c;for(let p=f.length-1;p>=0;p--){let w=f[p];if(w.parentNode!==h.parentNode)h.parentNode?.insertBefore(w,h);if(w._mounts)w._mounts.forEach((J)=>J());h=w}i=f});a(),r._cleanups.add(()=>g(a)),S(()=>g(a))}else{let c=D(s);if(r.appendChild(c),c._mounts)c._mounts.forEach((i)=>i())}};return l(t),r},k=(e)=>{let n=new Set,t=_,o=u,r=E.createElement("div");r.style.display="contents",r.setAttribute("role","presentation"),_={_cleanups:n},u=null;let l=(s)=>{if(!s)return;if(s._isRuntime)n.add(s.destroy),r.appendChild(s.container);else if(v(s))s.forEach(l);else r.appendChild(s instanceof Node?s:E.createTextNode(String(s==null?"":s)))};try{l(e({onCleanup:(s)=>n.add(s)}))}finally{_=t,u=o}return{_isRuntime:!0,container:r,destroy:()=>{n.forEach((s)=>s()),M(r),r.remove()}}},z=(e,n,{enter:t,leave:o}={})=>{let r=N("div",{style:"display:contents"}),l=null,s=(c,i)=>{if(!c)return i();let a=!1,d=()=>!a&&(a=!0,i());c.addEventListener("transitionend",d,{once:!0}),c.addEventListener("animationend",d,{once:!0}),setTimeout(d,500)};return O(e,(c)=>{if(c&&!l){let i=(l=n(n)).container.firstChild;if(r.appendChild(l.container),t&&i)i.classList.add(t),i.clientTop,i.classList.add(t+"-active"),s(i,()=>i.classList.remove(t,t+"-active"))}else if(!c&&l){let i=l.container.firstChild,a=()=>(l.destroy(),l=null);if(o&&i)i.classList.add(o),s(i,a);else a()}}),S(()=>l?.destroy()),r},K=(e,n,t)=>{let o=E.createTextNode(""),r=N("div",{style:"display:contents"},[o]),l=new Map;return O(()=>(y(e)?e():e)||[],(s)=>{let c=new Map,i=[],a=s||[];for(let f=0;fn(h,f));else l.delete(p);c.set(p,w),i.push(w)}l.forEach((f)=>f.destroy());let d=o;for(let f=i.length-1;f>=0;f--){let p=i[f].container;if(p.nextSibling!==d)r.insertBefore(p,d);d=p}l=c}),r},b=(e)=>{let n=()=>window.location.hash.slice(1)||"/",t=T(n()),o=()=>t(n());window.addEventListener("hashchange",o),S(()=>window.removeEventListener("hashchange",o));let r=N("div",{class:"router-hook"}),l=null;return O([t],()=>{let s=t(),c=e.find((i)=>{let a=i.path.split("/").filter(Boolean),d=s.split("/").filter(Boolean);return a.length===d.length&&a.every((f,h)=>f[0]===":"||f===d[h])})||e.find((i)=>i.path==="*");if(c){l?.destroy();let i={};c.path.split("/").filter(Boolean).forEach((a,d)=>{if(a[0]===":")i[a.slice(1)]=s.split("/").filter(Boolean)[d]}),b.params(i),l=k(()=>y(c.component)?c.component(i):c.component),r.replaceChildren(l.container)}}),r};b.params=T({});b.to=(e)=>window.location.hash=e.replace(/^#?\/?/,"#/");b.back=()=>window.history.back();b.path=()=>window.location.hash.replace(/^#/,"")||"/";var G=(e,n)=>{let t=typeof n==="string"?E.querySelector(n):n;if(!t)return;if(P.has(t))P.get(t).destroy();let o=k(y(e)?e:()=>e);return t.replaceChildren(o.container),P.set(t,o),o},re=Object.freeze({$:T,$$:B,watch:O,h:N,when:z,each:K,router:b,mount:G,batch:q});if(typeof window<"u")Object.assign(window,re),"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)=>N(e,n,t)});})(); +(()=>{var{defineProperty:L,getOwnPropertyNames:H,getOwnPropertyDescriptor:X}=Object,Y=Object.prototype.hasOwnProperty;function Z(e){return this[e]}var ee=(e)=>{var s=(M??=new WeakMap).get(e),t;if(s)return s;if(s=L({},"__esModule",{value:!0}),e&&typeof e==="object"||typeof e==="function"){for(var i of H(e))if(!Y.call(s,i))L(s,i,{get:Z.bind(e,i),enumerable:!(t=X(e,i))||t.enumerable})}return M.set(e,s),s},M;var te=(e)=>e;function ne(e,s){this[e]=te.bind(null,s)}var se=(e,s)=>{for(var t in s)L(e,t,{get:s[t],enumerable:!0,configurable:!0,set:ne.bind(s,t)})};var le={};se(le,{when:()=>F,watch:()=>A,router:()=>v,req:()=>G,mount:()=>K,h:()=>x,fx:()=>J,each:()=>z,batch:()=>W,$$:()=>D,$:()=>b});var y=(e)=>typeof e==="function",k=(e)=>e&&typeof e==="object",N=Array.isArray,g=typeof document<"u"?document:null,q=(e)=>e?._isRuntime?e.container:e instanceof Node?e:g.createTextNode(e==null?"":String(e)),p=null,m=null,O=!1,C=0,R=new Set,I=new WeakMap,P=Symbol("iter"),B=new WeakMap,E=(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((i)=>i()),t._cleanups.clear();if(t._children)t._children.forEach((i)=>s.push(i)),t._children.clear();if(t._deps)t._deps.forEach((i)=>i.delete(t)),t._deps.clear()}},S=(e)=>{if(m)(m._cleanups||=new Set).add(e)},oe=(e)=>{let s=p;p=null;try{return e()}finally{p=s}},T=(e,s=!1)=>{let t=()=>{if(t._disposed)return;if(t._deps)t._deps.forEach((c)=>c.delete(t));if(t._cleanups)t._cleanups.forEach((c)=>c()),t._cleanups.clear();let i=p,r=m;p=m=t;try{return t._result=e()}catch(c){console.error("[SigPro]",c)}finally{p=i,m=r}};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=m,m)(m._children||=new Set).add(t);return t},V=()=>{if(O)return;O=!0;let e=Array.from(R).sort((s,t)=>s._depth-t._depth);R.clear();for(let s of e)if(!s._disposed)s();O=!1},W=(e)=>{C++;try{return e()}finally{if(C--,C===0&&R.size>0&&!O)V()}},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 i of e){if(i===p||i._disposed)continue;if(i._isComputed){if(i._dirty=!0,i._subs)w(i._subs,!0)}else R.add(i),t=!0}if(t&&!O&&C===0)queueMicrotask(V)}},b=(e,s=null)=>{let t=new Set;if(y(e)){let i,r=()=>{if(r._dirty){let c=p;p=r;try{let n=e();if(!Object.is(i,n))i=n,w(t,!0)}finally{p=c}r._dirty=!1}return w(t),i};if(r._isComputed=!0,r._subs=t,r._dirty=!0,r._deps=null,r._disposed=!1,r.stop=()=>{},m)S(r.stop);return r}if(s)try{e=JSON.parse(localStorage.getItem(s))??e}catch(i){}return(...i)=>{if(i.length){let r=y(i[0])?i[0](e):i[0];if(!Object.is(e,r)){if(e=r,s)localStorage.setItem(s,JSON.stringify(e));w(t,!0)}}return w(t),e}},D=(e)=>{if(!k(e))return e;let s=I.get(e);if(s)return s;let t=new Map,i=(c)=>{let n=t.get(c);if(!n)t.set(c,n=new Set);return n},r=new Proxy(e,{get(c,n,o){if(typeof n!=="symbol")w(i(n));return D(Reflect.get(c,n,o))},set(c,n,o,l){let a=Reflect.has(c,n),f=Reflect.get(c,n,l),u=Reflect.set(c,n,o,l);if(u&&!Object.is(f,o)){if(w(i(n),!0),!a)w(i(P),!0)}return u},deleteProperty(c,n){let o=Reflect.deleteProperty(c,n);if(o)w(i(n),!0),w(i(P),!0);return o},ownKeys(c){return w(i(P)),Reflect.ownKeys(c)}});return I.set(e,r),r},A=(e,s)=>{if(s===void 0){let i=T(e);return i(),()=>E(i)}let t=T(()=>{let i=Array.isArray(e)?e.map((r)=>r()):e();oe(()=>s(i))});return t(),()=>E(t)},$=(e,s=!1)=>{if(!e)return;if(e._cleanups)e._cleanups.forEach((t)=>t()),e._cleanups.clear();if(e._ownerEffect)E(e._ownerEffect);if(!s&&e._sig_leave)return e._sig_leave(()=>{if(e.childNodes)e.childNodes.forEach((t)=>$(t,!0));e.remove()});if(e.childNodes)e.childNodes.forEach((t)=>$(t,!1))},re=/^\s*(javascript|data|vbscript):/i,ie=(e)=>e==="src"||e==="href"||e.startsWith("on"),U=(e,s)=>{if(s==null||s===!1)return null;if(ie(e)){let t=String(s);if(re.test(t))return console.warn(`[SigPro] Bloqueado protocolo peligroso en ${e}`),"#"}return s},x=(e,s={},t=[])=>{if(s instanceof Node||N(s)||!k(s))t=s,s={};if(y(e)){let n=T(()=>{let f=e(s,{children:t,emit:(u,...h)=>s[`on${u[0].toUpperCase()}${u.slice(1)}`]?.(...h)});return n._result=f,f});n();let o=n._result;if(o==null)return null;let l=o instanceof Node||N(o)&&o.every((f)=>f instanceof Node)?o:g.createTextNode(String(o)),a=(f)=>{if(k(f)&&!f._isRuntime)f._mounts=n._mounts||[],f._cleanups=n._cleanups||new Set,f._ownerEffect=n};return N(l)?l.forEach(a):a(l),l}let i=/^(svg|path|circle|rect|line|poly(line|gon)|g|defs|text(path)?|tspan|use|symbol|image|marker|ellipse)$/i.test(e),r=i?g.createElementNS("http://www.w3.org/2000/svg",e):g.createElement(e);r._cleanups=new Set;for(let n in s){if(!s.hasOwnProperty(n))continue;let o=s[n];if(n==="ref"){y(o)?o(r):o.current=r;continue}if(i&&n.startsWith("xlink:")){o==null?r.removeAttributeNS("http://www.w3.org/1999/xlink",n.slice(6)):r.setAttributeNS("http://www.w3.org/1999/xlink",n.slice(6),o);continue}if(n.startsWith("on")){let l=n.slice(2).toLowerCase();r.addEventListener(l,o);let a=()=>r.removeEventListener(l,o);r._cleanups.add(a),S(a)}else if(y(o)){let l=T(()=>{let a=U(n,o());if(n==="class")r.className=a||"";else if(a==null)r.removeAttribute(n);else if(n in r&&!i)r[n]=a;else r.setAttribute(n,a===!0?"":a)});if(l(),r._cleanups.add(()=>E(l)),S(()=>E(l)),/^(INPUT|TEXTAREA|SELECT)$/.test(r.tagName)&&(n==="value"||n==="checked")){let a=n==="checked"?"change":"input";r.addEventListener(a,(f)=>o(f.target[n]))}}else{let l=U(n,o);if(l!=null)if(n in r&&!i)r[n]=l;else r.setAttribute(n,l===!0?"":l)}}let c=(n)=>{if(N(n))return n.forEach(c);if(y(n)){let o=g.createTextNode("");r.appendChild(o);let l=[],a=T(()=>{let f=n(),u=(N(f)?f:[f]).map(q);l.forEach((d)=>{if(d._isRuntime)d.destroy();else $(d);if(d.parentNode)d.remove()});let h=o;for(let d=u.length-1;d>=0;d--){let _=u[d];if(_.parentNode!==h.parentNode)h.parentNode?.insertBefore(_,h);if(_._mounts)_._mounts.forEach((Q)=>Q());h=_}l=u});a(),r._cleanups.add(()=>E(a)),S(()=>E(a))}else{let o=q(n);if(r.appendChild(o),o._mounts)o._mounts.forEach((l)=>l())}};return c(t),r},j=(e)=>{let s=new Set,t=m,i=p,r=g.createElement("div");r.style.display="contents",r.setAttribute("role","presentation"),m={_cleanups:s},p=null;let c=(n)=>{if(!n)return;if(n._isRuntime)s.add(n.destroy),r.appendChild(n.container);else if(N(n))n.forEach(c);else r.appendChild(n instanceof Node?n:g.createTextNode(String(n==null?"":n)))};try{c(e({onCleanup:(n)=>s.add(n)}))}finally{m=t,p=i}return{_isRuntime:!0,container:r,destroy:()=>{if(s.forEach((n)=>n()),$(r),!r._sig_leave)r.remove()}}},F=(e,s,t=null)=>{let i=g.createTextNode(""),r=x("div",{style:"display:contents"},[i]),c=null;return A(()=>!!(y(e)?e():e),(n)=>{if(c)c.destroy(),c=null;let o=n?s:t;if(o)c=j(()=>y(o)?o():o),r.insertBefore(c.container,i)}),S(()=>c?.destroy()),r},J=({name:e,duration:s=200,scale:t,slide:i,rotate:r,blur:c},n)=>{let o=typeof n==="function"?n():n;if(!(o instanceof Node))return o;if(e)return o.style.animation=`${e}-in ${s}ms`,o._sig_leave=(a)=>{o.style.animation=`${e}-out ${s}ms`,o.addEventListener("animationend",a,{once:!0})},o;let l=t||i||r||c;if(o.style.transition=l?`all ${s}ms`:"",o.style.opacity="0",t)o.style.transform="scale(0.95)";if(i)o.style.transform="translateY(-10px)";if(r)o.style.transform="rotate(-2deg)";if(c)o.style.filter="blur(4px)";return requestAnimationFrame(()=>{o.style.opacity="1",o.style.transform=t||i||r||c?"":"none"}),o._sig_leave=(a)=>{o.style.opacity="0",o.addEventListener("transitionend",a,{once:!0})},o},z=(e,s,t)=>{let i=g.createTextNode(""),r=x("div",{style:"display:contents"},[i]),c=new Map;return A(()=>(y(e)?e():e)||[],(n)=>{let o=new Map,l=[],a=n||[];for(let u=0;us(h,u));else c.delete(d);o.set(d,_),l.push(_)}c.forEach((u)=>u.destroy());let f=i;for(let u=l.length-1;u>=0;u--){let d=l[u].container;if(d.nextSibling!==f)r.insertBefore(d,f);f=d}c=o}),r},v=(e)=>{let s=()=>window.location.hash.slice(1)||"/",t=b(s()),i=()=>t(s());window.addEventListener("hashchange",i),S(()=>window.removeEventListener("hashchange",i));let r=x("div",{class:"router-hook"}),c=null;return A([t],()=>{let n=t(),o=e.find((l)=>{let a=l.path.split("/").filter(Boolean),f=n.split("/").filter(Boolean);return a.length===f.length&&a.every((u,h)=>u[0]===":"||u===f[h])})||e.find((l)=>l.path==="*");if(o){c?.destroy();let l={};o.path.split("/").filter(Boolean).forEach((a,f)=>{if(a[0]===":")l[a.slice(1)]=n.split("/").filter(Boolean)[f]}),v.params(l),c=j(()=>y(o.component)?o.component(l):o.component),r.replaceChildren(c.container)}}),r};v.params=b({});v.to=(e)=>window.location.hash=e.replace(/^#?\/?/,"#/");v.back=()=>window.history.back();v.path=()=>window.location.hash.replace(/^#/,"")||"/";var G=({url:e,method:s="GET",headers:t={}})=>{let i=b(!1),r=b(null),c=b(null),n=null,o=null;return{run:async(f=null)=>{n?.abort(),clearTimeout(o),n=new AbortController,o=setTimeout(()=>n.abort(),1e4),i(!0),r(null);try{let u=f instanceof FormData,h=await fetch(e,{method:s,headers:u?t:{"Content-Type":"application/json",...t},body:u?f:f?JSON.stringify(f):void 0,signal:n.signal}),d=await h.text(),_=d?JSON.parse(d):null;if(!h.ok)throw Error(_?.message||h.statusText);return c(_),_}catch(u){if(u.name!=="AbortError")r(u.message);throw u}finally{i(!1),clearTimeout(o),n=null,o=null}},abort:()=>n?.abort(),loading:i,error:r,data:c}},K=(e,s)=>{let t=typeof s==="string"?g.querySelector(s):s;if(!t)return;if(B.has(t))B.get(t).destroy();let i=j(y(e)?e:()=>e);return t.replaceChildren(i.container),B.set(t,i),i},ce=Object.freeze({$:b,$$:D,watch:A,h:x,when:F,each:z,fx:J,router:v,req:G,mount:K,batch:W});if(typeof window<"u")Object.assign(window,ce),"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)=>x(e,s,t)});})(); diff --git a/docs/sigpro.js b/docs/sigpro.js index e33e608..5dcca77 100644 --- a/docs/sigpro.js +++ b/docs/sigpro.js @@ -43,8 +43,10 @@ when: () => when, watch: () => watch, router: () => router, + req: () => req, mount: () => mount, h: () => h, + fx: () => fx, each: () => each, batch: () => batch, $$: () => $$, @@ -284,15 +286,24 @@ effect(); return () => dispose(effect); }; - var cleanupNode = (node) => { + var cleanupNode = (node, skipLeave = false) => { + if (!node) + return; if (node._cleanups) { node._cleanups.forEach((fn) => fn()); node._cleanups.clear(); } if (node._ownerEffect) dispose(node._ownerEffect); + if (!skipLeave && node._sig_leave) { + return node._sig_leave(() => { + if (node.childNodes) + node.childNodes.forEach((n) => cleanupNode(n, true)); + node.remove(); + }); + } if (node.childNodes) - node.childNodes.forEach(cleanupNode); + node.childNodes.forEach((n) => cleanupNode(n, false)); }; var DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i; var isDangerousAttr = (key) => key === "src" || key === "href" || key.startsWith("on"); @@ -463,44 +474,61 @@ destroy: () => { cleanups.forEach((fn) => fn()); cleanupNode(container); - container.remove(); + if (!container._sig_leave) + container.remove(); } }; }; - var when = (cond, render2, { enter, leave } = {}) => { - const wrap = h("div", { style: "display:contents" }); - let view = null; - const wait = (el, cb) => { - if (!el) - return cb(); - let done = false; - const finish = () => !done && (done = true, cb()); - el.addEventListener("transitionend", finish, { once: true }); - el.addEventListener("animationend", finish, { once: true }); - setTimeout(finish, 500); - }; - watch(cond, (on) => { - if (on && !view) { - const el = (view = render2(render2)).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 destroyView = () => (view.destroy(), view = null); - if (leave && el) { - el.classList.add(leave); - wait(el, destroyView); - } else { - destroyView(); - } + 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; + } + const content = show ? SIP : NOP; + if (content) { + currentView = render(() => isFunc(content) ? content() : content); + root.insertBefore(currentView.container, anchor); } }); - return onUnmount(() => view?.destroy()), wrap; + onUnmount(() => currentView?.destroy()); + return root; + }; + var fx = ({ name, duration = 200, scale, slide, rotate, blur }, child) => { + const el = typeof child === "function" ? child() : child; + if (!(el instanceof Node)) + return el; + if (name) { + el.style.animation = `${name}-in ${duration}ms`; + el._sig_leave = (done) => { + el.style.animation = `${name}-out ${duration}ms`; + el.addEventListener("animationend", done, { once: true }); + }; + return el; + } + const hasTransform = scale || slide || rotate || blur; + el.style.transition = hasTransform ? `all ${duration}ms` : ""; + el.style.opacity = "0"; + if (scale) + el.style.transform = "scale(0.95)"; + if (slide) + el.style.transform = "translateY(-10px)"; + if (rotate) + el.style.transform = "rotate(-2deg)"; + if (blur) + el.style.filter = "blur(4px)"; + requestAnimationFrame(() => { + el.style.opacity = "1"; + el.style.transform = scale || slide || rotate || blur ? "" : "none"; + }); + el._sig_leave = (done) => { + el.style.opacity = "0"; + el.addEventListener("transitionend", done, { once: true }); + }; + return el; }; var each = (src, itemFn, keyFn) => { const anchor = doc.createTextNode(""); @@ -567,6 +595,47 @@ router.to = (p) => window.location.hash = p.replace(/^#?\/?/, "#/"); router.back = () => window.history.back(); router.path = () => window.location.hash.replace(/^#/, "") || "/"; + var req = ({ url, method = "GET", headers = {} }) => { + const loading = $(false); + const error = $(null); + const data = $(null); + let controller = null; + let timeoutId = null; + const run = async (body = null) => { + controller?.abort(); + clearTimeout(timeoutId); + controller = new AbortController; + timeoutId = setTimeout(() => controller.abort(), 1e4); + loading(true); + error(null); + try { + const isFormData = body instanceof FormData; + const res = await fetch(url, { + method, + headers: isFormData ? headers : { "Content-Type": "application/json", ...headers }, + body: isFormData ? body : body ? JSON.stringify(body) : undefined, + signal: controller.signal + }); + const text = await res.text(); + const json = text ? JSON.parse(text) : null; + if (!res.ok) + throw new Error(json?.message || res.statusText); + data(json); + return json; + } catch (e) { + if (e.name !== "AbortError") + error(e.message); + throw e; + } finally { + loading(false); + clearTimeout(timeoutId); + controller = null; + timeoutId = null; + } + }; + const abort = () => controller?.abort(); + return { run, abort, loading, error, data }; + }; var mount = (comp, target) => { const t = typeof target === "string" ? doc.querySelector(target) : target; if (!t) @@ -578,7 +647,7 @@ MOUNTED_NODES.set(t, inst); return inst; }; - var SigPro = Object.freeze({ $, $$, watch, h, when, each, router, mount, batch }); + var SigPro = Object.freeze({ $, $$, watch, h, when, each, fx, router, req, mount, batch }); if (typeof window !== "undefined") { Object.assign(window, SigPro); "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) => { diff --git a/sigpro.js b/sigpro.js index c47f945..badc138 100644 --- a/sigpro.js +++ b/sigpro.js @@ -1,4 +1,4 @@ -// sigpro 1.2.15 +// sigpro 1.2.16 const isFunc = f => typeof f === "function" const isObj = o => o && typeof o === "object" const isArr = Array.isArray @@ -222,14 +222,21 @@ const watch = (sources, cb) => { return () => dispose(effect) } -const cleanupNode = node => { +const cleanupNode = (node, skipLeave = false) => { + if (!node) return; if (node._cleanups) { - node._cleanups.forEach(fn => fn()) - node._cleanups.clear() + node._cleanups.forEach(fn => fn()); + node._cleanups.clear(); } - if (node._ownerEffect) dispose(node._ownerEffect) - if (node.childNodes) node.childNodes.forEach(cleanupNode) -} + if (node._ownerEffect) dispose(node._ownerEffect); + if (!skipLeave && node._sig_leave) { + return node._sig_leave(() => { + if (node.childNodes) node.childNodes.forEach(n => cleanupNode(n, true)); + node.remove(); + }); + } + if (node.childNodes) node.childNodes.forEach(n => cleanupNode(n, false)); +}; const DANGEROUS_PROTOCOL = /^\s*(javascript|data|vbscript):/i const isDangerousAttr = key => key === 'src' || key === 'href' || key.startsWith('on') @@ -251,7 +258,7 @@ const h = (tag, props = {}, children = []) => { children = props props = {} } - + if (isFunc(tag)) { const effect = createEffect(() => { const result = tag(props, { @@ -398,48 +405,70 @@ const render = renderFn => { destroy: () => { cleanups.forEach(fn => fn()) cleanupNode(container) - container.remove() + if (!container._sig_leave) container.remove() } } } -const when = (cond, render, { enter, leave } = {}) => { - const wrap = h('div', { style: 'display:contents' }) - let view = null +const when = (cond, SIP, NOP = null) => { + const anchor = doc.createTextNode("") + const root = h("div", { style: "display:contents" }, [anchor]) + let currentView = null - const wait = (el, cb) => { - if (!el) return cb() - let done = false - const finish = () => !done && (done = true, cb()) - el.addEventListener('transitionend', finish, { once: true }) - el.addEventListener('animationend', finish, { once: true }) - setTimeout(finish, 500) - } - - watch(cond, 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')) + watch( + () => !!(isFunc(cond) ? cond() : cond), + show => { + if (currentView) { + currentView.destroy() + currentView = null } - } else if (!on && view) { - const el = view.container.firstChild - const destroyView = () => (view.destroy(), view = null) - if (leave && el) { - el.classList.add(leave) - wait(el, destroyView) - } else { - destroyView() + + const content = show ? SIP : NOP + if (content) { + currentView = render(() => isFunc(content) ? content() : content) + root.insertBefore(currentView.container, anchor) } } - }) + ) - return onUnmount(() => view?.destroy()), wrap + onUnmount(() => currentView?.destroy()) + return root } +const fx = ({ name, duration = 200, scale, slide, rotate, blur }, child) => { + const el = typeof child === 'function' ? child() : child; + if (!(el instanceof Node)) return el; + + if (name) { + el.style.animation = `${name}-in ${duration}ms`; + el._sig_leave = (done) => { + el.style.animation = `${name}-out ${duration}ms`; + el.addEventListener('animationend', done, { once: true }); + }; + return el; + } + + const hasTransform = scale || slide || rotate || blur; + el.style.transition = hasTransform ? `all ${duration}ms` : ''; + el.style.opacity = '0'; + if (scale) el.style.transform = 'scale(0.95)'; + if (slide) el.style.transform = 'translateY(-10px)'; + if (rotate) el.style.transform = 'rotate(-2deg)'; + if (blur) el.style.filter = 'blur(4px)'; + + requestAnimationFrame(() => { + el.style.opacity = '1'; + el.style.transform = scale || slide || rotate || blur ? '' : 'none'; + }); + + el._sig_leave = (done) => { + el.style.opacity = '0'; + el.addEventListener('transitionend', done, { once: true }); + }; + + return el; +}; + const each = (src, itemFn, keyFn) => { const anchor = doc.createTextNode("") const root = h("div", { style: "display:contents" }, [anchor]) @@ -503,6 +532,52 @@ router.to = p => window.location.hash = p.replace(/^#?\/?/, "#/") router.back = () => window.history.back() router.path = () => window.location.hash.replace(/^#/, "") || "/" +const req = ({ url, method = 'GET', headers = {} }) => { + const loading = $(false); + const error = $(null); + const data = $(null); + let controller = null; + let timeoutId = null; + + const run = async (body = null) => { + controller?.abort(); + clearTimeout(timeoutId); + controller = new AbortController(); + timeoutId = setTimeout(() => controller.abort(), 10000); + loading(true); + error(null); + + try { + const isFormData = body instanceof FormData; + const res = await fetch(url, { + method, + headers: isFormData ? headers : { 'Content-Type': 'application/json', ...headers }, + body: isFormData ? body : (body ? JSON.stringify(body) : undefined), + signal: controller.signal + }); + + const text = await res.text(); + const json = text ? JSON.parse(text) : null; + + if (!res.ok) throw new Error(json?.message || res.statusText); + data(json); + return json; + } catch (e) { + if (e.name !== 'AbortError') error(e.message); + throw e; + } finally { + loading(false); + clearTimeout(timeoutId); + controller = null; + timeoutId = null; + } + }; + + const abort = () => controller?.abort(); + + return { run, abort, loading, error, data }; +}; + const mount = (comp, target) => { const t = typeof target === "string" ? doc.querySelector(target) : target if (!t) return @@ -513,7 +588,7 @@ const mount = (comp, target) => { return inst } -const SigPro = Object.freeze({ $, $$, watch, h, when, each, router, mount, batch }) +const SigPro = Object.freeze({ $, $$, watch, h, when, each, fx, router, req, mount, batch }) if (typeof window !== "undefined") { Object.assign(window, SigPro) @@ -523,4 +598,4 @@ if (typeof window !== "undefined") { }) } -export { $, $$, watch, h, when, each, router, mount, batch } \ No newline at end of file +export { $, $$, watch, h, when, each, fx, router, req, mount, batch } \ No newline at end of file