sprae 5.3.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 +45 -16
- package/readme.md +63 -66
- package/sprae.auto.js +24 -63
- package/sprae.auto.min.js +1 -1
- package/sprae.js +24 -63
- package/sprae.min.js +1 -1
- package/src/core.js +16 -11
- package/src/directives.js +22 -56
- package/test/{dom.js → attrs.js} +1 -323
- package/test/events.js +138 -0
- package/test/test.js +12 -1
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
|
@@ -240,18 +240,6 @@ secondary['value'] = (el, expr) => {
|
|
|
240
240
|
return (state) => update(evaluate(state))
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
secondary['on'] = (el, expr) => {
|
|
244
|
-
let evaluate = parseExpr(el, expr, ':on')
|
|
245
|
-
|
|
246
|
-
return (state) => {
|
|
247
|
-
let listeners = evaluate(state);
|
|
248
|
-
let offs = []; for (let evt in listeners) offs.push(on(el, evt, listeners[evt]));
|
|
249
|
-
return () => {
|
|
250
|
-
for (let off of offs) off()
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
243
|
// any unknown directive
|
|
256
244
|
export default (el, expr, state, name) => {
|
|
257
245
|
let evt = name.startsWith('on') && name.slice(2)
|
|
@@ -268,50 +256,30 @@ export default (el, expr, state, name) => {
|
|
|
268
256
|
return state => attr(el, name, evaluate(state))
|
|
269
257
|
}
|
|
270
258
|
|
|
271
|
-
// bind event to target
|
|
272
|
-
const on = (
|
|
273
|
-
if (!
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
ctx.
|
|
280
|
-
|
|
281
|
-
);
|
|
282
|
-
return ctx;
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
// single event bind
|
|
286
|
-
if (ctxs.length == 1) return addListenerWithMods(origFn, ctxs[0])
|
|
287
|
-
|
|
288
|
-
// events chain cycler
|
|
289
|
-
const onFn = (fn, cur=0) => {
|
|
290
|
-
let off
|
|
291
|
-
let curListener = e => {
|
|
292
|
-
if (cur) off(); // don't remove entry listener - we must keep chain entry always open
|
|
293
|
-
let nextFn = fn.call(target, e)
|
|
294
|
-
if (typeof nextFn !== 'function') nextFn = ()=>{}
|
|
295
|
-
if (cur+1 < ctxs.length) onFn(nextFn, !cur ? 1 : cur+1);
|
|
296
|
-
}
|
|
297
|
-
return off = addListenerWithMods(curListener, ctxs[cur])
|
|
298
|
-
}
|
|
299
|
-
let rootOff = onFn(origFn)
|
|
300
|
-
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
|
+
);
|
|
301
269
|
|
|
302
270
|
// add listener applying the context
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
)
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
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)
|
|
315
283
|
}
|
|
316
284
|
|
|
317
285
|
// event modifiers
|
|
@@ -329,8 +297,6 @@ const mods = {
|
|
|
329
297
|
window(ctx) { ctx.target = window },
|
|
330
298
|
document(ctx) { ctx.target = document },
|
|
331
299
|
|
|
332
|
-
toggle(ctx) { ctx.defer = (fn, out) => (e => out ? (out.call?.(ctx.target, e), out=null) : (out = fn())) },
|
|
333
|
-
|
|
334
300
|
throttle(ctx, limit) { ctx.defer = fn => throttle(fn, limit ? Number(limit) || 0 : 108) },
|
|
335
301
|
debounce(ctx, wait) { ctx.defer = fn => debounce(fn, wait ? Number(wait) || 0 : 108) },
|
|
336
302
|
|
package/test/{dom.js → attrs.js}
RENAMED
|
@@ -6,16 +6,6 @@ import sprae from '../src/index.js'
|
|
|
6
6
|
import h from 'hyperf'
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
Object.defineProperty(DocumentFragment.prototype, 'outerHTML', {
|
|
10
|
-
get() {
|
|
11
|
-
let s = ''
|
|
12
|
-
this.childNodes.forEach(n => {
|
|
13
|
-
s += n.nodeType === 3 ? n.textContent : n.outerHTML != null ? n.outerHTML : ''
|
|
14
|
-
})
|
|
15
|
-
return s
|
|
16
|
-
}
|
|
17
|
-
})
|
|
18
|
-
|
|
19
9
|
test.skip('autoinit', async () => {
|
|
20
10
|
is(window.x.innerHTML, '1')
|
|
21
11
|
})
|
|
@@ -570,318 +560,6 @@ test('each: with :with', () => {
|
|
|
570
560
|
is(el.outerHTML, `<ul><li>0</li><li>1</li><li>2</li></ul>`)
|
|
571
561
|
})
|
|
572
562
|
|
|
573
|
-
test('on: base', async () => {
|
|
574
|
-
let el = h`<div :on="{click(e){log.push('click')}, x}"></div>`
|
|
575
|
-
let log = []
|
|
576
|
-
let params = sprae(el, {x(){log.push('x')}, log})
|
|
577
|
-
|
|
578
|
-
is(el.outerHTML, `<div></div>`);
|
|
579
|
-
el.dispatchEvent(new window.Event('click'));
|
|
580
|
-
is(log, ['click'])
|
|
581
|
-
el.dispatchEvent(new window.Event('x'));
|
|
582
|
-
is(log, ['click','x'])
|
|
583
|
-
|
|
584
|
-
params.x = function(){log.push('xx')}
|
|
585
|
-
await tick()
|
|
586
|
-
el.dispatchEvent(new window.Event('x'));
|
|
587
|
-
is(log, ['click','x','xx']);
|
|
588
|
-
|
|
589
|
-
console.log('make null')
|
|
590
|
-
params.x = null;
|
|
591
|
-
await tick()
|
|
592
|
-
el.dispatchEvent(new window.Event('x'));
|
|
593
|
-
is(log, ['click','x','xx']);
|
|
594
|
-
})
|
|
595
|
-
|
|
596
|
-
test('onevt: this context', e => {
|
|
597
|
-
let el = h`<div :onx="function(){log.push(this)}"></div>`
|
|
598
|
-
let state = sprae(el, {log: []})
|
|
599
|
-
el.dispatchEvent(new window.Event('x'));
|
|
600
|
-
is(state.log, [el])
|
|
601
|
-
})
|
|
602
|
-
|
|
603
|
-
test('on: this in chains refers to el', () => {
|
|
604
|
-
let el = h`<div :ona..onb="function(e){x=this; log.push(1); return () => log.push(2)}"></div>`
|
|
605
|
-
let state = sprae(el, {log:[], x:null})
|
|
606
|
-
el.dispatchEvent(new window.Event('a'));
|
|
607
|
-
is(state.log, [1])
|
|
608
|
-
})
|
|
609
|
-
|
|
610
|
-
test('on: multiple events', e => {
|
|
611
|
-
let el = h`<div :onscroll:onclick:onx="e=>log.push(e.type)"></div>`
|
|
612
|
-
let state = sprae(el, {log:[]})
|
|
613
|
-
|
|
614
|
-
el.dispatchEvent(new window.Event('click'));
|
|
615
|
-
is(state.log, ['click'])
|
|
616
|
-
el.dispatchEvent(new window.Event('scroll'));
|
|
617
|
-
is(state.log, ['click','scroll'])
|
|
618
|
-
el.dispatchEvent(new window.Event('x'));
|
|
619
|
-
is(state.log, ['click','scroll','x'])
|
|
620
|
-
})
|
|
621
|
-
|
|
622
|
-
test('on: in-out events', e => {
|
|
623
|
-
// let el = document.createElement('x');
|
|
624
|
-
// el.setAttribute(':onmousedown..onmouseup', 'e=>(log.push(e.type),e=>log.push(e.type))')
|
|
625
|
-
let el = h`<x :onmousedown..onmouseup="(e) => { x=this; log.push(e.type); return e=>log.push(e.type); }"></x>`
|
|
626
|
-
|
|
627
|
-
let state = sprae(el, {log:[],x:null})
|
|
628
|
-
el.dispatchEvent(new window.Event('mousedown'));
|
|
629
|
-
is(state.x, el);
|
|
630
|
-
is(state.log, ['mousedown'])
|
|
631
|
-
el.dispatchEvent(new window.Event('mouseup'));
|
|
632
|
-
is(state.log, ['mousedown','mouseup'])
|
|
633
|
-
})
|
|
634
|
-
|
|
635
|
-
test('on: in-out side-effects', e => {
|
|
636
|
-
let log = []
|
|
637
|
-
|
|
638
|
-
// 1. skip in event and do directly out
|
|
639
|
-
let el = h`<x :onin..onout="io"></x>`
|
|
640
|
-
sprae(el, { io(e) {
|
|
641
|
-
log.push(e.type)
|
|
642
|
-
return (e) => (log.push(e.type), [1,2,3])
|
|
643
|
-
} })
|
|
644
|
-
|
|
645
|
-
el.dispatchEvent(new window.Event('out'));
|
|
646
|
-
is(log, [])
|
|
647
|
-
|
|
648
|
-
// 2. Some nonsensical return is fine
|
|
649
|
-
el.dispatchEvent(new window.Event('in'));
|
|
650
|
-
is(log, ['in'])
|
|
651
|
-
el.dispatchEvent(new window.Event('out'));
|
|
652
|
-
is(log, ['in','out'], 'out triggers right')
|
|
653
|
-
el.dispatchEvent(new window.Event('out'));
|
|
654
|
-
is(log, ['in','out'])
|
|
655
|
-
el.dispatchEvent(new window.Event('in'));
|
|
656
|
-
is(log, ['in','out','in'])
|
|
657
|
-
el.dispatchEvent(new window.Event('in'));
|
|
658
|
-
is(log, ['in','out','in', 'in'])
|
|
659
|
-
el.dispatchEvent(new window.Event('out'));
|
|
660
|
-
is(log, ['in','out','in','in','out','out'])
|
|
661
|
-
el.dispatchEvent(new window.Event('out'));
|
|
662
|
-
is(log, ['in','out','in','in','out','out'])
|
|
663
|
-
})
|
|
664
|
-
|
|
665
|
-
test('on: chain of events', e => {
|
|
666
|
-
let el = h`<div :onmousedown..onmousemove..onmouseup="e=>(log.push(e.type),e=>(log.push(e.type),e=>log.push(e.type)))"></div>`
|
|
667
|
-
let state = sprae(el, {log:[]})
|
|
668
|
-
|
|
669
|
-
el.dispatchEvent(new window.Event('mousedown'));
|
|
670
|
-
is(state.log, ['mousedown'])
|
|
671
|
-
el.dispatchEvent(new window.Event('mousemove'));
|
|
672
|
-
is(state.log, ['mousedown','mousemove'])
|
|
673
|
-
el.dispatchEvent(new window.Event('mouseup'));
|
|
674
|
-
is(state.log, ['mousedown','mousemove','mouseup'])
|
|
675
|
-
el.dispatchEvent(new window.Event('mouseup'));
|
|
676
|
-
is(state.log, ['mousedown','mousemove','mouseup'])
|
|
677
|
-
el.dispatchEvent(new window.Event('mousedown'));
|
|
678
|
-
is(state.log, ['mousedown','mousemove','mouseup','mousedown'])
|
|
679
|
-
})
|
|
680
|
-
|
|
681
|
-
test('on: parallel chains', e => {
|
|
682
|
-
let el = h`<div :onx..ony..onz="e=>('x',log.push(e.type),e=>('y',log.push(e.type),e=>('z',log.push(e.type))))"></div>`
|
|
683
|
-
let state = sprae(el, {log:[]})
|
|
684
|
-
|
|
685
|
-
console.log('emit x')
|
|
686
|
-
el.dispatchEvent(new window.Event('x'));
|
|
687
|
-
is(state.log, ['x'])
|
|
688
|
-
console.log('emit x')
|
|
689
|
-
el.dispatchEvent(new window.Event('x'));
|
|
690
|
-
is(state.log, ['x','x'])
|
|
691
|
-
console.log('emit y')
|
|
692
|
-
el.dispatchEvent(new window.Event('y'));
|
|
693
|
-
is(state.log, ['x','x','y','y'])
|
|
694
|
-
console.log('emit y')
|
|
695
|
-
el.dispatchEvent(new window.Event('y'));
|
|
696
|
-
is(state.log, ['x','x','y','y'])
|
|
697
|
-
console.log('emit z')
|
|
698
|
-
el.dispatchEvent(new window.Event('z'));
|
|
699
|
-
console.log('emit y')
|
|
700
|
-
el.dispatchEvent(new window.Event('y'));
|
|
701
|
-
is(state.log, ['x','x','y','y','z','z'])
|
|
702
|
-
el.dispatchEvent(new window.Event('z'));
|
|
703
|
-
is(state.log, ['x','x','y','y','z','z']);
|
|
704
|
-
el.dispatchEvent(new window.Event('x'));
|
|
705
|
-
is(state.log, ['x','x','y','y','z','z','x']);
|
|
706
|
-
})
|
|
707
|
-
|
|
708
|
-
test('on: state changes between chain of events', async e => {
|
|
709
|
-
let el = h`<x :on="{'x..y':fn}"></x>`
|
|
710
|
-
let log = []
|
|
711
|
-
let state = sprae(el, {log, fn: () => (log.push('x1'), ()=>log.push('y1'))})
|
|
712
|
-
console.log('emit x')
|
|
713
|
-
el.dispatchEvent(new window.Event('x'));
|
|
714
|
-
is(log, ['x1'])
|
|
715
|
-
console.log('update fn')
|
|
716
|
-
state.fn = () => (log.push('x2'), () => log.push('y2'))
|
|
717
|
-
await tick()
|
|
718
|
-
is(log, ['x1'])
|
|
719
|
-
// console.log('xx')
|
|
720
|
-
// NOTE: state update registers new chain listener before finishing prev chain
|
|
721
|
-
// el.dispatchEvent(new window.Event('x'));
|
|
722
|
-
// el.dispatchEvent(new window.Event('x'));
|
|
723
|
-
// is(log, [1])
|
|
724
|
-
console.log('emit y, y')
|
|
725
|
-
el.dispatchEvent(new window.Event('y'));
|
|
726
|
-
el.dispatchEvent(new window.Event('y'));
|
|
727
|
-
is(log, ['x1', 'y1'])
|
|
728
|
-
console.log('emit x')
|
|
729
|
-
el.dispatchEvent(new window.Event('x'));
|
|
730
|
-
is(log, ['x1','y1','x2'])
|
|
731
|
-
console.log('emit y, y')
|
|
732
|
-
el.dispatchEvent(new window.Event('y'));
|
|
733
|
-
el.dispatchEvent(new window.Event('y'));
|
|
734
|
-
is(log, ['x1','y1','x2','y2'])
|
|
735
|
-
})
|
|
736
|
-
|
|
737
|
-
test('on: once', e => {
|
|
738
|
-
// NOTE: if callback updates it's still rebound
|
|
739
|
-
let el = h`<x :onx.once="(e=>x&&log.push(this))" ></x>`
|
|
740
|
-
let log = []
|
|
741
|
-
let state = sprae(el, {log, x:1})
|
|
742
|
-
el.dispatchEvent(new window.Event('x'));
|
|
743
|
-
is(log, [el])
|
|
744
|
-
el.dispatchEvent(new window.Event('x'));
|
|
745
|
-
is(log, [el])
|
|
746
|
-
state.x = 2
|
|
747
|
-
el.dispatchEvent(new window.Event('x'));
|
|
748
|
-
el.dispatchEvent(new window.Event('x'));
|
|
749
|
-
is(log, [el])
|
|
750
|
-
})
|
|
751
|
-
|
|
752
|
-
test('on: capture, stop, prevent', e => {
|
|
753
|
-
let el = h`<x :onx.capture="e=>log.push(1)"><y :onx="e=>log.push(2)"></y></x>`
|
|
754
|
-
let state = sprae(el, {log:[]})
|
|
755
|
-
el.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
756
|
-
is(state.log, [1,2])
|
|
757
|
-
|
|
758
|
-
let el2 = h`<x :onx="e=>log.push(1)"><y :onx.stop="e=>log.push(2)"></y></x>`
|
|
759
|
-
let state2 = sprae(el2, {log:[]})
|
|
760
|
-
el2.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
761
|
-
is(state2.log, [2])
|
|
762
|
-
})
|
|
763
|
-
|
|
764
|
-
test('on: window, self', e => {
|
|
765
|
-
let el = h`<x :onx.self="e=>log.push(1)"><y :onx.window="e=>log.push(2)"></y></x>`
|
|
766
|
-
let state = sprae(el, {log:[]})
|
|
767
|
-
el.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
768
|
-
is(state.log, [])
|
|
769
|
-
el.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
770
|
-
is(state.log, [1])
|
|
771
|
-
window.dispatchEvent(new window.Event('x', {bubbles:true}));
|
|
772
|
-
is(state.log, [1,2])
|
|
773
|
-
})
|
|
774
|
-
|
|
775
|
-
test('on: keys', e => {
|
|
776
|
-
let el = h`<x :onkeydown.enter="e=>log.push(1)"></x>`
|
|
777
|
-
let state = {log:[]}
|
|
778
|
-
sprae(el, state)
|
|
779
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
780
|
-
is(state.log,[])
|
|
781
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
782
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
783
|
-
is(state.log,[1])
|
|
784
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
785
|
-
is(state.log,[1,1])
|
|
786
|
-
})
|
|
787
|
-
|
|
788
|
-
test('on: key combinations', e => {
|
|
789
|
-
let el = h`<x :onkeydown.ctrl-enter="e=>log.push(1)"></x>`
|
|
790
|
-
let state = {log:[]}
|
|
791
|
-
sprae(el, state)
|
|
792
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
793
|
-
is(state.log,[])
|
|
794
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
795
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
|
|
796
|
-
is(state.log,[])
|
|
797
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true }));
|
|
798
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
799
|
-
is(state.log,[1])
|
|
800
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
|
|
801
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', ctrlKey: true }));
|
|
802
|
-
is(state.log,[1,1])
|
|
803
|
-
let el2 = h`<x :onkeydown.ctrl-alt-enter="e=>log.push(1)"></x>`
|
|
804
|
-
})
|
|
805
|
-
|
|
806
|
-
test('on: keys with prevent', e => {
|
|
807
|
-
let el = h`<y :onkeydown="e=>log.push(e.key)"><x :ref="x" :onkeydown.enter.stop></x></y>`
|
|
808
|
-
let state = sprae(el, {log:[]})
|
|
809
|
-
state.x.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
810
|
-
console.log('enter')
|
|
811
|
-
state.x.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
|
812
|
-
is(state.log,['x'])
|
|
813
|
-
})
|
|
814
|
-
|
|
815
|
-
test('on: debounce', async e => {
|
|
816
|
-
let el = h`<x :onkeydown.debounce-1="e=>log.push(e.key)"></x>`
|
|
817
|
-
let state = sprae(el, {log:[]})
|
|
818
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
819
|
-
is(state.log, [])
|
|
820
|
-
await time(2)
|
|
821
|
-
is(state.log, ['x'])
|
|
822
|
-
})
|
|
823
|
-
|
|
824
|
-
test('on: debounce 0', async e => {
|
|
825
|
-
let el = h`<x :onkeydown.debounce-0="e=>log.push(e.key)"></x>`
|
|
826
|
-
let state = sprae(el, {log:[]})
|
|
827
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
828
|
-
is(state.log, [])
|
|
829
|
-
await time(2)
|
|
830
|
-
is(state.log, ['x'])
|
|
831
|
-
})
|
|
832
|
-
|
|
833
|
-
test('on: throttle', async e => {
|
|
834
|
-
let el = h`<x :onkeydown.throttle-10="e=>log.push(e.key)"></x>`
|
|
835
|
-
let state = sprae(el, {log:[]})
|
|
836
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
837
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
838
|
-
is(state.log, ['x'])
|
|
839
|
-
await time(5)
|
|
840
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
841
|
-
is(state.log, ['x'])
|
|
842
|
-
await time(10)
|
|
843
|
-
is(state.log, ['x', 'x'])
|
|
844
|
-
await time(10)
|
|
845
|
-
is(state.log, ['x', 'x'])
|
|
846
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
847
|
-
is(state.log, ['x', 'x', 'x'])
|
|
848
|
-
})
|
|
849
|
-
|
|
850
|
-
test('on: toggle', async e => {
|
|
851
|
-
let el = h`<x :onx.toggle="e=>(log.push(1),e=>log.push(2))"></x>`
|
|
852
|
-
let state = sprae(el, {log:[]})
|
|
853
|
-
el.dispatchEvent(new window.KeyboardEvent('x'));
|
|
854
|
-
is(state.log, [1])
|
|
855
|
-
el.dispatchEvent(new window.KeyboardEvent('x'));
|
|
856
|
-
is(state.log, [1,2])
|
|
857
|
-
el.dispatchEvent(new window.KeyboardEvent('x'));
|
|
858
|
-
is(state.log, [1,2,1])
|
|
859
|
-
el.dispatchEvent(new window.KeyboardEvent('x'));
|
|
860
|
-
is(state.log, [1,2,1,2])
|
|
861
|
-
})
|
|
862
|
-
|
|
863
|
-
test.skip('on: nexttick', async e => {
|
|
864
|
-
let el = h`<x :onkeydown.nexttick="e=>log.push(e.key)"></x>`
|
|
865
|
-
let state = sprae(el, {log:[]})
|
|
866
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
867
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
868
|
-
is(state.log, [])
|
|
869
|
-
await time()
|
|
870
|
-
is(state.log, ['x', 'x'])
|
|
871
|
-
})
|
|
872
|
-
|
|
873
|
-
test('on: modifiers chain', async e => {
|
|
874
|
-
let el = h`<x :onkeydown.letter..onkeyup.letter="e=>(log.push(e.key),(e)=>log.push(e.key))"></x>`
|
|
875
|
-
let state = sprae(el, {log:[]})
|
|
876
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'x', bubbles: true }));
|
|
877
|
-
el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
|
|
878
|
-
is(state.log,['x'])
|
|
879
|
-
el.dispatchEvent(new window.KeyboardEvent('keyup', { key: 'Enter', bubbles: true }));
|
|
880
|
-
is(state.log,['x'])
|
|
881
|
-
el.dispatchEvent(new window.KeyboardEvent('keyup', { key: 'x', bubbles: true }));
|
|
882
|
-
is(state.log,['x', 'x'])
|
|
883
|
-
})
|
|
884
|
-
|
|
885
563
|
test('with: inline', async () => {
|
|
886
564
|
let el = h`<x :with="{foo:'bar'}"><y :text="foo + baz"></y></x>`
|
|
887
565
|
let state = sprae(el, {baz: 'qux'})
|
|
@@ -929,7 +607,7 @@ test.skip('with: reactive transparency', () => {
|
|
|
929
607
|
is(el.innerHTML, `<y>13</y>`)
|
|
930
608
|
})
|
|
931
609
|
test('with: writes to state', async () => {
|
|
932
|
-
let a = h`<x :with="{a:1}"><y :
|
|
610
|
+
let a = h`<x :with="{a:1}"><y :onx="e=>a++" :text="a"></y></x>`
|
|
933
611
|
sprae(a)
|
|
934
612
|
is(a.innerHTML, `<y>1</y>`)
|
|
935
613
|
a.firstChild.dispatchEvent(new window.Event('x'))
|