sprae 9.1.0 → 10.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/sprae.min.js CHANGED
@@ -1 +1 @@
1
- var e=Object.defineProperty,t=(e,r,a,l=null)=>{let s,n,o,i=0,c=a.length,u=r.length,{remove:p,same:f,insert:d,replace:v}=t;for(;i<c&&i<u&&f(r[i],a[i]);)i++;for(;i<c&&i<u&&f(a[c-1],r[u-1]);)l=a[(--u,--c)];if(i==u)for(;i<c;)d(l,a[i++],e);else{for(s=r[i];i<c;)o=a[i++],n=s?s.nextSibling:l,f(s,o)?s=n:i<c&&f(a[i],n)?(v(s,o,e),s=n):d(s,o,e);for(;!f(s,l);)n=s.nextSibling,p(s,e),s=n}return a};t.same=(e,t)=>e==t,t.replace=(e,t,r)=>r.replaceChild(t,e),t.insert=(e,t,r)=>r.insertBefore(t,e),t.remove=(e,t)=>t.removeChild(e);var r,a=t,l={};((t,r)=>{for(var a in r)e(t,a,{get:r[a],enumerable:!0})})(l,{batch:()=>u,computed:()=>c,current:()=>r,effect:()=>i,signal:()=>o,untracked:()=>p});var s,n,o=(e,t,a=new Set)=>((t={get value(){return r?.deps.push(a.add(r)),e},set value(t){if(t!==e){e=t;for(let e of a)e(t)}},peek:()=>e}).toJSON=t.then=t.toString=t.valueOf=()=>t.value,t),i=(e,t,a,l)=>(l=(a=l=>{t?.call?.(),l=r,r=a;try{t=e()}finally{r=l}}).deps=[],a(),e=>{for(t?.call?.();e=l.pop();)e.delete(a)}),c=(e,t=o(),r,a)=>((r={get value(){return a||=i((()=>t.value=e())),t.value},peek:t.peek}).toJSON=r.then=r.toString=r.valueOf=()=>r.value,r),u=e=>e(),p=(e,t,a)=>(t=r,r=null,a=e(),r=t,a),f=e=>(s=0,n=e,e=h(),n[s]?d():e||""),d=(e="Bad syntax",t=n.slice(0,s).split("\n"),r=t.pop())=>{let a=n.slice(s-108,s).split("\n").pop(),l=n.slice(s,s+108).split("\n").shift();throw EvalError(`${e} at ${t.length}:${r.length} \`${s>=108?"…":""}${a}┃${l}\``,"font-weight: bold")},v=(e,t=s,r)=>{for(;r=e(n.charCodeAt(s));)s+=r;return n.slice(t,s)},y=(e=1,t=s)=>(s+=e,n.slice(t,s)),h=(e=0,t,r,a,l,n)=>{for(;(r=f.space())&&(l=((n=m[r])&&n(a,e))??(!a&&v(f.id)));)a=l;return t&&(r==t?s++:d()),a},m=(f.id=e=>e>=48&&e<=57||e>=65&&e<=90||e>=97&&e<=122||36==e||95==e||e>=192&&215!=e&&247!=e,f.space=e=>{for(;(e=n.charCodeAt(s))<=32;)s++;return e},[]),g=(e,t=32,r,a=e.charCodeAt(0),l=e.length,o=m[a],i=e.toUpperCase()!==e)=>m[a]=(a,c,u=s)=>c<t&&(l<2||n.substr(s,l)==e)&&(!i||!f.id(n.charCodeAt(s+l)))&&(s+=l,r(a,c))||(s=u,o?.(a,c)),b=(e,t,r=!1)=>g(e,t,((a,l)=>a&&(l=h(t-(r?.5:0)))&&[e,a,l])),k=(e,t,r)=>g(e,t,(a=>r?a&&[e,a]:!a&&(a=h(t-.5))&&[e,a])),A=(e,t)=>{g(e,t,((r,a)=>(a=h(t),(!r||r[0]!==e)&&(r=[e,r]),r.push(a),r)))},N=(e,t)=>g(e[0],t,(t=>!t&&[e,h(0,e.charCodeAt(1))])),O=(e,t)=>g(e[0],t,(t=>t&&[e[0],t,h(0,e.charCodeAt(1))])),w=f,C=e=>Array.isArray(e)?e[0]?S[e[0]](...e.slice(1)):()=>e[1]:C.id(e),S=(C.id=e=>t=>t?.[e],{}),x=(e,t,r=S[e])=>S[e]=(...e)=>t(...e)||r&&r(...e),$=(e,t,r,a,l)=>"()"===e[0]?$(e[1],t,r):"string"==typeof e?r=>t(r,e,r):"."===e[0]?(a=C(e[1]),l=e[2],e=>t(a(e),l,e)):"["===e[0]?(a=C(e[1]),l=C(e[2]),e=>t(a(e),l(e),e)):r?(e=C(e),r=>t([e(r)],0,r)):()=>d("Bad left value"),E=C,T=(e,t)=>[,(e=+v((e=>46===e||e>=48&&e<=57||(69===e||101===e?2:0))))!=e?d():e];m[46]=e=>!e&&T();for(let e=48;e<=57;e++)m[e]=e=>e?d():T();var W={n:"\n",r:"\r",t:"\t",b:"\b",f:"\f",v:"\v"},j=e=>(t,r,a="")=>{for(t&&d("Unexpected string"),y();(r=n.charCodeAt(s))-e;)92===r?(y(),r=y(),a+=W[r]||r):a+=y();return y()||d("Bad string"),[,a]};m[34]=j(34),m[39]=j(39),O("()",17),x("(",((e,t,r)=>(r=t?","===t[0]?(t=t.slice(1).map((e=>e?C(e):err())),e=>t.map((t=>t(e)))):(t=C(t),e=>[t(e)]):()=>[],$(e,((e,t,a)=>e[t](...r(a))),!0)))),O("[]",17),x("[",((e,t)=>t?(e=C(e),t=C(t),r=>e(r)[t(r)]):err())),b(".",17),x(".",((e,t)=>(e=C(e),t=t[0]?t:t[1],r=>e(r)[t]))),N("()",17),x("()",(e=>(!e&&d("Empty ()"),C(e))));var L=(...e)=>(e=e.map(C),t=>e.map((e=>e(t))).pop());A(",",1),x(",",L),A(";",1),x(";",L),b("*",12),x("*",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)*t(r)))),b("/",12),x("/",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)/t(r)))),b("%",12),x("%",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)%t(r)))),b("*=",2,!0),x("*=",((e,t)=>(t=C(t),$(e,((e,r,a)=>e[r]*=t(a)))))),b("/=",2,!0),x("/=",((e,t)=>(t=C(t),$(e,((e,r,a)=>e[r]/=t(a)))))),b("%=",2,!0),x("%=",((e,t)=>(t=C(t),$(e,((e,r,a)=>e[r]%=t(a)))))),k("+",14),x("+",((e,t)=>!t&&(e=C(e),t=>+e(t)))),k("-",14),x("-",((e,t)=>!t&&(e=C(e),t=>-e(t)))),b("+",11),x("+",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)+t(r)))),b("-",11),x("-",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)-t(r)))),b("+=",2,!0),x("+=",((e,t)=>(t=C(t),$(e,((e,r,a)=>e[r]+=t(a)))))),b("-=",2,!0),x("-=",((e,t)=>(t=C(t),$(e,((e,r,a)=>e[r]-=t(a)))))),g("++",15,(e=>e?["++-",e]:["++",h(14)])),x("++",(e=>$(e,((e,t,r)=>++e[t])))),x("++-",(e=>$(e,((e,t,r)=>e[t]++)))),g("--",15,(e=>e?["--+",e]:["--",h(14)])),x("--",(e=>$(e,((e,t,r)=>--e[t])))),x("--+",(e=>$(e,((e,t,r)=>e[t]--)))),k("~",14),x("~",((e,t)=>!t&&(e=C(e),t=>~e(t)))),b("|",5),x("|",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)|t(r)))),b("&",7),x("&",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)&t(r)))),b("^",6),x("^",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)^t(r)))),b(">>",10),x(">>",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)>>t(r)))),b("<<",10),x("<<",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)<<t(r)))),b("==",8),x("==",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)==t(r)))),b("!=",8),x("!=",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)!=t(r)))),b(">",9),x(">",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)>t(r)))),b("<",9),x("<",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)<t(r)))),b(">=",9),x(">=",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)>=t(r)))),b("<=",9),x("<=",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)<=t(r)))),k("!",14),x("!",((e,t)=>!t&&(e=C(e),t=>!e(t)))),b("||",3),x("||",((e,t)=>(e=C(e),t=C(t),r=>e(r)||t(r)))),b("&&",4),x("&&",((e,t)=>(e=C(e),t=C(t),r=>e(r)&&t(r)))),b("=",2,!0),x("=",((e,t)=>(t=C(t),$(e,((e,r,a)=>e[r]=t(a)))))),g("/*",20,((e,t)=>(v((e=>42!==e&&47!==n.charCodeAt(s+1))),y(2),e||h(t)||[]))),g("//",20,((e,t)=>(v((e=>e>=32)),e||h(t)||[""]))),b("**",13,!0),x("**",((e,t)=>t&&(e=C(e),t=C(t),r=>e(r)**t(r)))),g("?",2,((e,t,r)=>e&&(t=h(1.5,58))&&["?",e,t,h(1.5)])),x("?",((e,t,r)=>(e=C(e),t=C(t),r=C(r),a=>e(a)?t(a):r(a)))),g("true",20,(e=>e?err():[,!0])),g("false",20,(e=>e?err():[,!1])),N("[]",20),x("[]",((e,t)=>(e=(e=e?","===e[0]?e.slice(1):[e]:[]).map((e=>"..."===e[0]?(e=C(e[1]),t=>e(t)):(e=C(e),t=>[e(t)]))),t=>e.flatMap((e=>e(t)))))),N("{}",20),x("{}",((e,t)=>(e=(e=e?","!==e[0]?[e]:e.slice(1):[]).map((e=>C("string"==typeof e?[":",e,e]:e))),t=>Object.fromEntries(e.flatMap((e=>e(t))))))),b(":",1.5,!0),x(":",((e,t)=>(t=C(t),Array.isArray(e)?(e=C(e),r=>[[e(r),t(r)]]):r=>[[e,t(r)]]))),b("=>",2,!0),x("=>",((e,t)=>(e=(e="()"===e[0]?e[1]:e)?e=","===e[0]?e.slice(1):[e]:[],t=C("{}"===t[0]?t[1]:t),(r=null)=>(r=Object.create(r),(...a)=>(e.map(((e,t)=>r[e]=a[t])),t(r)))))),b(""),g("?.",17,(e=>e&&["?.",e])),x("?.",(e=>(e=C(e),t=>e(t)||(()=>{})))),g("?.",17,((e,t)=>e&&!(t=h(17))?.map&&["?.",e,t])),x("?.",((e,t)=>t&&(e=C(e),r=>e(r)?.[t]))),x("(",((e,t,r,a,l,s)=>"?."===e[0]&&(e[2]||Array.isArray(e[1]))&&(a=t?","===t[0]?(t=t.slice(1).map(C),e=>t.map((t=>t(e)))):(t=C(t),e=>[t(e)]):()=>[],!e[2]&&(e=e[1]),l="["===e[0]?C(e[2]):()=>e[2],r=C(e[1]),e=>r(e)?.[l(e)]?.(...a(e))))),k("...",14),x("...",(e=>(e=C(e),t=>Object.entries(e(t))))),b("in",9),x("in",((e,t)=>t&&(e=E(e),t=E(t),r=>e(r)in t(r)))),b("===",8),b("!==",9),x("===",((e,t)=>(e=E(e),t=E(t),r=>e(r)===t(r)))),x("!==",((e,t)=>(e=E(e),t=E(t),r=>e(r)!==t(r)))),b("??",3),x("??",((e,t)=>t&&(e=E(e),t=E(t),r=>e(r)??t(r)))),b("??=",2,!0),x("??=",((e,t)=>(t=E(t),$(e,((e,r,a)=>e[r]??=t(a)))))),b("||=",2,!0),x("||=",((e,t)=>(t=E(t),$(e,((e,r,a)=>e[r]||=t(a)))))),b("&&=",2,!0),x("&&=",((e,t)=>(t=E(t),$(e,((e,r,a)=>e[r]&&=t(a)))))),g("undefined",20,(e=>e?d():[,void 0])),g("NaN",20,(e=>e?d():[,NaN])),g("null",20,(e=>e?d():[,null]));var B=Symbol.dispose||=Symbol("dispose"),{signal:D,effect:M,batch:K,computed:P,untracked:J}=l,R={},U=new WeakMap;function Z(e,t){if(!e.children)return;if(U.has(e)){const[r,a]=U.get(e);for(let e in t)r[e]=t[e];J((()=>{for(let e of a)e()}))}const r=t||{},a=[],l=(e,t=e.parentNode)=>{if(e.attributes)for(let l=0;l<e.attributes.length;){let s=e.attributes[l];if(":"===s.name[0]){e.removeAttribute(s.name);let l=s.name.slice(1).split(":");for(let t of l){let l=(R[t]||R.default)(e,s.value,r,t);l&&(l[B]=M(l),a.push(l))}if(U.has(e))return;if(e.parentNode!==t)return!1}else l++}for(let t,r=0;t=e.children[r];r++)!1===l(t,e)&&r--};return l(e),U.has(e)||(U.set(e,[r,a]),e.classList?.add("∴"),e[B]=()=>{for(;a.length;)a.pop()[B]();e.classList.remove("∴"),U.delete(e);let t=e.getElementsByClassName("∴");for(;t.length;)t[0][B]?.()}),r}var z={},H=(e,t,r)=>{if(r=z[e=e.trim()])return r;try{r=E(w(e))}catch(r){throw Object.assign(r,{message:`∴ ${r.message}\n\n${t}${e?`="${e}"\n\n`:""}`,expr:e})}return z[e]=r},X=a,_=(e,t)=>e?.replace?e.replace(/\$<([^>]+)>/g,((e,r)=>t[r]?.valueOf?.()??"")):e;Z.use=e=>{e.signal&&(D=e.signal,M=e.effect,P=e.computed,K=e.batch||(e=>e()),J=e.untracked||K),e.swap&&(X=e.swap)};var q=Symbol(":each"),F={};R.each=(e,t,r,a)=>{let[l,s]=t.split(/\s+in\s+/),[n,o="_$"]=l.split(/\s*,\s*/);const i=e[q]=document.createTextNode("");e.replaceWith(i);const c=H(s,a),u=new WeakMap;e.removeAttribute(":key");let p=[];return()=>{let t=c(r)?.valueOf(),a=[];"number"==typeof t&&(t=Array.from({length:t},((e,t)=>t)));const l=new WeakSet;for(let s in t){let i=t[s],c=Object.create(r,{[o]:{value:s}});c[n]=i;let p,f=i.key??i.id??i;null==f?p=e.cloneNode(!0):(Object(f)!==f&&(f=F[f]||=Object(f)),l.has(f)?(console.warn("Duplicate key",f),p=e.cloneNode(!0)):(l.add(f),p=u.get(f)||u.set(f,e.cloneNode(!0)).get(f))),p.content&&(p=p.content.cloneNode(!0)),Z(p,c),11===p.nodeType?a.push(...p.childNodes):a.push(p)}X(i.parentNode,p,p=a,i)}};var G=Symbol("if");R.if=(e,t,r,a)=>{let l,s,n,o=e.parentNode,i=e.nextElementSibling,c=document.createTextNode(""),u=H(t,a),p=[];return e.after(c),e.content?(l=p,e.remove(),s=[...e.content.childNodes]):s=l=[e],i?.hasAttribute(":else")?(i.removeAttribute(":else"),i.hasAttribute(":if")?n=p:(i.remove(),n=i.content?[...i.content.childNodes]:[i])):n=p,()=>{const t=u(r)?.valueOf()?s:e[G]?p:n;if(i&&(i[G]=t===s),l!=t){l[0]?.[q]&&(l=[l[0][q]]),X(o,l,l=t,c);for(let e of l)Z(e,r)}}},R.ref=(e,t,r)=>{let a;return()=>{a&&delete r[a],r[a=_(t,r)]=e}},R.scope=(e,t,r,a)=>{let l=H(t,a);return()=>{Z(e,{...r,...l(r)?.valueOf?.()||{}})}},R.html=(e,t,r,a)=>{let l=H(t,a)(r);if(!l)return;let s=(l.content||l).cloneNode(!0);e.replaceChildren(s),Z(e,r)},R.text=(e,t,r)=>{let a=H(t,"text");return e.content&&e.replaceWith(e=document.createTextNode("")),()=>{let t=a(r)?.valueOf();e.textContent=null==t?"":t}},R.class=(e,t,r)=>{let a=H(t,"class"),l=new Set;return()=>{let t=a(r),s=new Set;t&&("string"==typeof t?_(t?.valueOf?.(),r).split(" ").map((e=>s.add(e))):Array.isArray(t)?t.map((e=>(e=_(e?.valueOf?.(),r))&&s.add(e))):Object.entries(t).map((([e,t])=>t?.valueOf?.()&&s.add(e))));for(let t of l)s.has(t)?s.delete(t):e.classList.remove(t);for(let t of l=s)e.classList.add(t)}},R.style=(e,t,r)=>{let a=H(t,"style"),l=e.getAttribute("style")||"";return l.endsWith(";")||(l+="; "),()=>{let t=a(r)?.valueOf();if("string"==typeof t)e.setAttribute("style",l+_(t,r));else{e.setAttribute("style",l);for(let a in t)e.style.setProperty(a,_(t[a],r))}}},R.default=(e,t,r,a)=>{let l=a.startsWith("on")&&a.slice(2),s=H(t,a);if(l){let t;return()=>(t?.(),t=I(e,l,s(r)))}return()=>{let t=s(r)?.valueOf();if(a)Y(e,a,_(t,r));else for(let a in t)Y(e,re(a),_(t[a],r))}};var I=(e,t,r=(()=>{}))=>{const a={evt:"",target:e,test:()=>!0};a.evt=t.replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,r="")=>(a.test=Q[t]?.(a,...r.split("-"))||a.test,"")));const{evt:l,target:s,test:n,defer:o,stop:i,prevent:c,...u}=a;o&&(r=o(r));const p=e=>n(e)&&(i&&e.stopPropagation(),c&&e.preventDefault(),r.call(s,e));return s.addEventListener(l,p,u),()=>s.removeEventListener(l,p,u)},Q={prevent(e){e.prevent=!0},stop(e){e.stop=!0},once(e){e.once=!0},passive(e){e.passive=!0},capture(e){e.capture=!0},window(e){e.target=window},document(e){e.target=document},throttle(e,t){e.defer=e=>ee(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>te(e,t?Number(t)||0:108)},outside:e=>t=>{let r=e.target;return!(r.contains(t.target)||!1===t.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:e=>t=>t.target===e.target,ctrl:(e,...t)=>e=>V.ctrl(e)&&t.every((t=>V[t]?V[t](e):e.key===t)),shift:(e,...t)=>e=>V.shift(e)&&t.every((t=>V[t]?V[t](e):e.key===t)),alt:(e,...t)=>e=>V.alt(e)&&t.every((t=>V[t]?V[t](e):e.key===t)),meta:(e,...t)=>e=>V.meta(e)&&t.every((t=>V[t]?V[t](e):e.key===t)),arrow:()=>V.arrow,enter:()=>V.enter,escape:()=>V.escape,tab:()=>V.tab,space:()=>V.space,backspace:()=>V.backspace,delete:()=>V.delete,digit:()=>V.digit,letter:()=>V.letter,character:()=>V.character},V={ctrl:e=>e.ctrlKey||"Control"===e.key||"Ctrl"===e.key,shift:e=>e.shiftKey||"Shift"===e.key,alt:e=>e.altKey||"Alt"===e.key,meta:e=>e.metaKey||"Meta"===e.key||"Command"===e.key,arrow:e=>e.key.startsWith("Arrow"),enter:e=>"Enter"===e.key,escape:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,backspace:e=>"Backspace"===e.key,delete:e=>"Delete"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^[a-zA-Z]$/.test(e.key),character:e=>/^\S$/.test(e.key)},Y=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},ee=(e,t)=>{let r,a,l=s=>{r=!0,setTimeout((()=>{if(r=!1,a)return a=!1,l(s),e(s)}),t)};return t=>r?a=!0:(l(t),e(t))},te=(e,t)=>{let r;return a=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(a)}),t)}},re=e=>e.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase()));R.value=(e,t,r)=>{let a,l,s=H(t,"value"),n="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(a=e.selectionStart,l=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),a&&e.setSelectionRange(a,l)):"checkbox"===e.type?t=>(e.checked=t,Y(e,"checked",t)):"select-one"===e.type?t=>{for(let t in e.options)t.removeAttribute("selected");e.value=t,e.selectedOptions[0]?.setAttribute("selected","")}:t=>e.value=t;return()=>n(s(r)?.valueOf?.())},R.fx=(e,t,r,a)=>{let l=H(t,a);return()=>l(r)};export{K as batch,H as compile,P as computed,Z as default,R as directive,M as effect,_ as ipol,D as signal,X as swap,J as untracked};
1
+ var e,t,r,l,n,s=Object.defineProperty,a=Symbol("signals"),o=Symbol("length");function i(t,r){if(!t)return t;if(t[a]&&!r)return t;if(Array.isArray(t))return function(t){let r;if(t[a])return t;let l=e(t.length),n=Array(t.length).fill(null);const s=new Proxy(n,{get:(s,c)=>c===o?l:c===a?n:"length"===c?Array.prototype[r]?l.peek():l.value:(r=c,n[c]?n[c].valueOf():c<n.length?(n[c]=e(i(t[c]))).value:void 0),set(e,t,r){if("length"===t){for(let e=r,t=n.length;e<t;e++)delete s[e];return l.value=n.length=r,!0}return c(n,t,r),t>=l.peek()&&(l.value=n.length=Number(t)+1),!0},deleteProperty:(e,t)=>(u(n,t),!0)});return s}(t);if(t.constructor!==Object)return t;let l=e(Object.values(t).length);r||={};const s=new Proxy(r,{get:(e,t)=>t===o?l:t===a?r:r[t]?.valueOf(),set:(e,t,n,s)=>(s=r[t],c(r,t,n),s||++l.value),deleteProperty:(e,t)=>u(r,t)&&l.value--,ownKeys:()=>(l.value,Reflect.ownKeys(r))});if(t[a])for(let e in t)r[e]=t[a][e];else for(let e in t){const l=Object.getOwnPropertyDescriptor(t,e);l?.get?(r[e]=n(l.get.bind(s)))._set=l.set?.bind(s):(r[e]=null,c(r,e,t[e]))}return s}function c(t,n,s){let a=t[n];if(a)if(s===a.peek());else if(a._set)a._set(s);else if(Array.isArray(s)&&Array.isArray(a.peek())){const e=a.peek();e[o]?r((()=>{l((()=>{let t=0,r=s.length;for(;t<r;t++)e[t]=s[t];e.length=r}))})):a.value=s}else a.value=i(s);else t[n]=a=s?.peek?s:e(i(s))}function u(e,t){const r=e[t];if(r){const l=r[Symbol.dispose];return l&&delete r[Symbol.dispose],delete e[t],l?.(),!0}}var f=Symbol.dispose||=Symbol("dispose"),p={},d=new WeakMap;function y(e,t){let r;e?.[Symbol.iterator]||(e=[e]);for(let l of e)l?.children&&(d.has(l)?Object.assign(d.get(l),t):(r||=i(t||{}),v(l,r),d.has(l)||d.set(l,r)));return r}function v(e,t,r=e.parentNode,l=[]){if(e.attributes)for(let n=0;n<e.attributes.length;){let s=e.attributes[n];if(":"===s.name[0]){e.removeAttribute(s.name);let n=s.name.slice(1).split(":");for(let r of n){let n=p[r]||p.default,a=(n.parse||g)(s.value,g),o=n(e,a,t,r);o&&l.push(o)}if(d.has(e))return;if(e.parentNode!==r)return}else n++}for(let r of[...e.children])v(r,t,e);e.classList?.add("∴"),e[f]=()=>{for(;l.length;)l.pop()();e.classList.remove("∴"),d.delete(e);let t=e.getElementsByClassName("∴");for(;t.length;)t[0][f]?.()}}var h,m={},g=(e,t,r)=>{if(r=m[e=e.trim()])return r;try{r=h(e)}catch(r){throw Object.assign(r,{message:`∴ ${r.message}\n\n${t}${e?`="${e}"\n\n`:""}`,expr:e})}return m[e]=r};y.use=s=>{s.signal&&function(s){e=s.signal,t=s.effect,n=s.computed,l=s.batch||(e=>e()),r=s.untracked||l}(s),s.compile&&(h=s.compile)};var b,k,A={};((e,t)=>{for(var r in t)s(e,r,{get:t[r],enumerable:!0})})(A,{batch:()=>N,computed:()=>O,effect:()=>w,signal:()=>S,untracked:()=>x});var S=(e,t,r=new Set)=>((t={get value(){return b?.deps.push(r.add(b)),e},set value(t){if(t!==e){e=t;for(let e of r)k?k.add(e):e()}},peek:()=>e}).toJSON=t.then=t.toString=t.valueOf=()=>t.value,t),w=(e,t,r,l)=>(l=(r=l=>{t?.call?.(),l=b,b=r;try{t=e()}finally{b=l}}).deps=[],r(),e=>{for(t?.call?.();e=l.pop();)e.delete(r)}),O=(e,t=S(),r,l)=>((r={get value(){return l||=w((()=>t.value=e())),t.value},peek:t.peek}).toJSON=r.then=r.toString=r.valueOf=()=>r.value,r),N=e=>{let t=k;t||(k=new Set);try{e()}finally{if(!t){t=k,k=null;for(const e of t)e()}}},x=(e,t,r)=>(t=b,b=null,r=e(),b=t,r),j=(e,t,r,l=null,{remove:n,insert:s}=j)=>{let a,o,i,c=0,u=new Set(r);for(;i=t[c++];)u.has(i)?a=a||i:n(i,e);for(a=a||l,c=0;i=r[c++];)o=a?a.nextSibling:l,a===i?a=o:(r[c]===o&&(a=o),s(i,a,e));return r};j.insert=(e,t,r)=>r.insertBefore(e,t),j.remove=(e,t)=>t.removeChild(e);var C=j,E=Symbol(":each");p.each=(e,[l,s,i],c)=>{const u=e[E]=document.createTextNode("");e.replaceWith(u);let f,p,d=0;const v=n((()=>{p=null;let e=i(c);return"number"==typeof e&&(e=Array.from({length:e},((e,t)=>t+1))),e?.constructor===Object&&(p=Object.keys(e),e=Object.values(e)),e||[]})),h=()=>{r((()=>{let t=0,r=v.value,n=r.length;if(f&&!f[o]){for(let e of f[a]||[])e[Symbol.dispose]();f=null,d=0}if(n<d)f.length=n;else{if(f)for(;t<d;t++)f[t]=r[t];else f=r;for(;t<n;t++){f[t]=r[t];let n=t,o=Object.create(c,{[l]:{get:()=>f[n]},[s]:{value:p?p[n]:n}}),i=(e.content||e).cloneNode(!0),d=e.content?[...i.childNodes]:[i];u.before(i),y(d,o),((f[a]||=[])[t]||={})[Symbol.dispose]=()=>{for(let e of d)e[Symbol.dispose](),e.remove()}}}d=n}))};let m=0;return t((()=>{f||v.value[o]?.value,m?m++:(h(),queueMicrotask((()=>(m&&h(),m=0))))}))},p.each.parse=(e,t)=>{let[r,l]=e.split(/\s+in\s+/),[n,s="$"]=r.split(/\s*,\s*/);return[n,s,t(l)]};var $=Symbol("if");p.if=(e,r,l)=>{let n,s,a,o=e.parentNode,i=e.nextElementSibling,c=document.createTextNode(""),u=[];return e.after(c),e.content?(n=u,e.remove(),s=[...e.content.childNodes]):s=n=[e],i?.hasAttribute(":else")?(i.removeAttribute(":else"),i.hasAttribute(":if")?a=u:(i.remove(),a=i.content?[...i.content.childNodes]:[i])):a=u,t((()=>{const t=r(l)?s:e[$]?u:a;if(i&&(i[$]=t===s),n!=t){n[0]?.[E]&&(n=[n[0][E]]);for(let e of n)e.remove();n=t;for(let e of n)o.insertBefore(e,c),y(e,l)}}))},p.default=(e,r,l,n)=>{let s,a=n.startsWith("on")&&n.slice(2);return t(a?()=>(s?.(),s=P(e,a,r(l))):()=>{let t=r(l);if(n)L(e,n,B(t,l));else for(let r in t)L(e,K(r),B(t[r],l))})};var P=(e,t,r=(()=>{}))=>{const l={evt:"",target:e,test:()=>!0};l.evt=t.replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,r="")=>(l.test=T[t]?.(l,...r.split("-"))||l.test,"")));const{evt:n,target:s,test:a,defer:o,stop:i,prevent:c,...u}=l;o&&(r=o(r));const f=e=>a(e)&&(i&&e.stopPropagation(),c&&e.preventDefault(),r.call(s,e));return s.addEventListener(n,f,u),()=>s.removeEventListener(n,f,u)},T={prevent(e){e.prevent=!0},stop(e){e.stop=!0},once(e){e.once=!0},passive(e){e.passive=!0},capture(e){e.capture=!0},window(e){e.target=window},document(e){e.target=document},throttle(e,t){e.defer=e=>_(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>D(e,t?Number(t)||0:108)},outside:e=>t=>{let r=e.target;return!(r.contains(t.target)||!1===t.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:e=>t=>t.target===e.target,ctrl:(e,...t)=>e=>W.ctrl(e)&&t.every((t=>W[t]?W[t](e):e.key===t)),shift:(e,...t)=>e=>W.shift(e)&&t.every((t=>W[t]?W[t](e):e.key===t)),alt:(e,...t)=>e=>W.alt(e)&&t.every((t=>W[t]?W[t](e):e.key===t)),meta:(e,...t)=>e=>W.meta(e)&&t.every((t=>W[t]?W[t](e):e.key===t)),arrow:()=>W.arrow,enter:()=>W.enter,escape:()=>W.escape,tab:()=>W.tab,space:()=>W.space,backspace:()=>W.backspace,delete:()=>W.delete,digit:()=>W.digit,letter:()=>W.letter,character:()=>W.character},W={ctrl:e=>e.ctrlKey||"Control"===e.key||"Ctrl"===e.key,shift:e=>e.shiftKey||"Shift"===e.key,alt:e=>e.altKey||"Alt"===e.key,meta:e=>e.metaKey||"Meta"===e.key||"Command"===e.key,arrow:e=>e.key.startsWith("Arrow"),enter:e=>"Enter"===e.key,escape:e=>e.key.startsWith("Esc"),tab:e=>"Tab"===e.key,space:e=>" "===e.key||"Space"===e.key||" "===e.key,backspace:e=>"Backspace"===e.key,delete:e=>"Delete"===e.key,digit:e=>/^\d$/.test(e.key),letter:e=>/^[a-zA-Z]$/.test(e.key),character:e=>/^\S$/.test(e.key)},L=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},_=(e,t)=>{let r,l,n=s=>{r=!0,setTimeout((()=>{if(r=!1,l)return l=!1,n(s),e(s)}),t)};return t=>r?l=!0:(n(t),e(t))},D=(e,t)=>{let r;return l=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(l)}),t)}},K=e=>e.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),B=(e,t)=>e?.replace?e.replace(/\$<([^>]+)>/g,((e,r)=>t[r]??"")):e;p.ref=(e,t,r)=>{Object.defineProperty(r,B(t,r),{value:e})},p.ref.parse=e=>e,p.with=(e,r,l)=>{let n,s;return t((()=>{s=r(l),Object.assign(n||=y(e,i(s,Object.create(l[a]))),s)}))},p.html=(e,t,r)=>{let l=t(r);if(!l)return;let n=(l.content||l).cloneNode(!0);e.replaceChildren(n),y(e,r)},p.text=(e,r,l)=>(e.content&&e.replaceWith(e=document.createTextNode("")),t((()=>{let t=r(l);e.textContent=null==t?"":t}))),p.class=(e,r,l)=>{let n=new Set;return t((()=>{let t=r(l),s=new Set;t&&("string"==typeof t?B(t,l).split(" ").map((e=>s.add(e))):Array.isArray(t)?t.map((e=>(e=B(e,l))&&s.add(e))):Object.entries(t).map((([e,t])=>t&&s.add(e))));for(let t of n)s.has(t)?s.delete(t):e.classList.remove(t);for(let t of n=s)e.classList.add(t)}))},p.style=(e,r,l)=>{let n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t((()=>{let t=r(l);if("string"==typeof t)e.setAttribute("style",n+B(t,l));else{e.setAttribute("style",n);for(let r in t)e.style.setProperty(r,B(t[r],l))}}))},p.value=(e,r,l)=>{let n,s,a="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(n=e.selectionStart,s=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),n&&e.setSelectionRange(n,s)):"checkbox"===e.type?t=>(e.checked=t,L(e,"checked",t)):"select-one"===e.type?t=>{for(let t in e.options)t.removeAttribute("selected");e.value=t,e.selectedOptions[0]?.setAttribute("selected","")}:t=>e.value=t;return t((()=>a(r(l))))},p.fx=(e,r,l)=>t((()=>r(l))),y.use(A),y.use({compile:e=>y.constructor("__scope",`with (__scope) { return ${e} };`)}),y.use({swap:C});var M=y;export{M as default};
package/package.json CHANGED
@@ -1,19 +1,20 @@
1
1
  {
2
2
  "name": "sprae",
3
3
  "description": "DOM microhydration.",
4
- "version": "9.1.0",
4
+ "version": "10.0.0",
5
5
  "main": "./sprae.js",
6
6
  "module": "./sprae.js",
7
7
  "type": "module",
8
8
  "files": [
9
9
  "core.js",
10
10
  "sprae.js",
11
+ "store.js",
12
+ "signal.js",
11
13
  "directive",
12
14
  "dist"
13
15
  ],
14
16
  "dependencies": {
15
- "signal-polyfill": "^0.1.0",
16
- "swapdom": "^1.2.1"
17
+ "ulive": "^1.0.2"
17
18
  },
18
19
  "devDependencies": {
19
20
  "@preact/signals": "^1.1.3",
@@ -22,10 +23,10 @@
22
23
  "esbuild": "^0.15.14",
23
24
  "hyperf": "^1.6.2",
24
25
  "jsdom": "^21.1.0",
25
- "subscript": "^8.3.4",
26
+ "signal-polyfill": "^0.1.1",
27
+ "subscript": "^8.3.5",
26
28
  "terser": "^5.15.1",
27
29
  "tst": "^7.1.1",
28
- "ulive": "^1.0.1",
29
30
  "usignal": "^0.9.0",
30
31
  "wait-please": "^3.1.0"
31
32
  },
package/readme.md CHANGED
@@ -2,9 +2,8 @@
2
2
 
3
3
  > DOM tree microhydration
4
4
 
5
- _Sprae_ is a compact & ergonomic progressive enhancement framework.<br/>
6
- It provides `:`-attributes for inline markup logic with [_signals_](https://github.com/proposal-signals/proposal-signals) reactivity.<br/>
7
- Perfect for small-scale websites, prototypes, or lightweight UI.<br/>
5
+ _Sprae_ is open & minimalistic progressive enhancement framework.<br/>
6
+ Perfect for small-scale websites, static pages, landings, prototypes, or lightweight UI.<br/>
8
7
 
9
8
 
10
9
  ## Usage
@@ -15,17 +14,17 @@ Perfect for small-scale websites, prototypes, or lightweight UI.<br/>
15
14
  </div>
16
15
 
17
16
  <script type="module">
18
- import sprae, { signal } from 'sprae'
17
+ import sprae from 'sprae'
19
18
 
20
- const name = signal('Kitty')
21
- sprae(container, { user: { name } }) // init
19
+ // init
20
+ const state = sprae(container, { user: { name: 'Kitty' } })
22
21
 
23
- name.value = 'Dolly' // update
22
+ // update
23
+ state.user.name = 'Dolly'
24
24
  </script>
25
25
  ```
26
26
 
27
- Sprae evaluates `:`-directives and evaporates them, attaching state to html.
28
-
27
+ Sprae evaluates `:`-directives and evaporates them, returning reactive state.
29
28
 
30
29
  ## Directives
31
30
 
@@ -43,21 +42,17 @@ Control flow of elements.
43
42
  ```
44
43
 
45
44
 
46
- #### `:each="item, index in items"`
45
+ #### `:each="item, index? in items"`
47
46
 
48
- Multiply element. Item is identified either by `item.id`, `item.key` or `item` itself.
47
+ Multiply element.
49
48
 
50
49
  ```html
51
50
  <ul><li :each="item in items" :text="item"/></ul>
52
51
 
53
52
  <!-- cases -->
54
- <li :each="item, idx in list" />
55
- <li :each="val, key in obj" />
56
- <li :each="idx in number" />
57
-
58
- <!-- by condition -->
59
- <li :if="items" :each="item in items" :text="item" />
60
- <li :else>Empty list</li>
53
+ <li :each="item, idx in array" />
54
+ <li :each="value, key in object" />
55
+ <li :each="count, idx in number" />
61
56
 
62
57
  <!-- fragment -->
63
58
  <template :each="item in items">
@@ -82,22 +77,28 @@ Welcome, <template :text="user.name" />.
82
77
 
83
78
  #### `:class="value"`
84
79
 
85
- Set class value, extends existing `class`.
80
+ Set class value.
86
81
 
87
82
  ```html
88
- <!-- string with interpolation -->
83
+ <!-- appends class -->
84
+ <div class="foo" :class="bar"></div>
85
+
86
+ <!-- interpolation -->
89
87
  <div :class="'foo $<bar>'"></div>
90
88
 
91
- <!-- array/object a-la clsx -->
89
+ <!-- array/object, a-la clsx -->
92
90
  <div :class="[foo && 'foo', {bar: bar}]"></div>
93
91
  ```
94
92
 
95
93
  #### `:style="value"`
96
94
 
97
- Set style value, extends existing `style`.
95
+ Set style value.
98
96
 
99
97
  ```html
100
- <!-- string with interpolation -->
98
+ <!-- extends style -->
99
+ <div style="foo: bar" :style="'baz: qux'">
100
+
101
+ <!-- interpolation -->
101
102
  <div :style="'foo: $<bar>'"></div>
102
103
 
103
104
  <!-- object -->
@@ -109,16 +110,19 @@ Set style value, extends existing `style`.
109
110
 
110
111
  #### `:value="value"`
111
112
 
112
- Set value of an input, textarea or select. Takes handle of `checked` and `selected` attributes.
113
+ Set value of an input, textarea or select.
113
114
 
114
115
  ```html
115
116
  <input :value="value" />
116
117
  <textarea :value="value" />
117
118
 
118
- <!-- selects right option -->
119
+ <!-- selects right option & handles selected attr -->
119
120
  <select :value="selected">
120
121
  <option :each="i in 5" :value="i" :text="i"></option>
121
122
  </select>
123
+
124
+ <!-- handles checked attr -->
125
+ <input type="checkbox" :value="checked" />
122
126
  ```
123
127
 
124
128
  #### `:[prop]="value"`, `:="values"`
@@ -135,33 +139,32 @@ Set any attribute(s).
135
139
  <input :="{ id: name, name, type: 'text', value }" />
136
140
  ```
137
141
 
138
- #### `:scope="data"`
142
+ #### `:with="values"`
139
143
 
140
- Define or extend data scope for a subtree.
144
+ Define values for a subtree.
141
145
 
142
146
  ```html
143
- <x :scope="{ foo: signal('bar') }">
144
- <!-- extends parent scope -->
145
- <y :scope="{ baz: 'qux' }" :text="foo + baz"></y>
147
+ <x :with="{ foo: signal('bar') }">
148
+ <y :with="{ baz: 'qux' }" :text="foo + baz"></y>
146
149
  </x>
147
150
  ```
148
151
 
149
152
  #### `:ref="name"`
150
153
 
151
- Expose element to current scope with `name`.
154
+ Expose element with `name`.
152
155
 
153
156
  ```html
154
157
  <textarea :ref="text" placeholder="Enter text..."></textarea>
155
158
 
156
159
  <!-- iterable items -->
157
160
  <li :each="item in items" :ref="item">
158
- <input :onfocus..onblur=="e => (item.classList.add('editing'), e => item.classList.remove('editing'))"/>
161
+ <input :onfocus..onblur="e => (item.classList.add('editing'), e => item.classList.remove('editing'))"/>
159
162
  </li>
160
163
  ```
161
164
 
162
165
  #### `:fx="code"`
163
166
 
164
- Run effect, not changing any attribute.<br/>Optional cleanup is called in-between effect calls or on disposal.
167
+ Run effect, not changing any attribute.
165
168
 
166
169
  ```html
167
170
  <div :fx="a.value ? foo() : bar()" />
@@ -172,7 +175,7 @@ Run effect, not changing any attribute.<br/>Optional cleanup is called in-betwee
172
175
 
173
176
  #### `:on[event]="handler"`
174
177
 
175
- Attach event(s) listener with possible modifiers.
178
+ Attach event(s) listener with optional modifiers.
176
179
 
177
180
  ```html
178
181
  <input type="checkbox" :onchange="e => isChecked = e.target.value">
@@ -183,7 +186,7 @@ Attach event(s) listener with possible modifiers.
183
186
  <!-- events sequence -->
184
187
  <button :onfocus..onblur="e => ( handleFocus(), e => handleBlur())">
185
188
 
186
- <!-- event modifiers -->
189
+ <!-- modifiers -->
187
190
  <button :onclick.throttle-500="handler">Not too often</button>
188
191
  ```
189
192
 
@@ -211,7 +214,7 @@ Hello, <template :html="user.name">Guest</template>.
211
214
 
212
215
  <!-- instantiate template -->
213
216
  <template :ref="tpl"><span :text="foo"></span></template>
214
- <div :html="tpl" :scope="{foo:'bar'}">...inserted here...</div>
217
+ <div :html="tpl" :with="{foo:'bar'}">...inserted here...</div>
215
218
  ```
216
219
 
217
220
  #### `:data="values"` 🔌
@@ -265,34 +268,39 @@ Trigger when element is connected / disconnected from DOM.
265
268
  ```
266
269
  -->
267
270
 
268
- ## Customization
269
-
270
- _Sprae_ can be reconfigured to use alternative signals provider, expressions evaluator or directives.
271
+ ## Signals
271
272
 
272
- ### Signals
273
-
274
- Sprae uses [standard signals](https://github.com/proposal-signals/proposal-signals) for reactivity, but can be switched to any preact-flavored signals library:
273
+ Sprae can take signal values. Signals provider can be switched to any preact-flavored implementation:
275
274
 
276
275
  ```js
277
- import sprae, { signal, computed, effect, batch, untracked } from 'sprae';
276
+ import sprae from 'sprae';
277
+ import { signal, computed, effect, batch, untracked } from 'sprae/signal';
278
278
  import * as signals from '@preact/signals-core';
279
279
 
280
+ // switch provider to @preact/signals-core
280
281
  sprae.use(signals);
281
282
 
282
- sprae(el, { name: signal('Kitty') });
283
+ // use signal as state value
284
+ const name = signal('Kitty')
285
+ sprae(el, { name });
286
+
287
+ // update state
288
+ name.value = 'Dolly';
283
289
  ```
284
290
 
285
291
  Provider | Size | Feature
286
292
  :---|:---|:---
287
- [`ulive`](https://ghub.io/ulive) | 350b | Minimal implementation, basic performance, good for small states
288
- [`@webreflection/signal`](https://ghib.io/@webreflection/signal) | 531b | Class-based, better performance, good for small-medium states
289
- [`usignal`](https://ghib.io/usignal) | 850b | Class-based with optimizations, good for medium states
290
- [`@preact/signals-core`](https://ghub.io/@preact/signals-core) | 1.47kb | Best performance, good for any states
293
+ [`ulive`](https://ghub.io/ulive) (default) | 350b | Minimal implementation, basic performance, good for small states.
294
+ [`@webreflection/signal`](https://ghib.io/@webreflection/signal) | 531b | Class-based, better performance, good for small-medium states.
295
+ [`usignal`](https://ghib.io/usignal) | 850b | Class-based with optimizations, good for medium states.
296
+ [`@preact/signals-core`](https://ghub.io/@preact/signals-core) | 1.47kb | Best performance, good for any states, industry standard.
297
+ [`signal-polyfill`](https://github.com/tc39/proposal-signals) | 2.5kb | Proposal signals. Use via [adapter](https://gist.github.com/dy/bbac687464ccf5322ab0e2fd0680dc4d).
291
298
 
292
299
 
293
- ### Evaluator
300
+ ## Evaluator
294
301
 
295
- Expressions use _new Function_ as default evaluator, which is fast & compact way, but violates "unsafe-eval" CSP. To make eval stricter & safer, an alternative evaluator can be configured, eg. _justin_:
302
+ Expressions use _new Function_ as default evaluator, which is fast & compact way, but violates "unsafe-eval" CSP.
303
+ To make eval stricter & safer, as well as sandbox expressions, an alternative evaluator can be used, eg. _justin_:
296
304
 
297
305
  ```js
298
306
  import sprae from 'sprae'
@@ -301,7 +309,7 @@ import justin from 'subscript/justin'
301
309
  sprae.use({compile: justin}) // set up justin as default compiler
302
310
  ```
303
311
 
304
- [_Justin_](https://github.com/dy/subscript?tab=readme-ov-file#justin) is minimal JS subset. It avoids "unsafe-eval" CSP and provides sandboxing.
312
+ [_Justin_](https://github.com/dy/subscript?tab=readme-ov-file#justin) is minimal JS subset that avoids "unsafe-eval" CSP and provides sandboxing.
305
313
 
306
314
  ###### Operators:
307
315
 
@@ -316,65 +324,43 @@ sprae.use({compile: justin}) // set up justin as default compiler
316
324
  `true false null undefined NaN`
317
325
 
318
326
 
319
- ## Directives
327
+ ## Custom Build
320
328
 
321
- Sprae build can be tailored to project needs via `sprae/core`:
329
+ _Sprae_ can be tailored to project needs via `sprae/core`:
322
330
 
323
331
  ```js
324
- import sprae, { directive } from 'sprae/core.js'
332
+ // sprae.custom.js
333
+ import sprae, { directive } from 'sprae/core'
334
+ import { effect } from 'sprae/signal'
335
+ import * as signals from '@preact/signals'
336
+ import compile from 'subscript'
325
337
 
326
338
  // include directives
327
339
  import 'sprae/directive/if.js'
328
340
  import 'sprae/directive/text.js'
329
341
 
330
- // define custom directive
342
+ // custom directive :id="expression"
331
343
  directive.id = (el, evaluate, state) => {
332
- return () => el.id = evaluate(state) // return update function
344
+ effect(() => el.id = evaluate(state))
333
345
  }
334
- ```
335
346
 
336
- See [`sprae.js`](./sprae.js) for example.
347
+ // configure signals
348
+ sprae.use(signals)
337
349
 
338
- <!--
339
- ### DOM diffing
340
-
341
- DOM diffing uses [swapdom](https://github.com/dy/swapdom), but can be reconfigured to [list-difference](https://github.com/paldepind/list-difference/), [udomdiff](https://github.com/WebReflection/udomdiff), [domdiff](https://github.com/WebReflection/domdiff), or any other ([benchmark](https://github.com/luwes/js-diff-benchmark)):
342
-
343
- ```js
344
- import sprae from 'sprae';
345
- import domdiff from 'list-difference';
346
-
347
- // swap(parentNode, prevEls, newEls, endNode?)
348
- sprae.use({ swap: domdiff });
350
+ // configure compiler
351
+ sprae.use({ compile })
349
352
  ```
350
- -->
351
-
352
353
 
353
354
  <!-- ## Dispose
354
355
 
355
356
  To destroy state and detach sprae handlers, call `element[Symbol.dispose]()`. -->
356
357
 
357
358
 
358
- <!--
359
- ## v9 changes
360
-
361
- * No autoinit → use manual init via `import sprae from 'sprae'; sprae(document.body, state)`.
362
- * No default globals (`console`, `setTimeout` etc) - pass to state if required.
363
- * ``:class="`abc ${def}`"`` → `:class="'abc $<def>'"` (_justin_)
364
- * `:with={x:'x'}` -> `:scope={x:'x'}`
365
- * No reactive store → use signals for reactive values.
366
- * `:render="tpl"` → `:html="tpl"`
367
- * `@click="event.target"` → `:onclick="event => event.target"`
368
- * Async props / events are not supported, pass async functions via state.
369
- * Directives order matters, eg. `<a :if :each :scope />` !== `<a :scope :each :if />`
370
- * Only one directive per `<template>`, eg. `<template :each />`, not `<template :if :each/>`
371
- -->
372
-
373
359
  ## Justification
374
360
 
375
- [Template-parts](https://github.com/dy/template-parts) / [templize](https://github.com/dy/templize) is progressive, but is stuck with native HTML quirks ([parsing table](https://github.com/github/template-parts/issues/24), [SVG attributes](https://github.com/github/template-parts/issues/25), [liquid syntax](https://shopify.github.io/liquid/tags/template/#raw) conflict etc). [Alpine](https://github.com/alpinejs/alpine) / [petite-vue](https://github.com/vuejs/petite-vue) / [lucia](https://github.com/aidenyabi/lucia) escape native HTML quirks, but have excessive API (`:`, `x-`, `{}`, `@`, `$`) and tend to [self-encapsulate](https://github.com/alpinejs/alpine/discussions/3223).
361
+ [Template-parts](https://github.com/dy/template-parts) is stuck with native HTML quirks ([parsing table](https://github.com/github/template-parts/issues/24), [SVG attributes](https://github.com/github/template-parts/issues/25), [liquid syntax](https://shopify.github.io/liquid/tags/template/#raw) conflict etc). [Alpine](https://github.com/alpinejs/alpine) / [petite-vue](https://github.com/vuejs/petite-vue) / [lucia](https://github.com/aidenyabi/lucia) escape native HTML quirks, but have excessive API (`:`, `x-`, `{}`, `@`, `$`) and tend to [self-encapsulate](https://github.com/alpinejs/alpine/discussions/3223).
376
362
 
377
- _Sprae_ holds open & minimalistic philosophy, combining _`:`-directives_ with _signals_.
363
+ _Sprae_ holds open & minimalistic philosophy, combining _`:`-directives_ with emerging _signals_.
378
364
 
379
365
  <!--
380
366
  | | [AlpineJS](https://github.com/alpinejs/alpine) | [Petite-Vue](https://github.com/vuejs/petite-vue) | Sprae |
package/signal.js ADDED
@@ -0,0 +1,10 @@
1
+ // signals adapter - allows switching signals implementation and not depend on core
2
+ export let signal, effect, untracked, batch, computed;
3
+
4
+ export function use(s) {
5
+ signal = s.signal
6
+ effect = s.effect
7
+ computed = s.computed
8
+ batch = s.batch || (fn => fn())
9
+ untracked = s.untracked || batch
10
+ }
package/sprae.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import sprae from './core.js'
2
2
 
3
- import * as signals from './signal.js'
4
- import swap from 'swapdom/inflate'
3
+ import * as signals from 'ulive'
4
+ import swap from 'swapdom/deflate'
5
5
 
6
6
  // default directives
7
7
  import './directive/if.js'
8
8
  import './directive/each.js'
9
9
  import './directive/ref.js'
10
- import './directive/scope.js'
10
+ import './directive/with.js'
11
11
  import './directive/html.js'
12
12
  import './directive/text.js'
13
13
  import './directive/class.js'
@@ -26,4 +26,3 @@ sprae.use({ compile: expr => sprae.constructor(`__scope`, `with (__scope) { retu
26
26
  sprae.use({ swap })
27
27
 
28
28
  export default sprae
29
- export { signal, computed, effect, batch, untracked } from './core.js'
package/store.js ADDED
@@ -0,0 +1,156 @@
1
+ // signals-based proxy
2
+ import { signal, computed, effect, batch, untracked } from './signal.js'
3
+
4
+ export const _signals = Symbol('signals'), _change = Symbol('length');
5
+
6
+ // object store is not lazy
7
+ export default function store(values, signals) {
8
+ if (!values) return values
9
+
10
+ // ignore existing state as argument
11
+ if (values[_signals] && !signals) return values;
12
+
13
+ // redirect for optimized array store
14
+ if (Array.isArray(values)) return list(values)
15
+
16
+ // ignore non-objects
17
+ if (values.constructor !== Object) return values;
18
+
19
+ // NOTE: if you decide to unlazy values, think about large arrays - init upfront can be costly
20
+ let _len = signal(Object.values(values).length)
21
+
22
+ signals ||= {}
23
+ // proxy conducts prop access to signals
24
+ const state = new Proxy(signals, {
25
+ get: (_, key) => key === _change ? _len : key === _signals ? signals : signals[key]?.valueOf(),
26
+ set: (_, key, v, s) => (s = signals[key], set(signals, key, v), s || (++_len.value)), // bump length for new signal
27
+ deleteProperty: (_, key) => del(signals, key) && _len.value--,
28
+ ownKeys() {
29
+ // subscribe to length when object is spread
30
+ _len.value
31
+ return Reflect.ownKeys(signals);
32
+ },
33
+ })
34
+
35
+ // take over existing store signals instead of creating new ones
36
+ if (values[_signals]) for (let key in values) signals[key] = values[_signals][key];
37
+ else for (let key in values) {
38
+ const desc = Object.getOwnPropertyDescriptor(values, key)
39
+
40
+ // getter turns into computed
41
+ if (desc?.get) {
42
+ // stash setter
43
+ (signals[key] = computed(desc.get.bind(state)))._set = desc.set?.bind(state);
44
+ }
45
+ else {
46
+ // init blank signal - make sure we don't take prototype one
47
+ signals[key] = null
48
+ set(signals, key, values[key]);
49
+ }
50
+ }
51
+
52
+ return state
53
+ }
54
+
55
+
56
+ // array store - signals are lazy since arrays can be very large & expensive
57
+ export function list(values) {
58
+ // track last accessed property to find out if .length was directly accessed from expression or via .push/etc method
59
+ let lastProp
60
+
61
+ // ignore existing state as argument
62
+ if (values[_signals]) return values;
63
+
64
+ // .length signal is stored separately, since it cannot be replaced on array
65
+ let _len = signal(values.length),
66
+ // gotta fill with null since proto methods like .reduce may fail
67
+ signals = Array(values.length).fill(null);
68
+
69
+ // proxy conducts prop access to signals
70
+ const state = new Proxy(signals, {
71
+ get(_, key) {
72
+ if (key === _change) return _len
73
+ if (key === _signals) return signals
74
+
75
+ // console.log('get', key)
76
+ // if .length is read within .push/etc - peek signal to avoid recursive subscription
77
+ if (key === 'length') return (Array.prototype[lastProp]) ? _len.peek() : _len.value;
78
+
79
+ lastProp = key;
80
+
81
+ if (signals[key]) return signals[key].valueOf()
82
+
83
+ // I hope reading values here won't diverge from signals
84
+ if (key < signals.length) return (signals[key] = signal(store(values[key]))).value
85
+ },
86
+
87
+ set(_, key, v) {
88
+ // .length
89
+ if (key === 'length') {
90
+ // force cleaning up tail
91
+ for (let i = v, l = signals.length; i < l; i++) delete state[i]
92
+ _len.value = signals.length = v;
93
+ return true
94
+ }
95
+
96
+ set(signals, key, v)
97
+
98
+ // force changing length, if eg. a=[]; a[1]=1 - need to come after setting the item
99
+ if (key >= _len.peek()) _len.value = signals.length = Number(key) + 1
100
+
101
+ return true
102
+ },
103
+
104
+ deleteProperty: (_, key) => (del(signals, key), true),
105
+
106
+ })
107
+
108
+ return state
109
+ }
110
+
111
+ // set/update signal value
112
+ function set(signals, key, v) {
113
+ let s = signals[key]
114
+
115
+ // new property
116
+ if (!s) {
117
+ // preserve signal value as is
118
+ signals[key] = s = v?.peek ? v : signal(store(v))
119
+ }
120
+ // skip unchanged (although can be handled by last condition - we skip a few checks this way)
121
+ else if (v === s.peek());
122
+ // stashed _set for value with getter/setter
123
+ else if (s._set) s._set(v)
124
+ // patch array
125
+ else if (Array.isArray(v) && Array.isArray(s.peek())) {
126
+ const cur = s.peek()
127
+ // if we update plain array (stored in signal) - take over value instead
128
+ if (cur[_change]) untracked(() => {
129
+ batch(() => {
130
+ let i = 0, l = v.length;
131
+ for (; i < l; i++) cur[i] = v[i]
132
+ cur.length = l // forces deleting tail signals
133
+ })
134
+ })
135
+ else {
136
+ s.value = v
137
+ }
138
+ }
139
+ // .x = y
140
+ else {
141
+ s.value = store(v)
142
+ }
143
+ }
144
+
145
+ // delete signal
146
+ function del(signals, key) {
147
+ // console.log('delete', key)
148
+ const s = signals[key]
149
+ if (s) {
150
+ const del = s[Symbol.dispose]
151
+ if (del) delete s[Symbol.dispose]
152
+ delete signals[key]
153
+ del?.()
154
+ return true
155
+ }
156
+ }
@@ -1,10 +0,0 @@
1
- import sprae, { directive } from "../core.js";
2
-
3
- // `:each` can redefine scope as `:each="a in {myScope}"`,
4
- // same time per-item scope as `:each="..." :scope="{collapsed:true}"` is useful
5
- directive.scope = (el, evaluate, rootState) => {
6
- // local state may contain signals that update, so we take them over
7
- return () => {
8
- sprae(el, { ...rootState, ...(evaluate(rootState)?.valueOf?.() || {}) });
9
- }
10
- };