sprae 2.7.1 → 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.1",
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
  }
@@ -623,6 +624,31 @@ secondary["text"] = (el, expr) => {
623
624
  el.textContent = value == null ? "" : value;
624
625
  };
625
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
+ };
626
652
  secondary["value"] = (el, expr) => {
627
653
  let evaluate = parseExpr(el, expr, ":value");
628
654
  let from, to;
@@ -646,16 +672,35 @@ secondary["on"] = (el, expr) => {
646
672
  };
647
673
  };
648
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
+ };
649
690
  var _stop = Symbol("stop");
650
691
  var addListener = (el, evt, startFn) => {
651
- if (evt.indexOf("..") < 0)
652
- 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);
653
699
  else {
654
- const evts = evt.split("..").map((e2) => e2.startsWith("on") ? e2.slice(2) : e2);
655
700
  const nextEvt = (fn, cur = 0) => {
656
- let curListener = (e2) => {
701
+ let curListener = (e3) => {
657
702
  el.removeEventListener(evts[cur], curListener);
658
- if (typeof (fn = fn.call(el, e2)) !== "function")
703
+ if (typeof (fn = fn.call(el, e3)) !== "function")
659
704
  fn = () => {
660
705
  };
661
706
  if (++cur < evts.length)
@@ -663,7 +708,7 @@ var addListener = (el, evt, startFn) => {
663
708
  else if (!startFn[_stop])
664
709
  nextEvt(startFn);
665
710
  };
666
- el.addEventListener(evts[cur], curListener);
711
+ el.addEventListener(evts[cur], curListener, opts);
667
712
  };
668
713
  nextEvt(startFn);
669
714
  }
@@ -673,43 +718,123 @@ var removeListener = (el, evt, fn) => {
673
718
  fn[_stop] = true;
674
719
  el.removeEventListener(evt, fn);
675
720
  };
676
- secondary["data"] = (el, expr) => {
677
- let evaluate = parseExpr(el, expr, ":data");
678
- return (state) => {
679
- let value = evaluate(state);
680
- for (let key in value)
681
- el.dataset[key] = value[key];
682
- };
683
- };
684
- secondary["aria"] = (el, expr) => {
685
- let evaluate = parseExpr(el, expr, ":aria");
686
- const update = (value) => {
687
- for (let key in value)
688
- attr(el, "aria-" + dashcase(key), value[key] == null ? null : value[key] + "");
689
- };
690
- return (state) => update(evaluate(state));
691
- };
692
- secondary[""] = (el, expr) => {
693
- let evaluate = parseExpr(el, expr, ":");
694
- if (evaluate)
695
- return (state) => {
696
- let value = evaluate(state);
697
- for (let key in value)
698
- 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
+ });
699
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
+ }
700
796
  };
701
- var directives_default = (el, expr, values, name) => {
702
- let evt = name.startsWith("on") && name.slice(2);
703
- let evaluate = parseExpr(el, expr, ":" + name);
704
- if (evaluate)
705
- return evt ? (state) => {
706
- let value = evaluate(state);
707
- if (value) {
708
- addListener(el, evt, value);
709
- return () => removeListener(el, evt, value);
710
- }
711
- } : (state) => attr(el, name, evaluate(state));
712
- };
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
+ }
713
838
  var attr = (el, name, v2) => {
714
839
  if (v2 == null || v2 === false)
715
840
  el.removeAttribute(name);
@@ -723,16 +848,16 @@ function parseExpr(el, expression, dir) {
723
848
  let rightSideSafeExpression = /^[\n\s]*if.*\(.*\)/.test(expression) || /^(let|const)\s/.test(expression) ? `(() => { ${expression} })()` : expression;
724
849
  try {
725
850
  evaluate = evaluatorMemo[expression] = new Function(`__scope`, `with (__scope) { return ${rightSideSafeExpression} };`);
726
- } catch (e2) {
727
- return exprError(e2, el, expression, dir);
851
+ } catch (e3) {
852
+ return exprError(e3, el, expression, dir);
728
853
  }
729
854
  }
730
855
  return (state) => {
731
856
  let result;
732
857
  try {
733
858
  result = evaluate.call(el, state);
734
- } catch (e2) {
735
- return exprError(e2, el, expression, dir);
859
+ } catch (e3) {
860
+ return exprError(e3, el, expression, dir);
736
861
  }
737
862
  return result;
738
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={};$.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))}},$.with=(t,e,i)=>{z(t,w(M(t,e,"with")(i),i))};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
@@ -182,6 +182,32 @@ secondary['text'] = (el, expr) => {
182
182
  }
183
183
  }
184
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
+
185
211
  // connect expr to element value
186
212
  secondary['value'] = (el, expr) => {
187
213
  let evaluate = parseExpr(el, expr, ':value')
@@ -219,13 +245,38 @@ secondary['on'] = (el, expr) => {
219
245
  }
220
246
  }
