@microsoft/fast-element 2.0.0-beta.14 → 2.0.0-beta.15

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.json CHANGED
@@ -1,6 +1,27 @@
1
1
  {
2
2
  "name": "@microsoft/fast-element",
3
3
  "entries": [
4
+ {
5
+ "date": "Tue, 25 Oct 2022 20:24:32 GMT",
6
+ "tag": "@microsoft/fast-element_v2.0.0-beta.15",
7
+ "version": "2.0.0-beta.15",
8
+ "comments": {
9
+ "prerelease": [
10
+ {
11
+ "author": "nicholasrice@users.noreply.github.com",
12
+ "package": "@microsoft/fast-element",
13
+ "commit": "220cc7e0e0de490e51cc8b6f42ff46b03228beaa",
14
+ "comment": "Updated the ElementController to connect behaviors prior to rendering element templates"
15
+ },
16
+ {
17
+ "author": "nicholasrice@users.noreply.github.com",
18
+ "package": "@microsoft/fast-element",
19
+ "commit": "32ca5de5feda4870ed36329f58ded4a0a0421f02",
20
+ "comment": "Added support to fast-element to support element hydration and the `defer-hydration` attribute"
21
+ }
22
+ ]
23
+ }
24
+ },
4
25
  {
5
26
  "date": "Fri, 14 Oct 2022 18:26:11 GMT",
6
27
  "tag": "@microsoft/fast-element_v2.0.0-beta.14",
package/CHANGELOG.md CHANGED
@@ -1,9 +1,18 @@
1
1
  # Change Log - @microsoft/fast-element
2
2
 
3
- This log was last generated on Fri, 14 Oct 2022 18:26:11 GMT and should not be manually modified.
3
+ This log was last generated on Tue, 25 Oct 2022 20:24:32 GMT and should not be manually modified.
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 2.0.0-beta.15
8
+
9
+ Tue, 25 Oct 2022 20:24:32 GMT
10
+
11
+ ### Changes
12
+
13
+ - Updated the ElementController to connect behaviors prior to rendering element templates (nicholasrice@users.noreply.github.com)
14
+ - Added support to fast-element to support element hydration and the `defer-hydration` attribute (nicholasrice@users.noreply.github.com)
15
+
7
16
  ## 2.0.0-beta.14
8
17
 
9
18
  Fri, 14 Oct 2022 18:26:11 GMT
@@ -1,10 +1,17 @@
1
1
  import { StyleStrategy, StyleTarget } from "../interfaces.js";
2
- import type { HostBehavior, HostController } from "../styles/host.js";
3
2
  import { PropertyChangeNotifier } from "../observation/notifier.js";
3
+ import { ElementStyles } from "../styles/element-styles.js";
4
+ import type { HostBehavior, HostController } from "../styles/host.js";
4
5
  import type { ElementViewTemplate } from "../templating/template.js";
5
6
  import type { ElementView } from "../templating/view.js";
6
- import { ElementStyles } from "../styles/element-styles.js";
7
7
  import { FASTElementDefinition } from "./fast-definitions.js";
8
+ /**
9
+ * A type that instantiates an ElementController
10
+ * @public
11
+ */
12
+ export interface ElementControllerStrategy {
13
+ new (element: HTMLElement, definition: FASTElementDefinition): ElementController;
14
+ }
8
15
  /**
9
16
  * Controls the lifecycle and rendering of a `FASTElement`.
10
17
  * @public
@@ -113,7 +120,6 @@ export declare class ElementController<TElement extends HTMLElement = HTMLElemen
113
120
  * Only emits events if connected.
114
121
  */
115
122
  emit(type: string, detail?: any, options?: Omit<CustomEventInit, "detail">): void | boolean;
116
- private finishInitialization;
117
123
  private renderTemplate;
118
124
  /**
119
125
  * Locates or creates a controller for the specified element.
@@ -124,6 +130,12 @@ export declare class ElementController<TElement extends HTMLElement = HTMLElemen
124
130
  * decorator or a call to `FASTElement.define`.
125
131
  */
126
132
  static forCustomElement(element: HTMLElement): ElementController;
133
+ /**
134
+ * Sets the strategy that ElementController.forCustomElement uses to construct
135
+ * ElementController instances for an element.
136
+ * @param strategy - The strategy to use.
137
+ */
138
+ static setStrategy(strategy: ElementControllerStrategy): void;
127
139
  }
128
140
  /**
129
141
  * https://wicg.github.io/construct-stylesheets/
@@ -0,0 +1,14 @@
1
+ import { ElementController } from "./element-controller.js";
2
+ /**
3
+ * An ElementController capable of hydrating FAST elements from
4
+ * Declarative Shadow DOM.
5
+ *
6
+ * @beta
7
+ */
8
+ export declare class HydratableElementController<TElement extends HTMLElement = HTMLElement> extends ElementController<TElement> {
9
+ private static hydrationObserver;
10
+ private static hydrationObserverHandler;
11
+ connect(): void;
12
+ disconnect(): void;
13
+ static install(): void;
14
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -24,4 +24,4 @@ export * from "./templating/node-observation.js";
24
24
  export * from "./components/fast-element.js";
25
25
  export * from "./components/fast-definitions.js";
26
26
  export * from "./components/attributes.js";
27
- export { ElementController } from "./components/element-controller.js";
27
+ export { ElementController, ElementControllerStrategy, } from "./components/element-controller.js";
@@ -20,3 +20,17 @@ export declare function composedParent<T extends HTMLElement>(element: T): HTMLE
20
20
  * @public
21
21
  */
22
22
  export declare function composedContains(reference: HTMLElement, test: HTMLElement): boolean;
23
+ /**
24
+ * @internal
25
+ */
26
+ export declare class UnobservableMutationObserver extends MutationObserver {
27
+ private readonly callback;
28
+ private observedNodes;
29
+ /**
30
+ * An extension of MutationObserver that supports unobserving nodes.
31
+ * @param callback - The callback to invoke when observed nodes are changed.
32
+ */
33
+ constructor(callback: MutationCallback);
34
+ observe(target: Node, options?: MutationObserverInit | undefined): void;
35
+ unobserve(target: Node): void;
36
+ }
@@ -15,6 +15,7 @@ function getShadowRoot(element) {
15
15
  var _a, _b;
16
16
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
17
17
  }
18
+ let elementControllerStrategy;
18
19
  /**
19
20
  * Controls the lifecycle and rendering of a `FASTElement`.
20
21
  * @public
@@ -256,11 +257,16 @@ export class ElementController extends PropertyChangeNotifier {
256
257
  if (this._isConnected) {
257
258
  return;
258
259
  }
259
- if (this.needsInitialization) {
260
- this.finishInitialization();
261
- }
262
- else if (this.view !== null) {
263
- this.view.bind(this.source);
260
+ // If we have any observables that were bound, re-apply their values.
261
+ if (this.boundObservables !== null) {
262
+ const element = this.source;
263
+ const boundObservables = this.boundObservables;
264
+ const propertyNames = Object.keys(boundObservables);
265
+ for (let i = 0, ii = propertyNames.length; i < ii; ++i) {
266
+ const propertyName = propertyNames[i];
267
+ element[propertyName] = boundObservables[propertyName];
268
+ }
269
+ this.boundObservables = null;
264
270
  }
265
271
  const behaviors = this.behaviors;
266
272
  if (behaviors !== null) {
@@ -268,6 +274,14 @@ export class ElementController extends PropertyChangeNotifier {
268
274
  key.connectedCallback && key.connectedCallback(this);
269
275
  }
270
276
  }
277
+ if (this.needsInitialization) {
278
+ this.renderTemplate(this.template);
279
+ this.addStyles(this.mainStyles);
280
+ this.needsInitialization = false;
281
+ }
282
+ else if (this.view !== null) {
283
+ this.view.bind(this.source);
284
+ }
271
285
  this.setIsConnected(true);
272
286
  }
273
287
  /**
@@ -314,22 +328,6 @@ export class ElementController extends PropertyChangeNotifier {
314
328
  }
315
329
  return false;
316
330
  }
317
- finishInitialization() {
318
- const element = this.source;
319
- const boundObservables = this.boundObservables;
320
- // If we have any observables that were bound, re-apply their values.
321
- if (boundObservables !== null) {
322
- const propertyNames = Object.keys(boundObservables);
323
- for (let i = 0, ii = propertyNames.length; i < ii; ++i) {
324
- const propertyName = propertyNames[i];
325
- element[propertyName] = boundObservables[propertyName];
326
- }
327
- this.boundObservables = null;
328
- }
329
- this.renderTemplate(this.template);
330
- this.addStyles(this.mainStyles);
331
- this.needsInitialization = false;
332
- }
333
331
  renderTemplate(template) {
334
332
  var _a;
335
333
  // When getting the host to render to, we start by looking
@@ -373,9 +371,19 @@ export class ElementController extends PropertyChangeNotifier {
373
371
  if (definition === void 0) {
374
372
  throw FAST.error(1401 /* Message.missingElementDefinition */);
375
373
  }
376
- return (element.$fastController = new ElementController(element, definition));
374
+ return (element.$fastController = new elementControllerStrategy(element, definition));
375
+ }
376
+ /**
377
+ * Sets the strategy that ElementController.forCustomElement uses to construct
378
+ * ElementController instances for an element.
379
+ * @param strategy - The strategy to use.
380
+ */
381
+ static setStrategy(strategy) {
382
+ elementControllerStrategy = strategy;
377
383
  }
