diff --git a/UI/sigpro-ui.min.js b/UI/sigpro-ui.min.js deleted file mode 100644 index 576adba..0000000 --- a/UI/sigpro-ui.min.js +++ /dev/null @@ -1 +0,0 @@ -export const UI=(t,l="es")=>{const e={},a={es:{close:"Cerrar",confirm:"Confirmar",cancel:"Cancelar",search:"Buscar...",loading:"Cargando..."},en:{close:"Close",confirm:"Confirm",cancel:"Cancel",search:"Search...",loading:"Loading..."}},s=t(l);e.SetLocale=t=>s(t);const A=t=>()=>a[s()][t]||t,n=t=>"function"==typeof t?t():t,o=(t,l)=>"function"==typeof l?()=>`${t} ${l()||""}`.trim():`${t} ${l||""}`.trim(),c="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACLSURBVDiN7dO9CQJBFEXhb38K0FwQrMNEVpuwB0NjrcYabECsQk0sQ1mTF4zIjrgmBh54MMx998AEwzOrmC5e8gJjbDHCJO7PHYI0v2JT4Ig9DljGwq5DkOZTLOCOMoIhBpknpHmFWx3ldaaUo6oTc2/ab7rl+508f8GvCC5oenTn4tM1cWg/nBNmD4fBH/Kfvt2TAAAAAElFTkSuQmCC",i="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAASVJREFUOI190r0uhFEQBuBnVxaF2PUTCkFchV0SV6BQi0rEbShFlCqNktJP0Iqf3i3YVSlXVEQozojP8e2+ySSTed+ZMzNnKnpjCFPhv+C9j/YPlnCBV3TCujhHq19iFftoYxOjBa4esTb2QvsP+7jFWJ9HxnEXRf5gGU9Z8gKucBl+sUgHTahE8AJnOCoIT/AcmhmsF7gtrGINBqWFFWcmLXMUhzjIuEbk1GA+2i/DNh4wUsK1MVfFV2GUHJO4xlsPHr8j1Eu44bAcDek2agP4lDZaxWMm3MEKbrL4hjT/8U+gJc00nglnw4qYkL5xMW9rTzqSvEiefI/dMrIaRTrSPzcKXCNinUguPeUfNKWj6kqH9Bz+aVnbvb6PtKTp8F/wUSb6Bu5YN5n7ff0kAAAAAElFTkSuQmCC",r="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAQtJREFUOI2F0jFOAlEQBuAPImoFqyTa6TEEbfUihruYDYfwCAg3UDsTY20na0VjgqUWWuxgHsuy/skk82bmn/fPm9eyHXs4Cn+Br4baNZxjhk8UYUtMMWwitjHGHNfoJrlexObIo3YDY9zjoOGSQzxEkzVc4O0fctqkwCANzkJiE9LmI9ytDrvKB+tWGQnylIAsOB04VcrfdluO55CeYo6THfygVUne4jX8S1zho1LTDu7fCL2KxCe8oF8zUqb8G51VYGrzEffD6jDCJA0MY6bqnHXoK9d4Vk3kyk/S1KSPR9zUJdvRpAiJWZLLIlYEufYrrzBQ7nyJ97ClcuYN2dX1pejgOPwFvuuKfgHXiDR+HL1j1AAAAABJRU5ErkJggg==",d="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAARZJREFUOI2V0j1KQ1EQBeDPp4lWRiMoKVyAK9AoiLgJGytxD9oJNhKyDyvBnw2IugC3YGKVRk1KRbR48yC5vjzwwIHL3DPnzp2ZGdMxj9U4D/BZoZ3ANu4wQj84xC3aVYkZuujhCItjd42I9dAJ7R908YDlikeaeAyTCezgpST5IJia9LFVlA0nOMd7It4IjuMttKeFQR17uKooPcUV9lHL0ArX0T8MPqLa1hx+MDNFWDX7LHLV4/VGiWghmGJJvhu1WXzLO5rhORGeYRf3SfwQNVwWgbZ8SZqJcD04jhX5GDfTsjryJUlN0uQnXJRdZmHSx7H8nwWWItaP5NJVLrCFG3mTXoNDXJeVPW185E1ai/MAX2WiX9S3NSPYbj+uAAAAAElFTkSuQmCC",m="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAARJJREFUOI2l0r8uRFEQBvAfu9glwUYiUaxHUEl0VDpKeq+wpZBINAqFRHgTKg0tCSqVhmKDEM1u/Esodm725rq7iC+ZzMnM982ZmXP4JwpdchWsYBrXeMkj9XQQV3GEi+BMYR63v+mqiDPUUrEaTiP3I1ZxEOcySnE+jFxXVPEQPimWiCYzOdCbKbCFPe1Z+8PgBvvBycVMCIdSsY2wBEPBmcnrYBtraKRib2EJGljHjswLLuI8Z6SS9hLTl15iIR08wZLv2AzLYjk0YATP8n9lVWbrgUJohosYxCdG8Zghdvp5ldCUi6hrPd0VjvGEVzTxEYLkogGMYQ67uEtvcgKzGA8y9IV/D9/Evdb89Q7d/Q1fB8U0mpUmzV0AAAAASUVORK5CYII=";return e.Request=(l,e=null,a={})=>{const s=t(null),A=t(!1),o=t(null),c=t(!1);let i=null;const r=async(t=null)=>{const r=n(l);if(r){i&&i.abort(),i=new AbortController,A(!0),o(null),c(!1);try{const l=t||e,n=await fetch(r,{method:a.method||(l?"POST":"GET"),headers:{"Content-Type":"application/json",...a.headers},body:l?JSON.stringify(l):null,signal:i.signal,...a});if(!n.ok)throw new Error(`HTTP ${n.status}`);let o=await n.json();"function"==typeof a.transform&&(o=a.transform(o)),s(o),c(!0)}catch(t){"AbortError"!==t.name&&o(t.message)}finally{A(!1)}}};return t((()=>(r(),()=>i?.abort()))),{data:s,loading:A,error:o,success:c,reload:t=>r(t)}},e.Response=(l,a)=>t.html("div",{class:"res-container"},[t.if(l.loading,t.html("div",{class:"flex justify-center p-4"},t.html("span",{class:"loading loading-dots text-primary"}))),t.if(l.error,(()=>t.html("div",{role:"alert",class:"alert alert-error"},[t.html("span",{},l.error()),e.Button({class:"btn-xs btn-ghost border-current",onclick:()=>l.reload()},"Retry")]))),t.if(l.success,(()=>{const t=l.data();return null!==t?a(t):null}))]),e.Button=(l,e)=>{const{badge:a,badgeClass:s,tooltip:A,icon:c,loading:i,...r}=l;let d=t.html("button",{...r,class:o("btn",l.class||l.class),disabled:()=>n(i)||n(l.disabled)||n(l.disabled)},[()=>n(i)?t.html("span",{class:"loading loading-spinner"}):null,c?t.html("span",{class:"mr-1"},c):null,e]);return a&&(d=t.html("div",{class:"indicator"},[t.html("span",{class:o("indicator-item badge",s||"badge-secondary")},a),d])),A?t.html("div",{class:"tooltip","data-tip":A},d):d},e.Input=l=>{const{label:e,tip:a,value:s,error:i,isSearch:r,icon:d,type:m="text",...h}=l,u="password"===m,g=t(!1),b={text:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAB2AAAAdgFOeyYIAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAMRJREFUOI3t0bFKAmAUBeAPURD1HQwUTKPJEmzQoSWQcKpVfIuWdvU9WnqNhsYWBx0a2lvLSMKGbvQ7SO564HA497/3cu/92SPFAS5QDN9CftviDhZYYRpNPtH/rzATOsQT6jhCFzmc4DTJL6AX067hPiimuAr95RglzMJ/4AyyUXSMw3iEauhN6C0eUEMFAyzTFZ7xiOvwL3jbsPYSr3hPg3dB/o43SVYY+TnsPPwXztMG5SDr39dGM8kr4RKNDdPtJL4BNXEmsdKC+S4AAAAASUVORK5CYII=",password:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAWQAAAFkBqp2phgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACQSURBVDiN7dKxDcJQDATQJ0YgXQQ1bAgDEIZBETPQwjakIjRQ8CMSyR8SiZKTrvHZd/r+JsYSNZrEI1ZR4ywzfElcJ55xwiITOECNTVDf4jDGoEEZ1Etcxxg8pmjRDiahb7BH20uKKPVUkVmL+YjQArdI+PT2bO9Pd/A34O71Rd9QeN/LAFUSckfUscWuG3oCgP8nrDH6T5AAAAAASUVORK5CYII=",date:c,number:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAB2AAAAdgFOeyYIAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAMxJREFUOI3t0bFKwlEUBvBfmmBEr1APIDZJ9AJJQyAIvkGP0C4uQruza+DUmuIc9AC9gBG4Nmpkw/8IB3Vw1w8u95zvnvPde77LEeUUV9HAF67QRA2nmMf5A+o4x3cWOsMYy8j7WMX6jaYbLBL/mAWe8RcHm1ihs8G94gVKQQzwlAouMcQo8p/Y28HdYpYFZmsi0MVdxD1MdrxsC500wijdvgtbI1AYtDbxMwkuFAZmE1uYwkkSqOIaHyHcxEU0vUXNPSqKr37fZ6xDwD9DPS0OyHjQHQAAAABJRU5ErkJggg==",email:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAC4SURBVDiNxdIxagJRFIXhLzLFBNJYaJslSEylWOhq3IorMGQ16SyjYCFiZWU5pTaDFvOUyTAZ8RHID69555577oXLf/OEGaY4R3g/4IhORHg3eOXYYvSAeRQ8OWQYYoNPvDQYnxUr7zBB1grCAv3QbIlxjXmAb7Txhq+rkFUKq9NUU8vcJiizwDtOWGEdmvTKqT+61H0GXsP7jSxpEGF/R1e3wkO0FBeVRnhTSBTneBB3yvOI4D/mAnvrIwKM5s4AAAAAAElFTkSuQmCC"},p=t.html("input",{...h,type:()=>u?g()?"text":"password":m,placeholder:l.placeholder||e||(r?A("search")():" "),class:o("grow order-2 focus:outline-none",l.class),value:s,oninput:t=>l.oninput?.(t),disabled:()=>n(l.disabled)}),f=d||(b[m]?t.html("img",{src:b[m],class:"w-5 h-5 opacity-50",alt:m}):null);return t.html("label",{class:()=>o("input input-bordered floating-label flex items-center gap-2 w-full relative",n(i)?"input-error":"")},[f?t.html("div",{class:"order-1 shrink-0"},f):null,e?t.html("span",{class:"text-base-content/60 order-0"},e):null,p,u?t.html("button",{type:"button",class:"order-3 btn btn-ghost btn-xs btn-circle opacity-50 hover:opacity-100",onclick:t=>{t.preventDefault(),g(!g())}},(()=>t.html("img",{class:"w-5 h-5",src:g()?"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADjSURBVDiN3dJNSgNBEAXgz4DZeAAVJ9tko2St3kaIFxAVt4KZeAD1GKKi7vQSydI/yHgALxAXU02GxniAFBR0v1ev+3V1sZSxjxtM8BM5wTX2/hNu4gFvOMI21iJ3cIwP3GMjF/dQ4RyraOMS34GPAmvjIrBeEnfwjoPGgSM8ooh8QtngB6Ep4BWnmaMqkY1LqqzmDC8tzNDK3/RHzLL9SloUYWfQIMuw3Yl8xrDBH6qbvZWALqbqBqVmlWF7GuKEDwPr5hbXcYdPnKBv/o39wL5wG7ULY1c9NGPzQRrjKrhli1/02zEjWyWMBwAAAABJRU5ErkJggg==":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAEDSURBVDiN1dK/K8VhFAbwD+VLGSxKcu9guSQ/Zils/gNkuaX4BxRZDTdklYU/QAaDlEVGGwu2Kz/uVbKJzWDwfuv1+jHz1Km3c85znuf0Hv4jxnD2W8MItnCJ5xAX2MQcHsOQL+jEAapYQD9aQwxiDy+B3JKSe1DHCpqQYQ0PeMJOpDyAmyAAirjGbDRwFYcoYCZSzjGP+8B1gqXEUT2QxyPlqaRnGceNeENzUswwil1MBocbSU9DCAXUUI6K25HtIo5QSVaooitP9OEO65iIbE+HXSvBVRbeNZQSR9pxGil3o83HNw5hEbfYR0dKFki5ci+u8OrzIQ1/R8xx7ocL+9t4B0HPOVXjoptxAAAAAElFTkSuQmCC"}))):null,a?t.html("div",{class:"tooltip tooltip-left order-4","data-tip":a},t.html("span",{class:"badge badge-ghost badge-xs cursor-help"},"?")):null,()=>n(i)?t.html("span",{class:"text-error text-[10px] absolute -bottom-5 left-2"},n(i)):null])},e.Select=l=>{const{label:e,options:a,value:s,...A}=l,c=t.html("select",{...A,class:o("select select-bordered w-full",l.class||l.class),value:s},t.for((()=>n(a)||[]),(l=>t.html("option",{value:l.value,$selected:()=>String(n(s))===String(l.value)},l.label)),(t=>t.value)));return e?t.html("label",{class:"fieldset-label flex flex-col gap-1"},[t.html("span",{},e),c]):c},e.Autocomplete=l=>{const{options:a=[],value:s,onSelect:o,label:c,placeholder:i,...r}=l,d=t(n(s)||""),m=t(!1),h=t(-1),u=t((()=>{const t=d().toLowerCase(),l=n(a)||[];return t?l.filter((l=>("string"==typeof l?l:l.label).toLowerCase().includes(t))):l})),g=t=>{const l="string"==typeof t?t:t.value,e="string"==typeof t?t:t.label;d(e),"function"==typeof s&&s(l),o?.(t),m(!1),h(-1)};return t.html("div",{class:"relative w-full"},[e.Input({label:c,placeholder:i||A("search")(),value:d,onfocus:()=>m(!0),onblur:()=>setTimeout((()=>m(!1)),150),onkeydown:t=>{const l=u();"ArrowDown"===t.key?(t.preventDefault(),m(!0),h(Math.min(h()+1,l.length-1))):"ArrowUp"===t.key?(t.preventDefault(),h(Math.max(h()-1,0))):"Enter"===t.key&&h()>=0?(t.preventDefault(),g(l[h()])):"Escape"===t.key&&m(!1)},oninput:t=>{const l=t.target.value;d(l),"function"==typeof s&&s(l),m(!0),h(-1)},...r}),t.html("ul",{class:"absolute left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",style:()=>m()&&u().length?"display:block":"display:none"},[t.for(u,((l,e)=>t.html("li",{},[t.html("a",{class:()=>"block w-full "+(h()===e?"active bg-primary text-primary-content":""),onclick:()=>g(l),onmouseenter:()=>h(e)},"string"==typeof l?l:l.label)])),((t,l)=>("string"==typeof t?t:t.value)+l)),()=>u().length?null:t.html("li",{class:"p-2 text-center opacity-50"},"No results")])])},e.Datepicker=l=>{const{value:a,range:s,label:A,placeholder:o,...i}=l,r=t(!1),d=t(new Date),m=t(null),h=()=>!0===n(s),u=new Date,g=`${u.getFullYear()}-${String(u.getMonth()+1).padStart(2,"0")}-${String(u.getDate()).padStart(2,"0")}`,b=t=>`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,p=t=>{const l=b(t),e=n(a);if(h())if(!e?.start||e.start&&e.end)"function"==typeof a&&a({start:l,end:null});else{const t=e.start;"function"==typeof a&&a(l{const t=n(a);return t?"string"==typeof t?t:t.start&&t.end?`${t.start} - ${t.end}`:t.start?`${t.start}...`:"":""})),v=t=>{const l=d();d(new Date(l.getFullYear(),l.getMonth()+t,1))},B=t=>{const l=d();d(new Date(l.getFullYear()+t,l.getMonth(),1))};return t.html("div",{class:"relative w-full"},[e.Input({label:A,placeholder:o||(h()?"Seleccionar rango...":"Seleccionar fecha..."),value:f,readonly:!0,icon:t.html("img",{src:c,class:"opacity-40"}),onclick:t=>{t.stopPropagation(),r(!r())},...i}),t.if(r,(()=>t.html("div",{class:"absolute left-0 mt-2 p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[100] w-80 select-none",onclick:t=>t.stopPropagation()},[t.html("div",{class:"flex justify-between items-center mb-4 gap-1"},[t.html("div",{class:"flex gap-0.5"},[t.html("button",{type:"button",class:"btn btn-ghost btn-xs px-1",onclick:()=>B(-1)},t.html("img",{src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABlSURBVDiN3ZLBDUBAEEUfmtCchA5woUMlOO1FCQrAwbqwf8eFhHd7mfzJn2Tg82TGvABywAmPUgOLD4XcDK9AJ/y5cOlrNsIvpCdPDL/FUbkX/t6Slv3+SjgQf6QBmIAZGAP+FzZJViOd89x8pAAAAABJRU5ErkJggg==",class:"opacity-40"})),t.html("button",{type:"button",class:"btn btn-ghost btn-xs px-1",onclick:()=>v(-1)},t.html("img",{src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABfSURBVDiNY2AY8oCZSHWxDAwMEgwMDHfJsaSAgYHhH9QQsjT/Z2BgKKe75gQGiLMLCSlkwiHOSI6t6ADmhYoBN6SIARIeidgkiUlIxxkYGB4xMDB8YmBguE6JSwYpAACvLRHTKwPjZgAAAABJRU5ErkJggg==",class:"opacity-40"}))]),t.html("span",{class:"font-bold uppercase flex-1 text-center"},[()=>d().toLocaleString("es-ES",{month:"short",year:"numeric"})]),t.html("div",{class:"flex gap-0.5"},[t.html("button",{type:"button",class:"btn btn-ghost btn-xs px-1",onclick:()=>v(1)},t.html("img",{src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABNSURBVDiN3dAxCoAwFATRh3fU2oAHiDbi5Y1F2jT+gKLbzyy7/DYjUo8g4cTWI8koOF6XrOqc5ifDDVGJthfsj8OLujtHYJgwR+GP5QKMxA9/SolDQgAAAABJRU5ErkJggg==",class:"opacity-40"})),t.html("button",{type:"button",class:"btn btn-ghost btn-xs px-1",onclick:()=>B(1)},t.html("img",{src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABmSURBVDiN3dGxCoAgEMbxfz1dL1BTREJzmUv08trgDYcg6VCD3/YD7zvkoLmMgFEegLmmwAAecOJVvNeUWCAAt7IHjt9LThkyiRf9qC8oCom70u0BuDL+bngj/tNm/JqJePucW8wDvGYdzT0nMUkAAAAASUVORK5CYII=",class:"opacity-40"}))])]),t.html("div",{class:"grid grid-cols-7 gap-1",onmouseleave:()=>m(null)},[...["L","M","X","J","V","S","D"].map((l=>t.html("div",{class:"text-[10px] opacity-40 font-bold text-center"},l))),()=>{const l=d(),e=l.getFullYear(),s=l.getMonth(),A=new Date(e,s,1).getDay(),o=0===A?6:A-1,c=new Date(e,s+1,0).getDate(),i=[];for(let l=0;l{const t=n(a),l=m(),e="string"==typeof t?t===o:t?.start===o,s=t?.end===o;let A=!1;if(h()&&t?.start){const e=t.start;!t.end&&l?A=o>e&&o<=l||o=l:t.end&&(A=o>e&&o{h()&&m(o)},onclick:()=>p(A)},[l.toString()]))}return i}])]))),t.if(r,(()=>t.html("div",{class:"fixed inset-0 z-[90]",onclick:()=>r(!1)})))])},e.Colorpicker=l=>{const{value:e,label:a,...s}=l,A=t(!1),o=["#000","#1A1A1A","#333","#4D4D4D","#666","#808080","#B3B3B3","#FFF","#450a0a","#7f1d1d","#991b1b","#b91c1c","#dc2626","#ef4444","#f87171","#fca5a5","#431407","#7c2d12","#9a3412","#c2410c","#ea580c","#f97316","#fb923c","#ffedd5","#713f12","#a16207","#ca8a04","#eab308","#facc15","#fde047","#fef08a","#fff9c4","#064e3b","#065f46","#059669","#10b981","#34d399","#4ade80","#84cc16","#d9f99d","#082f49","#075985","#0284c7","#0ea5e9","#38bdf8","#7dd3fc","#22d3ee","#cffafe","#1e1b4b","#312e81","#4338ca","#4f46e5","#6366f1","#818cf8","#a5b4fc","#e0e7ff","#2e1065","#4c1d95","#6d28d9","#7c3aed","#8b5cf6","#a855f7","#d946ef","#fae8ff"],c=()=>n(e)||"#000000";return t.html("div",{class:"relative w-fit"},[t.html("button",{type:"button",class:"btn px-3 bg-base-100 border-base-300 hover:border-primary/50 flex items-center gap-2 shadow-sm font-normal normal-case",onclick:t=>{t.stopPropagation(),A(!A())},...s},[t.html("div",{class:"size-5 rounded-sm shadow-inner border border-black/10 shrink-0",style:()=>`background-color: ${c()}`}),a?t.html("span",{class:"opacity-80"},a):null]),t.if(A,(()=>t.html("div",{class:"absolute left-0 mt-2 p-3 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[110] w-64 select-none",onclick:t=>t.stopPropagation()},[t.html("div",{class:"grid grid-cols-8 gap-1"},o.map((l=>t.html("button",{type:"button",style:`background-color: ${l}`,class:()=>"size-6 rounded-sm cursor-pointer transition-all hover:scale-125 hover:z-10 active:scale-95 outline-none border border-black/5 \n "+(c().toLowerCase()===l.toLowerCase()?"ring-2 ring-offset-1 ring-primary z-10 scale-110":""),onclick:()=>{e(l),A(!1)}}))))]))),t.if(A,(()=>t.html("div",{class:"fixed inset-0 z-[100]",onclick:()=>A(!1)})))])},e.CheckBox=l=>{const{value:e,tooltip:a,toggle:s,...A}=l,o=t.html("input",{...A,type:"checkbox",class:()=>n(s)?"toggle":"checkbox",checked:e}),c=t.html("label",{class:"label cursor-pointer justify-start gap-3"},[o,l.label?t.html("span",{class:"label-text"},l.label):null]);return a?t.html("div",{class:"tooltip","data-tip":a},c):c},e.Radio=l=>{const{label:e,tooltip:a,value:s,...A}=l,c=t.html("input",{...A,type:"radio",class:o("radio",l.class||l.class),checked:()=>n(s)===s,disabled:()=>n(l.disabled)||n(l.disabled),onclick:()=>"function"==typeof s&&s(s)});if(!e&&!a)return c;const i=t.html("label",{class:"label cursor-pointer justify-start gap-3"},[c,e?t.html("span",{class:"label-text"},e):null]);return a?t.html("div",{class:"tooltip","data-tip":a},i):i},e.Range=l=>{const{label:e,tooltip:a,value:s,...A}=l,c=t.html("input",{...A,type:"range",class:o("range",l.class),value:s,disabled:()=>n(l.disabled)});if(!e&&!a)return c;const i=t.html("div",{class:"flex flex-col gap-2"},[e?t.html("span",{class:"label-text"},e):null,c]);return a?t.html("div",{class:"tooltip","data-tip":a},i):i},e.Modal=(l,a)=>{const{title:s,buttons:n,open:o,...c}=l,i=()=>o(!1);return t.if(o,(()=>t.html("dialog",{...c,class:"modal modal-open"},[t.html("div",{class:"modal-box"},[s?t.html("h3",{class:"text-lg font-bold mb-4"},s):null,"function"==typeof a?a():a,t.html("div",{class:"modal-action flex gap-2"},[...(Array.isArray(n)?n:[n]).filter(Boolean),e.Button({onclick:i},A("close")())])]),t.html("form",{method:"dialog",class:"modal-backdrop",onclick:t=>(t.preventDefault(),i())},[t.html("button",{},"close")])])))},e.Grid=l=>{const{data:e,options:a,class:s}=l;let A=null;const o=t.html("div",{style:"height: 100%; width: 100%;",class:s}),c=new MutationObserver((()=>{A&&A.setGridOption("theme",getTheme(isDark()))}));c.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme"]}),o._cleanups.add((()=>c.disconnect()));const i=t.watch((()=>{const t=isDark(),l=getTheme(t),s=n(e)||[];A?A.setGridOption("theme",l):A=createGrid(o,{...n(a)||{},theme:l,rowData:s})}));o._cleanups.add(i);const r=t.watch((()=>{const t=n(e);A&&Array.isArray(t)&&A.setGridOption("rowData",t)}));return o._cleanups.add(r),o._cleanups.add((()=>{A&&(A.destroy(),A=null)})),o},e.Dropdown=(l,e)=>{const{label:a,icon:s,...A}=l;return t.html("div",{...A,class:()=>`dropdown ${n(l.class)||l.class||""}`},[t.html("div",{tabindex:0,role:"button",class:"btn m-1 flex items-center gap-2"},[s?"function"==typeof s?s():s:null,a?"function"==typeof a?a():a:null]),t.html("ul",{tabindex:0,class:"dropdown-content z-[50] menu p-2 shadow bg-base-100 rounded-box min-w-max border border-base-300"},["function"==typeof e?e():e])])},e.Accordion=(l,e)=>{const{title:a,name:s,open:A,...n}=l;return t.html("div",{...n,class:o("collapse collapse-arrow bg-base-200 mb-2",l.class||l.class)},[t.html("input",{type:s?"radio":"checkbox",name:s,checked:A}),t.html("div",{class:"collapse-title text-xl font-medium"},a),t.html("div",{class:"collapse-content"},e)])},e.Tabs=l=>{const{items:e,...a}=l,s="function"==typeof e?e:()=>e||[];return t.html("div",{...a,class:"flex flex-col gap-4 w-full"},[t.html("div",{role:"tablist",class:o("tabs tabs-box",l.class||l.class)},t.for(s,(l=>t.html("a",{role:"tab",class:()=>o("tab",n(l.active)&&"tab-active",n(l.disabled),l.tip),"data-tip":l.tip,onclick:t=>!n(l.disabled)&&l.onclick?.(t)},l.label)),(t=>t.label))),()=>{const l=s().find((t=>n(t.active)));if(!l)return null;const e=n(l.content);return t.html("div",{class:"p-4"},["function"==typeof e?e():e])}])},e.Badge=(l,e)=>t.html("span",{...l,class:o("badge",l.class||l.class)},e),e.Tooltip=(l,e)=>t.html("div",{...l,class:o("tooltip",l.class||l.class),"data-tip":l.tip},e),e.Navbar=(l,e)=>t.html("div",{...l,class:o("navbar bg-base-100 shadow-sm px-4",l.class||l.class)},e),e.Menu=l=>{const e=l=>t.for((()=>l||[]),(l=>t.html("li",{},[l.children?t.html("details",{open:l.open},[t.html("summary",{},[l.icon&&t.html("span",{class:"mr-2"},l.icon),l.label]),t.html("ul",{},e(l.children))]):t.html("a",{class:()=>n(l.active)?"active":"",onclick:l.onclick},[l.icon&&t.html("span",{class:"mr-2"},l.icon),l.label])])),((t,l)=>t.label||l));return t.html("ul",{...l,class:o("menu bg-base-200 rounded-box",l.class||l.class)},e(l.items))},e.Drawer=l=>t.html("div",{class:o("drawer",l.class||l.class)},[t.html("input",{id:l.id,type:"checkbox",class:"drawer-toggle",checked:l.open}),t.html("div",{class:"drawer-content"},l.content),t.html("div",{class:"drawer-side"},[t.html("label",{for:l.id,class:"drawer-overlay",onclick:()=>l.open?.(!1)}),t.html("div",{class:"min-h-full bg-base-200 w-80"},l.side)])]),e.Fieldset=(l,e)=>t.html("fieldset",{...l,class:o("fieldset bg-base-200 border border-base-300 p-4 rounded-lg",l.class||l.class)},[()=>{const e=n(l.legend);return e?t.html("legend",{class:"fieldset-legend font-bold"},[e]):null},e]),e.List=l=>{const{items:e,header:a,render:s,keyFn:A,class:c}=l;return t.html("ul",{class:o("list bg-base-100 rounded-box shadow-md",c)},[t.if(a,(()=>t.html("li",{class:"p-4 pb-2 text-xs opacity-60 tracking-wide"},[n(a)]))),t.for(e,((l,e)=>t.html("li",{class:"list-row"},[s(l,e)])),A)])},e.Stack=(l,e)=>t.html("div",{...l,class:o("stack",l.class||l.class)},e),e.Stat=l=>t.html("div",{...l,class:o("stat",l.class||l.class)},[l.icon&&t.html("div",{class:"stat-figure text-secondary"},l.icon),l.label&&t.html("div",{class:"stat-title"},l.label),t.html("div",{class:"stat-value"},(()=>n(l.value)??l.value)),l.desc&&t.html("div",{class:"stat-desc"},l.desc)]),e.Swap=l=>t.html("label",{class:o("swap",l.class||l.class)},[t.html("input",{type:"checkbox",checked:l.value}),t.html("div",{class:"swap-on"},l.on),t.html("div",{class:"swap-off"},l.off)]),e.Indicator=(l,e)=>t.html("div",{class:o("indicator",l.class||l.class)},[e,t.html("span",{class:o("indicator-item badge",l.badgeClass)},l.badge)]),e.Rating=l=>{const{value:e,count:a=5,mask:s="mask-star",readonly:A=!1,...o}=l,c=`rating-${Math.random().toString(36).slice(2,7)}`;return t.html("div",{...o,class:()=>`rating ${n(A)?"pointer-events-none":""} ${l.class||""}`},Array.from({length:n(a)},((l,a)=>{const o=a+1;return t.html("input",{type:"radio",name:c,class:`mask ${s}`,"aria-label":`${o} star`,checked:()=>Math.round(n(e))===o,onchange:()=>{n(A)||"function"!=typeof e||e(o)}})})))},e.Alert=(l,e)=>{const{type:a="info",soft:s=!0,...A}=l,o={info:i,success:r,warning:m,error:d},c=e||l.message;return t.html("div",{...A,role:"alert",class:()=>`alert ${(()=>{const t=n(a);return{info:"alert-info",success:"alert-success",warning:"alert-warning",error:"alert-error"}[t]||t})()} ${n(s)?"alert-soft":""} ${l.class||""}`},[t.html("img",{src:o[n(a)]||o.info,class:"w-4 h-4 object-contain",alt:n(a)}),t.html("div",{class:"flex-1"},[t.html("span",{},["function"==typeof c?c():c])]),l.actions?t.html("div",{class:"flex-none"},["function"==typeof l.actions?l.actions():l.actions]):null])},e.Timeline=l=>{const{items:e=[],vertical:a=!0,compact:s=!1,...A}=l,o={info:i,success:r,warning:m,error:d};return t.html("ul",{...A,class:()=>`timeline ${n(a)?"timeline-vertical":"timeline-horizontal"} ${n(s)?"timeline-compact":""} ${l.class||""}`},[t.for(e,((l,a)=>{const s=0===a,A=a===n(e).length-1,c=l.type||"success",i=t=>"function"==typeof t?t():t;return t.html("li",{class:"flex-1"},[s?null:t.html("hr",{class:l.completed?"bg-primary":""}),t.html("div",{class:"timeline-start"},[i(l.title)]),t.html("div",{class:"timeline-middle"},[t.html("img",{src:o[c]||l.icon||o.success,class:"w-4 h-4 object-contain mx-1",alt:c})]),t.html("div",{class:"timeline-end timeline-box shadow-sm"},[i(l.detail)]),A?null:t.html("hr",{class:l.completed?"bg-primary":""})])}),((t,l)=>t.id||l))])},e.Fab=l=>{const{icon:e,label:a,actions:s=[],position:A="bottom-6 right-6",...o}=l;return t.html("div",{...o,class:()=>`fab fixed ${n(A)} flex flex-col-reverse items-end gap-3 z-[100] ${l.class||""}`},[t.html("div",{tabindex:0,role:"button",class:"btn btn-lg btn-circle btn-primary shadow-2xl"},[e?"function"==typeof e?e():e:null,!e&&a?a:null]),...n(s).map((l=>t.html("div",{class:"flex items-center gap-3 transition-all duration-300"},[l.label?t.html("span",{class:"badge badge-ghost shadow-sm whitespace-nowrap"},l.label):null,t.html("button",{type:"button",class:`btn btn-circle shadow-lg ${l.class||""}`,onclick:t=>{t.stopPropagation(),l.onclick?.(t)}},[l.icon?"function"==typeof l.icon?l.icon():l.icon:l.text||""])])))])},e.Toast=(l,a="alert-success",s=3500)=>{let A=document.getElementById("sigpro-toast-container");A||(A=t.html("div",{id:"sigpro-toast-container",class:"fixed top-0 right-0 z-[9999] p-4 flex flex-col gap-2 pointer-events-none"}),document.body.appendChild(A));const n=t.html("div",{style:"display: contents"});let o;A.appendChild(n);const c=()=>{clearTimeout(o);const t=n.firstElementChild;t&&!t.classList.contains("opacity-0")?(t.classList.add("translate-x-full","opacity-0"),setTimeout((()=>{i.destroy(),n.remove(),A.hasChildNodes()||A.remove()}),300)):(i.destroy(),n.remove())},i=t.mount((()=>{const s=t.html("div",{class:`alert alert-soft ${a} shadow-lg transition-all duration-300 translate-x-10 opacity-0 pointer-events-auto`},[t.html("span","function"==typeof l?l:()=>l),e.Button({class:"btn-xs btn-circle btn-ghost",onclick:c},"✕")]);return requestAnimationFrame((()=>s.classList.remove("translate-x-10","opacity-0"))),s}),n);return s>0&&(o=setTimeout(c,s)),c},e.Loading=l=>t.if(l.$show,(()=>t.html("div",{class:"fixed inset-0 z-[100] flex items-center justify-center backdrop-blur-sm bg-base-100/30"},[t.html("span",{class:"loading loading-spinner loading-lg text-primary"})]))),e.tt=A,Object.keys(e).forEach((l=>{t[l]=e[l],Object.defineProperty(window,l,{value:e[l],writable:!1,configurable:!0,enumerable:!0})})),e}; \ No newline at end of file diff --git a/docs/404.html b/docs/404.html index fbc591c..f1feb24 100644 --- a/docs/404.html +++ b/docs/404.html @@ -17,7 +17,7 @@
- + \ No newline at end of file diff --git a/docs/api/for.html b/docs/api/for.html index c1facbc..9643960 100644 --- a/docs/api/for.html +++ b/docs/api/for.html @@ -3,7 +3,7 @@ - ♻️ Reactive Lists: $.for( ) | SigPro + ♻️ Reactive Lists: $for( ) | SigPro @@ -13,13 +13,13 @@ - + -
Skip to content

♻️ Reactive Lists: $.for( )

The $.for function is a high-performance list renderer. It maps an array (or a Signal containing an array) to DOM nodes. Unlike a simple .map(), $.for is keyed, meaning it only updates, moves, or deletes the specific items that changed.

🛠️ Function Signature

typescript
$.for(
+    
Skip to content

♻️ Reactive Lists: $for( )

The $for function is a high-performance list renderer. It maps an array (or a Signal containing an array) to DOM nodes. Unlike a simple .map(), $for is keyed, meaning it only updates, moves, or deletes the specific items that changed.

🛠️ Function Signature

typescript
$for(
   source: Signal<any[]> | Function | any[], 
   render: (item: any, index: number) => HTMLElement, 
   keyFn: (item: any, index: number) => string | number
@@ -29,16 +29,16 @@
 ]);
 
 Ul({ class: "list" }, [
-  $.for(users, 
+  $for(users, 
     (user) => Li({ class: "p-2" }, user.name),
     (user) => user.id
   )
 ]);

2. Handling Primitive Arrays

If your array contains simple strings or numbers, you can use the value itself or the index as a key (though the index is less efficient for reordering).

javascript
const tags = $(["Tech", "JS", "Web"]);
 
 Div({ class: "flex gap-1" }, [
-  $.for(tags, (tag) => Badge(tag), (tag) => tag)
-]);

🏗️ How it Works (The Reconciliation)

When the source signal changes, $.for performs the following steps:

  1. Key Diffing: It compares the new keys with the previous ones stored in an internal Map.
  2. Node Reuse: If a key already exists, the DOM node is reused and moved to its new position. No new elements are created.
  3. Cleanup: If a key disappears from the list, SigPro calls .destroy() on that specific item's instance. This stops all its internal watchers and removes its DOM nodes.

💡 Performance Tips

  • Stable Keys: Never use Math.random() as a key. This will force SigPro to destroy and recreate the entire list on every update, killing performance.
  • Component Encapsulation: If each item in your list has its own complex internal state, $.for ensures that state is preserved even if the list is reordered, as long as the key remains the same.

🧪 Summary Comparison

FeatureStandard Array.mapSigPro $.for
Re-renderRe-renders everythingOnly updates changes
DOM NodesRe-created every timeReused via Keys
MemoryPotential leaksAutomatic Cleanup
StateLost on re-renderPreserved per item
- + $for(tags, (tag) => Badge(tag), (tag) => tag) +]);

🏗️ How it Works (The Reconciliation)

When the source signal changes, $for performs the following steps:

  1. Key Diffing: It compares the new keys with the previous ones stored in an internal Map.
  2. Node Reuse: If a key already exists, the DOM node is reused and moved to its new position. No new elements are created.
  3. Cleanup: If a key disappears from the list, SigPro calls .destroy() on that specific item's instance. This stops all its internal watchers and removes its DOM nodes.

💡 Performance Tips

  • Stable Keys: Never use Math.random() as a key. This will force SigPro to destroy and recreate the entire list on every update, killing performance.
  • Component Encapsulation: If each item in your list has its own complex internal state, $for ensures that state is preserved even if the list is reordered, as long as the key remains the same.

🧪 Summary Comparison

FeatureStandard Array.mapSigPro $for
Re-renderRe-renders everythingOnly updates changes
DOM NodesRe-created every timeReused via Keys
MemoryPotential leaksAutomatic Cleanup
StateLost on re-renderPreserved per item
+ \ No newline at end of file diff --git a/docs/api/html.html b/docs/api/html.html index afae41c..94be151 100644 --- a/docs/api/html.html +++ b/docs/api/html.html @@ -3,7 +3,7 @@ - 🏗️ The DOM Factory: $.html( ) | SigPro + 🏗️ The DOM Factory: $html( ) | SigPro @@ -13,37 +13,31 @@ - + -
Skip to content

🏗️ The DOM Factory: $.html( )

$.html is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It uses $.watch to maintain a live, high-performance link between your Signals and the Document Object Model.

🛠 Function Signature

typescript
$.html(tagName: string, props?: Object, children?: any[] | any): HTMLElement
ParameterTypeRequiredDescription
tagNamestringYesValid HTML tag name (e.g., "div", "button").
propsObjectNoHTML attributes, event listeners, and reactive bindings.
childrenanyNoNested elements, text strings, or reactive functions.

📖 Key Features

1. Attribute Handling

SigPro intelligently decides how to apply each property:

  • Standard Props: Applied via setAttribute or direct property assignment.
  • Boolean Props: Uses toggleAttribute (e.g., checked, disabled, hidden).
  • Class Names: Supports class or className interchangeably.

2. Event Listeners & Modifiers

Events are defined by the on prefix. SigPro supports Dot Notation for common event operations:

javascript
$.html("button", {
-  // e.preventDefault() is called automatically
-  "onsubmit.prevent": (e) => save(e), 
-  
-  // e.stopPropagation() is called automatically
-  "onclick.stop": () => console.log("No bubbling"),
-  
-  // { once: true } listener option
-  "onclick.once": () => console.log("Runs only once")
-}, "Click Me");

3. Reactive Attributes

If an attribute value is a function (like a Signal), $.html creates an internal $.watch to keep the DOM in sync with the state.

javascript
$.html("div", {
+    
Skip to content

🏗️ The DOM Factory: $html( )

$html is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It uses $.watch to maintain a live, high-performance link between your Signals and the Document Object Model.

🛠 Function Signature

typescript
$html(tagName: string, props?: Object, children?: any[] | any): HTMLElement
ParameterTypeRequiredDescription
tagNamestringYesValid HTML tag name (e.g., "div", "button").
propsObjectNoHTML attributes, event listeners, and reactive bindings.
childrenanyNoNested elements, text strings, or reactive functions.

📖 Key Features

1. Attribute Handling

SigPro intelligently decides how to apply each property:

  • Standard Props: Applied via setAttribute or direct property assignment.
  • Class Names: Supports class or className interchangeably.
  • Boolean Props: Automatic handling for checked, disabled, hidden, etc.

2. Event Listeners

Events are defined by the on prefix. SigPro automatically registers the listener and ensures it is cleaned up when the element is destroyed.

javascript
Button({
+  onclick: (e) => console.log("Clicked!", e),
+}, "Click Me");

3. Reactive Attributes (One-Way)

If an attribute value is a function (like a Signal), $html creates an internal $.watch to keep the DOM in sync with the state.

javascript
Div({
   // Updates the class whenever 'theme()' changes
   class: () => theme() === "dark" ? "bg-black" : "bg-white"
-});

4. Reactive Children

Children can be static or dynamic. When a child is a function, SigPro creates a reactive boundary using $.watch for that specific part of the DOM.

javascript
$.html("div", {}, [
+});

4. Smart Two-Way Binding (Automatic)

SigPro automatically enables bidirectional synchronization when it detects a Signal assigned to a form-capable attribute (value or checked) on an input element (input, textarea, select).

javascript
// Syncs input value <-> signal automatically
+Input({ 
+  type: "text", 
+  value: username // No special symbols needed!
+})

Note: To use a Signal as read-only in an input, wrap it in an anonymous function: value: () => username().

5. Reactive Children

Children can be static or dynamic. When a child is a function, SigPro creates a reactive boundary using $.watch for that specific part of the DOM.

javascript
Div({}, [
   H1("Static Title"),
   // Only this text node re-renders when 'count' changes
   () => `Current count: ${count()}`
-]);

🔄 Two-Way Binding Operator ($)

When a property starts with $, $.html enables bidirectional synchronization. This is primarily used for form inputs.

javascript
$.html("input", {
-  type: "text",
-  $value: username // Syncs input value <-> signal
-});

🧹 Memory Management (Internal)

Every element created with $.html is "self-aware" regarding its reactive dependencies.

  • ._cleanups: A hidden Set attached to the element that stores all stop() functions from its internal $.watch calls and event listeners.
  • Lifecycle: When an element is removed by a Controller ($.If, $.For, or $.router), SigPro performs a recursive "sweep" to execute these cleanups, ensuring zero memory leaks.

💡 Tag Constructors (The Shortcuts)

Instead of writing $.html("div", ...) every time, SigPro provides PascalCase global functions:

javascript
// This:
+]);

🧹 Memory Management (Internal)

Every element created with $html is "self-aware" regarding its reactive dependencies.

  • ._cleanups: A hidden Set attached to the element that stores all stop() functions from its internal $.watch calls and event listeners.
  • Lifecycle: When an element is removed by a Controller ($.if, $.for, or $.router), SigPro performs a recursive "sweep" to execute these cleanups, ensuring zero memory leaks.

💡 Tag Constructors (The Shortcuts)

Instead of writing $html("div", ...) every time, SigPro provides PascalCase global functions for all standard HTML tags:

javascript
// This:
 Div({ class: "wrapper" }, [ Span("Hello") ])
 
 // Is exactly equivalent to:
-$.html("div", { class: "wrapper" }, [ $.html("span", {}, "Hello") ])
- +$html("div", { class: "wrapper" }, [ $html("span", {}, "Hello") ])
+ \ No newline at end of file diff --git a/docs/api/if.html b/docs/api/if.html index 6503b06..9fab53a 100644 --- a/docs/api/if.html +++ b/docs/api/if.html @@ -3,7 +3,7 @@ - 🔀 Reactive Branching: $.if( ) | SigPro + 🔀 Reactive Branching: $if( ) | SigPro @@ -13,13 +13,13 @@ - + -
Skip to content

🔀 Reactive Branching: $.if( )

The $.if function is a reactive control flow operator. It manages the conditional rendering of components, ensuring that only the active branch exists in the DOM and in memory.

🛠️ Function Signature

typescript
$.if(
+    
Skip to content

🔀 Reactive Branching: $if( )

The $if function is a reactive control flow operator. It manages the conditional rendering of components, ensuring that only the active branch exists in the DOM and in memory.

🛠️ Function Signature

typescript
$if(
   condition: Signal<boolean> | Function, 
   thenVal: Component | Node, 
   otherwiseVal?: Component | Node
@@ -28,17 +28,17 @@
 Div([
   Button({ onclick: () => isVisible(!isVisible()) }, "Toggle Message"),
   
-  $.if(isVisible, 
+  $if(isVisible, 
     P("Now you see me!"), 
     P("Now you don't...")
   )
-]);

2. Lazy Component Loading

Unlike using a hidden class (CSS display: none), $.if is lazy. The branch that isn't active is never created. This saves memory and initial processing time.

javascript
$.if(() => user.isLogged(), 
+]);

2. Lazy Component Loading

Unlike using a hidden class (CSS display: none), $if is lazy. The branch that isn't active is never created. This saves memory and initial processing time.

javascript
$if(() => user.isLogged(), 
   () => Dashboard(), // Only executed if logged in
   () => LoginGate()  // Only executed if guest
-)

🧹 Automatic Cleanup

One of the core strengths of $.if is its integrated Cleanup logic. SigPro ensures that when a branch is swapped out, it is completely purged.

  1. Stop Watchers: All $.watch calls inside the inactive branch are permanently stopped.
  2. Unbind Events: Event listeners attached via $.html are removed.
  3. Recursive Sweep: SigPro performs a deep "sweep" of the removed branch to ensure no nested reactive effects remain active.

💡 Best Practices

  • Function Wrappers: If your branches are heavy (e.g., they contain complex components), wrap them in a function () => MyComponent(). This prevents the component from being initialized until the condition actually meets its requirement.
  • Logical Expressions: You can pass a complex computed function as the condition:
    javascript
    $.if(() => count() > 10 && status() === 'ready', 
    +)

    🧹 Automatic Cleanup

    One of the core strengths of $if is its integrated Cleanup logic. SigPro ensures that when a branch is swapped out, it is completely purged.

    1. Stop Watchers: All $.watch calls inside the inactive branch are permanently stopped.
    2. Unbind Events: Event listeners attached via $.html are removed.
    3. Recursive Sweep: SigPro performs a deep "sweep" of the removed branch to ensure no nested reactive effects remain active.

    💡 Best Practices

    • Function Wrappers: If your branches are heavy (e.g., they contain complex components), wrap them in a function () => MyComponent(). This prevents the component from being initialized until the condition actually meets its requirement.
    • Logical Expressions: You can pass a complex computed function as the condition:
      javascript
      $if(() => count() > 10 && status() === 'ready', 
         Span("Threshold reached!")
      -)

    🏗️ Technical Comparison

    FeatureStandard CSS hiddenSigPro $.if
    DOM PresenceAlways presentOnly if active
    ReactivityStill processing in backgroundPaused/Destroyed
    Memory usageHigherOptimized
    CleanupManualAutomatic
- +)

🏗️ Technical Comparison

FeatureStandard CSS hiddenSigPro $if
DOM PresenceAlways presentOnly if active
ReactivityStill processing in backgroundPaused/Destroyed
Memory usageHigherOptimized
CleanupManualAutomatic
+ \ No newline at end of file diff --git a/docs/api/mount.html b/docs/api/mount.html index a535543..b6e7a1c 100644 --- a/docs/api/mount.html +++ b/docs/api/mount.html @@ -3,7 +3,7 @@ - 🔌 Application Mounter: $.mount( ) | SigPro + 🔌 Application Mounter: $mount( ) | SigPro @@ -13,17 +13,17 @@ - + -
Skip to content

🔌 Application Mounter: $.mount( )

The $.mount function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document and initializing its reactive lifecycle.

🛠️ Function Signature

typescript
$.mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject
ParameterTypeDefaultDescription
nodeFunction or NodeRequiredThe component function or DOM element to render.
targetstring or Nodedocument.bodyCSS selector or DOM element where the app will live.

Returns: A Runtime object containing the container and a destroy() method to wipe all reactivity and DOM nodes.


📖 Common Usage Scenarios

1. The SPA Entry Point

In a Single Page Application, you typically mount your main component to the body or a root div. SigPro manages the entire view from that point.

javascript
import { $ } from './sigpro.js';
+    
Skip to content

🔌 Application Mounter: $mount( )

The $mount function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document and initializing its reactive lifecycle.

🛠️ Function Signature

typescript
$mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject
ParameterTypeDefaultDescription
nodeFunction or NodeRequiredThe component function or DOM element to render.
targetstring or Nodedocument.bodyCSS selector or DOM element where the app will live.

Returns: A Runtime object containing the container and a destroy() method to wipe all reactivity and DOM nodes.


📖 Common Usage Scenarios

1. The SPA Entry Point

In a Single Page Application, you typically mount your main component to the body or a root div. SigPro manages the entire view from that point.

javascript
import { $ } from './sigpro.js';
 import App from './App.js';
 
 // Mounts your main App component
-$.mount(App, '#app-root');

2. Reactive "Islands"

SigPro is perfect for adding reactivity to static pages. You can mount small widgets into specific parts of an existing HTML layout.

javascript
const Counter = () => {
+$mount(App, '#app-root');

2. Reactive "Islands"

SigPro is perfect for adding reactivity to static pages. You can mount small widgets into specific parts of an existing HTML layout.

javascript
const Counter = () => {
   const count = $(0);
   return Button({ onclick: () => count(c => c + 1) }, [
     "Clicks: ", count
@@ -31,11 +31,11 @@
 };
 
 // Mount only the counter into a specific sidebar div
-$.mount(Counter, '#sidebar-widget');

🔄 How it Works (Lifecycle & Cleanup)

When $.mount is executed, it performs these critical steps to ensure a leak-free environment:

  1. Duplicate Detection: If you call $.mount on a target that already has a SigPro instance, it automatically calls .destroy() on the previous instance. This prevents "Zombie Effects" from stacking in memory.
  2. Internal Scoping: It executes the component function inside an internal Reactive Owner. This captures every $.watch and event listener created during the render.
  3. Target Injection: It clears the target using replaceChildren() and appends the new component.
  4. Runtime Creation: It returns a control object:
    • container: The actual DOM element created.
    • destroy(): The "kill switch" that runs all cleanups, stops all watchers, and removes the element from the DOM.

🛑 Manual Unmounting

While SigPro handles most cleanups automatically (via $.If, $.For, and $.router), you can manually destroy any mounted instance. This is vital for imperatively managed UI like Toasts or Modals.

javascript
const instance = $.mount(MyToast, '#toast-container');
+$mount(Counter, '#sidebar-widget');

🔄 How it Works (Lifecycle & Cleanup)

When $mount is executed, it performs these critical steps to ensure a leak-free environment:

  1. Duplicate Detection: If you call $mount on a target that already has a SigPro instance, it automatically calls .destroy() on the previous instance. This prevents "Zombie Effects" from stacking in memory.
  2. Internal Scoping: It executes the component function inside an internal Reactive Owner. This captures every $watch and event listener created during the render.
  3. Target Injection: It clears the target using replaceChildren() and appends the new component.
  4. Runtime Creation: It returns a control object:
    • container: The actual DOM element created.
    • destroy(): The "kill switch" that runs all cleanups, stops all watchers, and removes the element from the DOM.

🛑 Manual Unmounting

While SigPro handles most cleanups automatically (via $if, $for, and $router), you can manually destroy any mounted instance. This is vital for imperatively managed UI like Toasts or Modals.

javascript
const instance = $mount(MyToast, '#toast-container');
 
 // Later, to remove the toast and kill its reactivity:
-instance.destroy();

💡 Summary Cheat Sheet

GoalCode Pattern
Mount to body$.mount(App)
Mount to CSS Selector$.mount(App, '#root')
Mount to DOM Node$.mount(App, myElement)
Clean & Re-mountCalling $.mount again on the same target
Total Cleanupconst app = $.mount(App); app.destroy();
- +instance.destroy();

💡 Summary Cheat Sheet

GoalCode Pattern
Mount to body$mount(App)
Mount to CSS Selector$mount(App, '#root')
Mount to DOM Node$mount(App, myElement)
Clean & Re-mountCalling $mount again on the same target
Total Cleanupconst app = $mount(App); app.destroy();
+ \ No newline at end of file diff --git a/docs/api/quick.html b/docs/api/quick.html index 798d2ee..44b09c0 100644 --- a/docs/api/quick.html +++ b/docs/api/quick.html @@ -13,14 +13,14 @@ - + -
Skip to content

⚡ Quick API Reference

SigPro is a high-performance micro-framework that updates the Real DOM surgically. No Virtual DOM, no unnecessary re-renders, and built-in Cleanup (memory cleanup).

🟢 Core Functions

FunctionSignatureDescription
$(val, key?)(any, string?) => SignalCreates a Signal. If key is provided, it persists in localStorage.
$(fn)(function) => ComputedCreates a Computed Signal that auto-updates when its dependencies change.
$.watch(fn)(function) => stopFnAutomatic Mode: Tracks any signal touched inside. Returns a stop function.
$.watch(deps, fn)(Array, function) => stopFnExplicit Mode: Only runs when signals in deps change. Used for Cleanup.
$.html(tag, props, kids)(string, obj, any) => ElementThe low-level DOM factory. Attaches ._cleanups to every element.
$.if(cond, then, else?)(Signal, fn, fn?) => NodeReactive conditional. Automatically destroys the "else" branch memory.
$.for(list, itemFn)(Signal, fn) => NodeOptimized list renderer. Manages individual item lifecycles.
$.router(routes)(Array) => ElementHash-based SPA router. Uses Explicit Watch to prevent memory leaks.
$.mount(node, target)(any, string|Node) => RuntimeEntry point. Creates a root instance with .destroy() capabilities.

🏗️ Element Constructors (Tags)

SigPro provides PascalCase wrappers for all standard HTML5 tags (e.g., Div, Span, Button).

Syntax Pattern

javascript
Tag({ attributes }, [children])

Attribute & Content Handling

PatternCode ExampleBehavior
Staticclass: "text-red"Standard HTML attribute string.
Reactivedisabled: isLoadingUpdates automatically via internal $.watch.
Two-way$value: usernameBinding Operator: Syncs input $\leftrightarrow$ signal both ways.
TextP({}, () => count())Updates text node surgically without re-rendering the P.
Booleanhidden: isHiddenToggles the attribute based on signal truthiness.
- +
Skip to content

⚡ Quick API Reference

SigPro is a high-performance micro-framework that updates the Real DOM surgically. No Virtual DOM, no unnecessary re-renders, and built-in Cleanup (memory cleanup).

🟢 Core Functions

FunctionSignatureDescription
$(val, key?)(any, string?) => SignalCreates a Signal. If key is provided, it persists in localStorage.
$(fn)(function) => ComputedCreates a Computed Signal that auto-updates when its dependencies change.
$watch(fn)(function) => stopFnAutomatic Mode: Tracks any signal touched inside. Returns a stop function.
$watch(deps, fn)(Array, function) => stopFnExplicit Mode: Only runs when signals in deps change. Used for Cleanup.
$html(tag, props, kids)(string, obj, any) => ElementThe low-level DOM factory. Attaches ._cleanups to every element.
$if(cond, then, else?)(Signal, fn, fn?) => NodeReactive conditional. Automatically destroys the "else" branch memory.
$for(list, itemFn)(Signal, fn) => NodeOptimized list renderer. Manages individual item lifecycles.
$router(routes)(Array) => ElementHash-based SPA router. Uses Explicit Watch to prevent memory leaks.
$mount(node, target)(any, string|Node) => RuntimeEntry point. Creates a root instance with .destroy() capabilities.

🏗️ Element Constructors (Tags)

SigPro provides PascalCase wrappers for all standard HTML5 tags (e.g., Div, Span, Button).

Syntax Pattern

javascript
Tag({ attributes }, [children])

Attribute & Content Handling

PatternCode ExampleBehavior
Staticclass: "text-red"Standard HTML attribute string.
Reactivedisabled: isLoadingUpdates automatically via internal $watch.
Two-way$value: usernameBinding Operator: Syncs input $\leftrightarrow$ signal both ways.
TextP({}, () => count())Updates text node surgically without re-rendering the P.
Booleanhidden: isHiddenToggles the attribute based on signal truthiness.
+ \ No newline at end of file diff --git a/docs/api/router.html b/docs/api/router.html index 0ccbd9a..9749e83 100644 --- a/docs/api/router.html +++ b/docs/api/router.html @@ -3,7 +3,7 @@ - 🚦 Routing: $.router( ) & $.go( ) | SigPro + 🚦 Routing: $router() & Utilities | SigPro @@ -13,35 +13,36 @@ - + -
Skip to content

🚦 Routing: $.router( ) & $.go( )

SigPro includes a built-in, lightweight Hash Router to create Single Page Applications (SPA). It manages the URL state, matches components to paths, and handles the lifecycle of your pages automatically.

🛠 Router Signature

typescript
$.router(routes: Route[]): HTMLElement

Route Object

PropertyTypeDescription
pathstringThe URL fragment (e.g., "/home", "/user/:id", or "*").
componentFunctionA function that returns a Tag or a $.view.

📖 Usage Patterns

1. Defining Routes

The router returns a div element with the class .router-outlet. When the hash changes, the router destroys the previous view and mounts the new one inside this container.

javascript
const App = () => Div({ class: "app-layout" }, [
+    
Skip to content

🚦 Routing: $router() & Utilities

SigPro includes a built-in, lightweight Hash Router to create Single Page Applications (SPA). It manages the URL state, matches components to paths, and handles the lifecycle of your pages automatically.

🛠 Router Signature

typescript
$router(routes: Route[]): HTMLElement

Route Object

PropertyTypeDescription
pathstringThe URL fragment (e.g., "/", "/user/:id", or "*").
componentFunctionA function that returns a Node, a String, or a reactive View.

📖 Usage Patterns

1. Defining Routes

The $router returns a div element with the class .router-outlet. When the hash changes, the router destroys the previous view and mounts the new one inside this container.

javascript
const App = () => Div({ class: "app-layout" }, [
   Navbar(),
   // The router outlet is placed here
-  $.router([
+  $router([
     { path: "/", component: Home },
-    { path: "/profile/:id", component: UserProfile },
-    { path: "*", component: NotFound }
+    { path: "/profile/:id", component: (params) => UserProfile(params.id) },
+    { path: "*", component: () => H1("404 Not Found") }
   ])
-]);

2. Dynamic Segments (:id)

When a path contains a colon (e.g., :id), the router parses that segment and passes it as an object to the component function.

javascript
// If the URL is #/profile/42
+]);

2. Dynamic Segments (:id)

The router automatically parses URL parameters (like :id) and passes them as an object to the component function. You can also access them globally via $router.params().

javascript
// If the URL is #/profile/42
 const UserProfile = (params) => {
   return H1(`User ID is: ${params.id}`); // Displays "User ID is: 42"
-};

🏎 Programmatic Navigation: $.go( )

To navigate between pages without using an <a> tag, use $.go. This function updates the browser's hash, which in turn triggers the router to swap components.

Signature

typescript
$.go(path: string): void

Examples

javascript
// Navigate to a static path
-Button({ onclick: () => $.go("/") }, "Home")
+};

🏎 Navigation Utilities

SigPro provides a set of programmatic methods to control the history and read the state.

$router.to(path)

Navigates to a specific path. It automatically formats the hash (e.g., /home becomes #/home).

javascript
Button({ onclick: () => $router.to("/dashboard") }, "Go to Dashboard")

$router.back()

Goes back to the previous page in the browser history.

javascript
Button({ onclick: () => $router.back() }, "Back")

$router.path()

Returns the current path (without the #).

javascript
$watch(() => {
+  console.log("Current path is:", $router.path());
+});

⚡ Technical Behavior

  • Automatic Cleanup: Every time you navigate, the router calls .destroy() on the previous view. This ensures that all signals, effects, and event listeners from the old page are purged from memory.
  • Reactive Params: $router.params is a signal ($). You can react to parameter changes without re-mounting the entire router outlet.
  • Hash-Based: By using window.location.hash, SigPro works out-of-the-box on any static hosting (like GitHub Pages or Vercel) without needing server-side redirects.

🎨 Styling the Outlet

The router returns a standard div with the .router-outlet class. You can easily style it or add transitions:

css
.router-outlet {
+  display: block;
+  min-height: 100vh;
+  animation: fadeIn 0.3s ease;
+}
 
-// Navigate to a dynamic path
-Button({ 
-  onclick: () => $.go(`/profile/${user.id}`) 
-}, "View Profile")

⚡ Technical Behavior

  • Automatic Cleanup: Every time you navigate, the router calls .destroy() on the previous $.view. This ensures that all signals, effects, and event listeners from the old page are purged from memory.
  • Hash-Based: By using window.location.hash, SigPro works out-of-the-box on any static hosting (like GitHub Pages or Vercel) without needing server-side redirects.
  • Initial Load: On the first execution, $.router automatically reads the current hash or defaults to / if empty.

🎨 Styling the Outlet

Since the router returns a standard DOM element, you can style the transition or the container easily:

css
.router-outlet {
-  flex: 1;
-  padding: 2rem;
-  animation: fadeIn 0.2s ease-in;
-}
- +@keyframes fadeIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +}
+ \ No newline at end of file diff --git a/docs/api/signal.html b/docs/api/signal.html index 49fed55..3671009 100644 --- a/docs/api/signal.html +++ b/docs/api/signal.html @@ -19,7 +19,7 @@ -
Skip to content

💎 The Signal Function: $( )

The $( ) function is the core constructor of SigPro. It defines how data is stored, computed, and persisted.

🛠 Function Signature

typescript
$(initialValue: any, key?: string): Signal
+    
Skip to content

💎 The Signal Function: $( )

The $( ) function is the core constructor of SigPro. It defines how data is stored, computed, and persisted.

🛠 Function Signature

typescript
$(initialValue: any, key?: string): Signal
 $(computation: Function): ComputedSignal
ParameterTypeRequiredDescription
initialValueanyYes*The starting value of your signal.
computationFunctionYes*A function that returns a value based on other signals.
keystringNoA unique name to persist the signal in localStorage.

*Either an initial value or a computation function must be provided.


📖 Usage Patterns

1. Simple State

$(value) Creates a writable signal. It returns a function that acts as both getter and setter.

javascript
const count = $(0); 
 
 count();    // Read (0)
@@ -32,8 +32,8 @@
 const total = $(() => price() * (1 + tax()));

🔄 Updating with Logic

When calling the setter, you can pass an updater function to access the current value safely.

javascript
const list = $(["A", "B"]);
 
 // Adds "C" using the previous state
-list(prev => [...prev, "C"]);
- +list(prev => [...prev, "C"]);
+ \ No newline at end of file diff --git a/docs/api/tags.html b/docs/api/tags.html index 8a45b6c..321f7ef 100644 --- a/docs/api/tags.html +++ b/docs/api/tags.html @@ -13,49 +13,45 @@ - + -
Skip to content

🎨 Global Tag Helpers

In SigPro, you don't need to manually type $.html('div', ...) for every element. To keep your code declarative and readable, the engine automatically generates Global Helper Functions for all standard HTML5 tags upon initialization.

1. How it Works

SigPro iterates through a manifest of standard HTML tags and attaches a wrapper function for each one directly to the window object. This creates a specialized DSL (Domain Specific Language) that looks like a template engine but is 100% standard JavaScript.

  • Under the hood: $.html('button', { onclick: ... }, 'Click')
  • SigPro Style: Button({ onclick: ... }, 'Click')

2. The Complete Global Registry

The following functions are injected into the global scope (using PascalCase to prevent naming collisions with common JS variables) and are ready to use:

CategoryAvailable Global Functions
StructureDiv, Span, P, Section, Nav, Main, Header, Footer, Article, Aside
TypographyH1 to H6, Ul, Ol, Li, Dl, Dt, Dd, Strong, Em, Code, Pre, Small, B, U, Mark
InteractiveButton, A, Label, Br, Hr, Details, Summary, Dialog
FormsForm, Input, Select, Option, Textarea, Fieldset, Legend
TablesTable, Thead, Tbody, Tr, Th, Td, Tfoot, Caption
MediaImg, Canvas, Video, Audio, Svg, Iframe, Picture, Source

The SigPro Philosophy: Tags are not "magic strings" handled by a compiler. They are functional constructors. Every time you call Div(), you execute a pure JS function that returns a real, reactive DOM element.


3. Usage Patterns (Smart Arguments)

SigPro tag helpers are flexible. They automatically detect if you are passing attributes, children, or both.

A. Attributes + Children

The standard way to build structured UI.

javascript
Div({ class: 'container', id: 'main' }, [
+    
Skip to content

🎨 Global Tag Helpers

In SigPro, you don't need to manually type $html('div', ...) for every element. To keep your code declarative and readable, the engine automatically generates Global Helper Functions for all standard HTML5 tags upon initialization.

1. How it Works

SigPro iterates through a manifest of standard HTML tags and attaches a wrapper function for each one directly to the window object. This creates a specialized DSL (Domain Specific Language) that looks like a template engine but is 100% standard JavaScript.

  • Under the hood: $html('button', { onclick: ... }, 'Click')
  • SigPro Style: Button({ onclick: ... }, 'Click')

2. The Complete Global Registry

The following functions are injected into the global scope using PascalCase to prevent naming collisions with common JS variables:

CategoryAvailable Global Functions
StructureDiv, Span, P, Section, Nav, Main, Header, Footer, Article, Aside
TypographyH1 to H6, Ul, Ol, Li, Dl, Dt, Dd, Strong, Em, Code, Pre, Small, B, U, Mark
InteractiveButton, A, Label, Br, Hr, Details, Summary, Dialog
FormsForm, Input, Select, Option, Textarea, Fieldset, Legend
TablesTable, Thead, Tbody, Tr, Th, Td, Tfoot, Caption
MediaImg, Canvas, Video, Audio, Svg, Iframe, Picture, Source

3. Usage Patterns (Smart Arguments)

SigPro tag helpers are flexible. They automatically detect if you are passing attributes, children, or both.

A. Attributes + Children

javascript
Div({ class: 'container', id: 'main' }, [
   H1("Welcome to SigPro"),
   P("The zero-VDOM framework.")
-]);

B. Children Only (The "Skipper")

If you don't need attributes, you can skip the object and pass the content (string, array, or function) directly as the first argument.

javascript
Section([
+]);

B. Children Only (The "Skipper")

If you don't need attributes, you can pass the content directly as the first argument.

javascript
Section([
   H2("Clean Syntax"),
   Button("I have no props!")
-]);

C. Primitive Content

For simple tags, just pass a string or a number.

javascript
H1("Hello World"); 
-Span(42);

4. Reactive Power

These helpers are natively wired into SigPro's $.watch engine. No manual effect management is needed; the lifecycle is tied to the DOM node.

Reactive Attributes

Simply pass a Signal (function) to any attribute. SigPro creates an internal $.watch to keep the DOM in sync.

javascript
const theme = $("light");
+]);

4. Reactive Power

These helpers are natively wired into SigPro's $watch engine.

Reactive Attributes (One-Way)

Simply pass a Signal (function) to any attribute. SigPro creates an internal $watch to keep the DOM in sync.

javascript
const theme = $("light");
 
 Div({ 
-  // Updates 'class' automatically via internal $.watch
   class: () => `app-box ${theme()}` 
-}, "Themeable Box");

The Binding Operator ($)

Use the $ prefix for Two-Way Binding on inputs. This bridges the Signal and the Input element bi-directionally.

javascript
const search = $("");
+}, "Themeable Box");

Smart Two-Way Binding (Automatic)

SigPro automatically bridges the Signal and the Input element bi-directionally when you assign a Signal to value or checked. No special operators are required.

javascript
const search = $("");
 
+// UI updates Signal AND Signal updates UI automatically
 Input({ 
   type: "text", 
   placeholder: "Search...",
-  $value: search // UI updates Signal AND Signal updates UI
-});

Dynamic Flow & Cleanup

Combine tags with Core controllers for high-performance rendering. SigPro automatically cleans up the $.watch instances when nodes are removed.

javascript
const items = $(["Apple", "Banana", "Cherry"]);
+  value: search 
+});

Pro Tip: If you want an input to be read-only but still reactive, wrap the signal in an anonymous function: value: () => search(). This prevents the "backwards" synchronization.

Dynamic Flow & Cleanup

Combine tags with Core controllers. SigPro automatically cleans up the $watch instances and event listeners when nodes are removed from the DOM.

javascript
const items = $(["Apple", "Banana", "Cherry"]);
 
 Ul({ class: "list-disc" }, [
-  $.For(items, (item) => Li(item))
-]);

DANGER

⚠️ Important: Naming Conventions

Since SigPro injects these helpers into the global window object, follow these rules to avoid bugs:

  1. Avoid Shadowing: Don't name your local variables like the tags (e.g., const Div = ...). This will "hide" the SigPro helper.
  2. Custom Components: Always use PascalCase for your own component functions (e.g., UserCard, NavMenu) to distinguish them from the built-in Tag Helpers and maintain architectural clarity.

5. Logic to UI Comparison

Here is how a dynamic User Status component translates from SigPro logic to the final DOM structure, handled by the engine.

javascript
// SigPro Component
-const UserStatus = (name, $online) => (
+  $for(items, (item) => Li(item), (item) => item)
+]);

DANGER

⚠️ Important: Naming Conventions

  1. Avoid Shadowing: Don't name your local variables like the tags (e.g., const Div = ...). This will "hide" the global SigPro helper.
  2. Custom Components: Always use PascalCase for your own component functions (e.g., UserCard, NavMenu) to distinguish them from built-in Tag Helpers.

5. Logic to UI Comparison

Here is how a dynamic User Status component translates from SigPro logic to the final DOM structure.

javascript
const UserStatus = (name, online) => (
   Div({ class: 'flex items-center gap-2' }, [
     Span({ 
-      // Boolean toggle for 'hidden' attribute
-      hidden: () => !$online(), 
+      hidden: () => !online(), 
       class: 'w-3 h-3 bg-green-500 rounded-full' 
     }),
     P({ 
-      // Reactive text content via automatic $.watch
-      class: () => $online() ? "text-bold" : "text-gray-400" 
+      class: () => online() ? "text-bold" : "text-gray-400" 
     }, name)
   ])
-);
State ($online)Rendered HTMLMemory Management
true<div class="flex..."><span class="w-3..."></span><p class="text-bold">John</p></div>Watcher active
false<div class="flex..."><span hidden class="w-3..."></span><p class="text-gray-400">John</p></div>Attribute synced
- +);
State (online)Rendered HTMLMemory Management
true<div class="flex..."><span class="w-3..."></span><p class="text-bold">John</p></div>Watcher active
false<div class="flex..."><span hidden class="w-3..."></span><p class="text-gray-400">John</p></div>Attribute synced
+ \ No newline at end of file diff --git a/docs/api/watch.html b/docs/api/watch.html index 5453ada..19e8dc5 100644 --- a/docs/api/watch.html +++ b/docs/api/watch.html @@ -3,7 +3,7 @@ - ⚡ Reactivity Control: $.watch( ) | SigPro + ⚡ Reactivity Control: $watch( ) | SigPro @@ -13,46 +13,46 @@ - + -
Skip to content

⚡ Reactivity Control: $.watch( )

The $.watch function is the reactive engine of SigPro. It allows you to execute code automatically when signals change. $.watch is polymorphic: it can track dependencies automatically or follow an explicit list.

🛠 Function Signature

typescript
// Automatic Mode (Magic Tracking)
-$.watch(callback: Function): StopFunction
+    
Skip to content

⚡ Reactivity Control: $watch( )

The $watch function is the reactive engine of SigPro. It allows you to execute code automatically when signals change. $watch is polymorphic: it can track dependencies automatically or follow an explicit list.

🛠 Function Signature

typescript
// Automatic Mode (Magic Tracking)
+$watch(callback: Function): StopFunction
 
 // Explicit Mode (Isolated Dependencies)
-$.watch(deps: Signal[], callback: Function): StopFunction
ParameterTypeRequiredDescription
target / depsFunctionArrayYes
callbackFunctionOnly in ExplicitThe code that will run when the deps change.

Returns: A StopFunction that, when called, destroys the watcher and releases memory.


📖 Usage Patterns

1. Automatic Mode (Default)

Any signal you "touch" inside the callback becomes a dependency. SigPro tracks them behind the scenes.

javascript
const count = $(0);
+$watch(deps: Signal[], callback: Function): StopFunction
ParameterTypeRequiredDescription
target / depsFunctionArrayYes
callbackFunctionOnly in ExplicitThe code that will run when the deps change.

Returns: A StopFunction that, when called, destroys the watcher and releases memory.


📖 Usage Patterns

1. Automatic Mode (Default)

Any signal you "touch" inside the callback becomes a dependency. SigPro tracks them behind the scenes.

javascript
const count = $(0);
 
-$.watch(() => {
+$watch(() => {
   // Re-runs every time 'count' changes
   console.log(`Count is: ${count()}`);
 });

2. Explicit Mode (Advanced Cleanup) 🚀

This mode isolates execution. The callback only triggers when the signals in the array change. Any other signal accessed inside the callback will NOT trigger a re-run. This is the "gold standard" for Routers and heavy components.

javascript
const sPath = $("/home");
 const user = $("Admin");
 
-$.watch([sPath], () => {
+$watch([sPath], () => {
   // Only triggers when 'sPath' changes.
   // Changes to 'user' will NOT trigger this, preventing accidental re-renders.
   console.log(`Navigating to ${sPath()} as ${user()}`);
-});

3. Automatic Cleanup

If your logic creates timers, event listeners, or other reactive effects, SigPro tracks them as "children" of the current watch. When the watcher re-runs or stops, it kills everything inside automatically.

javascript
$.watch(() => {
+});

3. Automatic Cleanup

If your logic creates timers, event listeners, or other reactive effects, SigPro tracks them as "children" of the current watch. When the watcher re-runs or stops, it kills everything inside automatically.

javascript
$watch(() => {
   const timer = setInterval(() => console.log("Tick"), 1000);
   
   // Register a manual cleanup if needed
-  // Or simply rely on SigPro to kill nested $.watch() calls
+  // Or simply rely on SigPro to kill nested $watch() calls
   return () => clearInterval(timer);
-});

🛑 Stopping a Watcher

Call the returned function to manually kill the watcher. This is essential for manual DOM injections (like Toasts) or long-lived background processes.

javascript
const stop = $.watch(() => console.log(count()));
+});

🛑 Stopping a Watcher

Call the returned function to manually kill the watcher. This is essential for manual DOM injections (like Toasts) or long-lived background processes.

javascript
const stop = $watch(() => console.log(count()));
 
 // Later...
 stop(); // The link between the signal and this code is physically severed.

💡 Pro Tip: The Microtask Queue

SigPro batches updates. If you update multiple signals in the same execution block, the watcher will only fire once at the end of the task.

javascript
const a = $(0);
 const b = $(0);
 
-$.watch(() => console.log(a(), b()));
+$watch(() => console.log(a(), b()));
 
 // This triggers only ONE re-run.
 a(1);
-b(2);
- +b(2);
+ \ No newline at end of file diff --git a/docs/assets/api_for.md.CGMDz0Px.js b/docs/assets/api_for.md.3GRpYy3h.js similarity index 65% rename from docs/assets/api_for.md.CGMDz0Px.js rename to docs/assets/api_for.md.3GRpYy3h.js index 13b0f2f..c6e3700 100644 --- a/docs/assets/api_for.md.CGMDz0Px.js +++ b/docs/assets/api_for.md.3GRpYy3h.js @@ -1,4 +1,4 @@ -import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"♻️ Reactive Lists: $.for( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/for.md","filePath":"api/for.md"}'),n={name:"api/for.md"};function l(h,s,r,p,k,d){return t(),a("div",null,[...s[0]||(s[0]=[e(`

♻️ Reactive Lists: $.for( )

The $.for function is a high-performance list renderer. It maps an array (or a Signal containing an array) to DOM nodes. Unlike a simple .map(), $.for is keyed, meaning it only updates, moves, or deletes the specific items that changed.

🛠️ Function Signature

typescript
$.for(
+import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"♻️ Reactive Lists: $for( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/for.md","filePath":"api/for.md"}'),n={name:"api/for.md"};function l(h,s,r,p,k,d){return t(),a("div",null,[...s[0]||(s[0]=[e(`

♻️ Reactive Lists: $for( )

The $for function is a high-performance list renderer. It maps an array (or a Signal containing an array) to DOM nodes. Unlike a simple .map(), $for is keyed, meaning it only updates, moves, or deletes the specific items that changed.

🛠️ Function Signature

typescript
$for(
   source: Signal<any[]> | Function | any[], 
   render: (item: any, index: number) => HTMLElement, 
   keyFn: (item: any, index: number) => string | number
@@ -8,12 +8,12 @@ import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g
 ]);
 
 Ul({ class: "list" }, [
-  $.for(users, 
+  $for(users, 
     (user) => Li({ class: "p-2" }, user.name),
     (user) => user.id
   )
 ]);

2. Handling Primitive Arrays

If your array contains simple strings or numbers, you can use the value itself or the index as a key (though the index is less efficient for reordering).

javascript
const tags = $(["Tech", "JS", "Web"]);
 
 Div({ class: "flex gap-1" }, [
-  $.for(tags, (tag) => Badge(tag), (tag) => tag)
-]);

🏗️ How it Works (The Reconciliation)

When the source signal changes, $.for performs the following steps:

  1. Key Diffing: It compares the new keys with the previous ones stored in an internal Map.
  2. Node Reuse: If a key already exists, the DOM node is reused and moved to its new position. No new elements are created.
  3. Cleanup: If a key disappears from the list, SigPro calls .destroy() on that specific item's instance. This stops all its internal watchers and removes its DOM nodes.

💡 Performance Tips

  • Stable Keys: Never use Math.random() as a key. This will force SigPro to destroy and recreate the entire list on every update, killing performance.
  • Component Encapsulation: If each item in your list has its own complex internal state, $.for ensures that state is preserved even if the list is reordered, as long as the key remains the same.

🧪 Summary Comparison

FeatureStandard Array.mapSigPro $.for
Re-renderRe-renders everythingOnly updates changes
DOM NodesRe-created every timeReused via Keys
MemoryPotential leaksAutomatic Cleanup
StateLost on re-renderPreserved per item
`,24)])])}const E=i(n,[["render",l]]);export{g as __pageData,E as default}; + $for(tags, (tag) => Badge(tag), (tag) => tag) +]);

🏗️ How it Works (The Reconciliation)

When the source signal changes, $for performs the following steps:

  1. Key Diffing: It compares the new keys with the previous ones stored in an internal Map.
  2. Node Reuse: If a key already exists, the DOM node is reused and moved to its new position. No new elements are created.
  3. Cleanup: If a key disappears from the list, SigPro calls .destroy() on that specific item's instance. This stops all its internal watchers and removes its DOM nodes.

💡 Performance Tips

  • Stable Keys: Never use Math.random() as a key. This will force SigPro to destroy and recreate the entire list on every update, killing performance.
  • Component Encapsulation: If each item in your list has its own complex internal state, $for ensures that state is preserved even if the list is reordered, as long as the key remains the same.

🧪 Summary Comparison

FeatureStandard Array.mapSigPro $for
Re-renderRe-renders everythingOnly updates changes
DOM NodesRe-created every timeReused via Keys
MemoryPotential leaksAutomatic Cleanup
StateLost on re-renderPreserved per item
`,24)])])}const E=i(n,[["render",l]]);export{g as __pageData,E as default}; diff --git a/docs/assets/api_for.md.3GRpYy3h.lean.js b/docs/assets/api_for.md.3GRpYy3h.lean.js new file mode 100644 index 0000000..32e3603 --- /dev/null +++ b/docs/assets/api_for.md.3GRpYy3h.lean.js @@ -0,0 +1 @@ +import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"♻️ Reactive Lists: $for( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/for.md","filePath":"api/for.md"}'),n={name:"api/for.md"};function l(h,s,r,p,k,d){return t(),a("div",null,[...s[0]||(s[0]=[e("",24)])])}const E=i(n,[["render",l]]);export{g as __pageData,E as default}; diff --git a/docs/assets/api_for.md.CGMDz0Px.lean.js b/docs/assets/api_for.md.CGMDz0Px.lean.js deleted file mode 100644 index 66336f0..0000000 --- a/docs/assets/api_for.md.CGMDz0Px.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"♻️ Reactive Lists: $.for( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/for.md","filePath":"api/for.md"}'),n={name:"api/for.md"};function l(h,s,r,p,k,d){return t(),a("div",null,[...s[0]||(s[0]=[e("",24)])])}const E=i(n,[["render",l]]);export{g as __pageData,E as default}; diff --git a/docs/assets/api_html.md.DSCIaSlE.js b/docs/assets/api_html.md.DSCIaSlE.js deleted file mode 100644 index 4ddabda..0000000 --- a/docs/assets/api_html.md.DSCIaSlE.js +++ /dev/null @@ -1,24 +0,0 @@ -import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"🏗️ The DOM Factory: $.html( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/html.md","filePath":"api/html.md"}'),n={name:"api/html.md"};function l(h,s,p,r,o,k){return t(),a("div",null,[...s[0]||(s[0]=[e(`

