@adaas/are-html 0.0.21 → 0.0.23

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.
Files changed (154) hide show
  1. package/.conf/tsconfig.base.json +1 -0
  2. package/.conf/tsconfig.browser.json +1 -0
  3. package/.conf/tsconfig.node.json +1 -0
  4. package/dist/browser/index.d.mts +214 -3
  5. package/dist/browser/index.mjs +787 -201
  6. package/dist/browser/index.mjs.map +1 -1
  7. package/dist/node/{AreBinding.attribute-doUvtOjc.d.mts → AreBinding.attribute-BWzEIw6H.d.mts} +45 -0
  8. package/dist/node/{AreBinding.attribute-Bm5LlOyE.d.ts → AreBinding.attribute-GpT-5Qmf.d.ts} +45 -0
  9. package/dist/node/attributes/AreBinding.attribute.d.mts +1 -1
  10. package/dist/node/attributes/AreBinding.attribute.d.ts +1 -1
  11. package/dist/node/attributes/AreDirective.attribute.d.mts +1 -1
  12. package/dist/node/attributes/AreDirective.attribute.d.ts +1 -1
  13. package/dist/node/attributes/AreEvent.attribute.d.mts +1 -1
  14. package/dist/node/attributes/AreEvent.attribute.d.ts +1 -1
  15. package/dist/node/attributes/AreStatic.attribute.d.mts +1 -1
  16. package/dist/node/attributes/AreStatic.attribute.d.ts +1 -1
  17. package/dist/node/directives/AreDirectiveFor.directive.d.mts +55 -2
  18. package/dist/node/directives/AreDirectiveFor.directive.d.ts +55 -2
  19. package/dist/node/directives/AreDirectiveFor.directive.js +141 -12
  20. package/dist/node/directives/AreDirectiveFor.directive.js.map +1 -1
  21. package/dist/node/directives/AreDirectiveFor.directive.mjs +141 -12
  22. package/dist/node/directives/AreDirectiveFor.directive.mjs.map +1 -1
  23. package/dist/node/directives/AreDirectiveIf.directive.d.mts +1 -1
  24. package/dist/node/directives/AreDirectiveIf.directive.d.ts +1 -1
  25. package/dist/node/directives/AreDirectiveShow.directive.d.mts +1 -1
  26. package/dist/node/directives/AreDirectiveShow.directive.d.ts +1 -1
  27. package/dist/node/engine/AreHTML.compiler.d.mts +1 -1
  28. package/dist/node/engine/AreHTML.compiler.d.ts +1 -1
  29. package/dist/node/engine/AreHTML.compiler.js +4 -0
  30. package/dist/node/engine/AreHTML.compiler.js.map +1 -1
  31. package/dist/node/engine/AreHTML.compiler.mjs +4 -0
  32. package/dist/node/engine/AreHTML.compiler.mjs.map +1 -1
  33. package/dist/node/engine/AreHTML.constants.d.mts +33 -1
  34. package/dist/node/engine/AreHTML.constants.d.ts +33 -1
  35. package/dist/node/engine/AreHTML.constants.js +166 -0
  36. package/dist/node/engine/AreHTML.constants.js.map +1 -1
  37. package/dist/node/engine/AreHTML.constants.mjs +165 -1
  38. package/dist/node/engine/AreHTML.constants.mjs.map +1 -1
  39. package/dist/node/engine/AreHTML.context.d.mts +66 -0
  40. package/dist/node/engine/AreHTML.context.d.ts +66 -0
  41. package/dist/node/engine/AreHTML.context.js +98 -0
  42. package/dist/node/engine/AreHTML.context.js.map +1 -1
  43. package/dist/node/engine/AreHTML.context.mjs +98 -0
  44. package/dist/node/engine/AreHTML.context.mjs.map +1 -1
  45. package/dist/node/engine/AreHTML.interpreter.d.mts +3 -0
  46. package/dist/node/engine/AreHTML.interpreter.d.ts +3 -0
  47. package/dist/node/engine/AreHTML.interpreter.js +66 -10
  48. package/dist/node/engine/AreHTML.interpreter.js.map +1 -1
  49. package/dist/node/engine/AreHTML.interpreter.mjs +66 -10
  50. package/dist/node/engine/AreHTML.interpreter.mjs.map +1 -1
  51. package/dist/node/engine/AreHTML.lifecycle.d.mts +2 -2
  52. package/dist/node/engine/AreHTML.lifecycle.d.ts +2 -2
  53. package/dist/node/engine/AreHTML.lifecycle.js +32 -4
  54. package/dist/node/engine/AreHTML.lifecycle.js.map +1 -1
  55. package/dist/node/engine/AreHTML.lifecycle.mjs +32 -4
  56. package/dist/node/engine/AreHTML.lifecycle.mjs.map +1 -1
  57. package/dist/node/engine/AreHTML.tokenizer.d.mts +1 -1
  58. package/dist/node/engine/AreHTML.tokenizer.d.ts +1 -1
  59. package/dist/node/engine/AreHTML.tokenizer.js +7 -1
  60. package/dist/node/engine/AreHTML.tokenizer.js.map +1 -1
  61. package/dist/node/engine/AreHTML.tokenizer.mjs +7 -1
  62. package/dist/node/engine/AreHTML.tokenizer.mjs.map +1 -1
  63. package/dist/node/engine/AreHTML.transformer.d.mts +1 -1
  64. package/dist/node/engine/AreHTML.transformer.d.ts +1 -1
  65. package/dist/node/helpers/AreScheduler.helper.d.mts +39 -0
  66. package/dist/node/helpers/AreScheduler.helper.d.ts +39 -0
  67. package/dist/node/helpers/AreScheduler.helper.js +40 -0
  68. package/dist/node/helpers/AreScheduler.helper.js.map +1 -0
  69. package/dist/node/helpers/AreScheduler.helper.mjs +40 -0
  70. package/dist/node/helpers/AreScheduler.helper.mjs.map +1 -0
  71. package/dist/node/index.d.mts +4 -3
  72. package/dist/node/index.d.ts +4 -3
  73. package/dist/node/index.js +7 -0
  74. package/dist/node/index.mjs +1 -0
  75. package/dist/node/instructions/AddStaticHTML.instruction.d.mts +8 -0
  76. package/dist/node/instructions/AddStaticHTML.instruction.d.ts +8 -0
  77. package/dist/node/instructions/AddStaticHTML.instruction.js +31 -0
  78. package/dist/node/instructions/AddStaticHTML.instruction.js.map +1 -0
  79. package/dist/node/instructions/AddStaticHTML.instruction.mjs +24 -0
  80. package/dist/node/instructions/AddStaticHTML.instruction.mjs.map +1 -0
  81. package/dist/node/instructions/AreHTML.instructions.constants.d.mts +1 -0
  82. package/dist/node/instructions/AreHTML.instructions.constants.d.ts +1 -0
  83. package/dist/node/instructions/AreHTML.instructions.constants.js +1 -0
  84. package/dist/node/instructions/AreHTML.instructions.constants.js.map +1 -1
  85. package/dist/node/instructions/AreHTML.instructions.constants.mjs +1 -0
  86. package/dist/node/instructions/AreHTML.instructions.constants.mjs.map +1 -1
  87. package/dist/node/instructions/AreHTML.instructions.types.d.mts +9 -1
  88. package/dist/node/instructions/AreHTML.instructions.types.d.ts +9 -1
  89. package/dist/node/lib/AreDirective/AreDirective.component.d.mts +1 -1
  90. package/dist/node/lib/AreDirective/AreDirective.component.d.ts +1 -1
  91. package/dist/node/lib/AreDirective/AreDirective.types.d.mts +1 -1
  92. package/dist/node/lib/AreDirective/AreDirective.types.d.ts +1 -1
  93. package/dist/node/lib/AreHTML/AreHTML.tokenizer.d.mts +1 -1
  94. package/dist/node/lib/AreHTML/AreHTML.tokenizer.d.ts +1 -1
  95. package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.d.mts +1 -1
  96. package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.d.ts +1 -1
  97. package/dist/node/lib/AreHTMLNode/AreHTMLNode.d.mts +1 -1
  98. package/dist/node/lib/AreHTMLNode/AreHTMLNode.d.ts +1 -1
  99. package/dist/node/lib/AreHTMLNode/AreHTMLNode.js +51 -0
  100. package/dist/node/lib/AreHTMLNode/AreHTMLNode.js.map +1 -1
  101. package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs +51 -0
  102. package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs.map +1 -1
  103. package/dist/node/lib/AreRoot/AreRoot.component.js +1 -1
  104. package/dist/node/lib/AreRoot/AreRoot.component.js.map +1 -1
  105. package/dist/node/lib/AreRoot/AreRoot.component.mjs +1 -1
  106. package/dist/node/lib/AreRoot/AreRoot.component.mjs.map +1 -1
  107. package/dist/node/nodes/AreComment.d.mts +1 -1
  108. package/dist/node/nodes/AreComment.d.ts +1 -1
  109. package/dist/node/nodes/AreComponent.d.mts +1 -1
  110. package/dist/node/nodes/AreComponent.d.ts +1 -1
  111. package/dist/node/nodes/AreInterpolation.d.mts +1 -1
  112. package/dist/node/nodes/AreInterpolation.d.ts +1 -1
  113. package/dist/node/nodes/AreRoot.d.mts +1 -1
  114. package/dist/node/nodes/AreRoot.d.ts +1 -1
  115. package/dist/node/nodes/AreText.d.mts +1 -1
  116. package/dist/node/nodes/AreText.d.ts +1 -1
  117. package/examples/dashboard/concept.ts +1 -1
  118. package/examples/dashboard/dist/index.html +1 -1
  119. package/examples/dashboard/dist/{mq19zxz4-mnlgmd.js → mqiw5sqa-ypckmj.js} +2275 -1323
  120. package/examples/dashboard/src/concept.ts +3 -2
  121. package/examples/for-perf/concept.ts +45 -0
  122. package/examples/for-perf/containers/UI.container.ts +161 -0
  123. package/examples/for-perf/dist/index.html +270 -0
  124. package/examples/for-perf/dist/mqj1mpf2-z4aokv.js +15664 -0
  125. package/examples/for-perf/dist/mqj1mpff-4fr7mw.js +15664 -0
  126. package/examples/for-perf/public/index.html +270 -0
  127. package/examples/for-perf/src/components/PerfApp.component.ts +37 -0
  128. package/examples/for-perf/src/components/PerfControls.component.ts +34 -0
  129. package/examples/for-perf/src/components/PerfGrid.component.ts +225 -0
  130. package/examples/for-perf/src/components/PerfHeader.component.ts +34 -0
  131. package/examples/for-perf/src/components/PerfStats.component.ts +43 -0
  132. package/examples/for-perf/src/concept.ts +94 -0
  133. package/examples/jumpstart/dist/index.html +1 -1
  134. package/examples/jumpstart/dist/{mq7hqrxy-4kus50.js → mq7mgf58-vbf07e.js} +269 -91
  135. package/examples/signal-routing/dist/index.html +1 -1
  136. package/examples/signal-routing/dist/{mq7k53th-qiwy4x.js → mqiwo23h-bhcolu.js} +2090 -1430
  137. package/jest.config.ts +1 -0
  138. package/package.json +10 -9
  139. package/src/directives/AreDirectiveFor.directive.ts +233 -19
  140. package/src/engine/AreHTML.compiler.ts +13 -0
  141. package/src/engine/AreHTML.constants.ts +142 -0
  142. package/src/engine/AreHTML.context.ts +112 -0
  143. package/src/engine/AreHTML.interpreter.ts +114 -13
  144. package/src/engine/AreHTML.lifecycle.ts +91 -7
  145. package/src/engine/AreHTML.tokenizer.ts +30 -1
  146. package/src/helpers/AreScheduler.helper.ts +61 -0
  147. package/src/index.ts +1 -0
  148. package/src/instructions/AddStaticHTML.instruction.ts +23 -0
  149. package/src/instructions/AreHTML.instructions.constants.ts +1 -0
  150. package/src/instructions/AreHTML.instructions.types.ts +9 -0
  151. package/src/lib/AreHTMLNode/AreHTMLNode.ts +74 -0
  152. package/src/lib/AreRoot/AreRoot.component.ts +4 -1
  153. package/tests/StaticIsland.test.ts +115 -0
  154. package/tsconfig.json +1 -0