378
384
  }
385
+ // Set default strategy for ElementController
386
+ ElementController.setStrategy(ElementController);
379
387
  /**
380
388
  * Converts a styleTarget into the operative target. When the provided target is an Element
381
389
  * that is a FASTElement, the function will return the ShadowRoot for that element. Otherwise,
@@ -0,0 +1,35 @@
1
+ import { UnobservableMutationObserver } from "../utilities.js";
2
+ import { ElementController } from "./element-controller.js";
3
+ const deferHydrationAttribute = "defer-hydration";
4
+ /**
5
+ * An ElementController capable of hydrating FAST elements from
6
+ * Declarative Shadow DOM.
7
+ *
8
+ * @beta
9
+ */
10
+ export class HydratableElementController extends ElementController {
11
+ static hydrationObserverHandler(records) {
12
+ for (const record of records) {
13
+ HydratableElementController.hydrationObserver.unobserve(record.target);
14
+ record.target.$fastController.connect();
15
+ }
16
+ }
17
+ connect() {
18
+ if (this.source.hasAttribute(deferHydrationAttribute)) {
19
+ HydratableElementController.hydrationObserver.observe(this.source, {
20
+ attributeFilter: [deferHydrationAttribute],
21
+ });
22
+ }
23
+ else {
24
+ super.connect();
25
+ }
26
+ }
27
+ disconnect() {
28
+ super.disconnect();
29
+ HydratableElementController.hydrationObserver.unobserve(this.source);
30
+ }
31
+ static install() {
32
+ ElementController.setStrategy(HydratableElementController);
33
+ }
34
+ }
35
+ HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
@@ -0,0 +1,2 @@
1
+ import { HydratableElementController } from "./hydration.js";
2
+ HydratableElementController.install();
package/dist/esm/index.js CHANGED
@@ -27,4 +27,4 @@ export * from "./templating/node-observation.js";
27
27
  export * from "./components/fast-element.js";