🏗️ The DOM Factory: $.html( )

$.html is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It uses $.watch to maintain a live, high-performance link between your Signals and the Document Object Model.

🛠 Function Signature

typescript
$.html(tagName: string, props?: Object, children?: any[] | any): HTMLElement
ParameterTypeRequiredDescription
tagNamestringYesValid HTML tag name (e.g., "div", "button").
propsObjectNoHTML attributes, event listeners, and reactive bindings.
childrenanyNoNested elements, text strings, or reactive functions.

📖 Key Features

1. Attribute Handling

SigPro intelligently decides how to apply each property:

  • Standard Props: Applied via setAttribute or direct property assignment.
  • Boolean Props: Uses toggleAttribute (e.g., checked, disabled, hidden).
  • Class Names: Supports class or className interchangeably.

2. Event Listeners & Modifiers

Events are defined by the on prefix. SigPro supports Dot Notation for common event operations:

javascript
$.html("button", {
-  // e.preventDefault() is called automatically
-  "onsubmit.prevent": (e) => save(e), 
-  
-  // e.stopPropagation() is called automatically
-  "onclick.stop": () => console.log("No bubbling"),
-  
-  // { once: true } listener option
-  "onclick.once": () => console.log("Runs only once")
-}, "Click Me");

3. Reactive Attributes

