@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
|
@@ -3,13 +3,7 @@ import { PropertyChangeNotifier } from "../observation/notifier.js";
|
|
|
3
3
|
import { ExecutionContext, Observable, SourceLifetime, } from "../observation/observable.js";
|
|
4
4
|
import { FAST, makeSerializationNoop } from "../platform.js";
|
|
5
5
|
import { ElementStyles } from "../styles/element-styles.js";
|
|
6
|
-
import {
|
|
7
|
-
import { FASTElementDefinition, TemplateOptions, } from "./fast-definitions.js";
|
|
8
|
-
import { deferHydrationAttribute, HydrationMarkup, isHydratable } from "./hydration.js";
|
|
9
|
-
/**
|
|
10
|
-
* @deprecated Use the export from ./hydration.js instead.
|
|
11
|
-
*/
|
|
12
|
-
export { deferHydrationAttribute } from "./hydration.js";
|
|
6
|
+
import { FASTElementDefinition, getLateAttributeLookup, } from "./fast-definitions.js";
|
|
13
7
|
const defaultEventOptions = {
|
|
14
8
|
bubbles: true,
|
|
15
9
|
composed: true,
|
|
@@ -17,6 +11,7 @@ const defaultEventOptions = {
|
|
|
17
11
|
};
|
|
18
12
|
const isConnectedPropertyName = "isConnected";
|
|
19
13
|
const shadowRoots = new WeakMap();
|
|
14
|
+
const lateAttributeObserver = Symbol("fast-late-attribute-observer");
|
|
20
15
|
function getShadowRoot(element) {
|
|
21
16
|
var _a, _b;
|
|
22
17
|
return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
|
|
@@ -41,7 +36,7 @@ export var Stages;
|
|
|
41
36
|
* Controls the lifecycle and rendering of a `FASTElement`.
|
|
42
37
|
* @public
|
|
43
38
|
*/
|
|
44
|
-
export class ElementController
|
|
39
|
+
export class ElementController {
|
|
45
40
|
/**
|
|
46
41
|
* Indicates whether or not the custom element has been
|
|
47
42
|
* connected to the document.
|
|
@@ -54,14 +49,16 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
54
49
|
* The context the expression is evaluated against.
|
|
55
50
|
*/
|
|
56
51
|
get context() {
|
|
57
|
-
var _a
|
|
52
|
+
var _a;
|
|
53
|
+
var _b;
|
|
58
54
|
return (_b = (_a = this.view) === null || _a === void 0 ? void 0 : _a.context) !== null && _b !== void 0 ? _b : ExecutionContext.default;
|
|
59
55
|
}
|
|
60
56
|
/**
|
|
61
57
|
* Indicates whether the controller is bound.
|
|
62
58
|
*/
|
|
63
59
|
get isBound() {
|
|
64
|
-
var _a
|
|
60
|
+
var _a;
|
|
61
|
+
var _b;
|
|
65
62
|
return (_b = (_a = this.view) === null || _a === void 0 ? void 0 : _a.isBound) !== null && _b !== void 0 ? _b : false;
|
|
66
63
|
}
|
|
67
64
|
/**
|
|
@@ -163,7 +160,6 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
163
160
|
* @internal
|
|
164
161
|
*/
|
|
165
162
|
constructor(element, definition) {
|
|
166
|
-
super(element);
|
|
167
163
|
/**
|
|
168
164
|
* A map of observable properties that were set on the element before upgrade.
|
|
169
165
|
*/
|
|
@@ -176,6 +172,22 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
176
172
|
* Indicates whether the element has an existing shadow root (e.g. from declarative shadow DOM).
|
|
177
173
|
*/
|
|
178
174
|
this.hasExistingShadowRoot = false;
|
|
175
|
+
/**
|
|
176
|
+
* Resolves `true` when the element had an existing shadow root
|
|
177
|
+
* (from SSR or declarative shadow DOM) at connect time, `false`
|
|
178
|
+
* otherwise.
|
|
179
|
+
*/
|
|
180
|
+
this.isPrerendered = new Promise(resolve => {
|
|
181
|
+
this._resolvePrerendered = resolve;
|
|
182
|
+
});
|
|
183
|
+
/**
|
|
184
|
+
* Resolves `true` after prerendered content has been successfully
|
|
185
|
+
* hydrated, or `false` when the component is client-side rendered
|
|
186
|
+
* or hydration is not enabled.
|
|
187
|
+
*/
|
|
188
|
+
this.isHydrated = new Promise(resolve => {
|
|
189
|
+
this._resolveHydrated = resolve;
|
|
190
|
+
});
|
|
179
191
|
/**
|
|
180
192
|
* The template used to render the component.
|
|
181
193
|
*/
|
|
@@ -219,14 +231,12 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
219
231
|
* If `null` then the element is managing its own rendering.
|
|
220
232
|
*/
|
|
221
233
|
this.view = null;
|
|
234
|
+
this._notifier = new PropertyChangeNotifier(element);
|
|
222
235
|
this.source = element;
|
|
223
236
|
this.definition = definition;
|
|
224
237
|
this.shadowOptions = definition.shadowOptions;
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
// shadow the getter/setter that is required to make the observable operate.
|
|
228
|
-
// Later, in the connect callback, we'll re-apply the values.
|
|
229
|
-
const accessors = Observable.getAccessors(element);
|
|
238
|
+
const prototype = Reflect.getPrototypeOf(element);
|
|
239
|
+
const accessors = prototype === null ? [] : Observable.getAccessors(prototype);
|
|
230
240
|
if (accessors.length > 0) {
|
|
231
241
|
const boundObservables = (this.boundObservables = Object.create(null));
|
|
232
242
|
for (let i = 0, ii = accessors.length; i < ii; ++i) {
|
|
@@ -237,7 +247,42 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
237
247
|
boundObservables[propertyName] = value;
|
|
238
248
|
}
|
|
239
249
|
}
|
|
250
|
+
if (Object.keys(boundObservables).length === 0) {
|
|
251
|
+
this.boundObservables = null;
|
|
252
|
+
}
|
|
240
253
|
}
|
|
254
|
+
// Capture any observable values that were set after construction but before
|
|
255
|
+
// the first connect (for example, late-defined declarative accessors).
|
|
256
|
+
this.captureBoundObservables();
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* The subject that subscribers will receive notifications for.
|
|
260
|
+
*/
|
|
261
|
+
get subject() {
|
|
262
|
+
return this._notifier.subject;
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Notifies all subscribers of a property change.
|
|
266
|
+
* @param args - The property name that changed.
|
|
267
|
+
*/
|
|
268
|
+
notify(args) {
|
|
269
|
+
this._notifier.notify(args);
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Subscribes to notification of changes in the element's state.
|
|
273
|
+
* @param subscriber - The object that is subscribing for change notification.
|
|
274
|
+
* @param propertyToWatch - The name of the property to watch for changes.
|
|
275
|
+
*/
|
|
276
|
+
subscribe(subscriber, propertyToWatch) {
|
|
277
|
+
this._notifier.subscribe(subscriber, propertyToWatch);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Unsubscribes from notification of changes in the element's state.
|
|
281
|
+
* @param subscriber - The object that is unsubscribing from change notification.
|
|
282
|
+
* @param propertyToUnwatch - The name of the property to unsubscribe from.
|
|
283
|
+
*/
|
|
284
|
+
unsubscribe(subscriber, propertyToUnwatch) {
|
|
285
|
+
this._notifier.unsubscribe(subscriber, propertyToUnwatch);
|
|
241
286
|
}
|
|
242
287
|
/**
|
|
243
288
|
* Registers an unbind handler with the controller.
|
|
@@ -308,13 +353,7 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
308
353
|
target.append(styles);
|
|
309
354
|
}
|
|
310
355
|
else if (!styles.isAttachedTo(source)) {
|
|
311
|
-
const sourceBehaviors = styles.behaviors;
|
|
312
356
|
styles.addStylesTo(source);
|
|
313
|
-
if (sourceBehaviors !== null) {
|
|
314
|
-
for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
|
|
315
|
-
this.addBehavior(sourceBehaviors[i]);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
357
|
}
|
|
319
358
|
}
|
|
320
359
|
/**
|
|
@@ -332,13 +371,7 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
332
371
|
target.removeChild(styles);
|
|
333
372
|
}
|
|
334
373
|
else if (styles.isAttachedTo(source)) {
|
|
335
|
-
const sourceBehaviors = styles.behaviors;
|
|
336
374
|
styles.removeStylesFrom(source);
|
|
337
|
-
if (sourceBehaviors !== null) {
|
|
338
|
-
for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
|
|
339
|
-
this.removeBehavior(sourceBehaviors[i]);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
375
|
}
|
|
343
376
|
}
|
|
344
377
|
/**
|
|
@@ -349,6 +382,9 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
349
382
|
return;
|
|
350
383
|
}
|
|
351
384
|
this.stage = Stages.connecting;
|
|
385
|
+
this.captureBoundObservables();
|
|
386
|
+
this.syncLateAttributes();
|
|
387
|
+
this.observeLateAttributes();
|
|
352
388
|
this.bindObservables();
|
|
353
389
|
this.connectBehaviors();
|
|
354
390
|
if (this.needsInitialization) {
|
|
@@ -377,6 +413,99 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
377
413
|
this.boundObservables = null;
|
|
378
414
|
}
|
|
379
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Captures own-properties that shadow observable accessors on the prototype so
|
|
418
|
+
* they can be rebound through the accessor before rendering.
|
|
419
|
+
*/
|
|
420
|
+
captureBoundObservables() {
|
|
421
|
+
const element = this.source;
|
|
422
|
+
const propertyNames = Object.getOwnPropertyNames(element);
|
|
423
|
+
const hasPrototypeAccessor = (propertyName) => {
|
|
424
|
+
let currentTarget = Reflect.getPrototypeOf(element);
|
|
425
|
+
while (currentTarget !== null) {
|
|
426
|
+
const descriptor = Reflect.getOwnPropertyDescriptor(currentTarget, propertyName);
|
|
427
|
+
if ((descriptor === null || descriptor === void 0 ? void 0 : descriptor.get) || (descriptor === null || descriptor === void 0 ? void 0 : descriptor.set)) {
|
|
428
|
+
return true;
|
|
429
|
+
}
|
|
430
|
+
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
431
|
+
}
|
|
432
|
+
return false;
|
|
433
|
+
};
|
|
434
|
+
let boundObservables = this.boundObservables;
|
|
435
|
+
for (let i = 0, ii = propertyNames.length; i < ii; ++i) {
|
|
436
|
+
const ownPropertyName = propertyNames[i];
|
|
437
|
+
const propertyName = (ownPropertyName[0] === "_" ? ownPropertyName.slice(1) : ownPropertyName);
|
|
438
|
+
if (!hasPrototypeAccessor(propertyName)) {
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
const value = element[propertyName];
|
|
442
|
+
const isBackingField = ownPropertyName !== propertyName;
|
|
443
|
+
const isRebindableObject = value !== null &&
|
|
444
|
+
typeof value === "object" &&
|
|
445
|
+
!(value === null || value === void 0 ? void 0 : value.$isProxy) &&
|
|
446
|
+
!(Array.isArray(value) && (value === null || value === void 0 ? void 0 : value.$fastController));
|
|
447
|
+
if (value === void 0) {
|
|
448
|
+
if (!isBackingField) {
|
|
449
|
+
delete element[ownPropertyName];
|
|
450
|
+
}
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
if (isBackingField && !isRebindableObject) {
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
delete element[ownPropertyName];
|
|
457
|
+
(boundObservables !== null && boundObservables !== void 0 ? boundObservables : (boundObservables = this.boundObservables = Object.create(null)))[propertyName] = value;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Synchronizes late-defined attribute-map attributes from the live DOM to the
|
|
462
|
+
* associated property values before the initial render occurs.
|
|
463
|
+
*/
|
|
464
|
+
syncLateAttributes() {
|
|
465
|
+
const lateAttributes = getLateAttributeLookup(this.definition);
|
|
466
|
+
if (lateAttributes === null) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
for (const attributeName of Object.keys(lateAttributes)) {
|
|
470
|
+
if (!this.source.hasAttribute(attributeName)) {
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
this.onAttributeChangedCallback(attributeName, null, this.source.getAttribute(attributeName));
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* Observes late-defined attribute-map attributes that the platform does not
|
|
478
|
+
* surface through attributeChangedCallback because they were added after
|
|
479
|
+
* customElements.define() completed.
|
|
480
|
+
*/
|
|
481
|
+
observeLateAttributes() {
|
|
482
|
+
const lateAttributes = getLateAttributeLookup(this.definition);
|
|
483
|
+
if (lateAttributes === null) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const element = this.source;
|
|
487
|
+
if (element[lateAttributeObserver] !== void 0) {
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
element[lateAttributeObserver] = new MutationObserver(records => {
|
|
491
|
+
const controller = element.$fastController;
|
|
492
|
+
const lateAttributes = getLateAttributeLookup(controller.definition);
|
|
493
|
+
if (lateAttributes === null) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
for (let i = 0, ii = records.length; i < ii; ++i) {
|
|
497
|
+
const attributeName = records[i].attributeName;
|
|
498
|
+
if (attributeName === null || lateAttributes[attributeName] === void 0) {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
controller.onAttributeChangedCallback(attributeName, null, element.getAttribute(attributeName));
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
element[lateAttributeObserver].observe(element, {
|
|
505
|
+
attributes: true,
|
|
506
|
+
attributeFilter: Object.keys(lateAttributes),
|
|
507
|
+
});
|
|
508
|
+
}
|
|
380
509
|
/**
|
|
381
510
|
* Connects any existing behaviors on the associated element.
|
|
382
511
|
*/
|
|
@@ -457,30 +586,51 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
457
586
|
*/
|
|
458
587
|
renderTemplate(template) {
|
|
459
588
|
var _a;
|
|
460
|
-
// When getting the host to render to, we start by looking
|
|
461
|
-
// up the shadow root. If there isn't one, then that means
|
|
462
|
-
// we're doing a Light DOM render to the element's direct children.
|
|
463
589
|
const element = this.source;
|
|
464
590
|
const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
|
|
465
591
|
if (this.view !== null) {
|
|
466
|
-
// If there's already a view, we need to unbind and remove through dispose.
|
|
467
592
|
this.view.dispose();
|
|
468
593
|
this.view = null;
|
|
469
594
|
}
|
|
470
595
|
else if (!this.needsInitialization || this.hasExistingShadowRoot) {
|
|
471
|
-
this.hasExistingShadowRoot
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
596
|
+
if (!this.hasExistingShadowRoot || !this.needsInitialization) {
|
|
597
|
+
for (let child = host.firstChild; child !== null; child = host.firstChild) {
|
|
598
|
+
host.removeChild(child);
|
|
599
|
+
}
|
|
475
600
|
}
|
|
476
601
|
}
|
|
477
602
|
if (template) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
603
|
+
const hasPrerenderedContent = this.hasExistingShadowRoot && this.needsInitialization;
|
|
604
|
+
let didHydrate = false;
|
|
605
|
+
if (hasPrerenderedContent && ElementController.hydrationHook) {
|
|
606
|
+
didHydrate = ElementController.hydrationHook(this, template, element, host);
|
|
607
|
+
}
|
|
608
|
+
if (!didHydrate) {
|
|
609
|
+
this.renderClientSide(template, element, host);
|
|
610
|
+
}
|
|
611
|
+
this._resolvePrerendered(hasPrerenderedContent);
|
|
612
|
+
this._resolveHydrated(didHydrate);
|
|
613
|
+
}
|
|
614
|
+
else if (this.needsInitialization) {
|
|
615
|
+
this._resolvePrerendered(false);
|
|
616
|
+
this._resolveHydrated(false);
|
|
482
617
|
}
|
|
483
618
|
}
|
|
619
|
+
/**
|
|
620
|
+
* Standard client-side render: clears any stale content, clones the
|
|
621
|
+
* compiled fragment, binds, and appends to the host.
|
|
622
|
+
*/
|
|
623
|
+
renderClientSide(template, element, host) {
|
|
624
|
+
if (this.hasExistingShadowRoot) {
|
|
625
|
+
for (let child = host.firstChild; child !== null; child = host.firstChild) {
|
|
626
|
+
host.removeChild(child);
|
|
627
|
+
}
|
|
628
|
+
this.hasExistingShadowRoot = false;
|
|
629
|
+
}
|
|
630
|
+
this.view = template.render(element, host, element);
|
|
631
|
+
this.view.sourceLifetime =
|
|
632
|
+
SourceLifetime.coupled;
|
|
633
|
+
}
|
|
484
634
|
/**
|
|
485
635
|
* Locates or creates a controller for the specified element.
|
|
486
636
|
* @param element - The element to return the controller for.
|
|
@@ -521,7 +671,22 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
521
671
|
static setStrategy(strategy) {
|
|
522
672
|
elementControllerStrategy = strategy;
|
|
523
673
|
}
|
|
674
|
+
/**
|
|
675
|
+
* Installs the hydration hook. Called by enableHydration().
|
|
676
|
+
* @internal
|
|
677
|
+
*/
|
|
678
|
+
static installHydrationHook(hook) {
|
|
679
|
+
ElementController.hydrationHook = hook;
|
|
680
|
+
}
|
|
524
681
|
}
|
|
682
|
+
// --- Static hydration hook ---
|
|
683
|
+
/**
|
|
684
|
+
* A hook that, when set, handles prerendered content hydration.
|
|
685
|
+
* Called by renderTemplate when an existing shadow root is detected.
|
|
686
|
+
* Returns true if hydration was performed, false to fall back to client-side.
|
|
687
|
+
* @internal
|
|
688
|
+
*/
|
|
689
|
+
ElementController.hydrationHook = null;
|
|
525
690
|
makeSerializationNoop(ElementController);
|
|
526
691
|
// Set default strategy for ElementController
|
|
527
692
|
ElementController.setStrategy(ElementController);
|
|
@@ -633,7 +798,7 @@ if (ElementStyles.supportsAdoptedStyleSheets) {
|
|
|
633
798
|
}
|
|
634
799
|
};
|
|
635
800
|
}
|
|
636
|
-
catch (
|
|
801
|
+
catch (_e) {
|
|
637
802
|
// Do nothing if an error is thrown, the default
|
|
638
803
|
// case handles FrozenArray.
|
|
639
804
|
}
|
|
@@ -642,232 +807,3 @@ if (ElementStyles.supportsAdoptedStyleSheets) {
|
|
|
642
807
|
else {
|
|
643
808
|
ElementStyles.setDefaultStrategy(StyleElementStrategy);
|
|
644
809
|
}
|
|
645
|
-
/**
|
|
646
|
-
* The attribute used to indicate that an element needs hydration.
|
|
647
|
-
* @public
|
|
648
|
-
*/
|
|
649
|
-
export const needsHydrationAttribute = "needs-hydration";
|
|
650
|
-
/**
|
|
651
|
-
* An ElementController capable of hydrating FAST elements from
|
|
652
|
-
* Declarative Shadow DOM.
|
|
653
|
-
*
|
|
654
|
-
* @beta
|
|
655
|
-
*/
|
|
656
|
-
export class HydratableElementController extends ElementController {
|
|
657
|
-
/**
|
|
658
|
-
* {@inheritdoc ElementController.shadowOptions}
|
|
659
|
-
*/
|
|
660
|
-
get shadowOptions() {
|
|
661
|
-
return super.shadowOptions;
|
|
662
|
-
}
|
|
663
|
-
set shadowOptions(value) {
|
|
664
|
-
super.shadowOptions = value;
|
|
665
|
-
if ((this.hasExistingShadowRoot || (value !== void 0 && !this.template)) &&
|
|
666
|
-
this.definition.templateOptions === TemplateOptions.deferAndHydrate) {
|
|
667
|
-
this.source.toggleAttribute(deferHydrationAttribute, true);
|
|
668
|
-
this.source.toggleAttribute(needsHydrationAttribute, true);
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
/**
|
|
672
|
-
* Adds the current element instance to the hydrating instances map
|
|
673
|
-
*/
|
|
674
|
-
addHydratingInstance() {
|
|
675
|
-
if (!HydratableElementController.hydratingInstances) {
|
|
676
|
-
return;
|
|
677
|
-
}
|
|
678
|
-
const name = this.definition.name;
|
|
679
|
-
let instances = HydratableElementController.hydratingInstances.get(name);
|
|
680
|
-
if (!instances) {
|
|
681
|
-
instances = new Set();
|
|
682
|
-
HydratableElementController.hydratingInstances.set(name, instances);
|
|
683
|
-
}
|
|
684
|
-
instances.add(this.source);
|
|
685
|
-
}
|
|
686
|
-
/**
|
|
687
|
-
* Configure lifecycle callbacks for hydration events
|
|
688
|
-
*/
|
|
689
|
-
static config(callbacks) {
|
|
690
|
-
HydratableElementController.lifecycleCallbacks = callbacks;
|
|
691
|
-
return this;
|
|
692
|
-
}
|
|
693
|
-
static hydrationObserverHandler(records) {
|
|
694
|
-
for (const record of records) {
|
|
695
|
-
if (!record.target.hasAttribute(deferHydrationAttribute)) {
|
|
696
|
-
HydratableElementController.hydrationObserver.unobserve(record.target);
|
|
697
|
-
record.target.$fastController.connect();
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
/**
|
|
702
|
-
* Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
|
|
703
|
-
* Then resets the ElementController strategy to the default so that future elements
|
|
704
|
-
* don't use the HydratableElementController.
|
|
705
|
-
*
|
|
706
|
-
* @param deadline - the idle deadline object
|
|
707
|
-
*/
|
|
708
|
-
static checkHydrationComplete(deadline) {
|
|
709
|
-
var _a, _b, _c;
|
|
710
|
-
if (deadline.didTimeout) {
|
|
711
|
-
HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
|
|
712
|
-
return;
|
|
713
|
-
}
|
|
714
|
-
// If there are no more hydrating instances, invoke the hydrationComplete callback
|
|
715
|
-
if (((_a = HydratableElementController.hydratingInstances) === null || _a === void 0 ? void 0 : _a.size) === 0) {
|
|
716
|
-
try {
|
|
717
|
-
(_c = (_b = HydratableElementController.lifecycleCallbacks).hydrationComplete) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
718
|
-
}
|
|
719
|
-
catch (_d) {
|
|
720
|
-
// A lifecycle callback must never prevent post-hydration cleanup.
|
|
721
|
-
}
|
|
722
|
-
// Reset to the default strategy after hydration is complete
|
|
723
|
-
ElementController.setStrategy(ElementController);
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
/**
|
|
727
|
-
* Runs connected lifecycle behavior on the associated element.
|
|
728
|
-
*/
|
|
729
|
-
connect() {
|
|
730
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
731
|
-
// Initialize needsHydration on first connect
|
|
732
|
-
this.needsHydration =
|
|
733
|
-
(_a = this.needsHydration) !== null && _a !== void 0 ? _a : this.source.hasAttribute(needsHydrationAttribute);
|
|
734
|
-
if (this.needsHydration) {
|
|
735
|
-
this.addHydratingInstance();
|
|
736
|
-
}
|
|
737
|
-
// If the `defer-hydration` attribute exists on the source,
|
|
738
|
-
// wait for it to be removed before continuing connection behavior.
|
|
739
|
-
if (this.source.hasAttribute(deferHydrationAttribute)) {
|
|
740
|
-
this.addHydratingInstance();
|
|
741
|
-
HydratableElementController.hydrationObserver.observe(this.source, {
|
|
742
|
-
attributeFilter: [deferHydrationAttribute],
|
|
743
|
-
});
|
|
744
|
-
return;
|
|
745
|
-
}
|
|
746
|
-
// If the controller does not need to be hydrated, defer connection behavior
|
|
747
|
-
// to the base-class. This case handles element re-connection and initial connection
|
|
748
|
-
// of elements that did not get declarative shadow-dom emitted, as well as if an extending
|
|
749
|
-
// class
|
|
750
|
-
if (!this.needsHydration) {
|
|
751
|
-
super.connect();
|
|
752
|
-
this.removeHydratingInstance();
|
|
753
|
-
return;
|
|
754
|
-
}
|
|
755
|
-
if (this.stage !== Stages.disconnected) {
|
|
756
|
-
return;
|
|
757
|
-
}
|
|
758
|
-
if (!HydratableElementController.hydrationStarted) {
|
|
759
|
-
HydratableElementController.hydrationStarted = true;
|
|
760
|
-
try {
|
|
761
|
-
(_c = (_b = HydratableElementController.lifecycleCallbacks).hydrationStarted) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
762
|
-
}
|
|
763
|
-
catch (_h) {
|
|
764
|
-
// A lifecycle callback must never prevent hydration.
|
|
765
|
-
}
|
|
766
|
-
}
|
|
767
|
-
try {
|
|
768
|
-
(_e = (_d = HydratableElementController.lifecycleCallbacks).elementWillHydrate) === null || _e === void 0 ? void 0 : _e.call(_d, this.source);
|
|
769
|
-
}
|
|
770
|
-
catch (_j) {
|
|
771
|
-
// A lifecycle callback must never prevent hydration.
|
|
772
|
-
}
|
|
773
|
-
this.stage = Stages.connecting;
|
|
774
|
-
this.bindObservables();
|
|
775
|
-
this.connectBehaviors();
|
|
776
|
-
if (this.template) {
|
|
777
|
-
if (isHydratable(this.template)) {
|
|
778
|
-
const element = this.source;
|
|
779
|
-
const host = (_f = getShadowRoot(element)) !== null && _f !== void 0 ? _f : element;
|
|
780
|
-
let firstChild = host.firstChild;
|
|
781
|
-
let lastChild = host.lastChild;
|
|
782
|
-
if (element.shadowRoot === null) {
|
|
783
|
-
// handle element boundary markers when shadowRoot is not present
|
|
784
|
-
if (HydrationMarkup.isElementBoundaryStartMarker(firstChild)) {
|
|
785
|
-
firstChild.data = "";
|
|
786
|
-
firstChild = firstChild.nextSibling;
|
|
787
|
-
}
|
|
788
|
-
if (HydrationMarkup.isElementBoundaryEndMarker(lastChild)) {
|
|
789
|
-
lastChild.data = "";
|
|
790
|
-
lastChild = lastChild.previousSibling;
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
this.view = this.template.hydrate(firstChild, lastChild, element);
|
|
794
|
-
(_g = this.view) === null || _g === void 0 ? void 0 : _g.bind(this.source);
|
|
795
|
-
}
|
|
796
|
-
else {
|
|
797
|
-
this.renderTemplate(this.template);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
this.addStyles(this.mainStyles);
|
|
801
|
-
this.stage = Stages.connected;
|
|
802
|
-
this.source.removeAttribute(needsHydrationAttribute);
|
|
803
|
-
this.needsInitialization = this.needsHydration = false;
|
|
804
|
-
this.removeHydratingInstance();
|
|
805
|
-
Observable.notify(this, isConnectedPropertyName);
|
|
806
|
-
}
|
|
807
|
-
/**
|
|
808
|
-
* Removes the current element instance from the hydrating instances map
|
|
809
|
-
*/
|
|
810
|
-
removeHydratingInstance() {
|
|
811
|
-
var _a, _b;
|
|
812
|
-
if (!HydratableElementController.hydratingInstances) {
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
try {
|
|
816
|
-
(_b = (_a = HydratableElementController.lifecycleCallbacks).elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.source);
|
|
817
|
-
}
|
|
818
|
-
catch (_c) {
|
|
819
|
-
// A lifecycle callback must never prevent hydration.
|
|
820
|
-
}
|
|
821
|
-
const name = this.definition.name;
|
|
822
|
-
const instances = HydratableElementController.hydratingInstances.get(name);
|
|
823
|
-
if (instances) {
|
|
824
|
-
instances.delete(this.source);
|
|
825
|
-
if (!instances.size) {
|
|
826
|
-
HydratableElementController.hydratingInstances.delete(name);
|
|
827
|
-
}
|
|
828
|
-
if (HydratableElementController.idleCallbackId) {
|
|
829
|
-
cancelIdleCallback(HydratableElementController.idleCallbackId);
|
|
830
|
-
}
|
|
831
|
-
HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
|
|
832
|
-
}
|
|
833
|
-
}
|
|
834
|
-
/**
|
|
835
|
-
* Unregisters the hydration observer when the element is disconnected.
|
|
836
|
-
*/
|
|
837
|
-
disconnect() {
|
|
838
|
-
super.disconnect();
|
|
839
|
-
HydratableElementController.hydrationObserver.unobserve(this.source);
|
|
840
|
-
}
|
|
841
|
-
/**
|
|
842
|
-
* Sets the ElementController strategy to HydratableElementController.
|
|
843
|
-
* @remarks
|
|
844
|
-
* This method is typically called during application startup to enable
|
|
845
|
-
* hydration support for FAST elements.
|
|
846
|
-
*/
|
|
847
|
-
static install() {
|
|
848
|
-
ElementController.setStrategy(HydratableElementController);
|
|
849
|
-
}
|
|
850
|
-
}
|
|
851
|
-
HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
|
|
852
|
-
/**
|
|
853
|
-
* Lifecycle callbacks for hydration events
|
|
854
|
-
*/
|
|
855
|
-
HydratableElementController.lifecycleCallbacks = {};
|
|
856
|
-
/**
|
|
857
|
-
* Whether the hydrationStarted callback has already been invoked.
|
|
858
|
-
*/
|
|
859
|
-
HydratableElementController.hydrationStarted = false;
|
|
860
|
-
/**
|
|
861
|
-
* An idle callback ID used to track hydration completion
|
|
862
|
-
*/
|
|
863
|
-
HydratableElementController.idleCallbackId = null;
|
|
864
|
-
/**
|
|
865
|
-
* A map of element instances by the name of the custom element they are
|
|
866
|
-
* associated with. The key is the custom element name, and the value is the
|
|
867
|
-
* instances of hydratable elements which currently need to be hydrated.
|
|
868
|
-
*
|
|
869
|
-
* When all of the instances in the set have been hydrated, the set is
|
|
870
|
-
* cleared and removed from the map. If the map is empty, the
|
|
871
|
-
* hydrationComplete callback is invoked.
|
|
872
|
-
*/
|
|
873
|
-
HydratableElementController.hydratingInstances = new Map();
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { ElementController } from "./element-controller.js";
|
|
2
2
|
export * from "./hydration.js";
|