28
28
  export * from "./components/fast-definitions.js";
29
29
  export * from "./components/attributes.js";
30
- export { ElementController } from "./components/element-controller.js";
30
+ export { ElementController, } from "./components/element-controller.js";
@@ -42,3 +42,30 @@ export function composedContains(reference, test) {
42
42
  }
43
43
  return false;
44
44
  }
45
+ /**
46
+ * @internal
47
+ */
48
+ export class UnobservableMutationObserver extends MutationObserver {
49
+ /**
50
+ * An extension of MutationObserver that supports unobserving nodes.
51
+ * @param callback - The callback to invoke when observed nodes are changed.
52
+ */
53
+ constructor(callback) {
54
+ function handler(mutations) {
55
+ this.callback.call(null, mutations.filter(record => this.observedNodes.has(record.target)));
56
+ }
57
+ super(handler);
58
+ this.callback = callback;
59
+ this.observedNodes = new Set();
60
+ }
61
+ observe(target, options) {
62
+ this.observedNodes.add(target);
63
+ super.observe(target, options);
64
+ }
65
+ unobserve(target) {
66
+ this.observedNodes.delete(target);
67
+ if (this.observedNodes.size < 1) {
68
+ this.disconnect();
69
+ }
70
+ }
71
+ }
@@ -4267,6 +4267,53 @@
4267
4267
  ],
4268
4268
  "name": "removeStyles"
4269
4269
  },