If an attribute value is a function (like a Signal), $.html creates an internal $.watch to keep the DOM in sync with the state.

javascript
$.html("div", {
-  // Updates the class whenever 'theme()' changes
-  class: () => theme() === "dark" ? "bg-black" : "bg-white"
-});

4. Reactive Children

Children can be static or dynamic. When a child is a function, SigPro creates a reactive boundary using $.watch for that specific part of the DOM.

javascript
$.html("div", {}, [
-  H1("Static Title"),
-  // Only this text node re-renders when 'count' changes
-  () => \`Current count: \${count()}\`
-]);

🔄 Two-Way Binding Operator ($)

When a property starts with $, $.html enables bidirectional synchronization. This is primarily used for form inputs.

javascript
$.html("input", {
-  type: "text",
-  $value: username // Syncs input value <-> signal
-});

🧹 Memory Management (Internal)

Every element created with $.html is "self-aware" regarding its reactive dependencies.

  • ._cleanups: A hidden Set attached to the element that stores all stop() functions from its internal $.watch calls and event listeners.
  • Lifecycle: When an element is removed by a Controller ($.If, $.For, or $.router), SigPro performs a recursive "sweep" to execute these cleanups, ensuring zero memory leaks.

💡 Tag Constructors (The Shortcuts)

Instead of writing $.html("div", ...) every time, SigPro provides PascalCase global functions:

javascript
// This:
-Div({ class: "wrapper" }, [ Span("Hello") ])
-
-// Is exactly equivalent to:
-$.html("div", { class: "wrapper" }, [ $.html("span", {}, "Hello") ])
`,30)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/api_html.md.DSCIaSlE.lean.js b/docs/assets/api_html.md.DSCIaSlE.lean.js deleted file mode 100644 index e13d045..0000000 --- a/docs/assets/api_html.md.DSCIaSlE.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"🏗️ The DOM Factory: $.html( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/html.md","filePath":"api/html.md"}'),n={name:"api/html.md"};function l(h,s,p,r,o,k){return t(),a("div",null,[...s[0]||(s[0]=[e("",30)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/api_html.md.QmhwKd7f.js b/docs/assets/api_html.md.QmhwKd7f.js new file mode 100644 index 0000000..803a1cf --- /dev/null +++ b/docs/assets/api_html.md.QmhwKd7f.js @@ -0,0 +1,18 @@ +import{_ as t,o as i,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const k=JSON.parse('{"title":"🏗️ The DOM Factory: $html( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/html.md","filePath":"api/html.md"}'),n={name:"api/html.md"};function l(h,s,r,o,p,d){return i(),a("div",null,[...s[0]||(s[0]=[e(`

🏗️ The DOM Factory: $html( )

$html is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It uses $.watch to maintain a live, high-performance link between your Signals and the Document Object Model.

🛠 Function Signature

typescript
$html(tagName: string, props?: Object, children?: any[] | any): HTMLElement
ParameterTypeRequiredDescription
tagNamestringYesValid HTML tag name (e.g., "div", "button").
propsObjectNoHTML attributes, event listeners, and reactive bindings.
childrenanyNoNested elements, text strings, or reactive functions.

📖 Key Features

1. Attribute Handling

SigPro intelligently decides how to apply each property:

  • Standard Props: Applied via setAttribute or direct property assignment.
  • Class Names: Supports class or className interchangeably.
  • Boolean Props: Automatic handling for checked, disabled, hidden, etc.

2. Event Listeners

Events are defined by the on prefix. SigPro automatically registers the listener and ensures it is cleaned up when the element is destroyed.

javascript
Button({
+  onclick: (e) => console.log("Clicked!", e),
+}, "Click Me");

3. Reactive Attributes (One-Way)

If an attribute value is a function (like a Signal), $html creates an internal $.watch to keep the DOM in sync with the state.

javascript
Div({
+  // Updates the class whenever 'theme()' changes
+  class: () => theme() === "dark" ? "bg-black" : "bg-white"
+});

4. Smart Two-Way Binding (Automatic)

SigPro automatically enables bidirectional synchronization when it detects a Signal assigned to a form-capable attribute (value or checked) on an input element (input, textarea, select).

javascript
// Syncs input value <-> signal automatically
+Input({ 
+  type: "text", 
+  value: username // No special symbols needed!
+})

Note: To use a Signal as read-only in an input, wrap it in an anonymous function: value: () => username().

5. Reactive Children

Children can be static or dynamic. When a child is a function, SigPro creates a reactive boundary using $.watch for that specific part of the DOM.

javascript
Div({}, [
+  H1("Static Title"),
+  // Only this text node re-renders when 'count' changes
+  () => \`Current count: \${count()}\`
+]);

🧹 Memory Management (Internal)

Every element created with $html is "self-aware" regarding its reactive dependencies.

  • ._cleanups: A hidden Set attached to the element that stores all stop() functions from its internal $.watch calls and event listeners.
  • Lifecycle: When an element is removed by a Controller ($.if, $.for, or $.router), SigPro performs a recursive "sweep" to execute these cleanups, ensuring zero memory leaks.

💡 Tag Constructors (The Shortcuts)

Instead of writing $html("div", ...) every time, SigPro provides PascalCase global functions for all standard HTML tags:

javascript
// This:
+Div({ class: "wrapper" }, [ Span("Hello") ])
+
+// Is exactly equivalent to:
+$html("div", { class: "wrapper" }, [ $html("span", {}, "Hello") ])
`,31)])])}const g=t(n,[["render",l]]);export{k as __pageData,g as default}; diff --git a/docs/assets/api_html.md.QmhwKd7f.lean.js b/docs/assets/api_html.md.QmhwKd7f.lean.js new file mode 100644 index 0000000..f16ecc8 --- /dev/null +++ b/docs/assets/api_html.md.QmhwKd7f.lean.js @@ -0,0 +1 @@ +import{_ as t,o as i,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const k=JSON.parse('{"title":"🏗️ The DOM Factory: $html( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/html.md","filePath":"api/html.md"}'),n={name:"api/html.md"};function l(h,s,r,o,p,d){return i(),a("div",null,[...s[0]||(s[0]=[e("",31)])])}const g=t(n,[["render",l]]);export{k as __pageData,g as default}; diff --git a/docs/assets/api_if.md.C-36XjK0.js b/docs/assets/api_if.md.2-2cm9cb.js similarity index 58% rename from docs/assets/api_if.md.C-36XjK0.js rename to docs/assets/api_if.md.2-2cm9cb.js index d83340d..d900999 100644 --- a/docs/assets/api_if.md.C-36XjK0.js +++ b/docs/assets/api_if.md.2-2cm9cb.js @@ -1,4 +1,4 @@ -import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"🔀 Reactive Branching: $.if( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/if.md","filePath":"api/if.md"}'),n={name:"api/if.md"};function l(h,s,p,r,o,d){return t(),a("div",null,[...s[0]||(s[0]=[e(`

