sprae 6.1.2 → 8.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/package.json +3 -5
- package/readme.md +75 -13
- package/sprae.auto.js +682 -327
- package/sprae.auto.min.js +1 -1
- package/sprae.js +682 -327
- package/sprae.min.js +1 -1
- package/src/core.js +24 -25
- package/src/directives.js +109 -83
- package/src/domdiff.js +10 -7
- package/src/{state.js → state.proxy.js} +40 -17
- package/src/state.signals-proxy.js +133 -0
- package/src/state.signals.js +82 -0
package/sprae.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,l=class extends Map{#e=new FinalizationRegistry((e=>super.delete(e)));get size(){return[...this].length}constructor(e=[]){super();for(const[t,r]of e)this.set(t,r)}get(e){return super.get(e)?.deref()}set(e,t){let r=super.get(e);return r&&this.#e.unregister(r),r=(e=>n.get(e)||(e=>{const t=new WeakRef(e);return n.set(e,t),t})(e))(t),this.#e.register(t,e,r),super.set(e,r)}},s=new Set,i=new WeakMap,o=new WeakMap,a=new WeakMap,u=Symbol("parent"),c={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console,window:window,document:document,history:history,location:location},f={has:()=>!0,get(t,r){if("symbol"==typeof r)return t[r];if(!(r in t))return t[u]?.[r];if(r in Object.prototype||Array.isArray(t)&&r in Array.prototype&&"length"!==r)return t[r];let n=t[r];if(e){let n=i.get(t);n||i.set(t,n={}),n[r]?n[r].includes(e)||n[r].push(e):n[r]=[e]}if(n&&n.constructor===Object||Array.isArray(n)){let e=o.get(n);return e||o.set(n,e=new Proxy(n,f)),e}return n},set(e,t,r){if(!(t in e)&&e[u]&&t in e[u])return e[u][t]=r;if(!Array.isArray(e)&&Object.is(e[t],r))return!0;e[t]=r;let n=i.get(e)?.[t];if(n)for(let e of n)s.add(e);return h(),!0},deleteProperty:(e,t)=>(e[t]=void 0,delete e[t],!0)},p=(e,t)=>{if(o.has(e))return o.get(e);if(a.has(e))return e;let r=new Proxy(e,f);o.set(e,r),a.set(r,e);let n=Object.getOwnPropertyDescriptors(e);for(let t in n){let l=n[t];l.get&&l.get&&(l.get=l.get.bind(r),Object.defineProperty(e,t,l))}return e[u]=t?p(t):c,r},y=t=>{const r=()=>{let n=e;e=r,t(),e=n};return r(),r},h=()=>{t||(t=!0,r((()=>{for(let e of s)e.call();s.clear(),t=!1})))},d={},g={};d.if=(e,t)=>{let r=document.createTextNode(""),n=[j(e,t,":if")],l=[e],s=e;for(;(s=e.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(t=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),l.push(s),n.push(j(e,t,":else :if"))):(s.remove(),l.push(s),n.push((()=>1)));return e.replaceWith(s=r),e=>{let t=n.findIndex((t=>t(e)));l[t]!=s&&((s[m]||s).replaceWith(s=l[t]||r),O(s,e))}};var m=Symbol(":each");d.each=(e,t)=>{let r=function(e){let t=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,r=e.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!r)return;let n=r[2].trim(),l=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),s=l.match(t);return s?[l.replace(t,"").trim(),s[1].trim(),n]:[l,"",n]}(t);if(!r)return E(new Error,e,t,":each");const n=e[m]=document.createTextNode("");e.replaceWith(n);const s=j(e,r[2],":each"),i=e.getAttribute(":key"),o=i?j(null,i,":each"):null;e.removeAttribute(":key");const a=e.getAttribute(":ref"),u=new l,c=new l;let f=[];return l=>{let i=s(l);i?"number"==typeof i?i=Array.from({length:i},((e,t)=>[t+1,t])):Array.isArray(i)?i=i.map(((e,t)=>[t+1,e])):"object"==typeof i?i=Object.entries(i):E(Error("Bad list value"),e,t,":each"):i=[];let y=[],h=[];for(let[t,n]of i){let s,i,f=o?.({[r[0]]:n,[r[1]]:t});null==f?s=e.cloneNode(!0):(s=c.get(f))||c.set(f,s=e.cloneNode(!0)),y.push(s),null!=f&&(i=u.get(f))?i[r[0]]=n:(i=p({[r[0]]:n,[a||""]:null,[r[1]]:t},l),null!=f&&u.set(f,i)),h.push(i)}!function(e,t,r,n){const l=new Map,s=new Map;let i,o;for(i=0;i<t.length;i++)l.set(t[i],i);for(i=0;i<r.length;i++)s.set(r[i],i);for(i=o=0;i!==t.length||o!==r.length;){var a=t[i],u=r[o];if(null===a)i++;else if(r.length<=o)e.removeChild(t[i]),i++;else if(t.length<=i)e.insertBefore(u,t[i]||n),o++;else if(a===u)i++,o++;else{var c=s.get(a),f=l.get(u);void 0===c?(e.removeChild(t[i]),i++):void 0===f?(e.insertBefore(u,t[i]||n),o++):(e.insertBefore(t[f],t[i]||n),t[f]=null,f>i+1&&i++,o++)}}}(n.parentNode,f,y,n),f=y;for(let e=0;e<y.length;e++)O(y[e],h[e])}},d.with=(e,t,r)=>{const n=j(e,t,":with")(r);O(e,p(n,r))},d.ref=(e,t,r)=>{r[t]=e},g.render=(e,t,r)=>{let n=j(e,t,":render")(r);n||E(new Error("Template not found"),e,t,":render");let l=n.content.cloneNode(!0);e.replaceChildren(l),O(e,r)},g.id=(e,t)=>{let r=j(e,t,":id");return t=>{return n=r(t),e.id=n||0===n?n:"";var n}},g.class=(e,t)=>{let r=j(e,t,":class"),n=e.getAttribute("class");return t=>{let l=r(t),s=[n];l&&("string"==typeof l?s.push(l):Array.isArray(l)?s.push(...l):s.push(...Object.entries(l).map((([e,t])=>t?e:"")))),(s=s.filter(Boolean).join(" "))?e.setAttribute("class",s):e.removeAttribute("class")}},g.style=(e,t)=>{let r=j(e,t,":style"),n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t=>{let l=r(t);if("string"==typeof l)e.setAttribute("style",n+l);else{e.setAttribute("style",n);for(let t in l)e.style.setProperty(t,l[t])}}},g.text=(e,t)=>{let r=j(e,t,":text");return t=>{let n=r(t);e.textContent=null==n?"":n}},g[""]=(e,t)=>{let r=j(e,t,":");if(r)return t=>{let n=r(t);for(let t in n)W(e,t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),n[t])}},g.value=(e,t)=>{let r,n,l=j(e,t,":value"),s="text"===e.type||""===e.type?t=>e.setAttribute("value",e.value=null==t?"":t):"TEXTAREA"===e.tagName||"text"===e.type||""===e.type?t=>(r=e.selectionStart,n=e.selectionEnd,e.setAttribute("value",e.value=null==t?"":t),r&&e.setSelectionRange(r,n)):"checkbox"===e.type?t=>(e.value=t?"on":"",W(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 e=>s(l(e))};var b=(e,t,r,n)=>{let l=n.startsWith("on")&&n.slice(2),s=j(e,t,":"+n);if(s)return l?t=>{let r=s(t)||(()=>{});return v(e,l,r)}:t=>W(e,n,s(t))},v=(e,t,r)=>{if(!r)return;const n={evt:"",target:e,test:()=>!0};n.evt=(t.startsWith("on")?t.slice(2):t).replace(/\.(\w+)?-?([-\w]+)?/g,((e,t,r="")=>(n.test=A[t]?.(n,...r.split("-"))||n.test,"")));const{evt:l,target:s,test:i,defer:o,stop:a,prevent:u,...c}=n;o&&(r=o(r));const f=e=>i(e)&&(a&&e.stopPropagation(),u&&e.preventDefault(),r.call(s,e));return s.addEventListener(l,f,c),()=>s.removeEventListener(l,f,c)},A={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=>k(e,t?Number(t)||0:108)},debounce(e,t){e.defer=e=>x(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:e=>w.arrow,enter:e=>w.enter,escape:e=>w.escape,tab:e=>w.tab,space:e=>w.space,backspace:e=>w.backspace,delete:e=>w.delete,digit:e=>w.digit,letter:e=>w.letter,character:e=>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)},k=(e,t)=>{let r,n,l=s=>{r=!0,setTimeout((()=>{if(r=!1,n)return n=!1,l(s),e(s)}),t)};return t=>r?n=!0:(l(t),e(t))},x=(e,t)=>{let r;return n=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(n)}),t)}},W=(e,t,r)=>{null==r||!1===r?e.removeAttribute(t):e.setAttribute(t,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},S={};function j(e,t,r){let n=S[t];if(!n)try{n=S[t]=new Function("__scope",`with (__scope) { return ${t.trim()} };`)}catch(n){return E(n,e,t,r)}return l=>{let s;try{s=n.call(e,l)}catch(n){return E(n,e,t,r)}return s}}function E(e,t,n,l){Object.assign(e,{element:t,expression:n}),console.warn(`∴ ${e.message}\n\n${l}=${n?`"${n}"\n\n`:""}`,t),r((()=>{throw e}),0)}O.globals=c;var N=new WeakMap;function O(e,t){if(!e.children)return;if(N.has(e))return Object.assign(N.get(e),t);const r=p(t||{}),n=[],l=(e,t=e.parentNode)=>{for(let l in d){let s=":"+l;if(e.hasAttribute?.(s)){let i=e.getAttribute(s);if(e.removeAttribute(s),n.push(d[l](e,i,r,l)),N.has(e))return;if(e.parentNode!==t)return!1}}if(e.attributes)for(let t=0;t<e.attributes.length;){let l=e.attributes[t],s=l.name[0];if(":"===s||"@"===s){e.removeAttribute(l.name);let t="@"===s?`${l.value.includes("await")?"async":""} event=>{${l.value}}`:l.value,i=l.name.slice(1).split(s);for(let l of i){"@"===s&&(l="on"+l);let i=g[l]||b;n.push(i(e,t,r,l))}}else t++}for(let t,r=0;t=e.children[r];r++)!1===l(t,e)&&r--};l(e);for(let e of n)if(e){let t;y((()=>{"function"==typeof t&&t(),t=e(r)}))}return N.set(e,r),r}var $=O;document.currentScript&&O(document.documentElement);export{$ as default};
|
|
1
|
+
function t(){throw new Error("Cycle detected")}var e=Symbol.for("preact-signals");function r(){if(l>1)l--;else{for(var t,e=!1;void 0!==a;){var r=a;for(a=void 0,u++;void 0!==r;){var i=r.o;if(r.o=void 0,r.f&=-3,!(8&r.f)&&p(r))try{r.c()}catch(r){e||(t=r,e=!0)}r=i}}if(u=0,l--,e)throw t}}function i(t){if(l>0)return t();l++;try{return t()}finally{r()}}var n=void 0,o=0;function s(t){if(o>0)return t();var e=n;n=void 0,o++;try{return t()}finally{o--,n=e}}var a=void 0,l=0,u=0,f=0;function c(t){if(void 0!==n){var e=t.n;if(void 0===e||e.t!==n)return e={i:0,S:t,p:n.s,n:void 0,t:n,e:void 0,x:void 0,r:e},void 0!==n.s&&(n.s.n=e),n.s=e,t.n=e,32&n.f&&t.S(e),e;if(-1===e.i)return e.i=0,void 0!==e.n&&(e.n.p=e.p,void 0!==e.p&&(e.p.n=e.n),e.p=n.s,e.n=void 0,n.s.n=e,n.s=e),e}}function h(t){this.v=t,this.i=0,this.n=void 0,this.t=void 0}function v(t){return new h(t)}function p(t){for(var e=t.s;void 0!==e;e=e.n)if(e.S.i!==e.i||!e.S.h()||e.S.i!==e.i)return!0;return!1}function d(t){for(var e=t.s;void 0!==e;e=e.n){var r=e.S.n;if(void 0!==r&&(e.r=r),e.S.n=e,e.i=-1,void 0===e.n){t.s=e;break}}}function y(t){for(var e=t.s,r=void 0;void 0!==e;){var i=e.p;-1===e.i?(e.S.U(e),void 0!==i&&(i.n=e.n),void 0!==e.n&&(e.n.p=i)):r=e,e.S.n=e.r,void 0!==e.r&&(e.r=void 0),e=i}t.s=r}function m(t){h.call(this,void 0),this.x=t,this.s=void 0,this.g=f-1,this.f=4}function g(t){var e=t.u;if(t.u=void 0,"function"==typeof e){l++;var i=n;n=void 0;try{e()}catch(e){throw t.f&=-2,t.f|=8,b(t),e}finally{n=i,r()}}}function b(t){for(var e=t.s;void 0!==e;e=e.n)e.S.U(e);t.x=void 0,t.s=void 0,g(t)}function A(t){if(n!==this)throw new Error("Out-of-order effect");y(this),n=t,this.f&=-2,8&this.f&&b(this),r()}function w(t){this.x=t,this.u=void 0,this.s=void 0,this.o=void 0,this.f=32}function S(t){var e=new w(t);try{e.c()}catch(t){throw e.d(),t}return e.d.bind(e)}h.prototype.brand=e,h.prototype.h=function(){return!0},h.prototype.S=function(t){this.t!==t&&void 0===t.e&&(t.x=this.t,void 0!==this.t&&(this.t.e=t),this.t=t)},h.prototype.U=function(t){if(void 0!==this.t){var e=t.e,r=t.x;void 0!==e&&(e.x=r,t.e=void 0),void 0!==r&&(r.e=e,t.x=void 0),t===this.t&&(this.t=r)}},h.prototype.subscribe=function(t){var e=this;return S((function(){var r=e.value,i=32&this.f;this.f&=-33;try{t(r)}finally{this.f|=i}}))},h.prototype.valueOf=function(){return this.value},h.prototype.toString=function(){return this.value+""},h.prototype.toJSON=function(){return this.value},h.prototype.peek=function(){return this.v},Object.defineProperty(h.prototype,"value",{get:function(){var t=c(this);return void 0!==t&&(t.i=this.i),this.v},set:function(e){if(n instanceof m&&function(){throw new Error("Computed cannot have side-effects")}(),e!==this.v){u>100&&t(),this.v=e,this.i++,f++,l++;try{for(var i=this.t;void 0!==i;i=i.x)i.t.N()}finally{r()}}}}),(m.prototype=new h).h=function(){if(this.f&=-3,1&this.f)return!1;if(32==(36&this.f))return!0;if(this.f&=-5,this.g===f)return!0;if(this.g=f,this.f|=1,this.i>0&&!p(this))return this.f&=-2,!0;var t=n;try{d(this),n=this;var e=this.x();(16&this.f||this.v!==e||0===this.i)&&(this.v=e,this.f&=-17,this.i++)}catch(t){this.v=t,this.f|=16,this.i++}return n=t,y(this),this.f&=-2,!0},m.prototype.S=function(t){if(void 0===this.t){this.f|=36;for(var e=this.s;void 0!==e;e=e.n)e.S.S(e)}h.prototype.S.call(this,t)},m.prototype.U=function(t){if(void 0!==this.t&&(h.prototype.U.call(this,t),void 0===this.t)){this.f&=-33;for(var e=this.s;void 0!==e;e=e.n)e.S.U(e)}},m.prototype.N=function(){if(!(2&this.f)){this.f|=6;for(var t=this.t;void 0!==t;t=t.x)t.t.N()}},m.prototype.peek=function(){if(this.h()||t(),16&this.f)throw this.v;return this.v},Object.defineProperty(m.prototype,"value",{get:function(){1&this.f&&t();var e=c(this);if(this.h(),void 0!==e&&(e.i=this.i),16&this.f)throw this.v;return this.v}}),w.prototype.c=function(){var t=this.S();try{if(8&this.f)return;if(void 0===this.x)return;var e=this.x();"function"==typeof e&&(this.u=e)}finally{t()}},w.prototype.S=function(){1&this.f&&t(),this.f|=1,this.f&=-9,g(this),d(this),l++;var e=n;return n=this,A.bind(this,e)},w.prototype.N=function(){2&this.f||(this.f|=2,this.o=a,a=this)},w.prototype.d=function(){this.f|=8,1&this.f||b(this)};var k,x=Symbol.dispose||=Symbol("dispose"),O=Symbol("signals"),N={Array:Array,Object:Object,Number:Number,String:String,Boolean:Boolean,Date:Date,console:console,window:window,document:document,history:history,navigator:navigator,location:location,screen:screen,localStorage:localStorage,sessionStorage:sessionStorage,alert:alert,prompt:prompt,confirm:confirm,fetch:fetch,performance:performance,setTimeout:setTimeout,setInterval:setInterval,requestAnimationFrame:requestAnimationFrame};function E(t,e){if(t?.constructor!==Object&&!Array.isArray(t))return t;if(t[O]&&!e)return t;const r=t[O],n=Array.isArray(t)&&v((r||t).length),o=e?Object.create((e=E(e))[O]):Array.isArray(t)?[]:{},a=o.constructor.prototype,l=new Proxy(t,{has:()=>!0,get(t,r){if(n){if("length"===r)return a[k]?n.peek():n.value;k=r}if(a[r])return a[r];if(r===O)return o;const i=o[r]||u(r);return i?i.value:e?e[r]:N.hasOwnProperty(r)?N[r]:void 0},set(t,e,r){if(n&&"length"===e)return i((()=>{for(let t=r,e=o.length;t<e;t++)delete l[t];for(let t=o.length;t<r;t++)l[t]=null;n.value=o.length=t.length=r})),!0;const a=o[e]||u(e)||v(),f=a.peek();return r===f||(a._set?a._set(r):Array.isArray(r)&&Array.isArray(f)?s((()=>i((()=>{let i=0,n=r.length;for(;i<n;i++)f[i]=t[e][i]=r[i];f.length=n,a.value=null,a.value=f})))):(Array.isArray(f)&&(f.length=0),a.value=E(t[e]=r))),n&&e>=n.peek()&&(n.value=o.length=t.length=Number(e)+1),!0},deleteProperty:(t,e)=>(e in o&&(o[e]._del?.(),delete o[e],delete t[e]),!0)});for(let e in t)t[e],o[e]=r?.[e]??null;function u(e){if(t.hasOwnProperty(e)){const i=Object.getOwnPropertyDescriptor(t,e);return i?.get?((o[e]=(r=i.get.bind(l),new m(r)))._set=i.set?.bind(l),o[e]):o[e]=i.value?.peek?i.value:v(E(i.value))}var r}return l}var j=Promise.prototype.then.bind(Promise.resolve()),_={},P={};_.if=(t,e,r)=>{let i=document.createTextNode(""),n=[I(t,e,":if")],o=[t],s=t;for(;(s=t.nextElementSibling)&&s.hasAttribute(":else");)s.removeAttribute(":else"),(e=s.getAttribute(":if"))?(s.removeAttribute(":if"),s.remove(),o.push(s),n.push(I(t,e,":else :if"))):(s.remove(),o.push(s),n.push((()=>1)));t.replaceWith(s=i);const a=S((()=>{let t=n.findIndex((t=>t(r)));o[t]!=s&&((s[$]||s).replaceWith(s=o[t]||i),M(s,r))}));return()=>{for(const t of o)t[x]?.();a()}};var $=Symbol(":each");_.each=(t,e,r)=>{let n=function(t){let e=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,r=t.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!r)return;let i=r[2].trim(),n=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),o=n.match(e);return o?[n.replace(e,"").trim(),o[1].trim(),i]:[n,"",i]}(e);if(!n)return L(new Error,t,e,":each");const[o,a,l]=n,u=t[$]=document.createTextNode("");t.replaceWith(u);const f=I(t,l,":each");let c,h,v=0;return S((()=>{let n=f(r);return n?"number"==typeof n?n=Array.from({length:n},((t,e)=>e)):Array.isArray(n)||("object"==typeof n?(h=Object.keys(n),n=Object.values(n)):L(Error("Bad items value"),t,e,":each")):n=[],s((()=>i((()=>{if(c?.[O][0]?.peek){let t=n.length,e=0;for(;e<t;e++)c[e]=n[e];c.length=t}else{for(let t=0,e=c?.[O];t<v;t++)e[t]?._del();c=n,c[O]||={}}})))),S((()=>{let e=n.length;v!==e&&s((()=>i((()=>{const i=c[O];for(let n=v;n<e;n++){c[n];const e=t.cloneNode(!0),s=E({[o]:i[n]??c[n],[a]:h?.[n]??n},r);u.before(e),M(e,s);const{_del:l}=i[n]||={};i[n]._del=()=>{delete i[n],l?.(),e[x](),e.remove(),delete c[n]}}v=e}))))}))})),()=>i((()=>{for(let t of c[O])t?._del();c.length=0}))},_.with=(t,e,r)=>(M(t,E(I(t,e,":with")(r),r)),t[x]),_.ref=(t,e,r)=>{r[e]=t},P.render=(t,e,r)=>{let i=I(t,e,":render")(r);i||L(new Error("Template not found"),t,e,":render");let n=i.content.cloneNode(!0);return t.replaceChildren(n),M(t,r),t[x]},P.id=(t,e,r)=>{let i=I(t,e,":id");return S((()=>{return e=i(r),t.id=e||0===e?e:"";var e}))},P.class=(t,e,r)=>{let i=I(t,e,":class"),n=t.getAttribute("class");return S((()=>{let e=i(r),o=[n];e&&("string"==typeof e?o.push(e):Array.isArray(e)?o.push(...e):o.push(...Object.entries(e).map((([t,e])=>e?t:"")))),(o=o.filter(Boolean).join(" "))?t.setAttribute("class",o):t.removeAttribute("class")}))},P.style=(t,e,r)=>{let i=I(t,e,":style"),n=t.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),S((()=>{let e=i(r);"string"==typeof e?t.setAttribute("style",n+e):s((()=>{t.setAttribute("style",n);for(let r in e)"symbol"!=typeof e[r]&&t.style.setProperty(r,e[r])}))}))},P.text=(t,e,r)=>{let i=I(t,e,":text");return S((()=>{let e=i(r);t.textContent=null==e?"":e}))},P[""]=(t,e,r)=>{let i=I(t,e,":");if(i)return S((()=>{let e=i(r);for(let r in e)K(t,r.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(t=>"-"+t.toLowerCase())),e[r])}))},P.value=(t,e,r)=>{let i,n,o=I(t,e,":value"),s="text"===t.type||""===t.type?e=>t.setAttribute("value",t.value=null==e?"":e):"TEXTAREA"===t.tagName||"text"===t.type||""===t.type?e=>(i=t.selectionStart,n=t.selectionEnd,t.setAttribute("value",t.value=null==e?"":e),i&&t.setSelectionRange(i,n)):"checkbox"===t.type?e=>(t.value=e?"on":"",K(t,"checked",e)):"select-one"===t.type?e=>{for(let e in t.options)e.removeAttribute("selected");t.value=e,t.selectedOptions[0]?.setAttribute("selected","")}:e=>t.value=e;return S((()=>{s(o(r))}))};var T=(t,e,r,i)=>{let n=i.startsWith("on")&&i.slice(2),o=I(t,e,":"+i);if(o){if(n){let e,i=S((()=>{e&&(e(),e=null);let i=o(r);i&&(e=C(t,n,i))}));return()=>(e?.(),i())}return S((()=>{K(t,i,o(r))}))}},C=(t,e,r)=>{if(!r)return;const i={evt:"",target:t,test:()=>!0};i.evt=(e.startsWith("on")?e.slice(2):e).replace(/\.(\w+)?-?([-\w]+)?/g,((t,e,r="")=>(i.test=W[e]?.(i,...r.split("-"))||i.test,"")));const{evt:n,target:o,test:s,defer:a,stop:l,prevent:u,...f}=i;a&&(r=a(r));const c=t=>s(t)&&(l&&t.stopPropagation(),u&&t.preventDefault(),r.call(o,t));return o.addEventListener(n,c,f),()=>o.removeEventListener(n,c,f)},W={prevent(t){t.prevent=!0},stop(t){t.stop=!0},once(t){t.once=!0},passive(t){t.passive=!0},capture(t){t.capture=!0},window(t){t.target=window},document(t){t.target=document},throttle(t,e){t.defer=t=>U(t,e?Number(e)||0:108)},debounce(t,e){t.defer=t=>B(t,e?Number(e)||0:108)},outside:t=>e=>{let r=t.target;return!(r.contains(e.target)||!1===e.target.isConnected||r.offsetWidth<1&&r.offsetHeight<1)},self:t=>e=>e.target===t.target,ctrl:(t,...e)=>t=>D.ctrl(t)&&e.every((e=>D[e]?D[e](t):t.key===e)),shift:(t,...e)=>t=>D.shift(t)&&e.every((e=>D[e]?D[e](t):t.key===e)),alt:(t,...e)=>t=>D.alt(t)&&e.every((e=>D[e]?D[e](t):t.key===e)),meta:(t,...e)=>t=>D.meta(t)&&e.every((e=>D[e]?D[e](t):t.key===e)),arrow:t=>D.arrow,enter:t=>D.enter,escape:t=>D.escape,tab:t=>D.tab,space:t=>D.space,backspace:t=>D.backspace,delete:t=>D.delete,digit:t=>D.digit,letter:t=>D.letter,character:t=>D.character},D={ctrl:t=>t.ctrlKey||"Control"===t.key||"Ctrl"===t.key,shift:t=>t.shiftKey||"Shift"===t.key,alt:t=>t.altKey||"Alt"===t.key,meta:t=>t.metaKey||"Meta"===t.key||"Command"===t.key,arrow:t=>t.key.startsWith("Arrow"),enter:t=>"Enter"===t.key,escape:t=>t.key.startsWith("Esc"),tab:t=>"Tab"===t.key,space:t=>" "===t.key||"Space"===t.key||" "===t.key,backspace:t=>"Backspace"===t.key,delete:t=>"Delete"===t.key,digit:t=>/^\d$/.test(t.key),letter:t=>/^[a-zA-Z]$/.test(t.key),character:t=>/^\S$/.test(t.key)},U=(t,e)=>{let r,i,n=o=>{r=!0,setTimeout((()=>{if(r=!1,i)return i=!1,n(o),t(o)}),e)};return e=>r?i=!0:(n(e),t(e))},B=(t,e)=>{let r;return i=>{clearTimeout(r),r=setTimeout((()=>{r=null,t(i)}),e)}},K=(t,e,r)=>{null==r||!1===r?t.removeAttribute(e):t.setAttribute(e,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},F={};function I(t,e,r){let i=F[e];if(!i)try{i=F[e]=new Function("__scope",`with (__scope) { let __; return ${e.trim()} };`)}catch(i){return L(i,t,e,r)}return n=>{let o;try{o=i.call(t,n)}catch(i){return L(i,t,e,r)}return o}}function L(t,e,r,i){Object.assign(t,{element:e,expression:r}),console.warn(`∴ ${t.message}\n\n${i}=${r?`"${r}"\n\n`:""}`,e),j((()=>{throw t}),0)}M.globals=N;var q=new WeakMap;function M(t,e){if(!t.children)return;if(q.has(t))return i((()=>Object.assign(q.get(t),e)));const r=E(e||{}),n=[],o=(t,e=t.parentNode)=>{for(let i in _){let o=":"+i;if(t.hasAttribute?.(o)){let s=t.getAttribute(o);if(t.removeAttribute(o),n.push(_[i](t,s,r,i)),q.has(t))return;if(t.parentNode!==e)return!1}}if(t.attributes)for(let e=0;e<t.attributes.length;){let i=t.attributes[e],o=i.name[0];if(":"===o||"@"===o){t.removeAttribute(i.name);let e="@"===o?`${i.value.includes("await")?"async":""} event=>{${i.value}}`:i.value,s=i.name.slice(1).split(o);for(let i of s){"@"===o&&(i="on"+i);let s=P[i]||T;n.push(s(t,e,r,i))}}else e++}for(let e,r=0;e=t.children[r];r++)!1===o(e,t)&&r--};return o(t),q.has(t)||(q.set(t,r),n.length&&Object.defineProperty(t,x,{value:()=>{for(;n.length;)n.shift()?.();q.delete(t)}})),r}var R=M;document.currentScript&&M(document.documentElement);export{R as default};
|
package/src/core.js
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import defaultDirective, { primary, secondary
|
|
1
|
+
import createState, { batch, sandbox, _dispose } from './state.signals-proxy.js';
|
|
2
|
+
import defaultDirective, { primary, secondary } from './directives.js';
|
|
3
3
|
|
|
4
|
+
|
|
5
|
+
// default root sandbox
|
|
4
6
|
sprae.globals = sandbox
|
|
5
7
|
|
|
6
8
|
// sprae element: apply directives
|
|
7
9
|
const memo = new WeakMap
|
|
8
10
|
export default function sprae(container, values) {
|
|
9
|
-
if (!container.children) return
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
//
|
|
14
|
-
// if (observable(values[prop])) {
|
|
15
|
-
// let value = values[prop]; values[prop] = null
|
|
16
|
-
// sube(value, value => state[prop] = value)
|
|
17
|
-
// }
|
|
18
|
-
// }
|
|
11
|
+
if (!container.children) return // ignore what?
|
|
12
|
+
|
|
13
|
+
if (memo.has(container)) return batch(() => Object.assign(memo.get(container), values))
|
|
14
|
+
|
|
15
|
+
// take over existing state instead of creating clone
|
|
19
16
|
const state = createState(values || {});
|
|
20
|
-
const
|
|
17
|
+
const disposes = []
|
|
21
18
|
|
|
22
19
|
// init directives on element
|
|
23
20
|
const init = (el, parent = el.parentNode) => {
|
|
@@ -28,9 +25,9 @@ export default function sprae(container, values) {
|
|
|
28
25
|
let expr = el.getAttribute(attrName)
|
|
29
26
|
el.removeAttribute(attrName)
|
|
30
27
|
|
|
31
|
-
|
|
28
|
+
disposes.push(primary[name](el, expr, state, name))
|
|
32
29
|
|
|
33
|
-
// stop if element was spraed by directive or skipped (detached)
|
|
30
|
+
// stop if element was spraed by directive or skipped (detached) like in case of :if or :each
|
|
34
31
|
if (memo.has(el)) return
|
|
35
32
|
if (el.parentNode !== parent) return false
|
|
36
33
|
}
|
|
@@ -51,7 +48,7 @@ export default function sprae(container, values) {
|
|
|
51
48
|
// @click forwards to :onclick=event=>{...inline}
|
|
52
49
|
if (prefix === '@') name = `on` + name
|
|
53
50
|
let dir = secondary[name] || defaultDirective;
|
|
54
|
-
|
|
51
|
+
disposes.push(dir(el, expr, state, name));
|
|
55
52
|
// NOTE: secondary directives don't stop flow nor extend state, so no need to check
|
|
56
53
|
}
|
|
57
54
|
}
|
|
@@ -67,17 +64,19 @@ export default function sprae(container, values) {
|
|
|
67
64
|
|
|
68
65
|
init(container);
|
|
69
66
|
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
for (let update of updates) if (update) {
|
|
73
|
-
let teardown
|
|
74
|
-
fx(() => {
|
|
75
|
-
if (typeof teardown === 'function') teardown()
|
|
76
|
-
teardown = update(state)
|
|
77
|
-
});
|
|
78
|
-
}
|
|
67
|
+
// if element was spraed by :with or :each instruction - skip
|
|
68
|
+
if (memo.has(container)) return state //memo.get(container)
|
|
79
69
|
|
|
70
|
+
// save
|
|
80
71
|
memo.set(container, state);
|
|
81
72
|
|
|
73
|
+
// expose dispose
|
|
74
|
+
if (disposes.length) Object.defineProperty(container, _dispose, {
|
|
75
|
+
value: () => {
|
|
76
|
+
while (disposes.length) disposes.shift()?.();
|
|
77
|
+
memo.delete(container);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
82
81
|
return state;
|
|
83
82
|
}
|
package/src/directives.js
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// directives & parsing
|
|
2
2
|
import sprae from './core.js'
|
|
3
|
-
import
|
|
4
|
-
// import swap from 'swapdom'
|
|
5
|
-
import { state as createState } from './state.js'
|
|
3
|
+
import createState, { effect, computed, untracked, batch, _dispose, _signals } from './state.signals-proxy.js'
|
|
6
4
|
import { queueMicrotask, WeakishMap } from './util.js'
|
|
7
5
|
|
|
8
6
|
// reserved directives - order matters!
|
|
@@ -12,11 +10,12 @@ export const primary = {}, secondary = {}
|
|
|
12
10
|
// :if is interchangeable with :each depending on order, :if :each or :each :if have different meanings
|
|
13
11
|
// as for :if :with - :if must init first, since it is lazy, to avoid initializing component ahead of time by :with
|
|
14
12
|
// we consider :with={x} :if={x} case insignificant
|
|
15
|
-
primary['if'] = (el, expr) => {
|
|
13
|
+
primary['if'] = (el, expr, state) => {
|
|
16
14
|
let holder = document.createTextNode(''),
|
|
17
15
|
clauses = [parseExpr(el, expr, ':if')],
|
|
18
16
|
els = [el], cur = el
|
|
19
17
|
|
|
18
|
+
// consume all following siblings with :else, :if into a single group
|
|
20
19
|
while (cur = el.nextElementSibling) {
|
|
21
20
|
if (cur.hasAttribute(':else')) {
|
|
22
21
|
cur.removeAttribute(':else');
|
|
@@ -33,7 +32,8 @@ primary['if'] = (el, expr) => {
|
|
|
33
32
|
|
|
34
33
|
el.replaceWith(cur = holder)
|
|
35
34
|
|
|
36
|
-
|
|
35
|
+
const dispose = effect(() => {
|
|
36
|
+
// find matched clause (re-evaluates any time any clause updates)
|
|
37
37
|
let i = clauses.findIndex(f => f(state))
|
|
38
38
|
if (els[i] != cur) {
|
|
39
39
|
; (cur[_each] || cur).replaceWith(cur = els[i] || holder);
|
|
@@ -41,74 +41,89 @@ primary['if'] = (el, expr) => {
|
|
|
41
41
|
// but :if must come first to avoid preliminary caching
|
|
42
42
|
sprae(cur, state);
|
|
43
43
|
}
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
return () => {
|
|
47
|
+
for (const el of els) el[_dispose]?.() // dispose all internal spraes
|
|
48
|
+
dispose() // dispose subscription
|
|
44
49
|
}
|
|
45
50
|
}
|
|
46
51
|
|
|
47
52
|
const _each = Symbol(':each')
|
|
48
53
|
|
|
49
54
|
// :each must init before :ref, :id or any others, since it defines scope
|
|
50
|
-
primary['each'] = (tpl, expr) => {
|
|
55
|
+
primary['each'] = (tpl, expr, state) => {
|
|
51
56
|
let each = parseForExpression(expr);
|
|
52
57
|
if (!each) return exprError(new Error, tpl, expr, ':each');
|
|
53
58
|
|
|
54
|
-
|
|
59
|
+
const [itemVar, idxVar, itemsExpr] = each;
|
|
60
|
+
|
|
55
61
|
// we need :if to be able to replace holder instead of tpl for :if :each case
|
|
56
62
|
const holder = tpl[_each] = document.createTextNode('')
|
|
57
63
|
tpl.replaceWith(holder)
|
|
58
64
|
|
|
59
|
-
const evaluate = parseExpr(tpl,
|
|
60
|
-
|
|
61
|
-
const keyExpr = tpl.getAttribute(':key');
|
|
62
|
-
const itemKey = keyExpr ? parseExpr(null, keyExpr, ':each') : null;
|
|
63
|
-
tpl.removeAttribute(':key')
|
|
64
|
-
|
|
65
|
-
const refExpr = tpl.getAttribute(':ref');
|
|
66
|
-
|
|
67
|
-
const scopes = new WeakishMap() // stores scope per data item
|
|
68
|
-
const itemEls = new WeakishMap() // element per data item
|
|
69
|
-
let curEls = []
|
|
70
|
-
|
|
71
|
-
return (state) => {
|
|
72
|
-
// get items
|
|
73
|
-
let list = evaluate(state)
|
|
74
|
-
|
|
75
|
-
if (!list) list = []
|
|
76
|
-
else if (typeof list === 'number') list = Array.from({ length: list }, (_, i) => [i + 1, i])
|
|
77
|
-
else if (Array.isArray(list)) list = list.map((item, i) => [i + 1, item])
|
|
78
|
-
else if (typeof list === 'object') list = Object.entries(list)
|
|
79
|
-
else exprError(Error('Bad list value'), tpl, expr, ':each', list)
|
|
65
|
+
const evaluate = parseExpr(tpl, itemsExpr, ':each');
|
|
80
66
|
|
|
81
|
-
|
|
82
|
-
|
|
67
|
+
// we re-create items any time new items are produced
|
|
68
|
+
let items, prevl = 0, keys
|
|
69
|
+
effect(() => {
|
|
70
|
+
let newItems = evaluate(state)
|
|
83
71
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if (key != null) scopes.set(key, scope)
|
|
97
|
-
}
|
|
98
|
-
// need to explicitly set item to update existing children's values
|
|
99
|
-
else scope[each[0]] = item
|
|
100
|
-
|
|
101
|
-
elScopes.push(scope)
|
|
72
|
+
// convert items to array
|
|
73
|
+
if (!newItems) newItems = []
|
|
74
|
+
else if (typeof newItems === 'number') {
|
|
75
|
+
newItems = Array.from({ length: newItems }, (_, i) => i)
|
|
76
|
+
}
|
|
77
|
+
else if (Array.isArray(newItems));
|
|
78
|
+
else if (typeof newItems === 'object') {
|
|
79
|
+
keys = Object.keys(newItems);
|
|
80
|
+
newItems = Object.values(newItems);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
exprError(Error('Bad items value'), tpl, expr, ':each', newItems)
|
|
102
84
|
}
|
|
103
85
|
|
|
104
|
-
|
|
105
|
-
|
|
86
|
+
untracked(() => batch(() => {
|
|
87
|
+
// init items
|
|
88
|
+
if (!items?.[_signals][0]?.peek) {
|
|
89
|
+
// manual dispose for plain arrays (not states) - _signals here is just fake holder for destructors
|
|
90
|
+
for (let i = 0, signals = items?.[_signals]; i < prevl; i++) signals[i]?._del()
|
|
91
|
+
// NOTE: new items are initialized in length effect below
|
|
92
|
+
items = newItems, items[_signals] ||= {}
|
|
93
|
+
}
|
|
94
|
+
// patch existing items and insert new items - init happens in length effect
|
|
95
|
+
else {
|
|
96
|
+
let newl = newItems.length, i = 0
|
|
97
|
+
for (; i < newl; i++) items[i] = newItems[i]
|
|
98
|
+
items.length = newl // dispose tail (done internally in state)
|
|
99
|
+
}
|
|
100
|
+
}))
|
|
101
|
+
|
|
102
|
+
// length change effect
|
|
103
|
+
return effect(() => {
|
|
104
|
+
let newl = newItems.length // indicate that we track it
|
|
105
|
+
if (prevl !== newl) untracked(() => batch(() => {
|
|
106
|
+
// init new items
|
|
107
|
+
const signals = items[_signals]
|
|
108
|
+
for (let i = prevl; i < newl; i++) {
|
|
109
|
+
items[i]; // touch item to create signal
|
|
110
|
+
const el = tpl.cloneNode(true), scope = createState({
|
|
111
|
+
[itemVar]: signals[i] ?? items[i], [idxVar]: keys?.[i] ?? i
|
|
112
|
+
}, state)
|
|
113
|
+
holder.before(el)
|
|
114
|
+
sprae(el, scope)
|
|
115
|
+
const { _del } = (signals[i] ||= {});
|
|
116
|
+
signals[i]._del = () => { delete signals[i]; _del?.(); el[_dispose](), el.remove(), delete items[i] }
|
|
117
|
+
}
|
|
118
|
+
prevl = newl
|
|
119
|
+
}))
|
|
120
|
+
})
|
|
121
|
+
})
|
|
106
122
|
|
|
107
|
-
|
|
108
|
-
for (let
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
123
|
+
return () => batch(() => {
|
|
124
|
+
for (let _i of items[_signals]) _i?._del()
|
|
125
|
+
items.length = 0
|
|
126
|
+
})
|
|
112
127
|
}
|
|
113
128
|
|
|
114
129
|
// `:each` can redefine scope as `:each="a in {myScope}"`,
|
|
@@ -117,7 +132,8 @@ primary['with'] = (el, expr, rootState) => {
|
|
|
117
132
|
let evaluate = parseExpr(el, expr, ':with')
|
|
118
133
|
const localState = evaluate(rootState)
|
|
119
134
|
let state = createState(localState, rootState)
|
|
120
|
-
sprae(el, state)
|
|
135
|
+
sprae(el, state)
|
|
136
|
+
return el[_dispose];
|
|
121
137
|
}
|
|
122
138
|
|
|
123
139
|
// ref must be last within primaries, since that must be skipped by :each, but before secondaries
|
|
@@ -129,6 +145,7 @@ primary['ref'] = (el, expr, state) => {
|
|
|
129
145
|
|
|
130
146
|
|
|
131
147
|
// This was taken AlpineJS, former VueJS 2.* core. Thanks Alpine & Vue!
|
|
148
|
+
// takes instruction like `item in list`, returns ['item', '', 'list']
|
|
132
149
|
function parseForExpression(expression) {
|
|
133
150
|
let forIteratorRE = /,([^,\}\]]*)(?:,([^,\}\]]*))?$/
|
|
134
151
|
let stripParensRE = /^\s*\(|\)\s*$/g
|
|
@@ -159,18 +176,19 @@ secondary['render'] = (el, expr, state) => {
|
|
|
159
176
|
let content = tpl.content.cloneNode(true);
|
|
160
177
|
el.replaceChildren(content)
|
|
161
178
|
sprae(el, state)
|
|
179
|
+
return el[_dispose]
|
|
162
180
|
}
|
|
163
181
|
|
|
164
|
-
secondary['id'] = (el, expr) => {
|
|
182
|
+
secondary['id'] = (el, expr, state) => {
|
|
165
183
|
let evaluate = parseExpr(el, expr, ':id')
|
|
166
184
|
const update = v => el.id = v || v === 0 ? v : ''
|
|
167
|
-
return (
|
|
185
|
+
return effect(() => update(evaluate(state)))
|
|
168
186
|
}
|
|
169
187
|
|
|
170
|
-
secondary['class'] = (el, expr) => {
|
|
188
|
+
secondary['class'] = (el, expr, state) => {
|
|
171
189
|
let evaluate = parseExpr(el, expr, ':class')
|
|
172
190
|
let initClassName = el.getAttribute('class')
|
|
173
|
-
return (
|
|
191
|
+
return effect(() => {
|
|
174
192
|
let v = evaluate(state)
|
|
175
193
|
let className = [initClassName]
|
|
176
194
|
if (v) {
|
|
@@ -180,42 +198,44 @@ secondary['class'] = (el, expr) => {
|
|
|
180
198
|
}
|
|
181
199
|
if (className = className.filter(Boolean).join(' ')) el.setAttribute('class', className);
|
|
182
200
|
else el.removeAttribute('class')
|
|
183
|
-
}
|
|
201
|
+
})
|
|
184
202
|
}
|
|
185
203
|
|
|
186
|
-
secondary['style'] = (el, expr) => {
|
|
204
|
+
secondary['style'] = (el, expr, state) => {
|
|
187
205
|
let evaluate = parseExpr(el, expr, ':style')
|
|
188
206
|
let initStyle = el.getAttribute('style') || ''
|
|
189
207
|
if (!initStyle.endsWith(';')) initStyle += '; '
|
|
190
|
-
return (
|
|
208
|
+
return effect(() => {
|
|
191
209
|
let v = evaluate(state)
|
|
192
210
|
if (typeof v === 'string') el.setAttribute('style', initStyle + v)
|
|
193
211
|
else {
|
|
194
|
-
|
|
195
|
-
|
|
212
|
+
untracked(() => {
|
|
213
|
+
el.setAttribute('style', initStyle)
|
|
214
|
+
for (let k in v) if (typeof v[k] !== 'symbol') el.style.setProperty(k, v[k])
|
|
215
|
+
})
|
|
196
216
|
}
|
|
197
|
-
}
|
|
217
|
+
})
|
|
198
218
|
}
|
|
199
219
|
|
|
200
|
-
secondary['text'] = (el, expr) => {
|
|
220
|
+
secondary['text'] = (el, expr, state) => {
|
|
201
221
|
let evaluate = parseExpr(el, expr, ':text')
|
|
202
|
-
return (
|
|
222
|
+
return effect(() => {
|
|
203
223
|
let value = evaluate(state)
|
|
204
224
|
el.textContent = value == null ? '' : value;
|
|
205
|
-
}
|
|
225
|
+
})
|
|
206
226
|
}
|
|
207
227
|
|
|
208
228
|
// set props in-bulk or run effect
|
|
209
|
-
secondary[''] = (el, expr) => {
|
|
229
|
+
secondary[''] = (el, expr, state) => {
|
|
210
230
|
let evaluate = parseExpr(el, expr, ':')
|
|
211
|
-
if (evaluate) return (
|
|
231
|
+
if (evaluate) return effect(() => {
|
|
212
232
|
let value = evaluate(state)
|
|
213
233
|
for (let key in value) attr(el, dashcase(key), value[key]);
|
|
214
|
-
}
|
|
234
|
+
})
|
|
215
235
|
}
|
|
216
236
|
|
|
217
237
|
// connect expr to element value
|
|
218
|
-
secondary['value'] = (el, expr) => {
|
|
238
|
+
secondary['value'] = (el, expr, state) => {
|
|
219
239
|
let evaluate = parseExpr(el, expr, ':value')
|
|
220
240
|
|
|
221
241
|
let from, to
|
|
@@ -236,7 +256,7 @@ secondary['value'] = (el, expr) => {
|
|
|
236
256
|
value => el.value = value
|
|
237
257
|
)
|
|
238
258
|
|
|
239
|
-
return (
|
|
259
|
+
return effect(() => { update(evaluate(state)) })
|
|
240
260
|
}
|
|
241
261
|
|
|
242
262
|
// any unknown directive
|
|
@@ -246,17 +266,21 @@ export default (el, expr, state, name) => {
|
|
|
246
266
|
|
|
247
267
|
if (!evaluate) return
|
|
248
268
|
|
|
249
|
-
if (evt)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
269
|
+
if (evt) {
|
|
270
|
+
let off, dispose = effect(() => {
|
|
271
|
+
if (off) off(), off = null
|
|
272
|
+
// we need anonymous callback to enable modifiers like prevent
|
|
273
|
+
let value = evaluate(state)
|
|
274
|
+
if (value) off = on(el, evt, value)
|
|
275
|
+
})
|
|
276
|
+
return () => (off?.(), dispose())
|
|
277
|
+
}
|
|
254
278
|
|
|
255
|
-
return
|
|
279
|
+
return effect(() => { attr(el, name, evaluate(state)) })
|
|
256
280
|
}
|
|
257
281
|
|
|
258
282
|
// bind event to a target
|
|
259
|
-
|
|
283
|
+
const on = (el, e, fn) => {
|
|
260
284
|
if (!fn) return
|
|
261
285
|
|
|
262
286
|
const ctx = { evt: '', target: el, test: () => true };
|
|
@@ -278,7 +302,9 @@ export const on = (el, e, fn) => {
|
|
|
278
302
|
)
|
|
279
303
|
|
|
280
304
|
target.addEventListener(evt, cb, opts)
|
|
281
|
-
|
|
305
|
+
|
|
306
|
+
// return off
|
|
307
|
+
return () => (target.removeEventListener(evt, cb, opts))
|
|
282
308
|
}
|
|
283
309
|
|
|
284
310
|
// event modifiers
|
|
@@ -384,7 +410,7 @@ function parseExpr(el, expression, dir) {
|
|
|
384
410
|
|
|
385
411
|
if (!evaluate) {
|
|
386
412
|
try {
|
|
387
|
-
evaluate = evaluatorMemo[expression] = new Function(`__scope`, `with (__scope) { return ${expression.trim()} };`)
|
|
413
|
+
evaluate = evaluatorMemo[expression] = new Function(`__scope`, `with (__scope) { let __; return ${expression.trim()} };`)
|
|
388
414
|
} catch (e) {
|
|
389
415
|
return exprError(e, el, expression, dir)
|
|
390
416
|
}
|
package/src/domdiff.js
CHANGED
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
// https://github.com/luwes/js-diff-benchmark/blob/master/libs/list-difference.js
|
|
2
2
|
// this implementation is more persistent in terms of preserving in-between nodes
|
|
3
|
+
// also it handles _dispose
|
|
3
4
|
|
|
4
5
|
// a is old list, b is the new
|
|
5
|
-
export default function(parent, a, b, before) {
|
|
6
|
+
export default function (parent, a, b, before) {
|
|
6
7
|
const aIdx = new Map();
|
|
7
8
|
const bIdx = new Map();
|
|
8
9
|
let i;
|
|
9
10
|
let j;
|
|
10
11
|
|
|
11
|
-
// Create a mapping from keys to their position in the old list
|
|
12
|
-
for (i = 0; i < a.length; i++) {
|
|
13
|
-
aIdx.set(a[i], i);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
12
|
// Create a mapping from keys to their position in the new list
|
|
17
13
|
for (i = 0; i < b.length; i++) {
|
|
18
14
|
bIdx.set(b[i], i);
|
|
19
15
|
}
|
|
20
16
|
|
|
17
|
+
// Create a mapping from keys to their position in the old list
|
|
18
|
+
for (i = 0; i < a.length; i++) {
|
|
19
|
+
aIdx.set(a[i], i);
|
|
20
|
+
// dispose a[i] if is going to disappear
|
|
21
|
+
if (!bIdx.has(a[i])) a[i][Symbol.dispose]?.()
|
|
22
|
+
}
|
|
23
|
+
|
|
21
24
|
for (i = j = 0; i !== a.length || j !== b.length;) {
|
|
22
25
|
var aElm = a[i], bElm = b[j];
|
|
23
26
|
if (aElm === null) {
|
|
@@ -65,4 +68,4 @@ export default function(parent, a, b, before) {
|
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
70
|
return b;
|
|
68
|
-
};
|
|
71
|
+
};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
// proxy-based store implementation: more flexible, less optimal and with memory leak now
|
|
2
|
+
// FIXME: make simpler prototype chain, likely manual
|
|
3
|
+
// FIXME: detect circular updates
|
|
1
4
|
import { queueMicrotask } from "./util.js"
|
|
2
5
|
|
|
3
6
|
// currentFx stack of listeners
|
|
4
|
-
let currentFx, batch =
|
|
7
|
+
let currentFx, batch = [], pendingUpdate
|
|
5
8
|
|
|
6
9
|
const targetFxs = new WeakMap
|
|
7
10
|
const targetProxy = new WeakMap
|
|
@@ -14,19 +17,30 @@ export const sandbox = {
|
|
|
14
17
|
console, window, document, history, location
|
|
15
18
|
}
|
|
16
19
|
|
|
20
|
+
const callArg = (fn) => fn(), nop = () => { }
|
|
21
|
+
export { callArg as batch };
|
|
22
|
+
|
|
23
|
+
let lastProp
|
|
24
|
+
|
|
17
25
|
const handler = {
|
|
18
|
-
has(){
|
|
26
|
+
has() {
|
|
19
27
|
// sandbox everything
|
|
20
28
|
return true
|
|
21
29
|
},
|
|
22
30
|
|
|
23
31
|
get(target, prop) {
|
|
24
|
-
if (typeof prop === 'symbol') return target[prop]
|
|
32
|
+
if (typeof prop === 'symbol') return lastProp = null, target[prop]
|
|
25
33
|
if (!(prop in target)) return target[_parent]?.[prop]
|
|
34
|
+
|
|
35
|
+
// ignore .length effects called within .push
|
|
36
|
+
if (lastProp && prop === 'length') return lastProp = null, target[prop];
|
|
37
|
+
|
|
26
38
|
// .constructor, .slice etc
|
|
27
|
-
if (
|
|
39
|
+
if (Object.prototype[prop]) return target[prop]
|
|
40
|
+
if (Array.isArray(target) && Array.prototype[prop] && prop !== 'length') return lastProp = prop, target[prop];
|
|
28
41
|
|
|
29
42
|
let value = target[prop]
|
|
43
|
+
|
|
30
44
|
if (currentFx) {
|
|
31
45
|
// get actual target from prototype chain
|
|
32
46
|
// while (!target.hasOwnProperty(prop)) target = Object.getPrototypeOf(target)
|
|
@@ -61,7 +75,11 @@ const handler = {
|
|
|
61
75
|
// whenever target prop is set, call all dependent fxs
|
|
62
76
|
let propFxs = targetFxs.get(target)?.[prop]
|
|
63
77
|
|
|
64
|
-
if (propFxs) for (let fx of propFxs)
|
|
78
|
+
if (propFxs) for (let fx of propFxs) {
|
|
79
|
+
// put fx latest
|
|
80
|
+
if (fx.planned != null) batch[fx.planned] = null
|
|
81
|
+
fx.planned = batch.push(fx) - 1
|
|
82
|
+
}
|
|
65
83
|
planUpdate()
|
|
66
84
|
|
|
67
85
|
// FIXME: unsubscribe / delete effects by setting null/undefined
|
|
@@ -77,7 +95,7 @@ const handler = {
|
|
|
77
95
|
}
|
|
78
96
|
}
|
|
79
97
|
|
|
80
|
-
export
|
|
98
|
+
export default function state(obj, parent) {
|
|
81
99
|
if (targetProxy.has(obj)) return targetProxy.get(obj)
|
|
82
100
|
if (proxyTarget.has(obj)) return obj // is proxy already
|
|
83
101
|
|
|
@@ -85,7 +103,7 @@ export const state = (obj, parent) => {
|
|
|
85
103
|
targetProxy.set(obj, proxy)
|
|
86
104
|
proxyTarget.set(proxy, obj)
|
|
87
105
|
|
|
88
|
-
// bind all getters here to proxy
|
|
106
|
+
// bind all getters & functions here to proxy
|
|
89
107
|
// FIXME: alternatively we can store getters somewhere
|
|
90
108
|
let descriptors = Object.getOwnPropertyDescriptors(obj)
|
|
91
109
|
for (let name in descriptors) {
|
|
@@ -93,6 +111,9 @@ export const state = (obj, parent) => {
|
|
|
93
111
|
if (desc.get) {
|
|
94
112
|
if (desc.get) desc.get = desc.get.bind(proxy), Object.defineProperty(obj, name, desc)
|
|
95
113
|
}
|
|
114
|
+
// else if (typeof desc.value === 'function') {
|
|
115
|
+
// desc.value = desc.value.bind(proxy), Object.defineProperty(obj, name, desc)
|
|
116
|
+
// }
|
|
96
117
|
}
|
|
97
118
|
|
|
98
119
|
// inherit from parent state
|
|
@@ -101,7 +122,7 @@ export const state = (obj, parent) => {
|
|
|
101
122
|
return proxy
|
|
102
123
|
}
|
|
103
124
|
|
|
104
|
-
|
|
125
|
+
const fx = (fn) => {
|
|
105
126
|
const call = () => {
|
|
106
127
|
let prev = currentFx
|
|
107
128
|
currentFx = call
|
|
@@ -115,13 +136,15 @@ export const fx = (fn) => {
|
|
|
115
136
|
return call
|
|
116
137
|
}
|
|
117
138
|
|
|
118
|
-
|
|
119
|
-
if (!pendingUpdate) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
139
|
+
const planUpdate = () => {
|
|
140
|
+
// if (!pendingUpdate) {
|
|
141
|
+
// pendingUpdate = true
|
|
142
|
+
// queueMicrotask(() => {
|
|
143
|
+
let fx
|
|
144
|
+
while (batch.length) fx = batch.shift(), fx && (fx(), fx.planned = null)
|
|
145
|
+
// pendingUpdate = false
|
|
146
|
+
// })
|
|
147
|
+
// }
|
|
127
148
|
}
|
|
149
|
+
|
|
150
|
+
export { fx as effect };
|