4270
+ {
4271
+ "kind": "Method",
4272
+ "canonicalReference": "@microsoft/fast-element!ElementController.setStrategy:member(1)",
4273
+ "docComment": "/**\n * Sets the strategy that ElementController.forCustomElement uses to construct ElementController instances for an element.\n *\n * @param strategy - The strategy to use.\n */\n",
4274
+ "excerptTokens": [
4275
+ {
4276
+ "kind": "Content",
4277
+ "text": "static setStrategy(strategy: "
4278
+ },
4279
+ {
4280
+ "kind": "Reference",
4281
+ "text": "ElementControllerStrategy",
4282
+ "canonicalReference": "@microsoft/fast-element!ElementControllerStrategy:interface"
4283
+ },
4284
+ {
4285
+ "kind": "Content",
4286
+ "text": "): "
4287
+ },
4288
+ {
4289
+ "kind": "Content",
4290
+ "text": "void"
4291
+ },
4292
+ {
4293
+ "kind": "Content",
4294
+ "text": ";"
4295
+ }
4296
+ ],
4297
+ "isOptional": false,
4298
+ "isStatic": true,
4299
+ "returnTypeTokenRange": {
4300
+ "startIndex": 3,
4301
+ "endIndex": 4
4302
+ },
4303
+ "releaseTag": "Public",
4304
+ "overloadIndex": 1,
4305
+ "parameters": [
4306
+ {
4307
+ "parameterName": "strategy",
4308
+ "parameterTypeTokenRange": {
4309
+ "startIndex": 1,
4310
+ "endIndex": 2
4311
+ },
4312
+ "isOptional": false
4313
+ }
4314
+ ],
4315
+ "name": "setStrategy"
4316
+ },
4270
4317
  {
4271
4318
  "kind": "Property",
4272
4319
  "canonicalReference": "@microsoft/fast-element!ElementController#source:member",
@@ -4383,6 +4430,84 @@
4383
4430
  }
4384
4431
  ]
4385
4432
  },
4433
+ {
4434
+ "kind": "Interface",
4435
+ "canonicalReference": "@microsoft/fast-element!ElementControllerStrategy:interface",
4436
+ "docComment": "/**\n * A type that instantiates an ElementController\n *\n * @public\n */\n",
4437
+ "excerptTokens": [
4438
+ {
4439
+ "kind": "Content",
4440
+ "text": "export interface ElementControllerStrategy "
4441
+ }
4442
+ ],
4443
+ "releaseTag": "Public",
4444
+ "name": "ElementControllerStrategy",
4445
+ "members": [
4446
+ {
4447
+ "kind": "ConstructSignature",
4448
+ "canonicalReference": "@microsoft/fast-element!ElementControllerStrategy:new(1)",
4449
+ "docComment": "",
4450
+ "excerptTokens": [
4451
+ {
4452
+ "kind": "Content",
4453
+ "text": "new (element: "
4454
+ },
4455
+ {
4456
+ "kind": "Reference",
4457
+ "text": "HTMLElement",
4458
+ "canonicalReference": "!HTMLElement:interface"
4459
+ },
4460
+ {
4461
+ "kind": "Content",
4462
+ "text": ", definition: "
4463
+ },
4464
+ {
4465
+ "kind": "Reference",
4466
+ "text": "FASTElementDefinition",
4467
+ "canonicalReference": "@microsoft/fast-element!FASTElementDefinition:class"
4468
+ },
4469
+ {
4470
+ "kind": "Content",
4471
+ "text": "): "
4472
+ },
4473
+ {
4474
+ "kind": "Reference",
4475
+ "text": "ElementController",
4476
+ "canonicalReference": "@microsoft/fast-element!ElementController:class"
4477
+ },
4478
+ {
4479
+ "kind": "Content",
4480
+ "text": ";"
4481
+ }
4482
+ ],
4483
+ "returnTypeTokenRange": {
4484
+ "startIndex": 5,
4485
+ "endIndex": 6
4486
+ },
4487
+ "releaseTag": "Public",
4488
+ "overloadIndex": 1,
4489
+ "parameters": [
4490
+ {
4491
+ "parameterName": "element",
4492
+ "parameterTypeTokenRange": {
4493
+ "startIndex": 1,
4494
+ "endIndex": 2
4495
+ },
4496
+ "isOptional": false
4497
+ },
4498
+ {
4499
+ "parameterName": "definition",
4500
+ "parameterTypeTokenRange": {
4501
+ "startIndex": 3,
4502
+ "endIndex": 4
4503
+ },
4504
+ "isOptional": false
4505
+ }
4506
+ ]
4507
+ }
4508
+ ],
4509
+ "extendsTokenRanges": []
4510
+ },
4386
4511
  {
4387
4512
  "kind": "Variable",
4388
4513
  "canonicalReference": "@microsoft/fast-element!elements:var",
@@ -755,7 +755,6 @@ export declare class ElementController<TElement extends HTMLElement = HTMLElemen
755
755
  * Only emits events if connected.
756
756
  */
757
757
  emit(type: string, detail?: any, options?: Omit<CustomEventInit, "detail">): void | boolean;
758
- private finishInitialization;
759
758
  private renderTemplate;
760
759
  /**
761
760
  * Locates or creates a controller for the specified element.
@@ -766,6 +765,20 @@ export declare class ElementController<TElement extends HTMLElement = HTMLElemen
766
765
  * decorator or a call to `FASTElement.define`.
767
766
  */
768
767
  static forCustomElement(element: HTMLElement): ElementController;
768
+ /**
769
+ * Sets the strategy that ElementController.forCustomElement uses to construct
770
+ * ElementController instances for an element.
771
+ * @param strategy - The strategy to use.
772
+ */
773
+ static setStrategy(strategy: ElementControllerStrategy): void;
774
+ }
775
+
776
+ /**
777
+ * A type that instantiates an ElementController
778
+ * @public
779
+ */
780
+ export declare interface ElementControllerStrategy {
781
+ new (element: HTMLElement, definition: FASTElementDefinition): ElementController;
769
782
  }