🔀 Reactive Branching: $.if( )

The $.if function is a reactive control flow operator. It manages the conditional rendering of components, ensuring that only the active branch exists in the DOM and in memory.

🛠️ Function Signature

typescript
$.if(
+import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"🔀 Reactive Branching: $if( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/if.md","filePath":"api/if.md"}'),n={name:"api/if.md"};function l(h,s,p,r,o,d){return t(),a("div",null,[...s[0]||(s[0]=[e(`

🔀 Reactive Branching: $if( )

The $if function is a reactive control flow operator. It manages the conditional rendering of components, ensuring that only the active branch exists in the DOM and in memory.

🛠️ Function Signature

typescript
$if(
   condition: Signal<boolean> | Function, 
   thenVal: Component | Node, 
   otherwiseVal?: Component | Node
@@ -7,13 +7,13 @@ import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g
 Div([
   Button({ onclick: () => isVisible(!isVisible()) }, "Toggle Message"),
   
-  $.if(isVisible, 
+  $if(isVisible, 
     P("Now you see me!"), 
     P("Now you don't...")
   )
-]);

2. Lazy Component Loading

Unlike using a hidden class (CSS display: none), $.if is lazy. The branch that isn't active is never created. This saves memory and initial processing time.

javascript
$.if(() => user.isLogged(), 
+]);

2. Lazy Component Loading

Unlike using a hidden class (CSS display: none), $if is lazy. The branch that isn't active is never created. This saves memory and initial processing time.

javascript
$if(() => user.isLogged(), 
   () => Dashboard(), // Only executed if logged in
   () => LoginGate()  // Only executed if guest
-)

🧹 Automatic Cleanup

One of the core strengths of $.if is its integrated Cleanup logic. SigPro ensures that when a branch is swapped out, it is completely purged.

  1. Stop Watchers: All $.watch calls inside the inactive branch are permanently stopped.
  2. Unbind Events: Event listeners attached via $.html are removed.
  3. Recursive Sweep: SigPro performs a deep "sweep" of the removed branch to ensure no nested reactive effects remain active.

💡 Best Practices

  • Function Wrappers: If your branches are heavy (e.g., they contain complex components), wrap them in a function () => MyComponent(). This prevents the component from being initialized until the condition actually meets its requirement.
  • Logical Expressions: You can pass a complex computed function as the condition:
    javascript
    $.if(() => count() > 10 && status() === 'ready', 
    +)

    🧹 Automatic Cleanup

    One of the core strengths of $if is its integrated Cleanup logic. SigPro ensures that when a branch is swapped out, it is completely purged.

    1. Stop Watchers: All $.watch calls inside the inactive branch are permanently stopped.
    2. Unbind Events: Event listeners attached via $.html are removed.
    3. Recursive Sweep: SigPro performs a deep "sweep" of the removed branch to ensure no nested reactive effects remain active.

    💡 Best Practices

    • Function Wrappers: If your branches are heavy (e.g., they contain complex components), wrap them in a function () => MyComponent(). This prevents the component from being initialized until the condition actually meets its requirement.
    • Logical Expressions: You can pass a complex computed function as the condition:
      javascript
      $if(() => count() > 10 && status() === 'ready', 
         Span("Threshold reached!")
      -)

    🏗️ Technical Comparison

    FeatureStandard CSS hiddenSigPro $.if
    DOM PresenceAlways presentOnly if active
    ReactivityStill processing in backgroundPaused/Destroyed
    Memory usageHigherOptimized
    CleanupManualAutomatic
    `,24)])])}const c=i(n,[["render",l]]);export{g as __pageData,c as default}; +)

🏗️ Technical Comparison

FeatureStandard CSS hiddenSigPro $if
DOM PresenceAlways presentOnly if active
ReactivityStill processing in backgroundPaused/Destroyed
Memory usageHigherOptimized
CleanupManualAutomatic
`,24)])])}const c=i(n,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/api_if.md.2-2cm9cb.lean.js b/docs/assets/api_if.md.2-2cm9cb.lean.js new file mode 100644 index 0000000..ff80659 --- /dev/null +++ b/docs/assets/api_if.md.2-2cm9cb.lean.js @@ -0,0 +1 @@ +import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"🔀 Reactive Branching: $if( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/if.md","filePath":"api/if.md"}'),n={name:"api/if.md"};function l(h,s,p,r,o,d){return t(),a("div",null,[...s[0]||(s[0]=[e("",24)])])}const c=i(n,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/api_if.md.C-36XjK0.lean.js b/docs/assets/api_if.md.C-36XjK0.lean.js deleted file mode 100644 index dec52e9..0000000 --- a/docs/assets/api_if.md.C-36XjK0.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,o as t,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"🔀 Reactive Branching: $.if( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/if.md","filePath":"api/if.md"}'),n={name:"api/if.md"};function l(h,s,p,r,o,d){return t(),a("div",null,[...s[0]||(s[0]=[e("",24)])])}const c=i(n,[["render",l]]);export{g as __pageData,c as default}; diff --git a/docs/assets/api_mount.md.B3RUsjff.js b/docs/assets/api_mount.md.B3RUsjff.js new file mode 100644 index 0000000..06236d1 --- /dev/null +++ b/docs/assets/api_mount.md.B3RUsjff.js @@ -0,0 +1,16 @@ +import{_ as s,o as i,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const k=JSON.parse('{"title":"🔌 Application Mounter: $mount( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/mount.md","filePath":"api/mount.md"}'),n={name:"api/mount.md"};function l(o,t,h,r,p,d){return i(),a("div",null,[...t[0]||(t[0]=[e(`

🔌 Application Mounter: $mount( )

The $mount function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document and initializing its reactive lifecycle.

🛠️ Function Signature

typescript
$mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject
ParameterTypeDefaultDescription
nodeFunction or NodeRequiredThe component function or DOM element to render.
targetstring or Nodedocument.bodyCSS selector or DOM element where the app will live.

Returns: A Runtime object containing the container and a destroy() method to wipe all reactivity and DOM nodes.


📖 Common Usage Scenarios

1. The SPA Entry Point

In a Single Page Application, you typically mount your main component to the body or a root div. SigPro manages the entire view from that point.

javascript
import { $ } from './sigpro.js';
+import App from './App.js';
+
+// Mounts your main App component
+$mount(App, '#app-root');

2. Reactive "Islands"

SigPro is perfect for adding reactivity to static pages. You can mount small widgets into specific parts of an existing HTML layout.

javascript
const Counter = () => {
+  const count = $(0);
+  return Button({ onclick: () => count(c => c + 1) }, [
+    "Clicks: ", count
+  ]);
+};
+
+// Mount only the counter into a specific sidebar div
+$mount(Counter, '#sidebar-widget');

🔄 How it Works (Lifecycle & Cleanup)

When $mount is executed, it performs these critical steps to ensure a leak-free environment:

  1. Duplicate Detection: If you call $mount on a target that already has a SigPro instance, it automatically calls .destroy() on the previous instance. This prevents "Zombie Effects" from stacking in memory.
  2. Internal Scoping: It executes the component function inside an internal Reactive Owner. This captures every $watch and event listener created during the render.
  3. Target Injection: It clears the target using replaceChildren() and appends the new component.
  4. Runtime Creation: It returns a control object:
    • container: The actual DOM element created.
    • destroy(): The "kill switch" that runs all cleanups, stops all watchers, and removes the element from the DOM.

🛑 Manual Unmounting

While SigPro handles most cleanups automatically (via $if, $for, and $router), you can manually destroy any mounted instance. This is vital for imperatively managed UI like Toasts or Modals.

javascript
const instance = $mount(MyToast, '#toast-container');
+
+// Later, to remove the toast and kill its reactivity:
+instance.destroy();

💡 Summary Cheat Sheet

GoalCode Pattern
Mount to body$mount(App)
Mount to CSS Selector$mount(App, '#root')
Mount to DOM Node$mount(App, myElement)
Clean & Re-mountCalling $mount again on the same target
Total Cleanupconst app = $mount(App); app.destroy();
`,25)])])}const g=s(n,[["render",l]]);export{k as __pageData,g as default}; diff --git a/docs/assets/api_mount.md.B3RUsjff.lean.js b/docs/assets/api_mount.md.B3RUsjff.lean.js new file mode 100644 index 0000000..ff79430 --- /dev/null +++ b/docs/assets/api_mount.md.B3RUsjff.lean.js @@ -0,0 +1 @@ +import{_ as s,o as i,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const k=JSON.parse('{"title":"🔌 Application Mounter: $mount( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/mount.md","filePath":"api/mount.md"}'),n={name:"api/mount.md"};function l(o,t,h,r,p,d){return i(),a("div",null,[...t[0]||(t[0]=[e("",25)])])}const g=s(n,[["render",l]]);export{k as __pageData,g as default}; diff --git a/docs/assets/api_mount.md.CoM2SFqU.js b/docs/assets/api_mount.md.CoM2SFqU.js deleted file mode 100644 index c08aeed..0000000 --- a/docs/assets/api_mount.md.CoM2SFqU.js +++ /dev/null @@ -1,16 +0,0 @@ -import{_ as s,o as i,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const k=JSON.parse('{"title":"🔌 Application Mounter: $.mount( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/mount.md","filePath":"api/mount.md"}'),n={name:"api/mount.md"};function l(o,t,h,r,p,d){return i(),a("div",null,[...t[0]||(t[0]=[e(`

🔌 Application Mounter: $.mount( )

The $.mount function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document and initializing its reactive lifecycle.

🛠️ Function Signature

typescript
$.mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject
ParameterTypeDefaultDescription
nodeFunction or NodeRequiredThe component function or DOM element to render.
targetstring or Nodedocument.bodyCSS selector or DOM element where the app will live.

Returns: A Runtime object containing the container and a destroy() method to wipe all reactivity and DOM nodes.


📖 Common Usage Scenarios

1. The SPA Entry Point

In a Single Page Application, you typically mount your main component to the body or a root div. SigPro manages the entire view from that point.

javascript
import { $ } from './sigpro.js';
-import App from './App.js';
-
-// Mounts your main App component
-$.mount(App, '#app-root');

2. Reactive "Islands"

SigPro is perfect for adding reactivity to static pages. You can mount small widgets into specific parts of an existing HTML layout.

javascript
const Counter = () => {
-  const count = $(0);
-  return Button({ onclick: () => count(c => c + 1) }, [
-    "Clicks: ", count
-  ]);
-};
-
-// Mount only the counter into a specific sidebar div
-$.mount(Counter, '#sidebar-widget');

🔄 How it Works (Lifecycle & Cleanup)

When $.mount is executed, it performs these critical steps to ensure a leak-free environment:

  1. Duplicate Detection: If you call $.mount on a target that already has a SigPro instance, it automatically calls .destroy() on the previous instance. This prevents "Zombie Effects" from stacking in memory.
  2. Internal Scoping: It executes the component function inside an internal Reactive Owner. This captures every $.watch and event listener created during the render.
  3. Target Injection: It clears the target using replaceChildren() and appends the new component.
  4. Runtime Creation: It returns a control object:
    • container: The actual DOM element created.
    • destroy(): The "kill switch" that runs all cleanups, stops all watchers, and removes the element from the DOM.

🛑 Manual Unmounting

While SigPro handles most cleanups automatically (via $.If, $.For, and $.router), you can manually destroy any mounted instance. This is vital for imperatively managed UI like Toasts or Modals.

javascript
const instance = $.mount(MyToast, '#toast-container');
-
-// Later, to remove the toast and kill its reactivity:
-instance.destroy();

💡 Summary Cheat Sheet

GoalCode Pattern
Mount to body$.mount(App)
Mount to CSS Selector$.mount(App, '#root')
Mount to DOM Node$.mount(App, myElement)
Clean & Re-mountCalling $.mount again on the same target
Total Cleanupconst app = $.mount(App); app.destroy();
`,25)])])}const g=s(n,[["render",l]]);export{k as __pageData,g as default}; diff --git a/docs/assets/api_mount.md.CoM2SFqU.lean.js b/docs/assets/api_mount.md.CoM2SFqU.lean.js deleted file mode 100644 index 5893367..0000000 --- a/docs/assets/api_mount.md.CoM2SFqU.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as s,o as i,c as a,ae as e}from"./chunks/framework.C8AWLET_.js";const k=JSON.parse('{"title":"🔌 Application Mounter: $.mount( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/mount.md","filePath":"api/mount.md"}'),n={name:"api/mount.md"};function l(o,t,h,r,p,d){return i(),a("div",null,[...t[0]||(t[0]=[e("",25)])])}const g=s(n,[["render",l]]);export{k as __pageData,g as default}; diff --git a/docs/assets/api_quick.md.Coriz0Dx.js b/docs/assets/api_quick.md.Coriz0Dx.js new file mode 100644 index 0000000..1069523 --- /dev/null +++ b/docs/assets/api_quick.md.Coriz0Dx.js @@ -0,0 +1 @@ +import{_ as e,o as a,c as n,ae as l}from"./chunks/framework.C8AWLET_.js";const f=JSON.parse('{"title":"⚡ Quick API Reference","description":"","frontmatter":{},"headers":[],"relativePath":"api/quick.md","filePath":"api/quick.md"}'),o={name:"api/quick.md"};function d(s,t,r,i,c,g){return a(),n("div",null,[...t[0]||(t[0]=[l('

⚡ Quick API Reference

SigPro is a high-performance micro-framework that updates the Real DOM surgically. No Virtual DOM, no unnecessary re-renders, and built-in Cleanup (memory cleanup).

🟢 Core Functions

FunctionSignatureDescription
$(val, key?)(any, string?) => SignalCreates a Signal. If key is provided, it persists in localStorage.
$(fn)(function) => ComputedCreates a Computed Signal that auto-updates when its dependencies change.
$watch(fn)(function) => stopFnAutomatic Mode: Tracks any signal touched inside. Returns a stop function.
$watch(deps, fn)(Array, function) => stopFnExplicit Mode: Only runs when signals in deps change. Used for Cleanup.
$html(tag, props, kids)(string, obj, any) => ElementThe low-level DOM factory. Attaches ._cleanups to every element.
$if(cond, then, else?)(Signal, fn, fn?) => NodeReactive conditional. Automatically destroys the "else" branch memory.
$for(list, itemFn)(Signal, fn) => NodeOptimized list renderer. Manages individual item lifecycles.
$router(routes)(Array) => ElementHash-based SPA router. Uses Explicit Watch to prevent memory leaks.
$mount(node, target)(any, string|Node) => RuntimeEntry point. Creates a root instance with .destroy() capabilities.

🏗️ Element Constructors (Tags)

SigPro provides PascalCase wrappers for all standard HTML5 tags (e.g., Div, Span, Button).

Syntax Pattern

javascript
Tag({ attributes }, [children])

Attribute & Content Handling

PatternCode ExampleBehavior
Staticclass: "text-red"Standard HTML attribute string.
Reactivedisabled: isLoadingUpdates automatically via internal $watch.
Two-way$value: usernameBinding Operator: Syncs input $\\leftrightarrow$ signal both ways.
TextP({}, () => count())Updates text node surgically without re-rendering the P.
Booleanhidden: isHiddenToggles the attribute based on signal truthiness.
',11)])])}const u=e(o,[["render",d]]);export{f as __pageData,u as default}; diff --git a/docs/assets/api_quick.md.D3C2EJww.lean.js b/docs/assets/api_quick.md.Coriz0Dx.lean.js similarity index 100% rename from docs/assets/api_quick.md.D3C2EJww.lean.js rename to docs/assets/api_quick.md.Coriz0Dx.lean.js diff --git a/docs/assets/api_quick.md.D3C2EJww.js b/docs/assets/api_quick.md.D3C2EJww.js deleted file mode 100644 index 4208582..0000000 --- a/docs/assets/api_quick.md.D3C2EJww.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as e,o as a,c as n,ae as l}from"./chunks/framework.C8AWLET_.js";const f=JSON.parse('{"title":"⚡ Quick API Reference","description":"","frontmatter":{},"headers":[],"relativePath":"api/quick.md","filePath":"api/quick.md"}'),o={name:"api/quick.md"};function d(s,t,r,i,c,g){return a(),n("div",null,[...t[0]||(t[0]=[l('

⚡ Quick API Reference

SigPro is a high-performance micro-framework that updates the Real DOM surgically. No Virtual DOM, no unnecessary re-renders, and built-in Cleanup (memory cleanup).

🟢 Core Functions

FunctionSignatureDescription
$(val, key?)(any, string?) => SignalCreates a Signal. If key is provided, it persists in localStorage.
$(fn)(function) => ComputedCreates a Computed Signal that auto-updates when its dependencies change.
$.watch(fn)(function) => stopFnAutomatic Mode: Tracks any signal touched inside. Returns a stop function.
$.watch(deps, fn)(Array, function) => stopFnExplicit Mode: Only runs when signals in deps change. Used for Cleanup.
$.html(tag, props, kids)(string, obj, any) => ElementThe low-level DOM factory. Attaches ._cleanups to every element.
$.if(cond, then, else?)(Signal, fn, fn?) => NodeReactive conditional. Automatically destroys the "else" branch memory.
$.for(list, itemFn)(Signal, fn) => NodeOptimized list renderer. Manages individual item lifecycles.
$.router(routes)(Array) => ElementHash-based SPA router. Uses Explicit Watch to prevent memory leaks.
$.mount(node, target)(any, string|Node) => RuntimeEntry point. Creates a root instance with .destroy() capabilities.

🏗️ Element Constructors (Tags)

SigPro provides PascalCase wrappers for all standard HTML5 tags (e.g., Div, Span, Button).

Syntax Pattern

javascript
Tag({ attributes }, [children])

Attribute & Content Handling

PatternCode ExampleBehavior
Staticclass: "text-red"Standard HTML attribute string.
Reactivedisabled: isLoadingUpdates automatically via internal $.watch.
Two-way$value: usernameBinding Operator: Syncs input $\\leftrightarrow$ signal both ways.
TextP({}, () => count())Updates text node surgically without re-rendering the P.
Booleanhidden: isHiddenToggles the attribute based on signal truthiness.
',11)])])}const u=e(o,[["render",d]]);export{f as __pageData,u as default}; diff --git a/docs/assets/api_router.md.Cn98LjXO.js b/docs/assets/api_router.md.Cn98LjXO.js deleted file mode 100644 index 0c4a9bd..0000000 --- a/docs/assets/api_router.md.Cn98LjXO.js +++ /dev/null @@ -1,22 +0,0 @@ -import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"🚦 Routing: $.router( ) & $.go( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/router.md","filePath":"api/router.md"}'),n={name:"api/router.md"};function h(l,s,p,r,o,k){return a(),t("div",null,[...s[0]||(s[0]=[e(`

🚦 Routing: $.router( ) & $.go( )

SigPro includes a built-in, lightweight Hash Router to create Single Page Applications (SPA). It manages the URL state, matches components to paths, and handles the lifecycle of your pages automatically.

🛠 Router Signature

typescript
$.router(routes: Route[]): HTMLElement

Route Object

PropertyTypeDescription
pathstringThe URL fragment (e.g., "/home", "/user/:id", or "*").
componentFunctionA function that returns a Tag or a $.view.

📖 Usage Patterns

1. Defining Routes

The router returns a div element with the class .router-outlet. When the hash changes, the router destroys the previous view and mounts the new one inside this container.

javascript
const App = () => Div({ class: "app-layout" }, [
-  Navbar(),
-  // The router outlet is placed here
-  $.router([
-    { path: "/", component: Home },
-    { path: "/profile/:id", component: UserProfile },
-    { path: "*", component: NotFound }
-  ])
-]);

2. Dynamic Segments (:id)

When a path contains a colon (e.g., :id), the router parses that segment and passes it as an object to the component function.

javascript
// If the URL is #/profile/42
-const UserProfile = (params) => {
-  return H1(\`User ID is: \${params.id}\`); // Displays "User ID is: 42"
-};

🏎 Programmatic Navigation: $.go( )

To navigate between pages without using an <a> tag, use $.go. This function updates the browser's hash, which in turn triggers the router to swap components.

Signature

typescript
$.go(path: string): void

Examples

javascript
// Navigate to a static path
-Button({ onclick: () => $.go("/") }, "Home")
-
-// Navigate to a dynamic path
-Button({ 
-  onclick: () => $.go(\`/profile/\${user.id}\`) 
-}, "View Profile")

⚡ Technical Behavior

  • Automatic Cleanup: Every time you navigate, the router calls .destroy() on the previous $.view. This ensures that all signals, effects, and event listeners from the old page are purged from memory.
  • Hash-Based: By using window.location.hash, SigPro works out-of-the-box on any static hosting (like GitHub Pages or Vercel) without needing server-side redirects.
  • Initial Load: On the first execution, $.router automatically reads the current hash or defaults to / if empty.

🎨 Styling the Outlet

Since the router returns a standard DOM element, you can style the transition or the container easily:

css
.router-outlet {
-  flex: 1;
-  padding: 2rem;
-  animation: fadeIn 0.2s ease-in;
-}
`,28)])])}const c=i(n,[["render",h]]);export{g as __pageData,c as default}; diff --git a/docs/assets/api_router.md.Cn98LjXO.lean.js b/docs/assets/api_router.md.Cn98LjXO.lean.js deleted file mode 100644 index 9e5fc68..0000000 --- a/docs/assets/api_router.md.Cn98LjXO.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"🚦 Routing: $.router( ) & $.go( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/router.md","filePath":"api/router.md"}'),n={name:"api/router.md"};function h(l,s,p,r,o,k){return a(),t("div",null,[...s[0]||(s[0]=[e("",28)])])}const c=i(n,[["render",h]]);export{g as __pageData,c as default}; diff --git a/docs/assets/api_router.md.DxC_B3iU.js b/docs/assets/api_router.md.DxC_B3iU.js new file mode 100644 index 0000000..563cb4c --- /dev/null +++ b/docs/assets/api_router.md.DxC_B3iU.js @@ -0,0 +1,23 @@ +import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const E=JSON.parse('{"title":"🚦 Routing: $router() & Utilities","description":"","frontmatter":{},"headers":[],"relativePath":"api/router.md","filePath":"api/router.md"}'),n={name:"api/router.md"};function h(l,s,p,r,k,o){return a(),t("div",null,[...s[0]||(s[0]=[e(`

🚦 Routing: $router() & Utilities

SigPro includes a built-in, lightweight Hash Router to create Single Page Applications (SPA). It manages the URL state, matches components to paths, and handles the lifecycle of your pages automatically.

🛠 Router Signature

typescript
$router(routes: Route[]): HTMLElement

Route Object

PropertyTypeDescription
pathstringThe URL fragment (e.g., "/", "/user/:id", or "*").
componentFunctionA function that returns a Node, a String, or a reactive View.

📖 Usage Patterns

1. Defining Routes

The $router returns a div element with the class .router-outlet. When the hash changes, the router destroys the previous view and mounts the new one inside this container.

javascript
const App = () => Div({ class: "app-layout" }, [
+  Navbar(),
+  // The router outlet is placed here
+  $router([
+    { path: "/", component: Home },
+    { path: "/profile/:id", component: (params) => UserProfile(params.id) },
+    { path: "*", component: () => H1("404 Not Found") }
+  ])
+]);

2. Dynamic Segments (:id)

The router automatically parses URL parameters (like :id) and passes them as an object to the component function. You can also access them globally via $router.params().

javascript
// If the URL is #/profile/42
+const UserProfile = (params) => {
+  return H1(\`User ID is: \${params.id}\`); // Displays "User ID is: 42"
+};

🏎 Navigation Utilities

SigPro provides a set of programmatic methods to control the history and read the state.

$router.to(path)

Navigates to a specific path. It automatically formats the hash (e.g., /home becomes #/home).

javascript
Button({ onclick: () => $router.to("/dashboard") }, "Go to Dashboard")

$router.back()

Goes back to the previous page in the browser history.

javascript
Button({ onclick: () => $router.back() }, "Back")

$router.path()

Returns the current path (without the #).

javascript
$watch(() => {
+  console.log("Current path is:", $router.path());
+});

⚡ Technical Behavior

  • Automatic Cleanup: Every time you navigate, the router calls .destroy() on the previous view. This ensures that all signals, effects, and event listeners from the old page are purged from memory.
  • Reactive Params: $router.params is a signal ($). You can react to parameter changes without re-mounting the entire router outlet.
  • Hash-Based: By using window.location.hash, SigPro works out-of-the-box on any static hosting (like GitHub Pages or Vercel) without needing server-side redirects.

🎨 Styling the Outlet

The router returns a standard div with the .router-outlet class. You can easily style it or add transitions:

css
.router-outlet {
+  display: block;
+  min-height: 100vh;
+  animation: fadeIn 0.3s ease;
+}
+
+@keyframes fadeIn {
+  from { opacity: 0; transform: translateY(10px); }
+  to { opacity: 1; transform: translateY(0); }
+}
`,33)])])}const g=i(n,[["render",h]]);export{E as __pageData,g as default}; diff --git a/docs/assets/api_router.md.DxC_B3iU.lean.js b/docs/assets/api_router.md.DxC_B3iU.lean.js new file mode 100644 index 0000000..d4940a2 --- /dev/null +++ b/docs/assets/api_router.md.DxC_B3iU.lean.js @@ -0,0 +1 @@ +import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const E=JSON.parse('{"title":"🚦 Routing: $router() & Utilities","description":"","frontmatter":{},"headers":[],"relativePath":"api/router.md","filePath":"api/router.md"}'),n={name:"api/router.md"};function h(l,s,p,r,k,o){return a(),t("div",null,[...s[0]||(s[0]=[e("",33)])])}const g=i(n,[["render",h]]);export{E as __pageData,g as default}; diff --git a/docs/assets/api_tags.md.BKWIb8mV.js b/docs/assets/api_tags.md.BKWIb8mV.js deleted file mode 100644 index a3450a9..0000000 --- a/docs/assets/api_tags.md.BKWIb8mV.js +++ /dev/null @@ -1,36 +0,0 @@ -import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"🎨 Global Tag Helpers","description":"","frontmatter":{},"headers":[],"relativePath":"api/tags.md","filePath":"api/tags.md"}'),n={name:"api/tags.md"};function l(h,s,o,p,r,d){return a(),t("div",null,[...s[0]||(s[0]=[e(`

🎨 Global Tag Helpers

In SigPro, you don't need to manually type $.html('div', ...) for every element. To keep your code declarative and readable, the engine automatically generates Global Helper Functions for all standard HTML5 tags upon initialization.

1. How it Works

SigPro iterates through a manifest of standard HTML tags and attaches a wrapper function for each one directly to the window object. This creates a specialized DSL (Domain Specific Language) that looks like a template engine but is 100% standard JavaScript.

  • Under the hood: $.html('button', { onclick: ... }, 'Click')
  • SigPro Style: Button({ onclick: ... }, 'Click')

2. The Complete Global Registry

The following functions are injected into the global scope (using PascalCase to prevent naming collisions with common JS variables) and are ready to use:

CategoryAvailable Global Functions
StructureDiv, Span, P, Section, Nav, Main, Header, Footer, Article, Aside
TypographyH1 to H6, Ul, Ol, Li, Dl, Dt, Dd, Strong, Em, Code, Pre, Small, B, U, Mark
InteractiveButton, A, Label, Br, Hr, Details, Summary, Dialog
FormsForm, Input, Select, Option, Textarea, Fieldset, Legend
TablesTable, Thead, Tbody, Tr, Th, Td, Tfoot, Caption
MediaImg, Canvas, Video, Audio, Svg, Iframe, Picture, Source

The SigPro Philosophy: Tags are not "magic strings" handled by a compiler. They are functional constructors. Every time you call Div(), you execute a pure JS function that returns a real, reactive DOM element.


3. Usage Patterns (Smart Arguments)

SigPro tag helpers are flexible. They automatically detect if you are passing attributes, children, or both.

A. Attributes + Children

The standard way to build structured UI.

javascript
Div({ class: 'container', id: 'main' }, [
-  H1("Welcome to SigPro"),
-  P("The zero-VDOM framework.")
-]);

B. Children Only (The "Skipper")

If you don't need attributes, you can skip the object and pass the content (string, array, or function) directly as the first argument.

javascript
Section([
-  H2("Clean Syntax"),
-  Button("I have no props!")
-]);

C. Primitive Content

For simple tags, just pass a string or a number.

javascript
H1("Hello World"); 
-Span(42);

4. Reactive Power

These helpers are natively wired into SigPro's $.watch engine. No manual effect management is needed; the lifecycle is tied to the DOM node.

Reactive Attributes

Simply pass a Signal (function) to any attribute. SigPro creates an internal $.watch to keep the DOM in sync.

javascript
const theme = $("light");
-
-Div({ 
-  // Updates 'class' automatically via internal $.watch
-  class: () => \`app-box \${theme()}\` 
-}, "Themeable Box");

The Binding Operator ($)

Use the $ prefix for Two-Way Binding on inputs. This bridges the Signal and the Input element bi-directionally.

javascript
const search = $("");
-
-Input({ 
-  type: "text", 
-  placeholder: "Search...",
-  $value: search // UI updates Signal AND Signal updates UI
-});

Dynamic Flow & Cleanup

Combine tags with Core controllers for high-performance rendering. SigPro automatically cleans up the $.watch instances when nodes are removed.

javascript
const items = $(["Apple", "Banana", "Cherry"]);
-
-Ul({ class: "list-disc" }, [
-  $.For(items, (item) => Li(item))
-]);

DANGER

⚠️ Important: Naming Conventions

Since SigPro injects these helpers into the global window object, follow these rules to avoid bugs:

  1. Avoid Shadowing: Don't name your local variables like the tags (e.g., const Div = ...). This will "hide" the SigPro helper.
  2. Custom Components: Always use PascalCase for your own component functions (e.g., UserCard, NavMenu) to distinguish them from the built-in Tag Helpers and maintain architectural clarity.

5. Logic to UI Comparison

Here is how a dynamic User Status component translates from SigPro logic to the final DOM structure, handled by the engine.

javascript
// SigPro Component
-const UserStatus = (name, $online) => (
-  Div({ class: 'flex items-center gap-2' }, [
-    Span({ 
-      // Boolean toggle for 'hidden' attribute
-      hidden: () => !$online(), 
-      class: 'w-3 h-3 bg-green-500 rounded-full' 
-    }),
-    P({ 
-      // Reactive text content via automatic $.watch
-      class: () => $online() ? "text-bold" : "text-gray-400" 
-    }, name)
-  ])
-);
State ($online)Rendered HTMLMemory Management
true<div class="flex..."><span class="w-3..."></span><p class="text-bold">John</p></div>Watcher active
false<div class="flex..."><span hidden class="w-3..."></span><p class="text-gray-400">John</p></div>Attribute synced
`,41)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/api_tags.md.BPbc38UO.js b/docs/assets/api_tags.md.BPbc38UO.js new file mode 100644 index 0000000..5098a4c --- /dev/null +++ b/docs/assets/api_tags.md.BPbc38UO.js @@ -0,0 +1,32 @@ +import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"🎨 Global Tag Helpers","description":"","frontmatter":{},"headers":[],"relativePath":"api/tags.md","filePath":"api/tags.md"}'),n={name:"api/tags.md"};function l(h,s,o,p,d,r){return a(),t("div",null,[...s[0]||(s[0]=[e(`

🎨 Global Tag Helpers

In SigPro, you don't need to manually type $html('div', ...) for every element. To keep your code declarative and readable, the engine automatically generates Global Helper Functions for all standard HTML5 tags upon initialization.

1. How it Works

SigPro iterates through a manifest of standard HTML tags and attaches a wrapper function for each one directly to the window object. This creates a specialized DSL (Domain Specific Language) that looks like a template engine but is 100% standard JavaScript.

  • Under the hood: $html('button', { onclick: ... }, 'Click')
  • SigPro Style: Button({ onclick: ... }, 'Click')

2. The Complete Global Registry

The following functions are injected into the global scope using PascalCase to prevent naming collisions with common JS variables:

CategoryAvailable Global Functions
StructureDiv, Span, P, Section, Nav, Main, Header, Footer, Article, Aside
TypographyH1 to H6, Ul, Ol, Li, Dl, Dt, Dd, Strong, Em, Code, Pre, Small, B, U, Mark
InteractiveButton, A, Label, Br, Hr, Details, Summary, Dialog
FormsForm, Input, Select, Option, Textarea, Fieldset, Legend
TablesTable, Thead, Tbody, Tr, Th, Td, Tfoot, Caption
MediaImg, Canvas, Video, Audio, Svg, Iframe, Picture, Source

3. Usage Patterns (Smart Arguments)

SigPro tag helpers are flexible. They automatically detect if you are passing attributes, children, or both.

A. Attributes + Children

javascript
Div({ class: 'container', id: 'main' }, [
+  H1("Welcome to SigPro"),
+  P("The zero-VDOM framework.")
+]);

B. Children Only (The "Skipper")

If you don't need attributes, you can pass the content directly as the first argument.

javascript
Section([
+  H2("Clean Syntax"),
+  Button("I have no props!")
+]);

4. Reactive Power

These helpers are natively wired into SigPro's $watch engine.

Reactive Attributes (One-Way)

Simply pass a Signal (function) to any attribute. SigPro creates an internal $watch to keep the DOM in sync.

javascript
const theme = $("light");
+
+Div({ 
+  class: () => \`app-box \${theme()}\` 
+}, "Themeable Box");

Smart Two-Way Binding (Automatic)

SigPro automatically bridges the Signal and the Input element bi-directionally when you assign a Signal to value or checked. No special operators are required.

javascript
const search = $("");
+
+// UI updates Signal AND Signal updates UI automatically
+Input({ 
+  type: "text", 
+  placeholder: "Search...",
+  value: search 
+});

Pro Tip: If you want an input to be read-only but still reactive, wrap the signal in an anonymous function: value: () => search(). This prevents the "backwards" synchronization.

Dynamic Flow & Cleanup

Combine tags with Core controllers. SigPro automatically cleans up the $watch instances and event listeners when nodes are removed from the DOM.

javascript
const items = $(["Apple", "Banana", "Cherry"]);
+
+Ul({ class: "list-disc" }, [
+  $for(items, (item) => Li(item), (item) => item)
+]);

DANGER

⚠️ Important: Naming Conventions

  1. Avoid Shadowing: Don't name your local variables like the tags (e.g., const Div = ...). This will "hide" the global SigPro helper.
  2. Custom Components: Always use PascalCase for your own component functions (e.g., UserCard, NavMenu) to distinguish them from built-in Tag Helpers.

5. Logic to UI Comparison

Here is how a dynamic User Status component translates from SigPro logic to the final DOM structure.

javascript
const UserStatus = (name, online) => (
+  Div({ class: 'flex items-center gap-2' }, [
+    Span({ 
+      hidden: () => !online(), 
+      class: 'w-3 h-3 bg-green-500 rounded-full' 
+    }),
+    P({ 
+      class: () => online() ? "text-bold" : "text-gray-400" 
+    }, name)
+  ])
+);
State (online)Rendered HTMLMemory Management
true<div class="flex..."><span class="w-3..."></span><p class="text-bold">John</p></div>Watcher active
false<div class="flex..."><span hidden class="w-3..."></span><p class="text-gray-400">John</p></div>Attribute synced
`,37)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/api_tags.md.BKWIb8mV.lean.js b/docs/assets/api_tags.md.BPbc38UO.lean.js similarity index 67% rename from docs/assets/api_tags.md.BKWIb8mV.lean.js rename to docs/assets/api_tags.md.BPbc38UO.lean.js index b1d338e..2b9e756 100644 --- a/docs/assets/api_tags.md.BKWIb8mV.lean.js +++ b/docs/assets/api_tags.md.BPbc38UO.lean.js @@ -1 +1 @@ -import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"🎨 Global Tag Helpers","description":"","frontmatter":{},"headers":[],"relativePath":"api/tags.md","filePath":"api/tags.md"}'),n={name:"api/tags.md"};function l(h,s,o,p,r,d){return a(),t("div",null,[...s[0]||(s[0]=[e("",41)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; +import{_ as i,o as a,c as t,ae as e}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"🎨 Global Tag Helpers","description":"","frontmatter":{},"headers":[],"relativePath":"api/tags.md","filePath":"api/tags.md"}'),n={name:"api/tags.md"};function l(h,s,o,p,d,r){return a(),t("div",null,[...s[0]||(s[0]=[e("",37)])])}const g=i(n,[["render",l]]);export{c as __pageData,g as default}; diff --git a/docs/assets/api_watch.md.BEM6Qssx.lean.js b/docs/assets/api_watch.md.BEM6Qssx.lean.js deleted file mode 100644 index 41b0821..0000000 --- a/docs/assets/api_watch.md.BEM6Qssx.lean.js +++ /dev/null @@ -1 +0,0 @@ -import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const o=JSON.parse('{"title":"⚡ Reactivity Control: $.watch( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/watch.md","filePath":"api/watch.md"}'),e={name:"api/watch.md"};function h(l,s,p,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n("",25)])])}const g=i(e,[["render",h]]);export{o as __pageData,g as default}; diff --git a/docs/assets/api_watch.md.BEM6Qssx.js b/docs/assets/api_watch.md.Dc5wZqk9.js similarity index 61% rename from docs/assets/api_watch.md.BEM6Qssx.js rename to docs/assets/api_watch.md.Dc5wZqk9.js index 0809a92..4f9e050 100644 --- a/docs/assets/api_watch.md.BEM6Qssx.js +++ b/docs/assets/api_watch.md.Dc5wZqk9.js @@ -1,32 +1,32 @@ -import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const o=JSON.parse('{"title":"⚡ Reactivity Control: $.watch( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/watch.md","filePath":"api/watch.md"}'),e={name:"api/watch.md"};function h(l,s,p,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

⚡ Reactivity Control: $.watch( )

The $.watch function is the reactive engine of SigPro. It allows you to execute code automatically when signals change. $.watch is polymorphic: it can track dependencies automatically or follow an explicit list.

🛠 Function Signature

typescript
// Automatic Mode (Magic Tracking)
-$.watch(callback: Function): StopFunction
+import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const o=JSON.parse('{"title":"⚡ Reactivity Control: $watch( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/watch.md","filePath":"api/watch.md"}'),e={name:"api/watch.md"};function h(l,s,p,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

⚡ Reactivity Control: $watch( )

The $watch function is the reactive engine of SigPro. It allows you to execute code automatically when signals change. $watch is polymorphic: it can track dependencies automatically or follow an explicit list.

🛠 Function Signature

typescript
// Automatic Mode (Magic Tracking)
+$watch(callback: Function): StopFunction
 
 // Explicit Mode (Isolated Dependencies)
-$.watch(deps: Signal[], callback: Function): StopFunction
ParameterTypeRequiredDescription
target / depsFunctionArrayYes
callbackFunctionOnly in ExplicitThe code that will run when the deps change.

Returns: A StopFunction that, when called, destroys the watcher and releases memory.


📖 Usage Patterns

1. Automatic Mode (Default)

Any signal you "touch" inside the callback becomes a dependency. SigPro tracks them behind the scenes.

javascript
const count = $(0);
+$watch(deps: Signal[], callback: Function): StopFunction
ParameterTypeRequiredDescription
target / depsFunctionArrayYes
callbackFunctionOnly in ExplicitThe code that will run when the deps change.

Returns: A StopFunction that, when called, destroys the watcher and releases memory.


📖 Usage Patterns

1. Automatic Mode (Default)

Any signal you "touch" inside the callback becomes a dependency. SigPro tracks them behind the scenes.

javascript
const count = $(0);
 
-$.watch(() => {
+$watch(() => {
   // Re-runs every time 'count' changes
   console.log(\`Count is: \${count()}\`);
 });

2. Explicit Mode (Advanced Cleanup) 🚀

This mode isolates execution. The callback only triggers when the signals in the array change. Any other signal accessed inside the callback will NOT trigger a re-run. This is the "gold standard" for Routers and heavy components.

javascript
const sPath = $("/home");
 const user = $("Admin");
 
-$.watch([sPath], () => {
+$watch([sPath], () => {
   // Only triggers when 'sPath' changes.
   // Changes to 'user' will NOT trigger this, preventing accidental re-renders.
   console.log(\`Navigating to \${sPath()} as \${user()}\`);
-});

3. Automatic Cleanup

If your logic creates timers, event listeners, or other reactive effects, SigPro tracks them as "children" of the current watch. When the watcher re-runs or stops, it kills everything inside automatically.

javascript
$.watch(() => {
+});

3. Automatic Cleanup

If your logic creates timers, event listeners, or other reactive effects, SigPro tracks them as "children" of the current watch. When the watcher re-runs or stops, it kills everything inside automatically.

javascript
$watch(() => {
   const timer = setInterval(() => console.log("Tick"), 1000);
   
   // Register a manual cleanup if needed
-  // Or simply rely on SigPro to kill nested $.watch() calls
+  // Or simply rely on SigPro to kill nested $watch() calls
   return () => clearInterval(timer);
-});

🛑 Stopping a Watcher

Call the returned function to manually kill the watcher. This is essential for manual DOM injections (like Toasts) or long-lived background processes.

javascript
const stop = $.watch(() => console.log(count()));
+});

🛑 Stopping a Watcher

Call the returned function to manually kill the watcher. This is essential for manual DOM injections (like Toasts) or long-lived background processes.

javascript
const stop = $watch(() => console.log(count()));
 
 // Later...
 stop(); // The link between the signal and this code is physically severed.

💡 Pro Tip: The Microtask Queue

SigPro batches updates. If you update multiple signals in the same execution block, the watcher will only fire once at the end of the task.

javascript
const a = $(0);
 const b = $(0);
 
-$.watch(() => console.log(a(), b()));
+$watch(() => console.log(a(), b()));
 
 // This triggers only ONE re-run.
 a(1);
diff --git a/docs/assets/api_watch.md.Dc5wZqk9.lean.js b/docs/assets/api_watch.md.Dc5wZqk9.lean.js
new file mode 100644
index 0000000..6fa7196
--- /dev/null
+++ b/docs/assets/api_watch.md.Dc5wZqk9.lean.js
@@ -0,0 +1 @@
+import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const o=JSON.parse('{"title":"⚡ Reactivity Control: $watch( )","description":"","frontmatter":{},"headers":[],"relativePath":"api/watch.md","filePath":"api/watch.md"}'),e={name:"api/watch.md"};function h(l,s,p,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n("",25)])])}const g=i(e,[["render",h]]);export{o as __pageData,g as default};
diff --git a/docs/assets/install.md.C1tyiPaQ.js b/docs/assets/install.md.CrzPbqO_.js
similarity index 90%
rename from docs/assets/install.md.C1tyiPaQ.js
rename to docs/assets/install.md.CrzPbqO_.js
index aaecd8a..7496c18 100644
--- a/docs/assets/install.md.C1tyiPaQ.js
+++ b/docs/assets/install.md.CrzPbqO_.js
@@ -1,11 +1,11 @@
-import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Installation & Setup","description":"","frontmatter":{},"headers":[],"relativePath":"install.md","filePath":"install.md"}'),l={name:"install.md"};function e(h,s,p,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

Installation & Setup

SigPro is designed to be drop-in ready. Whether you are building a complex application with a bundler or a simple reactive widget in a single HTML file, SigPro scales with your needs.

1. Installation

Choose the method that best fits your workflow:

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
html
<script type="module">
+import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g=JSON.parse('{"title":"Installation & Setup","description":"","frontmatter":{},"headers":[],"relativePath":"install.md","filePath":"install.md"}'),l={name:"install.md"};function e(h,s,p,k,r,d){return a(),t("div",null,[...s[0]||(s[0]=[n(`

Installation & Setup

SigPro is designed to be drop-in ready. Whether you are building a complex application with a bundler or a simple reactive widget in a single HTML file, SigPro scales with your needs.

1. Installation

Choose the method that best fits your workflow:

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
html
<script type="module">
   // Import the core and UI components
   import { $ } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/+esm';
   import { UI } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/ui/+esm';
   
   // Initialize UI components globally
   UI($);
-</script>

2. Quick Start Examples

SigPro uses PascalCase for Tag Helpers (e.g., Div, Button) to provide a clean, component-like syntax without needing JSX.

javascript
// File: App.js
+</script>

2. Quick Start Examples

SigPro uses PascalCase for Tag Helpers (e.g., Div, Button) to provide a clean, component-like syntax without needing JSX.

javascript
// File: App.js
 import { $ } from 'sigpro'; 
 
 export const App = () => {
@@ -25,7 +25,7 @@ import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g
 import { $ } from 'sigpro';
 import { App } from './App.js';
 
-$.mount(App, '#app');
html
<!DOCTYPE html>
+$mount(App, '#app');
html
<!DOCTYPE html>
 <html lang="en">
 <body>
   <div id="app"></div>
@@ -46,7 +46,7 @@ import{_ as i,o as a,c as t,ae as n}from"./chunks/framework.C8AWLET_.js";const g
       })
     ]);
 
-    $.mount(App, '#app');
+    $mount(App, '#app');
   </script>
 </body>
 </html>

3. Global by Design

One of SigPro's core strengths is its Global API, which eliminates "Import Hell".

  • The $ Function: Once loaded, it attaches itself to window.$. It handles state, effects, and DOM mounting.
  • Tag Helpers (PascalCase): Common HTML tags (Div, Span, Button, Input, etc.) are automatically registered in the global scope.
  • Custom Components: We recommend using PascalCase (e.g., UserCard) or prefixes like _Input to keep your code organized and distinguish your logic from standard tags.

4. Why no build step?

Because SigPro uses native ES Modules and standard JavaScript functions to generate the DOM, you don't actually need a compiler like Babel or a transformer for JSX.

  • Development: Just save and refresh. Pure JS, no "transpilation" required.
  • Performance: Extremely lightweight. Use any modern bundler (Vite, esbuild) only when you are ready to minify and tree-shake for production.

5. Why SigPro? (The Competitive Edge)

SigPro stands out by removing the "Build Step" tax and the "Virtual DOM" overhead. It is the closest you can get to writing raw HTML/JS while maintaining modern reactivity.

FeatureSigProSolidJSSvelteReactVue
Bundle Size~2KB~7KB~4KB~40KB+~30KB
DOM StrategyDirect DOMDirect DOMCompiled DOMVirtual DOMVirtual DOM
ReactivityFine-grainedFine-grainedCompiledRe-rendersProxies
Build StepOptionalRequiredRequiredRequiredOptional
Learning CurveMinimalMediumLowHighMedium
InitializationUltra-FastVery FastFastSlowMedium

6. Key Advantages

  • Extreme Performance: No Virtual DOM reconciliation. SigPro updates the specific node or attribute instantly when a Signal changes.
  • Fine-Grained Reactivity: State changes only trigger updates where the data is actually used, not on the entire component.
  • Native Web Standards: Everything is a standard JS function. No custom template syntax to learn.
  • Zero Magic: No hidden compilers. What you write is what runs in the browser.
  • Global by Design: Tag Helpers and the $ function are available globally to eliminate "Import Hell" and keep your code clean.

7. Summary

SigPro isn't just another framework; it's a bridge to the native web. By using standard ES Modules and functional DOM generation, you gain the benefits of a modern library with the weight of a utility script.

Because, in the end... why fight the web when we can embrace it?

`,25)])])}const E=i(l,[["render",e]]);export{g as __pageData,E as default}; diff --git a/docs/assets/install.md.C1tyiPaQ.lean.js b/docs/assets/install.md.CrzPbqO_.lean.js similarity index 100% rename from docs/assets/install.md.C1tyiPaQ.lean.js rename to docs/assets/install.md.CrzPbqO_.lean.js diff --git a/docs/assets/ui_quick.md.CsppjR8J.js b/docs/assets/ui_quick.md.CL4k0g3a.js similarity index 75% rename from docs/assets/ui_quick.md.CsppjR8J.js rename to docs/assets/ui_quick.md.CL4k0g3a.js index f129766..719aed7 100644 --- a/docs/assets/ui_quick.md.CsppjR8J.js +++ b/docs/assets/ui_quick.md.CL4k0g3a.js @@ -1,4 +1,4 @@ -import{_ as i,o as a,c as e,ae as t}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"🧩 UI Components (WIP)","description":"","frontmatter":{},"headers":[],"relativePath":"ui/quick.md","filePath":"ui/quick.md"}'),n={name:"ui/quick.md"};function l(h,s,p,o,r,k){return a(),e("div",null,[...s[0]||(s[0]=[t(`

🧩 UI Components (WIP)

Status: Work In Progress. > These are high-level, complex visual components designed to speed up development. They replace native HTML elements with "superpowered" versions that handle their own internal logic, reactivity, and professional styling.

⚠️ Prerequisites

To ensure all components render correctly with their reactive themes and states, your project must have the following versions installed:

  • Tailwind CSS v4+: For the new engine performance and modern CSS variables.
  • DaisyUI v5+: Required for the updated theme-selectors and improved component classes used in the SigPro UI library.

1. What are UI Components?

Unlike Tag Helpers (which are just functional mirrors of HTML tags), SigPro UI Components are smart abstractions:

  • Stateful: They manage complex internal states (like date ranges, search filtering, or API lifecycles).
  • Reactive: Attributes prefixed with $ are automatically tracked via $.watch.
  • Self-Sane: They automatically use ._cleanups to destroy observers or event listeners when removed from the DOM.
  • Themed: Fully compatible with the DaisyUI v5 theme system and Tailwind v4 utility classes.

2. The UI Registry (Available Now)

CategoryComponents
Forms & InputsButton, Input, Select, Autocomplete, Datepicker, Colorpicker, CheckBox, Radio, Range, Rating, Swap
FeedbackAlert, Toast, Modal, Loading, Badge, Tooltip, Indicator
NavigationNavbar, Menu, Drawer, Tabs, Accordion, Dropdown
Data & LayoutRequest, Response, Grid (AG-Grid), List, Stack, Timeline, Stat, Fieldset, Fab

3. Examples with "Superpowers"

A. The Declarative API Flow (Request & Response)

Instead of manually managing loading and error flags, use these together to handle data fetching elegantly.

javascript
// 1. Define the request (it tracks dependencies automatically)
+import{_ as i,o as a,c as e,ae as t}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"🧩 UI Components (WIP)","description":"","frontmatter":{},"headers":[],"relativePath":"ui/quick.md","filePath":"ui/quick.md"}'),n={name:"ui/quick.md"};function l(h,s,p,o,r,k){return a(),e("div",null,[...s[0]||(s[0]=[t(`

🧩 UI Components (WIP)

Status: Work In Progress. > These are high-level, complex visual components designed to speed up development. They replace native HTML elements with "superpowered" versions that handle their own internal logic, reactivity, and professional styling.

⚠️ Prerequisites

To ensure all components render correctly with their reactive themes and states, your project must have the following versions installed:

  • Tailwind CSS v4+: For the new engine performance and modern CSS variables.
  • DaisyUI v5+: Required for the updated theme-selectors and improved component classes used in the SigPro UI library.

1. What are UI Components?

Unlike Tag Helpers (which are just functional mirrors of HTML tags), SigPro UI Components are smart abstractions:

  • Stateful: They manage complex internal states (like date ranges, search filtering, or API lifecycles).
  • Reactive: Attributes prefixed with $ are automatically tracked via $watch.
  • Self-Sane: They automatically use ._cleanups to destroy observers or event listeners when removed from the DOM.
  • Themed: Fully compatible with the DaisyUI v5 theme system and Tailwind v4 utility classes.

2. The UI Registry (Available Now)

CategoryComponents
Forms & InputsButton, Input, Select, Autocomplete, Datepicker, Colorpicker, CheckBox, Radio, Range, Rating, Swap
FeedbackAlert, Toast, Modal, Loading, Badge, Tooltip, Indicator
NavigationNavbar, Menu, Drawer, Tabs, Accordion, Dropdown
Data & LayoutRequest, Response, Grid (AG-Grid), List, Stack, Timeline, Stat, Fieldset, Fab

3. Examples with "Superpowers"

A. The Declarative API Flow (Request & Response)

Instead of manually managing loading and error flags, use these together to handle data fetching elegantly.

javascript
// 1. Define the request (it tracks dependencies automatically)
 const userProfile = Request(
   () => \`https://api.example.com/user/\${userId()}\`
 );
@@ -25,7 +25,7 @@ import{_ as i,o as a,c as e,ae as t}from"./chunks/framework.C8AWLET_.js";const c
   label: "Select Expiry Date",
   $value: myDate,
   range: false
-});

D. Imperative Toasts & Modals

Trigger complex UI elements from your logic. These components use $.mount internally to ensure they are properly cleaned up from memory after they close.

javascript
// Show a notification (Self-destroying after 3s)
+});

D. Imperative Toasts & Modals

Trigger complex UI elements from your logic. These components use $mount internally to ensure they are properly cleaned up from memory after they close.

javascript
// Show a notification (Self-destroying after 3s)
 Toast("Settings saved successfully!", "alert-success", 3000);
 
 // Control a modal with a simple signal
@@ -37,8 +37,8 @@ import{_ as i,o as a,c as e,ae as t}from"./chunks/framework.C8AWLET_.js";const c
   buttons: [
     Button({ class: "btn-error", onclick: doDelete }, "Confirm")
   ]
-}, "This action cannot be undone.");

4. Internationalization (i18n)

The UI library comes with a built-in locale system. It currently supports es and en.

javascript
// Set the global UI language
+}, "This action cannot be undone.");

4. Internationalization (i18n)

The UI library comes with a built-in locale system.

javascript
// Set the global UI language
 SetLocale("en");
 
 // Access translated strings (Returns a signal that tracks the current locale)
-const t = tt("confirm");

5. Best Practices

  • Use $ for Reactivity: If a property starts with $, the component expects a Signal (e.g., $value: mySignal).
  • Automatic Cleaning: You don't need to manually destroy these components if they are inside a $.If or $.For. SigPro's core will "sweep" their internal watchers automatically.
  • Manual Cleanups: If you build custom components using setInterval or third-party observers, always add the stop functions to the element's ._cleanups Set.
`,33)])])}const E=i(n,[["render",l]]);export{c as __pageData,E as default}; +const t = tt("confirm");
`,30)])])}const E=i(n,[["render",l]]);export{c as __pageData,E as default}; diff --git a/docs/assets/ui_quick.md.CsppjR8J.lean.js b/docs/assets/ui_quick.md.CL4k0g3a.lean.js similarity index 84% rename from docs/assets/ui_quick.md.CsppjR8J.lean.js rename to docs/assets/ui_quick.md.CL4k0g3a.lean.js index d5bccf8..e6fc2ca 100644 --- a/docs/assets/ui_quick.md.CsppjR8J.lean.js +++ b/docs/assets/ui_quick.md.CL4k0g3a.lean.js @@ -1 +1 @@ -import{_ as i,o as a,c as e,ae as t}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"🧩 UI Components (WIP)","description":"","frontmatter":{},"headers":[],"relativePath":"ui/quick.md","filePath":"ui/quick.md"}'),n={name:"ui/quick.md"};function l(h,s,p,o,r,k){return a(),e("div",null,[...s[0]||(s[0]=[t("",33)])])}const E=i(n,[["render",l]]);export{c as __pageData,E as default}; +import{_ as i,o as a,c as e,ae as t}from"./chunks/framework.C8AWLET_.js";const c=JSON.parse('{"title":"🧩 UI Components (WIP)","description":"","frontmatter":{},"headers":[],"relativePath":"ui/quick.md","filePath":"ui/quick.md"}'),n={name:"ui/quick.md"};function l(h,s,p,o,r,k){return a(),e("div",null,[...s[0]||(s[0]=[t("",30)])])}const E=i(n,[["render",l]]);export{c as __pageData,E as default}; diff --git a/docs/hashmap.json b/docs/hashmap.json index 44f9799..61f3919 100644 --- a/docs/hashmap.json +++ b/docs/hashmap.json @@ -1 +1 @@ -{"api_for.md":"CGMDz0Px","api_html.md":"DSCIaSlE","api_if.md":"C-36XjK0","api_mount.md":"CoM2SFqU","api_quick.md":"D3C2EJww","api_router.md":"Cn98LjXO","api_signal.md":"BmorvARW","api_tags.md":"BKWIb8mV","api_watch.md":"BEM6Qssx","index.md":"By6smViD","install.md":"C1tyiPaQ","ui_quick.md":"CsppjR8J","vite_plugin.md":"CTs8LDIL"} +{"api_for.md":"3GRpYy3h","api_html.md":"QmhwKd7f","api_if.md":"2-2cm9cb","api_mount.md":"B3RUsjff","api_quick.md":"Coriz0Dx","api_router.md":"DxC_B3iU","api_signal.md":"BmorvARW","api_tags.md":"BPbc38UO","api_watch.md":"Dc5wZqk9","index.md":"By6smViD","install.md":"CrzPbqO_","ui_quick.md":"CL4k0g3a","vite_plugin.md":"CTs8LDIL"} diff --git a/docs/index.html b/docs/index.html index 7017335..419363e 100644 --- a/docs/index.html +++ b/docs/index.html @@ -20,7 +20,7 @@
Skip to content

SigProAtomic Unified Reactive Engine

High-precision atomic reactivity. No Virtual DOM. No compiler. No dependencies.

SigPro Logo

Redefining Modern Reactivity

SigPro is not just another framework; it is a high-performance engine. While other libraries add layers of abstraction that slow down execution, SigPro returns to the essence of the web, leveraging the power of modern browser engines.

Why SigPro?

⚡️ Surgical DOM Efficiency

Unlike React or Vue, SigPro doesn't compare element trees. When a signal changes, SigPro knows exactly which DOM node depends on it and updates it instantly. It is reactive precision at its finest.

🔌 Modular Plugin System

The core is sacred. Any extra functionality—Routing, UI Helpers, or State Persistence—is integrated through a polymorphic plugin system. Load only what your application truly needs.

💾 Native Persistence

SigPro features first-class support for localStorage. Synchronizing your application state with persistent storage is as simple as providing a key when initializing your Signal.

🚦 Built-in Hash Routing

A robust routing system that supports Native Lazy Loading out of the box. Load your components only when the user navigates to them, keeping initial load times near zero.


The "No-Build" Philosophy

In an ecosystem obsessed with compilers, SigPro bets on standardization. Write code today that will still run 10 years from now, without depending on build tools that will eventually become obsolete.

"The best way to optimize code is to not have to process it at all."


Community & Vision

SigPro is an open-source project focused on simplicity and extreme speed. Designed for developers who love the web platform and hate unnecessary "bloatware".

text
Built with ❤️ by NatxoCC for the Modern Web.
- + \ No newline at end of file diff --git a/docs/install.html b/docs/install.html index b0b14f6..c0e74d7 100644 --- a/docs/install.html +++ b/docs/install.html @@ -13,20 +13,20 @@ - + -
Skip to content

Installation & Setup

SigPro is designed to be drop-in ready. Whether you are building a complex application with a bundler or a simple reactive widget in a single HTML file, SigPro scales with your needs.

1. Installation

Choose the method that best fits your workflow:

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
html
<script type="module">
+    
Skip to content

Installation & Setup

SigPro is designed to be drop-in ready. Whether you are building a complex application with a bundler or a simple reactive widget in a single HTML file, SigPro scales with your needs.

1. Installation

Choose the method that best fits your workflow:

bash
npm install sigpro
bash
pnpm add sigpro
bash
yarn add sigpro
bash
bun add sigpro
html
<script type="module">
   // Import the core and UI components
   import { $ } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/+esm';
   import { UI } from 'https://cdn.jsdelivr.net/npm/sigpro@latest/ui/+esm';
   
   // Initialize UI components globally
   UI($);
-</script>

2. Quick Start Examples

SigPro uses PascalCase for Tag Helpers (e.g., Div, Button) to provide a clean, component-like syntax without needing JSX.

javascript
// File: App.js
+</script>

2. Quick Start Examples

SigPro uses PascalCase for Tag Helpers (e.g., Div, Button) to provide a clean, component-like syntax without needing JSX.

javascript
// File: App.js
 import { $ } from 'sigpro'; 
 
 export const App = () => {
@@ -46,7 +46,7 @@
 import { $ } from 'sigpro';
 import { App } from './App.js';
 
-$.mount(App, '#app');
html
<!DOCTYPE html>
+$mount(App, '#app');
html
<!DOCTYPE html>
 <html lang="en">
 <body>
   <div id="app"></div>
@@ -67,11 +67,11 @@
       })
     ]);
 
-    $.mount(App, '#app');
+    $mount(App, '#app');
   </script>
 </body>
 </html>

3. Global by Design

One of SigPro's core strengths is its Global API, which eliminates "Import Hell".

  • The $ Function: Once loaded, it attaches itself to window.$. It handles state, effects, and DOM mounting.
  • Tag Helpers (PascalCase): Common HTML tags (Div, Span, Button, Input, etc.) are automatically registered in the global scope.
  • Custom Components: We recommend using PascalCase (e.g., UserCard) or prefixes like _Input to keep your code organized and distinguish your logic from standard tags.

4. Why no build step?

Because SigPro uses native ES Modules and standard JavaScript functions to generate the DOM, you don't actually need a compiler like Babel or a transformer for JSX.

  • Development: Just save and refresh. Pure JS, no "transpilation" required.
  • Performance: Extremely lightweight. Use any modern bundler (Vite, esbuild) only when you are ready to minify and tree-shake for production.

5. Why SigPro? (The Competitive Edge)

SigPro stands out by removing the "Build Step" tax and the "Virtual DOM" overhead. It is the closest you can get to writing raw HTML/JS while maintaining modern reactivity.

FeatureSigProSolidJSSvelteReactVue
Bundle Size~2KB~7KB~4KB~40KB+~30KB
DOM StrategyDirect DOMDirect DOMCompiled DOMVirtual DOMVirtual DOM
ReactivityFine-grainedFine-grainedCompiledRe-rendersProxies
Build StepOptionalRequiredRequiredRequiredOptional
Learning CurveMinimalMediumLowHighMedium
InitializationUltra-FastVery FastFastSlowMedium

6. Key Advantages

  • Extreme Performance: No Virtual DOM reconciliation. SigPro updates the specific node or attribute instantly when a Signal changes.
  • Fine-Grained Reactivity: State changes only trigger updates where the data is actually used, not on the entire component.
  • Native Web Standards: Everything is a standard JS function. No custom template syntax to learn.
  • Zero Magic: No hidden compilers. What you write is what runs in the browser.
  • Global by Design: Tag Helpers and the $ function are available globally to eliminate "Import Hell" and keep your code clean.

7. Summary

SigPro isn't just another framework; it's a bridge to the native web. By using standard ES Modules and functional DOM generation, you gain the benefits of a modern library with the weight of a utility script.

Because, in the end... why fight the web when we can embrace it?

- + \ No newline at end of file diff --git a/docs/ui/quick.html b/docs/ui/quick.html index 044734f..205adcf 100644 --- a/docs/ui/quick.html +++ b/docs/ui/quick.html @@ -13,13 +13,13 @@ - + -
Skip to content

🧩 UI Components (WIP)

Status: Work In Progress. > These are high-level, complex visual components designed to speed up development. They replace native HTML elements with "superpowered" versions that handle their own internal logic, reactivity, and professional styling.

⚠️ Prerequisites

To ensure all components render correctly with their reactive themes and states, your project must have the following versions installed:

  • Tailwind CSS v4+: For the new engine performance and modern CSS variables.
  • DaisyUI v5+: Required for the updated theme-selectors and improved component classes used in the SigPro UI library.

1. What are UI Components?

Unlike Tag Helpers (which are just functional mirrors of HTML tags), SigPro UI Components are smart abstractions:

  • Stateful: They manage complex internal states (like date ranges, search filtering, or API lifecycles).
  • Reactive: Attributes prefixed with $ are automatically tracked via $.watch.
  • Self-Sane: They automatically use ._cleanups to destroy observers or event listeners when removed from the DOM.
  • Themed: Fully compatible with the DaisyUI v5 theme system and Tailwind v4 utility classes.

2. The UI Registry (Available Now)

CategoryComponents
Forms & InputsButton, Input, Select, Autocomplete, Datepicker, Colorpicker, CheckBox, Radio, Range, Rating, Swap
FeedbackAlert, Toast, Modal, Loading, Badge, Tooltip, Indicator
NavigationNavbar, Menu, Drawer, Tabs, Accordion, Dropdown
Data & LayoutRequest, Response, Grid (AG-Grid), List, Stack, Timeline, Stat, Fieldset, Fab

3. Examples with "Superpowers"

A. The Declarative API Flow (Request & Response)

Instead of manually managing loading and error flags, use these together to handle data fetching elegantly.

javascript
// 1. Define the request (it tracks dependencies automatically)
+    
Skip to content

🧩 UI Components (WIP)

Status: Work In Progress. > These are high-level, complex visual components designed to speed up development. They replace native HTML elements with "superpowered" versions that handle their own internal logic, reactivity, and professional styling.

⚠️ Prerequisites

To ensure all components render correctly with their reactive themes and states, your project must have the following versions installed:

  • Tailwind CSS v4+: For the new engine performance and modern CSS variables.
  • DaisyUI v5+: Required for the updated theme-selectors and improved component classes used in the SigPro UI library.

1. What are UI Components?

Unlike Tag Helpers (which are just functional mirrors of HTML tags), SigPro UI Components are smart abstractions:

  • Stateful: They manage complex internal states (like date ranges, search filtering, or API lifecycles).
  • Reactive: Attributes prefixed with $ are automatically tracked via $watch.
  • Self-Sane: They automatically use ._cleanups to destroy observers or event listeners when removed from the DOM.
  • Themed: Fully compatible with the DaisyUI v5 theme system and Tailwind v4 utility classes.

2. The UI Registry (Available Now)

CategoryComponents
Forms & InputsButton, Input, Select, Autocomplete, Datepicker, Colorpicker, CheckBox, Radio, Range, Rating, Swap
FeedbackAlert, Toast, Modal, Loading, Badge, Tooltip, Indicator
NavigationNavbar, Menu, Drawer, Tabs, Accordion, Dropdown
Data & LayoutRequest, Response, Grid (AG-Grid), List, Stack, Timeline, Stat, Fieldset, Fab

3. Examples with "Superpowers"

A. The Declarative API Flow (Request & Response)

Instead of manually managing loading and error flags, use these together to handle data fetching elegantly.

javascript
// 1. Define the request (it tracks dependencies automatically)
 const userProfile = Request(
   () => `https://api.example.com/user/${userId()}`
 );
