@microsoft/fast-element 2.10.4 → 3.0.0-rc.2
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/CHANGELOG.md +52 -2
- package/README.md +244 -1
- package/dist/arrays/arrays.api.json +2621 -0
- package/dist/context/context.api.json +13 -13
- package/dist/declarative/declarative.api.json +8483 -0
- package/dist/di/di.api.json +16 -16
- package/dist/dts/__test__/helpers.d.ts +6 -0
- package/dist/dts/array-observer.d.ts +2 -0
- package/dist/dts/arrays.d.ts +2 -0
- package/dist/dts/attr.d.ts +1 -0
- package/dist/dts/binding/binding.d.ts +15 -5
- package/dist/dts/binding/one-time.d.ts +1 -1
- package/dist/dts/binding/one-way.d.ts +1 -1
- package/dist/dts/binding/signal.d.ts +6 -6
- package/dist/dts/binding/two-way.d.ts +2 -1
- package/dist/dts/binding.d.ts +7 -0
- package/dist/dts/components/attributes.d.ts +1 -4
- package/dist/dts/components/definition-schema-transforms.d.ts +9 -0
- package/dist/dts/components/element-controller.d.ts +80 -114
- package/dist/dts/components/element-hydration.d.ts +1 -1
- package/dist/dts/components/enable-hydration.d.ts +54 -0
- package/dist/dts/components/fast-definitions.d.ts +98 -46
- package/dist/dts/components/fast-element.d.ts +43 -16
- package/dist/dts/components/hydration-tracker.d.ts +83 -0
- package/dist/dts/components/hydration.d.ts +23 -53
- package/dist/dts/components/schema.d.ts +205 -0
- package/dist/dts/context.d.ts +13 -13
- package/dist/dts/css.d.ts +3 -0
- package/dist/dts/debug.d.ts +5 -1
- package/dist/dts/declarative/attribute-map.d.ts +58 -0
- package/dist/dts/declarative/debug.d.ts +4 -0
- package/dist/dts/declarative/index.d.ts +14 -0
- package/dist/dts/declarative/interfaces.d.ts +8 -0
- package/dist/dts/declarative/observer-map-utilities.d.ts +58 -0
- package/dist/dts/declarative/observer-map.d.ts +89 -0
- package/dist/dts/declarative/runtime.d.ts +5 -0
- package/dist/dts/declarative/syntax.d.ts +21 -0
- package/dist/dts/declarative/template-bridge.d.ts +33 -0
- package/dist/dts/declarative/template-parser.d.ts +98 -0
- package/dist/dts/declarative/template.d.ts +10 -0
- package/dist/dts/declarative/utilities.d.ts +358 -0
- package/dist/dts/di/di.d.ts +7 -7
- package/dist/dts/directives/children.d.ts +2 -0
- package/dist/dts/directives/node-observation.d.ts +2 -0
- package/dist/dts/directives/ref.d.ts +2 -0
- package/dist/dts/directives/repeat.d.ts +4 -0
- package/dist/dts/directives/slotted.d.ts +2 -0
- package/dist/dts/directives/when.d.ts +3 -0
- package/dist/dts/dom-policy.d.ts +23 -5
- package/dist/dts/dom.d.ts +4 -16
- package/dist/dts/html.d.ts +5 -0
- package/dist/dts/hydration/diagnostics.d.ts +93 -0
- package/dist/dts/hydration/hydration-debugger.d.ts +35 -0
- package/dist/dts/hydration/messages.d.ts +62 -0
- package/dist/dts/hydration/runtime.d.ts +7 -0
- package/dist/dts/hydration/target-builder.d.ts +40 -12
- package/dist/dts/hydration.d.ts +18 -0
- package/dist/dts/index.d.ts +42 -42
- package/dist/dts/index.debug.d.ts +0 -1
- package/dist/dts/index.rollup.debug.d.ts +0 -1
- package/dist/dts/interfaces.d.ts +2 -49
- package/dist/dts/observable.d.ts +3 -6
- package/dist/dts/observation/arrays.d.ts +1 -1
- package/dist/dts/observation/observable.d.ts +3 -3
- package/dist/dts/observation/update-queue.d.ts +1 -1
- package/dist/dts/platform.d.ts +45 -8
- package/dist/dts/registry.d.ts +1 -0
- package/dist/dts/render.d.ts +7 -0
- package/dist/dts/schema.d.ts +1 -0
- package/dist/dts/state/exports.d.ts +1 -1
- package/dist/dts/state/state.d.ts +2 -2
- package/dist/dts/styles/css-directive.d.ts +5 -12
- package/dist/dts/styles/css.d.ts +5 -7
- package/dist/dts/styles/element-styles.d.ts +0 -10
- package/dist/dts/styles.d.ts +6 -0
- package/dist/dts/templating/compiler.d.ts +1 -1
- package/dist/dts/templating/html-binding-directive.d.ts +10 -2
- package/dist/dts/templating/html-directive.d.ts +19 -1
- package/dist/dts/templating/hydration-view.d.ts +130 -0
- package/dist/dts/templating/render.d.ts +1 -1
- package/dist/dts/templating/repeat.d.ts +1 -1
- package/dist/dts/templating/template.d.ts +15 -7
- package/dist/dts/templating/view.d.ts +25 -102
- package/dist/dts/templating.d.ts +10 -0
- package/dist/dts/testing/exports.d.ts +2 -2
- package/dist/dts/testing/fakes.d.ts +4 -4
- package/dist/dts/updates.d.ts +1 -0
- package/dist/dts/volatile.d.ts +2 -0
- package/dist/esm/__test__/helpers.js +22 -0
- package/dist/esm/__test__/setup-node.js +18 -0
- package/dist/esm/array-observer.js +1 -0
- package/dist/esm/arrays.js +1 -0
- package/dist/esm/attr.js +1 -0
- package/dist/esm/binding/normalize.js +1 -1
- package/dist/esm/binding/signal.js +4 -4
- package/dist/esm/binding/two-way.js +3 -3
- package/dist/esm/binding.js +4 -0
- package/dist/esm/components/attributes.js +18 -11
- package/dist/esm/components/definition-schema-transforms.js +23 -0
- package/dist/esm/components/element-controller.js +206 -270
- package/dist/esm/components/element-hydration.js +1 -1
- package/dist/esm/components/enable-hydration.js +124 -0
- package/dist/esm/components/fast-definitions.js +219 -56
- package/dist/esm/components/fast-element.js +18 -27
- package/dist/esm/components/hydration-tracker.js +122 -0
- package/dist/esm/components/hydration.js +137 -140
- package/dist/esm/components/schema.js +253 -0
- package/dist/esm/context.js +6 -6
- package/dist/esm/css.js +3 -0
- package/dist/esm/debug.js +27 -26
- package/dist/esm/declarative/attribute-map.js +122 -0
- package/dist/esm/declarative/debug.js +4 -0
- package/dist/esm/declarative/index.js +4 -0
- package/dist/esm/declarative/interfaces.js +9 -0
- package/dist/esm/declarative/observer-map-utilities.js +565 -0
- package/dist/esm/declarative/observer-map.js +216 -0
- package/dist/esm/declarative/runtime.js +14 -0
- package/dist/esm/declarative/syntax.js +36 -0
- package/dist/esm/declarative/template-bridge.js +160 -0
- package/dist/esm/declarative/template-parser.js +306 -0
- package/dist/esm/declarative/template.js +143 -0
- package/dist/esm/declarative/utilities.js +1069 -0
- package/dist/esm/di/di.js +8 -9
- package/dist/esm/directives/children.js +1 -0
- package/dist/esm/directives/node-observation.js +1 -0
- package/dist/esm/directives/ref.js +1 -0
- package/dist/esm/directives/repeat.js +1 -0
- package/dist/esm/directives/slotted.js +1 -0
- package/dist/esm/directives/when.js +1 -0
- package/dist/esm/dom-policy.js +35 -6
- package/dist/esm/dom.js +1 -1
- package/dist/esm/html.js +2 -0
- package/dist/esm/hydration/diagnostics.js +50 -0
- package/dist/esm/hydration/hydration-debugger.js +112 -0
- package/dist/esm/hydration/messages.js +84 -0
- package/dist/esm/hydration/runtime.js +33 -0
- package/dist/esm/hydration/target-builder.js +144 -91
- package/dist/esm/hydration.js +6 -0
- package/dist/esm/index.debug.js +2 -1
- package/dist/esm/index.js +38 -29
- package/dist/esm/index.rollup.debug.js +3 -2
- package/dist/esm/index.rollup.js +1 -1
- package/dist/esm/interfaces.js +2 -45
- package/dist/esm/metadata.js +2 -8
- package/dist/esm/observable.js +1 -4
- package/dist/esm/observation/arrays.js +1 -1
- package/dist/esm/observation/notifier.js +2 -4
- package/dist/esm/observation/observable.js +5 -5
- package/dist/esm/observation/update-queue.js +47 -58
- package/dist/esm/platform.js +31 -30
- package/dist/esm/registry.js +1 -0
- package/dist/esm/render.js +1 -0
- package/dist/esm/schema.js +1 -0
- package/dist/esm/state/exports.js +1 -1
- package/dist/esm/styles/css-directive.js +1 -2
- package/dist/esm/styles/css.js +15 -56
- package/dist/esm/styles/element-styles.js +69 -15
- package/dist/esm/styles.js +2 -0
- package/dist/esm/templating/html-binding-directive.js +11 -9
- package/dist/esm/templating/hydration-view.js +228 -0
- package/dist/esm/templating/render.js +39 -18
- package/dist/esm/templating/repeat.js +69 -33
- package/dist/esm/templating/template.js +7 -7
- package/dist/esm/templating/view.js +25 -234
- package/dist/esm/templating.js +7 -0
- package/dist/esm/testing/exports.js +2 -2
- package/dist/esm/testing/fixture.js +2 -2
- package/dist/esm/testing/timeout.js +2 -2
- package/dist/esm/updates.js +1 -0
- package/dist/esm/volatile.js +1 -0
- package/dist/fast-element.api.json +14389 -11138
- package/dist/fast-element.d.ts +3651 -809
- package/dist/fast-element.debug.js +5666 -4722
- package/dist/fast-element.debug.min.js +2 -2
- package/dist/fast-element.js +5394 -4381
- package/dist/fast-element.min.js +2 -2
- package/dist/fast-element.untrimmed.d.ts +923 -472
- package/dist/hydration/hydration.api.json +6460 -0
- package/dist/styles/styles.api.json +2672 -0
- package/package.json +165 -45
- package/ARCHITECTURE_FASTELEMENT.md +0 -63
- package/ARCHITECTURE_HTML_TAGGED_TEMPLATE_LITERAL.md +0 -36
- package/ARCHITECTURE_INTRO.md +0 -10
- package/ARCHITECTURE_OVERVIEW.md +0 -52
- package/ARCHITECTURE_UPDATES.md +0 -11
- package/CHANGELOG.json +0 -2275
- package/DESIGN.md +0 -510
- package/api-extractor.context.json +0 -14
- package/api-extractor.di.json +0 -14
- package/biome.json +0 -4
- package/dist/dts/components/install-hydration.d.ts +0 -1
- package/dist/dts/pending-task.d.ts +0 -32
- package/dist/dts/styles/css-binding-directive.d.ts +0 -60
- package/dist/dts/templating/install-hydratable-view-templates.d.ts +0 -1
- package/dist/esm/components/install-hydration.js +0 -3
- package/dist/esm/pending-task.js +0 -28
- package/dist/esm/polyfills.js +0 -60
- package/dist/esm/styles/css-binding-directive.js +0 -76
- package/dist/esm/templating/install-hydratable-view-templates.js +0 -23
- package/docs/ACKNOWLEDGEMENTS.md +0 -12
- package/docs/api-report.api.md +0 -1122
- package/docs/context/api-report.api.md +0 -69
- package/docs/di/api-report.api.md +0 -315
- package/docs/fast-element-2-changes.md +0 -15
- package/playwright.config.ts +0 -26
- package/scripts/run-api-extractor.js +0 -51
- package/test/index.html +0 -11
- package/test/main.ts +0 -104
- package/test/vite.config.ts +0 -19
- package/tsconfig.api-extractor.json +0 -6
- /package/dist/dts/{polyfills.d.ts → __test__/setup-node.d.ts} +0 -0
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
var _a;
|
|
2
|
+
import { Hydratable } from "../components/hydration.js";
|
|
3
|
+
import { getHostName, getHydrationDiagnostic, } from "../hydration/diagnostics.js";
|
|
4
|
+
import { buildViewBindingTargets, createRangeForNodes, HydrationTargetElementError, targetFactory, } from "../hydration/target-builder.js";
|
|
5
|
+
import { SourceLifetime } from "../observation/observable.js";
|
|
6
|
+
import { makeSerializationNoop } from "../platform.js";
|
|
7
|
+
import { DefaultExecutionContext, removeNodeSequence, } from "./view.js";
|
|
8
|
+
/** @public */
|
|
9
|
+
export const HydrationStage = {
|
|
10
|
+
unhydrated: "unhydrated",
|
|
11
|
+
hydrating: "hydrating",
|
|
12
|
+
hydrated: "hydrated",
|
|
13
|
+
};
|
|
14
|
+
/** @public */
|
|
15
|
+
export class HydrationBindingError extends Error {
|
|
16
|
+
constructor(
|
|
17
|
+
/**
|
|
18
|
+
* The error message
|
|
19
|
+
*/
|
|
20
|
+
message,
|
|
21
|
+
/**
|
|
22
|
+
* The factory that was unable to be bound
|
|
23
|
+
*/
|
|
24
|
+
factory,
|
|
25
|
+
/**
|
|
26
|
+
* A DocumentFragment containing a clone of the
|
|
27
|
+
* view's Nodes.
|
|
28
|
+
*/
|
|
29
|
+
fragment,
|
|
30
|
+
/**
|
|
31
|
+
* String representation of the HTML in the template that
|
|
32
|
+
* threw the binding error.
|
|
33
|
+
*/
|
|
34
|
+
templateString,
|
|
35
|
+
/**
|
|
36
|
+
* Structured description of the binding the hydration walk was
|
|
37
|
+
* attempting to apply when the mismatch was detected.
|
|
38
|
+
*/
|
|
39
|
+
expected,
|
|
40
|
+
/**
|
|
41
|
+
* Structured description of the server-rendered DOM that was
|
|
42
|
+
* encountered at the mismatch point.
|
|
43
|
+
*/
|
|
44
|
+
received) {
|
|
45
|
+
super(message);
|
|
46
|
+
this.factory = factory;
|
|
47
|
+
this.fragment = fragment;
|
|
48
|
+
this.templateString = templateString;
|
|
49
|
+
this.expected = expected;
|
|
50
|
+
this.received = received;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export class HydrationView extends DefaultExecutionContext {
|
|
54
|
+
get hydrationStage() {
|
|
55
|
+
return this._hydrationStage;
|
|
56
|
+
}
|
|
57
|
+
get targets() {
|
|
58
|
+
return this._targets;
|
|
59
|
+
}
|
|
60
|
+
get bindingViewBoundaries() {
|
|
61
|
+
return this._bindingViewBoundaries;
|
|
62
|
+
}
|
|
63
|
+
constructor(firstChild, lastChild, sourceTemplate, hostBindingTarget) {
|
|
64
|
+
super();
|
|
65
|
+
this.firstChild = firstChild;
|
|
66
|
+
this.lastChild = lastChild;
|
|
67
|
+
this.sourceTemplate = sourceTemplate;
|
|
68
|
+
this.hostBindingTarget = hostBindingTarget;
|
|
69
|
+
this[_a] = Hydratable;
|
|
70
|
+
this.context = this;
|
|
71
|
+
this.source = null;
|
|
72
|
+
this.isBound = false;
|
|
73
|
+
this.sourceLifetime = SourceLifetime.unknown;
|
|
74
|
+
this.unbindables = [];
|
|
75
|
+
this.fragment = null;
|
|
76
|
+
this.behaviors = null;
|
|
77
|
+
this._hydrationStage = HydrationStage.unhydrated;
|
|
78
|
+
this._bindingViewBoundaries = {};
|
|
79
|
+
this._targets = {};
|
|
80
|
+
this.factories = sourceTemplate.compile().factories;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* no-op. Hydrated views are don't need to be moved from a documentFragment
|
|
84
|
+
* to the target node.
|
|
85
|
+
*/
|
|
86
|
+
insertBefore(node) {
|
|
87
|
+
// No-op in cases where this is called before the view is removed,
|
|
88
|
+
// because the nodes will already be in the document and just need hydrating.
|
|
89
|
+
if (this.fragment === null) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (this.fragment.hasChildNodes()) {
|
|
93
|
+
node.parentNode.insertBefore(this.fragment, node);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
const end = this.lastChild;
|
|
97
|
+
if (node.previousSibling === end)
|
|
98
|
+
return;
|
|
99
|
+
const parentNode = node.parentNode;
|
|
100
|
+
let current = this.firstChild;
|
|
101
|
+
let next;
|
|
102
|
+
while (current !== end) {
|
|
103
|
+
next = current.nextSibling;
|
|
104
|
+
parentNode.insertBefore(current, node);
|
|
105
|
+
current = next;
|
|
106
|
+
}
|
|
107
|
+
parentNode.insertBefore(end, node);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Appends the view to a node. In cases where this is called before the
|
|
112
|
+
* view has been removed, the method will no-op.
|
|
113
|
+
* @param node - the node to append the view to.
|
|
114
|
+
*/
|
|
115
|
+
appendTo(node) {
|
|
116
|
+
if (this.fragment !== null) {
|
|
117
|
+
node.appendChild(this.fragment);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
remove() {
|
|
121
|
+
const fragment = this.fragment || (this.fragment = document.createDocumentFragment());
|
|
122
|
+
const end = this.lastChild;
|
|
123
|
+
let current = this.firstChild;
|
|
124
|
+
let next;
|
|
125
|
+
while (current !== end) {
|
|
126
|
+
next = current.nextSibling;
|
|
127
|
+
if (!next) {
|
|
128
|
+
throw new Error(`Unmatched first/last child inside "${end.getRootNode().host.nodeName}".`);
|
|
129
|
+
}
|
|
130
|
+
fragment.appendChild(current);
|
|
131
|
+
current = next;
|
|
132
|
+
}
|
|
133
|
+
fragment.appendChild(end);
|
|
134
|
+
}
|
|
135
|
+
bind(source, context = this) {
|
|
136
|
+
if (this.source === source && this.context === context) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (this.hydrationStage !== HydrationStage.hydrated) {
|
|
140
|
+
this._hydrationStage = HydrationStage.hydrating;
|
|
141
|
+
}
|
|
142
|
+
let behaviors = this.behaviors;
|
|
143
|
+
if (behaviors === null) {
|
|
144
|
+
this.source = source;
|
|
145
|
+
this.context = context;
|
|
146
|
+
try {
|
|
147
|
+
const { targets, boundaries } = buildViewBindingTargets(this.firstChild, this.lastChild, this.factories);
|
|
148
|
+
this._targets = targets;
|
|
149
|
+
this._bindingViewBoundaries = boundaries;
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
if (error instanceof HydrationTargetElementError) {
|
|
153
|
+
let templateString = this.sourceTemplate.html;
|
|
154
|
+
if (typeof templateString !== "string") {
|
|
155
|
+
templateString = templateString.innerHTML;
|
|
156
|
+
}
|
|
157
|
+
error.templateString = templateString;
|
|
158
|
+
}
|
|
159
|
+
throw error;
|
|
160
|
+
}
|
|
161
|
+
this.behaviors = behaviors = new Array(this.factories.length);
|
|
162
|
+
const factories = this.factories;
|
|
163
|
+
for (let i = 0, ii = factories.length; i < ii; ++i) {
|
|
164
|
+
const factory = factories[i];
|
|
165
|
+
if (factory.targetNodeId === "h" && this.hostBindingTarget) {
|
|
166
|
+
targetFactory(factory, this.hostBindingTarget, this._targets);
|
|
167
|
+
}
|
|
168
|
+
// If the binding has been targeted or it is a host binding and the view has a hostBindingTarget
|
|
169
|
+
if (factory.targetNodeId in this.targets) {
|
|
170
|
+
const behavior = factory.createBehavior();
|
|
171
|
+
behavior.bind(this);
|
|
172
|
+
behaviors[i] = behavior;
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
let templateString = this.sourceTemplate.html;
|
|
176
|
+
if (typeof templateString !== "string") {
|
|
177
|
+
templateString = templateString.innerHTML;
|
|
178
|
+
}
|
|
179
|
+
const fragment = createRangeForNodes(this.firstChild, this.lastChild).cloneContents();
|
|
180
|
+
const result = getHydrationDiagnostic().formatBindingMismatch(factory, this.firstChild, this.lastChild, getHostName(this.firstChild));
|
|
181
|
+
throw new HydrationBindingError(result.message, factory, fragment, templateString, result.expected, result.received);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
if (this.source !== null) {
|
|
187
|
+
this.evaluateUnbindables();
|
|
188
|
+
}
|
|
189
|
+
this.isBound = false;
|
|
190
|
+
this.source = source;
|
|
191
|
+
this.context = context;
|
|
192
|
+
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
193
|
+
behaviors[i].bind(this);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
this.isBound = true;
|
|
197
|
+
this._hydrationStage = HydrationStage.hydrated;
|
|
198
|
+
}
|
|
199
|
+
unbind() {
|
|
200
|
+
if (!this.isBound || this.source === null) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
this.evaluateUnbindables();
|
|
204
|
+
this.source = null;
|
|
205
|
+
this.context = this;
|
|
206
|
+
this.isBound = false;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Removes the view and unbinds its behaviors, disposing of DOM nodes afterward.
|
|
210
|
+
* Once a view has been disposed, it cannot be inserted or bound again.
|
|
211
|
+
*/
|
|
212
|
+
dispose() {
|
|
213
|
+
removeNodeSequence(this.firstChild, this.lastChild);
|
|
214
|
+
this.unbind();
|
|
215
|
+
}
|
|
216
|
+
onUnbind(behavior) {
|
|
217
|
+
this.unbindables.push(behavior);
|
|
218
|
+
}
|
|
219
|
+
evaluateUnbindables() {
|
|
220
|
+
const unbindables = this.unbindables;
|
|
221
|
+
for (let i = 0, ii = unbindables.length; i < ii; ++i) {
|
|
222
|
+
unbindables[i].unbind(this);
|
|
223
|
+
}
|
|
224
|
+
unbindables.length = 0;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
_a = Hydratable;
|
|
228
|
+
makeSerializationNoop(HydrationView);
|
|
@@ -4,11 +4,12 @@ import { oneTime } from "../binding/one-time.js";
|
|
|
4
4
|
import { oneWay } from "../binding/one-way.js";
|
|
5
5
|
import { FASTElementDefinition } from "../components/fast-definitions.js";
|
|
6
6
|
import { isHydratable } from "../components/hydration.js";
|
|
7
|
-
import { isFunction, isString } from "../interfaces.js";
|
|
7
|
+
import { isFunction, isString, Message } from "../interfaces.js";
|
|
8
|
+
import { FAST } from "../platform.js";
|
|
8
9
|
import { HTMLDirective, } from "./html-directive.js";
|
|
10
|
+
import { HydrationStage } from "./hydration-view.js";
|
|
9
11
|
import { Markup } from "./markup.js";
|
|
10
12
|
import { html, ViewTemplate, } from "./template.js";
|
|
11
|
-
import { HydrationStage } from "./view.js";
|
|
12
13
|
/**
|
|
13
14
|
* A Behavior that enables advanced rendering.
|
|
14
15
|
* @public
|
|
@@ -45,11 +46,10 @@ export class RenderBehavior {
|
|
|
45
46
|
if (viewNodes) {
|
|
46
47
|
this.view = this.template.hydrate(viewNodes.first, viewNodes.last);
|
|
47
48
|
this.bindView(this.view);
|
|
49
|
+
return;
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
|
-
|
|
51
|
-
this.refreshView();
|
|
52
|
-
}
|
|
52
|
+
this.refreshView();
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
55
|
* Unbinds this behavior.
|
|
@@ -172,6 +172,32 @@ function instructionToTemplate(def) {
|
|
|
172
172
|
}
|
|
173
173
|
return def.template;
|
|
174
174
|
}
|
|
175
|
+
function resolveTemplateBindingValue(result, dataBinding, source, context) {
|
|
176
|
+
var _a;
|
|
177
|
+
if (isString(result)) {
|
|
178
|
+
return instructionToTemplate(getForInstance(dataBinding.evaluate(source, context), result));
|
|
179
|
+
}
|
|
180
|
+
if (result instanceof Node) {
|
|
181
|
+
return (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
function adaptTemplateBinding(binding, dataBinding) {
|
|
186
|
+
const evaluateTemplate = binding.evaluate;
|
|
187
|
+
const adapter = Object.create(Object.getPrototypeOf(binding));
|
|
188
|
+
for (const propertyName of Object.getOwnPropertyNames(binding)) {
|
|
189
|
+
if (propertyName !== "evaluate") {
|
|
190
|
+
Object.defineProperty(adapter, propertyName, Object.getOwnPropertyDescriptor(binding, propertyName));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
Object.defineProperty(adapter, "evaluate", {
|
|
194
|
+
configurable: true,
|
|
195
|
+
enumerable: true,
|
|
196
|
+
value: (source, context) => resolveTemplateBindingValue(evaluateTemplate(source, context), dataBinding, source, context),
|
|
197
|
+
writable: true,
|
|
198
|
+
});
|
|
199
|
+
return adapter;
|
|
200
|
+
}
|
|
175
201
|
function createElementTemplate(tagName, options) {
|
|
176
202
|
const markup = [];
|
|
177
203
|
const values = [];
|
|
@@ -251,6 +277,13 @@ function register(optionsOrInstruction) {
|
|
|
251
277
|
const instruction = instanceOf(optionsOrInstruction)
|
|
252
278
|
? optionsOrInstruction
|
|
253
279
|
: create(optionsOrInstruction);
|
|
280
|
+
if (lookup[instruction.name] !== void 0) {
|
|
281
|
+
const typeName = instruction.type.name || "(anonymous)";
|
|
282
|
+
FAST.warn(Message.duplicateRenderInstruction, {
|
|
283
|
+
type: typeName,
|
|
284
|
+
name: instruction.name,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
254
287
|
return (lookup[instruction.name] = instruction);
|
|
255
288
|
}
|
|
256
289
|
function getByType(type, name) {
|
|
@@ -425,19 +458,7 @@ export function render(value, template) {
|
|
|
425
458
|
});
|
|
426
459
|
}
|
|
427
460
|
else if (template instanceof Binding) {
|
|
428
|
-
|
|
429
|
-
template.evaluate = (s, c) => {
|
|
430
|
-
var _a;
|
|
431
|
-
let result = evaluateTemplate(s, c);
|
|
432
|
-
if (isString(result)) {
|
|
433
|
-
result = instructionToTemplate(getForInstance(dataBinding.evaluate(s, c), result));
|
|
434
|
-
}
|
|
435
|
-
else if (result instanceof Node) {
|
|
436
|
-
result = (_a = result.$fastTemplate) !== null && _a !== void 0 ? _a : new NodeTemplate(result);
|
|
437
|
-
}
|
|
438
|
-
return result;
|
|
439
|
-
};
|
|
440
|
-
templateBinding = template;
|
|
461
|
+
templateBinding = adaptTemplateBinding(template, dataBinding);
|
|
441
462
|
}
|
|
442
463
|
else {
|
|
443
464
|
templateBinding = oneTime((s, c) => template);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { normalizeBinding } from "../binding/normalize.js";
|
|
2
2
|
import { HydrationMarkup, isHydratable } from "../components/hydration.js";
|
|
3
|
-
import { ArrayObserver
|
|
3
|
+
import { ArrayObserver } from "../observation/arrays.js";
|
|
4
4
|
import { Observable, } from "../observation/observable.js";
|
|
5
5
|
import { emptyArray } from "../platform.js";
|
|
6
6
|
import { HTMLDirective, } from "./html-directive.js";
|
|
7
|
+
import { HydrationStage } from "./hydration-view.js";
|
|
7
8
|
import { Markup } from "./markup.js";
|
|
8
|
-
import { HTMLView
|
|
9
|
+
import { HTMLView } from "./view.js";
|
|
9
10
|
const defaultRepeatOptions = Object.freeze({
|
|
10
11
|
positioning: false,
|
|
11
12
|
recycle: true,
|
|
@@ -25,6 +26,21 @@ function bindWithPositioning(view, items, index, controller) {
|
|
|
25
26
|
function isCommentNode(node) {
|
|
26
27
|
return node.nodeType === Node.COMMENT_NODE;
|
|
27
28
|
}
|
|
29
|
+
function removeNodeRange(first, last) {
|
|
30
|
+
const parentNode = first.parentNode;
|
|
31
|
+
if (parentNode === null) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
let current = first;
|
|
35
|
+
while (current !== null) {
|
|
36
|
+
const next = current.nextSibling;
|
|
37
|
+
parentNode.removeChild(current);
|
|
38
|
+
if (current === last) {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
current = next;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
28
44
|
export class HydrationRepeatError extends Error {
|
|
29
45
|
constructor(
|
|
30
46
|
/**
|
|
@@ -284,63 +300,83 @@ export class RepeatBehavior {
|
|
|
284
300
|
}
|
|
285
301
|
}
|
|
286
302
|
hydrateViews(template) {
|
|
303
|
+
var _a;
|
|
287
304
|
if (!this.items) {
|
|
288
305
|
return;
|
|
289
306
|
}
|
|
290
|
-
|
|
307
|
+
const items = this.items;
|
|
308
|
+
const itemCount = items.length;
|
|
309
|
+
const views = (this.views = new Array(itemCount));
|
|
310
|
+
// First pass: collect all repeat marker pairs by walking backward.
|
|
311
|
+
// Each entry tracks both the item content range and its SSR markers.
|
|
312
|
+
const itemRanges = [];
|
|
291
313
|
let current = this.location.previousSibling;
|
|
292
314
|
while (current !== null) {
|
|
293
|
-
if (!isCommentNode(current)
|
|
294
|
-
current
|
|
295
|
-
continue;
|
|
296
|
-
}
|
|
297
|
-
const index = HydrationMarkup.parseRepeatEndMarker(current.data);
|
|
298
|
-
if (index === null) {
|
|
315
|
+
if (!isCommentNode(current) ||
|
|
316
|
+
!HydrationMarkup.isRepeatViewEndMarker(current.data)) {
|
|
299
317
|
current = current.previousSibling;
|
|
300
318
|
continue;
|
|
301
319
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
const end =
|
|
320
|
+
const endMarker = current;
|
|
321
|
+
endMarker.data = "";
|
|
322
|
+
const end = endMarker.previousSibling;
|
|
305
323
|
if (!end) {
|
|
306
324
|
throw new Error(`Error when hydrating inside "${this.location.getRootNode().host.nodeName}": end should never be null.`);
|
|
307
325
|
}
|
|
308
|
-
//
|
|
326
|
+
// Find matching start marker via balanced counting
|
|
309
327
|
let start = end;
|
|
310
|
-
|
|
311
|
-
let unmatchedEndMarkers = 0;
|
|
328
|
+
let depth = 0;
|
|
312
329
|
while (start !== null) {
|
|
313
330
|
if (isCommentNode(start)) {
|
|
314
331
|
if (HydrationMarkup.isRepeatViewEndMarker(start.data)) {
|
|
315
|
-
|
|
332
|
+
depth++;
|
|
316
333
|
}
|
|
317
334
|
else if (HydrationMarkup.isRepeatViewStartMarker(start.data)) {
|
|
318
|
-
if (
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
const view = template.hydrate(start, end);
|
|
332
|
-
this.views[index] = view;
|
|
333
|
-
this.bindView(view, this.items, index, this.controller);
|
|
335
|
+
if (depth === 0) {
|
|
336
|
+
const startMarker = start;
|
|
337
|
+
startMarker.data = "";
|
|
338
|
+
current = startMarker.previousSibling;
|
|
339
|
+
const itemStart = (_a = startMarker.nextSibling) !== null && _a !== void 0 ? _a : endMarker;
|
|
340
|
+
// Empty item: start and end markers are adjacent.
|
|
341
|
+
const itemEnd = end === startMarker ? itemStart : end;
|
|
342
|
+
itemRanges.push({
|
|
343
|
+
start: itemStart,
|
|
344
|
+
end: itemEnd,
|
|
345
|
+
startMarker,
|
|
346
|
+
endMarker,
|
|
347
|
+
});
|
|
334
348
|
break;
|
|
335
349
|
}
|
|
350
|
+
depth--;
|
|
336
351
|
}
|
|
337
352
|
}
|
|
338
353
|
start = start.previousSibling;
|
|
339
354
|
}
|
|
340
355
|
if (!start) {
|
|
341
|
-
throw new Error(`Error when hydrating inside "${this.location.getRootNode().host.nodeName}": start
|
|
356
|
+
throw new Error(`Error when hydrating inside "${this.location.getRootNode().host.nodeName}": repeat start marker not found.`);
|
|
342
357
|
}
|
|
343
358
|
}
|
|
359
|
+
// Ranges were collected backward (last item first).
|
|
360
|
+
// Reverse so index 0 = first SSR item.
|
|
361
|
+
itemRanges.reverse();
|
|
362
|
+
// Hydrate each SSR item at its correct index (0-based from start).
|
|
363
|
+
const hydrationCount = Math.min(itemRanges.length, itemCount);
|
|
364
|
+
for (let i = 0; i < hydrationCount; i++) {
|
|
365
|
+
const { start, end } = itemRanges[i];
|
|
366
|
+
const view = template.hydrate(start, end);
|
|
367
|
+
views[i] = view;
|
|
368
|
+
this.bindView(view, items, i, this.controller);
|
|
369
|
+
}
|
|
370
|
+
for (let i = hydrationCount; i < itemCount; i++) {
|
|
371
|
+
const view = template.create();
|
|
372
|
+
views[i] = view;
|
|
373
|
+
this.bindView(view, items, i, this.controller);
|
|
374
|
+
view.insertBefore(this.location);
|
|
375
|
+
}
|
|
376
|
+
for (let i = itemCount, ii = itemRanges.length; i < ii; i++) {
|
|
377
|
+
const { startMarker, endMarker } = itemRanges[i];
|
|
378
|
+
removeNodeRange(startMarker, endMarker);
|
|
379
|
+
}
|
|
344
380
|
}
|
|
345
381
|
}
|
|
346
382
|
/**
|
|
@@ -77,13 +77,6 @@ export class ViewTemplate {
|
|
|
77
77
|
}
|
|
78
78
|
return this.result;
|
|
79
79
|
}
|
|
80
|
-
/**
|
|
81
|
-
* Creates an HTMLView instance based on this template definition.
|
|
82
|
-
* @param hostBindingTarget - The element that host behaviors will be bound to.
|
|
83
|
-
*/
|
|
84
|
-
create(hostBindingTarget) {
|
|
85
|
-
return this.compile().createView(hostBindingTarget);
|
|
86
|
-
}
|
|
87
80
|
/**
|
|
88
81
|
* Returns a directive that can inline the template.
|
|
89
82
|
*/
|
|
@@ -121,6 +114,13 @@ export class ViewTemplate {
|
|
|
121
114
|
view.appendTo(host);
|
|
122
115
|
return view;
|
|
123
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Creates an HTMLView instance based on this template definition.
|
|
119
|
+
* @param hostBindingTarget - The element that host behaviors will be bound to.
|
|
120
|
+
*/
|
|
121
|
+
create(hostBindingTarget) {
|
|
122
|
+
return this.compile().createView(hostBindingTarget);
|
|
123
|
+
}
|
|
124
124
|
/**
|
|
125
125
|
* Processes the tagged template literal's static strings and interpolated values and
|
|
126
126
|
* creates a ViewTemplate.
|