diff --git a/dist/sigpro.db.js b/dist/sigpro.db.js new file mode 100644 index 0000000..49008cb --- /dev/null +++ b/dist/sigpro.db.js @@ -0,0 +1 @@ +var o=async(e,n={},t=null)=>{if(t)t(!0);try{let r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),credentials:"include"});if(!r.ok){let s=await r.text();throw Error(`Error ${r.status}: ${s}`)}return await r.json()}finally{if(t)t(!1)}};export{o as db}; diff --git a/dist/sigpro.js b/dist/sigpro.js index 6ba93cb..cf8976d 100644 --- a/dist/sigpro.js +++ b/dist/sigpro.js @@ -1 +1 @@ -var m=(e)=>typeof e=="function",q=(e)=>e&&typeof e=="object",g=Array.isArray,N=typeof document<"u"?document:null,w=(e)=>N.createTextNode(e==null?"":String(e)),G=(e)=>e?._rt?e._cnt:e instanceof Node?e:w(e),B=(e)=>e.children,A=(e)=>m(e)?e():e,d=null,p=null,E=0,M=0,C=new Set,L=new WeakMap,D="http://www.w3.org/2000/svg",I="http://www.w3.org/1999/xlink",R=new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")),z=new Set(["src","href","formaction","action","background","code","archive"]),k=(e)=>{if(e)e.forEach((l)=>l()),e.clear()},b=(e)=>{if(!e||e._x)return;e._x=1;let l=[e],r;while(r=l.pop()){if(k(r._c),r._ch)r._ch.forEach((o)=>l.push(o)),r._ch.clear();if(r._d)r._d.forEach((o)=>o.delete(r)),r._d.clear()}},S=(e)=>p&&(p._c||=new Set).add(e),J=(e)=>{let l=d;d=null;try{return e()}finally{d=l}},v=(e,l=0)=>{let r=()=>{if(r._x)return;if(r._d)r._d.forEach((a)=>a.delete(r));k(r._c);let o=d,n=p;d=p=r;try{return r._res=e()}catch(a){console.error("[SigPro]",a)}finally{d=o,p=n}};if(r._d=r._c=r._ch=null,r._x=0,r._iC=l,r._dp=d?d._dp+1:0,r._m=[],r._p=p,p)(p._ch||=new Set).add(r);return r},W=()=>{if(E)return;E=1;let e=[...C].sort((l,r)=>l._dp-r._dp);C.clear();for(let l of e)if(!l._x)l();E=0},V=(e)=>{M++;try{return e()}finally{if(!--M&&C.size&&!E)W()}},x=(e,l=0)=>{if(!l&&d&&!d._x)e.add(d),(d._d||=new Set).add(e);else if(l&&e.size){let r=0;for(let o of e){if(o===d||o._x)continue;if(o._iC){if(o._dt=1,o._sb)x(o._sb,1)}else C.add(o),r=1}if(r&&!E&&!M)queueMicrotask(W)}},X=(e,l=null)=>{let r=new Set;if(m(e)){let o,n=()=>{if(n._dt){let a=d;d=n;try{let t=e();if(!Object.is(o,t))o=t,x(r,1)}finally{d=a}n._dt=0}return x(r),o};return n._iC=n._dt=1,n._sb=r,n._d=null,n._x=0,n}if(l)try{e=JSON.parse(localStorage.getItem(l))??e}catch(o){}return(...o)=>{if(o.length){let n=m(o[0])?o[0](e):o[0];if(!Object.is(e,n)){if(e=n,l)localStorage.setItem(l,JSON.stringify(e));x(r,1)}}return x(r),e}},P=(e,l)=>{let r=v(l?()=>{let o=g(e)?e.map((n)=>n()):e();J(()=>l(o))}:e);return r(),()=>b(r)},U=(e)=>{if(!e)return;if(k(e._c),e._oE)b(e._oE);if(e.childNodes)e.childNodes.forEach(U)},j=(e,l)=>l==null||l===!1?null:(z.has(e)||e.startsWith("on"))&&/^\s*(javascript|data|vbscript):/i.test(String(l))?"#":l,O=(e,l={},r=[])=>{if(l instanceof Node||g(l)||!q(l))r=l,l={};if(m(e)){let t=v(()=>t._res=e(l,{children:r,emit:(i,...f)=>l[`on${i[0].toUpperCase()}${i.slice(1)}`]?.(...f)}));if(t(),t._res==null)return null;let s=t._res instanceof Node||g(t._res)&&t._res.every((i)=>i instanceof Node)?t._res:w(t._res),c=(i)=>{if(q(i)&&!i._rt)i._m=t._m||[],i._c=t._c||new Set,i._oE=t};return g(s)?s.forEach(c):c(s),s}let o=R.has(e),n=o?N.createElementNS(D,e):N.createElement(e);n._c=new Set;for(let t in l){let s=l[t];if(t==="ref"){m(s)?s(n):s.current=n;continue}if(o&&t.startsWith("xlink:")){let c=j(t.slice(6),s);c==null?n.removeAttributeNS(I,t.slice(6)):n.setAttributeNS(I,t.slice(6),c);continue}if(t.startsWith("on")){let c=t.slice(2).toLowerCase();n.addEventListener(c,s);let i=()=>n.removeEventListener(c,s);n._c.add(i),S(i)}else if(m(s)){let c=v(()=>{let i=j(t,s());if(t==="class")n.className=i||"";else if(i==null)n.removeAttribute(t);else if(t==="style"&&typeof i=="string")n.setAttribute("style",i);else if(t in n&&!o)n[t]=i;else n.setAttribute(t,i===!0?"":i)});if(c(),n._c.add(()=>b(c)),S(()=>b(c)),/^(INPUT|TEXTAREA|SELECT)$/.test(n.tagName)&&(t==="value"||t==="checked"))n.addEventListener(t==="checked"?"change":"input",(i)=>s(i.target[t]))}else{let c=j(t,s);if(c!=null)if(t==="style"&&typeof c=="string")n.setAttribute("style",c);else if(t in n&&!o)n[t]=c;else n.setAttribute(t,c===!0?"":c)}}let a=(t)=>{if(g(t))return t.forEach(a);if(m(t)){let s=w(""),c=[];n.appendChild(s);let i=v(()=>{let f=t(),h=(g(f)?f:[f]).map(G),y=s;c.forEach((u)=>{if(u._rt?u.destroy():U(u),u.parentNode)u.remove()});for(let u=h.length-1;u>=0;u--){let _=h[u];if(_.parentNode!==y.parentNode)y.parentNode?.insertBefore(_,y);if(_._m)_._m.forEach(($)=>$());y=_}c=h});i(),n._c.add(()=>b(i)),S(()=>b(i))}else{let s=G(t);if(n.appendChild(s),s._m)s._m.forEach((c)=>c())}};return a(r),n},T=(e)=>{let l=new Set,r=p,o=d,n=N.createElement("div");n.style.display="contents",n.setAttribute("role","presentation"),p={_c:l},d=null;let a=(t)=>{if(!t)return;if(t._rt)l.add(t.destroy),n.appendChild(t._cnt);else if(g(t))t.forEach(a);else n.appendChild(t instanceof Node?t:w(t))};try{a(e({onCleanup:(t)=>l.add(t)}))}finally{p=r,d=o}return{_rt:1,_cnt:n,destroy:()=>{k(l),U(n),n.remove()}}},F=(e,l,r=null)=>{let o=w(""),n=O("div",{style:"display:contents"},[o]),a;return P(()=>!!A(e),(t)=>{if(a)a.destroy(),a=null;let s=t?l:r;if(s)a=T(()=>A(s)),n.insertBefore(a._cnt,o)}),S(()=>a?.destroy()),n},K=(e,l,r)=>{let o=w(""),n=O("div",{style:"display:contents"},[o]),a=new Map;return P(()=>A(e)||[],(t)=>{let s=new Map,c=[];for(let f=0,h=(t||[]).length;fl(y,f));else a.delete(u);s.set(u,_),c.push(_)}a.forEach((f)=>f.destroy());let i=o;for(let f=c.length-1;f>=0;f--){let h=c[f]._cnt;if(h.nextSibling!==i)n.insertBefore(h,i);i=h}a=s}),n},Q=(e,l)=>{let r=typeof l=="string"?N.querySelector(l):l;if(!r)return;if(L.has(r))L.get(r).destroy();let o=T(m(e)?e:()=>e);return r.replaceChildren(o._cnt),L.set(r,o),o},H="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",Y={$:X,watch:P,batch:V,h:O,fragment:B,render:T,mount:Q,when:F,each:K,onUnmount:S,val:A,isA:g,isF:m,isO:q};if(typeof window<"u")window.SigPro=Y,H.split(" ").forEach((e)=>{window[e]=(l,r)=>O(e,l,r)});export{F as when,P as watch,A as val,T as render,S as onUnmount,Q as mount,q as isO,m as isF,g as isA,O as h,B as fragment,K as each,V as batch,Y as SigPro,X as $}; +var q=(j)=>typeof j=="function",u=(j)=>j&&typeof j=="object",R=Array.isArray,m=typeof document<"u"?document:null,C=(j)=>m.createTextNode(j==null?"":String(j)),g=(j)=>j?._rt?j._cnt:j instanceof Node?j:C(j),k=(j)=>j.children,S=(j)=>q(j)?j():j,I=null,V=null,Q=0,w=0,b=new Set,A=new WeakMap,l="http://www.w3.org/2000/svg",h="http://www.w3.org/1999/xlink",i=new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")),v=new Set(["src","href","formaction","action","background","code","archive"]),x=(j)=>{if(j)j.forEach((H)=>H()),j.clear()},_=(j)=>{if(!j||j._x)return;j._x=1;let H=[j],z;while(z=H.pop()){if(x(z._c),z._ch)z._ch.forEach((J)=>H.push(J)),z._ch.clear();if(z._d)z._d.forEach((J)=>J.delete(z)),z._d.clear()}},T=(j)=>V&&(V._c||=new Set).add(j),p=(j)=>{let H=I;I=null;try{return j()}finally{I=H}},D=(j,H=0)=>{let z=()=>{if(z._x)return;if(z._d)z._d.forEach(($)=>$.delete(z));x(z._c);let J=I,B=V;I=V=z;try{return z._res=j()}catch($){console.error("[SigPro]",$)}finally{I=J,V=B}};if(z._d=z._c=z._ch=null,z._x=0,z._iC=H,z._dp=I?I._dp+1:0,z._m=[],z._p=V,V)(V._ch||=new Set).add(z);return z},o=()=>{if(Q)return;Q=1;let j=[...b].sort((H,z)=>H._dp-z._dp);b.clear();for(let H of j)if(!H._x)H();Q=0},f=(j)=>{w++;try{return j()}finally{if(!--w&&b.size&&!Q)o()}},G=(j,H=0)=>{if(!H&&I&&!I._x)j.add(I),(I._d||=new Set).add(j);else if(H&&j.size){let z=0;for(let J of j){if(J===I||J._x)continue;if(J._iC){if(J._dt=1,J._sb)G(J._sb,1)}else b.add(J),z=1}if(z&&!Q&&!w)queueMicrotask(o)}},a=(j,H=null)=>{let z=new Set;if(q(j)){let J,B=()=>{if(B._dt){let $=I;I=B;try{let y=j();if(!Object.is(J,y))J=y,G(z,1)}finally{I=$}B._dt=0}return G(z),J};return B._iC=B._dt=1,B._sb=z,B._d=null,B._x=0,B}if(H)try{j=JSON.parse(localStorage.getItem(H))??j}catch(J){}return(...J)=>{if(J.length){let B=q(J[0])?J[0](j):J[0];if(!Object.is(j,B)){if(j=B,H)localStorage.setItem(H,JSON.stringify(j));G(z,1)}}return G(z),j}},F=(j,H)=>{let z=D(H?()=>{let J=R(j)?j.map((B)=>B()):j();p(()=>H(J))}:j);return z(),()=>_(z)},O=(j)=>{if(!j)return;if(x(j._c),j._oE)_(j._oE);if(j.childNodes)j.childNodes.forEach(O)},N=(j,H)=>H==null||H===!1?null:(v.has(j)||j.startsWith("on"))&&/^\s*(javascript|data|vbscript):/i.test(String(H))?"#":H,E=(j,H={},z=[])=>{if(H instanceof Node||R(H)||!u(H))z=H,H={};if(q(j)){let y=D(()=>y._res=j(H,{children:z,emit:(W,...K)=>H[`on${W[0].toUpperCase()}${W.slice(1)}`]?.(...K)}));if(y(),y._res==null)return null;let Z=y._res instanceof Node||R(y._res)&&y._res.every((W)=>W instanceof Node)?y._res:C(y._res),P=(W)=>{if(u(W)&&!W._rt)W._m=y._m||[],W._c=y._c||new Set,W._oE=y};return R(Z)?Z.forEach(P):P(Z),Z}let J=i.has(j),B=J?m.createElementNS(l,j):m.createElement(j);B._c=new Set;for(let y in H){let Z=H[y];if(y==="ref"){q(Z)?Z(B):Z.current=B;continue}if(J&&y.startsWith("xlink:")){let P=N(y.slice(6),Z);P==null?B.removeAttributeNS(h,y.slice(6)):B.setAttributeNS(h,y.slice(6),P);continue}if(y.startsWith("on")){let P=y.slice(2).toLowerCase();B.addEventListener(P,Z);let W=()=>B.removeEventListener(P,Z);B._c.add(W),T(W)}else if(q(Z)){let P=D(()=>{let W=N(y,Z());if(y==="class")B.className=W||"";else if(W==null)B.removeAttribute(y);else if(y==="style"&&typeof W=="string")B.setAttribute("style",W);else if(y in B&&!J)B[y]=W;else B.setAttribute(y,W===!0?"":W)});if(P(),B._c.add(()=>_(P)),T(()=>_(P)),/^(INPUT|TEXTAREA|SELECT)$/.test(B.tagName)&&(y==="value"||y==="checked"))B.addEventListener(y==="checked"?"change":"input",(W)=>Z(W.target[y]))}else{let P=N(y,Z);if(P!=null)if(y==="style"&&typeof P=="string")B.setAttribute("style",P);else if(y in B&&!J)B[y]=P;else B.setAttribute(y,P===!0?"":P)}}let $=(y)=>{if(R(y))return y.forEach($);if(q(y)){let Z=C(""),P=[];B.appendChild(Z);let W=D(()=>{let K=y(),Y=(R(K)?K:[K]).map(g),M=Z;P.forEach((L)=>{if(L._rt?L.destroy():O(L),L.parentNode)L.remove()});for(let L=Y.length-1;L>=0;L--){let X=Y[L];if(X.parentNode!==M.parentNode)M.parentNode?.insertBefore(X,M);if(X._m)X._m.forEach((d)=>d());M=X}P=Y});W(),B._c.add(()=>_(W)),T(()=>_(W))}else{let Z=g(y);if(B.appendChild(Z),Z._m)Z._m.forEach((P)=>P())}};return $(z),B},U=(j)=>{let H=new Set,z=V,J=I,B=m.createElement("div");B.style.display="contents",B.setAttribute("role","presentation"),V={_c:H},I=null;let $=(y)=>{if(!y)return;if(y._rt)H.add(y.destroy),B.appendChild(y._cnt);else if(R(y))y.forEach($);else B.appendChild(y instanceof Node?y:C(y))};try{$(j({onCleanup:(y)=>H.add(y)}))}finally{V=z,I=J}return{_rt:1,_cnt:B,destroy:()=>{x(H),O(B),B.remove()}}},s=(j,H,z=null)=>{let J=C(""),B=E("div",{style:"display:contents"},[J]),$;return F(()=>!!S(j),(y)=>{if($)$.destroy(),$=null;let Z=y?H:z;if(Z)$=U(()=>S(Z)),B.insertBefore($._cnt,J)}),T(()=>$?.destroy()),B},r=(j,H,z)=>{let J=C(""),B=E("div",{style:"display:contents"},[J]),$=new Map;return F(()=>S(j)||[],(y)=>{let Z=new Map,P=[];for(let K=0,Y=(y||[]).length;KH(M,K));else $.delete(L);Z.set(L,X),P.push(X)}$.forEach((K)=>K.destroy());let W=J;for(let K=P.length-1;K>=0;K--){let Y=P[K]._cnt;if(Y.nextSibling!==W)B.insertBefore(Y,W);W=Y}$=Z}),B},t=(j,H)=>{let z=typeof H=="string"?m.querySelector(H):H;if(!z)return;if(A.has(z))A.get(z).destroy();let J=U(q(j)?j:()=>j);return z.replaceChildren(J._cnt),A.set(z,J),J},c={$:a,watch:F,batch:f,h:E,fragment:k,render:U,mount:t,when:s,each:r,onUnmount:T,val:S,isA:R,isF:q,isO:u};if(typeof window<"u")window.SigPro=c;export{s as when,F as watch,S as val,U as render,T as onUnmount,t as mount,u as isO,q as isF,R as isA,E as h,k as fragment,r as each,f as batch,c as SigPro,a as $}; diff --git a/dist/sigpro.locale.js b/dist/sigpro.locale.js new file mode 100644 index 0000000..ed8b9f2 --- /dev/null +++ b/dist/sigpro.locale.js @@ -0,0 +1 @@ +var{$:c}=window.SigPro,s=c("en"),n={},e=(t)=>{for(let o of Object.keys(t)){if(!n[o])n[o]={};Object.assign(n[o],t[o])}},r=(t)=>{if(t&&n[t])s(t)},a=(t)=>{return()=>n[s()]?.[t]??t};export{a as t,r as setLocale,e as addLang}; diff --git a/dist/sigpro.router.js b/dist/sigpro.router.js new file mode 100644 index 0000000..8910d0d --- /dev/null +++ b/dist/sigpro.router.js @@ -0,0 +1 @@ +var{$:p,h:u,watch:m,render:g,isF:y}=window.SigPro,d=()=>window.location.hash.slice(1)||"/",s=p(d());window.addEventListener("hashchange",()=>s(d()));var w=p({}),c=(n)=>{let i=u("div",{class:"router-hook"}),r=null;return m([s],()=>{let l=s(),t=n.find((o)=>{let e=o.path.split("/").filter(Boolean),a=l.split("/").filter(Boolean);return e.length===a.length&&e.every((h,f)=>h[0]===":"||h===a[f])})||n.find((o)=>o.path==="*");if(t){r?.destroy();let o={};t.path.split("/").filter(Boolean).forEach((e,a)=>{if(e[0]===":")o[e.slice(1)]=l.split("/").filter(Boolean)[a]}),w(o),r=g(()=>y(t.component)?t.component(o):t.component),i.replaceChildren(r.container)}}),i.destroy=()=>{r?.destroy()},i};c.params=w;c.to=(n)=>window.location.hash=n.replace(/^#?\/?/,"#/");c.back=()=>window.history.back();c.path=()=>s();export{w as routerParams,c as router}; diff --git a/dist/sigpro.utils.js b/dist/sigpro.utils.js deleted file mode 100644 index 2d948dc..0000000 --- a/dist/sigpro.utils.js +++ /dev/null @@ -1 +0,0 @@ -var{$:d,h:m,watch:g,render:x,isF:b}=window.SigPro,l=(t)=>{let e=()=>window.location.hash.slice(1)||"/",o=d(e()),n=()=>o(e());window.addEventListener("hashchange",n);let s=m("div",{class:"router-hook"}),h=null;return g([o],()=>{let f=o(),a=t.find((r)=>{let c=r.path.split("/").filter(Boolean),p=f.split("/").filter(Boolean);return c.length===p.length&&c.every((w,y)=>w[0]===":"||w===p[y])})||t.find((r)=>r.path==="*");if(a){h?.destroy();let r={};a.path.split("/").filter(Boolean).forEach((c,p)=>{if(c[0]===":")r[c.slice(1)]=f.split("/").filter(Boolean)[p]}),l.params(r),h=x(()=>b(a.component)?a.component(r):a.component),s.replaceChildren(h.container)}}),s.destroy=()=>{window.removeEventListener("hashchange",n),h?.destroy()},s};l.params=d({});l.to=(t)=>window.location.hash=t.replace(/^#?\/?/,"#/");l.back=()=>window.history.back();l.path=()=>window.location.hash.replace(/^#/,"")||"/";var k=async(t,e={},o=null)=>{if(o)o(!0);try{let n=await fetch(t,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(e),credentials:"include"});if(!n.ok){let s=await n.text();throw Error(`Error ${n.status}: ${s}`)}return await n.json()}finally{if(o)o(!1)}},u=d("en"),i={},v=(t)=>{for(let e of Object.keys(t)){if(!i[e])i[e]={};Object.assign(i[e],t[e])}},E=(t)=>{if(t&&i[t])u(t)},L=(t)=>{return()=>i[u()]?.[t]??t};export{L as t,E as setLocale,l as router,k as db,v as addLang}; diff --git a/docs/router.md b/docs/router.md index 0191fa7..5965a9e 100644 --- a/docs/router.md +++ b/docs/router.md @@ -17,7 +17,7 @@ router(routes: Route[]): HTMLElement **Returns:** A `div` element (with class `"router-hook"`) that acts as the router outlet. The router automatically destroys the previous view and mounts the matched component when the hash changes. -> **Availability:** `router` and its helper methods (`router.to`, `router.back`, `router.path`, `router.params`) are exported from the SigPro module. In **ESM** you must import them (`import { router } from 'sigpro/utils'`). In the **IIFE** classic script, they are automatically available on `window`. The examples below assume the functions are already in scope. +> You must import them (`import { router } from 'sigpro/router'`). The examples below assume the functions are already in scope. --- @@ -28,6 +28,9 @@ router(routes: Route[]): HTMLElement Place the `router` element where you want the page content to appear. Inside the routes array, define your routes. ```javascript +// remember import router +import { router } from 'sigpro/router' + const Home = () => h1("Home Page"); const UserProfile = (params) => h1(`User ID: ${params.id}`); const NotFound = () => h1("404 – Page not found"); @@ -189,6 +192,10 @@ mount(App, "#app"); | `router.params()` | Reactive signal of the current route parameters. | + +--- + + # Vite Plugin: File-based Routing The `sigproRouter` plugin for Vite automates route generation by scanning your `pages` directory. It creates a **virtual module** that you can import directly into your code, eliminating the need to maintain a manual routes array. diff --git a/docs/sigpro.db.js b/docs/sigpro.db.js new file mode 100644 index 0000000..49008cb --- /dev/null +++ b/docs/sigpro.db.js @@ -0,0 +1 @@ +var o=async(e,n={},t=null)=>{if(t)t(!0);try{let r=await fetch(e,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(n),credentials:"include"});if(!r.ok){let s=await r.text();throw Error(`Error ${r.status}: ${s}`)}return await r.json()}finally{if(t)t(!1)}};export{o as db}; diff --git a/docs/sigpro.js b/docs/sigpro.js index 6ba93cb..cf8976d 100644 --- a/docs/sigpro.js +++ b/docs/sigpro.js @@ -1 +1 @@ -var m=(e)=>typeof e=="function",q=(e)=>e&&typeof e=="object",g=Array.isArray,N=typeof document<"u"?document:null,w=(e)=>N.createTextNode(e==null?"":String(e)),G=(e)=>e?._rt?e._cnt:e instanceof Node?e:w(e),B=(e)=>e.children,A=(e)=>m(e)?e():e,d=null,p=null,E=0,M=0,C=new Set,L=new WeakMap,D="http://www.w3.org/2000/svg",I="http://www.w3.org/1999/xlink",R=new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")),z=new Set(["src","href","formaction","action","background","code","archive"]),k=(e)=>{if(e)e.forEach((l)=>l()),e.clear()},b=(e)=>{if(!e||e._x)return;e._x=1;let l=[e],r;while(r=l.pop()){if(k(r._c),r._ch)r._ch.forEach((o)=>l.push(o)),r._ch.clear();if(r._d)r._d.forEach((o)=>o.delete(r)),r._d.clear()}},S=(e)=>p&&(p._c||=new Set).add(e),J=(e)=>{let l=d;d=null;try{return e()}finally{d=l}},v=(e,l=0)=>{let r=()=>{if(r._x)return;if(r._d)r._d.forEach((a)=>a.delete(r));k(r._c);let o=d,n=p;d=p=r;try{return r._res=e()}catch(a){console.error("[SigPro]",a)}finally{d=o,p=n}};if(r._d=r._c=r._ch=null,r._x=0,r._iC=l,r._dp=d?d._dp+1:0,r._m=[],r._p=p,p)(p._ch||=new Set).add(r);return r},W=()=>{if(E)return;E=1;let e=[...C].sort((l,r)=>l._dp-r._dp);C.clear();for(let l of e)if(!l._x)l();E=0},V=(e)=>{M++;try{return e()}finally{if(!--M&&C.size&&!E)W()}},x=(e,l=0)=>{if(!l&&d&&!d._x)e.add(d),(d._d||=new Set).add(e);else if(l&&e.size){let r=0;for(let o of e){if(o===d||o._x)continue;if(o._iC){if(o._dt=1,o._sb)x(o._sb,1)}else C.add(o),r=1}if(r&&!E&&!M)queueMicrotask(W)}},X=(e,l=null)=>{let r=new Set;if(m(e)){let o,n=()=>{if(n._dt){let a=d;d=n;try{let t=e();if(!Object.is(o,t))o=t,x(r,1)}finally{d=a}n._dt=0}return x(r),o};return n._iC=n._dt=1,n._sb=r,n._d=null,n._x=0,n}if(l)try{e=JSON.parse(localStorage.getItem(l))??e}catch(o){}return(...o)=>{if(o.length){let n=m(o[0])?o[0](e):o[0];if(!Object.is(e,n)){if(e=n,l)localStorage.setItem(l,JSON.stringify(e));x(r,1)}}return x(r),e}},P=(e,l)=>{let r=v(l?()=>{let o=g(e)?e.map((n)=>n()):e();J(()=>l(o))}:e);return r(),()=>b(r)},U=(e)=>{if(!e)return;if(k(e._c),e._oE)b(e._oE);if(e.childNodes)e.childNodes.forEach(U)},j=(e,l)=>l==null||l===!1?null:(z.has(e)||e.startsWith("on"))&&/^\s*(javascript|data|vbscript):/i.test(String(l))?"#":l,O=(e,l={},r=[])=>{if(l instanceof Node||g(l)||!q(l))r=l,l={};if(m(e)){let t=v(()=>t._res=e(l,{children:r,emit:(i,...f)=>l[`on${i[0].toUpperCase()}${i.slice(1)}`]?.(...f)}));if(t(),t._res==null)return null;let s=t._res instanceof Node||g(t._res)&&t._res.every((i)=>i instanceof Node)?t._res:w(t._res),c=(i)=>{if(q(i)&&!i._rt)i._m=t._m||[],i._c=t._c||new Set,i._oE=t};return g(s)?s.forEach(c):c(s),s}let o=R.has(e),n=o?N.createElementNS(D,e):N.createElement(e);n._c=new Set;for(let t in l){let s=l[t];if(t==="ref"){m(s)?s(n):s.current=n;continue}if(o&&t.startsWith("xlink:")){let c=j(t.slice(6),s);c==null?n.removeAttributeNS(I,t.slice(6)):n.setAttributeNS(I,t.slice(6),c);continue}if(t.startsWith("on")){let c=t.slice(2).toLowerCase();n.addEventListener(c,s);let i=()=>n.removeEventListener(c,s);n._c.add(i),S(i)}else if(m(s)){let c=v(()=>{let i=j(t,s());if(t==="class")n.className=i||"";else if(i==null)n.removeAttribute(t);else if(t==="style"&&typeof i=="string")n.setAttribute("style",i);else if(t in n&&!o)n[t]=i;else n.setAttribute(t,i===!0?"":i)});if(c(),n._c.add(()=>b(c)),S(()=>b(c)),/^(INPUT|TEXTAREA|SELECT)$/.test(n.tagName)&&(t==="value"||t==="checked"))n.addEventListener(t==="checked"?"change":"input",(i)=>s(i.target[t]))}else{let c=j(t,s);if(c!=null)if(t==="style"&&typeof c=="string")n.setAttribute("style",c);else if(t in n&&!o)n[t]=c;else n.setAttribute(t,c===!0?"":c)}}let a=(t)=>{if(g(t))return t.forEach(a);if(m(t)){let s=w(""),c=[];n.appendChild(s);let i=v(()=>{let f=t(),h=(g(f)?f:[f]).map(G),y=s;c.forEach((u)=>{if(u._rt?u.destroy():U(u),u.parentNode)u.remove()});for(let u=h.length-1;u>=0;u--){let _=h[u];if(_.parentNode!==y.parentNode)y.parentNode?.insertBefore(_,y);if(_._m)_._m.forEach(($)=>$());y=_}c=h});i(),n._c.add(()=>b(i)),S(()=>b(i))}else{let s=G(t);if(n.appendChild(s),s._m)s._m.forEach((c)=>c())}};return a(r),n},T=(e)=>{let l=new Set,r=p,o=d,n=N.createElement("div");n.style.display="contents",n.setAttribute("role","presentation"),p={_c:l},d=null;let a=(t)=>{if(!t)return;if(t._rt)l.add(t.destroy),n.appendChild(t._cnt);else if(g(t))t.forEach(a);else n.appendChild(t instanceof Node?t:w(t))};try{a(e({onCleanup:(t)=>l.add(t)}))}finally{p=r,d=o}return{_rt:1,_cnt:n,destroy:()=>{k(l),U(n),n.remove()}}},F=(e,l,r=null)=>{let o=w(""),n=O("div",{style:"display:contents"},[o]),a;return P(()=>!!A(e),(t)=>{if(a)a.destroy(),a=null;let s=t?l:r;if(s)a=T(()=>A(s)),n.insertBefore(a._cnt,o)}),S(()=>a?.destroy()),n},K=(e,l,r)=>{let o=w(""),n=O("div",{style:"display:contents"},[o]),a=new Map;return P(()=>A(e)||[],(t)=>{let s=new Map,c=[];for(let f=0,h=(t||[]).length;fl(y,f));else a.delete(u);s.set(u,_),c.push(_)}a.forEach((f)=>f.destroy());let i=o;for(let f=c.length-1;f>=0;f--){let h=c[f]._cnt;if(h.nextSibling!==i)n.insertBefore(h,i);i=h}a=s}),n},Q=(e,l)=>{let r=typeof l=="string"?N.querySelector(l):l;if(!r)return;if(L.has(r))L.get(r).destroy();let o=T(m(e)?e:()=>e);return r.replaceChildren(o._cnt),L.set(r,o),o},H="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",Y={$:X,watch:P,batch:V,h:O,fragment:B,render:T,mount:Q,when:F,each:K,onUnmount:S,val:A,isA:g,isF:m,isO:q};if(typeof window<"u")window.SigPro=Y,H.split(" ").forEach((e)=>{window[e]=(l,r)=>O(e,l,r)});export{F as when,P as watch,A as val,T as render,S as onUnmount,Q as mount,q as isO,m as isF,g as isA,O as h,B as fragment,K as each,V as batch,Y as SigPro,X as $}; +var q=(j)=>typeof j=="function",u=(j)=>j&&typeof j=="object",R=Array.isArray,m=typeof document<"u"?document:null,C=(j)=>m.createTextNode(j==null?"":String(j)),g=(j)=>j?._rt?j._cnt:j instanceof Node?j:C(j),k=(j)=>j.children,S=(j)=>q(j)?j():j,I=null,V=null,Q=0,w=0,b=new Set,A=new WeakMap,l="http://www.w3.org/2000/svg",h="http://www.w3.org/1999/xlink",i=new Set("svg,path,circle,rect,line,polyline,polygon,g,defs,text,textPath,tspan,use,symbol,image,marker,ellipse".split(",")),v=new Set(["src","href","formaction","action","background","code","archive"]),x=(j)=>{if(j)j.forEach((H)=>H()),j.clear()},_=(j)=>{if(!j||j._x)return;j._x=1;let H=[j],z;while(z=H.pop()){if(x(z._c),z._ch)z._ch.forEach((J)=>H.push(J)),z._ch.clear();if(z._d)z._d.forEach((J)=>J.delete(z)),z._d.clear()}},T=(j)=>V&&(V._c||=new Set).add(j),p=(j)=>{let H=I;I=null;try{return j()}finally{I=H}},D=(j,H=0)=>{let z=()=>{if(z._x)return;if(z._d)z._d.forEach(($)=>$.delete(z));x(z._c);let J=I,B=V;I=V=z;try{return z._res=j()}catch($){console.error("[SigPro]",$)}finally{I=J,V=B}};if(z._d=z._c=z._ch=null,z._x=0,z._iC=H,z._dp=I?I._dp+1:0,z._m=[],z._p=V,V)(V._ch||=new Set).add(z);return z},o=()=>{if(Q)return;Q=1;let j=[...b].sort((H,z)=>H._dp-z._dp);b.clear();for(let H of j)if(!H._x)H();Q=0},f=(j)=>{w++;try{return j()}finally{if(!--w&&b.size&&!Q)o()}},G=(j,H=0)=>{if(!H&&I&&!I._x)j.add(I),(I._d||=new Set).add(j);else if(H&&j.size){let z=0;for(let J of j){if(J===I||J._x)continue;if(J._iC){if(J._dt=1,J._sb)G(J._sb,1)}else b.add(J),z=1}if(z&&!Q&&!w)queueMicrotask(o)}},a=(j,H=null)=>{let z=new Set;if(q(j)){let J,B=()=>{if(B._dt){let $=I;I=B;try{let y=j();if(!Object.is(J,y))J=y,G(z,1)}finally{I=$}B._dt=0}return G(z),J};return B._iC=B._dt=1,B._sb=z,B._d=null,B._x=0,B}if(H)try{j=JSON.parse(localStorage.getItem(H))??j}catch(J){}return(...J)=>{if(J.length){let B=q(J[0])?J[0](j):J[0];if(!Object.is(j,B)){if(j=B,H)localStorage.setItem(H,JSON.stringify(j));G(z,1)}}return G(z),j}},F=(j,H)=>{let z=D(H?()=>{let J=R(j)?j.map((B)=>B()):j();p(()=>H(J))}:j);return z(),()=>_(z)},O=(j)=>{if(!j)return;if(x(j._c),j._oE)_(j._oE);if(j.childNodes)j.childNodes.forEach(O)},N=(j,H)=>H==null||H===!1?null:(v.has(j)||j.startsWith("on"))&&/^\s*(javascript|data|vbscript):/i.test(String(H))?"#":H,E=(j,H={},z=[])=>{if(H instanceof Node||R(H)||!u(H))z=H,H={};if(q(j)){let y=D(()=>y._res=j(H,{children:z,emit:(W,...K)=>H[`on${W[0].toUpperCase()}${W.slice(1)}`]?.(...K)}));if(y(),y._res==null)return null;let Z=y._res instanceof Node||R(y._res)&&y._res.every((W)=>W instanceof Node)?y._res:C(y._res),P=(W)=>{if(u(W)&&!W._rt)W._m=y._m||[],W._c=y._c||new Set,W._oE=y};return R(Z)?Z.forEach(P):P(Z),Z}let J=i.has(j),B=J?m.createElementNS(l,j):m.createElement(j);B._c=new Set;for(let y in H){let Z=H[y];if(y==="ref"){q(Z)?Z(B):Z.current=B;continue}if(J&&y.startsWith("xlink:")){let P=N(y.slice(6),Z);P==null?B.removeAttributeNS(h,y.slice(6)):B.setAttributeNS(h,y.slice(6),P);continue}if(y.startsWith("on")){let P=y.slice(2).toLowerCase();B.addEventListener(P,Z);let W=()=>B.removeEventListener(P,Z);B._c.add(W),T(W)}else if(q(Z)){let P=D(()=>{let W=N(y,Z());if(y==="class")B.className=W||"";else if(W==null)B.removeAttribute(y);else if(y==="style"&&typeof W=="string")B.setAttribute("style",W);else if(y in B&&!J)B[y]=W;else B.setAttribute(y,W===!0?"":W)});if(P(),B._c.add(()=>_(P)),T(()=>_(P)),/^(INPUT|TEXTAREA|SELECT)$/.test(B.tagName)&&(y==="value"||y==="checked"))B.addEventListener(y==="checked"?"change":"input",(W)=>Z(W.target[y]))}else{let P=N(y,Z);if(P!=null)if(y==="style"&&typeof P=="string")B.setAttribute("style",P);else if(y in B&&!J)B[y]=P;else B.setAttribute(y,P===!0?"":P)}}let $=(y)=>{if(R(y))return y.forEach($);if(q(y)){let Z=C(""),P=[];B.appendChild(Z);let W=D(()=>{let K=y(),Y=(R(K)?K:[K]).map(g),M=Z;P.forEach((L)=>{if(L._rt?L.destroy():O(L),L.parentNode)L.remove()});for(let L=Y.length-1;L>=0;L--){let X=Y[L];if(X.parentNode!==M.parentNode)M.parentNode?.insertBefore(X,M);if(X._m)X._m.forEach((d)=>d());M=X}P=Y});W(),B._c.add(()=>_(W)),T(()=>_(W))}else{let Z=g(y);if(B.appendChild(Z),Z._m)Z._m.forEach((P)=>P())}};return $(z),B},U=(j)=>{let H=new Set,z=V,J=I,B=m.createElement("div");B.style.display="contents",B.setAttribute("role","presentation"),V={_c:H},I=null;let $=(y)=>{if(!y)return;if(y._rt)H.add(y.destroy),B.appendChild(y._cnt);else if(R(y))y.forEach($);else B.appendChild(y instanceof Node?y:C(y))};try{$(j({onCleanup:(y)=>H.add(y)}))}finally{V=z,I=J}return{_rt:1,_cnt:B,destroy:()=>{x(H),O(B),B.remove()}}},s=(j,H,z=null)=>{let J=C(""),B=E("div",{style:"display:contents"},[J]),$;return F(()=>!!S(j),(y)=>{if($)$.destroy(),$=null;let Z=y?H:z;if(Z)$=U(()=>S(Z)),B.insertBefore($._cnt,J)}),T(()=>$?.destroy()),B},r=(j,H,z)=>{let J=C(""),B=E("div",{style:"display:contents"},[J]),$=new Map;return F(()=>S(j)||[],(y)=>{let Z=new Map,P=[];for(let K=0,Y=(y||[]).length;KH(M,K));else $.delete(L);Z.set(L,X),P.push(X)}$.forEach((K)=>K.destroy());let W=J;for(let K=P.length-1;K>=0;K--){let Y=P[K]._cnt;if(Y.nextSibling!==W)B.insertBefore(Y,W);W=Y}$=Z}),B},t=(j,H)=>{let z=typeof H=="string"?m.querySelector(H):H;if(!z)return;if(A.has(z))A.get(z).destroy();let J=U(q(j)?j:()=>j);return z.replaceChildren(J._cnt),A.set(z,J),J},c={$:a,watch:F,batch:f,h:E,fragment:k,render:U,mount:t,when:s,each:r,onUnmount:T,val:S,isA:R,isF:q,isO:u};if(typeof window<"u")window.SigPro=c;export{s as when,F as watch,S as val,U as render,T as onUnmount,t as mount,u as isO,q as isF,R as isA,E as h,k as fragment,r as each,f as batch,c as SigPro,a as $}; diff --git a/docs/sigpro.locale.js b/docs/sigpro.locale.js new file mode 100644 index 0000000..ed8b9f2 --- /dev/null +++ b/docs/sigpro.locale.js @@ -0,0 +1 @@ +var{$:c}=window.SigPro,s=c("en"),n={},e=(t)=>{for(let o of Object.keys(t)){if(!n[o])n[o]={};Object.assign(n[o],t[o])}},r=(t)=>{if(t&&n[t])s(t)},a=(t)=>{return()=>n[s()]?.[t]??t};export{a as t,r as setLocale,e as addLang}; diff --git a/docs/sigpro.router.js b/docs/sigpro.router.js new file mode 100644 index 0000000..8910d0d --- /dev/null +++ b/docs/sigpro.router.js @@ -0,0 +1 @@ +var{$:p,h:u,watch:m,render:g,isF:y}=window.SigPro,d=()=>window.location.hash.slice(1)||"/",s=p(d());window.addEventListener("hashchange",()=>s(d()));var w=p({}),c=(n)=>{let i=u("div",{class:"router-hook"}),r=null;return m([s],()=>{let l=s(),t=n.find((o)=>{let e=o.path.split("/").filter(Boolean),a=l.split("/").filter(Boolean);return e.length===a.length&&e.every((h,f)=>h[0]===":"||h===a[f])})||n.find((o)=>o.path==="*");if(t){r?.destroy();let o={};t.path.split("/").filter(Boolean).forEach((e,a)=>{if(e[0]===":")o[e.slice(1)]=l.split("/").filter(Boolean)[a]}),w(o),r=g(()=>y(t.component)?t.component(o):t.component),i.replaceChildren(r.container)}}),i.destroy=()=>{r?.destroy()},i};c.params=w;c.to=(n)=>window.location.hash=n.replace(/^#?\/?/,"#/");c.back=()=>window.history.back();c.path=()=>s();export{w as routerParams,c as router}; diff --git a/package.json b/package.json index 93ee3f9..91ee25d 100644 --- a/package.json +++ b/package.json @@ -19,10 +19,33 @@ "import": "./dist/sigpro.js", "default": "./dist/sigpro.js" }, - "./utils": "./dist/sigpro.utils.js", - "./grid": "./dist/sigpro.grid.js", - "./editor": "./dist/sigpro.editor.js", - "./vite": "./dist/sigpro.vite.js", + "./db": { + "types": "./sigpro.d.ts", + "import": "./dist/sigpro.db.js", + "default": "./dist/sigpro.db.js" + }, + "./router": { + "types": "./sigpro.d.ts", + "import": "./dist/sigpro.router.js", + "default": "./dist/sigpro.router.js" + }, + "./locale": { + "types": "./sigpro.d.ts", + "import": "./dist/sigpro.locale.js", + "default": "./dist/sigpro.locale.js" + }, + "./grid": { + "import": "./dist/sigpro.grid.js", + "default": "./dist/sigpro.grid.js" + }, + "./editor": { + "import": "./dist/sigpro.editor.js", + "default": "./dist/sigpro.editor.js" + }, + "./vite": { + "import": "./dist/sigpro.vite.js", + "default": "./dist/sigpro.vite.js" + }, "./css": "./dist/sigpro.ui.css", "./ui": { "types": "./sigpro.ui.d.ts", @@ -34,7 +57,8 @@ "dist/", "README.md", "LICENSE", - "sigpro.d.ts" + "sigpro.d.ts", + "sigpro.ui.d.ts" ], "homepage": "https://sigpro.natxocc.com/#/", "repository": { @@ -50,14 +74,16 @@ "clean": "rm -rf dist", "prebuild": "npm run clean", "build:core": "bun build ./src/sigpro.js --bundle --outfile=./dist/sigpro.js --format=esm --minify", - "build:utils": "bun build ./src/sigpro.utils.js --bundle --outfile=./dist/sigpro.utils.js --format=esm --external ./src/sigpro.js --minify", + "build:db": "bun build ./src/sigpro.db.js --bundle --outfile=./dist/sigpro.db.js --format=esm --minify", + "build:router": "bun build ./src/sigpro.router.js --bundle --outfile=./dist/sigpro.router.js --format=esm --external ./src/sigpro.js --minify", + "build:locale": "bun build ./src/sigpro.locale.js --bundle --outfile=./dist/sigpro.locale.js --format=esm --external ./src/sigpro.js --minify", "build:ui": "bun build ./src/sigpro.ui.js --bundle --outfile=./dist/sigpro.ui.js --format=esm --external ./src/sigpro.js --minify", "build:grid": "bun build ./src/sigpro.grid.js --bundle --external sigpro --outfile=./dist/sigpro.grid.js --format=esm --minify", "build:editor": "bun build ./src/sigpro.editor.js --bundle --external sigpro --outfile=./dist/sigpro.editor.js --format=esm --minify", - "build:vite": "bun build ./src/sigpro.vite.js --bundle --outfile=./dist/sigpro.vite.js --format=esm --external fs --external path --minify", + "build:vite": "bun build ./src/sigpro.vite.js --bundle --outfile=./dist/sigpro.vite.js --format=esm --external fs --external path --minify", "build:css": "tailwindcss -i ./src/sigpro.ui.css -o ./dist/sigpro.ui.css --minify --content './src/tailwind' && du -h ./dist/sigpro.ui.css", "build:convert": "bun build ./src/sigpro.convert.js --bundle --outfile=./docs/sigpro.convert.js --format=esm --external ./src/sigpro.js --minify", - "build": "bun run build:core && bun run build:utils && bun run build:ui && bun run build:grid && bun run build:editor && bun run build:vite && bun run build:css && bun run build:convert && cp ./dist/* ./docs", + "build": "bun run build:core && bun run build:db && bun run build:router && bun run build:locale && bun run build:ui && bun run build:grid && bun run build:editor && bun run build:vite && bun run build:css && bun run build:convert && cp ./dist/* ./docs", "docs": "bun x serve docs" }, "devDependencies": { @@ -83,4 +109,4 @@ "lightweight", "sigpro" ] -} +} \ No newline at end of file diff --git a/src/sigpro.db.js b/src/sigpro.db.js new file mode 100644 index 0000000..b0bd3cd --- /dev/null +++ b/src/sigpro.db.js @@ -0,0 +1,18 @@ +export const db = async (url, data = {}, loading = null) => { + if (loading) loading(true); + try { + const res = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data), + credentials: 'include' + }); + if (!res.ok) { + const errorText = await res.text(); + throw new Error(`Error ${res.status}: ${errorText}`); + } + return await res.json(); + } finally { + if (loading) loading(false); + } +}; \ No newline at end of file diff --git a/src/sigpro.js b/src/sigpro.js index 02e20f0..be72d13 100644 --- a/src/sigpro.js +++ b/src/sigpro.js @@ -242,13 +242,13 @@ export const mount = (c, tgt) => { t.replaceChildren(i._cnt); MOUNTED.set(t, i); return i; }; -const htmlTags = "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"; +// const htmlTags = "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"; export const SigPro = { $, watch, batch, h, fragment, render, mount, when, each, onUnmount, val, isA, isF, isO }; if (typeof window !== "undefined") { window.SigPro = SigPro; - htmlTags.split(" ").forEach(tag => { - window[tag] = (props, children) => h(tag, props, children); - }); + // htmlTags.split(" ").forEach(tag => { + // window[tag] = (props, children) => h(tag, props, children); + // }); } \ No newline at end of file diff --git a/src/sigpro.locale.js b/src/sigpro.locale.js new file mode 100644 index 0000000..b6e2428 --- /dev/null +++ b/src/sigpro.locale.js @@ -0,0 +1,21 @@ +const { $ } = window.SigPro; + +const currentLocale = $("en"); +const translations = {}; + +export const addLang = obj => { + for (const locale of Object.keys(obj)) { + if (!translations[locale]) translations[locale] = {}; + Object.assign(translations[locale], obj[locale]); + } +}; + +export const setLocale = locale => { + if (locale && translations[locale]) { + currentLocale(locale); + } +}; + +export const t = key => { + return () => translations[currentLocale()]?.[key] ?? key; +}; \ No newline at end of file diff --git a/src/sigpro.router.js b/src/sigpro.router.js new file mode 100644 index 0000000..4b4cac5 --- /dev/null +++ b/src/sigpro.router.js @@ -0,0 +1,49 @@ +const { $, h, watch, render, isF } = window.SigPro; + +const getHash = () => window.location.hash.slice(1) || "/"; +const currentPath = $(getHash()); + +window.addEventListener("hashchange", () => currentPath(getHash())); + +export const routerParams = $({}); + +export const router = routes => { + const hook = h("div", { class: "router-hook" }); + let currentView = null; + + watch([currentPath], () => { + const cur = currentPath(); + + const route = routes.find(r => { + const p1 = r.path.split("/").filter(Boolean); + const p2 = cur.split("/").filter(Boolean); + return p1.length === p2.length && p1.every((p, i) => p[0] === ":" || p === p2[i]); + }) || routes.find(r => r.path === "*"); + + if (route) { + currentView?.destroy(); + + const params = {}; + route.path.split("/").filter(Boolean).forEach((p, i) => { + if (p[0] === ":") params[p.slice(1)] = cur.split("/").filter(Boolean)[i]; + }); + + routerParams(params); + + currentView = render(() => isF(route.component) ? route.component(params) : route.component); + + hook.replaceChildren(currentView.container); + } + }); + + hook.destroy = () => { + currentView?.destroy(); + }; + + return hook; +}; + +router.params = routerParams; +router.to = p => window.location.hash = p.replace(/^#?\/?/, "#/"); +router.back = () => window.history.back(); +router.path = () => currentPath(); \ No newline at end of file diff --git a/src/sigpro.utils.js b/src/sigpro.utils.js deleted file mode 100644 index 615e649..0000000 --- a/src/sigpro.utils.js +++ /dev/null @@ -1,90 +0,0 @@ -/// - -const { $, h, watch, render, isF } = window.SigPro; - -// router -export const router = routes => { - const getHash = () => window.location.hash.slice(1) || "/"; - const path = $(getHash()); - const handler = () => path(getHash()); - window.addEventListener("hashchange", handler); - - const hook = h("div", { class: "router-hook" }); - let currentView = null; - - watch([path], () => { - const cur = path(); - const route = routes.find(r => { - const p1 = r.path.split("/").filter(Boolean); - const p2 = cur.split("/").filter(Boolean); - return p1.length === p2.length && p1.every((p, i) => p[0] === ":" || p === p2[i]); - }) || routes.find(r => r.path === "*"); - - if (route) { - currentView?.destroy(); - const params = {}; - route.path.split("/").filter(Boolean).forEach((p, i) => { - if (p[0] === ":") params[p.slice(1)] = cur.split("/").filter(Boolean)[i]; - }); - router.params(params); - currentView = render(() => isF(route.component) ? route.component(params) : route.component); - hook.replaceChildren(currentView.container); - } - }); - - hook.destroy = () => { - window.removeEventListener("hashchange", handler); - currentView?.destroy(); - }; - - return hook; -}; - -router.params = $({}); -router.to = p => window.location.hash = p.replace(/^#?\/?/, "#/"); -router.back = () => window.history.back(); -router.path = () => window.location.hash.replace(/^#/, "") || "/"; - -// db -export const db = async (url, data = {}, loading = null) => { - if (loading) loading(true); - try { - const res = await fetch(url, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify(data), - credentials: 'include' - }); - if (!res.ok) { - const errorText = await res.text(); - throw new Error(`Error ${res.status}: ${errorText}`); - } - return await res.json(); - } finally { - if (loading) loading(false); - } -}; - -const currentLocale = $('en'); - -const translations = {}; - -// addLang -export const addLang = (obj) => { - for (const locale of Object.keys(obj)) { - if (!translations[locale]) translations[locale] = {}; - Object.assign(translations[locale], obj[locale]); - } -}; - -// setLocale -export const setLocale = (locale) => { - if (locale && translations[locale]) { - currentLocale(locale); - } -}; - -// t -export const t = (key) => { - return () => translations[currentLocale()]?.[key] ?? key; -}; \ No newline at end of file