sprae 2.7.0 → 2.8.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 CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "sprae",
3
3
  "description": "DOM microhydration.",
4
- "version": "2.7.0",
4
+ "version": "2.8.0",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
7
7
  "type": "module",
8
8
  "dependencies": {
9
9
  "@preact/signals-core": "^1.2.2",
10
10
  "primitive-pool": "^2.0.0",
11
- "signal-struct": "^1.9.0",
11
+ "signal-struct": "^1.10.0",
12
12
  "swapdom": "^1.1.1"
13
13
  },
14
14
  "devDependencies": {
package/r&d.md CHANGED
@@ -289,9 +289,9 @@
289
289
  * @sprae/item: `<x :item="{type:a, scope:b}"` – provide microdata
290
290
  - can be solved naturally, unless there's special meaning
291
291
  * @sprae/hcodes: `<x :hcode=""` – provide microformats
292
- * @sprae/with
293
- * @sprae/onconnected
294
292
  * @sprae/onvisible?
293
+ - can be solved externally
294
+ * @sprae/onintersects
295
295
 
296
296
  ## [x] Write any-attributes via `:<prop>? -> yep`
297
297
 
@@ -330,7 +330,7 @@
330
330
  + Provides precisely controlled sandbox
331
331
  - Some limited lang opportunities
332
332
  - need to match many syntax quirks, can be tedious
333
- ~ can be fine to limit expressions to meaningful default: why Proxy, generators, awaits, global access etc.
333
+ ~ can be fine to limit expressions to meaningful default: no Proxy, generators, awaits, global access etc.
334
334
  - Somewhat heavy to bundle
335
335
  ~ 1-2kb is not super-heavy, besides kicks out signal-struct (with preact signals?)
336
336
  + Scope is easier to provide: no need for signal proxy
@@ -340,6 +340,7 @@
340
340
  + Deps can be analyzed / implemented without signals
341
341
  - Screwed up debugging / stacktrace (unless errored properly)
342
342
  ~+ can actually provide better trace since no internal framework stuff is shown
343
+ + can let means to enhance subscript's logs
343
344
  + that "unlimits" returned struct, so that any property can be added/deleted.
344
345
  - doesn't really save from `new (()=>{}).constructor` hack: we gotta substitute objects too.
345
346
  + allows easier handle of `:with="a=1,b=2,c=3"` - we just naturally get local variables without messup with global
@@ -349,9 +350,10 @@
349
350
 
350
351
  2. Use sandboxed proxy
351
352
  - tough evaluation
352
- - tough implementation
353
+ - implementation hiccups
353
354
  - screwed up data
354
355
  - no full protection
356
+ - relatively slow
355
357
  + does minimal catch
356
358
 
357
359
  ## [x] :onclick="direct code" ? -> no: immediately invoked.
@@ -374,19 +376,23 @@
374
376
  ? maybe id should have same signature
375
377
  ? should it be very similar mechanism to `:with="a=1,b=2"`
376
378
 
377
- ## [ ] Event modifiers :ona.once, `:ona`
379
+ ## [x] Event modifiers :ona.once, `:ona` -> let's try, there's a lot of use for both props and event
378
380
 
379
381
  - .prevent,.stop - not needed since expects a function
380
382
  ? or should we just trigger it for user?
381
383
  ? :onclick.outside
382
384
  ? :onclick.window, :onclick.document
383
- ? :onclick.once
384
- ? :onclick.debounce
385
- ? :onclick.throttle.750ms
385
+ + can be needed, eg. mousedown..up can happen outside of current element (stop caret tracking in waveplay)
386
+ ? :onclick.once, :onclick.passive, :onclick.capture
387
+ + can pass props to event listener: there's no other way to do that
388
+ ? :onclick.debounce-330
389
+ ? :onclick.throttle-750
390
+ + ...just handy everydayers and saves tons of noise
386
391
  ? :onclick.self
392
+ ~ clicked on self, not the child
387
393
  ? :onspecial-event.camel, :onx-y.dot
388
- ? :onclick.passive
389
- ? :onkeypress.shift.enter
394
+ ~
395
+ ? :onkeypress.shift.enter, :onmousemove.shift, :onmousemove.alt
390
396
  .shift Shift
391
397
  .enter Enter
392
398
  .space Space
@@ -394,15 +400,57 @@
394
400
  .cmd Cmd
395
401
  .meta Cmd on Mac, Windows key on Windows
396
402
  .alt Alt
397
- .up .down .left .right Up/Down/Left/Right arrows
403
+ .up .down .left .right
398
404
  .escape Escape
399
405
  .tab Tab
400
406
  .caps-lock Caps Lock
401
- .equal Equal, =
402
- .period Period, .
403
- .slash Foward Slash, /
407
+ .equal Equal
408
+ .period Period
409
+ .slash Foward Slash
410
+ + allows separating various key handlers: atm waveplay handles separate keys in the same method `handleKey`
411
+ + allows tracking mouse interactions with shift hold
404
412
  - conflict with dot-separated events
413
+ ~not so popular nor encouraged
405
414
  - lots of ad-hoc non-standard rules, can be handled in code
415
+ + allow multiple setters for same props or multiple listeners for same events
416
+ + oldschool jquery-compatible events
417
+
418
+ ## [x] Mods design -> use event modifiers, not props modifiers
419
+
420
+ * [ ] ~~Prop modifiers~~
421
+ - overall seems code complication without much benefit
422
+ * [ ] value.bind? value.watch?
423
+ - let's wait for use-case: value can be too big to set it every time
424
+ * [ ] prop.reflect, prop.observe
425
+ - let's wait for use-case
426
+ * [ ] prop.boolean, .number, .string, .array, .object
427
+ - let's wait for use-case
428
+ * [ ] prop.once, prop.fx ? prop.init?
429
+ - doesn't seem required, let's wait for use case
430
+ * [ ] prop.change - run only if value changes
431
+ - seems like unnecessary manual optimization that must be done always automatically
432
+ ? are there cases where force-update is necessary?
433
+ * [ ] prop.throttle-xxx, prop.debounce-xxx
434
+ - let's wait until that's really a problem
435
+ * [ ] prop.class
436
+ ? what's the use-case
437
+
438
+ * [x] Event modifiers
439
+ * [x] onevt.x, onevt.y
440
+ + jquery-like
441
+ + multiple same events
442
+ * [x] onevt.once, onevt.passive, onevt.capture
443
+ + standard props passing
444
+ * [x] onevt.prevent, onevt.stop
445
+ + conventional mods
446
+ * [x] onevt.outside onevt.window, onevt.document, onevt.self
447
+ + useful handlers, conventional mods
448
+ * [x] onevt.throttle-xxx, onevt.debounce-xxx
449
+ + conventional
450
+ * [x] onevt.shift, onevt.cmd.shift, onevt.meta
451
+ + conventional
452
+ * [x] onkey.enter, .space, .up|.down|.left|.right, .escape, .tab, .period, .slash, .caps-lock
453
+ + conventional
406
454
 
407
455
  ## [x] Writing props on elements (like ones in :each) -> nah, just use `:x="this.x=abc"`
408
456
 
package/readme.md CHANGED
@@ -2,9 +2,6 @@
2
2
 
3
3
  > DOM microhydration with `:` attributes
4
4
 
5
- A lightweight essential alternative to [alpine](https://github.com/alpinejs/alpine), [petite-vue](https://github.com/vuejs/petite-vue), [templize](https://github.com/dy/templize) or JSX with better ergonomics[*](#justification).<br>
6
- It is reminiscent of [XSLT](https://www.w3schools.com/xml/xsl_intro.asp), considered a [buried treasure](https://github.com/bahrus/be-restated) by web-connoisseurs.
7
-
8
5
 
9
6
  ## Usage
10
7
 
@@ -163,10 +160,22 @@ Add event listeners.
163
160
  }
164
161
  }">
165
162
 
163
+ <!-- Event modifiers -->
164
+ <button :onclick.throttle-500="handler">Not too often</button>
165
+
166
166
  <!-- Bulk/custom events -->
167
167
  <button :on="{ click: handler, touch: handler, special: handler }">Submit</button>
168
168
  ```
169
169
 
170
+ ##### Event modifiers
171
+
172
+ * `.once`, `.passive`, `.capture` – listener [options](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener#options).
173
+ * `.prevent`, `.stop` – prevent default or stop propagation.
174
+ * `.window`, `.document`, `.outside`, `.self` – specify event target.
175
+ * `.throttle-108`, `.debounce-108` – define throttling or postponing callback with (optional) timeout in ms.
176
+ * `.ctrl`, `.shift`, `.alt`, `.meta`, `.cmd`, `.down`, `.up`, `.left`, `.right`, `.end`, `.home`, `.pagedown`, `.pageup`, `.enter`, `.plus`, `.minus`, `.star`, `.slash`, `.period`, `.equal`, `.underscore`, `.esc`, `.escape`, `.tab`, `.space`, `.backspace`, `.delete` – filter by [`event.key`](https://developer.mozilla.org/en-US/docs/Web/API/UI_Events/Keyboard_event_key_values).
177
+ * `.*` – any other modifier has no effect, but allows binding multiple handlers to same event (like jQuery event classes).
178
+
170
179
  #### `:data="values"`
171
180
 
172
181
  Set [data-*](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/data-*) attributes. CamelCase is converted to dash-case.
@@ -334,11 +343,13 @@ This way, for example, _@preact/signals_ or _rxjs_ can be connected directly byp
334
343
 
335
344
  ## Justification
336
345
 
346
+ _Sprae_ is lightweight essential alternative to [alpine](https://github.com/alpinejs/alpine), [petite-vue](https://github.com/vuejs/petite-vue), [templize](https://github.com/dy/templize) or JSX with better ergonomics[*](#justification).
347
+
337
348
  * [Template-parts](https://github.com/dy/template-parts) / [templize](https://github.com/dy/templize) is progressive, but is stuck with native HTML quirks ([parsing table](https://github.com/github/template-parts/issues/24), [svg attributes](https://github.com/github/template-parts/issues/25), [liquid syntax](https://shopify.github.io/liquid/tags/template/#raw) conflict etc). Also ergonomics of `attr="{{}}"` is inferior to `:attr=""` since it creates flash of uninitialized values.
338
349
  * [Alpine](https://github.com/alpinejs/alpine) / [vue](https://github.com/vuejs/petite-vue) / [lit](https://github.com/lit/lit/tree/main/packages/lit-html) escapes native HTML quirks, but the syntax is a bit scattered: `:attr`, `v-*`,`x-*`, `@evt`, `{{}}` can be expressed with single convention. Besides, functionality is too broad and can be reduced to essence: perfection is when there's nothing to take away, not add. Also they tend to [self-encapsulate](https://github.com/alpinejs/alpine/discussions/3223), making interop hard.
339
350
  * React/[preact](https://ghub.io/preact) does the job wiring up JS to HTML, but with an extreme of migrating HTML to JSX and enforcing SPA, which is not organic for HTML. Also it doesn't support reactive fields (needs render call).
340
351
 
341
- _sprae_ takes convention of _templize directives_ (_alpine_/_vue_ attrs) and builds upon [_@preact/signals_](https://ghub.io/@preact/signals).
352
+ _Sprae_ takes convention of _templize directives_ (_alpine_/_vue_ attrs) and builds upon [_@preact/signals_](https://ghub.io/@preact/signals).
342
353
 
343
354
  * It doesn't break static html markup.
344
355
  * It falls back to element content if uninitialized.
@@ -349,4 +360,8 @@ _sprae_ takes convention of _templize directives_ (_alpine_/_vue_ attrs) and bui
349
360
  * Input data may contain [signals](https://ghub.io/@preact/signals) or [reactive values](https://ghub.io/sube).
350
361
  * Elements / data API is open and enable easy interop.
351
362
 
363
+
364
+ It is reminiscent of [XSLT](https://www.w3schools.com/xml/xsl_intro.asp), considered a [buried treasure](https://github.com/bahrus/be-restated) by web-connoisseurs.
365
+
366
+
352
367
  <p align="center"><a href="https://github.com/krsnzd/license/">🕉</a></p>
package/sprae.js CHANGED
@@ -71,16 +71,16 @@ function v(i2) {
71
71
  }
72
72
  }
73
73
  }
74
- function e(i2) {
74
+ function e2(i2) {
75
75
  this.v = i2;
76
76
  this.i = 0;
77
77
  this.n = void 0;
78
78
  this.t = void 0;
79
79
  }
80
- e.prototype.h = function() {
80
+ e2.prototype.h = function() {
81
81
  return true;
82
82
  };
83
- e.prototype.S = function(i2) {
83
+ e2.prototype.S = function(i2) {
84
84
  if (this.t !== i2 && void 0 === i2.e) {
85
85
  i2.x = this.t;
86
86
  if (void 0 !== this.t)
@@ -88,7 +88,7 @@ e.prototype.S = function(i2) {
88
88
  this.t = i2;
89
89
  }
90
90
  };
91
- e.prototype.U = function(i2) {
91
+ e2.prototype.U = function(i2) {
92
92
  var t2 = i2.e, h2 = i2.x;
93
93
  if (void 0 !== t2) {
94
94
  t2.x = h2;
@@ -101,7 +101,7 @@ e.prototype.U = function(i2) {
101
101
  if (i2 === this.t)
102
102
  this.t = h2;
103
103
  };
104
- e.prototype.subscribe = function(i2) {
104
+ e2.prototype.subscribe = function(i2) {
105
105
  var t2 = this;
106
106
  return b(function() {
107
107
  var h2 = t2.value, o2 = 32 & this.f;
@@ -113,16 +113,16 @@ e.prototype.subscribe = function(i2) {
113
113
  }
114
114
  });
115
115
  };
116
- e.prototype.valueOf = function() {
116
+ e2.prototype.valueOf = function() {
117
117
  return this.value;
118
118
  };
119
- e.prototype.toString = function() {
119
+ e2.prototype.toString = function() {
120
120
  return this.value + "";
121
121
  };
122
- e.prototype.peek = function() {
122
+ e2.prototype.peek = function() {
123
123
  return this.v;
124
124
  };
125
- Object.defineProperty(e.prototype, "value", { get: function() {
125
+ Object.defineProperty(e2.prototype, "value", { get: function() {
126
126
  var i2 = v(this);
127
127
  if (void 0 !== i2)
128
128
  i2.i = this.i;
@@ -144,7 +144,7 @@ Object.defineProperty(e.prototype, "value", { get: function() {
144
144
  }
145
145
  } });
146
146
  function u(i2) {
147
- return new e(i2);
147
+ return new e2(i2);
148
148
  }
149
149
  function d(i2) {
150
150
  for (var t2 = i2.s; void 0 !== t2; t2 = t2.n)
@@ -183,13 +183,13 @@ function a(i2) {
183
183
  i2.s = h2;
184
184
  }
185
185
  function l(i2) {
186
- e.call(this, void 0);
186
+ e2.call(this, void 0);
187
187
  this.x = i2;
188
188
  this.s = void 0;
189
189
  this.g = f - 1;
190
190
  this.f = 4;
191
191
  }
192
- (l.prototype = new e()).h = function() {
192
+ (l.prototype = new e2()).h = function() {
193
193
  this.f &= -3;
194
194
  if (1 & this.f)
195
195
  return false;
@@ -230,10 +230,10 @@ l.prototype.S = function(i2) {
230
230
  for (var t2 = this.s; void 0 !== t2; t2 = t2.n)
231
231
  t2.S.S(t2);
232
232
  }
233
- e.prototype.S.call(this, i2);
233
+ e2.prototype.S.call(this, i2);
234
234
  };
235
235
  l.prototype.U = function(i2) {
236
- e.prototype.U.call(this, i2);
236
+ e2.prototype.U.call(this, i2);
237
237
  if (void 0 === this.t) {
238
238
  this.f &= -33;
239
239
  for (var t2 = this.s; void 0 !== t2; t2 = t2.n)
@@ -408,7 +408,7 @@ function signalStruct(values, proto) {
408
408
  try {
409
409
  Object.assign(s2.value, v2);
410
410
  return;
411
- } catch (e2) {
411
+ } catch (e3) {
412
412
  }
413
413
  s2.value = Object.seal(signalStruct(v2));
414
414
  } else if (Array.isArray(v2))
@@ -425,7 +425,8 @@ function signalStruct(values, proto) {
425
425
  return state;
426
426
  }
427
427
  if (Array.isArray(values) && !isStruct(values[0])) {
428
- return values.map((v2) => signalStruct(v2));
428
+ for (let i2 = 0; i2 < values.length; i2++)
429
+ values[i2] = signalStruct(values[i2]);
429
430
  }
430
431
  return values;
431
432
  }
@@ -486,10 +487,6 @@ var primitive_pool_default = (key) => {
486
487
  // src/directives.js
487
488
  var primary = {};
488
489
  var secondary = {};
489
- primary["with"] = (el, expr, rootState) => {
490
- let evaluate = parseExpr(el, expr, "with");
491
- sprae(el, signalStruct(evaluate(rootState), rootState));
492
- };
493
490
  primary["if"] = (el, expr) => {
494
491
  let holder = document.createTextNode(""), clauses = [parseExpr(el, expr, ":if")], els = [el], cur = el;
495
492
  while (cur = el.nextElementSibling) {
@@ -511,11 +508,16 @@ primary["if"] = (el, expr) => {
511
508
  return (state) => {
512
509
  let i2 = clauses.findIndex((f2) => f2(state));
513
510
  if (els[i2] != cur) {
511
+ ;
514
512
  (cur[_each] || cur).replaceWith(cur = els[i2] || holder);
515
513
  sprae(cur, state);
516
514
  }
517
515
  };
518
516
  };
517
+ primary["with"] = (el, expr, rootState) => {
518
+ let evaluate = parseExpr(el, expr, "with");
519
+ sprae(el, signalStruct(evaluate(rootState), rootState));
520
+ };
519
521
  var _each = Symbol(":each");
520
522
  primary["each"] = (tpl, expr) => {
521
523
  let each = parseForExpression(expr);
@@ -622,6 +624,31 @@ secondary["text"] = (el, expr) => {
622
624
  el.textContent = value == null ? "" : value;
623
625
  };
624
626
  };
627
+ secondary["data"] = (el, expr) => {
628
+ let evaluate = parseExpr(el, expr, ":data");
629
+ return (state) => {
630
+ let value = evaluate(state);
631
+ for (let key in value)
632
+ el.dataset[key] = value[key];
633
+ };
634
+ };
635
+ secondary["aria"] = (el, expr) => {
636
+ let evaluate = parseExpr(el, expr, ":aria");
637
+ const update = (value) => {
638
+ for (let key in value)
639
+ attr(el, "aria-" + dashcase(key), value[key] == null ? null : value[key] + "");
640
+ };
641
+ return (state) => update(evaluate(state));
642
+ };
643
+ secondary[""] = (el, expr) => {
644
+ let evaluate = parseExpr(el, expr, ":");
645
+ if (evaluate)
646
+ return (state) => {
647
+ let value = evaluate(state);
648
+ for (let key in value)
649
+ attr(el, dashcase(key), value[key]);
650
+ };
651
+ };
625
652
  secondary["value"] = (el, expr) => {
626
653
  let evaluate = parseExpr(el, expr, ":value");
627
654
  let from, to;
@@ -645,16 +672,35 @@ secondary["on"] = (el, expr) => {
645
672
  };
646
673
  };
647
674
  };
675
+ var directives_default = (el, expr, state, name) => {
676
+ let evt = name.startsWith("on") && name.slice(2);
677
+ let evaluate = parseExpr(el, expr, ":" + name);
678
+ if (!evaluate)
679
+ return;
680
+ if (evt)
681
+ return (state2) => {
682
+ let value = evaluate(state2);
683
+ if (value) {
684
+ addListener(el, evt, value);
685
+ return () => removeListener(el, evt, value);
686
+ }
687
+ };
688
+ return (state2) => attr(el, name, evaluate(state2));
689
+ };
648
690
  var _stop = Symbol("stop");
649
691
  var addListener = (el, evt, startFn) => {
650
- if (evt.indexOf("..") < 0)
651
- el.addEventListener(evt, startFn);
692
+ let evts = evt.split("..").map((e3) => e3.startsWith("on") ? e3.slice(2) : e3), opts = {};
693
+ evts[0] = evts[0].replace(
694
+ /\.(\w+)?-?([\w]+)?/g,
695
+ (match, mod, param) => (mod = mods[mod]) ? ([el, startFn] = mod(el, startFn, opts, param), "") : ""
696
+ );
697
+ if (evts.length == 1)
698
+ el.addEventListener(evts[0], startFn, opts);
652
699
  else {
653
- const evts = evt.split("..").map((e2) => e2.startsWith("on") ? e2.slice(2) : e2);
654
700
  const nextEvt = (fn, cur = 0) => {
655
- let curListener = (e2) => {
701
+ let curListener = (e3) => {
656
702
  el.removeEventListener(evts[cur], curListener);
657
- if (typeof (fn = fn.call(el, e2)) !== "function")
703
+ if (typeof (fn = fn.call(el, e3)) !== "function")
658
704
  fn = () => {
659
705
  };
660
706
  if (++cur < evts.length)
@@ -662,7 +708,7 @@ var addListener = (el, evt, startFn) => {
662
708
  else if (!startFn[_stop])
663
709
  nextEvt(startFn);
664
710
  };
665
- el.addEventListener(evts[cur], curListener);
711
+ el.addEventListener(evts[cur], curListener, opts);
666
712
  };
667
713
  nextEvt(startFn);
668
714
  }
@@ -672,43 +718,123 @@ var removeListener = (el, evt, fn) => {
672
718
  fn[_stop] = true;
673
719
  el.removeEventListener(evt, fn);
674
720
  };
675
- secondary["data"] = (el, expr) => {
676
- let evaluate = parseExpr(el, expr, ":data");
677
- return (state) => {
678
- let value = evaluate(state);
679
- for (let key in value)
680
- el.dataset[key] = value[key];
681
- };
682
- };
683
- secondary["aria"] = (el, expr) => {
684
- let evaluate = parseExpr(el, expr, ":aria");
685
- const update = (value) => {
686
- for (let key in value)
687
- attr(el, "aria-" + dashcase(key), value[key] == null ? null : value[key] + "");
688
- };
689
- return (state) => update(evaluate(state));
690
- };
691
- secondary[""] = (el, expr) => {
692
- let evaluate = parseExpr(el, expr, ":");
693
- if (evaluate)
694
- return (state) => {
695
- let value = evaluate(state);
696
- for (let key in value)
697
- attr(el, dashcase(key), value[key]);
721
+ var mods = {
722
+ throttle(el, cb, opts, limit) {
723
+ limit = Number(limit) || 108;
724
+ let pause, planned, block = () => {
725
+ pause = true;
726
+ setTimeout(() => {
727
+ pause = false;
728
+ if (planned)
729
+ cb(e), planned = false, block();
730
+ });
698
731
  };
732
+ return [el, (e3) => {
733
+ if (pause)
734
+ return planned = true;
735
+ cb(e3);
736
+ block();
737
+ }];
738
+ },
739
+ debounce(el, cb, opts, wait) {
740
+ wait = Number(wait) || 108;
741
+ let timeout, later = () => {
742
+ timeout = null;
743
+ cb(e);
744
+ };
745
+ return [el, (e3) => {
746
+ clearTimeout(timeout);
747
+ timeout = setTimeout(later, wait);
748
+ }];
749
+ },
750
+ window(el, cb) {
751
+ return [window, cb];
752
+ },
753
+ document(el, cb) {
754
+ return [document, cb];
755
+ },
756
+ outside(el, cb) {
757
+ return [el, (e3) => {
758
+ if (el.contains(e3.target))
759
+ return;
760
+ if (e3.target.isConnected === false)
761
+ return;
762
+ if (el.offsetWidth < 1 && el.offsetHeight < 1)
763
+ return;
764
+ cb(e3);
765
+ }];
766
+ },
767
+ prevent(el, cb) {
768
+ return [el, (e3) => {
769
+ e3.preventDefault();
770
+ cb(e3);
771
+ }];
772
+ },
773
+ stop(el, cb) {
774
+ return [el, (e3) => {
775
+ e3.stopPropagation();
776
+ cb(e3);
777
+ }];
778
+ },
779
+ self(el, cb) {
780
+ return [el, (e3) => {
781
+ e3.target === el && cb(e3);
782
+ }];
783
+ },
784
+ once(el, cb, opts) {
785
+ opts.once = true;
786
+ return [el, cb];
787
+ },
788
+ passive(el, cb, opts) {
789
+ opts.passive = true;
790
+ return [el, cb];
791
+ },
792
+ capture(el, cb, opts) {
793
+ opts.capture = true;
794
+ return [el, cb];
795
+ }
699
796
  };
700
- var directives_default = (el, expr, values, name) => {
701
- let evt = name.startsWith("on") && name.slice(2);
702
- let evaluate = parseExpr(el, expr, ":" + name);
703
- if (evaluate)
704
- return evt ? (state) => {
705
- let value = evaluate(state);
706
- if (value) {
707
- addListener(el, evt, value);
708
- return () => removeListener(el, evt, value);
709
- }
710
- } : (state) => attr(el, name, evaluate(state));
711
- };
797
+ var keys = {
798
+ ctrl: "Control Ctrl",
799
+ shift: "Shift",
800
+ alt: "Alt",
801
+ meta: "Meta",
802
+ cmd: "Meta",
803
+ down: "ArrowDown",
804
+ up: "ArrowUp",
805
+ left: "ArrowLeft",
806
+ right: "ArrowRight",
807
+ arrowdown: "ArrowDown",
808
+ arrowup: "ArrowUp",
809
+ arrowleft: "ArrowLeft",
810
+ arrowright: "ArrowRight",
811
+ end: "End",
812
+ home: "Home",
813
+ pagedown: "PageDown",
814
+ pageup: "PageUp",
815
+ enter: "Enter",
816
+ plus: "+",
817
+ minus: "-",
818
+ star: "*",
819
+ slash: "/",
820
+ period: ".",
821
+ equal: "=",
822
+ underscore: "_",
823
+ esc: "Escape",
824
+ escape: "Escape",
825
+ tab: "Tab",
826
+ space: " ",
827
+ backspace: "Backspace",
828
+ delete: "Delete"
829
+ };
830
+ for (let keyAttr in keys) {
831
+ let keyName = keys[keyAttr];
832
+ mods[keyAttr] = (el, cb, opts, extraKey) => [el, (e3) => {
833
+ if (!e3.key || !keyName.includes(e3.key))
834
+ return;
835
+ cb(e3);
836
+ }];
837
+ }
712
838
  var attr = (el, name, v2) => {
713
839
  if (v2 == null || v2 === false)
714
840
  el.removeAttribute(name);
@@ -722,16 +848,16 @@ function parseExpr(el, expression, dir) {
722
848
  let rightSideSafeExpression = /^[\n\s]*if.*\(.*\)/.test(expression) || /^(let|const)\s/.test(expression) ? `(() => { ${expression} })()` : expression;
723
849
  try {
724
850
  evaluate = evaluatorMemo[expression] = new Function(`__scope`, `with (__scope) { return ${rightSideSafeExpression} };`);
725
- } catch (e2) {
726
- return exprError(e2, el, expression, dir);
851
+ } catch (e3) {
852
+ return exprError(e3, el, expression, dir);
727
853
  }
728
854
  }
729
855
  return (state) => {
730
856
  let result;
731
857
  try {
732
858
  result = evaluate.call(el, state);
733
- } catch (e2) {
734
- return exprError(e2, el, expression, dir);
859
+ } catch (e3) {
860
+ return exprError(e3, el, expression, dir);
735
861
  }
736
862
  return result;
737
863
  };
package/sprae.min.js CHANGED
@@ -1 +1 @@
1
- function t(){throw new Error("Cycle detected")}function e(){if(n>1)n--;else{for(var t,e=!1;void 0!==r;){var i=r;for(r=void 0,o++;void 0!==i;){var s=i.o;if(i.o=void 0,i.f&=-3,!(8&i.f)&&u(i))try{i.c()}catch(i){e||(t=i,e=!0)}i=s}}if(o=0,n--,e)throw t}}var i=void 0,r=void 0,n=0,o=0,s=0;function l(t){if(void 0!==i){var e=t.n;if(void 0===e||e.t!==i)return i.s=e={i:0,S:t,p:void 0,n:i.s,t:i,e:void 0,x:void 0,r:e},t.n=e,32&i.f&&t.S(e),e;if(-1===e.i)return e.i=0,void 0!==e.p&&(e.p.n=e.n,void 0!==e.n&&(e.n.p=e.p),e.p=void 0,e.n=i.s,i.s.p=e,i.s=e),e}}function f(t){this.v=t,this.i=0,this.n=void 0,this.t=void 0}function u(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 a(t){for(var e=t.s;void 0!==e;e=e.n){var i=e.S.n;void 0!==i&&(e.r=i),e.S.n=e,e.i=-1}}function h(t){for(var e=t.s,i=void 0;void 0!==e;){var r=e.n;-1===e.i?(e.S.U(e),e.n=void 0):(void 0!==i&&(i.p=e),e.p=void 0,e.n=i,i=e),e.S.n=e.r,void 0!==e.r&&(e.r=void 0),e=r}t.s=i}function c(t){f.call(this,void 0),this.x=t,this.s=void 0,this.g=s-1,this.f=4}function v(t){var r=t.u;if(t.u=void 0,"function"==typeof r){n++;var o=i;i=void 0;try{r()}catch(e){throw t.f&=-2,t.f|=8,p(t),e}finally{i=o,e()}}}function p(t){for(var e=t.s;void 0!==e;e=e.n)e.S.U(e);t.x=void 0,t.s=void 0,v(t)}function d(t){if(i!==this)throw new Error("Out-of-order effect");h(this),i=t,this.f&=-2,8&this.f&&p(this),e()}function y(t){this.x=t,this.u=void 0,this.s=void 0,this.o=void 0,this.f=32}function b(t){var e=new y(t);return e.c(),e.d.bind(e)}f.prototype.h=function(){return!0},f.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)},f.prototype.U=function(t){var e=t.e,i=t.x;void 0!==e&&(e.x=i,t.e=void 0),void 0!==i&&(i.e=e,t.x=void 0),t===this.t&&(this.t=i)},f.prototype.subscribe=function(t){var e=this;return b((function(){var i=e.value,r=32&this.f;this.f&=-33;try{t(i)}finally{this.f|=r}}))},f.prototype.valueOf=function(){return this.value},f.prototype.toString=function(){return this.value+""},f.prototype.peek=function(){return this.v},Object.defineProperty(f.prototype,"value",{get:function(){var t=l(this);return void 0!==t&&(t.i=this.i),this.v},set:function(i){if(i!==this.v){o>100&&t(),this.v=i,this.i++,s++,n++;try{for(var r=this.t;void 0!==r;r=r.x)r.t.N()}finally{e()}}}}),(c.prototype=new f).h=function(){if(this.f&=-3,1&this.f)return!1;if(32==(36&this.f))return!0;if(this.f&=-5,this.g===s)return!0;if(this.g=s,this.f|=1,this.i>0&&!u(this))return this.f&=-2,!0;var t=i;try{a(this),i=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 i=t,h(this),this.f&=-2,!0},c.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)}f.prototype.S.call(this,t)},c.prototype.U=function(t){if(f.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)}},c.prototype.N=function(){if(!(2&this.f)){this.f|=6;for(var t=this.t;void 0!==t;t=t.x)t.t.N()}},c.prototype.peek=function(){if(this.h()||t(),16&this.f)throw this.v;return this.v},Object.defineProperty(c.prototype,"value",{get:function(){1&this.f&&t();var e=l(this);if(this.h(),void 0!==e&&(e.i=this.i),16&this.f)throw this.v;return this.v}}),y.prototype.c=function(){var t=this.S();try{8&this.f||void 0===this.x||(this.u=this.x())}finally{t()}},y.prototype.S=function(){1&this.f&&t(),this.f|=1,this.f&=-9,v(this),a(this),n++;var e=i;return i=this,d.bind(this,e)},y.prototype.N=function(){2&this.f||(this.f|=2,this.o=r,r=this)},y.prototype.d=function(){this.f|=8,1&this.f||p(this)},Symbol.observable||=Symbol("observable");var m=new FinalizationRegistry((t=>t.call?.())),g=(t,e,i,r,n,o)=>{return t&&(s=(t[Symbol.observable]?.()||t).subscribe?.(e,i,r),o=s&&(()=>s.unsubscribe?.())||t.set&&t.call?.(n,e)||(t.then?.((t=>(!n&&e(t),r?.())),i)||(async o=>{try{for await(o of t){if(n)return;e(o)}r?.()}catch(t){i?.(t)}})())&&(t=>n=1),m.register(t,o),o);var s},S=t=>t&&t[A],A=Symbol("signal-struct");function w(t,e){if(S(t)&&!e)return t;if(x(t)){const n=Object.create(e||Object.getPrototypeOf(t)),o={},s=Object.getOwnPropertyDescriptors(t);for(let t in s){let e=s[t];if(e.get){let i=o[t]=new c(e.get.bind(n));Object.defineProperty(n,t,{get:()=>i.value,set:e.set?.bind(n),configurable:!1,enumerable:!0})}else{let s=e.value,l=(r=s)&&!!(r[Symbol.observable]||r[Symbol.asyncIterator]||r.call&&r.set||r.subscribe||r.then),u=o[t]=(i=s)&&i.peek?s:new f(l?void 0:x(s)?Object.seal(w(s)):Array.isArray(s)?w(s):s);l&&g(s,(t=>u.value=t)),Object.defineProperty(n,t,{get:()=>u.value,set(t){if(x(t)){if(x(u.value))try{return void Object.assign(u.value,t)}catch(t){}u.value=Object.seal(w(t))}else Array.isArray(t)?u.value=w(t):u.value=t},enumerable:!0,configurable:!1})}}return Object.defineProperty(n,A,{configurable:!1,enumerable:!1,value:!0}),n}var i,r;return Array.isArray(t)&&!S(t[0])?t.map((t=>w(t))):t}function x(t){return t&&t.constructor===Object}w.isStruct=S;var O=(t,e,i,r=null)=>{let n,o,s,l=0,f=i.length,u=e.length,{remove:a,same:h,insert:c,replace:v}=O;for(;l<f&&l<u&&h(e[l],i[l]);)l++;for(;l<f&&l<u&&h(i[f-1],e[u-1]);)r=i[(--u,--f)];if(l==u)for(;l<f;)c(r,i[l++],t);else{for(n=e[l];l<f;)s=i[l++],o=n?n.nextSibling:r,h(n,s)?n=o:l<f&&h(i[l],o)?(v(n,s,t),n=o):c(n,s,t);for(;!h(n,r);)o=n.nextSibling,a(n,t),n=o}return i};O.same=(t,e)=>t==e,O.replace=(t,e,i)=>i.replaceChild(e,t),O.insert=(t,e,i)=>i.insertBefore(e,t),O.remove=(t,e)=>e.removeChild(t);var j=O,N={},E={},k={},W=t=>null===t?E:void 0===t?k:"number"==typeof t||t instanceof Number?N[t]||(N[t]=new Number(t)):"string"==typeof t||t instanceof String?N[t]||(N[t]=new String(t)):"boolean"==typeof t||t instanceof Boolean?N[t]||(N[t]=new Boolean(t)):t,$={},P={};$.with=(t,e,i)=>{z(t,w(M(t,e,"with")(i),i))},$.if=(t,e)=>{let i=document.createTextNode(""),r=[M(t,e,":if")],n=[t],o=t;for(;(o=t.nextElementSibling)&&o.hasAttribute(":else");)o.removeAttribute(":else"),(e=o.getAttribute(":if"))?(o.removeAttribute(":if"),o.remove(),n.push(o),r.push(M(t,e,":else :if"))):(o.remove(),n.push(o),r.push((()=>1)));return t.replaceWith(o=i),t=>{let e=r.findIndex((e=>e(t)));n[e]!=o&&((o[C]||o).replaceWith(o=n[e]||i),z(o,t))}};var C=Symbol(":each");$.each=(t,e)=>{let i=function(t){let e=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,i=t.match(/([\s\S]*?)\s+(?:in|of)\s+([\s\S]*)/);if(!i)return;let r=i[2].trim(),n=i[1].replace(/^\s*\(|\)\s*$/g,"").trim(),o=n.match(e);return o?[n.replace(e,"").trim(),o[1].trim(),r]:[n,"",r]}(e);if(!i)return R(new Error,t,e);const r=t[C]=document.createTextNode("");t.replaceWith(r);const n=M(t,i[2],":each"),o=t.getAttribute(":key"),s=o?M(null,o):null;t.removeAttribute(":key");const l=new WeakMap,f=new WeakMap;let u=[];return o=>{let a=n(o);a?"number"==typeof a?a=Array.from({length:a},((t,e)=>[e,e+1])):Array.isArray(a)?a=a.map(((t,e)=>[e+1,t])):"object"==typeof a?a=Object.entries(a):R(Error("Bad list value"),t,e,":each"):a=[];let h=[],c=[];for(let[e,r]of a){let n,u,a=s?.({[i[0]]:r,[i[1]]:e});("string"==typeof(v=a)||"boolean"==typeof v||"number"==typeof v)&&(a=W(a)),null==a?n=t.cloneNode(!0):(n=f.get(a))||f.set(a,n=t.cloneNode(!0)),h.push(n),null!=a&&(u=l.get(a))?u[i[0]]=r:(u=w({[i[0]]:r,[i[1]]:e},o),null!=a&&l.set(a,u)),c.push(u)}var v;j(r.parentNode,u,h,r),u=h;for(let t=0;t<h.length;t++)z(h[t],c[t])}},P.ref=(t,e,i)=>{i[e]=t},P.id=(t,e)=>{let i=M(t,e,":id");return e=>{return r=i(e),t.id=r||0===r?r:"";var r}},P.class=(t,e)=>{let i=M(t,e,":class"),r=t.className;return e=>{let n=i(e);t.className=r+typeof n=="string"?n:(Array.isArray(n)?n:Object.entries(n).map((([t,e])=>e?t:""))).filter(Boolean).join(" ")}},P.style=(t,e)=>{let i=M(t,e,":style"),r=t.getAttribute("style")||"";return r.endsWith(";")||(r+="; "),e=>{let n=i(e);if("string"==typeof n)t.setAttribute("style",r+n);else for(let e in n)t.style[e]=n[e]}},P.text=(t,e)=>{let i=M(t,e,":text");return e=>{let r=i(e);t.textContent=null==r?"":r}},P.value=(t,e)=>{let i,r,n=M(t,e,":value"),o="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,r=t.selectionEnd,t.setAttribute("value",t.value=null==e?"":e),i&&t.setSelectionRange(i,r)):"checkbox"===t.type?e=>(t.value=e?"on":"",D(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 t=>o(n(t))},P.on=(t,e)=>{let i=M(t,e,":on");return e=>{let r=i(e);for(let e in r)B(t,e,r[e]);return()=>{for(let e in r)L(t,e,r[e])}}};var U=Symbol("stop"),B=(t,e,i)=>{if(e.indexOf("..")<0)t.addEventListener(e,i);else{const r=e.split("..").map((t=>t.startsWith("on")?t.slice(2):t)),n=(e,o=0)=>{let s=l=>{t.removeEventListener(r[o],s),"function"!=typeof(e=e.call(t,l))&&(e=()=>{}),++o<r.length?n(e,o):i[U]||n(i)};t.addEventListener(r[o],s)};n(i)}},L=(t,e,i)=>{e.indexOf("..")>=0&&(i[U]=!0),t.removeEventListener(e,i)};P.data=(t,e)=>{let i=M(t,e,":data");return e=>{let r=i(e);for(let e in r)t.dataset[e]=r[e]}},P.aria=(t,e)=>{let i=M(t,e,":aria");return e=>(e=>{for(let i in e)D(t,"aria-"+F(i),null==e[i]?null:e[i]+"")})(i(e))},P[""]=(t,e)=>{let i=M(t,e,":");if(i)return e=>{let r=i(e);for(let e in r)D(t,F(e),r[e])}};var T=(t,e,i,r)=>{let n=r.startsWith("on")&&r.slice(2),o=M(t,e,":"+r);if(o)return n?e=>{let i=o(e);if(i)return B(t,n,i),()=>L(t,n,i)}:e=>D(t,r,o(e))},D=(t,e,i)=>{null==i||!1===i?t.removeAttribute(e):t.setAttribute(e,!0===i?"":"number"==typeof i||"string"==typeof i?i:"")},_={};function M(t,e,i){let r=_[e];if(!r){let n=/^[\n\s]*if.*\(.*\)/.test(e)||/^(let|const)\s/.test(e)?`(() => { ${e} })()`:e;try{r=_[e]=new Function("__scope",`with (__scope) { return ${n} };`)}catch(r){return R(r,t,e,i)}}return n=>{let o;try{o=r.call(t,n)}catch(r){return R(r,t,e,i)}return o}}function R(t,e,i,r){Object.assign(t,{element:e,expression:i}),console.warn(`∴ ${t.message}\n\n${r}=${i?`"${i}"\n\n`:""}`,e),setTimeout((()=>{throw t}),0)}function F(t){return t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(t=>"-"+t.toLowerCase()))}var I=new WeakMap;function z(t,i){if(!t.children)return;if(I.has(t)){let r=I.get(t);return function(t){if(n>0)return t();n++;try{t()}finally{e()}}((()=>Object.assign(r,i))),r}const r=w(i||{}),o=[],s=(t,e=t.parentNode)=>{for(let i in $){let n=":"+i;if(t.hasAttribute?.(n)){let s=t.getAttribute(n);if(t.removeAttribute(n),!s)continue;if(o.push($[i](t,s,r,i)),I.has(t)||t.parentNode!==e)return!1}}if(t.attributes)for(let i=0;i<t.attributes.length;){let n=t.attributes[i];if(":"!==n.name[0]){i++;continue}t.removeAttribute(n.name);let s=n.value;if(!s)continue;let l=n.name.slice(1).split(":");for(let i of l){let n=P[i]||T;if(o.push(n(t,s,r,i)),I.has(t)||t.parentNode!==e)return!1}}for(let e,i=0;e=t.children[i];i++)!1===s(e,t)&&i--};s(t);for(let t of o)if(t){let e;b((()=>{"function"==typeof e&&e(),e=t(r)}))}return Object.seal(r),I.set(t,r),r}var X=z;export{X as default};
1
+ function t(){throw new Error("Cycle detected")}function r(){if(o>1)o--;else{for(var t,e=!1;void 0!==n;){var r=n;for(n=void 0,s++;void 0!==r;){var i=r.o;if(r.o=void 0,r.f&=-3,!(8&r.f)&&f(r))try{r.c()}catch(r){e||(t=r,e=!0)}r=i}}if(s=0,o--,e)throw t}}var i=void 0,n=void 0,o=0,s=0,l=0;function a(t){if(void 0!==i){var e=t.n;if(void 0===e||e.t!==i)return i.s=e={i:0,S:t,p:void 0,n:i.s,t:i,e:void 0,x:void 0,r:e},t.n=e,32&i.f&&t.S(e),e;if(-1===e.i)return e.i=0,void 0!==e.p&&(e.p.n=e.n,void 0!==e.n&&(e.n.p=e.p),e.p=void 0,e.n=i.s,i.s.p=e,i.s=e),e}}function u(t){this.v=t,this.i=0,this.n=void 0,this.t=void 0}function f(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 c(t){for(var e=t.s;void 0!==e;e=e.n){var r=e.S.n;void 0!==r&&(e.r=r),e.S.n=e,e.i=-1}}function h(t){for(var e=t.s,r=void 0;void 0!==e;){var i=e.n;-1===e.i?(e.S.U(e),e.n=void 0):(void 0!==r&&(r.p=e),e.p=void 0,e.n=r,r=e),e.S.n=e.r,void 0!==e.r&&(e.r=void 0),e=i}t.s=r}function v(t){u.call(this,void 0),this.x=t,this.s=void 0,this.g=l-1,this.f=4}function p(t){var e=t.u;if(t.u=void 0,"function"==typeof e){o++;var n=i;i=void 0;try{e()}catch(e){throw t.f&=-2,t.f|=8,d(t),e}finally{i=n,r()}}}function d(t){for(var e=t.s;void 0!==e;e=e.n)e.S.U(e);t.x=void 0,t.s=void 0,p(t)}function y(t){if(i!==this)throw new Error("Out-of-order effect");h(this),i=t,this.f&=-2,8&this.f&&d(this),r()}function b(t){this.x=t,this.u=void 0,this.s=void 0,this.o=void 0,this.f=32}function m(t){var e=new b(t);return e.c(),e.d.bind(e)}u.prototype.h=function(){return!0},u.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)},u.prototype.U=function(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)},u.prototype.subscribe=function(t){var e=this;return m((function(){var r=e.value,i=32&this.f;this.f&=-33;try{t(r)}finally{this.f|=i}}))},u.prototype.valueOf=function(){return this.value},u.prototype.toString=function(){return this.value+""},u.prototype.peek=function(){return this.v},Object.defineProperty(u.prototype,"value",{get:function(){var t=a(this);return void 0!==t&&(t.i=this.i),this.v},set:function(e){if(e!==this.v){s>100&&t(),this.v=e,this.i++,l++,o++;try{for(var i=this.t;void 0!==i;i=i.x)i.t.N()}finally{r()}}}}),(v.prototype=new u).h=function(){if(this.f&=-3,1&this.f)return!1;if(32==(36&this.f))return!0;if(this.f&=-5,this.g===l)return!0;if(this.g=l,this.f|=1,this.i>0&&!f(this))return this.f&=-2,!0;var t=i;try{c(this),i=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 i=t,h(this),this.f&=-2,!0},v.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)}u.prototype.S.call(this,t)},v.prototype.U=function(t){if(u.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)}},v.prototype.N=function(){if(!(2&this.f)){this.f|=6;for(var t=this.t;void 0!==t;t=t.x)t.t.N()}},v.prototype.peek=function(){if(this.h()||t(),16&this.f)throw this.v;return this.v},Object.defineProperty(v.prototype,"value",{get:function(){1&this.f&&t();var e=a(this);if(this.h(),void 0!==e&&(e.i=this.i),16&this.f)throw this.v;return this.v}}),b.prototype.c=function(){var t=this.S();try{8&this.f||void 0===this.x||(this.u=this.x())}finally{t()}},b.prototype.S=function(){1&this.f&&t(),this.f|=1,this.f&=-9,p(this),c(this),o++;var e=i;return i=this,y.bind(this,e)},b.prototype.N=function(){2&this.f||(this.f|=2,this.o=n,n=this)},b.prototype.d=function(){this.f|=8,1&this.f||d(this)},Symbol.observable||=Symbol("observable");var g=new FinalizationRegistry((t=>t.call?.())),w=(t,e,r,i,n,o)=>{return t&&(s=(t[Symbol.observable]?.()||t).subscribe?.(e,r,i),o=s&&(()=>s.unsubscribe?.())||t.set&&t.call?.(n,e)||(t.then?.((t=>(!n&&e(t),i?.())),r)||(async o=>{try{for await(o of t){if(n)return;e(o)}i?.()}catch(t){r?.(t)}})())&&(t=>n=1),g.register(t,o),o);var s},A=t=>t&&t[S],S=Symbol("signal-struct");function x(t,e){if(A(t)&&!e)return t;if(O(t)){const n=Object.create(e||Object.getPrototypeOf(t)),o={},s=Object.getOwnPropertyDescriptors(t);for(let t in s){let e=s[t];if(e.get){let r=o[t]=new v(e.get.bind(n));Object.defineProperty(n,t,{get:()=>r.value,set:e.set?.bind(n),configurable:!1,enumerable:!0})}else{let s=e.value,l=(i=s)&&!!(i[Symbol.observable]||i[Symbol.asyncIterator]||i.call&&i.set||i.subscribe||i.then),a=o[t]=(r=s)&&r.peek?s:new u(l?void 0:O(s)?Object.seal(x(s)):Array.isArray(s)?x(s):s);l&&w(s,(t=>a.value=t)),Object.defineProperty(n,t,{get:()=>a.value,set(t){if(O(t)){if(O(a.value))try{return void Object.assign(a.value,t)}catch(t){}a.value=Object.seal(x(t))}else Array.isArray(t)?a.value=x(t):a.value=t},enumerable:!0,configurable:!1})}}return Object.defineProperty(n,S,{configurable:!1,enumerable:!1,value:!0}),n}var r,i;if(Array.isArray(t)&&!A(t[0]))for(let e=0;e<t.length;e++)t[e]=x(t[e]);return t}function O(t){return t&&t.constructor===Object}x.isStruct=A;var j=(t,e,r,i=null)=>{let n,o,s,l=0,a=r.length,u=e.length,{remove:f,same:c,insert:h,replace:v}=j;for(;l<a&&l<u&&c(e[l],r[l]);)l++;for(;l<a&&l<u&&c(r[a-1],e[u-1]);)i=r[(--u,--a)];if(l==u)for(;l<a;)h(i,r[l++],t);else{for(n=e[l];l<a;)s=r[l++],o=n?n.nextSibling:i,c(n,s)?n=o:l<a&&c(r[l],o)?(v(n,s,t),n=o):h(n,s,t);for(;!c(n,i);)o=n.nextSibling,f(n,t),n=o}return r};j.same=(t,e)=>t==e,j.replace=(t,e,r)=>r.replaceChild(e,t),j.insert=(t,e,r)=>r.insertBefore(e,t),j.remove=(t,e)=>e.removeChild(t);var N=j,E={},k={},P={},W=t=>null===t?k:void 0===t?P:"number"==typeof t||t instanceof Number?E[t]||(E[t]=new Number(t)):"string"==typeof t||t instanceof String?E[t]||(E[t]=new String(t)):"boolean"==typeof t||t instanceof Boolean?E[t]||(E[t]=new Boolean(t)):t,C={},D={};C.if=(t,e)=>{let r=document.createTextNode(""),i=[H(t,e,":if")],n=[t],o=t;for(;(o=t.nextElementSibling)&&o.hasAttribute(":else");)o.removeAttribute(":else"),(e=o.getAttribute(":if"))?(o.removeAttribute(":if"),o.remove(),n.push(o),i.push(H(t,e,":else :if"))):(o.remove(),n.push(o),i.push((()=>1)));return t.replaceWith(o=r),t=>{let e=i.findIndex((e=>e(t)));n[e]!=o&&((o[T]||o).replaceWith(o=n[e]||r),X(o,t))}},C.with=(t,e,r)=>{X(t,x(H(t,e,"with")(r),r))};var T=Symbol(":each");C.each=(t,e)=>{let r=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(!r)return I(new Error,t,e);const i=t[T]=document.createTextNode("");t.replaceWith(i);const n=H(t,r[2],":each"),o=t.getAttribute(":key"),s=o?H(null,o):null;t.removeAttribute(":key");const l=new WeakMap,a=new WeakMap;let u=[];return o=>{let f=n(o);f?"number"==typeof f?f=Array.from({length:f},((t,e)=>[e,e+1])):Array.isArray(f)?f=f.map(((t,e)=>[e+1,t])):"object"==typeof f?f=Object.entries(f):I(Error("Bad list value"),t,e,":each"):f=[];let c=[],h=[];for(let[e,i]of f){let n,u,f=s?.({[r[0]]:i,[r[1]]:e});("string"==typeof(v=f)||"boolean"==typeof v||"number"==typeof v)&&(f=W(f)),null==f?n=t.cloneNode(!0):(n=a.get(f))||a.set(f,n=t.cloneNode(!0)),c.push(n),null!=f&&(u=l.get(f))?u[r[0]]=i:(u=x({[r[0]]:i,[r[1]]:e},o),null!=f&&l.set(f,u)),h.push(u)}var v;N(i.parentNode,u,c,i),u=c;for(let t=0;t<c.length;t++)X(c[t],h[t])}},D.ref=(t,e,r)=>{r[e]=t},D.id=(t,e)=>{let r=H(t,e,":id");return e=>{return i=r(e),t.id=i||0===i?i:"";var i}},D.class=(t,e)=>{let r=H(t,e,":class"),i=t.className;return e=>{let n=r(e);t.className=i+typeof n=="string"?n:(Array.isArray(n)?n:Object.entries(n).map((([t,e])=>e?t:""))).filter(Boolean).join(" ")}},D.style=(t,e)=>{let r=H(t,e,":style"),i=t.getAttribute("style")||"";return i.endsWith(";")||(i+="; "),e=>{let n=r(e);if("string"==typeof n)t.setAttribute("style",i+n);else for(let e in n)t.style[e]=n[e]}},D.text=(t,e)=>{let r=H(t,e,":text");return e=>{let i=r(e);t.textContent=null==i?"":i}},D.data=(t,e)=>{let r=H(t,e,":data");return e=>{let i=r(e);for(let e in i)t.dataset[e]=i[e]}},D.aria=(t,e)=>{let r=H(t,e,":aria");return e=>(e=>{for(let r in e)_(t,"aria-"+q(r),null==e[r]?null:e[r]+"")})(r(e))},D[""]=(t,e)=>{let r=H(t,e,":");if(r)return e=>{let i=r(e);for(let e in i)_(t,q(e),i[e])}},D.value=(t,e)=>{let r,i,n=H(t,e,":value"),o="text"===t.type||""===t.type?e=>t.setAttribute("value",t.value=null==e?"":e):"TEXTAREA"===t.tagName||"text"===t.type||""===t.type?e=>(r=t.selectionStart,i=t.selectionEnd,t.setAttribute("value",t.value=null==e?"":e),r&&t.setSelectionRange(r,i)):"checkbox"===t.type?e=>(t.value=e?"on":"",_(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 t=>o(n(t))},D.on=(t,e)=>{let r=H(t,e,":on");return e=>{let i=r(e);for(let e in i)L(t,e,i[e]);return()=>{for(let e in i)B(t,e,i[e])}}};var U=(t,e,r,i)=>{let n=i.startsWith("on")&&i.slice(2),o=H(t,e,":"+i);if(o)return n?e=>{let r=o(e);if(r)return L(t,n,r),()=>B(t,n,r)}:e=>_(t,i,o(e))},$=Symbol("stop"),L=(t,e,r)=>{let i=e.split("..").map((t=>t.startsWith("on")?t.slice(2):t)),n={};if(i[0]=i[0].replace(/\.(\w+)?-?([\w]+)?/g,((e,i,o)=>(i=M[i])?([t,r]=i(t,r,n,o),""):"")),1==i.length)t.addEventListener(i[0],r,n);else{const e=(o,s=0)=>{let l=n=>{t.removeEventListener(i[s],l),"function"!=typeof(o=o.call(t,n))&&(o=()=>{}),++s<i.length?e(o,s):r[$]||e(r)};t.addEventListener(i[s],l,n)};e(r)}},B=(t,e,r)=>{e.indexOf("..")>=0&&(r[$]=!0),t.removeEventListener(e,r)},M={throttle(t,r,i,n){n=Number(n)||108;let o,s,l=()=>{o=!0,setTimeout((()=>{o=!1,s&&(r(e),s=!1,l())}))};return[t,t=>{if(o)return s=!0;r(t),l()}]},debounce(t,r,i,n){n=Number(n)||108;let o,s=()=>{o=null,r(e)};return[t,t=>{clearTimeout(o),o=setTimeout(s,n)}]},window:(t,e)=>[window,e],document:(t,e)=>[document,e],outside:(t,e)=>[t,r=>{t.contains(r.target)||!1!==r.target.isConnected&&(t.offsetWidth<1&&t.offsetHeight<1||e(r))}],prevent:(t,e)=>[t,t=>{t.preventDefault(),e(t)}],stop:(t,e)=>[t,t=>{t.stopPropagation(),e(t)}],self:(t,e)=>[t,r=>{r.target===t&&e(r)}],once:(t,e,r)=>(r.once=!0,[t,e]),passive:(t,e,r)=>(r.passive=!0,[t,e]),capture:(t,e,r)=>(r.capture=!0,[t,e])},R={ctrl:"Control Ctrl",shift:"Shift",alt:"Alt",meta:"Meta",cmd:"Meta",down:"ArrowDown",up:"ArrowUp",left:"ArrowLeft",right:"ArrowRight",arrowdown:"ArrowDown",arrowup:"ArrowUp",arrowleft:"ArrowLeft",arrowright:"ArrowRight",end:"End",home:"Home",pagedown:"PageDown",pageup:"PageUp",enter:"Enter",plus:"+",minus:"-",star:"*",slash:"/",period:".",equal:"=",underscore:"_",esc:"Escape",escape:"Escape",tab:"Tab",space:" ",backspace:"Backspace",delete:"Delete"};for(let t in R){let e=R[t];M[t]=(t,r,i,n)=>[t,t=>{t.key&&e.includes(t.key)&&r(t)}]}var _=(t,e,r)=>{null==r||!1===r?t.removeAttribute(e):t.setAttribute(e,!0===r?"":"number"==typeof r||"string"==typeof r?r:"")},F={};function H(t,e,r){let i=F[e];if(!i){let n=/^[\n\s]*if.*\(.*\)/.test(e)||/^(let|const)\s/.test(e)?`(() => { ${e} })()`:e;try{i=F[e]=new Function("__scope",`with (__scope) { return ${n} };`)}catch(i){return I(i,t,e,r)}}return n=>{let o;try{o=i.call(t,n)}catch(i){return I(i,t,e,r)}return o}}function I(t,e,r,i){Object.assign(t,{element:e,expression:r}),console.warn(`∴ ${t.message}\n\n${i}=${r?`"${r}"\n\n`:""}`,e),setTimeout((()=>{throw t}),0)}function q(t){return t.replace(/[A-Z\u00C0-\u00D6\u00D8-\u00DE]/g,(t=>"-"+t.toLowerCase()))}var z=new WeakMap;function X(t,e){if(!t.children)return;if(z.has(t)){let i=z.get(t);return function(t){if(o>0)return t();o++;try{t()}finally{r()}}((()=>Object.assign(i,e))),i}const i=x(e||{}),n=[],s=(t,e=t.parentNode)=>{for(let r in C){let o=":"+r;if(t.hasAttribute?.(o)){let s=t.getAttribute(o);if(t.removeAttribute(o),!s)continue;if(n.push(C[r](t,s,i,r)),z.has(t)||t.parentNode!==e)return!1}}if(t.attributes)for(let r=0;r<t.attributes.length;){let o=t.attributes[r];if(":"!==o.name[0]){r++;continue}t.removeAttribute(o.name);let s=o.value;if(!s)continue;let l=o.name.slice(1).split(":");for(let r of l){let o=D[r]||U;if(n.push(o(t,s,i,r)),z.has(t)||t.parentNode!==e)return!1}}for(let e,r=0;e=t.children[r];r++)!1===s(e,t)&&r--};s(t);for(let t of n)if(t){let e;m((()=>{"function"==typeof e&&e(),e=t(i)}))}return Object.seal(i),z.set(t,i),i}var Z=X;export{Z as default};
package/src/directives.js CHANGED
@@ -9,13 +9,9 @@ import p from 'primitive-pool'
9
9
  export const primary = {}, secondary = {}
10
10
 
11
11
 
12
- // :with must come before :if or :each or anyone else
13
- primary['with'] = (el, expr, rootState) => {
14
- let evaluate = parseExpr(el, expr, 'with')
15
- sprae(el, signalStruct(evaluate(rootState), rootState));
16
- }
17
-
18
12
  // :if is interchangeable with :each depending on order, :if :each or :each :if have different meanings
13
+ // as for :if :with - :if must init first, since it is lazy, to avoid initializing component ahead of time by :with
14
+ // we consider :with={x} :if={x} case insignificant
19
15
  primary['if'] = (el, expr) => {
20
16
  let holder = document.createTextNode(''),
21
17
  clauses = [parseExpr(el, expr, ':if')],
@@ -40,13 +36,20 @@ primary['if'] = (el, expr) => {
40
36
  return (state) => {
41
37
  let i = clauses.findIndex(f => f(state))
42
38
  if (els[i] != cur) {
43
- (cur[_each] || cur).replaceWith(cur = els[i] || holder);
39
+ ;(cur[_each] || cur).replaceWith(cur = els[i] || holder);
44
40
  // NOTE: it lazily initializes elements on insertion, it's safe to sprae multiple times
41
+ // but :if must come first to avoid preliminary caching
45
42
  sprae(cur, state);
46
43
  }
47
44
  }
48
45
  }
49
46
 
47
+ // :with must come before :each, but :if has primary importance
48
+ primary['with'] = (el, expr, rootState) => {
49
+ let evaluate = parseExpr(el, expr, 'with')
50
+ sprae(el, signalStruct(evaluate(rootState), rootState));
51
+ }
52
+
50
53
  const _each = Symbol(':each')
51
54
 
52
55
  // :each must init before :ref, :id or any others, since it defines scope
@@ -179,6 +182,32 @@ secondary['text'] = (el, expr) => {
179
182
  }
180
183
  }
181
184
 
185
+ secondary['data'] = (el, expr) => {
186
+ let evaluate = parseExpr(el, expr, ':data')
187
+
188
+ return ((state) => {
189
+ let value = evaluate(state)
190
+ for (let key in value) el.dataset[key] = value[key];
191
+ })
192
+ }
193
+
194
+ secondary['aria'] = (el, expr) => {
195
+ let evaluate = parseExpr(el, expr, ':aria')
196
+ const update = (value) => {
197
+ for (let key in value) attr(el, 'aria-' + dashcase(key), value[key] == null ? null : value[key] + '');
198
+ }
199
+ return ((state) => update(evaluate(state)))
200
+ }
201
+
202
+ // set props in-bulk or run effect
203
+ secondary[''] = (el, expr) => {
204
+ let evaluate = parseExpr(el, expr, ':')
205
+ if (evaluate) return (state) => {
206
+ let value = evaluate(state)
207
+ for (let key in value) attr(el, dashcase(key), value[key]);
208
+ }
209
+ }
210
+
182
211
  // connect expr to element value
183
212
  secondary['value'] = (el, expr) => {
184
213
  let evaluate = parseExpr(el, expr, ':value')
@@ -216,13 +245,38 @@ secondary['on'] = (el, expr) => {
216
245
  }
217
246
  }
218
247
 
248
+ // any unknown directive
249
+ export default (el, expr, state, name) => {
250
+ let evt = name.startsWith('on') && name.slice(2)
251
+ let evaluate = parseExpr(el, expr, ':'+name)
252
+
253
+ if (!evaluate) return
254
+
255
+ if (evt) return state => {
256
+ let value = evaluate(state)
257
+ if (value) {
258
+ addListener(el, evt, value)
259
+ return () => removeListener(el, evt, value)
260
+ }
261
+ }
262
+
263
+ return state => attr(el, name, evaluate(state))
264
+ }
265
+
219
266
  const _stop = Symbol('stop')
220
267
  const addListener = (el, evt, startFn) => {
221
- if (evt.indexOf('..')<0) el.addEventListener(evt, startFn);
268
+ // ona..onb
269
+ let evts = evt.split('..').map(e => e.startsWith('on') ? e.slice(2) : e),
270
+ opts = {}
222
271
 
223
- // sequences like `a..b`: fn
272
+ // onevt.debounce-108
273
+ evts[0] = evts[0].replace(
274
+ /\.(\w+)?-?([\w]+)?/g,
275
+ (match, mod, param) => (mod=mods[mod]) ? ([el, startFn] = mod(el, startFn, opts, param), '') : ''
276
+ );
277
+
278
+ if (evts.length == 1) el.addEventListener(evts[0], startFn, opts);
224
279
  else {
225
- const evts = evt.split('..').map(e => e.startsWith('on') ? e.slice(2) : e)
226
280
  const nextEvt = (fn, cur=0) => {
227
281
  let curListener = e => {
228
282
  el.removeEventListener(evts[cur], curListener)
@@ -230,7 +284,7 @@ const addListener = (el, evt, startFn) => {
230
284
  if (++cur < evts.length) nextEvt(fn, cur);
231
285
  else if (!startFn[_stop]) nextEvt(startFn); // update only if chain isn't stopped
232
286
  }
233
- el.addEventListener(evts[cur],curListener)
287
+ el.addEventListener(evts[cur], curListener, opts)
234
288
  }
235
289
  nextEvt(startFn)
236
290
  }
@@ -240,44 +294,67 @@ const removeListener = (el, evt, fn) => {
240
294
  el.removeEventListener(evt, fn);
241
295
  }
242
296
 
243
- secondary['data'] = (el, expr) => {
244
- let evaluate = parseExpr(el, expr, ':data')
245
-
246
- return ((state) => {
247
- let value = evaluate(state)
248
- for (let key in value) el.dataset[key] = value[key];
249
- })
250
- }
251
-
252
- secondary['aria'] = (el, expr) => {
253
- let evaluate = parseExpr(el, expr, ':aria')
254
- const update = (value) => {
255
- for (let key in value) attr(el, 'aria-' + dashcase(key), value[key] == null ? null : value[key] + '');
256
- }
257
- return ((state) => update(evaluate(state)))
258
- }
259
-
260
- // set props in-bulk or run effect
261
- secondary[''] = (el, expr) => {
262
- let evaluate = parseExpr(el, expr, ':')
263
- if (evaluate) return (state) => {
264
- let value = evaluate(state)
265
- for (let key in value) attr(el, dashcase(key), value[key]);
266
- }
267
- }
268
-
269
- // any other prop directive
270
- export default (el, expr, values, name) => {
271
- let evt = name.startsWith('on') && name.slice(2)
272
- let evaluate = parseExpr(el, expr, ':'+name)
273
- if (evaluate) return evt ? state => {
274
- let value = evaluate(state)
275
- if (value) {
276
- addListener(el, evt, value)
277
- return () => removeListener(el, evt, value)
297
+ // event modifiers
298
+ const mods = {
299
+ throttle(el, cb, opts, limit) {
300
+ limit = Number(limit) || 108
301
+ let pause, planned, block = () => {
302
+ pause = true
303
+ setTimeout(() => {
304
+ pause = false
305
+ // if event happened during blocked time, it schedules call by the end
306
+ if (planned) cb(e), planned = false, block();
307
+ })
278
308
  }
279
- }
280
- : state => attr(el, name, evaluate(state))
309
+ return [el, e => {
310
+ if (pause) return planned = true
311
+ cb(e); block();
312
+ }]
313
+ },
314
+
315
+ debounce(el, cb, opts, wait) {
316
+ wait = Number(wait) || 108;
317
+ let timeout, later = () => { timeout = null; cb(e) }
318
+ return [el, (e) => {
319
+ clearTimeout(timeout)
320
+ timeout = setTimeout(later, wait)
321
+ }]
322
+ },
323
+
324
+ window(el, cb) { return [window, cb] },
325
+ document(el, cb) { return [document, cb] },
326
+ outside(el, cb) {
327
+ return [el, (e) => {
328
+ if (el.contains(e.target)) return
329
+ if (e.target.isConnected === false) return
330
+ if (el.offsetWidth < 1 && el.offsetHeight < 1) return
331
+ cb(e)
332
+ }]
333
+ },
334
+ prevent(el, cb) { return [el, e => { e.preventDefault(); cb(e) } ]},
335
+ stop(el, cb) { return [el, e => { e.stopPropagation(); cb(e) } ]},
336
+ self(el, cb) { return [el, e => { e.target === el && cb(e) } ]},
337
+ once(el, cb, opts) { opts.once = true; return [el, cb] },
338
+ passive(el, cb, opts) { opts.passive = true; return [el, cb] },
339
+ capture(el, cb, opts) { opts.capture = true; return [el, cb] },
340
+ };
341
+ let keys = {
342
+ ctrl:'Control Ctrl',
343
+ shift:'Shift',
344
+ alt:'Alt',
345
+ meta:'Meta',cmd:'Meta',
346
+ down:'ArrowDown',up:'ArrowUp',left:'ArrowLeft',right:'ArrowRight',
347
+ end:'End',home:'Home',pagedown:'PageDown',pageup:'PageUp',
348
+ enter:'Enter',plus:'+',minus:'-',star:'*',slash:'/',period:'.',equal:'=',underscore:'_',
349
+ esc:'Escape',escape:'Escape',tab:'Tab',space:' ',
350
+ backspace:'Backspace', delete:'Delete'
351
+ }
352
+ for (let keyAttr in keys) {
353
+ let keyName = keys[keyAttr]
354
+ mods[keyAttr] = (el, cb, opts, extraKey) => [el, e => {
355
+ if (!e.key || !keyName.includes(e.key)) return
356
+ cb(e)
357
+ }]
281
358
  }
282
359
 
283
360
  // set attr
@@ -286,7 +363,6 @@ const attr = (el, name, v) => {
286
363
  else el.setAttribute(name, v === true ? '' : (typeof v === 'number' || typeof v === 'string') ? v : '')
287
364
  }
288
365
 
289
-
290
366
  let evaluatorMemo = {}
291
367
 
292
368
  // borrowed from alpine: https://github.com/alpinejs/alpine/blob/main/packages/alpinejs/src/evaluator.js#L61
@@ -323,7 +399,7 @@ function parseExpr(el, expression, dir) {
323
399
  }
324
400
  }
325
401
 
326
- export function exprError(error, element, expression, dir) {
402
+ function exprError(error, element, expression, dir) {
327
403
  Object.assign( error, { element, expression } )
328
404
  console.warn(`∴ ${error.message}\n\n${dir}=${ expression ? `"${expression}"\n\n` : '' }`, element)
329
405
  setTimeout(() => { throw error }, 0)
package/test/test.js CHANGED
@@ -148,7 +148,7 @@ test('text: core', async () => {
148
148
  is(el.outerHTML, `<div></div>`)
149
149
  })
150
150
 
151
- test('conditions: base', async () => {
151
+ test('if: base', async () => {
152
152
  let el = h`<p>
153
153
  <span :if="a==1">a</span>
154
154
  <span :else :if="a==2">b</span>
@@ -166,7 +166,7 @@ test('conditions: base', async () => {
166
166
  is(el.innerHTML, '<span>c</span>')
167
167
  })
168
168
 
169
- test('conditions: short with insertions', async () => {
169
+ test('if: short with insertions', async () => {
170
170
  let el = h`<p>
171
171
  <span :if="a==1" :text="'1:'+a"></span>
172
172
  <span :else :if="a==2" :text="'2:'+a"></span>
@@ -191,7 +191,7 @@ test('conditions: short with insertions', async () => {
191
191
  params.a = null
192
192
  })
193
193
 
194
- test('conditions: reactive values', async () => {
194
+ test('if: reactive values', async () => {
195
195
  let el = h`<p>
196
196
  <span :if="a==1" :text="'1:'+a"></span>
197
197
  <span :else :if="a==2" :text="'2:'+a"></span>
@@ -215,12 +215,27 @@ test('conditions: reactive values', async () => {
215
215
  is(el.innerHTML, '<span>4</span>')
216
216
  })
217
217
 
218
- test('conditions (#3): subsequent content is not abandoned', async () => {
218
+ test('if: (#3) subsequent content is not abandoned', async () => {
219
219
  let x = h`<x><y :if="!!y"></y><z :text="123"></z></x>`
220
220
  sprae(x, {y: false})
221
221
  is(x.outerHTML, `<x><z>123</z></x>`)
222
222
  })
223
223
 
224
+ test('if: + :with doesnt prevent secondary effects from happening', () => {
225
+ let el = h`<div><x :if="x" :with="{}" :text="x"></x></div>`
226
+ let state = sprae(el, {x:''})
227
+ is(el.innerHTML, ``)
228
+ state.x = '123'
229
+ is(el.innerHTML, `<x>123</x>`)
230
+
231
+ // NOTE: we ignore this case
232
+ // let el2 = h`<div><x :if="x" :with="{x:cond}" :text="x"></x></div>`
233
+ // let state2 = sprae(el, {cond:''})
234
+ // is(el2.innerHTML, ``)
235
+ // state2.cond = '123'
236
+ // is(el2.innerHTML, `<x>123</x>`)
237
+ })
238
+
224
239
  test('each: array', async () => {
225
240
  // FIXME: in some conspicuous reason jsdom fails to update text nodes somehow
226
241
  let el = h`<p>
@@ -409,12 +424,16 @@ test.todo('each: unmounted elements remove listeners', async () => {
409
424
 
410
425
  test('each: internal children get updated by state update, also: update by running again', () => {
411
426
  let el = h`<><x :each="item, idx in items" :text="item" :key="idx"></x></>`
412
- sprae(el, { items: [1,2,3] })
427
+ let state = sprae(el, { items: [1,2,3] })
413
428
  is(el.textContent, '123')
414
- let state = sprae(el, { items: [0,2,3] })
429
+ state.items = [2, 2, 3]
430
+ is(el.textContent, '223')
431
+ state = sprae(el, { items: [0,2,3] })
415
432
  is(el.textContent, '023')
433
+ // NOTE: this doesn't update items, since they're new array
434
+ console.log('set items')
416
435
  state.items[0] = 1
417
- state.items = state.items
436
+ state.items = [...state.items]
418
437
  is(el.textContent, '123')
419
438
  })
420
439
 
@@ -554,6 +573,57 @@ test('on: state changes between chain of events', e => {
554
573
  is(log, [1, 1.1, 2])
555
574
  })
556
575
 
576
+ test('on: once', e => {
577
+ // NOTE: if callback updates it's still rebound
578
+ let el = h`<x :onx.once="(e=>x&&log.push(this))" ></x>`
579
+ let log = []
580
+ let state = sprae(el, {log, x:1})
581
+ el.dispatchEvent(new window.Event('x'));
582
+ is(log, [el])
583
+ el.dispatchEvent(new window.Event('x'));
584
+ is(log, [el])
585
+ state.x = 2
586
+ el.dispatchEvent(new window.Event('x'));
587
+ el.dispatchEvent(new window.Event('x'));
588
+ is(log, [el])
589
+ })
590
+
591
+ test('on: capture, stop, prevent', e => {
592
+ let el = h`<x :onx.capture="e=>log.push(1)"><y :onx="e=>log.push(2)"></y></x>`
593
+ let state = sprae(el, {log:[]})
594
+ el.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
595
+ is(state.log, [1,2])
596
+
597
+ let el2 = h`<x :onx="e=>log.push(1)"><y :onx.stop="e=>log.push(2)"></y></x>`
598
+ let state2 = sprae(el2, {log:[]})
599
+ el2.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
600
+ is(state2.log, [2])
601
+ })
602
+
603
+ test('on: window, self', e => {
604
+ let el = h`<x :onx.self="e=>log.push(1)"><y :onx.window="e=>log.push(2)"></y></x>`
605
+ let state = sprae(el, {log:[]})
606
+ el.firstChild.dispatchEvent(new window.Event('x', {bubbles:true}));
607
+ is(state.log, [])
608
+ el.dispatchEvent(new window.Event('x', {bubbles:true}));
609
+ is(state.log, [1])
610
+ window.dispatchEvent(new window.Event('x', {bubbles:true}));
611
+ is(state.log, [1,2])
612
+ })
613
+
614
+ test('on: keys', e => {
615
+ let el = h`<x :onkeydown.enter="e=>log.push(1)"></x>`
616
+ let state = {log:[]}
617
+ sprae(el, state)
618
+ el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
619
+ is(state.log,[])
620
+ el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
621
+ el.dispatchEvent(new window.KeyboardEvent('keydown', { key: '' }));
622
+ is(state.log,[1])
623
+ el.dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' }));
624
+ is(state.log,[1,1])
625
+ })
626
+
557
627
  test('with: inline', () => {
558
628
  let el = h`<x :with="{foo:'bar'}"><y :text="foo + baz"></y></x>`
559
629
  let state = sprae(el, {baz: 'qux'})
package/todo.md CHANGED
@@ -39,4 +39,7 @@
39
39
  * [ ] frameworks benchmark
40
40
  * [ ] examples
41
41
  * [x] todomvc
42
- * [x] waveplay
42
+ * [x] waveplay
43
+ * [x] evt modifiers
44
+ * [x] once, capture, passive
45
+ * [x] ...rest