sprae 2.6.1 → 2.7.1

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,7 +1,7 @@
1
1
  {
2
2
  "name": "sprae",
3
3
  "description": "DOM microhydration.",
4
- "version": "2.6.1",
4
+ "version": "2.7.1",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
7
7
  "type": "module",
package/readme.md CHANGED
@@ -261,7 +261,21 @@ This way, for example, _@preact/signals_ or _rxjs_ can be connected directly byp
261
261
 
262
262
  ## Hints
263
263
 
264
- **1.** Data supports signal values, which can be an alternative way to control template state:
264
+ **1.** To batch-update state (avoid multiple DOM changes), rerun sprae with new state:
265
+
266
+ ```html
267
+ <li :each="item, id in items" :key="id" :text="item"></li>
268
+
269
+ <script type="module">
270
+ sprae(el, {items: ['foo', 'bar', 'baz']})
271
+ // <li>foo</li><li>bar</li><li>baz</li>
272
+
273
+ sprae(el, {items: ['foo', 'qux']})
274
+ // <li>foo</li><li>qux</li>
275
+ </script>
276
+ ```
277
+
278
+ **2.** Data supports signal values, which can be an alternative way to control template state:
265
279
 
266
280
  ```html
267
281
  <div id="done" :text="loading ? 'loading' : result">...</div>
@@ -283,7 +297,7 @@ This way, for example, _@preact/signals_ or _rxjs_ can be connected directly byp
283
297
  </script>
284
298
  ```
285
299
 
286
- **2.** Data recognizes reactive values as inputs as well: _Promise_ / _Thenable_, _Observable_ / _Subscribable_, _AsyncIterable_ (etc., see [sube](https://github.com/dy/sube/blob/main/README.md)). This way, for example, _rxjs_ can be connected to template directly.
300
+ **3.** Data recognizes reactive values as inputs as well: _Promise_ / _Thenable_, _Observable_ / _Subscribable_, _AsyncIterable_ (etc., see [sube](https://github.com/dy/sube/blob/main/README.md)). This way, for example, _rxjs_ can be connected to template directly.
287
301
 
288
302
  ```html
289
303
  <div :text="clicks">#</div> clicks
@@ -297,7 +311,7 @@ This way, for example, _@preact/signals_ or _rxjs_ can be connected directly byp
297
311
  </script>
298
312
  ```
299
313
 
300
- **3.** Getters turn into computed values automatically:
314
+ **4.** Getters turn into computed values automatically (setters remain as is):
301
315
 
302
316
  ```html
303
317
  <div id="x-plus-y">
package/sprae.js CHANGED
@@ -6,23 +6,23 @@ function t() {
6
6
  if (!(n > 1)) {
7
7
  var i2, t2 = false;
8
8
  while (void 0 !== r) {
9
- var h = r;
9
+ var h2 = r;
10
10
  r = void 0;
11
11
  s++;
12
- while (void 0 !== h) {
13
- var o2 = h.o;
14
- h.o = void 0;
15
- h.f &= -3;
16
- if (!(8 & h.f) && d(h))
12
+ while (void 0 !== h2) {
13
+ var o2 = h2.o;
14
+ h2.o = void 0;
15
+ h2.f &= -3;
16
+ if (!(8 & h2.f) && d(h2))
17
17
  try {
18
- h.c();
19
- } catch (h2) {
18
+ h2.c();
19
+ } catch (h3) {
20
20
  if (!t2) {
21
- i2 = h2;
21
+ i2 = h3;
22
22
  t2 = true;
23
23
  }
24
24
  }
25
- h = o2;
25
+ h2 = o2;
26
26
  }
27
27
  }
28
28
  s = 0;
@@ -32,6 +32,16 @@ function t() {
32
32
  } else
33
33
  n--;
34
34
  }
35
+ function h(i2) {
36
+ if (n > 0)
37
+ return i2();
38
+ n++;
39
+ try {
40
+ return i2();
41
+ } finally {
42
+ t();
43
+ }
44
+ }
35
45
  var o = void 0;
36
46
  var r = void 0;
37
47
  var n = 0;
@@ -79,25 +89,25 @@ e.prototype.S = function(i2) {
79
89
  }
80
90
  };
81
91
  e.prototype.U = function(i2) {
82
- var t2 = i2.e, h = i2.x;
92
+ var t2 = i2.e, h2 = i2.x;
83
93
  if (void 0 !== t2) {
84
- t2.x = h;
94
+ t2.x = h2;
85
95
  i2.e = void 0;
86
96
  }
87
- if (void 0 !== h) {
88
- h.e = t2;
97
+ if (void 0 !== h2) {
98
+ h2.e = t2;
89
99
  i2.x = void 0;
90
100
  }
91
101
  if (i2 === this.t)
92
- this.t = h;
102
+ this.t = h2;
93
103
  };
94
104
  e.prototype.subscribe = function(i2) {
95
105
  var t2 = this;
96
106
  return b(function() {
97
- var h = t2.value, o2 = 32 & this.f;
107
+ var h2 = t2.value, o2 = 32 & this.f;
98
108
  this.f &= -33;
99
109
  try {
100
- i2(h);
110
+ i2(h2);
101
111
  } finally {
102
112
  this.f |= o2;
103
113
  }
@@ -117,11 +127,11 @@ Object.defineProperty(e.prototype, "value", { get: function() {
117
127
  if (void 0 !== i2)
118
128
  i2.i = this.i;
119
129
  return this.v;
120
- }, set: function(h) {
121
- if (h !== this.v) {
130
+ }, set: function(h2) {
131
+ if (h2 !== this.v) {
122
132
  if (s > 100)
123
133
  i();
124
- this.v = h;
134
+ this.v = h2;
125
135
  this.i++;
126
136
  f++;
127
137
  n++;
@@ -144,33 +154,33 @@ function d(i2) {
144
154
  }
145
155
  function c(i2) {
146
156
  for (var t2 = i2.s; void 0 !== t2; t2 = t2.n) {
147
- var h = t2.S.n;
148
- if (void 0 !== h)
149
- t2.r = h;
157
+ var h2 = t2.S.n;
158
+ if (void 0 !== h2)
159
+ t2.r = h2;
150
160
  t2.S.n = t2;
151
161
  t2.i = -1;
152
162
  }
153
163
  }
154
164
  function a(i2) {
155
- var t2 = i2.s, h = void 0;
165
+ var t2 = i2.s, h2 = void 0;
156
166
  while (void 0 !== t2) {
157
167
  var o2 = t2.n;
158
168
  if (-1 === t2.i) {
159
169
  t2.S.U(t2);
160
170
  t2.n = void 0;
161
171
  } else {
162
- if (void 0 !== h)
163
- h.p = t2;
172
+ if (void 0 !== h2)
173
+ h2.p = t2;
164
174
  t2.p = void 0;
165
- t2.n = h;
166
- h = t2;
175
+ t2.n = h2;
176
+ h2 = t2;
167
177
  }
168
178
  t2.S.n = t2.r;
169
179
  if (void 0 !== t2.r)
170
180
  t2.r = void 0;
171
181
  t2 = o2;
172
182
  }
173
- i2.s = h;
183
+ i2.s = h2;
174
184
  }
175
185
  function l(i2) {
176
186
  e.call(this, void 0);
@@ -259,14 +269,14 @@ function w(i2) {
259
269
  return new l(i2);
260
270
  }
261
271
  function y(i2) {
262
- var h = i2.u;
272
+ var h2 = i2.u;
263
273
  i2.u = void 0;
264
- if ("function" == typeof h) {
274
+ if ("function" == typeof h2) {
265
275
  n++;
266
276
  var r2 = o;
267
277
  o = void 0;
268
278
  try {
269
- h();
279
+ h2();
270
280
  } catch (t2) {
271
281
  i2.f &= -2;
272
282
  i2.f |= 8;
@@ -474,39 +484,9 @@ var primitive_pool_default = (key) => {
474
484
  };
475
485
 
476
486
  // src/directives.js
477
- var directives = {};
478
- var directives_default = (el, expr, values, name) => {
479
- let evt = name.startsWith("on") && name.slice(2);
480
- let evaluate = parseExpr(el, expr, ":" + name);
481
- if (evaluate)
482
- return evt ? (state) => {
483
- let value = evaluate(state);
484
- if (value) {
485
- addListener(el, evt, value);
486
- return () => removeListener(el, evt, value);
487
- }
488
- } : (state) => attr(el, name, evaluate(state));
489
- };
490
- var attr = (el, name, v2) => {
491
- if (v2 == null || v2 === false)
492
- el.removeAttribute(name);
493
- else
494
- el.setAttribute(name, v2 === true ? "" : typeof v2 === "number" || typeof v2 === "string" ? v2 : "");
495
- };
496
- directives[""] = (el, expr) => {
497
- let evaluate = parseExpr(el, expr, ":");
498
- if (evaluate)
499
- return (state) => {
500
- let value = evaluate(state);
501
- for (let key in value)
502
- attr(el, dashcase(key), value[key]);
503
- };
504
- };
505
- directives["with"] = (el, expr, rootState) => {
506
- let evaluate = parseExpr(el, expr, "with");
507
- sprae(el, signalStruct(evaluate(rootState), rootState));
508
- };
509
- directives["if"] = (el, expr) => {
487
+ var primary = {};
488
+ var secondary = {};
489
+ primary["if"] = (el, expr) => {
510
490
  let holder = document.createTextNode(""), clauses = [parseExpr(el, expr, ":if")], els = [el], cur = el;
511
491
  while (cur = el.nextElementSibling) {
512
492
  if (cur.hasAttribute(":else")) {
@@ -527,37 +507,25 @@ directives["if"] = (el, expr) => {
527
507
  return (state) => {
528
508
  let i2 = clauses.findIndex((f2) => f2(state));
529
509
  if (els[i2] != cur) {
510
+ ;
530
511
  (cur[_each] || cur).replaceWith(cur = els[i2] || holder);
531
512
  sprae(cur, state);
532
513
  }
533
514
  };
534
515
  };
535
- var _each = Symbol(":each");
536
- var _ref = Symbol(":ref");
537
- var _key = Symbol(":key");
538
- directives["ref"] = (el, expr, state) => {
539
- if (el.hasAttribute(":each")) {
540
- el[_ref] = expr;
541
- return;
542
- }
543
- ;
544
- state[expr] = el;
545
- };
546
- directives["key"] = (el, expr, state) => {
547
- if (el.hasAttribute(":each")) {
548
- el[_key] = expr;
549
- return;
550
- }
551
- ;
516
+ primary["with"] = (el, expr, rootState) => {
517
+ let evaluate = parseExpr(el, expr, "with");
518
+ sprae(el, signalStruct(evaluate(rootState), rootState));
552
519
  };
553
- directives["each"] = (tpl, expr) => {
520
+ var _each = Symbol(":each");
521
+ primary["each"] = (tpl, expr) => {
554
522
  let each = parseForExpression(expr);
555
523
  if (!each)
556
524
  return exprError(new Error(), tpl, expr);
557
525
  const holder = tpl[_each] = document.createTextNode("");
558
526
  tpl.replaceWith(holder);
559
- const evaluate = parseExpr(tpl, each.items, ":each");
560
- const keyExpr = tpl[_key] || tpl.getAttribute(":key");
527
+ const evaluate = parseExpr(tpl, each[2], ":each");
528
+ const keyExpr = tpl.getAttribute(":key");
561
529
  const itemKey = keyExpr ? parseExpr(null, keyExpr) : null;
562
530
  tpl.removeAttribute(":key");
563
531
  const scopes = /* @__PURE__ */ new WeakMap();
@@ -577,7 +545,7 @@ directives["each"] = (tpl, expr) => {
577
545
  exprError(Error("Bad list value"), tpl, expr, ":each", list);
578
546
  let newEls = [], elScopes = [];
579
547
  for (let [idx, item] of list) {
580
- let el, scope, key = itemKey?.({ [each.item]: item, [each.index || ""]: idx });
548
+ let el, scope, key = itemKey?.({ [each[0]]: item, [each[1]]: idx });
581
549
  if (isPrimitive(key))
582
550
  key = primitive_pool_default(key);
583
551
  if (key == null)
@@ -586,15 +554,11 @@ directives["each"] = (tpl, expr) => {
586
554
  (el = itemEls.get(key)) || itemEls.set(key, el = tpl.cloneNode(true));
587
555
  newEls.push(el);
588
556
  if (key == null || !(scope = scopes.get(key))) {
589
- scope = Object.create(state);
590
- scope[each.item] = item;
591
- if (each.index)
592
- scope[each.index] = idx;
593
- if (tpl[_ref])
594
- scope[tpl[_ref]] = el;
557
+ scope = signalStruct({ [each[0]]: item, [each[1]]: idx }, state);
595
558
  if (key != null)
596
559
  scopes.set(key, scope);
597
- }
560
+ } else
561
+ scope[each[0]] = item;
598
562
  elScopes.push(scope);
599
563
  }
600
564
  swap_inflate_default(holder.parentNode, curEls, newEls, holder);
@@ -611,24 +575,26 @@ function parseForExpression(expression) {
611
575
  let inMatch = expression.match(forAliasRE);
612
576
  if (!inMatch)
613
577
  return;
614
- let res = {};
615
- res.items = inMatch[2].trim();
578
+ let items = inMatch[2].trim();
616
579
  let item = inMatch[1].replace(stripParensRE, "").trim();
617
580
  let iteratorMatch = item.match(forIteratorRE);
618
- if (iteratorMatch) {
619
- res.item = item.replace(forIteratorRE, "").trim();
620
- res.index = iteratorMatch[1].trim();
621
- } else {
622
- res.item = item;
623
- }
624
- return res;
581
+ if (iteratorMatch)
582
+ return [
583
+ item.replace(forIteratorRE, "").trim(),
584
+ iteratorMatch[1].trim(),
585
+ items
586
+ ];
587
+ return [item, "", items];
625
588
  }
626
- directives["id"] = (el, expr) => {
589
+ secondary["ref"] = (el, expr, state) => {
590
+ state[expr] = el;
591
+ };
592
+ secondary["id"] = (el, expr) => {
627
593
  let evaluate = parseExpr(el, expr, ":id");
628
594
  const update = (v2) => el.id = v2 || v2 === 0 ? v2 : "";
629
595
  return (state) => update(evaluate(state));
630
596
  };
631
- directives["class"] = (el, expr) => {
597
+ secondary["class"] = (el, expr) => {
632
598
  let evaluate = parseExpr(el, expr, ":class");
633
599
  let initClassName = el.className;
634
600
  return (state) => {
@@ -636,7 +602,7 @@ directives["class"] = (el, expr) => {
636
602
  el.className = initClassName + typeof v2 === "string" ? v2 : (Array.isArray(v2) ? v2 : Object.entries(v2).map(([k, v3]) => v3 ? k : "")).filter(Boolean).join(" ");
637
603
  };
638
604
  };
639
- directives["style"] = (el, expr) => {
605
+ secondary["style"] = (el, expr) => {
640
606
  let evaluate = parseExpr(el, expr, ":style");
641
607
  let initStyle = el.getAttribute("style") || "";
642
608
  if (!initStyle.endsWith(";"))
@@ -650,14 +616,14 @@ directives["style"] = (el, expr) => {
650
616
  el.style[k] = v2[k];
651
617
  };
652
618
  };
653
- directives["text"] = (el, expr) => {
619
+ secondary["text"] = (el, expr) => {
654
620
  let evaluate = parseExpr(el, expr, ":text");
655
621
  return (state) => {
656
622
  let value = evaluate(state);
657
623
  el.textContent = value == null ? "" : value;
658
624
  };
659
625
  };
660
- directives["value"] = (el, expr) => {
626
+ secondary["value"] = (el, expr) => {
661
627
  let evaluate = parseExpr(el, expr, ":value");
662
628
  let from, to;
663
629
  let update = el.type === "text" || el.type === "" ? (value) => el.setAttribute("value", el.value = value == null ? "" : value) : el.tagName === "TEXTAREA" || el.type === "text" || el.type === "" ? (value) => (from = el.selectionStart, to = el.selectionEnd, el.setAttribute("value", el.value = value == null ? "" : value), from && el.setSelectionRange(from, to)) : el.type === "checkbox" ? (value) => (el.value = value ? "on" : "", attr(el, "checked", value)) : el.type === "select-one" ? (value) => {
@@ -668,7 +634,7 @@ directives["value"] = (el, expr) => {
668
634
  } : (value) => el.value = value;
669
635
  return (state) => update(evaluate(state));
670
636
  };
671
- directives["on"] = (el, expr) => {
637
+ secondary["on"] = (el, expr) => {
672
638
  let evaluate = parseExpr(el, expr, ":on");
673
639
  return (state) => {
674
640
  let listeners = evaluate(state);
@@ -707,7 +673,7 @@ var removeListener = (el, evt, fn) => {
707
673
  fn[_stop] = true;
708
674
  el.removeEventListener(evt, fn);
709
675
  };
710
- directives["data"] = (el, expr) => {
676
+ secondary["data"] = (el, expr) => {
711
677
  let evaluate = parseExpr(el, expr, ":data");
712
678
  return (state) => {
713
679
  let value = evaluate(state);
@@ -715,7 +681,7 @@ directives["data"] = (el, expr) => {
715
681
  el.dataset[key] = value[key];
716
682
  };
717
683
  };
718
- directives["aria"] = (el, expr) => {
684
+ secondary["aria"] = (el, expr) => {
719
685
  let evaluate = parseExpr(el, expr, ":aria");
720
686
  const update = (value) => {
721
687
  for (let key in value)
@@ -723,6 +689,33 @@ directives["aria"] = (el, expr) => {
723
689
  };
724
690
  return (state) => update(evaluate(state));
725
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]);
699
+ };
700
+ };
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
+ };
713
+ var attr = (el, name, v2) => {
714
+ if (v2 == null || v2 === false)
715
+ el.removeAttribute(name);
716
+ else
717
+ el.setAttribute(name, v2 === true ? "" : typeof v2 === "number" || typeof v2 === "string" ? v2 : "");
718
+ };
726
719
  var evaluatorMemo = {};
727
720
  function parseExpr(el, expression, dir) {
728
721
  let evaluate = evaluatorMemo[expression];
@@ -767,11 +760,26 @@ var memo = /* @__PURE__ */ new WeakMap();
767
760
  function sprae(container, values) {
768
761
  if (!container.children)
769
762
  return;
770
- if (memo.has(container))
771
- return memo.get(container);
763
+ if (memo.has(container)) {
764
+ let state2 = memo.get(container);
765
+ h(() => Object.assign(state2, values));
766
+ return state2;
767
+ }
772
768
  const state = signalStruct(values || {});
773
769
  const updates = [];
774
770
  const init = (el, parent = el.parentNode) => {
771
+ for (let name in primary) {
772
+ let attrName = ":" + name;
773
+ if (el.hasAttribute?.(attrName)) {
774
+ let expr = el.getAttribute(attrName);
775
+ el.removeAttribute(attrName);
776
+ if (!expr)
777
+ continue;
778
+ updates.push(primary[name](el, expr, state, name));
779
+ if (memo.has(el) || el.parentNode !== parent)
780
+ return false;
781
+ }
782
+ }
775
783
  if (el.attributes) {
776
784
  for (let i2 = 0; i2 < el.attributes.length; ) {
777
785
  let attr2 = el.attributes[i2];
@@ -785,9 +793,8 @@ function sprae(container, values) {
785
793
  continue;
786
794
  let attrNames = attr2.name.slice(1).split(":");
787
795
  for (let attrName of attrNames) {
788
- let dir = directives[attrName] || directives_default;
789
- updates.push(dir(el, expr, state, attrName) || (() => {
790
- }));
796
+ let dir = secondary[attrName] || directives_default;
797
+ updates.push(dir(el, expr, state, attrName));
791
798
  if (memo.has(el) || el.parentNode !== parent)
792
799
  return false;
793
800
  }
@@ -799,14 +806,15 @@ function sprae(container, values) {
799
806
  }
800
807
  };
801
808
  init(container);
802
- for (let update of updates) {
803
- let teardown;
804
- b(() => {
805
- if (typeof teardown === "function")
806
- teardown();
807
- teardown = update(state);
808
- });
809
- }
809
+ for (let update of updates)
810
+ if (update) {
811
+ let teardown;
812
+ b(() => {
813
+ if (typeof teardown === "function")
814
+ teardown();
815
+ teardown = update(state);
816
+ });
817
+ }
810
818
  Object.seal(state);
811
819
  memo.set(container, state);
812
820
  return state;
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)&&f(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 a(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 u(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){a.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)}a.prototype.h=function(){return!0},a.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)},a.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)},a.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}}))},a.prototype.valueOf=function(){return this.value},a.prototype.toString=function(){return this.value+""},a.prototype.peek=function(){return this.v},Object.defineProperty(a.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 a).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&&!f(this))return this.f&=-2,!0;var t=i;try{u(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)}a.prototype.S.call(this,t)},c.prototype.U=function(t){if(a.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),u(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[x],x=Symbol("signal-struct");function A(t,e){if(S(t)&&!e)return t;if(w(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),f=o[t]=(i=s)&&i.peek?s:new a(l?void 0:w(s)?Object.seal(A(s)):Array.isArray(s)?A(s):s);l&&g(s,(t=>f.value=t)),Object.defineProperty(n,t,{get:()=>f.value,set(t){if(w(t)){if(w(f.value))try{return void Object.assign(f.value,t)}catch(t){}f.value=Object.seal(A(t))}else Array.isArray(t)?f.value=A(t):f.value=t},enumerable:!0,configurable:!1})}}return Object.defineProperty(n,x,{configurable:!1,enumerable:!1,value:!0}),n}var i,r;return Array.isArray(t)&&!S(t[0])?t.map((t=>A(t))):t}function w(t){return t&&t.constructor===Object}A.isStruct=S;var O=(t,e,i,r=null)=>{let n,o,s,l=0,a=i.length,f=e.length,{remove:u,same:h,insert:c,replace:v}=O;for(;l<a&&l<f&&h(e[l],i[l]);)l++;for(;l<a&&l<f&&h(i[a-1],e[f-1]);)r=i[(--f,--a)];if(l==f)for(;l<a;)c(r,i[l++],t);else{for(n=e[l];l<a;)s=i[l++],o=n?n.nextSibling:r,h(n,s)?n=o:l<a&&h(i[l],o)?(v(n,s,t),n=o):c(n,s,t);for(;!h(n,r);)o=n.nextSibling,u(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=(t,e,i,r)=>{let n=r.startsWith("on")&&r.slice(2),o=R(t,e,":"+r);if(o)return n?e=>{let i=o(e);if(i)return D(t,n,i),()=>_(t,n,i)}:e=>C(t,r,o(e))},C=(t,e,i)=>{null==i||!1===i?t.removeAttribute(e):t.setAttribute(e,!0===i?"":"number"==typeof i||"string"==typeof i?i:"")};$[""]=(t,e)=>{let i=R(t,e,":");if(i)return e=>{let r=i(e);for(let e in r)C(t,I(e),r[e])}},$.with=(t,e,i)=>{X(t,A(R(t,e,"with")(i),i))},$.if=(t,e)=>{let i=document.createTextNode(""),r=[R(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(R(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[U]||o).replaceWith(o=n[e]||i),X(o,t))}};var U=Symbol(":each"),B=Symbol(":ref"),L=Symbol(":key");$.ref=(t,e,i)=>{t.hasAttribute(":each")?t[B]=e:i[e]=t},$.key=(t,e,i)=>{t.hasAttribute(":each")&&(t[L]=e)},$.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={};r.items=i[2].trim();let n=i[1].replace(/^\s*\(|\)\s*$/g,"").trim(),o=n.match(e);return o?(r.item=n.replace(e,"").trim(),r.index=o[1].trim()):r.item=n,r}(e);if(!i)return F(new Error,t,e);const r=t[U]=document.createTextNode("");t.replaceWith(r);const n=R(t,i.items,":each"),o=t[L]||t.getAttribute(":key"),s=o?R(null,o):null;t.removeAttribute(":key");const l=new WeakMap,a=new WeakMap;let f=[];return o=>{let u=n(o);u?"number"==typeof u?u=Array.from({length:u},((t,e)=>[e,e+1])):Array.isArray(u)?u=u.map(((t,e)=>[e+1,t])):"object"==typeof u?u=Object.entries(u):F(Error("Bad list value"),t,e,":each"):u=[];let h=[],c=[];for(let[e,r]of u){let n,f,u=s?.({[i.item]:r,[i.index||""]:e});("string"==typeof(v=u)||"boolean"==typeof v||"number"==typeof v)&&(u=W(u)),null==u?n=t.cloneNode(!0):(n=a.get(u))||a.set(u,n=t.cloneNode(!0)),h.push(n),null!=u&&(f=l.get(u))||(f=Object.create(o),f[i.item]=r,i.index&&(f[i.index]=e),t[B]&&(f[t[B]]=n),null!=u&&l.set(u,f)),c.push(f)}var v;j(r.parentNode,f,h,r),f=h;for(let t=0;t<h.length;t++)X(h[t],c[t])}},$.id=(t,e)=>{let i=R(t,e,":id");return e=>{return r=i(e),t.id=r||0===r?r:"";var r}},$.class=(t,e)=>{let i=R(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(" ")}},$.style=(t,e)=>{let i=R(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]}},$.text=(t,e)=>{let i=R(t,e,":text");return e=>{let r=i(e);t.textContent=null==r?"":r}},$.value=(t,e)=>{let i,r,n=R(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":"",C(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))},$.on=(t,e)=>{let i=R(t,e,":on");return e=>{let r=i(e);for(let e in r)D(t,e,r[e]);return()=>{for(let e in r)_(t,e,r[e])}}};var T=Symbol("stop"),D=(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[T]||n(i)};t.addEventListener(r[o],s)};n(i)}},_=(t,e,i)=>{e.indexOf("..")>=0&&(i[T]=!0),t.removeEventListener(e,i)};$.data=(t,e)=>{let i=R(t,e,":data");return e=>{let r=i(e);for(let e in r)t.dataset[e]=r[e]}},$.aria=(t,e)=>{let i=R(t,e,":aria");return e=>(e=>{for(let i in e)C(t,"aria-"+I(i),null==e[i]?null:e[i]+"")})(i(e))};var M={};function R(t,e,i){let r=M[e];if(!r){let n=/^[\n\s]*if.*\(.*\)/.test(e)||/^(let|const)\s/.test(e)?`(() => { ${e} })()`:e;try{r=M[e]=new Function("__scope",`with (__scope) { return ${n} };`)}catch(r){return F(r,t,e,i)}}return n=>{let o;try{o=r.call(t,n)}catch(r){return F(r,t,e,i)}return o}}function F(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 I(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))return z.get(t);const i=A(e||{}),r=[],n=(t,e=t.parentNode)=>{if(t.attributes)for(let n=0;n<t.attributes.length;){let o=t.attributes[n];if(":"!==o.name[0]){n++;continue}t.removeAttribute(o.name);let s=o.value;if(!s)continue;let l=o.name.slice(1).split(":");for(let n of l){let o=$[n]||P;if(r.push(o(t,s,i,n)||(()=>{})),z.has(t)||t.parentNode!==e)return!1}}for(let e,i=0;e=t.children[i];i++)!1===n(e,t)&&i--};n(t);for(let t of r){let e;b((()=>{"function"==typeof e&&e(),e=t(i)}))}return Object.seal(i),z.set(t,i),i}var Z=X;export{Z as default};
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};
package/src/core.js CHANGED
@@ -1,12 +1,16 @@
1
1
  import signalStruct from 'signal-struct';
2
- import defaultDirective, { directives } from './directives.js';
3
- import { effect } from '@preact/signals-core'
2
+ import defaultDirective, { primary, secondary } from './directives.js';
3
+ import { effect, batch } from '@preact/signals-core'
4
4
 
5
5
  // sprae element: apply directives
6
6
  const memo = new WeakMap
7
7
  export default function sprae(container, values) {
8
8
  if (!container.children) return
9
- if (memo.has(container)) return memo.get(container)
9
+ if (memo.has(container)) {
10
+ let state = memo.get(container)
11
+ batch(() => Object.assign(state, values))
12
+ return state
13
+ }
10
14
 
11
15
  // signalStruct returns values if it's signalStruct already
12
16
  const state = signalStruct(values || {});
@@ -14,6 +18,21 @@ export default function sprae(container, values) {
14
18
 
15
19
  // init directives on element
16
20
  const init = (el, parent=el.parentNode) => {
21
+ // init primary attributes first
22
+ for (let name in primary) {
23
+ let attrName = ':' + name
24
+ if (el.hasAttribute?.(attrName)) {
25
+ let expr = el.getAttribute(attrName)
26
+ el.removeAttribute(attrName)
27
+ if (!expr) continue
28
+ updates.push(primary[name](el, expr, state, name))
29
+
30
+ // stop if element was spraed by directive or skipped (detached)
31
+ if (memo.has(el) || el.parentNode !== parent) return false
32
+ }
33
+ }
34
+
35
+ // catch other attributes as secondary
17
36
  if (el.attributes) {
18
37
  for (let i = 0; i < el.attributes.length;) {
19
38
  let attr = el.attributes[i]
@@ -21,12 +40,13 @@ export default function sprae(container, values) {
21
40
  el.removeAttribute(attr.name)
22
41
  let expr = attr.value
23
42
  if (!expr) continue
43
+ // multiple attributes like :id:for=""
24
44
  let attrNames = attr.name.slice(1).split(':')
25
45
  for (let attrName of attrNames) {
26
- let dir = directives[attrName] || defaultDirective;
27
- updates.push(dir(el, expr, state, attrName) || (()=>{}));
46
+ let dir = secondary[attrName] || defaultDirective;
47
+ updates.push(dir(el, expr, state, attrName));
28
48
 
29
- // stop if element was spraed by directive or skipped
49
+ // stop if element was spraed by directive or skipped (detached)
30
50
  if (memo.has(el) || el.parentNode !== parent) return false
31
51
  }
32
52
  }
@@ -42,7 +62,7 @@ export default function sprae(container, values) {
42
62
 
43
63
  // call updates: subscribes directives to state;
44
64
  // state is created after inits because directives can extend init values (expose refs etc)
45
- for (let update of updates) {
65
+ for (let update of updates) if (update) {
46
66
  let teardown
47
67
  effect(() => {
48
68
  if (typeof teardown === 'function') teardown()
package/src/directives.js CHANGED
@@ -5,42 +5,14 @@ import signalStruct from 'signal-struct'
5
5
  import p from 'primitive-pool'
6
6
 
7
7
  // reserved directives - order matters!
8
- export const directives = {}
8
+ // primary initialized first by selector, secondary initialized by iterating attributes
9
+ export const primary = {}, secondary = {}
9
10
 
10
- // any-prop directives
11
- export default (el, expr, values, name) => {
12
- let evt = name.startsWith('on') && name.slice(2)
13
- let evaluate = parseExpr(el, expr, ':'+name)
14
- if (evaluate) return evt ? state => {
15
- let value = evaluate(state)
16
- if (value) {
17
- addListener(el, evt, value)
18
- return () => removeListener(el, evt, value)
19
- }
20
- }
21
- : state => attr(el, name, evaluate(state))
22
- }
23
-
24
- // set attr
25
- const attr = (el, name, v) => {
26
- if (v == null || v === false) el.removeAttribute(name)
27
- else el.setAttribute(name, v === true ? '' : (typeof v === 'number' || typeof v === 'string') ? v : '')
28
- }
29
11
 
30
- directives[''] = (el, expr) => {
31
- let evaluate = parseExpr(el, expr, ':')
32
- if (evaluate) return (state) => {
33
- let value = evaluate(state)
34
- for (let key in value) attr(el, dashcase(key), value[key]);
35
- }
36
- }
37
-
38
- directives['with'] = (el, expr, rootState) => {
39
- let evaluate = parseExpr(el, expr, 'with')
40
- sprae(el, signalStruct(evaluate(rootState), rootState));
41
- }
42
-
43
- directives['if'] = (el, expr) => {
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
15
+ primary['if'] = (el, expr) => {
44
16
  let holder = document.createTextNode(''),
45
17
  clauses = [parseExpr(el, expr, ':if')],
46
18
  els = [el], cur = el
@@ -64,30 +36,24 @@ directives['if'] = (el, expr) => {
64
36
  return (state) => {
65
37
  let i = clauses.findIndex(f => f(state))
66
38
  if (els[i] != cur) {
67
- (cur[_each] || cur).replaceWith(cur = els[i] || holder);
39
+ ;(cur[_each] || cur).replaceWith(cur = els[i] || holder);
68
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
69
42
  sprae(cur, state);
70
43
  }
71
44
  }
72
45
  }
73
46
 
74
- const _each = Symbol(':each'), _ref = Symbol(':ref'), _key = Symbol(':key')
75
-
76
- directives['ref'] = (el, expr, state) => {
77
- // make sure :ref is initialized after :each (return to avoid initializing as signal)
78
- if (el.hasAttribute(':each')) {el[_ref] = expr; return};
79
-
80
- // FIXME: wait for complex ref use-case
81
- // parseExpr(el, `__scope[${expr}]=this`, ':ref')(values)
82
- state[expr] = el;
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));
83
51
  }
84
52
 
85
- directives['key'] = (el, expr, state) => {
86
- // make sure :ref is initialized after :each (return to avoid initializing as signal)
87
- if (el.hasAttribute(':each')) {el[_key] = expr; return};
88
- }
53
+ const _each = Symbol(':each')
89
54
 
90
- directives['each'] = (tpl, expr) => {
55
+ // :each must init before :ref, :id or any others, since it defines scope
56
+ primary['each'] = (tpl, expr) => {
91
57
  let each = parseForExpression(expr);
92
58
  if (!each) return exprError(new Error, tpl, expr);
93
59
 
@@ -96,9 +62,9 @@ directives['each'] = (tpl, expr) => {
96
62
  const holder = tpl[_each] = document.createTextNode('')
97
63
  tpl.replaceWith(holder)
98
64
 
99
- const evaluate = parseExpr(tpl, each.items, ':each');
65
+ const evaluate = parseExpr(tpl, each[2], ':each');
100
66
 
101
- const keyExpr = tpl[_key] || tpl.getAttribute(':key');
67
+ const keyExpr = tpl.getAttribute(':key');
102
68
  const itemKey = keyExpr ? parseExpr(null, keyExpr) : null;
103
69
  tpl.removeAttribute(':key')
104
70
 
@@ -121,7 +87,7 @@ directives['each'] = (tpl, expr) => {
121
87
  let newEls = [], elScopes = []
122
88
 
123
89
  for (let [idx, item] of list) {
124
- let el, scope, key = itemKey?.({[each.item]: item, [each.index || '']: idx})
90
+ let el, scope, key = itemKey?.({[each[0]]: item, [each[1]]: idx})
125
91
  if (isPrimitive(key)) key = p(key); // singletonize key
126
92
 
127
93
  // we consider if data items are primitive, then nodes needn't be cached
@@ -132,13 +98,12 @@ directives['each'] = (tpl, expr) => {
132
98
  newEls.push(el)
133
99
 
134
100
  if (key == null || !(scope = scopes.get(key))) {
135
- scope = Object.create(state)
136
- scope[each.item] = item
137
- if (each.index) scope[each.index] = idx;
138
- // provide ref, if indicated
139
- if (tpl[_ref]) scope[tpl[_ref]] = el
101
+ scope = signalStruct({[each[0]]: item, [each[1]]:idx}, state)
140
102
  if (key != null) scopes.set(key, scope)
141
103
  }
104
+ // need to explicitly set item to update existing children's values
105
+ else scope[each[0]] = item
106
+
142
107
  elScopes.push(scope)
143
108
  }
144
109
 
@@ -162,28 +127,33 @@ function parseForExpression(expression) {
162
127
 
163
128
  if (!inMatch) return
164
129
 
165
- let res = {}
166
- res.items = inMatch[2].trim()
130
+ let items = inMatch[2].trim()
167
131
  let item = inMatch[1].replace(stripParensRE, '').trim()
168
132
  let iteratorMatch = item.match(forIteratorRE)
169
133
 
170
- if (iteratorMatch) {
171
- res.item = item.replace(forIteratorRE, '').trim()
172
- res.index = iteratorMatch[1].trim()
173
- } else {
174
- res.item = item
175
- }
134
+ if (iteratorMatch) return [
135
+ item.replace(forIteratorRE, '').trim(),
136
+ iteratorMatch[1].trim(),
137
+ items
138
+ ]
139
+
140
+ return [item, '', items]
141
+ }
176
142
 
177
- return res
143
+
144
+ secondary['ref'] = (el, expr, state) => {
145
+ // FIXME: wait for complex ref use-case
146
+ // parseExpr(el, `__scope[${expr}]=this`, ':ref')(values)
147
+ state[expr] = el;
178
148
  }
179
149
 
180
- directives['id'] = (el, expr) => {
150
+ secondary['id'] = (el, expr) => {
181
151
  let evaluate = parseExpr(el, expr, ':id')
182
152
  const update = v => el.id = v || v === 0 ? v : ''
183
153
  return (state) => update(evaluate(state))
184
154
  }
185
155
 
186
- directives['class'] = (el, expr) => {
156
+ secondary['class'] = (el, expr) => {
187
157
  let evaluate = parseExpr(el, expr, ':class')
188
158
  let initClassName = el.className
189
159
  return (state) => {
@@ -192,7 +162,7 @@ directives['class'] = (el, expr) => {
192
162
  }
193
163
  }
194
164
 
195
- directives['style'] = (el, expr) => {
165
+ secondary['style'] = (el, expr) => {
196
166
  let evaluate = parseExpr(el, expr, ':style')
197
167
  let initStyle = el.getAttribute('style') || ''
198
168
  if (!initStyle.endsWith(';')) initStyle += '; '
@@ -203,7 +173,7 @@ directives['style'] = (el, expr) => {
203
173
  }
204
174
  }
205
175
 
206
- directives['text'] = (el, expr) => {
176
+ secondary['text'] = (el, expr) => {
207
177
  let evaluate = parseExpr(el, expr, ':text')
208
178
 
209
179
  return (state) => {
@@ -213,7 +183,7 @@ directives['text'] = (el, expr) => {
213
183
  }
214
184
 
215
185
  // connect expr to element value
216
- directives['value'] = (el, expr) => {
186
+ secondary['value'] = (el, expr) => {
217
187
  let evaluate = parseExpr(el, expr, ':value')
218
188
 
219
189
  let from, to
@@ -237,7 +207,7 @@ directives['value'] = (el, expr) => {
237
207
  return (state) => update(evaluate(state))
238
208
  }
239
209
 
240
- directives['on'] = (el, expr) => {
210
+ secondary['on'] = (el, expr) => {
241
211
  let evaluate = parseExpr(el, expr, ':on')
242
212
 
243
213
  return (state) => {
@@ -273,7 +243,7 @@ const removeListener = (el, evt, fn) => {
273
243
  el.removeEventListener(evt, fn);
274
244
  }
275
245
 
276
- directives['data'] = (el, expr) => {
246
+ secondary['data'] = (el, expr) => {
277
247
  let evaluate = parseExpr(el, expr, ':data')
278
248
 
279
249
  return ((state) => {
@@ -282,7 +252,7 @@ directives['data'] = (el, expr) => {
282
252
  })
283
253
  }
284
254
 
285
- directives['aria'] = (el, expr) => {
255
+ secondary['aria'] = (el, expr) => {
286
256
  let evaluate = parseExpr(el, expr, ':aria')
287
257
  const update = (value) => {
288
258
  for (let key in value) attr(el, 'aria-' + dashcase(key), value[key] == null ? null : value[key] + '');
@@ -290,6 +260,35 @@ directives['aria'] = (el, expr) => {
290
260
  return ((state) => update(evaluate(state)))
291
261
  }
292
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)
281
+ }
282
+ }
283
+ : state => attr(el, name, evaluate(state))
284
+ }
285
+
286
+ // set attr
287
+ const attr = (el, name, v) => {
288
+ if (v == null || v === false) el.removeAttribute(name)
289
+ else el.setAttribute(name, v === true ? '' : (typeof v === 'number' || typeof v === 'string') ? v : '')
290
+ }
291
+
293
292
 
294
293
  let evaluatorMemo = {}
295
294
 
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>
@@ -404,7 +419,24 @@ test('each: keyed', async () => {
404
419
  })
405
420
 
406
421
  test.todo('each: unmounted elements remove listeners', async () => {
422
+ // let's hope they get removed without memory leaks :')
423
+ })
424
+
425
+ test('each: internal children get updated by state update, also: update by running again', () => {
426
+ let el = h`<><x :each="item, idx in items" :text="item" :key="idx"></x></>`
427
+ sprae(el, { items: [1,2,3] })
428
+ is(el.textContent, '123')
429
+ let state = sprae(el, { items: [0,2,3] })
430
+ is(el.textContent, '023')
431
+ state.items[0] = 1
432
+ state.items = state.items
433
+ is(el.textContent, '123')
434
+ })
407
435
 
436
+ test('each: :id and others must receive value from context', () => {
437
+ let el = h`<div><x :id="idx" :each="item, idx in items"></x></div>`
438
+ sprae(el, {items:[1,2,3]})
439
+ is(el.innerHTML,`<x id="1"></x><x id="2"></x><x id="3"></x>`)
408
440
  })
409
441
 
410
442
  test('on: base', () => {
package/todo.md CHANGED
@@ -34,8 +34,8 @@
34
34
  * [x] implement :with
35
35
  * [x] update :value without losing focus / position
36
36
  * ~~[x] run tiredown if element got removed from condition or loop (free memory)~~ no need just make sure no refs to elements stored
37
- * [ ] `sprae(el, newState)` can update element's state directly (as batch!?) -> must be tested against repeats in directives
38
- * [ ] :if :ref, :if :with -> context setters must come first always
37
+ * [x] `sprae(el, newState)` can update element's state directly (as batch!?) -> must be tested against repeats in directives
38
+ * [x] :if :ref, :if :with -> context setters must come first always
39
39
  * [ ] frameworks benchmark
40
40
  * [ ] examples
41
41
  * [x] todomvc