sprae 5.2.0 → 6.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/examples/todomvc.html +14 -15
- package/package.json +2 -2
- package/r&d.md +58 -17
- package/readme.md +77 -87
- package/sprae.auto.js +31 -70
- package/sprae.auto.min.js +1 -1
- package/sprae.js +31 -70
- package/sprae.min.js +1 -1
- package/src/core.js +16 -11
- package/src/directives.js +32 -65
- package/test/{dom.js → attrs.js} +9 -325
- package/test/events.js +138 -0
- package/test/test.js +12 -1
- package/todo.md +2 -1
package/sprae.js
CHANGED
|
@@ -222,12 +222,6 @@ primary["if"] = (el, expr) => {
|
|
|
222
222
|
}
|
|
223
223
|
};
|
|
224
224
|
};
|
|
225
|
-
primary["with"] = (el, expr, rootState) => {
|
|
226
|
-
let evaluate = parseExpr(el, expr, ":with");
|
|
227
|
-
const localState = evaluate(rootState);
|
|
228
|
-
let state2 = state(localState, rootState);
|
|
229
|
-
sprae(el, state2);
|
|
230
|
-
};
|
|
231
225
|
var _each = Symbol(":each");
|
|
232
226
|
primary["each"] = (tpl, expr) => {
|
|
233
227
|
let each = parseForExpression(expr);
|
|
@@ -248,7 +242,7 @@ primary["each"] = (tpl, expr) => {
|
|
|
248
242
|
if (!list)
|
|
249
243
|
list = [];
|
|
250
244
|
else if (typeof list === "number")
|
|
251
|
-
list = Array.from({ length: list }, (_, i) => [i
|
|
245
|
+
list = Array.from({ length: list }, (_, i) => [i + 1, i]);
|
|
252
246
|
else if (Array.isArray(list))
|
|
253
247
|
list = list.map((item, i) => [i + 1, item]);
|
|
254
248
|
else if (typeof list === "object")
|
|
@@ -278,6 +272,12 @@ primary["each"] = (tpl, expr) => {
|
|
|
278
272
|
}
|
|
279
273
|
};
|
|
280
274
|
};
|
|
275
|
+
primary["with"] = (el, expr, rootState) => {
|
|
276
|
+
let evaluate = parseExpr(el, expr, ":with");
|
|
277
|
+
const localState = evaluate(rootState);
|
|
278
|
+
let state2 = state(localState, rootState);
|
|
279
|
+
sprae(el, state2);
|
|
280
|
+
};
|
|
281
281
|
primary["ref"] = (el, expr, state2) => {
|
|
282
282
|
state2[expr] = el;
|
|
283
283
|
};
|
|
@@ -375,19 +375,6 @@ secondary["value"] = (el, expr) => {
|
|
|
375
375
|
} : (value) => el.value = value;
|
|
376
376
|
return (state2) => update(evaluate(state2));
|
|
377
377
|
};
|
|
378
|
-
secondary["on"] = (el, expr) => {
|
|
379
|
-
let evaluate = parseExpr(el, expr, ":on");
|
|
380
|
-
return (state2) => {
|
|
381
|
-
let listeners = evaluate(state2);
|
|
382
|
-
let offs = [];
|
|
383
|
-
for (let evt in listeners)
|
|
384
|
-
offs.push(on(el, evt, listeners[evt]));
|
|
385
|
-
return () => {
|
|
386
|
-
for (let off of offs)
|
|
387
|
-
off();
|
|
388
|
-
};
|
|
389
|
-
};
|
|
390
|
-
};
|
|
391
378
|
var directives_default = (el, expr, state2, name) => {
|
|
392
379
|
let evt = name.startsWith("on") && name.slice(2);
|
|
393
380
|
let evaluate = parseExpr(el, expr, ":" + name);
|
|
@@ -401,43 +388,20 @@ var directives_default = (el, expr, state2, name) => {
|
|
|
401
388
|
};
|
|
402
389
|
return (state3) => attr(el, name, evaluate(state3));
|
|
403
390
|
};
|
|
404
|
-
var on = (
|
|
405
|
-
if (!
|
|
391
|
+
var on = (el, e, fn) => {
|
|
392
|
+
if (!fn)
|
|
406
393
|
return;
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
let off;
|
|
419
|
-
let curListener = (e) => {
|
|
420
|
-
if (cur)
|
|
421
|
-
off();
|
|
422
|
-
let nextFn = fn.call(target, e);
|
|
423
|
-
if (typeof nextFn !== "function")
|
|
424
|
-
nextFn = () => {
|
|
425
|
-
};
|
|
426
|
-
if (cur + 1 < ctxs.length)
|
|
427
|
-
onFn(nextFn, !cur ? 1 : cur + 1);
|
|
428
|
-
};
|
|
429
|
-
return off = addListenerWithMods(curListener, ctxs[cur]);
|
|
430
|
-
};
|
|
431
|
-
let rootOff = onFn(origFn);
|
|
432
|
-
return () => rootOff();
|
|
433
|
-
function addListenerWithMods(fn, { evt: evt2, target: target2, test, defer, stop, prevent, ...opts }) {
|
|
434
|
-
if (defer)
|
|
435
|
-
fn = defer(fn);
|
|
436
|
-
let cb = (e) => test(e) && (stop && e.stopPropagation(), prevent && e.preventDefault(), fn.call(target2, e));
|
|
437
|
-
target2.addEventListener(evt2, cb, opts);
|
|
438
|
-
return () => target2.removeEventListener(evt2, cb, opts);
|
|
439
|
-
}
|
|
440
|
-
;
|
|
394
|
+
const ctx = { evt: "", target: el, test: () => true };
|
|
395
|
+
ctx.evt = (e.startsWith("on") ? e.slice(2) : e).replace(
|
|
396
|
+
/\.(\w+)?-?([-\w]+)?/g,
|
|
397
|
+
(match, mod, param = "") => (ctx.test = mods[mod]?.(ctx, ...param.split("-")) || ctx.test, "")
|
|
398
|
+
);
|
|
399
|
+
const { evt, target, test, defer, stop, prevent, ...opts } = ctx;
|
|
400
|
+
if (defer)
|
|
401
|
+
fn = defer(fn);
|
|
402
|
+
const cb = (e2) => test(e2) && (stop && e2.stopPropagation(), prevent && e2.preventDefault(), fn.call(target, e2));
|
|
403
|
+
target.addEventListener(evt, cb, opts);
|
|
404
|
+
return () => target.removeEventListener(evt, cb, opts);
|
|
441
405
|
};
|
|
442
406
|
var mods = {
|
|
443
407
|
prevent(ctx) {
|
|
@@ -461,9 +425,6 @@ var mods = {
|
|
|
461
425
|
document(ctx) {
|
|
462
426
|
ctx.target = document;
|
|
463
427
|
},
|
|
464
|
-
toggle(ctx) {
|
|
465
|
-
ctx.defer = (fn, out) => (e) => out ? (out.call?.(ctx.target, e), out = null) : out = fn();
|
|
466
|
-
},
|
|
467
428
|
throttle(ctx, limit) {
|
|
468
429
|
ctx.defer = (fn) => throttle(fn, limit ? Number(limit) || 0 : 108);
|
|
469
430
|
},
|
|
@@ -605,18 +566,18 @@ function sprae(container, values) {
|
|
|
605
566
|
}
|
|
606
567
|
if (el.attributes) {
|
|
607
568
|
for (let i = 0; i < el.attributes.length; ) {
|
|
608
|
-
let attr2 = el.attributes[i];
|
|
609
|
-
if (
|
|
569
|
+
let attr2 = el.attributes[i], prefix = attr2.name[0];
|
|
570
|
+
if (prefix === ":" || prefix === "@") {
|
|
571
|
+
el.removeAttribute(attr2.name);
|
|
572
|
+
let expr = prefix === "@" ? `event=>{${attr2.value}}` : attr2.value, names = attr2.name.slice(1).split(prefix);
|
|
573
|
+
for (let name of names) {
|
|
574
|
+
if (prefix === "@")
|
|
575
|
+
name = `on` + name;
|
|
576
|
+
let dir = secondary[name] || directives_default;
|
|
577
|
+
updates.push(dir(el, expr, state2, name));
|
|
578
|
+
}
|
|
579
|
+
} else
|
|
610
580
|
i++;
|
|
611
|
-
continue;
|
|
612
|
-
}
|
|
613
|
-
el.removeAttribute(attr2.name);
|
|
614
|
-
let expr = attr2.value;
|
|
615
|
-
let attrNames = attr2.name.slice(1).split(":");
|
|
616
|
-
for (let attrName of attrNames) {
|
|
617
|
-
let dir = secondary[attrName] || directives_default;
|
|
618
|
-
updates.push(dir(el, expr, state2, attrName));
|
|
619
|
-
}
|
|
620
581
|
}
|
|
621
582
|
}
|
|
622
583
|
for (let i = 0, child; child = el.children[i]; i++) {
|
package/sprae.min.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,
|
|
1
|
+
var e,t,r=Promise.prototype.then.bind(Promise.resolve()),n=new WeakMap,s=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)}},l=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)l.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 s=n[t];s.get&&s.get&&(s.get=s.get.bind(r),Object.defineProperty(e,t,s))}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 l)e.call();l.clear(),t=!1})))},d={},g={};d.if=(e,t)=>{let r=document.createTextNode(""),n=[j(e,t,":if")],s=[e],l=e;for(;(l=e.nextElementSibling)&&l.hasAttribute(":else");)l.removeAttribute(":else"),(t=l.getAttribute(":if"))?(l.removeAttribute(":if"),l.remove(),s.push(l),n.push(j(e,t,":else :if"))):(l.remove(),s.push(l),n.push((()=>1)));return e.replaceWith(l=r),e=>{let t=n.findIndex((t=>t(e)));s[t]!=l&&((l[b]||l).replaceWith(l=s[t]||r),O(l,e))}};var b=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(),s=r[1].replace(/^\s*\(|\)\s*$/g,"").trim(),l=s.match(t);return l?[s.replace(t,"").trim(),l[1].trim(),n]:[s,"",n]}(t);if(!r)return E(new Error,e,t,":each");const n=e[b]=document.createTextNode("");e.replaceWith(n);const l=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 s,c=new s;let f=[];return s=>{let i=l(s);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 l,i,f=o?.({[r[0]]:n,[r[1]]:t});null==f?l=e.cloneNode(!0):(l=c.get(f))||c.set(f,l=e.cloneNode(!0)),y.push(l),null!=f&&(i=u.get(f))?i[r[0]]=n:(i=p({[r[0]]:n,[a||""]:null,[r[1]]:t},s),null!=f&&u.set(f,i)),h.push(i)}!function(e,t,r,n){const s=new Map,l=new Map;let i,o;for(i=0;i<t.length;i++)s.set(t[i],i);for(i=0;i<r.length;i++)l.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=l.get(a),f=s.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 s=n.content.cloneNode(!0);e.replaceChildren(s),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 s=r(t),l=[n];s&&("string"==typeof s?l.push(s):Array.isArray(s)?l.push(...s):l.push(...Object.entries(s).map((([e,t])=>t?e:"")))),(l=l.filter(Boolean).join(" "))?e.setAttribute("class",l):e.removeAttribute("class")}},g.style=(e,t)=>{let r=j(e,t,":style"),n=e.getAttribute("style")||"";return n.endsWith(";")||(n+="; "),t=>{let s=r(t);if("string"==typeof s)e.setAttribute("style",n+s);else{e.setAttribute("style",n);for(let t in s)e.style.setProperty(t,s[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)x(e,t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(e=>"-"+e.toLowerCase())),n[t])}},g.value=(e,t)=>{let r,n,s=j(e,t,":value"),l="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":"",x(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=>l(s(e))};var m=(e,t,r,n)=>{let s=n.startsWith("on")&&n.slice(2),l=j(e,t,":"+n);if(l)return s?t=>{let r=l(t)||(()=>{});return v(e,s,r)}:t=>x(e,n,l(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:s,target:l,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(l,e));return l.addEventListener(s,f,c),()=>l.removeEventListener(s,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=>W(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,s=l=>{r=!0,setTimeout((()=>{if(r=!1,n)return n=!1,s(l),e(l)}),t)};return t=>r?n=!0:(s(t),e(t))},W=(e,t)=>{let r;return n=>{clearTimeout(r),r=setTimeout((()=>{r=null,e(n)}),t)}},x=(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){let s=/^[\n\s]*if.*\(.*\)/.test(t)||/\b(let|const)\s/.test(t)&&!r.startsWith(":on")?`(() => {${t}})()`:t;try{n=S[t]=new Function("__scope",`with (__scope) { return ${s.trim()} };`)}catch(n){return E(n,e,t,r)}}return s=>{let l;try{l=n.call(e,s)}catch(n){return E(n,e,t,r)}return l}}function E(e,t,n,s){Object.assign(e,{element:t,expression:n}),console.warn(`∴ ${e.message}\n\n${s}=${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=[],s=(e,t=e.parentNode)=>{for(let s in d){let l=":"+s;if(e.hasAttribute?.(l)){let i=e.getAttribute(l);if(e.removeAttribute(l),n.push(d[s](e,i,r,s)),N.has(e))return;if(e.parentNode!==t)return!1}}if(e.attributes)for(let t=0;t<e.attributes.length;){let s=e.attributes[t],l=s.name[0];if(":"===l||"@"===l){e.removeAttribute(s.name);let t="@"===l?`event=>{${s.value}}`:s.value,i=s.name.slice(1).split(l);for(let s of i){"@"===l&&(s="on"+s);let i=g[s]||m;n.push(i(e,t,r,s))}}else t++}for(let t,r=0;t=e.children[r];r++)!1===s(t,e)&&r--};s(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};
|
package/src/core.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { state as createState, fx, sandbox } from './state.js';
|
|
2
|
-
import defaultDirective, { primary, secondary } from './directives.js';
|
|
2
|
+
import defaultDirective, { primary, secondary, on } from './directives.js';
|
|
3
3
|
|
|
4
4
|
sprae.globals=sandbox
|
|
5
5
|
|
|
@@ -39,18 +39,23 @@ export default function sprae(container, values) {
|
|
|
39
39
|
// catch other attributes as secondary
|
|
40
40
|
if (el.attributes) {
|
|
41
41
|
for (let i = 0; i < el.attributes.length;) {
|
|
42
|
-
let attr = el.attributes[i]
|
|
43
|
-
if (attr.name[0] !== ':') {i++; continue}
|
|
44
|
-
el.removeAttribute(attr.name)
|
|
45
|
-
let expr = attr.value
|
|
42
|
+
let attr = el.attributes[i], prefix = attr.name[0]
|
|
46
43
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
//
|
|
44
|
+
if (prefix === ':' || prefix === '@') {
|
|
45
|
+
el.removeAttribute(attr.name)
|
|
46
|
+
let expr = prefix === '@' ? `event=>{${attr.value}}` : attr.value,
|
|
47
|
+
names = attr.name.slice(1).split(prefix)
|
|
48
|
+
|
|
49
|
+
// multiple attributes like :id:for="" or @click@touchstart
|
|
50
|
+
for (let name of names) {
|
|
51
|
+
// @click forwards to :onclick=event=>{...inline}
|
|
52
|
+
if (prefix === '@') name = `on` + name
|
|
53
|
+
let dir = secondary[name] || defaultDirective;
|
|
54
|
+
updates.push(dir(el, expr, state, name));
|
|
55
|
+
// NOTE: secondary directives don't stop flow nor extend state, so no need to check
|
|
56
|
+
}
|
|
53
57
|
}
|
|
58
|
+
else i++;
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
61
|
|
package/src/directives.js
CHANGED
|
@@ -45,14 +45,6 @@ primary['if'] = (el, expr) => {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
// :with must come before :each, but :if has primary importance
|
|
49
|
-
primary['with'] = (el, expr, rootState) => {
|
|
50
|
-
let evaluate = parseExpr(el, expr, ':with')
|
|
51
|
-
const localState = evaluate(rootState)
|
|
52
|
-
let state = createState(localState, rootState)
|
|
53
|
-
sprae(el, state);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
48
|
const _each = Symbol(':each')
|
|
57
49
|
|
|
58
50
|
// :each must init before :ref, :id or any others, since it defines scope
|
|
@@ -82,7 +74,7 @@ primary['each'] = (tpl, expr) => {
|
|
|
82
74
|
let list = evaluate(state)
|
|
83
75
|
|
|
84
76
|
if (!list) list = []
|
|
85
|
-
else if (typeof list === 'number') list = Array.from({length: list}, (_, i)=>[i, i
|
|
77
|
+
else if (typeof list === 'number') list = Array.from({length: list}, (_, i)=>[i+1, i])
|
|
86
78
|
else if (Array.isArray(list)) list = list.map((item,i) => [i+1, item])
|
|
87
79
|
else if (typeof list === 'object') list = Object.entries(list)
|
|
88
80
|
else exprError(Error('Bad list value'), tpl, expr, ':each', list)
|
|
@@ -120,6 +112,15 @@ primary['each'] = (tpl, expr) => {
|
|
|
120
112
|
}
|
|
121
113
|
}
|
|
122
114
|
|
|
115
|
+
// `:each` can redefine scope as `:each="a in {myScope}"`,
|
|
116
|
+
// same time per-item scope as `:each="..." :with="{collapsed:true}"` is useful
|
|
117
|
+
primary['with'] = (el, expr, rootState) => {
|
|
118
|
+
let evaluate = parseExpr(el, expr, ':with')
|
|
119
|
+
const localState = evaluate(rootState)
|
|
120
|
+
let state = createState(localState, rootState)
|
|
121
|
+
sprae(el, state);
|
|
122
|
+
}
|
|
123
|
+
|
|
123
124
|
// ref must be last within primaries, since that must be skipped by :each, but before secondaries
|
|
124
125
|
primary['ref'] = (el, expr, state) => {
|
|
125
126
|
// FIXME: wait for complex ref use-case
|
|
@@ -239,18 +240,6 @@ secondary['value'] = (el, expr) => {
|
|
|
239
240
|
return (state) => update(evaluate(state))
|
|
240
241
|
}
|
|
241
242
|
|
|
242
|
-
secondary['on'] = (el, expr) => {
|
|
243
|
-
let evaluate = parseExpr(el, expr, ':on')
|
|
244
|
-
|
|
245
|
-
return (state) => {
|
|
246
|
-
let listeners = evaluate(state);
|
|
247
|
-
let offs = []; for (let evt in listeners) offs.push(on(el, evt, listeners[evt]));
|
|
248
|
-
return () => {
|
|
249
|
-
for (let off of offs) off()
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
243
|
// any unknown directive
|
|
255
244
|
export default (el, expr, state, name) => {
|
|
256
245
|
let evt = name.startsWith('on') && name.slice(2)
|
|
@@ -267,50 +256,30 @@ export default (el, expr, state, name) => {
|
|
|
267
256
|
return state => attr(el, name, evaluate(state))
|
|
268
257
|
}
|
|
269
258
|
|
|
270
|
-
// bind event to target
|
|
271
|
-
const on = (
|
|
272
|
-
if (!
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
ctx.
|
|
279
|
-
|
|
280
|
-
);
|
|
281
|
-
return ctx;
|
|
282
|
-
});
|
|
283
|
-
|
|
284
|
-
// single event bind
|
|
285
|
-
if (ctxs.length == 1) return addListenerWithMods(origFn, ctxs[0])
|
|
286
|
-
|
|
287
|
-
// events chain cycler
|
|
288
|
-
const onFn = (fn, cur=0) => {
|
|
289
|
-
let off
|
|
290
|
-
let curListener = e => {
|
|
291
|
-
if (cur) off(); // don't remove entry listener - we must keep chain entry always open
|
|
292
|
-
let nextFn = fn.call(target, e)
|
|
293
|
-
if (typeof nextFn !== 'function') nextFn = ()=>{}
|
|
294
|
-
if (cur+1 < ctxs.length) onFn(nextFn, !cur ? 1 : cur+1);
|
|
295
|
-
}
|
|
296
|
-
return off = addListenerWithMods(curListener, ctxs[cur])
|
|
297
|
-
}
|
|
298
|
-
let rootOff = onFn(origFn)
|
|
299
|
-
return () => rootOff()
|
|
259
|
+
// bind event to a target
|
|
260
|
+
export const on = (el, e, fn) => {
|
|
261
|
+
if (!fn) return
|
|
262
|
+
|
|
263
|
+
const ctx = { evt:'', target:el, test:()=>true };
|
|
264
|
+
|
|
265
|
+
// onevt.debounce-108 -> evt.debounce-108
|
|
266
|
+
ctx.evt = (e.startsWith('on') ? e.slice(2) : e).replace(/\.(\w+)?-?([-\w]+)?/g,
|
|
267
|
+
(match, mod, param='') => (ctx.test = mods[mod]?.(ctx, ...param.split('-')) || ctx.test, '')
|
|
268
|
+
);
|
|
300
269
|
|
|
301
270
|
// add listener applying the context
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
)
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
271
|
+
const {evt, target, test, defer, stop, prevent, ...opts} = ctx;
|
|
272
|
+
|
|
273
|
+
if (defer) fn = defer(fn)
|
|
274
|
+
|
|
275
|
+
const cb = e => test(e) && (
|
|
276
|
+
stop&&e.stopPropagation(),
|
|
277
|
+
prevent&&e.preventDefault(),
|
|
278
|
+
fn.call(target, e)
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
target.addEventListener(evt, cb, opts)
|
|
282
|
+
return () => target.removeEventListener(evt, cb, opts)
|
|
314
283
|
}
|
|
315
284
|
|
|
316
285
|
// event modifiers
|
|
@@ -328,8 +297,6 @@ const mods = {
|
|
|
328
297
|
window(ctx) { ctx.target = window },
|
|
329
298
|
document(ctx) { ctx.target = document },
|
|
330
299
|
|
|
331
|
-
toggle(ctx) { ctx.defer = (fn, out) => (e => out ? (out.call?.(ctx.target, e), out=null) : (out = fn())) },
|
|
332
|
-
|
|
333
300
|
throttle(ctx, limit) { ctx.defer = fn => throttle(fn, limit ? Number(limit) || 0 : 108) },
|
|
334
301
|
debounce(ctx, wait) { ctx.defer = fn => debounce(fn, wait ? Number(wait) || 0 : 108) },
|
|
335
302
|
|