221
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
+
222
266
  const _stop = Symbol('stop')
223
267
  const addListener = (el, evt, startFn) => {
224
- 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 = {}
271
+
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
+ );
225
277
 
226
- // sequences like `a..b`: fn
278
+ if (evts.length == 1) el.addEventListener(evts[0], startFn, opts);
227
279
  else {
228
- const evts = evt.split('..').map(e => e.startsWith('on') ? e.slice(2) : e)
229
280
  const nextEvt = (fn, cur=0) => {
230
281
  let curListener = e => {
231
282
  el.removeEventListener(evts[cur], curListener)
@@ -233,7 +284,7 @@ const addListener = (el, evt, startFn) => {
233
284
  if (++cur < evts.length) nextEvt(fn, cur);
234
285
  else if (!startFn[_stop]) nextEvt(startFn); // update only if chain isn't stopped
235
286
  }
236
- el.addEventListener(evts[cur],curListener)
287
+ el.addEventListener(evts[cur], curListener, opts)
237
288
  }
238
289
  nextEvt(startFn)
239
290
  }
@@ -243,44 +294,67 @@ const removeListener = (el, evt, fn) => {
243
294
  el.removeEventListener(evt, fn);
244
295
  }
245
296
 
246
- secondary['data'] = (el, expr) => {
247
- let evaluate = parseExpr(el, expr, ':data')
248
-
249
- return ((state) => {
250
- let value = evaluate(state)
251
- for (let key in value) el.dataset[key] = value[key];
252
- })
253
- }
254
-
255
- secondary['aria'] = (el, expr) => {
256
- let evaluate = parseExpr(el, expr, ':aria')
257
- const update = (value) => {
258
- for (let key in value) attr(el, 'aria-' + dashcase(key), value[key] == null ? null : value[key] + '');
259
- }
260
- return ((state) => update(evaluate(state)))
261
- }
262
-
263
- // set props in-bulk or run effect
264
- secondary[''] = (el, expr) => {
265
- let evaluate = parseExpr(el, expr, ':')
266
- if (evaluate) return (state) => {
267
- let value = evaluate(state)
268
- for (let key in value) attr(el, dashcase(key), value[key]);
269
- }
270
- }
271
-
272
- // any other prop directive
273
- export default (el, expr, values, name) => {
274
- let evt = name.startsWith('on') && name.slice(2)
275
- let evaluate = parseExpr(el, expr, ':'+name)
276
- if (evaluate) return evt ? state => {
277
- let value = evaluate(state)
278
- if (value) {
279
- addListener(el, evt, value)
280
- 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
+ })
281
308
  }
282
- }
283
- : 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
+ }]
284
358
  }
285
359
 
286
360
  // set attr
@@ -289,7 +363,6 @@ const attr = (el, name, v) => {
289
363
  else el.setAttribute(name, v === true ? '' : (typeof v === 'number' || typeof v === 'string') ? v : '')
290
364
  }
291
365
 
292
-
293
366
  let evaluatorMemo = {}
294
367
 
295
368
  // borrowed from alpine: https://github.com/alpinejs/alpine/blob/main/packages/alpinejs/src/evaluator.js#L61
@@ -326,7 +399,7 @@ function parseExpr(el, expression, dir) {
326
399
  }
327
400
  }
328
401
 
329
- export function exprError(error, element, expression, dir) {
402
+ function exprError(error, element, expression, dir) {
330
403
  Object.assign( error, { element, expression } )
331
404
  console.warn(`∴ ${error.message}\n\n${dir}=${ expression ? `"${expression}"\n\n` : '' }`, element)
332
405
  setTimeout(() => { throw error }, 0)
package/test/test.js CHANGED
@@ -424,12 +424,16 @@ test.todo('each: unmounted elements remove listeners', async () => {
424
424
 
425
425
  test('each: internal children get updated by state update, also: update by running again', () => {
426
426
  let el = h`<><x :each="item, idx in items" :text="item" :key="idx"></x></>`
427
- sprae(el, { items: [1,2,3] })
427
+ let state = sprae(el, { items: [1,2,3] })
428
428
  is(el.textContent, '123')
429
- 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] })
430
432
  is(el.textContent, '023')
433
+ // NOTE: this doesn't update items, since they're new array
434
+ console.log('set items')
431
435
  state.items[0] = 1
432
- state.items = state.items
436
+ state.items = [...state.items]
433
437
  is(el.textContent, '123')
434
438
  })
435
439
 
@@ -569,6 +573,57 @@ test('on: state changes between chain of events', e => {
569
573
  is(log, [1, 1.1, 2])
570
574
  })
571
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
+
572
627
  test('with: inline', () => {
573
628
  let el = h`<x :with="{foo:'bar'}"><y :text="foo + baz"></y></x>`
574
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