@@ -46,7 +46,7 @@
   label: "Select Expiry Date",
   $value: myDate,
   range: false
-});

D. Imperative Toasts & Modals

Trigger complex UI elements from your logic. These components use $.mount internally to ensure they are properly cleaned up from memory after they close.

javascript
// Show a notification (Self-destroying after 3s)
+});

D. Imperative Toasts & Modals

Trigger complex UI elements from your logic. These components use $mount internally to ensure they are properly cleaned up from memory after they close.

javascript
// Show a notification (Self-destroying after 3s)
 Toast("Settings saved successfully!", "alert-success", 3000);
 
 // Control a modal with a simple signal
@@ -58,12 +58,12 @@
   buttons: [
     Button({ class: "btn-error", onclick: doDelete }, "Confirm")
   ]
-}, "This action cannot be undone.");

4. Internationalization (i18n)

The UI library comes with a built-in locale system. It currently supports es and en.

javascript
// Set the global UI language
+}, "This action cannot be undone.");

4. Internationalization (i18n)

The UI library comes with a built-in locale system.

javascript
// Set the global UI language
 SetLocale("en");
 
 // Access translated strings (Returns a signal that tracks the current locale)
-const t = tt("confirm");

5. Best Practices

  • Use $ for Reactivity: If a property starts with $, the component expects a Signal (e.g., $value: mySignal).
  • Automatic Cleaning: You don't need to manually destroy these components if they are inside a $.If or $.For. SigPro's core will "sweep" their internal watchers automatically.
  • Manual Cleanups: If you build custom components using setInterval or third-party observers, always add the stop functions to the element's ._cleanups Set.