@@ -65,11 +65,56 @@ declare class AreStaticAttribute extends AreHTMLAttribute {
65
65
  }
66
66
 
67
67
  declare class AreHTMLNode extends AreNode {
68
+ /**
69
+ * When set, this node is a *static island* root: its entire inner subtree
70
+ * was detected (at tokenize time) to contain no ARE-reactive constructs —
71
+ * no interpolations, no dynamic attributes and only standard HTML tags.
72
+ *
73
+ * Instead of being exploded into one child AreNode per element/text node,
74
+ * the inner markup is preserved verbatim here and materialised in a single
75
+ * pass by the interpreter (browser-parsed `innerHTML` / cached `<template>`
76
+ * clone). The node's OWN attributes (including any dynamic `:`/`@`/`$` on
77
+ * the island root) still compile and stay reactive as usual.
78
+ */
79
+ protected _staticInnerHTML?: string;
68
80
  /**
69
81
  * Actual node type.
70
82
  * By default it's a tag name
71
83
  */
72
84
  get tag(): string;
85
+ /**
86
+ * The verbatim inner markup captured when this node was identified as a
87
+ * static island, or `undefined` for ordinary (per-node) nodes.
88
+ */
89
+ get staticInnerHTML(): string | undefined;
90
+ /**
91
+ * Whether this node is a static-island root (see `_staticInnerHTML`).
92
+ */
93
+ get isStaticIsland(): boolean;
94
+ /**
95
+ * Marks this node as a static-island root, capturing the verbatim inner
96
+ * markup to be materialised in one shot by the interpreter. Called by the
97
+ * tokenizer when the node's inner content is detected to be fully static.
98
+ */
99
+ markStatic(innerHTML: string): void;
100
+ /**
101
+ * Deep-clone the node. Overridden to carry over the static-island marker
102
+ * (`_staticInnerHTML`), which lives on AreHTMLNode and is therefore NOT
103
+ * copied by the base AreNode.clone(). Without this, cloning a directive
104
+ * template ($if/$for) that wraps a static island (e.g. `<span $if>★</span>`)
105
+ * would drop the captured inner markup and render an empty element. The
106
+ * base clone() recurses via each child's polymorphic clone(), so nested
107
+ * island children are preserved automatically through this override.
108
+ */
109
+ clone<T extends AreNode = AreNode>(this: T): T;
110
+ /**
111
+ * Clone the node while transferring its existing scope to the clone (used by
112
+ * the $if/$for directives to turn the original node into a lightweight group
113
+ * container). Overridden for the same reason as `clone()`: the static-island
114
+ * marker must survive so a directive applied to an island root keeps its
115
+ * inner markup.
116
+ */
117
+ cloneWithScope<T extends AreNode = AreNode>(this: T): T;
73
118
  /**
74
119
  * The static attributes defined for the node, which are typically used to represent static properties or characteristics of the node that do not change based on the context or state. These attributes are usually defined in the template and are not reactive.
75
120
  *
@@ -65,11 +65,56 @@ declare class AreStaticAttribute extends AreHTMLAttribute {
65
65
  }
66
66
 
67
67
  declare class AreHTMLNode extends AreNode {
68
+ /**
69
+ * When set, this node is a *static island* root: its entire inner subtree
70
+ * was detected (at tokenize time) to contain no ARE-reactive constructs —
71
+ * no interpolations, no dynamic attributes and only standard HTML tags.
72
+ *
73
+ * Instead of being exploded into one child AreNode per element/text node,
74
+ * the inner markup is preserved verbatim here and materialised in a single
75
+ * pass by the interpreter (browser-parsed `innerHTML` / cached `<template>`
76
+ * clone). The node's OWN attributes (including any dynamic `:`/`@`/`$` on
77
+ * the island root) still compile and stay reactive as usual.
78
+ */
79
+ protected _staticInnerHTML?: string;
68
80
  /**
69
81
  * Actual node type.
70
82
  * By default it's a tag name
71
83
  */
72
84
  get tag(): string;
85
+ /**
86
+ * The verbatim inner markup captured when this node was identified as a
87
+ * static island, or `undefined` for ordinary (per-node) nodes.
88
+ */
89
+ get staticInnerHTML(): string | undefined;
90
+ /**
91
+ * Whether this node is a static-island root (see `_staticInnerHTML`).
92
+ */
93
+ get isStaticIsland(): boolean;
94
+ /**
95
+ * Marks this node as a static-island root, capturing the verbatim inner
96
+ * markup to be materialised in one shot by the interpreter. Called by the
97
+ * tokenizer when the node's inner content is detected to be fully static.
98
+ */
99
+ markStatic(innerHTML: string): void;
100
+ /**
101
+ * Deep-clone the node. Overridden to carry over the static-island marker
102
+ * (`_staticInnerHTML`), which lives on AreHTMLNode and is therefore NOT
103
+ * copied by the base AreNode.clone(). Without this, cloning a directive
104
+ * template ($if/$for) that wraps a static island (e.g. `<span $if>★</span>`)
105
+ * would drop the captured inner markup and render an empty element. The
106
+ * base clone() recurses via each child's polymorphic clone(), so nested
107
+ * island children are preserved automatically through this override.
108
+ */
109
+ clone<T extends AreNode = AreNode>(this: T): T;
110
+ /**
111
+ * Clone the node while transferring its existing scope to the clone (used by
112
+ * the $if/$for directives to turn the original node into a lightweight group
113
+ * container). Overridden for the same reason as `clone()`: the static-island
114
+ * marker must survive so a directive applied to an island root keeps its
115
+ * inner markup.
116
+ */
117
+ cloneWithScope<T extends AreNode = AreNode>(this: T): T;
73
118
  /**
74
119
  * The static attributes defined for the node, which are typically used to represent static properties or characteristics of the node that do not change based on the context or state. These attributes are usually defined in the template and are not reactive.
75
120
  *
@@ -1,4 +1,4 @@
1
- export { A as AreBindingAttribute } from '../AreBinding.attribute-doUvtOjc.mjs';
1
+ export { A as AreBindingAttribute } from '../AreBinding.attribute-BWzEIw6H.mjs';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.mjs';
@@ -1,4 +1,4 @@
1
- export { A as AreBindingAttribute } from '../AreBinding.attribute-Bm5LlOyE.js';
1
+ export { A as AreBindingAttribute } from '../AreBinding.attribute-GpT-5Qmf.js';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.js';
@@ -1,4 +1,4 @@
1
- export { b as AreDirectiveAttribute } from '../AreBinding.attribute-doUvtOjc.mjs';
1
+ export { b as AreDirectiveAttribute } from '../AreBinding.attribute-BWzEIw6H.mjs';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.mjs';
@@ -1,4 +1,4 @@
1
- export { b as AreDirectiveAttribute } from '../AreBinding.attribute-Bm5LlOyE.js';
1
+ export { b as AreDirectiveAttribute } from '../AreBinding.attribute-GpT-5Qmf.js';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.js';
@@ -1,4 +1,4 @@
1
- export { c as AreEventAttribute } from '../AreBinding.attribute-doUvtOjc.mjs';
1
+ export { c as AreEventAttribute } from '../AreBinding.attribute-BWzEIw6H.mjs';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.mjs';
@@ -1,4 +1,4 @@
1
- export { c as AreEventAttribute } from '../AreBinding.attribute-Bm5LlOyE.js';
1
+ export { c as AreEventAttribute } from '../AreBinding.attribute-GpT-5Qmf.js';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.js';
@@ -1,4 +1,4 @@
1
- export { f as AreStaticAttribute } from '../AreBinding.attribute-doUvtOjc.mjs';
1
+ export { f as AreStaticAttribute } from '../AreBinding.attribute-BWzEIw6H.mjs';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.mjs';
@@ -1,4 +1,4 @@
1
- export { f as AreStaticAttribute } from '../AreBinding.attribute-Bm5LlOyE.js';
1
+ export { f as AreStaticAttribute } from '../AreBinding.attribute-GpT-5Qmf.js';
2
2
  import '@adaas/are';
3
3
  import '@adaas/a-concept';
4
4
  import '../lib/AreStyle/AreStyle.context.js';
@@ -1,13 +1,60 @@
1
1
  import { A_Scope } from '@adaas/a-concept';
2
2
  import { A_Logger } from '@adaas/a-utils/a-logger';
3
- import { a as AreDirective, b as AreDirectiveAttribute } from '../AreBinding.attribute-doUvtOjc.mjs';
3
+ import { a as AreDirective, b as AreDirectiveAttribute } from '../AreBinding.attribute-BWzEIw6H.mjs';
4
4
  import { AreStore, AreScene } from '@adaas/are';
5
5
  import '../lib/AreStyle/AreStyle.context.mjs';
6
6
 
7
7
  declare class AreDirectiveFor extends AreDirective {
8
+ /**
9
+ * Lists whose number of NEW item nodes is at or below this threshold render
10
+ * fully synchronously — byte-for-byte the previous behavior. Typical UIs
11
+ * (menus, small tables) are therefore completely unaffected; only genuinely
12
+ * large lists pay the (tiny) scheduling cost to keep the main thread responsive.
13
+ */
14
+ private static readonly SYNC_THRESHOLD;
15
+ /**
16
+ * Per-chunk time budget (ms). During a large-list render we mount item nodes
17
+ * until this much time has elapsed, then yield to the browser so it can paint
18
+ * and process input before the next chunk. ~16ms targets one animation frame.
19
+ */
20
+ private static readonly CHUNK_BUDGET_MS;
21
+ /**
22
+ * Per-attribute serialization state. A new update() that arrives while a
23
+ * chunked render of the SAME `$for` is still in flight does NOT start a second
24
+ * concurrent pass (which could interleave mutations on the shared children
25
+ * list); instead it marks `pending` and the in-flight run re-runs once more
26
+ * with the latest data when it finishes. This guarantees the children list is
27
+ * only ever mutated by one pass at a time and the final state always reflects
28
+ * the most recent store value.
29
+ */
30
+ private static readonly renderState;
8
31
  transform(attribute: AreDirectiveAttribute, scope: A_Scope, store: AreStore, scene: AreScene, logger: A_Logger, ...args: any[]): void;
9
32
  compile(attribute: AreDirectiveAttribute, store: AreStore, scene: AreScene, ...args: any[]): void;
10
- update(attribute: AreDirectiveAttribute, store: AreStore, scene: AreScene, ...args: any[]): void;
33
+ update(attribute: AreDirectiveAttribute, store: AreStore, scene: AreScene, ...args: any[]): void | Promise<void>;
34
+ /**
35
+ * Core of the `$for` update: re-diff the source array against the current
36
+ * children, reconcile reused/removed items, then mount the new ones (small
37
+ * lists synchronously, large lists time-sliced). Never called while another
38
+ * pass for the same `$for` is in flight (see `update`).
39
+ */
40
+ private performUpdate;
41
+ /**
42
+ * Repositions the item nodes' DOM elements so the rendered order matches the
43
+ * source array order. The keyed diff (steps 1–4) reuses existing nodes in
44
+ * place and mounts new ones at the end; without this pass a `prepend` or
45
+ * `shuffle` would leave reused rows where they were and pile new rows at the
46
+ * bottom. We walk the desired order RIGHT-TO-LEFT, keeping a `ref` pointer to
47
+ * the element each item must precede (starting at the `$for` anchor comment),
48
+ * and only call `insertBefore` when an element is not already in position —
49
+ * so a plain `append` (already-correct order) performs ZERO DOM moves.
50
+ */
51
+ private reconcileOrder;
52
+ /**
53
+ * Completes an update pass. If another update() arrived while a chunked
54
+ * render was streaming, run exactly one more pass now from the latest store
55
+ * value so the final DOM always reflects the most recent data.
56
+ */
57
+ private finishUpdate;
11
58
  /**
12
59
  * Walks the node's ancestor chain (inclusive) and reports whether the
13
60
  * whole path is currently active — i.e. the subtree is actually rendered
@@ -39,6 +86,12 @@ declare class AreDirectiveFor extends AreDirective {
39
86
  * Supports both plain key lookups and function-call expressions:
40
87
  * items → store.get('items')
41
88
  * filter(items) → store.get('filter')(store.get('items'))
89
+ *
90
+ * `contextScope` carries item-scoped variables introduced by an enclosing
91
+ * directive (e.g. the `row` of an outer `$for`). It is consulted BEFORE the
92
+ * store so a nested `$for="cell in row.cells"` resolves `row` from the
93
+ * parent iteration instead of looking for a (non-existent) top-level store
94
+ * key. Leading identifiers not present in the context fall back to the store.
42
95
  */
43
96
  private resolveArray;
44
97
  /**
@@ -1,13 +1,60 @@
1
1
  import { A_Scope } from '@adaas/a-concept';
2
2
  import { A_Logger } from '@adaas/a-utils/a-logger';
3
- import { a as AreDirective, b as AreDirectiveAttribute } from '../AreBinding.attribute-Bm5LlOyE.js';
3
+ import { a as AreDirective, b as AreDirectiveAttribute } from '../AreBinding.attribute-GpT-5Qmf.js';
4
4
  import { AreStore, AreScene } from '@adaas/are';
5
5
  import '../lib/AreStyle/AreStyle.context.js';
6
6
 
7
7
  declare class AreDirectiveFor extends AreDirective {
8
+ /**
9
+ * Lists whose number of NEW item nodes is at or below this threshold render
10
+ * fully synchronously — byte-for-byte the previous behavior. Typical UIs
11
+ * (menus, small tables) are therefore completely unaffected; only genuinely
12
+ * large lists pay the (tiny) scheduling cost to keep the main thread responsive.
13
+ */
14
+ private static readonly SYNC_THRESHOLD;
15
+ /**
16
+ * Per-chunk time budget (ms). During a large-list render we mount item nodes
17
+ * until this much time has elapsed, then yield to the browser so it can paint
18
+ * and process input before the next chunk. ~16ms targets one animation frame.
19
+ */
20
+ private static readonly CHUNK_BUDGET_MS;
21
+ /**
22
+ * Per-attribute serialization state. A new update() that arrives while a
23
+ * chunked render of the SAME `$for` is still in flight does NOT start a second
24
+ * concurrent pass (which could interleave mutations on the shared children
25
+ * list); instead it marks `pending` and the in-flight run re-runs once more
26
+ * with the latest data when it finishes. This guarantees the children list is
27
+ * only ever mutated by one pass at a time and the final state always reflects
28
+ * the most recent store value.
29
+ */
30
+ private static readonly renderState;
8
31
  transform(attribute: AreDirectiveAttribute, scope: A_Scope, store: AreStore, scene: AreScene, logger: A_Logger, ...args: any[]): void;
9
32
  compile(attribute: AreDirectiveAttribute, store: AreStore, scene: AreScene, ...args: any[]): void;
10
- update(attribute: AreDirectiveAttribute, store: AreStore, scene: AreScene, ...args: any[]): void;
33
+ update(attribute: AreDirectiveAttribute, store: AreStore, scene: AreScene, ...args: any[]): void | Promise<void>;
34
+ /**
35
+ * Core of the `$for` update: re-diff the source array against the current
36
+ * children, reconcile reused/removed items, then mount the new ones (small
37
+ * lists synchronously, large lists time-sliced). Never called while another
38
+ * pass for the same `$for` is in flight (see `update`).
39
+ */
40
+ private performUpdate;
41
+ /**
42
+ * Repositions the item nodes' DOM elements so the rendered order matches the
43
+ * source array order. The keyed diff (steps 1–4) reuses existing nodes in
44
+ * place and mounts new ones at the end; without this pass a `prepend` or
45
+ * `shuffle` would leave reused rows where they were and pile new rows at the
46
+ * bottom. We walk the desired order RIGHT-TO-LEFT, keeping a `ref` pointer to
47
+ * the element each item must precede (starting at the `$for` anchor comment),
48
+ * and only call `insertBefore` when an element is not already in position —
49
+ * so a plain `append` (already-correct order) performs ZERO DOM moves.
50
+ */
51
+ private reconcileOrder;
52
+ /**
53
+ * Completes an update pass. If another update() arrived while a chunked
54
+ * render was streaming, run exactly one more pass now from the latest store
55
+ * value so the final DOM always reflects the most recent data.
56
+ */
57
+ private finishUpdate;
11
58
  /**
12
59
  * Walks the node's ancestor chain (inclusive) and reports whether the
13
60
  * whole path is currently active — i.e. the subtree is actually rendered
@@ -39,6 +86,12 @@ declare class AreDirectiveFor extends AreDirective {
39
86
  * Supports both plain key lookups and function-call expressions:
40
87
  * items → store.get('items')
41
88
  * filter(items) → store.get('filter')(store.get('items'))
89
+ *
90
+ * `contextScope` carries item-scoped variables introduced by an enclosing
91
+ * directive (e.g. the `row` of an outer `$for`). It is consulted BEFORE the
92
+ * store so a nested `$for="cell in row.cells"` resolves `row` from the
93
+ * parent iteration instead of looking for a (non-existent) top-level store
94
+ * key. Leading identifiers not present in the context fall back to the store.
42
95
  */
43
96
  private resolveArray;
44
97
  /**
@@ -7,6 +7,8 @@ var AreDirective_component = require('@adaas/are-html/directive/AreDirective.com
7
7
  var AddComment_instruction = require('@adaas/are-html/instructions/AddComment.instruction');
8
8
  var AreDirective_context = require('@adaas/are-html/directive/AreDirective.context');
9
9
  var core = require('@adaas/a-frame/core');
10
+ var AreScheduler_helper = require('@adaas/are-html/helpers/AreScheduler.helper');
11
+ var context = require('@adaas/are-html/context');
10
12
 
11
13
  var __defProp = Object.defineProperty;
12
14
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -32,7 +34,8 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
32
34
  node.init();
33
35
  attribute.template = forTemplate;
34
36
  const { key, index, arrayExpr } = this.parseExpression(attribute.content);
35
- const array = this.resolveArray(store, arrayExpr, attribute.content);
37
+ const contextScope = attribute.owner.scope.resolve(AreDirective_context.AreDirectiveContext)?.scope || {};
38
+ const array = this.resolveArray(store, arrayExpr, attribute.content, contextScope);
36
39
  attribute.value = array;
37
40
  for (let i = 0; i < array.length; i++) {
38
41
  this.spawnItemNode(attribute.template, attribute.owner, key, index, array[i], i);
@@ -47,9 +50,28 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
47
50
  scene.unPlan(hostInstruction);
48
51
  }
49
52
  update(attribute, store, scene, ...args) {
53
+ let state = exports.AreDirectiveFor.renderState.get(attribute);
54
+ if (!state) {
55
+ state = { running: false, pending: false };
56
+ exports.AreDirectiveFor.renderState.set(attribute, state);
57
+ }
58
+ if (state.running) {
59
+ state.pending = true;
60
+ return;
61
+ }
62
+ return this.performUpdate(attribute, store, scene, state);
63
+ }
64
+ /**
65
+ * Core of the `$for` update: re-diff the source array against the current
66
+ * children, reconcile reused/removed items, then mount the new ones (small
67
+ * lists synchronously, large lists time-sliced). Never called while another
68
+ * pass for the same `$for` is in flight (see `update`).
69
+ */
70
+ performUpdate(attribute, store, scene, state) {
50
71
  const { key, index, arrayExpr, trackExpr } = this.parseExpression(attribute.content);
51
- const newArray = this.resolveArray(store, arrayExpr, attribute.content);
52
72
  const owner = attribute.owner;
73
+ const contextScope = owner.scope.resolve(AreDirective_context.AreDirectiveContext)?.scope || {};
74
+ const newArray = this.resolveArray(store, arrayExpr, attribute.content, contextScope);
53
75
  const currentChildren = [...owner.children];
54
76
  attribute.value = newArray;
55
77
  const attached = this.isAttached(owner);
@@ -63,13 +85,17 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
63
85
  childByKey.set(k, child);
64
86
  remaining.add(child);
65
87
  }
66
- const newOnes = [];
88
+ const toCreate = [];
89
+ const finalByKey = /* @__PURE__ */ new Map();
90
+ const orderedKeys = new Array(newArray.length);
67
91
  for (let i = 0; i < newArray.length; i++) {
68
92
  const item = newArray[i];
69
93
  const k = computeKey(item, i);
94
+ orderedKeys[i] = k;
70
95
  const existing = childByKey.get(k);
71
96
  if (existing) {
72
97
  remaining.delete(existing);
98
+ finalByKey.set(k, existing);
73
99
  let directiveContext = existing.scope.resolveFlat(AreDirective_context.AreDirectiveContext);
74
100
  if (!directiveContext) {
75
101
  directiveContext = new AreDirective_context.AreDirectiveContext(existing.aseid);
@@ -81,18 +107,88 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
81
107
  [index || "index"]: i
82
108
  };
83
109
  } else {
84
- const itemNode = this.spawnItemNode(attribute.template, owner, key, index, item, i);
85
- newOnes.push(itemNode);
110
+ toCreate.push({ item, idx: i, key: k });
86
111
  }
87
112
  }
88
113
  for (const child of remaining) {
89
114
  if (attached) child.unmount();
90
115
  owner.removeChild(child);
91
116
  }
92
- for (const child of newOnes) {
117
+ const createItem = (desc) => {
118
+ const child = this.spawnItemNode(attribute.template, owner, key, index, desc.item, desc.idx);
119
+ finalByKey.set(desc.key, child);
93
120
  child.transform();
94
121
  child.compile();
95
122
  if (attached) child.mount();
123
+ };
124
+ if (toCreate.length <= exports.AreDirectiveFor.SYNC_THRESHOLD) {
125
+ for (const desc of toCreate) createItem(desc);
126
+ if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);
127
+ return this.finishUpdate(attribute, store, scene, state);
128
+ }
129
+ state.running = true;
130
+ let cursor = 0;
131
+ const processChunk = () => {
132
+ try {
133
+ const start = AreScheduler_helper.AreSchedulerHelper.now();
134
+ while (cursor < toCreate.length) {
135
+ createItem(toCreate[cursor]);
136
+ cursor++;
137
+ if (AreScheduler_helper.AreSchedulerHelper.now() - start >= exports.AreDirectiveFor.CHUNK_BUDGET_MS) break;
138
+ }
139
+ } catch (error) {
140
+ state.running = false;
141
+ state.pending = false;
142
+ throw error;
143
+ }
144
+ if (cursor < toCreate.length) {
145
+ return new Promise((resolve) => {
146
+ AreScheduler_helper.AreSchedulerHelper.scheduleMacrotask(() => resolve(processChunk()));
147
+ });
148
+ }
149
+ if (attached) this.reconcileOrder(owner, orderedKeys, finalByKey);
150
+ return this.finishUpdate(attribute, store, scene, state);
151
+ };
152
+ return processChunk();
153
+ }
154
+ /**
155
+ * Repositions the item nodes' DOM elements so the rendered order matches the
156
+ * source array order. The keyed diff (steps 1–4) reuses existing nodes in
157
+ * place and mounts new ones at the end; without this pass a `prepend` or
158
+ * `shuffle` would leave reused rows where they were and pile new rows at the
159
+ * bottom. We walk the desired order RIGHT-TO-LEFT, keeping a `ref` pointer to
160
+ * the element each item must precede (starting at the `$for` anchor comment),
161
+ * and only call `insertBefore` when an element is not already in position —
162
+ * so a plain `append` (already-correct order) performs ZERO DOM moves.
163
+ */
164
+ reconcileOrder(owner, orderedKeys, finalByKey) {
165
+ const context$1 = owner.scope.resolve(context.AreHTMLEngineContext);
166
+ if (!context$1) return;
167
+ const anchor = context$1.getNodeElement(owner);
168
+ if (!anchor || !anchor.parentNode) return;
169
+ const parent = anchor.parentNode;
170
+ let ref = anchor;
171
+ for (let i = orderedKeys.length - 1; i >= 0; i--) {
172
+ const node = finalByKey.get(orderedKeys[i]);
173
+ if (!node) continue;
174
+ const element = context$1.getNodeElement(node);
175
+ if (!element || element.parentNode !== parent) continue;
176
+ if (element.nextSibling !== ref) {
177
+ parent.insertBefore(element, ref);
178
+ }
179
+ ref = element;
180
+ }
181
+ }
182
+ /**
183
+ * Completes an update pass. If another update() arrived while a chunked
184
+ * render was streaming, run exactly one more pass now from the latest store
185
+ * value so the final DOM always reflects the most recent data.
186
+ */
187
+ finishUpdate(attribute, store, scene, state) {
188
+ state.running = false;
189
+ if (state.pending) {
190
+ state.pending = false;
191
+ return this.performUpdate(attribute, store, scene, state);
96
192
  }
97
193
  }
