@microsoft/fast-element 2.8.0 → 2.8.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.json CHANGED
@@ -2,7 +2,37 @@
2
2
  "name": "@microsoft/fast-element",
3
3
  "entries": [
4
4
  {
5
- "date": "Mon, 13 Oct 2025 00:36:35 GMT",
5
+ "date": "Fri, 31 Oct 2025 20:45:23 GMT",
6
+ "version": "2.8.2",
7
+ "tag": "@microsoft/fast-element_v2.8.2",
8
+ "comments": {
9
+ "patch": [
10
+ {
11
+ "author": "863023+radium-v@users.noreply.github.com",
12
+ "package": "@microsoft/fast-element",
13
+ "commit": "1c008047c3c0b651dbec6dd74ece2da71693cbf3",
14
+ "comment": "fix: use shadowOptions getter override to set hydration attributes"
15
+ }
16
+ ]
17
+ }
18
+ },
19
+ {
20
+ "date": "Tue, 21 Oct 2025 16:17:27 GMT",
21
+ "version": "2.8.1",
22
+ "tag": "@microsoft/fast-element_v2.8.1",
23
+ "comments": {
24
+ "patch": [
25
+ {
26
+ "author": "863023+radium-v@users.noreply.github.com",
27
+ "package": "@microsoft/fast-element",
28
+ "commit": "d66ae6571c97341890e5320048fc1a3ef38dc5be",
29
+ "comment": "feat: add methods to track hydrating instances"
30
+ }
31
+ ]
32
+ }
33
+ },
34
+ {
35
+ "date": "Mon, 13 Oct 2025 00:37:08 GMT",
6
36
  "version": "2.8.0",
7
37
  "tag": "@microsoft/fast-element_v2.8.0",
8
38
  "comments": {
package/CHANGELOG.md CHANGED
@@ -1,12 +1,28 @@
1
1
  # Change Log - @microsoft/fast-element
2
2
 
3
- <!-- This log was last generated on Mon, 13 Oct 2025 00:36:35 GMT and should not be manually modified. -->
3
+ <!-- This log was last generated on Fri, 31 Oct 2025 20:45:23 GMT and should not be manually modified. -->
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 2.8.2
8
+
9
+ Fri, 31 Oct 2025 20:45:23 GMT
10
+
11
+ ### Patches
12
+
13
+ - fix: use shadowOptions getter override to set hydration attributes (863023+radium-v@users.noreply.github.com)
14
+
15
+ ## 2.8.1
16
+
17
+ Tue, 21 Oct 2025 16:17:27 GMT
18
+
19
+ ### Patches
20
+
21
+ - feat: add methods to track hydrating instances (863023+radium-v@users.noreply.github.com)
22
+
7
23
  ## 2.8.0
8
24
 
9
- Mon, 13 Oct 2025 00:36:35 GMT
25
+ Mon, 13 Oct 2025 00:37:08 GMT
10
26
 
11
27
  ### Minor changes
12
28
 
@@ -13,10 +13,18 @@ import { FASTElementDefinition, ShadowRootOptions } from "./fast-definitions.js"
13
13
  export interface ElementControllerStrategy {
14
14
  new (element: HTMLElement, definition: FASTElementDefinition): ElementController;
15
15
  }
16
- declare const enum Stages {
16
+ /**
17
+ * The various lifecycle stages of an ElementController.
18
+ * @public
19
+ */
20
+ export declare const enum Stages {
21
+ /** The element is in the process of connecting. */
17
22
  connecting = 0,
23
+ /** The element is connected. */
18
24
  connected = 1,
25
+ /** The element is in the process of disconnecting. */
19
26
  disconnecting = 2,
27
+ /** The element is disconnected. */
20
28
  disconnected = 3
21
29
  }
22
30
  /**
@@ -24,11 +32,29 @@ declare const enum Stages {
24
32
  * @public
25
33
  */
26
34
  export declare class ElementController<TElement extends HTMLElement = HTMLElement> extends PropertyChangeNotifier implements HostController<TElement> {
35
+ /**
36
+ * A map of observable properties that were set on the element before upgrade.
37
+ */
27
38
  private boundObservables;
39
+ /**
40
+ * Indicates whether the controller needs to perform initial rendering.
41
+ */
28
42
  protected needsInitialization: boolean;
29
- private hasExistingShadowRoot;
43
+ /**
44
+ * Indicates whether the element has an existing shadow root (e.g. from declarative shadow DOM).
45
+ */
46
+ protected hasExistingShadowRoot: boolean;
47
+ /**
48
+ * The template used to render the component.
49
+ */
30
50
  private _template;
51
+ /**
52
+ * The shadow root options for the component.
53
+ */
31
54
  private _shadowRootOptions;
55
+ /**
56
+ * The current lifecycle stage of the controller.
57
+ */
32
58
  protected stage: Stages;
33
59
  /**
34
60
  * A guard against connecting behaviors multiple times
@@ -36,12 +62,19 @@ export declare class ElementController<TElement extends HTMLElement = HTMLElemen
36
62
  * another behavior during it's connectedCallback
37
63
  */
38
64
  private guardBehaviorConnection;
65
+ /**
66
+ * The behaviors associated with the component.
67
+ */
39
68
  protected behaviors: Map<HostBehavior<TElement>, number> | null;
40
69
  /**
41
70
  * Tracks whether behaviors are connected so that
42
71
  * behaviors cant be connected multiple times
43
72
  */
44
73
  private behaviorsConnected;
74
+ /**
75
+ * The main set of styles used for the component, independent of any
76
+ * dynamically added styles.
77
+ */
45
78
  private _mainStyles;
46
79
  /**
47
80
  * This allows Observable.getNotifier(...) to return the Controller
@@ -91,6 +124,9 @@ export declare class ElementController<TElement extends HTMLElement = HTMLElemen
91
124
  */
92
125
  get template(): ElementViewTemplate<TElement> | null;
93
126
  set template(value: ElementViewTemplate<TElement> | null);
127
+ /**
128
+ * The shadow root options for the component.
129
+ */
94
130
  get shadowOptions(): ShadowRootOptions | undefined;
95
131
  set shadowOptions(value: ShadowRootOptions | undefined);
96
132
  /**
@@ -139,8 +175,17 @@ export declare class ElementController<TElement extends HTMLElement = HTMLElemen
139
175
  * Runs connected lifecycle behavior on the associated element.
140
176
  */
141
177
  connect(): void;
178
+ /**
179
+ * Binds any observables that were set before upgrade.
180
+ */
142
181
  protected bindObservables(): void;
182
+ /**
183
+ * Connects any existing behaviors on the associated element.
184
+ */
143
185
  protected connectBehaviors(): void;
186
+ /**
187
+ * Disconnects any behaviors on the associated element.
188
+ */
144
189
  protected disconnectBehaviors(): void;
145
190
  /**
146
191
  * Runs disconnected lifecycle behavior on the associated element.
@@ -162,6 +207,13 @@ export declare class ElementController<TElement extends HTMLElement = HTMLElemen
162
207
  * Only emits events if connected.
163
208
  */
164
209
  emit(type: string, detail?: any, options?: Omit<CustomEventInit, "detail">): void | boolean;
210
+ /**
211
+ * Renders the provided template to the element.
212
+ *
213
+ * @param template - The template to render.
214
+ * @remarks
215
+ * If `null` is provided, any existing view will be removed.
216
+ */
165
217
  protected renderTemplate(template: ElementViewTemplate | null | undefined): void;
166
218
  /**
167
219
  * Locates or creates a controller for the specified element.
@@ -204,7 +256,15 @@ export declare class StyleElementStrategy implements StyleStrategy {
204
256
  addStylesTo(target: StyleTarget): void;
205
257
  removeStylesFrom(target: StyleTarget): void;
206
258
  }
259
+ /**
260
+ * The attribute used to defer hydration of an element.
261
+ * @public
262
+ */
207
263
  export declare const deferHydrationAttribute = "defer-hydration";
264
+ /**
265
+ * The attribute used to indicate that an element needs hydration.
266
+ * @public
267
+ */
208
268
  export declare const needsHydrationAttribute = "needs-hydration";
209
269
  /**
210
270
  * Lifecycle callbacks for element hydration events
@@ -238,22 +298,63 @@ export declare class HydratableElementController<TElement extends HTMLElement =
238
298
  */
239
299
  protected needsHydration?: boolean;
240
300
  private static hydrationObserver;
301
+ /**
302
+ * {@inheritdoc ElementController.shadowOptions}
303
+ */
304
+ get shadowOptions(): ShadowRootOptions | undefined;
305
+ set shadowOptions(value: ShadowRootOptions | undefined);
241
306
  /**
242
307
  * Lifecycle callbacks for hydration events
243
308
  */
244
- private static lifecycleCallbacks?;
309
+ static lifecycleCallbacks?: HydrationControllerCallbacks;
310
+ /**
311
+ * An idle callback ID used to track hydration completion
312
+ */
313
+ private static idleCallbackId;
314
+ /**
315
+ * Adds the current element instance to the hydrating instances map
316
+ */
317
+ private addHydratingInstance;
245
318
  /**
246
319
  * Configure lifecycle callbacks for hydration events
247
320
  */
248
321
  static config(callbacks: HydrationControllerCallbacks): typeof HydratableElementController;
249
322
  private static hydrationObserverHandler;
250
323
  /**
251
- * Checks if all elements have completed hydration and dispatches event if complete
324
+ * Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
325
+ * Then resets the ElementController strategy to the default so that future elements
326
+ * don't use the HydratableElementController.
327
+ *
328
+ * @param deadline - the idle deadline object
252
329
  */
253
330
  private static checkHydrationComplete;
254
- static forCustomElement(element: HTMLElement, override?: boolean): ElementController<HTMLElement>;
331
+ /**
332
+ * Runs connected lifecycle behavior on the associated element.
333
+ */
255
334
  connect(): void;
335
+ /**
336
+ * A map of element instances by the name of the custom element they are
337
+ * associated with. The key is the custom element name, and the value is the
338
+ * instances of hydratable elements which currently need to be hydrated.
339
+ *
340
+ * When all of the instances in the set have been hydrated, the set is
341
+ * cleared and removed from the map. If the map is empty, the
342
+ * hydrationComplete callback is invoked.
343
+ */
344
+ private static hydratingInstances?;
345
+ /**
346
+ * Removes the current element instance from the hydrating instances map
347
+ */
348
+ private removeHydratingInstance;
349
+ /**
350
+ * Unregisters the hydration observer when the element is disconnected.
351
+ */
256
352
  disconnect(): void;
353
+ /**
354
+ * Sets the ElementController strategy to HydratableElementController.
355
+ * @remarks
356
+ * This method is typically called during application startup to enable
357
+ * hydration support for FAST elements.
358
+ */
257
359
  static install(): void;
258
360
  }
259
- export {};
@@ -31,4 +31,4 @@ export { render, RenderBehavior, RenderDirective } from "./templating/render.js"
31
31
  export { customElement, FASTElement } from "./components/fast-element.js";
32
32
  export { FASTElementDefinition, PartialFASTElementDefinition, ShadowRootOptions, fastElementRegistry, TemplateOptions, TypeRegistry, type TemplateLifecycleCallbacks, } from "./components/fast-definitions.js";
33
33
  export { attr, AttributeConfiguration, AttributeDefinition, AttributeMode, booleanConverter, DecoratorAttributeConfiguration, nullableBooleanConverter, nullableNumberConverter, ValueConverter, } from "./components/attributes.js";
34
- export { ElementController, ElementControllerStrategy, HydratableElementController, type HydrationControllerCallbacks, } from "./components/element-controller.js";
34
+ export { deferHydrationAttribute, ElementController, ElementControllerStrategy, HydratableElementController, type HydrationControllerCallbacks, needsHydrationAttribute, Stages, } from "./components/element-controller.js";
@@ -32,10 +32,25 @@ export class ElementController extends PropertyChangeNotifier {
32
32
  */
33
33
  constructor(element, definition) {
34
34
  super(element);
35
+ /**
36
+ * A map of observable properties that were set on the element before upgrade.
37
+ */
35
38
  this.boundObservables = null;
39
+ /**
40
+ * Indicates whether the controller needs to perform initial rendering.
41
+ */
36
42
  this.needsInitialization = true;
43
+ /**
44
+ * Indicates whether the element has an existing shadow root (e.g. from declarative shadow DOM).
45
+ */
37
46
  this.hasExistingShadowRoot = false;
47
+ /**
48
+ * The template used to render the component.
49
+ */
38
50
  this._template = null;
51
+ /**
52
+ * The current lifecycle stage of the controller.
53
+ */
39
54
  this.stage = 3 /* Stages.disconnected */;
40
55
  /**
41
56
  * A guard against connecting behaviors multiple times
@@ -43,12 +58,19 @@ export class ElementController extends PropertyChangeNotifier {
43
58
  * another behavior during it's connectedCallback
44
59
  */
45
60
  this.guardBehaviorConnection = false;
61
+ /**
62
+ * The behaviors associated with the component.
63
+ */
46
64
  this.behaviors = null;
47
65
  /**
48
66
  * Tracks whether behaviors are connected so that
49
67
  * behaviors cant be connected multiple times
50
68
  */
51
69
  this.behaviorsConnected = false;
70
+ /**
71
+ * The main set of styles used for the component, independent of any
72
+ * dynamically added styles.
73
+ */
52
74
  this._mainStyles = null;
53
75
  /**
54
76
  * This allows Observable.getNotifier(...) to return the Controller
@@ -144,6 +166,9 @@ export class ElementController extends PropertyChangeNotifier {
144
166
  this.renderTemplate(value);
145
167
  }
146
168
  }
169
+ /**
170
+ * The shadow root options for the component.
171
+ */
147
172
  get shadowOptions() {
148
173
  return this._shadowRootOptions;
149
174
  }
@@ -318,6 +343,9 @@ export class ElementController extends PropertyChangeNotifier {
318
343
  this.stage = 1 /* Stages.connected */;
319
344
  Observable.notify(this, isConnectedPropertyName);
320
345
  }
346
+ /**
347
+ * Binds any observables that were set before upgrade.
348
+ */
321
349
  bindObservables() {
322
350
  if (this.boundObservables !== null) {
323
351
  const element = this.source;
@@ -330,6 +358,9 @@ export class ElementController extends PropertyChangeNotifier {
330
358
  this.boundObservables = null;
331
359
  }
332
360
  }
361
+ /**
362
+ * Connects any existing behaviors on the associated element.
363
+ */
333
364
  connectBehaviors() {
334
365
  if (this.behaviorsConnected === false) {
335
366
  const behaviors = this.behaviors;
@@ -343,6 +374,9 @@ export class ElementController extends PropertyChangeNotifier {
343
374
  this.behaviorsConnected = true;
344
375
  }
345
376
  }
377
+ /**
378
+ * Disconnects any behaviors on the associated element.
379
+ */
346
380
  disconnectBehaviors() {
347
381
  if (this.behaviorsConnected === true) {
348
382
  const behaviors = this.behaviors;
@@ -395,6 +429,13 @@ export class ElementController extends PropertyChangeNotifier {
395
429
  }
396
430
  return false;
397
431
  }
432
+ /**
433
+ * Renders the provided template to the element.
434
+ *
435
+ * @param template - The template to render.
436
+ * @remarks
437
+ * If `null` is provided, any existing view will be removed.
438
+ */
398
439
  renderTemplate(template) {
399
440
  var _a;
400
441
  // When getting the host to render to, we start by looking
@@ -582,7 +623,15 @@ if (ElementStyles.supportsAdoptedStyleSheets) {
582
623
  else {
583
624
  ElementStyles.setDefaultStrategy(StyleElementStrategy);
584
625
  }
626
+ /**
627
+ * The attribute used to defer hydration of an element.
628
+ * @public
629
+ */
585
630
  export const deferHydrationAttribute = "defer-hydration";
631
+ /**
632
+ * The attribute used to indicate that an element needs hydration.
633
+ * @public
634
+ */
586
635
  export const needsHydrationAttribute = "needs-hydration";
587
636
  /**
588
637
  * An ElementController capable of hydrating FAST elements from
@@ -591,6 +640,35 @@ export const needsHydrationAttribute = "needs-hydration";
591
640
  * @beta
592
641
  */
593
642
  export class HydratableElementController extends ElementController {
643
+ /**
644
+ * {@inheritdoc ElementController.shadowOptions}
645
+ */
646
+ get shadowOptions() {
647
+ return super.shadowOptions;
648
+ }
649
+ set shadowOptions(value) {
650
+ super.shadowOptions = value;
651
+ if (this.hasExistingShadowRoot &&
652
+ this.definition.templateOptions === TemplateOptions.deferAndHydrate) {
653
+ this.source.toggleAttribute(deferHydrationAttribute, true);
654
+ this.source.toggleAttribute(needsHydrationAttribute, true);
655
+ }
656
+ }
657
+ /**
658
+ * Adds the current element instance to the hydrating instances map
659
+ */
660
+ addHydratingInstance() {
661
+ if (!HydratableElementController.hydratingInstances) {
662
+ return;
663
+ }
664
+ const name = this.definition.name;
665
+ let instances = HydratableElementController.hydratingInstances.get(name);
666
+ if (!instances) {
667
+ instances = new Set();
668
+ HydratableElementController.hydratingInstances.set(name, instances);
669
+ }
670
+ instances.add(this.source);
671
+ }
594
672
  /**
595
673
  * Configure lifecycle callbacks for hydration events
596
674
  */
@@ -600,38 +678,47 @@ export class HydratableElementController extends ElementController {
600
678
  }
601
679
  static hydrationObserverHandler(records) {
602
680
  for (const record of records) {
603
- HydratableElementController.hydrationObserver.unobserve(record.target);
604
- record.target.$fastController.connect();
681
+ if (!record.target.hasAttribute(deferHydrationAttribute)) {
682
+ HydratableElementController.hydrationObserver.unobserve(record.target);
683
+ record.target.$fastController.connect();
684
+ }
605
685
  }
606
686
  }
607
687
  /**
608
- * Checks if all elements have completed hydration and dispatches event if complete
688
+ * Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
689
+ * Then resets the ElementController strategy to the default so that future elements
690
+ * don't use the HydratableElementController.
691
+ *
692
+ * @param deadline - the idle deadline object
609
693
  */
610
- static checkHydrationComplete() {
611
- var _a, _b;
612
- if (!document.querySelector(`[${needsHydrationAttribute}]`)) {
613
- (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.hydrationComplete) === null || _b === void 0 ? void 0 : _b.call(_a);
694
+ static checkHydrationComplete(deadline) {
695
+ var _a, _b, _c;
696
+ if (deadline.didTimeout) {
697
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
698
+ return;
614
699
  }
615
- }
616
- static forCustomElement(element, override) {
617
- const definition = FASTElementDefinition.getForInstance(element);
618
- if ((definition === null || definition === void 0 ? void 0 : definition.templateOptions) === TemplateOptions.deferAndHydrate &&
619
- !definition.template) {
620
- element.toggleAttribute(deferHydrationAttribute, true);
621
- element.toggleAttribute(needsHydrationAttribute, true);
700
+ // If there are no more hydrating instances, invoke the hydrationComplete callback
701
+ if (((_a = HydratableElementController.hydratingInstances) === null || _a === void 0 ? void 0 : _a.size) === 0) {
702
+ (_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.hydrationComplete) === null || _c === void 0 ? void 0 : _c.call(_b);
703
+ // Reset to the default strategy after hydration is complete
704
+ ElementController.setStrategy(ElementController);
622
705
  }
623
- return super.forCustomElement(element, override);
624
706
  }
707
+ /**
708
+ * Runs connected lifecycle behavior on the associated element.
709
+ */
625
710
  connect() {
626
- var _a, _b, _c, _d, _e, _f;
711
+ var _a, _b, _c, _d, _e;
627
712
  // Initialize needsHydration on first connect
628
- if (this.needsHydration === undefined) {
629
- this.needsHydration =
630
- this.source.getAttribute(needsHydrationAttribute) !== null;
713
+ this.needsHydration =
714
+ (_a = this.needsHydration) !== null && _a !== void 0 ? _a : this.source.hasAttribute(needsHydrationAttribute);
715
+ if (this.needsHydration) {
716
+ (_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementWillHydrate) === null || _c === void 0 ? void 0 : _c.call(_b, this.definition.name);
631
717
  }
632
718
  // If the `defer-hydration` attribute exists on the source,
633
719
  // wait for it to be removed before continuing connection behavior.
634
720
  if (this.source.hasAttribute(deferHydrationAttribute)) {
721
+ this.addHydratingInstance();
635
722
  HydratableElementController.hydrationObserver.observe(this.source, {
636
723
  attributeFilter: [deferHydrationAttribute],
637
724
  });
@@ -643,20 +730,19 @@ export class HydratableElementController extends ElementController {
643
730
  // class
644
731
  if (!this.needsHydration) {
645
732
  super.connect();
733
+ this.removeHydratingInstance();
646
734
  return;
647
735
  }
648
736
  if (this.stage !== 3 /* Stages.disconnected */) {
649
737
  return;
650
738
  }
651
- // Callback: Before hydration has started
652
- (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementWillHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
653
739
  this.stage = 0 /* Stages.connecting */;
654
740
  this.bindObservables();
655
741
  this.connectBehaviors();
656
- const element = this.source;
657
- const host = (_c = getShadowRoot(element)) !== null && _c !== void 0 ? _c : element;
658
742
  if (this.template) {
659
743
  if (isHydratable(this.template)) {
744
+ const element = this.source;
745
+ const host = (_d = getShadowRoot(element)) !== null && _d !== void 0 ? _d : element;
660
746
  let firstChild = host.firstChild;
661
747
  let lastChild = host.lastChild;
662
748
  if (element.shadowRoot === null) {
@@ -671,7 +757,7 @@ export class HydratableElementController extends ElementController {
671
757
  }
672
758
  }
673
759
  this.view = this.template.hydrate(firstChild, lastChild, element);
674
- (_d = this.view) === null || _d === void 0 ? void 0 : _d.bind(this.source);
760
+ (_e = this.view) === null || _e === void 0 ? void 0 : _e.bind(this.source);
675
761
  }
676
762
  else {
677
763
  this.renderTemplate(this.template);
@@ -681,18 +767,61 @@ export class HydratableElementController extends ElementController {
681
767
  this.stage = 1 /* Stages.connected */;
682
768
  this.source.removeAttribute(needsHydrationAttribute);
683
769
  this.needsInitialization = this.needsHydration = false;
770
+ this.removeHydratingInstance();
684
771
  Observable.notify(this, isConnectedPropertyName);
772
+ }
773
+ /**
774
+ * Removes the current element instance from the hydrating instances map
775
+ */
776
+ removeHydratingInstance() {
777
+ var _a, _b;
778
+ if (!HydratableElementController.hydratingInstances) {
779
+ return;
780
+ }
781
+ const name = this.definition.name;
782
+ const instances = HydratableElementController.hydratingInstances.get(name);
685
783
  // Callback: After hydration has finished
686
- (_f = (_e = HydratableElementController.lifecycleCallbacks) === null || _e === void 0 ? void 0 : _e.elementDidHydrate) === null || _f === void 0 ? void 0 : _f.call(_e, this.definition.name);
687
- // Check if hydration is complete after this element is hydrated
688
- HydratableElementController.checkHydrationComplete();
784
+ (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
785
+ if (instances) {
786
+ instances.delete(this.source);
787
+ if (!instances.size) {
788
+ HydratableElementController.hydratingInstances.delete(name);
789
+ }
790
+ if (HydratableElementController.idleCallbackId) {
791
+ cancelIdleCallback(HydratableElementController.idleCallbackId);
792
+ }
793
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
794
+ }
689
795
  }
796
+ /**
797
+ * Unregisters the hydration observer when the element is disconnected.
798
+ */
690
799
  disconnect() {
691
800
  super.disconnect();
692
801
  HydratableElementController.hydrationObserver.unobserve(this.source);
693
802
  }
803
+ /**
804
+ * Sets the ElementController strategy to HydratableElementController.
805
+ * @remarks
806
+ * This method is typically called during application startup to enable
807
+ * hydration support for FAST elements.
808
+ */
694
809
  static install() {
695
810
  ElementController.setStrategy(HydratableElementController);
696
811
  }
697
812
  }
698
813
  HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
814
+ /**
815
+ * An idle callback ID used to track hydration completion
816
+ */
817
+ HydratableElementController.idleCallbackId = null;
818
+ /**
819
+ * A map of element instances by the name of the custom element they are
820
+ * associated with. The key is the custom element name, and the value is the
821
+ * instances of hydratable elements which currently need to be hydrated.
822
+ *
823
+ * When all of the instances in the set have been hydrated, the set is
824
+ * cleared and removed from the map. If the map is empty, the
825
+ * hydrationComplete callback is invoked.
826
+ */
827
+ HydratableElementController.hydratingInstances = new Map();
@@ -1,12 +1,3 @@
1
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
- return new (P || (P = Promise))(function (resolve, reject) {
4
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
- step((generator = generator.apply(thisArg, _arguments || [])).next());
8
- });
9
- };
10
1
  import { isFunction } from "../interfaces.js";
11
2
  import { ElementController } from "./element-controller.js";
12
3
  import { FASTElementDefinition, } from "./fast-definitions.js";
@@ -41,11 +32,21 @@ function compose(type, nameOrDef) {
41
32
  return FASTElementDefinition.compose(this, type);
42
33
  }
43
34
  function defineAsync(type, nameOrDef) {
44
- return __awaiter(this, void 0, void 0, function* () {
45
- if (isFunction(type)) {
46
- return (yield FASTElementDefinition.composeAsync(type, nameOrDef)).define().type;
47
- }
48
- return (yield FASTElementDefinition.composeAsync(this, type)).define().type;
35
+ if (isFunction(type)) {
36
+ return new Promise(resolve => {
37
+ FASTElementDefinition.composeAsync(type, nameOrDef).then(value => {
38
+ resolve(value);
39
+ });
40
+ }).then(value => {
41
+ return value.define().type;
42
+ });
43
+ }
44
+ return new Promise(resolve => {
45
+ FASTElementDefinition.composeAsync(this, type).then(value => {
46
+ resolve(value);
47
+ });
48
+ }).then(value => {
49
+ return value.define().type;
49
50
  });
50
51
  }
51
52
  function define(type, nameOrDef) {
package/dist/esm/index.js CHANGED
@@ -36,4 +36,4 @@ export { render, RenderBehavior, RenderDirective } from "./templating/render.js"
36
36
  export { customElement, FASTElement } from "./components/fast-element.js";
37
37
  export { FASTElementDefinition, fastElementRegistry, TemplateOptions, } from "./components/fast-definitions.js";
38
38
  export { attr, AttributeConfiguration, AttributeDefinition, booleanConverter, nullableBooleanConverter, nullableNumberConverter, } from "./components/attributes.js";
39
- export { ElementController, HydratableElementController, } from "./components/element-controller.js";
39
+ export { deferHydrationAttribute, ElementController, HydratableElementController, needsHydrationAttribute, } from "./components/element-controller.js";
@@ -25,3 +25,36 @@
25
25
  result.globalThis = result;
26
26
  }
27
27
  })();
28
+ (function requestIdleCallbackPolyfill() {
29
+ if ("requestIdleCallback" in globalThis) {
30
+ return;
31
+ }
32
+ /**
33
+ * A polyfill for requestIdleCallback that falls back to setTimeout.
34
+ *
35
+ * @param callback - The function to call when the browser is idle.
36
+ * @param options - Options object that may contain a timeout property.
37
+ * @returns An ID that can be used to cancel the callback.
38
+ * @public
39
+ */
40
+ globalThis.requestIdleCallback = function requestIdleCallback(callback, options) {
41
+ const start = Date.now();
42
+ return setTimeout(() => {
43
+ callback({
44
+ didTimeout: (options === null || options === void 0 ? void 0 : options.timeout)
45
+ ? Date.now() - start >= options.timeout
46
+ : false,
47
+ timeRemaining: () => 0,
48
+ });
49
+ }, 1);
50
+ };
51
+ /**
52
+ * A polyfill for cancelIdleCallback that falls back to clearTimeout.
53
+ *
54
+ * @param id - The ID of the callback to cancel.
55
+ * @public
56
+ */
57
+ globalThis.cancelIdleCallback = function cancelIdleCallback(id) {
58
+ clearTimeout(id);
59
+ };
60
+ })();