@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.
- package/.conf/tsconfig.base.json +1 -0
- package/.conf/tsconfig.browser.json +1 -0
- package/.conf/tsconfig.node.json +1 -0
- package/dist/browser/index.d.mts +214 -3
- package/dist/browser/index.mjs +787 -201
- package/dist/browser/index.mjs.map +1 -1
- package/dist/node/{AreBinding.attribute-doUvtOjc.d.mts → AreBinding.attribute-BWzEIw6H.d.mts} +45 -0
- package/dist/node/{AreBinding.attribute-Bm5LlOyE.d.ts → AreBinding.attribute-GpT-5Qmf.d.ts} +45 -0
- package/dist/node/attributes/AreBinding.attribute.d.mts +1 -1
- package/dist/node/attributes/AreBinding.attribute.d.ts +1 -1
- package/dist/node/attributes/AreDirective.attribute.d.mts +1 -1
- package/dist/node/attributes/AreDirective.attribute.d.ts +1 -1
- package/dist/node/attributes/AreEvent.attribute.d.mts +1 -1
- package/dist/node/attributes/AreEvent.attribute.d.ts +1 -1
- package/dist/node/attributes/AreStatic.attribute.d.mts +1 -1
- package/dist/node/attributes/AreStatic.attribute.d.ts +1 -1
- package/dist/node/directives/AreDirectiveFor.directive.d.mts +55 -2
- package/dist/node/directives/AreDirectiveFor.directive.d.ts +55 -2
- package/dist/node/directives/AreDirectiveFor.directive.js +141 -12
- package/dist/node/directives/AreDirectiveFor.directive.js.map +1 -1
- package/dist/node/directives/AreDirectiveFor.directive.mjs +141 -12
- package/dist/node/directives/AreDirectiveFor.directive.mjs.map +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.d.mts +1 -1
- package/dist/node/directives/AreDirectiveIf.directive.d.ts +1 -1
- package/dist/node/directives/AreDirectiveShow.directive.d.mts +1 -1
- package/dist/node/directives/AreDirectiveShow.directive.d.ts +1 -1
- package/dist/node/engine/AreHTML.compiler.d.mts +1 -1
- package/dist/node/engine/AreHTML.compiler.d.ts +1 -1
- package/dist/node/engine/AreHTML.compiler.js +4 -0
- package/dist/node/engine/AreHTML.compiler.js.map +1 -1
- package/dist/node/engine/AreHTML.compiler.mjs +4 -0
- package/dist/node/engine/AreHTML.compiler.mjs.map +1 -1
- package/dist/node/engine/AreHTML.constants.d.mts +33 -1
- package/dist/node/engine/AreHTML.constants.d.ts +33 -1
- package/dist/node/engine/AreHTML.constants.js +166 -0
- package/dist/node/engine/AreHTML.constants.js.map +1 -1
- package/dist/node/engine/AreHTML.constants.mjs +165 -1
- package/dist/node/engine/AreHTML.constants.mjs.map +1 -1
- package/dist/node/engine/AreHTML.context.d.mts +66 -0
- package/dist/node/engine/AreHTML.context.d.ts +66 -0
- package/dist/node/engine/AreHTML.context.js +98 -0
- package/dist/node/engine/AreHTML.context.js.map +1 -1
- package/dist/node/engine/AreHTML.context.mjs +98 -0
- package/dist/node/engine/AreHTML.context.mjs.map +1 -1
- package/dist/node/engine/AreHTML.interpreter.d.mts +3 -0
- package/dist/node/engine/AreHTML.interpreter.d.ts +3 -0
- package/dist/node/engine/AreHTML.interpreter.js +66 -10
- package/dist/node/engine/AreHTML.interpreter.js.map +1 -1
- package/dist/node/engine/AreHTML.interpreter.mjs +66 -10
- package/dist/node/engine/AreHTML.interpreter.mjs.map +1 -1
- package/dist/node/engine/AreHTML.lifecycle.d.mts +2 -2
- package/dist/node/engine/AreHTML.lifecycle.d.ts +2 -2
- package/dist/node/engine/AreHTML.lifecycle.js +32 -4
- package/dist/node/engine/AreHTML.lifecycle.js.map +1 -1
- package/dist/node/engine/AreHTML.lifecycle.mjs +32 -4
- package/dist/node/engine/AreHTML.lifecycle.mjs.map +1 -1
- package/dist/node/engine/AreHTML.tokenizer.d.mts +1 -1
- package/dist/node/engine/AreHTML.tokenizer.d.ts +1 -1
- package/dist/node/engine/AreHTML.tokenizer.js +7 -1
- package/dist/node/engine/AreHTML.tokenizer.js.map +1 -1
- package/dist/node/engine/AreHTML.tokenizer.mjs +7 -1
- package/dist/node/engine/AreHTML.tokenizer.mjs.map +1 -1
- package/dist/node/engine/AreHTML.transformer.d.mts +1 -1
- package/dist/node/engine/AreHTML.transformer.d.ts +1 -1
- package/dist/node/helpers/AreScheduler.helper.d.mts +39 -0
- package/dist/node/helpers/AreScheduler.helper.d.ts +39 -0
- package/dist/node/helpers/AreScheduler.helper.js +40 -0
- package/dist/node/helpers/AreScheduler.helper.js.map +1 -0
- package/dist/node/helpers/AreScheduler.helper.mjs +40 -0
- package/dist/node/helpers/AreScheduler.helper.mjs.map +1 -0
- package/dist/node/index.d.mts +4 -3
- package/dist/node/index.d.ts +4 -3
- package/dist/node/index.js +7 -0
- package/dist/node/index.mjs +1 -0
- package/dist/node/instructions/AddStaticHTML.instruction.d.mts +8 -0
- package/dist/node/instructions/AddStaticHTML.instruction.d.ts +8 -0
- package/dist/node/instructions/AddStaticHTML.instruction.js +31 -0
- package/dist/node/instructions/AddStaticHTML.instruction.js.map +1 -0
- package/dist/node/instructions/AddStaticHTML.instruction.mjs +24 -0
- package/dist/node/instructions/AddStaticHTML.instruction.mjs.map +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.d.mts +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.d.ts +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.js +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.js.map +1 -1
- package/dist/node/instructions/AreHTML.instructions.constants.mjs +1 -0
- package/dist/node/instructions/AreHTML.instructions.constants.mjs.map +1 -1
- package/dist/node/instructions/AreHTML.instructions.types.d.mts +9 -1
- package/dist/node/instructions/AreHTML.instructions.types.d.ts +9 -1
- package/dist/node/lib/AreDirective/AreDirective.component.d.mts +1 -1
- package/dist/node/lib/AreDirective/AreDirective.component.d.ts +1 -1
- package/dist/node/lib/AreDirective/AreDirective.types.d.mts +1 -1
- package/dist/node/lib/AreDirective/AreDirective.types.d.ts +1 -1
- package/dist/node/lib/AreHTML/AreHTML.tokenizer.d.mts +1 -1
- package/dist/node/lib/AreHTML/AreHTML.tokenizer.d.ts +1 -1
- package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.d.mts +1 -1
- package/dist/node/lib/AreHTMLAttribute/AreHTML.attribute.d.ts +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.d.mts +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.d.ts +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.js +51 -0
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.js.map +1 -1
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs +51 -0
- package/dist/node/lib/AreHTMLNode/AreHTMLNode.mjs.map +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.js +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.js.map +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.mjs +1 -1
- package/dist/node/lib/AreRoot/AreRoot.component.mjs.map +1 -1
- package/dist/node/nodes/AreComment.d.mts +1 -1
- package/dist/node/nodes/AreComment.d.ts +1 -1
- package/dist/node/nodes/AreComponent.d.mts +1 -1
- package/dist/node/nodes/AreComponent.d.ts +1 -1
- package/dist/node/nodes/AreInterpolation.d.mts +1 -1
- package/dist/node/nodes/AreInterpolation.d.ts +1 -1
- package/dist/node/nodes/AreRoot.d.mts +1 -1
- package/dist/node/nodes/AreRoot.d.ts +1 -1
- package/dist/node/nodes/AreText.d.mts +1 -1
- package/dist/node/nodes/AreText.d.ts +1 -1
- package/examples/dashboard/concept.ts +1 -1
- package/examples/dashboard/dist/index.html +1 -1
- package/examples/dashboard/dist/{mq19zxz4-mnlgmd.js → mqiw5sqa-ypckmj.js} +2275 -1323
- package/examples/dashboard/src/concept.ts +3 -2
- package/examples/for-perf/concept.ts +45 -0
- package/examples/for-perf/containers/UI.container.ts +161 -0
- package/examples/for-perf/dist/index.html +270 -0
- package/examples/for-perf/dist/mqj1mpf2-z4aokv.js +15664 -0
- package/examples/for-perf/dist/mqj1mpff-4fr7mw.js +15664 -0
- package/examples/for-perf/public/index.html +270 -0
- package/examples/for-perf/src/components/PerfApp.component.ts +37 -0
- package/examples/for-perf/src/components/PerfControls.component.ts +34 -0
- package/examples/for-perf/src/components/PerfGrid.component.ts +225 -0
- package/examples/for-perf/src/components/PerfHeader.component.ts +34 -0
- package/examples/for-perf/src/components/PerfStats.component.ts +43 -0
- package/examples/for-perf/src/concept.ts +94 -0
- package/examples/jumpstart/dist/index.html +1 -1
- package/examples/jumpstart/dist/{mq7hqrxy-4kus50.js → mq7mgf58-vbf07e.js} +269 -91
- package/examples/signal-routing/dist/index.html +1 -1
- package/examples/signal-routing/dist/{mq7k53th-qiwy4x.js → mqiwo23h-bhcolu.js} +2090 -1430
- package/jest.config.ts +1 -0
- package/package.json +10 -9
- package/src/directives/AreDirectiveFor.directive.ts +233 -19
- package/src/engine/AreHTML.compiler.ts +13 -0
- package/src/engine/AreHTML.constants.ts +142 -0
- package/src/engine/AreHTML.context.ts +112 -0
- package/src/engine/AreHTML.interpreter.ts +114 -13
- package/src/engine/AreHTML.lifecycle.ts +91 -7
- package/src/engine/AreHTML.tokenizer.ts +30 -1
- package/src/helpers/AreScheduler.helper.ts +61 -0
- package/src/index.ts +1 -0
- package/src/instructions/AddStaticHTML.instruction.ts +23 -0
- package/src/instructions/AreHTML.instructions.constants.ts +1 -0
- package/src/instructions/AreHTML.instructions.types.ts +9 -0
- package/src/lib/AreHTMLNode/AreHTMLNode.ts +74 -0
- package/src/lib/AreRoot/AreRoot.component.ts +4 -1
- package/tests/StaticIsland.test.ts +115 -0
- package/tsconfig.json +1 -0
package/dist/node/{AreBinding.attribute-doUvtOjc.d.mts → AreBinding.attribute-BWzEIw6H.d.mts}
RENAMED
|
@@ -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,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-
|
|
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-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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)),
|