98
194
  /**
@@ -173,13 +269,23 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
173
269
  * Supports both plain key lookups and function-call expressions:
174
270
  * items → store.get('items')
175
271
  * filter(items) → store.get('filter')(store.get('items'))
272
+ *
273
+ * `contextScope` carries item-scoped variables introduced by an enclosing
274
+ * directive (e.g. the `row` of an outer `$for`). It is consulted BEFORE the
275
+ * store so a nested `$for="cell in row.cells"` resolves `row` from the
276
+ * parent iteration instead of looking for a (non-existent) top-level store
277
+ * key. Leading identifiers not present in the context fall back to the store.
176
278
  */
177
- resolveArray(store, arrayExpr, fullContent) {
279
+ resolveArray(store, arrayExpr, fullContent, contextScope = {}) {
280
+ const getRoot = (rawKey) => {
281
+ const k = rawKey.replace(/\?$/, "");
282
+ return k in contextScope ? contextScope[k] : store.get(k);
283
+ };
178
284
  let result;
179
285
  const callMatch = arrayExpr.match(/^([^(]+)\((.+)\)$/);
180
286
  if (callMatch) {
181
287
  const fnName = callMatch[1].trim();
182
- const fn = store.get(fnName);
288
+ const fn = getRoot(fnName);
183
289
  if (typeof fn !== "function")
184
290
  throw new are.AreCompilerError({
185
291
  title: 'Invalid "for" Directive Function',
@@ -193,25 +299,25 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
193
299
  const stripped = arg.replace(/\?$/, "");
194
300
  if (stripped.includes(".")) {
195
301
  const parts = stripped.split(".").map((p) => p.replace(/\?$/, ""));
196
- let val = store.get(parts[0]);
302
+ let val = getRoot(parts[0]);
197
303
  for (let j = 1; j < parts.length; j++) {
198
304
  if (val == null) return void 0;
199
305
  val = val[parts[j]];
200
306
  }
201
307
  return val ?? void 0;
202
308
  }
203
- return store.get(stripped);
309
+ return getRoot(stripped);
204
310
  });
205
311
  result = fn(...resolvedArgs);
206
312
  } else if (arrayExpr.includes(".")) {
207
313
  const parts = arrayExpr.split(".").map((p) => p.replace(/\?$/, ""));
208
- result = store.get(parts[0]);
314
+ result = getRoot(parts[0]);
209
315
  for (let i = 1; i < parts.length; i++) {
210
316
  if (result == null) break;
211
317
  result = result[parts[i]];
212
318
  }
213
319
  } else {
214
- result = store.get(arrayExpr.replace(/\?$/, ""));
320
+ result = getRoot(arrayExpr);
215
321
  }
216
322
  if (result == null) return [];
217
323
  if (!Array.isArray(result))
@@ -253,6 +359,29 @@ exports.AreDirectiveFor = class AreDirectiveFor extends AreDirective_component.A
253
359
  return itemNode;
254
360
  }
255
361
  };
362
+ /**
363
+ * Lists whose number of NEW item nodes is at or below this threshold render
364
+ * fully synchronously — byte-for-byte the previous behavior. Typical UIs
365
+ * (menus, small tables) are therefore completely unaffected; only genuinely
366
+ * large lists pay the (tiny) scheduling cost to keep the main thread responsive.
367
+ */
368
+ exports.AreDirectiveFor.SYNC_THRESHOLD = 100;
369
+ /**
370
+ * Per-chunk time budget (ms). During a large-list render we mount item nodes
371
+ * until this much time has elapsed, then yield to the browser so it can paint
372
+ * and process input before the next chunk. ~16ms targets one animation frame.
373
+ */
374
+ exports.AreDirectiveFor.CHUNK_BUDGET_MS = 16;
375
+ /**
376
+ * Per-attribute serialization state. A new update() that arrives while a
377
+ * chunked render of the SAME `$for` is still in flight does NOT start a second
378
+ * concurrent pass (which could interleave mutations on the shared children
379
+ * list); instead it marks `pending` and the in-flight run re-runs once more
380
+ * with the latest data when it finishes. This guarantees the children list is
381
+ * only ever mutated by one pass at a time and the final state always reflects
382
+ * the most recent store value.
383
+ */
384
+ exports.AreDirectiveFor.renderState = /* @__PURE__ */ new WeakMap();
256
385
  __decorateClass([
257
386
  AreDirective_component.AreDirective.Transform,
258
387
  __decorateParam(0, aConcept.A_Inject(aConcept.A_Caller)),