770
783
 
771
784
  /**
@@ -3574,6 +3574,7 @@ function getShadowRoot(element) {
3574
3574
  var _a, _b;
3575
3575
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
3576
3576
  }
3577
+ let elementControllerStrategy;
3577
3578
  /**
3578
3579
  * Controls the lifecycle and rendering of a `FASTElement`.
3579
3580
  * @public
@@ -3815,11 +3816,16 @@ class ElementController extends PropertyChangeNotifier {
3815
3816
  if (this._isConnected) {
3816
3817
  return;
3817
3818
  }
3818
- if (this.needsInitialization) {
3819
- this.finishInitialization();
3820
- }
3821
- else if (this.view !== null) {
3822
- this.view.bind(this.source);
3819
+ // If we have any observables that were bound, re-apply their values.
3820
+ if (this.boundObservables !== null) {
3821
+ const element = this.source;
3822
+ const boundObservables = this.boundObservables;
3823
+ const propertyNames = Object.keys(boundObservables);
3824
+ for (let i = 0, ii = propertyNames.length; i < ii; ++i) {
3825
+ const propertyName = propertyNames[i];
3826
+ element[propertyName] = boundObservables[propertyName];
3827
+ }
3828
+ this.boundObservables = null;
3823
3829
  }
3824
3830
  const behaviors = this.behaviors;
3825
3831
  if (behaviors !== null) {
@@ -3827,6 +3833,14 @@ class ElementController extends PropertyChangeNotifier {
3827
3833
  key.connectedCallback && key.connectedCallback(this);
3828
3834
  }
3829
3835
  }
3836
+ if (this.needsInitialization) {
3837
+ this.renderTemplate(this.template);
3838
+ this.addStyles(this.mainStyles);
3839
+ this.needsInitialization = false;
3840
+ }
3841
+ else if (this.view !== null) {
3842
+ this.view.bind(this.source);
3843
+ }
3830
3844
  this.setIsConnected(true);
3831
3845
  }
3832
3846
  /**
@@ -3873,22 +3887,6 @@ class ElementController extends PropertyChangeNotifier {
3873
3887
  }
3874
3888
  return false;
3875
3889
  }
3876
- finishInitialization() {
3877
- const element = this.source;
3878
- const boundObservables = this.boundObservables;
3879
- // If we have any observables that were bound, re-apply their values.
3880
- if (boundObservables !== null) {
3881
- const propertyNames = Object.keys(boundObservables);
3882
- for (let i = 0, ii = propertyNames.length; i < ii; ++i) {
3883
- const propertyName = propertyNames[i];
3884
- element[propertyName] = boundObservables[propertyName];
3885
- }
3886
- this.boundObservables = null;
3887
- }
3888
- this.renderTemplate(this.template);
3889
- this.addStyles(this.mainStyles);
3890
- this.needsInitialization = false;
3891
- }
3892
3890
  renderTemplate(template) {
3893
3891
  var _a;
3894
3892
  // When getting the host to render to, we start by looking
@@ -3932,9 +3930,19 @@ class ElementController extends PropertyChangeNotifier {
3932
3930
  if (definition === void 0) {
3933
3931
  throw FAST.error(1401 /* Message.missingElementDefinition */);
3934
3932
  }
3935
- return (element.$fastController = new ElementController(element, definition));
3933
+ return (element.$fastController = new elementControllerStrategy(element, definition));
3934
+ }
3935
+ /**
3936
+ * Sets the strategy that ElementController.forCustomElement uses to construct
3937
+ * ElementController instances for an element.
3938
+ * @param strategy - The strategy to use.
3939
+ */
3940
+ static setStrategy(strategy) {
3941
+ elementControllerStrategy = strategy;
3936
3942
  }
3937
3943
  }
3944
+ // Set default strategy for ElementController
3945
+ ElementController.setStrategy(ElementController);
3938
3946
  /**
3939
3947
  * Converts a styleTarget into the operative target. When the provided target is an Element
3940
3948
  * that is a FASTElement, the function will return the ShadowRoot for that element. Otherwise,