@microsoft/fast-element 2.0.0-beta.9 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +1 -1
- package/CHANGELOG.json +518 -0
- package/CHANGELOG.md +181 -1
- package/README.md +1 -9
- package/api-extractor.context.json +14 -0
- package/api-extractor.di.json +14 -0
- package/dist/context/context.api.json +1068 -0
- package/dist/di/di.api.json +4929 -0
- package/dist/dts/binding/binding.d.ts +49 -0
- package/dist/dts/binding/normalize.d.ts +9 -0
- package/dist/dts/binding/one-time.d.ts +11 -0
- package/dist/dts/binding/one-way.d.ts +20 -0
- package/dist/dts/{templating/binding-signal.d.ts → binding/signal.d.ts} +19 -4
- package/dist/dts/{templating/binding-two-way.d.ts → binding/two-way.d.ts} +9 -5
- package/dist/dts/components/attributes.d.ts +7 -1
- package/dist/dts/components/element-controller.d.ts +104 -8
- package/dist/dts/components/element-hydration.d.ts +2 -0
- package/dist/dts/components/fast-definitions.d.ts +6 -0
- package/dist/dts/components/hydration.d.ts +56 -0
- package/dist/dts/components/install-hydration.d.ts +1 -0
- package/dist/dts/context.d.ts +29 -15
- package/dist/dts/di/di.d.ts +0 -5
- package/dist/dts/dom-policy.d.ts +83 -0
- package/dist/dts/dom.d.ts +100 -0
- package/dist/dts/hydration/target-builder.d.ts +63 -0
- package/dist/dts/index.d.ts +33 -26
- package/dist/dts/index.rollup.d.ts +0 -1
- package/dist/dts/index.rollup.debug.d.ts +0 -1
- package/dist/dts/interfaces.d.ts +32 -82
- package/dist/dts/metadata.d.ts +6 -5
- package/dist/dts/observation/arrays.d.ts +1 -1
- package/dist/dts/observation/observable.bench.d.ts +18 -0
- package/dist/dts/observation/observable.d.ts +5 -5
- package/dist/dts/pending-task.d.ts +19 -7
- package/dist/dts/platform.d.ts +11 -2
- package/dist/dts/polyfills.d.ts +0 -8
- package/dist/dts/styles/css-binding-directive.d.ts +60 -0
- package/dist/dts/styles/css.d.ts +9 -7
- package/dist/dts/styles/element-styles.d.ts +1 -14
- package/dist/dts/styles/host.d.ts +2 -5
- package/dist/dts/styles/style-strategy.d.ts +42 -0
- package/dist/dts/templating/compiler.d.ts +11 -13
- package/dist/dts/templating/{binding.d.ts → html-binding-directive.d.ts} +21 -41
- package/dist/dts/templating/html-directive.d.ts +44 -140
- package/dist/dts/templating/install-hydratable-view-templates.d.ts +1 -0
- package/dist/dts/templating/node-observation.d.ts +11 -1
- package/dist/dts/templating/ref.d.ts +4 -0
- package/dist/dts/templating/render.bench.d.ts +3 -0
- package/dist/dts/templating/render.d.ts +49 -9
- package/dist/dts/templating/repeat-basic-reverse.bench.d.ts +3 -0
- package/dist/dts/templating/repeat-basic-shift.bench.d.ts +3 -0
- package/dist/dts/templating/repeat.d.ts +31 -9
- package/dist/dts/templating/template.d.ts +97 -12
- package/dist/dts/templating/view.d.ts +146 -29
- package/dist/dts/templating/when-basic.bench.d.ts +3 -0
- package/dist/dts/templating/when-conditional.bench.d.ts +3 -0
- package/dist/dts/templating/when-switch.bench.d.ts +3 -0
- package/dist/dts/templating/when.d.ts +3 -1
- package/dist/dts/testing/fakes.d.ts +12 -1
- package/dist/dts/tsdoc-metadata.json +1 -1
- package/dist/dts/utilities.d.ts +55 -1
- package/dist/esm/binding/binding.js +18 -0
- package/dist/esm/binding/normalize.js +17 -0
- package/dist/esm/binding/one-time.js +21 -0
- package/dist/esm/binding/one-way.js +30 -0
- package/dist/esm/{templating/binding-signal.js → binding/signal.js} +22 -6
- package/dist/esm/{templating/binding-two-way.js → binding/two-way.js} +18 -12
- package/dist/esm/components/attributes.js +19 -6
- package/dist/esm/components/element-controller.js +319 -49
- package/dist/esm/components/element-hydration.js +2 -0
- package/dist/esm/components/fast-definitions.js +12 -4
- package/dist/esm/components/fast-element.js +3 -1
- package/dist/esm/components/hydration.js +104 -0
- package/dist/esm/components/install-hydration.js +3 -0
- package/dist/esm/context.js +26 -4
- package/dist/esm/debug.js +8 -2
- package/dist/esm/di/di.js +9 -12
- package/dist/esm/dom-policy.js +345 -0
- package/dist/esm/dom.js +101 -0
- package/dist/esm/hydration/target-builder.js +175 -0
- package/dist/esm/index.js +34 -25
- package/dist/esm/index.rollup.debug.js +3 -1
- package/dist/esm/index.rollup.js +3 -1
- package/dist/esm/interfaces.js +51 -3
- package/dist/esm/metadata.js +11 -8
- package/dist/esm/observation/arrays.js +1 -1
- package/dist/esm/observation/observable.bench.js +79 -0
- package/dist/esm/observation/observable.js +20 -15
- package/dist/esm/observation/update-queue.js +2 -2
- package/dist/esm/pending-task.js +13 -1
- package/dist/esm/platform.js +12 -2
- package/dist/esm/polyfills.js +3 -61
- package/dist/esm/styles/css-binding-directive.js +76 -0
- package/dist/esm/styles/css.js +14 -7
- package/dist/esm/styles/element-styles.js +0 -33
- package/dist/esm/styles/style-strategy.js +1 -0
- package/dist/esm/templating/children.js +8 -4
- package/dist/esm/templating/compiler.js +37 -44
- package/dist/esm/templating/html-binding-directive.js +218 -0
- package/dist/esm/templating/html-directive.js +25 -152
- package/dist/esm/templating/install-hydratable-view-templates.js +17 -0
- package/dist/esm/templating/node-observation.js +14 -8
- package/dist/esm/templating/ref.js +1 -1
- package/dist/esm/templating/render.bench.js +56 -0
- package/dist/esm/templating/render.js +74 -30
- package/dist/esm/templating/repeat-basic-reverse.bench.js +43 -0
- package/dist/esm/templating/repeat-basic-shift.bench.js +43 -0
- package/dist/esm/templating/repeat.js +116 -17
- package/dist/esm/templating/template.js +135 -60
- package/dist/esm/templating/view.js +254 -34
- package/dist/esm/templating/when-basic.bench.js +36 -0
- package/dist/esm/templating/when-conditional.bench.js +39 -0
- package/dist/esm/templating/when-switch.bench.js +68 -0
- package/dist/esm/templating/when.js +12 -5
- package/dist/esm/testing/fakes.js +32 -1
- package/dist/esm/testing/fixture.js +1 -1
- package/dist/esm/utilities.js +97 -1
- package/dist/fast-element.api.json +9789 -5667
- package/dist/fast-element.d.ts +813 -2392
- package/dist/fast-element.debug.js +2788 -974
- package/dist/fast-element.debug.min.js +3 -1
- package/dist/fast-element.js +2641 -833
- package/dist/fast-element.min.js +3 -1
- package/dist/fast-element.untrimmed.d.ts +662 -314
- package/docs/{api-report.md → api-report.api.md} +238 -151
- package/docs/context/api-report.api.md +69 -0
- package/docs/di/api-report.api.md +315 -0
- package/karma.conf.cjs +2 -1
- package/package.json +59 -47
- package/scripts/run-api-extractor.js +51 -0
- package/scripts/run-benchmarks.js +46 -0
- package/tensile.config.js +12 -0
- package/dist/dts/templating/dom.d.ts +0 -41
- package/dist/esm/templating/binding.js +0 -282
- package/dist/esm/templating/dom.js +0 -49
- package/docs/guide/declaring-templates.md +0 -230
- package/docs/guide/defining-elements.md +0 -214
- package/docs/guide/leveraging-css.md +0 -253
- package/docs/guide/next-steps.md +0 -13
- package/docs/guide/observables-and-state.md +0 -213
- package/docs/guide/using-directives.md +0 -576
- package/docs/guide/working-with-shadow-dom.md +0 -296
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import "../interfaces.js";
|
|
2
2
|
import { PropertyChangeNotifier } from "../observation/notifier.js";
|
|
3
|
-
import { Observable, SourceLifetime } from "../observation/observable.js";
|
|
4
|
-
import { FAST } from "../platform.js";
|
|
3
|
+
import { ExecutionContext, Observable, SourceLifetime, } from "../observation/observable.js";
|
|
4
|
+
import { FAST, makeSerializationNoop } from "../platform.js";
|
|
5
|
+
import { ElementStyles } from "../styles/element-styles.js";
|
|
6
|
+
import { UnobservableMutationObserver } from "../utilities.js";
|
|
5
7
|
import { FASTElementDefinition } from "./fast-definitions.js";
|
|
8
|
+
import { HydrationMarkup, isHydratable } from "./hydration.js";
|
|
6
9
|
const defaultEventOptions = {
|
|
7
10
|
bubbles: true,
|
|
8
11
|
composed: true,
|
|
@@ -14,6 +17,7 @@ function getShadowRoot(element) {
|
|
|
14
17
|
var _a, _b;
|
|
15
18
|
return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
|
|
16
19
|
}
|
|
20
|
+
let elementControllerStrategy;
|
|
17
21
|
/**
|
|
18
22
|
* Controls the lifecycle and rendering of a `FASTElement`.
|
|
19
23
|
* @public
|
|
@@ -32,8 +36,19 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
32
36
|
this.needsInitialization = true;
|
|
33
37
|
this.hasExistingShadowRoot = false;
|
|
34
38
|
this._template = null;
|
|
35
|
-
this.
|
|
39
|
+
this.stage = 3 /* Stages.disconnected */;
|
|
40
|
+
/**
|
|
41
|
+
* A guard against connecting behaviors multiple times
|
|
42
|
+
* during connect in scenarios where a behavior adds
|
|
43
|
+
* another behavior during it's connectedCallback
|
|
44
|
+
*/
|
|
45
|
+
this.guardBehaviorConnection = false;
|
|
36
46
|
this.behaviors = null;
|
|
47
|
+
/**
|
|
48
|
+
* Tracks whether behaviors are connected so that
|
|
49
|
+
* behaviors cant be connected multiple times
|
|
50
|
+
*/
|
|
51
|
+
this.behaviorsConnected = false;
|
|
37
52
|
this._mainStyles = null;
|
|
38
53
|
/**
|
|
39
54
|
* This allows Observable.getNotifier(...) to return the Controller
|
|
@@ -88,11 +103,28 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
88
103
|
*/
|
|
89
104
|
get isConnected() {
|
|
90
105
|
Observable.track(this, isConnectedPropertyName);
|
|
91
|
-
return this.
|
|
106
|
+
return this.stage === 1 /* Stages.connected */;
|
|
92
107
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
/**
|
|
109
|
+
* The context the expression is evaluated against.
|
|
110
|
+
*/
|
|
111
|
+
get context() {
|
|
112
|
+
var _a, _b;
|
|
113
|
+
return (_b = (_a = this.view) === null || _a === void 0 ? void 0 : _a.context) !== null && _b !== void 0 ? _b : ExecutionContext.default;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Indicates whether the controller is bound.
|
|
117
|
+
*/
|
|
118
|
+
get isBound() {
|
|
119
|
+
var _a, _b;
|
|
120
|
+
return (_b = (_a = this.view) === null || _a === void 0 ? void 0 : _a.isBound) !== null && _b !== void 0 ? _b : false;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Indicates how the source's lifetime relates to the controller's lifetime.
|
|
124
|
+
*/
|
|
125
|
+
get sourceLifetime() {
|
|
126
|
+
var _a;
|
|
127
|
+
return (_a = this.view) === null || _a === void 0 ? void 0 : _a.sourceLifetime;
|
|
96
128
|
}
|
|
97
129
|
/**
|
|
98
130
|
* Gets/sets the template used to render the component.
|
|
@@ -156,6 +188,14 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
156
188
|
this.addStyles(value);
|
|
157
189
|
}
|
|
158
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* Registers an unbind handler with the controller.
|
|
193
|
+
* @param behavior - An object to call when the controller unbinds.
|
|
194
|
+
*/
|
|
195
|
+
onUnbind(behavior) {
|
|
196
|
+
var _a;
|
|
197
|
+
(_a = this.view) === null || _a === void 0 ? void 0 : _a.onUnbind(behavior);
|
|
198
|
+
}
|
|
159
199
|
/**
|
|
160
200
|
* Adds the behavior to the component.
|
|
161
201
|
* @param behavior - The behavior to add.
|
|
@@ -167,7 +207,9 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
167
207
|
if (count === 0) {
|
|
168
208
|
targetBehaviors.set(behavior, 1);
|
|
169
209
|
behavior.addedCallback && behavior.addedCallback(this);
|
|
170
|
-
if (behavior.connectedCallback &&
|
|
210
|
+
if (behavior.connectedCallback &&
|
|
211
|
+
!this.guardBehaviorConnection &&
|
|
212
|
+
(this.stage === 1 /* Stages.connected */ || this.stage === 0 /* Stages.connecting */)) {
|
|
171
213
|
behavior.connectedCallback(this);
|
|
172
214
|
}
|
|
173
215
|
}
|
|
@@ -191,7 +233,7 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
191
233
|
}
|
|
192
234
|
if (count === 1 || force) {
|
|
193
235
|
targetBehaviors.delete(behavior);
|
|
194
|
-
if (behavior.disconnectedCallback && this.
|
|
236
|
+
if (behavior.disconnectedCallback && this.stage !== 3 /* Stages.disconnected */) {
|
|
195
237
|
behavior.disconnectedCallback(this);
|
|
196
238
|
}
|
|
197
239
|
behavior.removedCallback && behavior.removedCallback(this);
|
|
@@ -210,13 +252,13 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
210
252
|
return;
|
|
211
253
|
}
|
|
212
254
|
const source = this.source;
|
|
213
|
-
const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
|
|
214
255
|
if (styles instanceof HTMLElement) {
|
|
256
|
+
const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : this.source;
|
|
215
257
|
target.append(styles);
|
|
216
258
|
}
|
|
217
|
-
else if (!styles.isAttachedTo(
|
|
259
|
+
else if (!styles.isAttachedTo(source)) {
|
|
218
260
|
const sourceBehaviors = styles.behaviors;
|
|
219
|
-
styles.addStylesTo(
|
|
261
|
+
styles.addStylesTo(source);
|
|
220
262
|
if (sourceBehaviors !== null) {
|
|
221
263
|
for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
|
|
222
264
|
this.addBehavior(sourceBehaviors[i]);
|
|
@@ -234,16 +276,16 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
234
276
|
return;
|
|
235
277
|
}
|
|
236
278
|
const source = this.source;
|
|
237
|
-
const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source.getRootNode();
|
|
238
279
|
if (styles instanceof HTMLElement) {
|
|
280
|
+
const target = (_a = getShadowRoot(source)) !== null && _a !== void 0 ? _a : source;
|
|
239
281
|
target.removeChild(styles);
|
|
240
282
|
}
|
|
241
|
-
else if (styles.isAttachedTo(
|
|
283
|
+
else if (styles.isAttachedTo(source)) {
|
|
242
284
|
const sourceBehaviors = styles.behaviors;
|
|
243
|
-
styles.removeStylesFrom(
|
|
285
|
+
styles.removeStylesFrom(source);
|
|
244
286
|
if (sourceBehaviors !== null) {
|
|
245
287
|
for (let i = 0, ii = sourceBehaviors.length; i < ii; ++i) {
|
|
246
|
-
this.
|
|
288
|
+
this.removeBehavior(sourceBehaviors[i]);
|
|
247
289
|
}
|
|
248
290
|
}
|
|
249
291
|
}
|
|
@@ -252,40 +294,73 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
252
294
|
* Runs connected lifecycle behavior on the associated element.
|
|
253
295
|
*/
|
|
254
296
|
connect() {
|
|
255
|
-
if (this.
|
|
297
|
+
if (this.stage !== 3 /* Stages.disconnected */) {
|
|
256
298
|
return;
|
|
257
299
|
}
|
|
300
|
+
this.stage = 0 /* Stages.connecting */;
|
|
301
|
+
this.bindObservables();
|
|
302
|
+
this.connectBehaviors();
|
|
258
303
|
if (this.needsInitialization) {
|
|
259
|
-
this.
|
|
304
|
+
this.renderTemplate(this.template);
|
|
305
|
+
this.addStyles(this.mainStyles);
|
|
306
|
+
this.needsInitialization = false;
|
|
260
307
|
}
|
|
261
308
|
else if (this.view !== null) {
|
|
262
309
|
this.view.bind(this.source);
|
|
263
310
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
311
|
+
this.stage = 1 /* Stages.connected */;
|
|
312
|
+
Observable.notify(this, isConnectedPropertyName);
|
|
313
|
+
}
|
|
314
|
+
bindObservables() {
|
|
315
|
+
if (this.boundObservables !== null) {
|
|
316
|
+
const element = this.source;
|
|
317
|
+
const boundObservables = this.boundObservables;
|
|
318
|
+
const propertyNames = Object.keys(boundObservables);
|
|
319
|
+
for (let i = 0, ii = propertyNames.length; i < ii; ++i) {
|
|
320
|
+
const propertyName = propertyNames[i];
|
|
321
|
+
element[propertyName] = boundObservables[propertyName];
|
|
322
|
+
}
|
|
323
|
+
this.boundObservables = null;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
connectBehaviors() {
|
|
327
|
+
if (this.behaviorsConnected === false) {
|
|
328
|
+
const behaviors = this.behaviors;
|
|
329
|
+
if (behaviors !== null) {
|
|
330
|
+
this.guardBehaviorConnection = true;
|
|
331
|
+
for (const key of behaviors.keys()) {
|
|
332
|
+
key.connectedCallback && key.connectedCallback(this);
|
|
333
|
+
}
|
|
334
|
+
this.guardBehaviorConnection = false;
|
|
335
|
+
}
|
|
336
|
+
this.behaviorsConnected = true;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
disconnectBehaviors() {
|
|
340
|
+
if (this.behaviorsConnected === true) {
|
|
341
|
+
const behaviors = this.behaviors;
|
|
342
|
+
if (behaviors !== null) {
|
|
343
|
+
for (const key of behaviors.keys()) {
|
|
344
|
+
key.disconnectedCallback && key.disconnectedCallback(this);
|
|
345
|
+
}
|
|
268
346
|
}
|
|
347
|
+
this.behaviorsConnected = false;
|
|
269
348
|
}
|
|
270
|
-
this.setIsConnected(true);
|
|
271
349
|
}
|
|
272
350
|
/**
|
|
273
351
|
* Runs disconnected lifecycle behavior on the associated element.
|
|
274
352
|
*/
|
|
275
353
|
disconnect() {
|
|
276
|
-
if (
|
|
354
|
+
if (this.stage !== 1 /* Stages.connected */) {
|
|
277
355
|
return;
|
|
278
356
|
}
|
|
279
|
-
this.
|
|
357
|
+
this.stage = 2 /* Stages.disconnecting */;
|
|
358
|
+
Observable.notify(this, isConnectedPropertyName);
|
|
280
359
|
if (this.view !== null) {
|
|
281
360
|
this.view.unbind();
|
|
282
361
|
}
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
for (const key of behaviors.keys()) {
|
|
286
|
-
key.disconnectedCallback && key.disconnectedCallback(this);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
362
|
+
this.disconnectBehaviors();
|
|
363
|
+
this.stage = 3 /* Stages.disconnected */;
|
|
289
364
|
}
|
|
290
365
|
/**
|
|
291
366
|
* Runs the attribute changed callback for the associated element.
|
|
@@ -308,27 +383,11 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
308
383
|
* Only emits events if connected.
|
|
309
384
|
*/
|
|
310
385
|
emit(type, detail, options) {
|
|
311
|
-
if (this.
|
|
386
|
+
if (this.stage === 1 /* Stages.connected */) {
|
|
312
387
|
return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
|
|
313
388
|
}
|
|
314
389
|
return false;
|
|
315
390
|
}
|
|
316
|
-
finishInitialization() {
|
|
317
|
-
const element = this.source;
|
|
318
|
-
const boundObservables = this.boundObservables;
|
|
319
|
-
// If we have any observables that were bound, re-apply their values.
|
|
320
|
-
if (boundObservables !== null) {
|
|
321
|
-
const propertyNames = Object.keys(boundObservables);
|
|
322
|
-
for (let i = 0, ii = propertyNames.length; i < ii; ++i) {
|
|
323
|
-
const propertyName = propertyNames[i];
|
|
324
|
-
element[propertyName] = boundObservables[propertyName];
|
|
325
|
-
}
|
|
326
|
-
this.boundObservables = null;
|
|
327
|
-
}
|
|
328
|
-
this.renderTemplate(this.template);
|
|
329
|
-
this.addStyles(this.mainStyles);
|
|
330
|
-
this.needsInitialization = false;
|
|
331
|
-
}
|
|
332
391
|
renderTemplate(template) {
|
|
333
392
|
var _a;
|
|
334
393
|
// When getting the host to render to, we start by looking
|
|
@@ -372,6 +431,217 @@ export class ElementController extends PropertyChangeNotifier {
|
|
|
372
431
|
if (definition === void 0) {
|
|
373
432
|
throw FAST.error(1401 /* Message.missingElementDefinition */);
|
|
374
433
|
}
|
|
375
|
-
return (element.$fastController = new
|
|
434
|
+
return (element.$fastController = new elementControllerStrategy(element, definition));
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Sets the strategy that ElementController.forCustomElement uses to construct
|
|
438
|
+
* ElementController instances for an element.
|
|
439
|
+
* @param strategy - The strategy to use.
|
|
440
|
+
*/
|
|
441
|
+
static setStrategy(strategy) {
|
|
442
|
+
elementControllerStrategy = strategy;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
makeSerializationNoop(ElementController);
|
|
446
|
+
// Set default strategy for ElementController
|
|
447
|
+
ElementController.setStrategy(ElementController);
|
|
448
|
+
/**
|
|
449
|
+
* Converts a styleTarget into the operative target. When the provided target is an Element
|
|
450
|
+
* that is a FASTElement, the function will return the ShadowRoot for that element. Otherwise,
|
|
451
|
+
* it will return the root node for the element.
|
|
452
|
+
* @param target
|
|
453
|
+
* @returns
|
|
454
|
+
*/
|
|
455
|
+
function normalizeStyleTarget(target) {
|
|
456
|
+
var _a;
|
|
457
|
+
if ("adoptedStyleSheets" in target) {
|
|
458
|
+
return target;
|
|
459
|
+
}
|
|
460
|
+
else {
|
|
461
|
+
return ((_a = getShadowRoot(target)) !== null && _a !== void 0 ? _a : target.getRootNode());
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
// Default StyleStrategy implementations are defined in this module because they
|
|
465
|
+
// require access to element shadowRoots, and we don't want to leak shadowRoot
|
|
466
|
+
// objects out of this module.
|
|
467
|
+
/**
|
|
468
|
+
* https://wicg.github.io/construct-stylesheets/
|
|
469
|
+
* https://developers.google.com/web/updates/2019/02/constructable-stylesheets
|
|
470
|
+
*
|
|
471
|
+
* @internal
|
|
472
|
+
*/
|
|
473
|
+
export class AdoptedStyleSheetsStrategy {
|
|
474
|
+
constructor(styles) {
|
|
475
|
+
const styleSheetCache = AdoptedStyleSheetsStrategy.styleSheetCache;
|
|
476
|
+
this.sheets = styles.map((x) => {
|
|
477
|
+
if (x instanceof CSSStyleSheet) {
|
|
478
|
+
return x;
|
|
479
|
+
}
|
|
480
|
+
let sheet = styleSheetCache.get(x);
|
|
481
|
+
if (sheet === void 0) {
|
|
482
|
+
sheet = new CSSStyleSheet();
|
|
483
|
+
sheet.replaceSync(x);
|
|
484
|
+
styleSheetCache.set(x, sheet);
|
|
485
|
+
}
|
|
486
|
+
return sheet;
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
addStylesTo(target) {
|
|
490
|
+
addAdoptedStyleSheets(normalizeStyleTarget(target), this.sheets);
|
|
491
|
+
}
|
|
492
|
+
removeStylesFrom(target) {
|
|
493
|
+
removeAdoptedStyleSheets(normalizeStyleTarget(target), this.sheets);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
AdoptedStyleSheetsStrategy.styleSheetCache = new Map();
|
|
497
|
+
let id = 0;
|
|
498
|
+
const nextStyleId = () => `fast-${++id}`;
|
|
499
|
+
function usableStyleTarget(target) {
|
|
500
|
+
return target === document ? document.body : target;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* @internal
|
|
504
|
+
*/
|
|
505
|
+
export class StyleElementStrategy {
|
|
506
|
+
constructor(styles) {
|
|
507
|
+
this.styles = styles;
|
|
508
|
+
this.styleClass = nextStyleId();
|
|
509
|
+
}
|
|
510
|
+
addStylesTo(target) {
|
|
511
|
+
target = usableStyleTarget(normalizeStyleTarget(target));
|
|
512
|
+
const styles = this.styles;
|
|
513
|
+
const styleClass = this.styleClass;
|
|
514
|
+
for (let i = 0; i < styles.length; i++) {
|
|
515
|
+
const element = document.createElement("style");
|
|
516
|
+
element.innerHTML = styles[i];
|
|
517
|
+
element.className = styleClass;
|
|
518
|
+
target.append(element);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
removeStylesFrom(target) {
|
|
522
|
+
target = usableStyleTarget(normalizeStyleTarget(target));
|
|
523
|
+
const styles = target.querySelectorAll(`.${this.styleClass}`);
|
|
524
|
+
for (let i = 0, ii = styles.length; i < ii; ++i) {
|
|
525
|
+
target.removeChild(styles[i]);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
let addAdoptedStyleSheets = (target, sheets) => {
|
|
530
|
+
target.adoptedStyleSheets = [...target.adoptedStyleSheets, ...sheets];
|
|
531
|
+
};
|
|
532
|
+
let removeAdoptedStyleSheets = (target, sheets) => {
|
|
533
|
+
target.adoptedStyleSheets = target.adoptedStyleSheets.filter((x) => sheets.indexOf(x) === -1);
|
|
534
|
+
};
|
|
535
|
+
if (ElementStyles.supportsAdoptedStyleSheets) {
|
|
536
|
+
try {
|
|
537
|
+
// Test if browser implementation uses FrozenArray.
|
|
538
|
+
// If not, use push / splice to alter the stylesheets
|
|
539
|
+
// in place. This circumvents a bug in Safari 16.4 where
|
|
540
|
+
// periodically, assigning the array would previously
|
|
541
|
+
// cause sheets to be removed.
|
|
542
|
+
document.adoptedStyleSheets.push();
|
|
543
|
+
document.adoptedStyleSheets.splice();
|
|
544
|
+
addAdoptedStyleSheets = (target, sheets) => {
|
|
545
|
+
target.adoptedStyleSheets.push(...sheets);
|
|
546
|
+
};
|
|
547
|
+
removeAdoptedStyleSheets = (target, sheets) => {
|
|
548
|
+
for (const sheet of sheets) {
|
|
549
|
+
const index = target.adoptedStyleSheets.indexOf(sheet);
|
|
550
|
+
if (index !== -1) {
|
|
551
|
+
target.adoptedStyleSheets.splice(index, 1);
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
catch (e) {
|
|
557
|
+
// Do nothing if an error is thrown, the default
|
|
558
|
+
// case handles FrozenArray.
|
|
559
|
+
}
|
|
560
|
+
ElementStyles.setDefaultStrategy(AdoptedStyleSheetsStrategy);
|
|
561
|
+
}
|
|
562
|
+
else {
|
|
563
|
+
ElementStyles.setDefaultStrategy(StyleElementStrategy);
|
|
564
|
+
}
|
|
565
|
+
const deferHydrationAttribute = "defer-hydration";
|
|
566
|
+
const needsHydrationAttribute = "needs-hydration";
|
|
567
|
+
/**
|
|
568
|
+
* An ElementController capable of hydrating FAST elements from
|
|
569
|
+
* Declarative Shadow DOM.
|
|
570
|
+
*
|
|
571
|
+
* @beta
|
|
572
|
+
*/
|
|
573
|
+
export class HydratableElementController extends ElementController {
|
|
574
|
+
static hydrationObserverHandler(records) {
|
|
575
|
+
for (const record of records) {
|
|
576
|
+
HydratableElementController.hydrationObserver.unobserve(record.target);
|
|
577
|
+
record.target.$fastController.connect();
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
connect() {
|
|
581
|
+
var _a, _b;
|
|
582
|
+
// Initialize needsHydration on first connect
|
|
583
|
+
if (this.needsHydration === undefined) {
|
|
584
|
+
this.needsHydration =
|
|
585
|
+
this.source.getAttribute(needsHydrationAttribute) !== null;
|
|
586
|
+
}
|
|
587
|
+
// If the `defer-hydration` attribute exists on the source,
|
|
588
|
+
// wait for it to be removed before continuing connection behavior.
|
|
589
|
+
if (this.source.hasAttribute(deferHydrationAttribute)) {
|
|
590
|
+
HydratableElementController.hydrationObserver.observe(this.source, {
|
|
591
|
+
attributeFilter: [deferHydrationAttribute],
|
|
592
|
+
});
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
// If the controller does not need to be hydrated, defer connection behavior
|
|
596
|
+
// to the base-class. This case handles element re-connection and initial connection
|
|
597
|
+
// of elements that did not get declarative shadow-dom emitted, as well as if an extending
|
|
598
|
+
// class
|
|
599
|
+
if (!this.needsHydration) {
|
|
600
|
+
super.connect();
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
if (this.stage !== 3 /* Stages.disconnected */) {
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
this.stage = 0 /* Stages.connecting */;
|
|
607
|
+
this.bindObservables();
|
|
608
|
+
this.connectBehaviors();
|
|
609
|
+
const element = this.source;
|
|
610
|
+
const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
|
|
611
|
+
if (this.template) {
|
|
612
|
+
if (isHydratable(this.template)) {
|
|
613
|
+
let firstChild = host.firstChild;
|
|
614
|
+
let lastChild = host.lastChild;
|
|
615
|
+
if (element.shadowRoot === null) {
|
|
616
|
+
// handle element boundary markers when shadowRoot is not present
|
|
617
|
+
if (HydrationMarkup.isElementBoundaryStartMarker(firstChild)) {
|
|
618
|
+
firstChild.data = "";
|
|
619
|
+
firstChild = firstChild.nextSibling;
|
|
620
|
+
}
|
|
621
|
+
if (HydrationMarkup.isElementBoundaryEndMarker(lastChild)) {
|
|
622
|
+
lastChild.data = "";
|
|
623
|
+
lastChild = lastChild.previousSibling;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
this.view = this.template.hydrate(firstChild, lastChild, element);
|
|
627
|
+
(_b = this.view) === null || _b === void 0 ? void 0 : _b.bind(this.source);
|
|
628
|
+
}
|
|
629
|
+
else {
|
|
630
|
+
this.renderTemplate(this.template);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
this.addStyles(this.mainStyles);
|
|
634
|
+
this.stage = 1 /* Stages.connected */;
|
|
635
|
+
this.source.removeAttribute(needsHydrationAttribute);
|
|
636
|
+
this.needsInitialization = this.needsHydration = false;
|
|
637
|
+
Observable.notify(this, isConnectedPropertyName);
|
|
638
|
+
}
|
|
639
|
+
disconnect() {
|
|
640
|
+
super.disconnect();
|
|
641
|
+
HydratableElementController.hydrationObserver.unobserve(this.source);
|
|
642
|
+
}
|
|
643
|
+
static install() {
|
|
644
|
+
ElementController.setStrategy(HydratableElementController);
|
|
376
645
|
}
|
|
377
646
|
}
|
|
647
|
+
HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { isString } from "../interfaces.js";
|
|
1
|
+
import { isString, KernelServiceId } from "../interfaces.js";
|
|
2
2
|
import { Observable } from "../observation/observable.js";
|
|
3
3
|
import { createTypeRegistry, FAST } from "../platform.js";
|
|
4
4
|
import { ElementStyles } from "../styles/element-styles.js";
|
|
5
5
|
import { AttributeDefinition } from "./attributes.js";
|
|
6
6
|
const defaultShadowOptions = { mode: "open" };
|
|
7
7
|
const defaultElementOptions = {};
|
|
8
|
-
const
|
|
8
|
+
const fastElementBaseTypes = new Set();
|
|
9
|
+
const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
|
|
9
10
|
/**
|
|
10
11
|
* Defines metadata for a FASTElement.
|
|
11
12
|
* @public
|
|
@@ -80,13 +81,20 @@ export class FASTElementDefinition {
|
|
|
80
81
|
* that describes the element to define.
|
|
81
82
|
*/
|
|
82
83
|
static compose(type, nameOrDef) {
|
|
83
|
-
|
|
84
|
-
if (found) {
|
|
84
|
+
if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
|
|
85
85
|
return new FASTElementDefinition(class extends type {
|
|
86
86
|
}, nameOrDef);
|
|
87
87
|
}
|
|
88
88
|
return new FASTElementDefinition(type, nameOrDef);
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Registers a FASTElement base type.
|
|
92
|
+
* @param type - The type to register as a base type.
|
|
93
|
+
* @internal
|
|
94
|
+
*/
|
|
95
|
+
static registerBaseType(type) {
|
|
96
|
+
fastElementBaseTypes.add(type);
|
|
97
|
+
}
|
|
90
98
|
}
|
|
91
99
|
/**
|
|
92
100
|
* Gets the element definition associated with the specified type.
|
|
@@ -3,7 +3,7 @@ import { ElementController } from "./element-controller.js";
|
|
|
3
3
|
import { FASTElementDefinition, } from "./fast-definitions.js";
|
|
4
4
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
5
5
|
function createFASTElement(BaseType) {
|
|
6
|
-
|
|
6
|
+
const type = class extends BaseType {
|
|
7
7
|
constructor() {
|
|
8
8
|
/* eslint-disable-next-line */
|
|
9
9
|
super();
|
|
@@ -22,6 +22,8 @@ function createFASTElement(BaseType) {
|
|
|
22
22
|
this.$fastController.onAttributeChangedCallback(name, oldValue, newValue);
|
|
23
23
|
}
|
|
24
24
|
};
|
|
25
|
+
FASTElementDefinition.registerBaseType(type);
|
|
26
|
+
return type;
|
|
25
27
|
}
|
|
26
28
|
function compose(type, nameOrDef) {
|
|
27
29
|
if (isFunction(type)) {
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
const bindingStartMarker = /fe-b\$\$start\$\$(\d+)\$\$(.+)\$\$fe-b/;
|
|
2
|
+
const bindingEndMarker = /fe-b\$\$end\$\$(\d+)\$\$(.+)\$\$fe-b/;
|
|
3
|
+
const repeatViewStartMarker = /fe-repeat\$\$start\$\$(\d+)\$\$fe-repeat/;
|
|
4
|
+
const repeatViewEndMarker = /fe-repeat\$\$end\$\$(\d+)\$\$fe-repeat/;
|
|
5
|
+
const elementBoundaryStartMarker = /^(?:.{0,1000})fe-eb\$\$start\$\$(.+?)\$\$fe-eb/;
|
|
6
|
+
const elementBoundaryEndMarker = /fe-eb\$\$end\$\$(.{0,1000})\$\$fe-eb(?:.{0,1000})$/;
|
|
7
|
+
function isComment(node) {
|
|
8
|
+
return node && node.nodeType === Node.COMMENT_NODE;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Markup utilities to aid in template hydration.
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
export const HydrationMarkup = Object.freeze({
|
|
15
|
+
attributeMarkerName: "data-fe-b",
|
|
16
|
+
attributeBindingSeparator: " ",
|
|
17
|
+
contentBindingStartMarker(index, uniqueId) {
|
|
18
|
+
return `fe-b$$start$$${index}$$${uniqueId}$$fe-b`;
|
|
19
|
+
},
|
|
20
|
+
contentBindingEndMarker(index, uniqueId) {
|
|
21
|
+
return `fe-b$$end$$${index}$$${uniqueId}$$fe-b`;
|
|
22
|
+
},
|
|
23
|
+
repeatStartMarker(index) {
|
|
24
|
+
return `fe-repeat$$start$$${index}$$fe-repeat`;
|
|
25
|
+
},
|
|
26
|
+
repeatEndMarker(index) {
|
|
27
|
+
return `fe-repeat$$end$$${index}$$fe-repeat`;
|
|
28
|
+
},
|
|
29
|
+
isContentBindingStartMarker(content) {
|
|
30
|
+
return bindingStartMarker.test(content);
|
|
31
|
+
},
|
|
32
|
+
isContentBindingEndMarker(content) {
|
|
33
|
+
return bindingEndMarker.test(content);
|
|
34
|
+
},
|
|
35
|
+
isRepeatViewStartMarker(content) {
|
|
36
|
+
return repeatViewStartMarker.test(content);
|
|
37
|
+
},
|
|
38
|
+
isRepeatViewEndMarker(content) {
|
|
39
|
+
return repeatViewEndMarker.test(content);
|
|
40
|
+
},
|
|
41
|
+
isElementBoundaryStartMarker(node) {
|
|
42
|
+
return isComment(node) && elementBoundaryStartMarker.test(node.data.trim());
|
|
43
|
+
},
|
|
44
|
+
isElementBoundaryEndMarker(node) {
|
|
45
|
+
return isComment(node) && elementBoundaryEndMarker.test(node.data);
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
* Returns the indexes of the ViewBehaviorFactories affecting
|
|
49
|
+
* attributes for the element, or null if no factories were found.
|
|
50
|
+
*/
|
|
51
|
+
parseAttributeBinding(node) {
|
|
52
|
+
const attr = node.getAttribute(this.attributeMarkerName);
|
|
53
|
+
return attr === null
|
|
54
|
+
? attr
|
|
55
|
+
: attr.split(this.attributeBindingSeparator).map(i => parseInt(i));
|
|
56
|
+
},
|
|
57
|
+
/**
|
|
58
|
+
* Parses the ViewBehaviorFactory index from string data. Returns
|
|
59
|
+
* the binding index or null if the index cannot be retrieved.
|
|
60
|
+
*/
|
|
61
|
+
parseContentBindingStartMarker(content) {
|
|
62
|
+
return parseIndexAndIdMarker(bindingStartMarker, content);
|
|
63
|
+
},
|
|
64
|
+
parseContentBindingEndMarker(content) {
|
|
65
|
+
return parseIndexAndIdMarker(bindingEndMarker, content);
|
|
66
|
+
},
|
|
67
|
+
/**
|
|
68
|
+
* Parses the index of a repeat directive from a content string.
|
|
69
|
+
*/
|
|
70
|
+
parseRepeatStartMarker(content) {
|
|
71
|
+
return parseIntMarker(repeatViewStartMarker, content);
|
|
72
|
+
},
|
|
73
|
+
parseRepeatEndMarker(content) {
|
|
74
|
+
return parseIntMarker(repeatViewEndMarker, content);
|
|
75
|
+
},
|
|
76
|
+
/**
|
|
77
|
+
* Parses element Id from element boundary markers
|
|
78
|
+
*/
|
|
79
|
+
parseElementBoundaryStartMarker(content) {
|
|
80
|
+
return parseStringMarker(elementBoundaryStartMarker, content.trim());
|
|
81
|
+
},
|
|
82
|
+
parseElementBoundaryEndMarker(content) {
|
|
83
|
+
return parseStringMarker(elementBoundaryEndMarker, content);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
function parseIntMarker(regex, content) {
|
|
87
|
+
const match = regex.exec(content);
|
|
88
|
+
return match === null ? match : parseInt(match[1]);
|
|
89
|
+
}
|
|
90
|
+
function parseStringMarker(regex, content) {
|
|
91
|
+
const match = regex.exec(content);
|
|
92
|
+
return match === null ? match : match[1];
|
|
93
|
+
}
|
|
94
|
+
function parseIndexAndIdMarker(regex, content) {
|
|
95
|
+
const match = regex.exec(content);
|
|
96
|
+
return match === null ? match : [parseInt(match[1]), match[2]];
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* @internal
|
|
100
|
+
*/
|
|
101
|
+
export const Hydratable = Symbol.for("fe-hydration");
|
|
102
|
+
export function isHydratable(value) {
|
|
103
|
+
return value[Hydratable] === Hydratable;
|
|
104
|
+
}
|