- +const t = tt("confirm");
+ \ No newline at end of file diff --git a/docs/vite/plugin.html b/docs/vite/plugin.html index 1559d33..353bc7e 100644 --- a/docs/vite/plugin.html +++ b/docs/vite/plugin.html @@ -19,7 +19,7 @@ -
Skip to content

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.

1. Project Structure

To use the plugin, organize your files within the src/pages directory. The folder hierarchy directly determines your application's URL structure. SigPro uses brackets [param] for dynamic segments.

text
my-sigpro-app/
+    
Skip to content

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.

1. Project Structure

To use the plugin, organize your files within the src/pages directory. The folder hierarchy directly determines your application's URL structure. SigPro uses brackets [param] for dynamic segments.

text
my-sigpro-app/
 ├── src/
 │   ├── pages/
 │   │   ├── index.js          →  #/
@@ -68,7 +68,7 @@
   { path: '/users/:id', component: () => import('/src/pages/users/[id].js') },
   // ...
 ];

Because it uses dynamic import(), Vite automatically performs Code Splitting, meaning each page is its own small JS file that only loads when the user navigates to it.

- + \ No newline at end of file diff --git a/sigpro/sigpro.min.js b/sigpro/sigpro.min.js index 4a44032..dc5cf9c 100644 --- a/sigpro/sigpro.min.js +++ b/sigpro/sigpro.min.js @@ -1 +1 @@ -(()=>{let e=null,t=null;const n=new Set;let o=!1;const r=new WeakMap,s=()=>{if(!o){for(o=!0;n.size>0;){const e=Array.from(n).sort(((e,t)=>(e.depth||0)-(t.depth||0)));n.clear();for(const t of e)t._deleted||t()}o=!1}},a=t=>{e&&!e._deleted&&(t.add(e),e._deps.add(t))},c=t=>{for(const o of t)o===e||o._deleted||(o._isComputed?(o.markDirty(),o._subs&&c(o._subs)):n.add(o));o||queueMicrotask(s)},l=(n,o=null)=>{if("function"==typeof n){const o=new Set;let r,s=!0;const l=()=>{if(l._deleted)return;l._deps.forEach((e=>e.delete(l))),l._deps.clear();const t=e;e=l;try{const a=n();Object.is(r,a)&&!s||(r=a,s=!1,c(o))}finally{e=t}};return l._deps=new Set,l._isComputed=!0,l._subs=o,l._deleted=!1,l.markDirty=()=>s=!0,l.stop=()=>{l._deleted=!0,l._deps.forEach((e=>e.delete(l))),o.clear()},t&&t.cleanups.add(l.stop),()=>(s&&l(),a(o),r)}let r=n;if(o){const e=localStorage.getItem(o);if(null!==e)try{r=JSON.parse(e)}catch{r=e}}const s=new Set;return(...e)=>{if(e.length){const t="function"==typeof e[0]?e[0](r):e[0];Object.is(r,t)||(r=t,o&&localStorage.setItem(o,JSON.stringify(r)),c(s))}return a(s),r}};l.watch=(o,r)=>{const s=Array.isArray(o),a=s?r:o,c=s?o:null;if("function"!=typeof a)return()=>{};const l=t,d=()=>{if(d._deleted)return;d._deps.forEach((e=>e.delete(d))),d._deps.clear(),d._cleanups.forEach((e=>e())),d._cleanups.clear();const n=e,o=t;e=d,t={cleanups:d._cleanups},d.depth=n?n.depth+1:0;try{s?(e=null,a(),e=d,c.forEach((e=>"function"==typeof e&&e()))):a()}finally{e=n,t=o}};return d._deps=new Set,d._cleanups=new Set,d._deleted=!1,d.stop=()=>{d._deleted||(d._deleted=!0,n.delete(d),d._deps.forEach((e=>e.delete(d))),d._cleanups.forEach((e=>e())),l&&l.cleanups.delete(d.stop))},l&&l.cleanups.add(d.stop),d(),d.stop};const d=e=>{e._cleanups&&(e._cleanups.forEach((e=>e())),e._cleanups.clear()),e.childNodes?.forEach(d)},i=e=>{const n=new Set,o=t,r=document.createElement("div");r.style.display="contents",t={cleanups:n};try{const s=e({onCleanup:e=>n.add(e)}),a=e=>{e&&(e._isRuntime?(n.add(e.destroy),r.appendChild(e.container)):Array.isArray(e)?e.forEach(a):r.appendChild(e instanceof Node?e:document.createTextNode(String(e))))};a(s)}finally{t=o}return{_isRuntime:!0,container:r,destroy:()=>{n.forEach((e=>e())),d(r),r.remove()}}};l.html=(e,t={},n=[])=>{(t instanceof Node||Array.isArray(t)||"object"!=typeof t)&&(n=t,t={});const o=document.createElement(e);o._cleanups=new Set;for(let[e,n]of Object.entries(t))if(e.startsWith("on")){const t=e.slice(2).toLowerCase().split(".")[0],r=e=>n(e);o.addEventListener(t,r),o._cleanups.add((()=>o.removeEventListener(t,r)))}else if(e.startsWith("$")){const t=e.slice(1);if(o._cleanups.add(l.watch((()=>{const e="function"==typeof n?n():n;o[t]!==e&&(o[t]=e)}))),"function"==typeof n){const e="checked"===t?"change":"input";o.addEventListener(e,(e=>n(e.target[t])))}}else"function"==typeof n?o._cleanups.add(l.watch((()=>{const t=n();"class"===e?o.className=t||"":null==t?o.removeAttribute(e):o.setAttribute(e,t)}))):o.setAttribute(e,n);const r=e=>{if(Array.isArray(e))return e.forEach(r);if("function"==typeof e){const t=document.createTextNode("");o.appendChild(t);let n=[];o._cleanups.add(l.watch((()=>{const o=e(),r=(Array.isArray(o)?o:[o]).map((e=>e?._isRuntime?e.container:e instanceof Node?e:document.createTextNode(e??"")));n.forEach((e=>{d(e),e.remove()})),r.forEach((e=>t.parentNode?.insertBefore(e,t))),n=r})))}else o.appendChild(e instanceof Node?e:document.createTextNode(e??""))};return r(n),o},l.if=(e,t,n=null)=>{const o=document.createTextNode(""),r=l.html("div",{style:"display:contents"},[o]);let s=null,a=null;return l.watch((()=>{const c=!!("function"==typeof e?e():e);if(c!==a){a=c,s&&s.destroy();const e=c?t:n;e&&(s=i((()=>"function"==typeof e?e():e)),r.insertBefore(s.container,o))}})),r},l.for=(e,t,n)=>{const o=document.createTextNode(""),r=l.html("div",{style:"display:contents"},[o]),s=new Map;return l.watch((()=>{const a=("function"==typeof e?e():e)||[],c=new Set;a.forEach(((e,a)=>{const l=n(e,a);c.add(l);let d=s.get(l);d||(d=i((()=>t(e,a))),s.set(l,d)),r.insertBefore(d.container,o)})),s.forEach(((e,t)=>{c.has(t)||(e.destroy(),s.delete(t))}))})),r},l.router=e=>{const t=l(window.location.hash.replace(/^#/,"")||"/");window.addEventListener("hashchange",(()=>t(window.location.hash.replace(/^#/,"")||"/")));const n=Div({class:"router-outlet"});let o=null;return l.watch([t],(()=>{o&&o.destroy();const r=t(),s=e.find((e=>{const t=e.path.split("/").filter(Boolean),n=r.split("/").filter(Boolean);return t.length===n.length&&t.every(((e,t)=>e.startsWith(":")||e===n[t]))}))||e.find((e=>"*"===e.path));if(s){const e={};s.path.split("/").filter(Boolean).forEach(((t,n)=>{t.startsWith(":")&&(e[t.slice(1)]=r.split("/").filter(Boolean)[n])})),o=i((()=>{const t=s.component(e);return"function"==typeof t?t():t})),n.appendChild(o.container)}})),n},l.go=e=>window.location.hash=e.replace(/^#?\/?/,"#/"),l.mount=(e,t)=>{const n="string"==typeof t?document.querySelector(t):t;if(!n)return;r.has(n)&&r.get(n).destroy();const o=i("function"==typeof e?e:()=>e);return n.replaceChildren(o.container),r.set(n,o),o};"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer address ul ol li dl dt dd a em strong small i b u mark time sub sup pre code blockquote details summary dialog form label input textarea select button option fieldset legend table thead tbody tfoot tr th td caption img video audio canvas svg iframe picture source progress meter".split(/\s+/).forEach((e=>{window[e.charAt(0).toUpperCase()+e.slice(1)]=(t,n)=>l.html(e,t,n)})),window.$=l})();export const{$:$}=window; \ No newline at end of file +(()=>{let e=null,t=null;const n=new Set;let o=!1;const r=new WeakMap,a=()=>{if(!o){for(o=!0;n.size>0;){const e=Array.from(n).sort(((e,t)=>(e.depth||0)-(t.depth||0)));n.clear();for(const t of e)t._deleted||t()}o=!1}},s=t=>{e&&!e._deleted&&(t.add(e),e._deps.add(t))},c=t=>{for(const o of t)o===e||o._deleted||(o._isComputed?(o.markDirty(),o._subs&&c(o._subs)):n.add(o));o||queueMicrotask(a)},l=(n,o=null)=>{if("function"==typeof n){const o=new Set;let r,a=!0;const l=()=>{if(l._deleted)return;l._deps.forEach((e=>e.delete(l))),l._deps.clear();const t=e;e=l;try{const s=n();Object.is(r,s)&&!a||(r=s,a=!1,c(o))}finally{e=t}};return l._deps=new Set,l._isComputed=!0,l._subs=o,l._deleted=!1,l.markDirty=()=>a=!0,l.stop=()=>{l._deleted=!0,l._deps.forEach((e=>e.delete(l))),o.clear()},t&&t.cleanups.add(l.stop),()=>(a&&l(),s(o),r)}let r=n;if(o){const e=localStorage.getItem(o);if(null!==e)try{r=JSON.parse(e)}catch{r=e}}const a=new Set;return(...e)=>{if(e.length){const t="function"==typeof e[0]?e[0](r):e[0];Object.is(r,t)||(r=t,o&&localStorage.setItem(o,JSON.stringify(r)),c(a))}return s(a),r}},d=(o,r)=>{const a=Array.isArray(o),s=a?r:o,c=a?o:null;if("function"!=typeof s)return()=>{};const l=t,d=()=>{if(d._deleted)return;d._deps.forEach((e=>e.delete(d))),d._deps.clear(),d._cleanups.forEach((e=>e())),d._cleanups.clear();const n=e,o=t;e=d,t={cleanups:d._cleanups},d.depth=n?n.depth+1:0;try{a?(e=null,s(),e=d,c.forEach((e=>"function"==typeof e&&e()))):s()}finally{e=n,t=o}};return d._deps=new Set,d._cleanups=new Set,d._deleted=!1,d.stop=()=>{d._deleted||(d._deleted=!0,n.delete(d),d._deps.forEach((e=>e.delete(d))),d._cleanups.forEach((e=>e())),l&&l.cleanups.delete(d.stop))},l&&l.cleanups.add(d.stop),d(),d.stop},i=e=>{e._cleanups&&(e._cleanups.forEach((e=>e())),e._cleanups.clear()),e.childNodes?.forEach(i)},u=e=>{const n=new Set,o=t,r=document.createElement("div");r.style.display="contents",t={cleanups:n};try{const a=e({onCleanup:e=>n.add(e)}),s=e=>{e&&(e._isRuntime?(n.add(e.destroy),r.appendChild(e.container)):Array.isArray(e)?e.forEach(s):r.appendChild(e instanceof Node?e:document.createTextNode(String(e))))};s(a)}finally{t=o}return{_isRuntime:!0,container:r,destroy:()=>{n.forEach((e=>e())),i(r),r.remove()}}},p=(e,t={},n=[])=>{(t instanceof Node||Array.isArray(t)||"object"!=typeof t)&&(n=t,t={});const o=document.createElement(e);o._cleanups=new Set;for(let[e,n]of Object.entries(t)){const t="function"==typeof n,r=["INPUT","TEXTAREA","SELECT"].includes(o.tagName),a="value"===e||"checked"===e;if(r&&a&&t){o._cleanups.add(d((()=>{const t=n();o[e]!==t&&(o[e]=t)})));const t="checked"===e?"change":"input",r=t=>n(t.target[e]);o.addEventListener(t,r),o._cleanups.add((()=>o.removeEventListener(t,r)))}else if(e.startsWith("on")){const t=e.slice(2).toLowerCase().split(".")[0],r=e=>n(e);o.addEventListener(t,r),o._cleanups.add((()=>o.removeEventListener(t,r)))}else t?o._cleanups.add(d((()=>{const t=n();"class"===e?o.className=t||"":null==t?o.removeAttribute(e):o.setAttribute(e,t)}))):o.setAttribute(e,n)}const r=e=>{if(Array.isArray(e))return e.forEach(r);if("function"==typeof e){const t=document.createTextNode("");o.appendChild(t);let n=[];o._cleanups.add(d((()=>{const o=e(),r=(Array.isArray(o)?o:[o]).map((e=>e?._isRuntime?e.container:e instanceof Node?e:document.createTextNode(e??"")));n.forEach((e=>{i(e),e.remove()})),r.forEach((e=>t.parentNode?.insertBefore(e,t))),n=r})))}else o.appendChild(e instanceof Node?e:document.createTextNode(e??""))};return r(n),o},f=(e,t,n=null)=>{const o=document.createTextNode(""),r=p("div",{style:"display:contents"},[o]);let a=null,s=null;return d((()=>{const c=!!("function"==typeof e?e():e);if(c!==s){s=c,a&&a.destroy();const e=c?t:n;e&&(a=u((()=>"function"==typeof e?e():e)),r.insertBefore(a.container,o))}})),r};f.not=(e,t,n)=>f((()=>!("function"==typeof e?e():e)),t,n);const h=e=>{const t=l(window.location.hash.replace(/^#/,"")||"/");window.addEventListener("hashchange",(()=>t(window.location.hash.replace(/^#/,"")||"/")));const n=Div({class:"router-outlet"});let o=null;return d([t],(()=>{o&&o.destroy();const r=t(),a=e.find((e=>{const t=e.path.split("/").filter(Boolean),n=r.split("/").filter(Boolean);return t.length===n.length&&t.every(((e,t)=>e.startsWith(":")||e===n[t]))}))||e.find((e=>"*"===e.path));if(a){const e={};a.path.split("/").filter(Boolean).forEach(((t,n)=>{t.startsWith(":")&&(e[t.slice(1)]=r.split("/").filter(Boolean)[n])})),h.params&&h.params(e),o=u((()=>{const t=a.component(e);return"function"==typeof t?t():t})),n.appendChild(o.container)}})),n};h.params=l({}),h.to=e=>window.location.hash=e.replace(/^#?\/?/,"#/"),h.back=()=>window.history.back(),h.path=()=>window.location.hash.replace(/^#/,"")||"/";const y={$:l,$if:f,$for:(e,t,n)=>{const o=document.createTextNode(""),r=p("div",{style:"display:contents"},[o]),a=new Map;return d((()=>{const s=("function"==typeof e?e():e)||[],c=new Set;s.forEach(((e,s)=>{const l=n(e,s);c.add(l);let d=a.get(l);d||(d=u((()=>t(e,s))),a.set(l,d)),r.insertBefore(d.container,o)})),a.forEach(((e,t)=>{c.has(t)||(e.destroy(),a.delete(t))}))})),r},$watch:d,$mount:(e,t)=>{const n="string"==typeof t?document.querySelector(t):t;if(!n)return;r.has(n)&&r.get(n).destroy();const o=u("function"==typeof e?e:()=>e);return n.replaceChildren(o.container),r.set(n,o),o},$router:h,$html:p};for(const[e,t]of Object.entries(y))Object.defineProperty(window,e,{value:t,writable:!1,configurable:!1});"div span p h1 h2 h3 h4 h5 h6 br hr section article aside nav main header footer address ul ol li dl dt dd a em strong small i b u mark time sub sup pre code blockquote details summary dialog form label input textarea select button option fieldset legend table thead tbody tfoot tr th td caption img video audio canvas svg iframe picture source progress meter".split(/\s+/).forEach((e=>{const t=e.charAt(0).toUpperCase()+e.slice(1);Object.defineProperty(window,t,{value:(t,n)=>p(e,t,n),writable:!1,configurable:!0,enumerable:!0})}))})();export const{$:$,$watch:$watch,$html:$html,$if:$if,$for:$for,$router:$router,$mount:$mount}=window; \ No newline at end of file diff --git a/src/docs/.vitepress/config.js b/src/docs/.vitepress/config.js index 5c57f93..f2a3c32 100644 --- a/src/docs/.vitepress/config.js +++ b/src/docs/.vitepress/config.js @@ -40,13 +40,13 @@ export default defineConfig({ text: 'API Reference', items: [ { text: 'Quick Start', link: '/api/quick' }, - { text: '$', link: '/api/signal' }, - { text: '$.watch', link: '/api/watch' }, - { text: '$.if', link: '/api/if' }, - { text: '$.for', link: '/api/for' }, - { text: '$.router', link: '/api/router' }, - { text: '$.mount', link: '/api/mount' }, - { text: '$.html', link: '/api/html' }, + { text: '$ signals', link: '/api/signal' }, + { text: '$watch', link: '/api/watch' }, + { text: '$if', link: '/api/if' }, + { text: '$for', link: '/api/for' }, + { text: '$router', link: '/api/router' }, + { text: '$mount', link: '/api/mount' }, + { text: '$html', link: '/api/html' }, { text: 'Tags', link: '/api/tags' }, ] }, diff --git a/src/docs/api/for.md b/src/docs/api/for.md index 588229a..c6efc87 100644 --- a/src/docs/api/for.md +++ b/src/docs/api/for.md @@ -1,11 +1,11 @@ -# ♻️ Reactive Lists: `$.for( )` +# ♻️ Reactive Lists: `$for( )` -The `$.for` function is a high-performance list renderer. It maps an array (or a Signal containing an array) to DOM nodes. Unlike a simple `.map()`, `$.for` is **keyed**, meaning it only updates, moves, or deletes the specific items that changed. +The `$for` function is a high-performance list renderer. It maps an array (or a Signal containing an array) to DOM nodes. Unlike a simple `.map()`, `$for` is **keyed**, meaning it only updates, moves, or deletes the specific items that changed. ## 🛠️ Function Signature ```typescript -$.for( +$for( source: Signal | Function | any[], render: (item: any, index: number) => HTMLElement, keyFn: (item: any, index: number) => string | number @@ -34,7 +34,7 @@ const users = $([ ]); Ul({ class: "list" }, [ - $.for(users, + $for(users, (user) => Li({ class: "p-2" }, user.name), (user) => user.id ) @@ -48,7 +48,7 @@ If your array contains simple strings or numbers, you can use the value itself o const tags = $(["Tech", "JS", "Web"]); Div({ class: "flex gap-1" }, [ - $.for(tags, (tag) => Badge(tag), (tag) => tag) + $for(tags, (tag) => Badge(tag), (tag) => tag) ]); ``` @@ -56,7 +56,7 @@ Div({ class: "flex gap-1" }, [ ## 🏗️ How it Works (The Reconciliation) -When the `source` signal changes, `$.for` performs the following steps: +When the `source` signal changes, `$for` performs the following steps: 1. **Key Diffing**: It compares the new keys with the previous ones stored in an internal `Map`. 2. **Node Reuse**: If a key already exists, the DOM node is **reused** and moved to its new position. No new elements are created. @@ -69,13 +69,13 @@ When the `source` signal changes, `$.for` performs the following steps: ## 💡 Performance Tips * **Stable Keys**: Never use `Math.random()` as a key. This will force SigPro to destroy and recreate the entire list on every update, killing performance. -* **Component Encapsulation**: If each item in your list has its own complex internal state, `$.for` ensures that state is preserved even if the list is reordered, as long as the key remains the same. +* **Component Encapsulation**: If each item in your list has its own complex internal state, `$for` ensures that state is preserved even if the list is reordered, as long as the key remains the same. --- ## 🧪 Summary Comparison -| Feature | Standard `Array.map` | SigPro `$.for` | +| Feature | Standard `Array.map` | SigPro `$for` | | :--- | :--- | :--- | | **Re-render** | Re-renders everything | Only updates changes | | **DOM Nodes** | Re-created every time | **Reused via Keys** | diff --git a/src/docs/api/html.md b/src/docs/api/html.md index e77938f..2b96e8c 100644 --- a/src/docs/api/html.md +++ b/src/docs/api/html.md @@ -1,11 +1,11 @@ -# 🏗️ The DOM Factory: `$.html( )` +# 🏗️ The DOM Factory: `$html( )` -`$.html` is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It uses `$.watch` to maintain a live, high-performance link between your Signals and the Document Object Model. +`$html` is the internal engine that creates, attributes, and attaches reactivity to DOM elements. It uses `$.watch` to maintain a live, high-performance link between your Signals and the Document Object Model. ## 🛠 Function Signature ```typescript -$.html(tagName: string, props?: Object, children?: any[] | any): HTMLElement +$html(tagName: string, props?: Object, children?: any[] | any): HTMLElement ``` | Parameter | Type | Required | Description | @@ -34,7 +34,7 @@ Button({ ``` ### 3. Reactive Attributes (One-Way) -If an attribute value is a **function** (like a Signal), `$.html` creates an internal **`$.watch`** to keep the DOM in sync with the state. +If an attribute value is a **function** (like a Signal), `$html` creates an internal **`$.watch`** to keep the DOM in sync with the state. ```javascript Div({ @@ -72,7 +72,7 @@ Div({}, [ --- ## 🧹 Memory Management (Internal) -Every element created with `$.html` is "self-aware" regarding its reactive dependencies. +Every element created with `$html` is "self-aware" regarding its reactive dependencies. * **`._cleanups`**: A hidden `Set` attached to the element that stores all `stop()` functions from its internal `$.watch` calls and event listeners. * **Lifecycle**: When an element is removed by a Controller (`$.if`, `$.for`, or `$.router`), SigPro performs a recursive **"sweep"** to execute these cleanups, ensuring **zero memory leaks**. @@ -80,12 +80,12 @@ Every element created with `$.html` is "self-aware" regarding its reactive depen ## 💡 Tag Constructors (The Shortcuts) -Instead of writing `$.html("div", ...)` every time, SigPro provides PascalCase global functions for all standard HTML tags: +Instead of writing `$html("div", ...)` every time, SigPro provides PascalCase global functions for all standard HTML tags: ```javascript // This: Div({ class: "wrapper" }, [ Span("Hello") ]) // Is exactly equivalent to: -$.html("div", { class: "wrapper" }, [ $.html("span", {}, "Hello") ]) +$html("div", { class: "wrapper" }, [ $html("span", {}, "Hello") ]) ``` diff --git a/src/docs/api/if.md b/src/docs/api/if.md index 5326d1a..204cfa0 100644 --- a/src/docs/api/if.md +++ b/src/docs/api/if.md @@ -1,12 +1,12 @@ -# 🔀 Reactive Branching: `$.if( )` +# 🔀 Reactive Branching: `$if( )` -The `$.if` function is a reactive control flow operator. It manages the conditional rendering of components, ensuring that only the active branch exists in the DOM and in memory. +The `$if` function is a reactive control flow operator. It manages the conditional rendering of components, ensuring that only the active branch exists in the DOM and in memory. ## 🛠️ Function Signature ```typescript -$.if( +$if( condition: Signal | Function, thenVal: Component | Node, otherwiseVal?: Component | Node @@ -34,7 +34,7 @@ const isVisible = $(false); Div([ Button({ onclick: () => isVisible(!isVisible()) }, "Toggle Message"), - $.if(isVisible, + $if(isVisible, P("Now you see me!"), P("Now you don't...") ) @@ -42,10 +42,10 @@ Div([ ``` ### 2. Lazy Component Loading -Unlike using a hidden class (CSS `display: none`), `$.if` is **lazy**. The branch that isn't active **is never created**. This saves memory and initial processing time. +Unlike using a hidden class (CSS `display: none`), `$if` is **lazy**. The branch that isn't active **is never created**. This saves memory and initial processing time. ```javascript -$.if(() => user.isLogged(), +$if(() => user.isLogged(), () => Dashboard(), // Only executed if logged in () => LoginGate() // Only executed if guest ) @@ -55,7 +55,7 @@ $.if(() => user.isLogged(), ## 🧹 Automatic Cleanup -One of the core strengths of `$.if` is its integrated **Cleanup** logic. SigPro ensures that when a branch is swapped out, it is completely purged. +One of the core strengths of `$if` is its integrated **Cleanup** logic. SigPro ensures that when a branch is swapped out, it is completely purged. 1. **Stop Watchers**: All `$.watch` calls inside the inactive branch are permanently stopped. 2. **Unbind Events**: Event listeners attached via `$.html` are removed. @@ -70,7 +70,7 @@ One of the core strengths of `$.if` is its integrated **Cleanup** logic. SigPro * **Function Wrappers**: If your branches are heavy (e.g., they contain complex components), wrap them in a function `() => MyComponent()`. This prevents the component from being initialized until the condition actually meets its requirement. * **Logical Expressions**: You can pass a complex computed function as the condition: ```javascript - $.if(() => count() > 10 && status() === 'ready', + $if(() => count() > 10 && status() === 'ready', Span("Threshold reached!") ) ``` @@ -79,7 +79,7 @@ One of the core strengths of `$.if` is its integrated **Cleanup** logic. SigPro ## 🏗️ Technical Comparison -| Feature | Standard CSS `hidden` | SigPro `$.if` | +| Feature | Standard CSS `hidden` | SigPro `$if` | | :--- | :--- | :--- | | **DOM Presence** | Always present | Only if active | | **Reactivity** | Still processing in background | **Paused/Destroyed** | diff --git a/src/docs/api/mount.md b/src/docs/api/mount.md index 874d26c..bee414b 100644 --- a/src/docs/api/mount.md +++ b/src/docs/api/mount.md @@ -1,11 +1,11 @@ -# 🔌 Application Mounter: `$.mount( )` +# 🔌 Application Mounter: `$mount( )` -The `$.mount` function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document and initializing its reactive lifecycle. +The `$mount` function is the entry point of your reactive world. It bridges the gap between your SigPro logic and the browser's Real DOM by injecting a component into the document and initializing its reactive lifecycle. ## 🛠️ Function Signature ```typescript -$.mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject +$mount(node: Function | HTMLElement, target?: string | HTMLElement): RuntimeObject ``` | Parameter | Type | Default | Description | @@ -27,7 +27,7 @@ import { $ } from './sigpro.js'; import App from './App.js'; // Mounts your main App component -$.mount(App, '#app-root'); +$mount(App, '#app-root'); ``` ### 2. Reactive "Islands" @@ -42,17 +42,17 @@ const Counter = () => { }; // Mount only the counter into a specific sidebar div -$.mount(Counter, '#sidebar-widget'); +$mount(Counter, '#sidebar-widget'); ``` --- ## 🔄 How it Works (Lifecycle & Cleanup) -When `$.mount` is executed, it performs these critical steps to ensure a leak-free environment: +When `$mount` is executed, it performs these critical steps to ensure a leak-free environment: -1. **Duplicate Detection**: If you call `$.mount` on a target that already has a SigPro instance, it automatically calls `.destroy()` on the previous instance. This prevents "Zombie Effects" from stacking in memory. -2. **Internal Scoping**: It executes the component function inside an internal **Reactive Owner**. This captures every `$.watch` and event listener created during the render. +1. **Duplicate Detection**: If you call `$mount` on a target that already has a SigPro instance, it automatically calls `.destroy()` on the previous instance. This prevents "Zombie Effects" from stacking in memory. +2. **Internal Scoping**: It executes the component function inside an internal **Reactive Owner**. This captures every `$watch` and event listener created during the render. 3. **Target Injection**: It clears the target using `replaceChildren()` and appends the new component. 4. **Runtime Creation**: It returns a control object: * `container`: The actual DOM element created. @@ -62,10 +62,10 @@ When `$.mount` is executed, it performs these critical steps to ensure a leak-fr ## 🛑 Manual Unmounting -While SigPro handles most cleanups automatically (via `$.If`, `$.For`, and `$.router`), you can manually destroy any mounted instance. This is vital for imperatively managed UI like **Toasts** or **Modals**. +While SigPro handles most cleanups automatically (via `$if`, `$for`, and `$router`), you can manually destroy any mounted instance. This is vital for imperatively managed UI like **Toasts** or **Modals**. ```javascript -const instance = $.mount(MyToast, '#toast-container'); +const instance = $mount(MyToast, '#toast-container'); // Later, to remove the toast and kill its reactivity: instance.destroy(); @@ -77,9 +77,9 @@ instance.destroy(); | Goal | Code Pattern | | :--- | :--- | -| **Mount to body** | `$.mount(App)` | -| **Mount to CSS Selector** | `$.mount(App, '#root')` | -| **Mount to DOM Node** | `$.mount(App, myElement)` | -| **Clean & Re-mount** | Calling `$.mount` again on the same target | -| **Total Cleanup** | `const app = $.mount(App); app.destroy();` | +| **Mount to body** | `$mount(App)` | +| **Mount to CSS Selector** | `$mount(App, '#root')` | +| **Mount to DOM Node** | `$mount(App, myElement)` | +| **Clean & Re-mount** | Calling `$mount` again on the same target | +| **Total Cleanup** | `const app = $mount(App); app.destroy();` | diff --git a/src/docs/api/quick.md b/src/docs/api/quick.md index e5982cd..45385cc 100644 --- a/src/docs/api/quick.md +++ b/src/docs/api/quick.md @@ -8,13 +8,13 @@ SigPro is a high-performance micro-framework that updates the **Real DOM** surgi | :--- | :--- | :--- | | **`$(val, key?)`** | `(any, string?) => Signal` | Creates a **Signal**. If `key` is provided, it persists in `localStorage`. | | **`$(fn)`** | `(function) => Computed` | Creates a **Computed Signal** that auto-updates when its dependencies change. | -| **`$.watch(fn)`** | `(function) => stopFn` | **Automatic Mode:** Tracks any signal touched inside. Returns a stop function. | -| **`$.watch(deps, fn)`** | `(Array, function) => stopFn` | **Explicit Mode:** Only runs when signals in `deps` change. Used for Cleanup. | -| **`$.html(tag, props, kids)`** | `(string, obj, any) => Element` | The low-level DOM factory. Attaches `._cleanups` to every element. | -| **`$.if(cond, then, else?)`** | `(Signal, fn, fn?) => Node` | Reactive conditional. Automatically destroys the "else" branch memory. | -| **`$.for(list, itemFn)`** | `(Signal, fn) => Node` | Optimized list renderer. Manages individual item lifecycles. | -| **`$.router(routes)`** | `(Array) => Element` | Hash-based SPA router. Uses Explicit Watch to prevent memory leaks. | -| **`$.mount(node, target)`** | `(any, string\|Node) => Runtime` | Entry point. Creates a root instance with `.destroy()` capabilities. | +| **`$watch(fn)`** | `(function) => stopFn` | **Automatic Mode:** Tracks any signal touched inside. Returns a stop function. | +| **`$watch(deps, fn)`** | `(Array, function) => stopFn` | **Explicit Mode:** Only runs when signals in `deps` change. Used for Cleanup. | +| **`$html(tag, props, kids)`** | `(string, obj, any) => Element` | The low-level DOM factory. Attaches `._cleanups` to every element. | +| **`$if(cond, then, else?)`** | `(Signal, fn, fn?) => Node` | Reactive conditional. Automatically destroys the "else" branch memory. | +| **`$for(list, itemFn)`** | `(Signal, fn) => Node` | Optimized list renderer. Manages individual item lifecycles. | +| **`$router(routes)`** | `(Array) => Element` | Hash-based SPA router. Uses Explicit Watch to prevent memory leaks. | +| **`$mount(node, target)`** | `(any, string\|Node) => Runtime` | Entry point. Creates a root instance with `.destroy()` capabilities. | --- @@ -32,7 +32,7 @@ Tag({ attributes }, [children]) | Pattern | Code Example | Behavior | | :--- | :--- | :--- | | **Static** | `class: "text-red"` | Standard HTML attribute string. | -| **Reactive** | `disabled: isLoading` | Updates automatically via internal `$.watch`. | +| **Reactive** | `disabled: isLoading` | Updates automatically via internal `$watch`. | | **Two-way** | `$value: username` | **Binding Operator**: Syncs input $\leftrightarrow$ signal both ways. | | **Text** | `P({}, () => count())` | Updates text node surgically without re-rendering the `P`. | | **Boolean** | `hidden: isHidden` | Toggles the attribute based on signal truthiness. | diff --git a/src/docs/api/router.md b/src/docs/api/router.md index 73950c6..473bd4d 100644 --- a/src/docs/api/router.md +++ b/src/docs/api/router.md @@ -1,40 +1,40 @@ -# 🚦 Routing: `$.router( )` & `$.go( )` +# 🚦 Routing: `$router()` & Utilities SigPro includes a built-in, lightweight **Hash Router** to create Single Page Applications (SPA). It manages the URL state, matches components to paths, and handles the lifecycle of your pages automatically. ## 🛠 Router Signature ```typescript -$.router(routes: Route[]): HTMLElement +$router(routes: Route[]): HTMLElement ``` ### Route Object | Property | Type | Description | | :--- | :--- | :--- | -| **`path`** | `string` | The URL fragment (e.g., `"/home"`, `"/user/:id"`, or `"*"`). | -| **`component`** | `Function` | A function that returns a Tag or a `$.view`. | +| **`path`** | `string` | The URL fragment (e.g., `"/"`, `"/user/:id"`, or `"*"`). | +| **`component`** | `Function` | A function that returns a Node, a String, or a reactive View. | --- ## 📖 Usage Patterns ### 1. Defining Routes -The router returns a `div` element with the class `.router-outlet`. When the hash changes, the router destroys the previous view and mounts the new one inside this container. +The `$router` returns a `div` element with the class `.router-outlet`. When the hash changes, the router destroys the previous view and mounts the new one inside this container. ```javascript const App = () => Div({ class: "app-layout" }, [ Navbar(), // The router outlet is placed here - $.router([ + $router([ { path: "/", component: Home }, - { path: "/profile/:id", component: UserProfile }, - { path: "*", component: NotFound } + { path: "/profile/:id", component: (params) => UserProfile(params.id) }, + { path: "*", component: () => H1("404 Not Found") } ]) ]); ``` ### 2. Dynamic Segments (`:id`) -When a path contains a colon (e.g., `:id`), the router parses that segment and passes it as an object to the component function. +The router automatically parses URL parameters (like `:id`) and passes them as an object to the component function. You can also access them globally via `$router.params()`. ```javascript // If the URL is #/profile/42 @@ -45,43 +45,52 @@ const UserProfile = (params) => { --- -## 🏎 Programmatic Navigation: `$.go( )` +## 🏎 Navigation Utilities -To navigate between pages without using an `` tag, use `$.go`. This function updates the browser's hash, which in turn triggers the router to swap components. +SigPro provides a set of programmatic methods to control the history and read the state. -### Signature -```typescript -$.go(path: string): void +### `$router.to(path)` +Navigates to a specific path. It automatically formats the hash (e.g., `/home` becomes `#/home`). +```javascript +Button({ onclick: () => $router.to("/dashboard") }, "Go to Dashboard") ``` -### Examples +### `$router.back()` +Goes back to the previous page in the browser history. ```javascript -// Navigate to a static path -Button({ onclick: () => $.go("/") }, "Home") +Button({ onclick: () => $router.back() }, "Back") +``` -// Navigate to a dynamic path -Button({ - onclick: () => $.go(`/profile/${user.id}`) -}, "View Profile") +### `$router.path()` +Returns the current path (without the `#`). +```javascript +$watch(() => { + console.log("Current path is:", $router.path()); +}); ``` --- ## ⚡ Technical Behavior -* **Automatic Cleanup**: Every time you navigate, the router calls `.destroy()` on the previous `$.view`. This ensures that all **signals, effects, and event listeners** from the old page are purged from memory. +* **Automatic Cleanup**: Every time you navigate, the router calls `.destroy()` on the previous view. This ensures that all **signals, effects, and event listeners** from the old page are purged from memory. +* **Reactive Params**: `$router.params` is a signal (`$`). You can react to parameter changes without re-mounting the entire router outlet. * **Hash-Based**: By using `window.location.hash`, SigPro works out-of-the-box on any static hosting (like GitHub Pages or Vercel) without needing server-side redirects. -* **Initial Load**: On the first execution, `$.router` automatically reads the current hash or defaults to `/` if empty. --- ## 🎨 Styling the Outlet -Since the router returns a standard DOM element, you can style the transition or the container easily: +The router returns a standard `div` with the `.router-outlet` class. You can easily style it or add transitions: ```css .router-outlet { - flex: 1; - padding: 2rem; - animation: fadeIn 0.2s ease-in; + display: block; + min-height: 100vh; + animation: fadeIn 0.3s ease; } -``` + +@keyframes fadeIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} +``` \ No newline at end of file diff --git a/src/docs/api/tags.md b/src/docs/api/tags.md index bad34b5..a07f366 100644 --- a/src/docs/api/tags.md +++ b/src/docs/api/tags.md @@ -1,12 +1,12 @@ # 🎨 Global Tag Helpers -In **SigPro**, you don't need to manually type `$.html('div', ...)` for every element. To keep your code declarative and readable, the engine automatically generates **Global Helper Functions** for all standard HTML5 tags upon initialization. +In **SigPro**, you don't need to manually type `$html('div', ...)` for every element. To keep your code declarative and readable, the engine automatically generates **Global Helper Functions** for all standard HTML5 tags upon initialization. ## 1. How it Works SigPro iterates through a manifest of standard HTML tags and attaches a wrapper function for each one directly to the `window` object. This creates a specialized **DSL** (Domain Specific Language) that looks like a template engine but is **100% standard JavaScript**. -* **Under the hood:** `$.html('button', { onclick: ... }, 'Click')` +* **Under the hood:** `$html('button', { onclick: ... }, 'Click')` * **SigPro Style:** `Button({ onclick: ... }, 'Click')` --- @@ -51,10 +51,10 @@ Section([ ## 4. Reactive Power -These helpers are natively wired into SigPro's **`$.watch`** engine. +These helpers are natively wired into SigPro's **`$watch`** engine. ### Reactive Attributes (One-Way) -Simply pass a Signal (function) to any attribute. SigPro creates an internal `$.watch` to keep the DOM in sync. +Simply pass a Signal (function) to any attribute. SigPro creates an internal `$watch` to keep the DOM in sync. ```javascript const theme = $("light"); @@ -80,12 +80,12 @@ Input({ > **Pro Tip:** If you want an input to be **read-only** but still reactive, wrap the signal in an anonymous function: `value: () => search()`. This prevents the "backwards" synchronization. ### Dynamic Flow & Cleanup -Combine tags with Core controllers. SigPro automatically cleans up the `$.watch` instances and event listeners when nodes are removed from the DOM. +Combine tags with Core controllers. SigPro automatically cleans up the `$watch` instances and event listeners when nodes are removed from the DOM. ```javascript const items = $(["Apple", "Banana", "Cherry"]); Ul({ class: "list-disc" }, [ - $.for(items, (item) => Li(item), (item) => item) + $for(items, (item) => Li(item), (item) => item) ]); ``` diff --git a/src/docs/api/watch.md b/src/docs/api/watch.md index 6bb6456..5f7f38c 100644 --- a/src/docs/api/watch.md +++ b/src/docs/api/watch.md @@ -1,15 +1,15 @@ -# ⚡ Reactivity Control: `$.watch( )` +# ⚡ Reactivity Control: `$watch( )` -The `$.watch` function is the reactive engine of SigPro. It allows you to execute code automatically when signals change. `$.watch` is **polymorphic**: it can track dependencies automatically or follow an explicit list. +The `$watch` function is the reactive engine of SigPro. It allows you to execute code automatically when signals change. `$watch` is **polymorphic**: it can track dependencies automatically or follow an explicit list. ## 🛠 Function Signature ```typescript // Automatic Mode (Magic Tracking) -$.watch(callback: Function): StopFunction +$watch(callback: Function): StopFunction // Explicit Mode (Isolated Dependencies) -$.watch(deps: Signal[], callback: Function): StopFunction +$watch(deps: Signal[], callback: Function): StopFunction ``` | Parameter | Type | Required | Description | @@ -29,7 +29,7 @@ Any signal you "touch" inside the callback becomes a dependency. SigPro tracks t ```javascript const count = $(0); -$.watch(() => { +$watch(() => { // Re-runs every time 'count' changes console.log(`Count is: ${count()}`); }); @@ -42,7 +42,7 @@ This mode **isolates** execution. The callback only triggers when the signals in const sPath = $("/home"); const user = $("Admin"); -$.watch([sPath], () => { +$watch([sPath], () => { // Only triggers when 'sPath' changes. // Changes to 'user' will NOT trigger this, preventing accidental re-renders. console.log(`Navigating to ${sPath()} as ${user()}`); @@ -53,11 +53,11 @@ $.watch([sPath], () => { If your logic creates timers, event listeners, or other reactive effects, SigPro tracks them as "children" of the current watch. When the watcher re-runs or stops, it kills everything inside automatically. ```javascript -$.watch(() => { +$watch(() => { const timer = setInterval(() => console.log("Tick"), 1000); // Register a manual cleanup if needed - // Or simply rely on SigPro to kill nested $.watch() calls + // Or simply rely on SigPro to kill nested $watch() calls return () => clearInterval(timer); }); ``` @@ -68,7 +68,7 @@ $.watch(() => { Call the returned function to manually kill the watcher. This is essential for manual DOM injections (like Toasts) or long-lived background processes. ```javascript -const stop = $.watch(() => console.log(count())); +const stop = $watch(() => console.log(count())); // Later... stop(); // The link between the signal and this code is physically severed. @@ -83,7 +83,7 @@ SigPro batches updates. If you update multiple signals in the same execution blo const a = $(0); const b = $(0); -$.watch(() => console.log(a(), b())); +$watch(() => console.log(a(), b())); // This triggers only ONE re-run. a(1); diff --git a/src/docs/install.md b/src/docs/install.md index d373251..e0f9db3 100644 --- a/src/docs/install.md +++ b/src/docs/install.md @@ -66,7 +66,7 @@ export const App = () => { import { $ } from 'sigpro'; import { App } from './App.js'; -$.mount(App, '#app'); +$mount(App, '#app'); ``` ```html [Classic (Direct CDN)] @@ -91,7 +91,7 @@ $.mount(App, '#app'); }) ]); - $.mount(App, '#app'); + $mount(App, '#app'); diff --git a/src/docs/ui/buttons.md b/src/docs/ui/buttons.md deleted file mode 100644 index 2df61cd..0000000 --- a/src/docs/ui/buttons.md +++ /dev/null @@ -1,51 +0,0 @@ -# Buttons - -The **SigPro** Button component wraps [DaisyUI 5](https://daisyui.com/components/button/) styles with native reactive logic. - -## Basic Usage - - - - diff --git a/src/docs/ui/quick.md b/src/docs/ui/quick.md index 9339cfe..35e92e4 100644 --- a/src/docs/ui/quick.md +++ b/src/docs/ui/quick.md @@ -17,7 +17,7 @@ To ensure all components render correctly with their reactive themes and states, Unlike **Tag Helpers** (which are just functional mirrors of HTML tags), SigPro UI Components are smart abstractions: * **Stateful**: They manage complex internal states (like date ranges, search filtering, or API lifecycles). -* **Reactive**: Attributes prefixed with `$` are automatically tracked via `$.watch`. +* **Reactive**: Attributes prefixed with `$` are automatically tracked via `$watch`. * **Self-Sane**: They automatically use `._cleanups` to destroy observers or event listeners when removed from the DOM. * **Themed**: Fully compatible with the DaisyUI v5 theme system and Tailwind v4 utility classes. @@ -85,7 +85,7 @@ Datepicker({ ``` ### D. Imperative Toasts & Modals -Trigger complex UI elements from your logic. These components use `$.mount` internally to ensure they are properly cleaned up from memory after they close. +Trigger complex UI elements from your logic. These components use `$mount` internally to ensure they are properly cleaned up from memory after they close. ```javascript // Show a notification (Self-destroying after 3s) @@ -107,7 +107,7 @@ Modal({ ## 4. Internationalization (i18n) -The UI library comes with a built-in locale system. It currently supports `es` and `en`. +The UI library comes with a built-in locale system. ```javascript // Set the global UI language @@ -117,11 +117,4 @@ SetLocale("en"); const t = tt("confirm"); ``` ---- - -## 5. Best Practices - -* **Use `$` for Reactivity**: If a property starts with `$`, the component expects a Signal (e.g., `$value: mySignal`). -* **Automatic Cleaning**: You don't need to manually destroy these components if they are inside a `$.If` or `$.For`. SigPro's core will "sweep" their internal watchers automatically. -* **Manual Cleanups**: If you build custom components using `setInterval` or third-party observers, always add the stop functions to the element's `._cleanups` Set. diff --git a/UI/aggrid/aggrid-lib.js b/ui/aggrid/aggrid-lib.js similarity index 100% rename from UI/aggrid/aggrid-lib.js rename to ui/aggrid/aggrid-lib.js diff --git a/UI/aggrid/aggrid.js b/ui/aggrid/aggrid.js similarity index 100% rename from UI/aggrid/aggrid.js rename to ui/aggrid/aggrid.js diff --git a/UI/aggrid/vite.config.js b/ui/aggrid/vite.config.js similarity index 100% rename from UI/aggrid/vite.config.js rename to ui/aggrid/vite.config.js diff --git a/UI/index.js b/ui/index.js similarity index 100% rename from UI/index.js rename to ui/index.js diff --git a/UI/sigpro-ui.js b/ui/sigpro-ui.js similarity index 99% rename from UI/sigpro-ui.js rename to ui/sigpro-ui.js index 30dbb12..5cf5af4 100644 --- a/UI/sigpro-ui.js +++ b/ui/sigpro-ui.js @@ -2,8 +2,8 @@ * SigPro UI - daisyUI v5 & Tailwind v4 Plugin * Provides a set of reactive functional components, flow control and i18n. */ -export const UI = (core, defaultLang = "es") => { - const { $, $if, $for, $watch, $html, $mount } = core; +import { $, $if, $for, $watch, $html, $mount } from './sigpro.js'; +export const UI = (defaultLang = "es") => { const ui = {}; // --- I18N CORE --- diff --git a/ui/sigpro-ui.min.js b/ui/sigpro-ui.min.js new file mode 100644 index 0000000..7dd6264 --- /dev/null +++ b/ui/sigpro-ui.min.js @@ -0,0 +1 @@ +import{$,$if,$for,$watch,$html,$mount}from"./sigpro.js";export const UI=(t="es")=>{const l={},e={es:{close:"Cerrar",confirm:"Confirmar",cancel:"Cancelar",search:"Buscar...",loading:"Cargando..."},en:{close:"Close",confirm:"Confirm",cancel:"Cancel",search:"Search...",loading:"Loading..."}},a=$(t);l.SetLocale=t=>a(t);const s=t=>()=>e[a()][t]||t,A=t=>"function"==typeof t?t():t,n=(t,l)=>"function"==typeof l?()=>`${t} ${l()||""}`.trim():`${t} ${l||""}`.trim(),o="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACLSURBVDiN7dO9CQJBFEXhb38K0FwQrMNEVpuwB0NjrcYabECsQk0sQ1mTF4zIjrgmBh54MMx998AEwzOrmC5e8gJjbDHCJO7PHYI0v2JT4Ig9DljGwq5DkOZTLOCOMoIhBpknpHmFWx3ldaaUo6oTc2/ab7rl+508f8GvCC5oenTn4tM1cWg/nBNmD4fBH/Kfvt2TAAAAAElFTkSuQmCC",c="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAASVJREFUOI190r0uhFEQBuBnVxaF2PUTCkFchV0SV6BQi0rEbShFlCqNktJP0Iqf3i3YVSlXVEQozojP8e2+ySSTed+ZMzNnKnpjCFPhv+C9j/YPlnCBV3TCujhHq19iFftoYxOjBa4esTb2QvsP+7jFWJ9HxnEXRf5gGU9Z8gKucBl+sUgHTahE8AJnOCoIT/AcmhmsF7gtrGINBqWFFWcmLXMUhzjIuEbk1GA+2i/DNh4wUsK1MVfFV2GUHJO4xlsPHr8j1Eu44bAcDek2agP4lDZaxWMm3MEKbrL4hjT/8U+gJc00nglnw4qYkL5xMW9rTzqSvEiefI/dMrIaRTrSPzcKXCNinUguPeUfNKWj6kqH9Bz+aVnbvb6PtKTp8F/wUSb6Bu5YN5n7ff0kAAAAAElFTkSuQmCC",i="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAQtJREFUOI2F0jFOAlEQBuAPImoFqyTa6TEEbfUihruYDYfwCAg3UDsTY20na0VjgqUWWuxgHsuy/skk82bmn/fPm9eyHXs4Cn+Br4baNZxjhk8UYUtMMWwitjHGHNfoJrlexObIo3YDY9zjoOGSQzxEkzVc4O0fctqkwCANzkJiE9LmI9ytDrvKB+tWGQnylIAsOB04VcrfdluO55CeYo6THfygVUne4jX8S1zho1LTDu7fCL2KxCe8oF8zUqb8G51VYGrzEffD6jDCJA0MY6bqnHXoK9d4Vk3kyk/S1KSPR9zUJdvRpAiJWZLLIlYEufYrrzBQ7nyJ97ClcuYN2dX1pejgOPwFvuuKfgHXiDR+HL1j1AAAAABJRU5ErkJggg==",r="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAARZJREFUOI2V0j1KQ1EQBeDPp4lWRiMoKVyAK9AoiLgJGytxD9oJNhKyDyvBnw2IugC3YGKVRk1KRbR48yC5vjzwwIHL3DPnzp2ZGdMxj9U4D/BZoZ3ANu4wQj84xC3aVYkZuujhCItjd42I9dAJ7R908YDlikeaeAyTCezgpST5IJia9LFVlA0nOMd7It4IjuMttKeFQR17uKooPcUV9lHL0ArX0T8MPqLa1hx+MDNFWDX7LHLV4/VGiWghmGJJvhu1WXzLO5rhORGeYRf3SfwQNVwWgbZ8SZqJcD04jhX5GDfTsjryJUlN0uQnXJRdZmHSx7H8nwWWItaP5NJVLrCFG3mTXoNDXJeVPW185E1ai/MAX2WiX9S3NSPYbj+uAAAAAElFTkSuQmCC",d="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAnXAAAJ1wGxbhe3AAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAARJJREFUOI2l0r8uRFEQBvAfu9glwUYiUaxHUEl0VDpKeq+wpZBINAqFRHgTKg0tCSqVhmKDEM1u/Esodm725rq7iC+ZzMnM982ZmXP4JwpdchWsYBrXeMkj9XQQV3GEi+BMYR63v+mqiDPUUrEaTiP3I1ZxEOcySnE+jFxXVPEQPimWiCYzOdCbKbCFPe1Z+8PgBvvBycVMCIdSsY2wBEPBmcnrYBtraKRib2EJGljHjswLLuI8Z6SS9hLTl15iIR08wZLv2AzLYjk0YATP8n9lVWbrgUJohosYxCdG8Zghdvp5ldCUi6hrPd0VjvGEVzTxEYLkogGMYQ67uEtvcgKzGA8y9IV/D9/Evdb89Q7d/Q1fB8U0mpUmzV0AAAAASUVORK5CYII=";return l.Request=(t,l=null,e={})=>{const a=$(null),s=$(!1),n=$(null),o=$(!1);let c=null;const i=async(i=null)=>{const r=A(t);if(r){c&&c.abort(),c=new AbortController,s(!0),n(null),o(!1);try{const t=i||l,A=await fetch(r,{method:e.method||(t?"POST":"GET"),headers:{"Content-Type":"application/json",...e.headers},body:t?JSON.stringify(t):null,signal:c.signal,...e});if(!A.ok)throw new Error(`HTTP ${A.status}`);let n=await A.json();"function"==typeof e.transform&&(n=e.transform(n)),a(n),o(!0)}catch(t){"AbortError"!==t.name&&n(t.message)}finally{s(!1)}}};return $((()=>(i(),()=>c?.abort()))),{data:a,loading:s,error:n,success:o,reload:t=>i(t)}},l.Response=(t,e)=>$html("div",{class:"res-container"},[$if(t.loading,$html("div",{class:"flex justify-center p-4"},$html("span",{class:"loading loading-dots text-primary"}))),$if(t.error,(()=>$html("div",{role:"alert",class:"alert alert-error"},[$html("span",{},t.error()),l.Button({class:"btn-xs btn-ghost border-current",onclick:()=>t.reload()},"Retry")]))),$if(t.success,(()=>{const l=t.data();return null!==l?e(l):null}))]),l.Button=(t,l)=>{const{badge:e,badgeClass:a,tooltip:s,icon:o,loading:c,...i}=t;let r=$html("button",{...i,class:n("btn",t.class||t.class),disabled:()=>A(c)||A(t.disabled)||A(t.disabled)},[()=>A(c)?$html("span",{class:"loading loading-spinner"}):null,o?$html("span",{class:"mr-1"},o):null,l]);return e&&(r=$html("div",{class:"indicator"},[$html("span",{class:n("indicator-item badge",a||"badge-secondary")},e),r])),s?$html("div",{class:"tooltip","data-tip":s},r):r},l.Input=t=>{const{label:l,tip:e,value:a,error:c,isSearch:i,icon:r,type:d="text",...m}=t,h="password"===d,u=$(!1),g={text:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAB2AAAAdgFOeyYIAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAMRJREFUOI3t0bFKAmAUBeAPURD1HQwUTKPJEmzQoSWQcKpVfIuWdvU9WnqNhsYWBx0a2lvLSMKGbvQ7SO564HA497/3cu/92SPFAS5QDN9CftviDhZYYRpNPtH/rzATOsQT6jhCFzmc4DTJL6AX067hPiimuAr95RglzMJ/4AyyUXSMw3iEauhN6C0eUEMFAyzTFZ7xiOvwL3jbsPYSr3hPg3dB/o43SVYY+TnsPPwXztMG5SDr39dGM8kr4RKNDdPtJL4BNXEmsdKC+S4AAAAASUVORK5CYII=",password:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAWQAAAFkBqp2phgAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAACQSURBVDiN7dKxDcJQDATQJ0YgXQQ1bAgDEIZBETPQwjakIjRQ8CMSyR8SiZKTrvHZd/r+JsYSNZrEI1ZR4ywzfElcJ55xwiITOECNTVDf4jDGoEEZ1Etcxxg8pmjRDiahb7BH20uKKPVUkVmL+YjQArdI+PT2bO9Pd/A34O71Rd9QeN/LAFUSckfUscWuG3oCgP8nrDH6T5AAAAAASUVORK5CYII=",date:o,number:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAB2AAAAdgFOeyYIAAAAGXRFWHRTb2Z0d2FyZQB3d3cuaW5rc2NhcGUub3Jnm+48GgAAAMxJREFUOI3t0bFKwlEUBvBfmmBEr1APIDZJ9AJJQyAIvkGP0C4uQruza+DUmuIc9AC9gBG4Nmpkw/8IB3Vw1w8u95zvnvPde77LEeUUV9HAF67QRA2nmMf5A+o4x3cWOsMYy8j7WMX6jaYbLBL/mAWe8RcHm1ihs8G94gVKQQzwlAouMcQo8p/Y28HdYpYFZmsi0MVdxD1MdrxsC500wijdvgtbI1AYtDbxMwkuFAZmE1uYwkkSqOIaHyHcxEU0vUXNPSqKr37fZ6xDwD9DPS0OyHjQHQAAAABJRU5ErkJggg==",email:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAC4SURBVDiNxdIxagJRFIXhLzLFBNJYaJslSEylWOhq3IorMGQ16SyjYCFiZWU5pTaDFvOUyTAZ8RHID69555577oXLf/OEGaY4R3g/4IhORHg3eOXYYvSAeRQ8OWQYYoNPvDQYnxUr7zBB1grCAv3QbIlxjXmAb7Txhq+rkFUKq9NUU8vcJiizwDtOWGEdmvTKqT+61H0GXsP7jSxpEGF/R1e3wkO0FBeVRnhTSBTneBB3yvOI4D/mAnvrIwKM5s4AAAAAAElFTkSuQmCC"},b=$html("input",{...m,type:()=>h?u()?"text":"password":d,placeholder:t.placeholder||l||(i?s("search")():" "),class:n("grow order-2 focus:outline-none",t.class),value:a,oninput:l=>t.oninput?.(l),disabled:()=>A(t.disabled)}),p=r||(g[d]?$html("img",{src:g[d],class:"w-5 h-5 opacity-50",alt:d}):null);return $html("label",{class:()=>n("input input-bordered floating-label flex items-center gap-2 w-full relative",A(c)?"input-error":"")},[p?$html("div",{class:"order-1 shrink-0"},p):null,l?$html("span",{class:"text-base-content/60 order-0"},l):null,b,h?$html("button",{type:"button",class:"order-3 btn btn-ghost btn-xs btn-circle opacity-50 hover:opacity-100",onclick:t=>{t.preventDefault(),u(!u())}},(()=>$html("img",{class:"w-5 h-5",src:u()?"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADjSURBVDiN3dJNSgNBEAXgz4DZeAAVJ9tko2St3kaIFxAVt4KZeAD1GKKi7vQSydI/yHgALxAXU02GxniAFBR0v1ev+3V1sZSxjxtM8BM5wTX2/hNu4gFvOMI21iJ3cIwP3GMjF/dQ4RyraOMS34GPAmvjIrBeEnfwjoPGgSM8ooh8QtngB6Ep4BWnmaMqkY1LqqzmDC8tzNDK3/RHzLL9SloUYWfQIMuw3Yl8xrDBH6qbvZWALqbqBqVmlWF7GuKEDwPr5hbXcYdPnKBv/o39wL5wG7ULY1c9NGPzQRrjKrhli1/02zEjWyWMBwAAAABJRU5ErkJggg==":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAEDSURBVDiN1dK/K8VhFAbwD+VLGSxKcu9guSQ/Zils/gNkuaX4BxRZDTdklYU/QAaDlEVGGwu2Kz/uVbKJzWDwfuv1+jHz1Km3c85znuf0Hv4jxnD2W8MItnCJ5xAX2MQcHsOQL+jEAapYQD9aQwxiDy+B3JKSe1DHCpqQYQ0PeMJOpDyAmyAAirjGbDRwFYcoYCZSzjGP+8B1gqXEUT2QxyPlqaRnGceNeENzUswwil1MBocbSU9DCAXUUI6K25HtIo5QSVaooitP9OEO65iIbE+HXSvBVRbeNZQSR9pxGil3o83HNw5hEbfYR0dKFki5ci+u8OrzIQ1/R8xx7ocL+9t4B0HPOVXjoptxAAAAAElFTkSuQmCC"}))):null,e?$html("div",{class:"tooltip tooltip-left order-4","data-tip":e},$html("span",{class:"badge badge-ghost badge-xs cursor-help"},"?")):null,()=>A(c)?$html("span",{class:"text-error text-[10px] absolute -bottom-5 left-2"},A(c)):null])},l.Select=t=>{const{label:l,options:e,value:a,...s}=t,o=$html("select",{...s,class:n("select select-bordered w-full",t.class||t.class),value:a},$for((()=>A(e)||[]),(t=>$html("option",{value:t.value,$selected:()=>String(A(a))===String(t.value)},t.label)),(t=>t.value)));return l?$html("label",{class:"fieldset-label flex flex-col gap-1"},[$html("span",{},l),o]):o},l.Autocomplete=t=>{const{options:e=[],value:a,onSelect:n,label:o,placeholder:c,...i}=t,r=$(A(a)||""),d=$(!1),m=$(-1),h=$((()=>{const t=r().toLowerCase(),l=A(e)||[];return t?l.filter((l=>("string"==typeof l?l:l.label).toLowerCase().includes(t))):l})),u=t=>{const l="string"==typeof t?t:t.value,e="string"==typeof t?t:t.label;r(e),"function"==typeof a&&a(l),n?.(t),d(!1),m(-1)};return $html("div",{class:"relative w-full"},[l.Input({label:o,placeholder:c||s("search")(),value:r,onfocus:()=>d(!0),onblur:()=>setTimeout((()=>d(!1)),150),onkeydown:t=>{const l=h();"ArrowDown"===t.key?(t.preventDefault(),d(!0),m(Math.min(m()+1,l.length-1))):"ArrowUp"===t.key?(t.preventDefault(),m(Math.max(m()-1,0))):"Enter"===t.key&&m()>=0?(t.preventDefault(),u(l[m()])):"Escape"===t.key&&d(!1)},oninput:t=>{const l=t.target.value;r(l),"function"==typeof a&&a(l),d(!0),m(-1)},...i}),$html("ul",{class:"absolute left-0 w-full menu bg-base-100 rounded-box mt-1 p-2 shadow-xl max-h-60 overflow-y-auto border border-base-300 z-50",style:()=>d()&&h().length?"display:block":"display:none"},[$for(h,((t,l)=>$html("li",{},[$html("a",{class:()=>"block w-full "+(m()===l?"active bg-primary text-primary-content":""),onclick:()=>u(t),onmouseenter:()=>m(l)},"string"==typeof t?t:t.label)])),((t,l)=>("string"==typeof t?t:t.value)+l)),()=>h().length?null:$html("li",{class:"p-2 text-center opacity-50"},"No results")])])},l.Datepicker=t=>{const{value:e,range:a,label:s,placeholder:n,...c}=t,i=$(!1),r=$(new Date),d=$(null),m=()=>!0===A(a),h=new Date,u=`${h.getFullYear()}-${String(h.getMonth()+1).padStart(2,"0")}-${String(h.getDate()).padStart(2,"0")}`,g=t=>`${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,"0")}-${String(t.getDate()).padStart(2,"0")}`,b=t=>{const l=g(t),a=A(e);if(m())if(!a?.start||a.start&&a.end)"function"==typeof e&&e({start:l,end:null});else{const t=a.start;"function"==typeof e&&e(l{const t=A(e);return t?"string"==typeof t?t:t.start&&t.end?`${t.start} - ${t.end}`:t.start?`${t.start}...`:"":""})),f=t=>{const l=r();r(new Date(l.getFullYear(),l.getMonth()+t,1))},v=t=>{const l=r();r(new Date(l.getFullYear()+t,l.getMonth(),1))};return $html("div",{class:"relative w-full"},[l.Input({label:s,placeholder:n||(m()?"Seleccionar rango...":"Seleccionar fecha..."),value:p,readonly:!0,icon:$html("img",{src:o,class:"opacity-40"}),onclick:t=>{t.stopPropagation(),i(!i())},...c}),$if(i,(()=>$html("div",{class:"absolute left-0 mt-2 p-4 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[100] w-80 select-none",onclick:t=>t.stopPropagation()},[$html("div",{class:"flex justify-between items-center mb-4 gap-1"},[$html("div",{class:"flex gap-0.5"},[$html("button",{type:"button",class:"btn btn-ghost btn-xs px-1",onclick:()=>v(-1)},$html("img",{src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABlSURBVDiN3ZLBDUBAEEUfmtCchA5woUMlOO1FCQrAwbqwf8eFhHd7mfzJn2Tg82TGvABywAmPUgOLD4XcDK9AJ/y5cOlrNsIvpCdPDL/FUbkX/t6Slv3+SjgQf6QBmIAZGAP+FzZJViOd89x8pAAAAABJRU5ErkJggg==",class:"opacity-40"})),$html("button",{type:"button",class:"btn btn-ghost btn-xs px-1",onclick:()=>f(-1)},$html("img",{src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABfSURBVDiNY2AY8oCZSHWxDAwMEgwMDHfJsaSAgYHhH9QQsjT/Z2BgKKe75gQGiLMLCSlkwiHOSI6t6ADmhYoBN6SIARIeidgkiUlIxxkYGB4xMDB8YmBguE6JSwYpAACvLRHTKwPjZgAAAABJRU5ErkJggg==",class:"opacity-40"}))]),$html("span",{class:"font-bold uppercase flex-1 text-center"},[()=>r().toLocaleString("es-ES",{month:"short",year:"numeric"})]),$html("div",{class:"flex gap-0.5"},[$html("button",{type:"button",class:"btn btn-ghost btn-xs px-1",onclick:()=>f(1)},$html("img",{src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABNSURBVDiN3dAxCoAwFATRh3fU2oAHiDbi5Y1F2jT+gKLbzyy7/DYjUo8g4cTWI8koOF6XrOqc5ifDDVGJthfsj8OLujtHYJgwR+GP5QKMxA9/SolDQgAAAABJRU5ErkJggg==",class:"opacity-40"})),$html("button",{type:"button",class:"btn btn-ghost btn-xs px-1",onclick:()=>v(1)},$html("img",{src:"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAAdgAAAHYBTnsmCAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAABmSURBVDiN3dGxCoAgEMbxfz1dL1BTREJzmUv08trgDYcg6VCD3/YD7zvkoLmMgFEegLmmwAAecOJVvNeUWCAAt7IHjt9LThkyiRf9qC8oCom70u0BuDL+bngj/tNm/JqJePucW8wDvGYdzT0nMUkAAAAASUVORK5CYII=",class:"opacity-40"}))])]),$html("div",{class:"grid grid-cols-7 gap-1",onmouseleave:()=>d(null)},[...["L","M","X","J","V","S","D"].map((t=>$html("div",{class:"text-[10px] opacity-40 font-bold text-center"},t))),()=>{const t=r(),l=t.getFullYear(),a=t.getMonth(),s=new Date(l,a,1).getDay(),n=0===s?6:s-1,o=new Date(l,a+1,0).getDate(),c=[];for(let t=0;t{const t=A(e),l=d(),a="string"==typeof t?t===n:t?.start===n,s=t?.end===n;let o=!1;if(m()&&t?.start){const e=t.start;!t.end&&l?o=n>e&&n<=l||n=l:t.end&&(o=n>e&&n{m()&&d(n)},onclick:()=>b(s)},[t.toString()]))}return c}])]))),$if(i,(()=>$html("div",{class:"fixed inset-0 z-[90]",onclick:()=>i(!1)})))])},l.Colorpicker=t=>{const{value:l,label:e,...a}=t,s=$(!1),n=["#000","#1A1A1A","#333","#4D4D4D","#666","#808080","#B3B3B3","#FFF","#450a0a","#7f1d1d","#991b1b","#b91c1c","#dc2626","#ef4444","#f87171","#fca5a5","#431407","#7c2d12","#9a3412","#c2410c","#ea580c","#f97316","#fb923c","#ffedd5","#713f12","#a16207","#ca8a04","#eab308","#facc15","#fde047","#fef08a","#fff9c4","#064e3b","#065f46","#059669","#10b981","#34d399","#4ade80","#84cc16","#d9f99d","#082f49","#075985","#0284c7","#0ea5e9","#38bdf8","#7dd3fc","#22d3ee","#cffafe","#1e1b4b","#312e81","#4338ca","#4f46e5","#6366f1","#818cf8","#a5b4fc","#e0e7ff","#2e1065","#4c1d95","#6d28d9","#7c3aed","#8b5cf6","#a855f7","#d946ef","#fae8ff"],o=()=>A(l)||"#000000";return $html("div",{class:"relative w-fit"},[$html("button",{type:"button",class:"btn px-3 bg-base-100 border-base-300 hover:border-primary/50 flex items-center gap-2 shadow-sm font-normal normal-case",onclick:t=>{t.stopPropagation(),s(!s())},...a},[$html("div",{class:"size-5 rounded-sm shadow-inner border border-black/10 shrink-0",style:()=>`background-color: ${o()}`}),e?$html("span",{class:"opacity-80"},e):null]),$if(s,(()=>$html("div",{class:"absolute left-0 mt-2 p-3 bg-base-100 border border-base-300 shadow-2xl rounded-box z-[110] w-64 select-none",onclick:t=>t.stopPropagation()},[$html("div",{class:"grid grid-cols-8 gap-1"},n.map((t=>$html("button",{type:"button",style:`background-color: ${t}`,class:()=>"size-6 rounded-sm cursor-pointer transition-all hover:scale-125 hover:z-10 active:scale-95 outline-none border border-black/5 \n "+(o().toLowerCase()===t.toLowerCase()?"ring-2 ring-offset-1 ring-primary z-10 scale-110":""),onclick:()=>{l(t),s(!1)}}))))]))),$if(s,(()=>$html("div",{class:"fixed inset-0 z-[100]",onclick:()=>s(!1)})))])},l.CheckBox=t=>{const{value:l,tooltip:e,toggle:a,...s}=t,n=$html("input",{...s,type:"checkbox",class:()=>A(a)?"toggle":"checkbox",checked:l}),o=$html("label",{class:"label cursor-pointer justify-start gap-3"},[n,t.label?$html("span",{class:"label-text"},t.label):null]);return e?$html("div",{class:"tooltip","data-tip":e},o):o},l.Radio=t=>{const{label:l,tooltip:e,value:a,...s}=t,o=$html("input",{...s,type:"radio",class:n("radio",t.class||t.class),checked:()=>A(a)===a,disabled:()=>A(t.disabled)||A(t.disabled),onclick:()=>"function"==typeof a&&a(a)});if(!l&&!e)return o;const c=$html("label",{class:"label cursor-pointer justify-start gap-3"},[o,l?$html("span",{class:"label-text"},l):null]);return e?$html("div",{class:"tooltip","data-tip":e},c):c},l.Range=t=>{const{label:l,tooltip:e,value:a,...s}=t,o=$html("input",{...s,type:"range",class:n("range",t.class),value:a,disabled:()=>A(t.disabled)});if(!l&&!e)return o;const c=$html("div",{class:"flex flex-col gap-2"},[l?$html("span",{class:"label-text"},l):null,o]);return e?$html("div",{class:"tooltip","data-tip":e},c):c},l.Modal=(t,e)=>{const{title:a,buttons:A,open:n,...o}=t,c=()=>n(!1);return $if(n,(()=>$html("dialog",{...o,class:"modal modal-open"},[$html("div",{class:"modal-box"},[a?$html("h3",{class:"text-lg font-bold mb-4"},a):null,"function"==typeof e?e():e,$html("div",{class:"modal-action flex gap-2"},[...(Array.isArray(A)?A:[A]).filter(Boolean),l.Button({onclick:c},s("close")())])]),$html("form",{method:"dialog",class:"modal-backdrop",onclick:t=>(t.preventDefault(),c())},[$html("button",{},"close")])])))},l.Grid=t=>{const{data:l,options:e,class:a}=t;let s=null;const n=$html("div",{style:"height: 100%; width: 100%;",class:a}),o=new MutationObserver((()=>{s&&s.setGridOption("theme",getTheme(isDark()))}));o.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme"]}),n._cleanups.add((()=>o.disconnect()));const c=$watch((()=>{const t=isDark(),a=getTheme(t),o=A(l)||[];s?s.setGridOption("theme",a):s=createGrid(n,{...A(e)||{},theme:a,rowData:o})}));n._cleanups.add(c);const i=$watch((()=>{const t=A(l);s&&Array.isArray(t)&&s.setGridOption("rowData",t)}));return n._cleanups.add(i),n._cleanups.add((()=>{s&&(s.destroy(),s=null)})),n},l.Dropdown=(t,l)=>{const{label:e,icon:a,...s}=t;return $html("div",{...s,class:()=>`dropdown ${A(t.class)||t.class||""}`},[$html("div",{tabindex:0,role:"button",class:"btn m-1 flex items-center gap-2"},[a?"function"==typeof a?a():a:null,e?"function"==typeof e?e():e:null]),$html("ul",{tabindex:0,class:"dropdown-content z-[50] menu p-2 shadow bg-base-100 rounded-box min-w-max border border-base-300"},["function"==typeof l?l():l])])},l.Accordion=(t,l)=>{const{title:e,name:a,open:s,...A}=t;return $html("div",{...A,class:n("collapse collapse-arrow bg-base-200 mb-2",t.class||t.class)},[$html("input",{type:a?"radio":"checkbox",name:a,checked:s}),$html("div",{class:"collapse-title text-xl font-medium"},e),$html("div",{class:"collapse-content"},l)])},l.Tabs=t=>{const{items:l,...e}=t,a="function"==typeof l?l:()=>l||[];return $html("div",{...e,class:"flex flex-col gap-4 w-full"},[$html("div",{role:"tablist",class:n("tabs tabs-box",t.class||t.class)},$for(a,(t=>$html("a",{role:"tab",class:()=>n("tab",A(t.active)&&"tab-active",A(t.disabled),t.tip),"data-tip":t.tip,onclick:l=>!A(t.disabled)&&t.onclick?.(l)},t.label)),(t=>t.label))),()=>{const t=a().find((t=>A(t.active)));if(!t)return null;const l=A(t.content);return $html("div",{class:"p-4"},["function"==typeof l?l():l])}])},l.Badge=(t,l)=>$html("span",{...t,class:n("badge",t.class||t.class)},l),l.Tooltip=(t,l)=>$html("div",{...t,class:n("tooltip",t.class||t.class),"data-tip":t.tip},l),l.Navbar=(t,l)=>$html("div",{...t,class:n("navbar bg-base-100 shadow-sm px-4",t.class||t.class)},l),l.Menu=t=>{const l=t=>$for((()=>t||[]),(t=>$html("li",{},[t.children?$html("details",{open:t.open},[$html("summary",{},[t.icon&&$html("span",{class:"mr-2"},t.icon),t.label]),$html("ul",{},l(t.children))]):$html("a",{class:()=>A(t.active)?"active":"",onclick:t.onclick},[t.icon&&$html("span",{class:"mr-2"},t.icon),t.label])])),((t,l)=>t.label||l));return $html("ul",{...t,class:n("menu bg-base-200 rounded-box",t.class||t.class)},l(t.items))},l.Drawer=t=>$html("div",{class:n("drawer",t.class||t.class)},[$html("input",{id:t.id,type:"checkbox",class:"drawer-toggle",checked:t.open}),$html("div",{class:"drawer-content"},t.content),$html("div",{class:"drawer-side"},[$html("label",{for:t.id,class:"drawer-overlay",onclick:()=>t.open?.(!1)}),$html("div",{class:"min-h-full bg-base-200 w-80"},t.side)])]),l.Fieldset=(t,l)=>$html("fieldset",{...t,class:n("fieldset bg-base-200 border border-base-300 p-4 rounded-lg",t.class||t.class)},[()=>{const l=A(t.legend);return l?$html("legend",{class:"fieldset-legend font-bold"},[l]):null},l]),l.List=t=>{const{items:l,header:e,render:a,keyFn:s,class:o}=t;return $html("ul",{class:n("list bg-base-100 rounded-box shadow-md",o)},[$if(e,(()=>$html("li",{class:"p-4 pb-2 text-xs opacity-60 tracking-wide"},[A(e)]))),$for(l,((t,l)=>$html("li",{class:"list-row"},[a(t,l)])),s)])},l.Stack=(t,l)=>$html("div",{...t,class:n("stack",t.class||t.class)},l),l.Stat=t=>$html("div",{...t,class:n("stat",t.class||t.class)},[t.icon&&$html("div",{class:"stat-figure text-secondary"},t.icon),t.label&&$html("div",{class:"stat-title"},t.label),$html("div",{class:"stat-value"},(()=>A(t.value)??t.value)),t.desc&&$html("div",{class:"stat-desc"},t.desc)]),l.Swap=t=>$html("label",{class:n("swap",t.class||t.class)},[$html("input",{type:"checkbox",checked:t.value}),$html("div",{class:"swap-on"},t.on),$html("div",{class:"swap-off"},t.off)]),l.Indicator=(t,l)=>$html("div",{class:n("indicator",t.class||t.class)},[l,$html("span",{class:n("indicator-item badge",t.badgeClass)},t.badge)]),l.Rating=t=>{const{value:l,count:e=5,mask:a="mask-star",readonly:s=!1,...n}=t,o=`rating-${Math.random().toString(36).slice(2,7)}`;return $html("div",{...n,class:()=>`rating ${A(s)?"pointer-events-none":""} ${t.class||""}`},Array.from({length:A(e)},((t,e)=>{const n=e+1;return $html("input",{type:"radio",name:o,class:`mask ${a}`,"aria-label":`${n} star`,checked:()=>Math.round(A(l))===n,onchange:()=>{A(s)||"function"!=typeof l||l(n)}})})))},l.Alert=(t,l)=>{const{type:e="info",soft:a=!0,...s}=t,n={info:c,success:i,warning:d,error:r},o=l||t.message;return $html("div",{...s,role:"alert",class:()=>`alert ${(()=>{const t=A(e);return{info:"alert-info",success:"alert-success",warning:"alert-warning",error:"alert-error"}[t]||t})()} ${A(a)?"alert-soft":""} ${t.class||""}`},[$html("img",{src:n[A(e)]||n.info,class:"w-4 h-4 object-contain",alt:A(e)}),$html("div",{class:"flex-1"},[$html("span",{},["function"==typeof o?o():o])]),t.actions?$html("div",{class:"flex-none"},["function"==typeof t.actions?t.actions():t.actions]):null])},l.Timeline=t=>{const{items:l=[],vertical:e=!0,compact:a=!1,...s}=t,n={info:c,success:i,warning:d,error:r};return $html("ul",{...s,class:()=>`timeline ${A(e)?"timeline-vertical":"timeline-horizontal"} ${A(a)?"timeline-compact":""} ${t.class||""}`},[$for(l,((t,e)=>{const a=0===e,s=e===A(l).length-1,o=t.type||"success",c=t=>"function"==typeof t?t():t;return $html("li",{class:"flex-1"},[a?null:$html("hr",{class:t.completed?"bg-primary":""}),$html("div",{class:"timeline-start"},[c(t.title)]),$html("div",{class:"timeline-middle"},[$html("img",{src:n[o]||t.icon||n.success,class:"w-4 h-4 object-contain mx-1",alt:o})]),$html("div",{class:"timeline-end timeline-box shadow-sm"},[c(t.detail)]),s?null:$html("hr",{class:t.completed?"bg-primary":""})])}),((t,l)=>t.id||l))])},l.Fab=t=>{const{icon:l,label:e,actions:a=[],position:s="bottom-6 right-6",...n}=t;return $html("div",{...n,class:()=>`fab fixed ${A(s)} flex flex-col-reverse items-end gap-3 z-[100] ${t.class||""}`},[$html("div",{tabindex:0,role:"button",class:"btn btn-lg btn-circle btn-primary shadow-2xl"},[l?"function"==typeof l?l():l:null,!l&&e?e:null]),...A(a).map((t=>$html("div",{class:"flex items-center gap-3 transition-all duration-300"},[t.label?$html("span",{class:"badge badge-ghost shadow-sm whitespace-nowrap"},t.label):null,$html("button",{type:"button",class:`btn btn-circle shadow-lg ${t.class||""}`,onclick:l=>{l.stopPropagation(),t.onclick?.(l)}},[t.icon?"function"==typeof t.icon?t.icon():t.icon:t.text||""])])))])},l.Toast=(t,e="alert-success",a=3500)=>{let s=document.getElementById("sigpro-toast-container");s||(s=$html("div",{id:"sigpro-toast-container",class:"fixed top-0 right-0 z-[9999] p-4 flex flex-col gap-2 pointer-events-none"}),document.body.appendChild(s));const A=$html("div",{style:"display: contents"});let n;s.appendChild(A);const o=()=>{clearTimeout(n);const t=A.firstElementChild;t&&!t.classList.contains("opacity-0")?(t.classList.add("translate-x-full","opacity-0"),setTimeout((()=>{c.destroy(),A.remove(),s.hasChildNodes()||s.remove()}),300)):(c.destroy(),A.remove())},c=$mount((()=>{const a=$html("div",{class:`alert alert-soft ${e} shadow-lg transition-all duration-300 translate-x-10 opacity-0 pointer-events-auto`},[$html("span","function"==typeof t?t:()=>t),l.Button({class:"btn-xs btn-circle btn-ghost",onclick:o},"✕")]);return requestAnimationFrame((()=>a.classList.remove("translate-x-10","opacity-0"))),a}),A);return a>0&&(n=setTimeout(o,a)),o},l.Loading=t=>$if(t.$show,(()=>$html("div",{class:"fixed inset-0 z-[100] flex items-center justify-center backdrop-blur-sm bg-base-100/30"},[$html("span",{class:"loading loading-spinner loading-lg text-primary"})]))),l.tt=s,Object.keys(l).forEach((t=>{$[t]=l[t],Object.defineProperty(window,t,{value:l[t],writable:!1,configurable:!0,enumerable:!0})})),l}; \ No newline at end of file