@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.
@@ -85,6 +85,39 @@ const noop = () => void 0;
85
85
  result.globalThis = result;
86
86
  }
87
87
  })();
88
+ (function requestIdleCallbackPolyfill() {
89
+ if ("requestIdleCallback" in globalThis) {
90
+ return;
91
+ }
92
+ /**
93
+ * A polyfill for requestIdleCallback that falls back to setTimeout.
94
+ *
95
+ * @param callback - The function to call when the browser is idle.
96
+ * @param options - Options object that may contain a timeout property.
97
+ * @returns An ID that can be used to cancel the callback.
98
+ * @public
99
+ */
100
+ globalThis.requestIdleCallback = function requestIdleCallback(callback, options) {
101
+ const start = Date.now();
102
+ return setTimeout(() => {
103
+ callback({
104
+ didTimeout: (options === null || options === void 0 ? void 0 : options.timeout)
105
+ ? Date.now() - start >= options.timeout
106
+ : false,
107
+ timeRemaining: () => 0,
108
+ });
109
+ }, 1);
110
+ };
111
+ /**
112
+ * A polyfill for cancelIdleCallback that falls back to clearTimeout.
113
+ *
114
+ * @param id - The ID of the callback to cancel.
115
+ * @public
116
+ */
117
+ globalThis.cancelIdleCallback = function cancelIdleCallback(id) {
118
+ clearTimeout(id);
119
+ };
120
+ })();
88
121
 
89
122
  // ensure FAST global - duplicated debug.ts
90
123
  const propConfig = {
@@ -5397,10 +5430,25 @@ class ElementController extends PropertyChangeNotifier {
5397
5430
  */
5398
5431
  constructor(element, definition) {
5399
5432
  super(element);
5433
+ /**
5434
+ * A map of observable properties that were set on the element before upgrade.
5435
+ */
5400
5436
  this.boundObservables = null;
5437
+ /**
5438
+ * Indicates whether the controller needs to perform initial rendering.
5439
+ */
5401
5440
  this.needsInitialization = true;
5441
+ /**
5442
+ * Indicates whether the element has an existing shadow root (e.g. from declarative shadow DOM).
5443
+ */
5402
5444
  this.hasExistingShadowRoot = false;
5445
+ /**
5446
+ * The template used to render the component.
5447
+ */
5403
5448
  this._template = null;
5449
+ /**
5450
+ * The current lifecycle stage of the controller.
5451
+ */
5404
5452
  this.stage = 3 /* Stages.disconnected */;
5405
5453
  /**
5406
5454
  * A guard against connecting behaviors multiple times
@@ -5408,12 +5456,19 @@ class ElementController extends PropertyChangeNotifier {
5408
5456
  * another behavior during it's connectedCallback
5409
5457
  */
5410
5458
  this.guardBehaviorConnection = false;
5459
+ /**
5460
+ * The behaviors associated with the component.
5461
+ */
5411
5462
  this.behaviors = null;
5412
5463
  /**
5413
5464
  * Tracks whether behaviors are connected so that
5414
5465
  * behaviors cant be connected multiple times
5415
5466
  */
5416
5467
  this.behaviorsConnected = false;
5468
+ /**
5469
+ * The main set of styles used for the component, independent of any
5470
+ * dynamically added styles.
5471
+ */
5417
5472
  this._mainStyles = null;
5418
5473
  /**
5419
5474
  * This allows Observable.getNotifier(...) to return the Controller
@@ -5509,6 +5564,9 @@ class ElementController extends PropertyChangeNotifier {
5509
5564
  this.renderTemplate(value);
5510
5565
  }
5511
5566
  }
5567
+ /**
5568
+ * The shadow root options for the component.
5569
+ */
5512
5570
  get shadowOptions() {
5513
5571
  return this._shadowRootOptions;
5514
5572
  }
@@ -5683,6 +5741,9 @@ class ElementController extends PropertyChangeNotifier {
5683
5741
  this.stage = 1 /* Stages.connected */;
5684
5742
  Observable.notify(this, isConnectedPropertyName);
5685
5743
  }
5744
+ /**
5745
+ * Binds any observables that were set before upgrade.
5746
+ */
5686
5747
  bindObservables() {
5687
5748
  if (this.boundObservables !== null) {
5688
5749
  const element = this.source;
@@ -5695,6 +5756,9 @@ class ElementController extends PropertyChangeNotifier {
5695
5756
  this.boundObservables = null;
5696
5757
  }
5697
5758
  }
5759
+ /**
5760
+ * Connects any existing behaviors on the associated element.
5761
+ */
5698
5762
  connectBehaviors() {
5699
5763
  if (this.behaviorsConnected === false) {
5700
5764
  const behaviors = this.behaviors;
@@ -5708,6 +5772,9 @@ class ElementController extends PropertyChangeNotifier {
5708
5772
  this.behaviorsConnected = true;
5709
5773
  }
5710
5774
  }
5775
+ /**
5776
+ * Disconnects any behaviors on the associated element.
5777
+ */
5711
5778
  disconnectBehaviors() {
5712
5779
  if (this.behaviorsConnected === true) {
5713
5780
  const behaviors = this.behaviors;
@@ -5760,6 +5827,13 @@ class ElementController extends PropertyChangeNotifier {
5760
5827
  }
5761
5828
  return false;
5762
5829
  }
5830
+ /**
5831
+ * Renders the provided template to the element.
5832
+ *
5833
+ * @param template - The template to render.
5834
+ * @remarks
5835
+ * If `null` is provided, any existing view will be removed.
5836
+ */
5763
5837
  renderTemplate(template) {
5764
5838
  var _a;
5765
5839
  // When getting the host to render to, we start by looking
@@ -5947,7 +6021,15 @@ if (ElementStyles.supportsAdoptedStyleSheets) {
5947
6021
  else {
5948
6022
  ElementStyles.setDefaultStrategy(StyleElementStrategy);
5949
6023
  }
6024
+ /**
6025
+ * The attribute used to defer hydration of an element.
6026
+ * @public
6027
+ */
5950
6028
  const deferHydrationAttribute = "defer-hydration";
6029
+ /**
6030
+ * The attribute used to indicate that an element needs hydration.
6031
+ * @public
6032
+ */
5951
6033
  const needsHydrationAttribute = "needs-hydration";
5952
6034
  /**
5953
6035
  * An ElementController capable of hydrating FAST elements from
@@ -5956,6 +6038,35 @@ const needsHydrationAttribute = "needs-hydration";
5956
6038
  * @beta
5957
6039
  */
5958
6040
  class HydratableElementController extends ElementController {
6041
+ /**
6042
+ * {@inheritdoc ElementController.shadowOptions}
6043
+ */
6044
+ get shadowOptions() {
6045
+ return super.shadowOptions;
6046
+ }
6047
+ set shadowOptions(value) {
6048
+ super.shadowOptions = value;
6049
+ if (this.hasExistingShadowRoot &&
6050
+ this.definition.templateOptions === TemplateOptions.deferAndHydrate) {
6051
+ this.source.toggleAttribute(deferHydrationAttribute, true);
6052
+ this.source.toggleAttribute(needsHydrationAttribute, true);
6053
+ }
6054
+ }
6055
+ /**
6056
+ * Adds the current element instance to the hydrating instances map
6057
+ */
6058
+ addHydratingInstance() {
6059
+ if (!HydratableElementController.hydratingInstances) {
6060
+ return;
6061
+ }
6062
+ const name = this.definition.name;
6063
+ let instances = HydratableElementController.hydratingInstances.get(name);
6064
+ if (!instances) {
6065
+ instances = new Set();
6066
+ HydratableElementController.hydratingInstances.set(name, instances);
6067
+ }
6068
+ instances.add(this.source);
6069
+ }
5959
6070
  /**
5960
6071
  * Configure lifecycle callbacks for hydration events
5961
6072
  */
@@ -5965,38 +6076,47 @@ class HydratableElementController extends ElementController {
5965
6076
  }
5966
6077
  static hydrationObserverHandler(records) {
5967
6078
  for (const record of records) {
5968
- HydratableElementController.hydrationObserver.unobserve(record.target);
5969
- record.target.$fastController.connect();
6079
+ if (!record.target.hasAttribute(deferHydrationAttribute)) {
6080
+ HydratableElementController.hydrationObserver.unobserve(record.target);
6081
+ record.target.$fastController.connect();
6082
+ }
5970
6083
  }
5971
6084
  }
5972
6085
  /**
5973
- * Checks if all elements have completed hydration and dispatches event if complete
6086
+ * Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
6087
+ * Then resets the ElementController strategy to the default so that future elements
6088
+ * don't use the HydratableElementController.
6089
+ *
6090
+ * @param deadline - the idle deadline object
5974
6091
  */
5975
- static checkHydrationComplete() {
5976
- var _a, _b;
5977
- if (!document.querySelector(`[${needsHydrationAttribute}]`)) {
5978
- (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.hydrationComplete) === null || _b === void 0 ? void 0 : _b.call(_a);
6092
+ static checkHydrationComplete(deadline) {
6093
+ var _a, _b, _c;
6094
+ if (deadline.didTimeout) {
6095
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
6096
+ return;
5979
6097
  }
5980
- }
5981
- static forCustomElement(element, override) {
5982
- const definition = FASTElementDefinition.getForInstance(element);
5983
- if ((definition === null || definition === void 0 ? void 0 : definition.templateOptions) === TemplateOptions.deferAndHydrate &&
5984
- !definition.template) {
5985
- element.toggleAttribute(deferHydrationAttribute, true);
5986
- element.toggleAttribute(needsHydrationAttribute, true);
6098
+ // If there are no more hydrating instances, invoke the hydrationComplete callback
6099
+ if (((_a = HydratableElementController.hydratingInstances) === null || _a === void 0 ? void 0 : _a.size) === 0) {
6100
+ (_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.hydrationComplete) === null || _c === void 0 ? void 0 : _c.call(_b);
6101
+ // Reset to the default strategy after hydration is complete
6102
+ ElementController.setStrategy(ElementController);
5987
6103
  }
5988
- return super.forCustomElement(element, override);
5989
6104
  }
6105
+ /**
6106
+ * Runs connected lifecycle behavior on the associated element.
6107
+ */
5990
6108
  connect() {
5991
- var _a, _b, _c, _d, _e, _f;
6109
+ var _a, _b, _c, _d, _e;
5992
6110
  // Initialize needsHydration on first connect
5993
- if (this.needsHydration === undefined) {
5994
- this.needsHydration =
5995
- this.source.getAttribute(needsHydrationAttribute) !== null;
6111
+ this.needsHydration =
6112
+ (_a = this.needsHydration) !== null && _a !== void 0 ? _a : this.source.hasAttribute(needsHydrationAttribute);
6113
+ if (this.needsHydration) {
6114
+ (_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementWillHydrate) === null || _c === void 0 ? void 0 : _c.call(_b, this.definition.name);
5996
6115
  }
5997
6116
  // If the `defer-hydration` attribute exists on the source,
5998
6117
  // wait for it to be removed before continuing connection behavior.
5999
6118
  if (this.source.hasAttribute(deferHydrationAttribute)) {
6119
+ this.addHydratingInstance();
6000
6120
  HydratableElementController.hydrationObserver.observe(this.source, {
6001
6121
  attributeFilter: [deferHydrationAttribute],
6002
6122
  });
@@ -6008,20 +6128,19 @@ class HydratableElementController extends ElementController {
6008
6128
  // class
6009
6129
  if (!this.needsHydration) {
6010
6130
  super.connect();
6131
+ this.removeHydratingInstance();
6011
6132
  return;
6012
6133
  }
6013
6134
  if (this.stage !== 3 /* Stages.disconnected */) {
6014
6135
  return;
6015
6136
  }
6016
- // Callback: Before hydration has started
6017
- (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementWillHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
6018
6137
  this.stage = 0 /* Stages.connecting */;
6019
6138
  this.bindObservables();
6020
6139
  this.connectBehaviors();
6021
- const element = this.source;
6022
- const host = (_c = getShadowRoot(element)) !== null && _c !== void 0 ? _c : element;
6023
6140
  if (this.template) {
6024
6141
  if (isHydratable(this.template)) {
6142
+ const element = this.source;
6143
+ const host = (_d = getShadowRoot(element)) !== null && _d !== void 0 ? _d : element;
6025
6144
  let firstChild = host.firstChild;
6026
6145
  let lastChild = host.lastChild;
6027
6146
  if (element.shadowRoot === null) {
@@ -6036,7 +6155,7 @@ class HydratableElementController extends ElementController {
6036
6155
  }
6037
6156
  }
6038
6157
  this.view = this.template.hydrate(firstChild, lastChild, element);
6039
- (_d = this.view) === null || _d === void 0 ? void 0 : _d.bind(this.source);
6158
+ (_e = this.view) === null || _e === void 0 ? void 0 : _e.bind(this.source);
6040
6159
  }
6041
6160
  else {
6042
6161
  this.renderTemplate(this.template);
@@ -6046,21 +6165,64 @@ class HydratableElementController extends ElementController {
6046
6165
  this.stage = 1 /* Stages.connected */;
6047
6166
  this.source.removeAttribute(needsHydrationAttribute);
6048
6167
  this.needsInitialization = this.needsHydration = false;
6168
+ this.removeHydratingInstance();
6049
6169
  Observable.notify(this, isConnectedPropertyName);
6170
+ }
6171
+ /**
6172
+ * Removes the current element instance from the hydrating instances map
6173
+ */
6174
+ removeHydratingInstance() {
6175
+ var _a, _b;
6176
+ if (!HydratableElementController.hydratingInstances) {
6177
+ return;
6178
+ }
6179
+ const name = this.definition.name;
6180
+ const instances = HydratableElementController.hydratingInstances.get(name);
6050
6181
  // Callback: After hydration has finished
6051
- (_f = (_e = HydratableElementController.lifecycleCallbacks) === null || _e === void 0 ? void 0 : _e.elementDidHydrate) === null || _f === void 0 ? void 0 : _f.call(_e, this.definition.name);
6052
- // Check if hydration is complete after this element is hydrated
6053
- HydratableElementController.checkHydrationComplete();
6182
+ (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
6183
+ if (instances) {
6184
+ instances.delete(this.source);
6185
+ if (!instances.size) {
6186
+ HydratableElementController.hydratingInstances.delete(name);
6187
+ }
6188
+ if (HydratableElementController.idleCallbackId) {
6189
+ cancelIdleCallback(HydratableElementController.idleCallbackId);
6190
+ }
6191
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
6192
+ }
6054
6193
  }
6194
+ /**
6195
+ * Unregisters the hydration observer when the element is disconnected.
6196
+ */
6055
6197
  disconnect() {
6056
6198
  super.disconnect();
6057
6199
  HydratableElementController.hydrationObserver.unobserve(this.source);
6058
6200
  }
6201
+ /**
6202
+ * Sets the ElementController strategy to HydratableElementController.
6203
+ * @remarks
6204
+ * This method is typically called during application startup to enable
6205
+ * hydration support for FAST elements.
6206
+ */
6059
6207
  static install() {
6060
6208
  ElementController.setStrategy(HydratableElementController);
6061
6209
  }
6062
6210
  }
6063
6211
  HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
6212
+ /**
6213
+ * An idle callback ID used to track hydration completion
6214
+ */
6215
+ HydratableElementController.idleCallbackId = null;
6216
+ /**
6217
+ * A map of element instances by the name of the custom element they are
6218
+ * associated with. The key is the custom element name, and the value is the
6219
+ * instances of hydratable elements which currently need to be hydrated.
6220
+ *
6221
+ * When all of the instances in the set have been hydrated, the set is
6222
+ * cleared and removed from the map. If the map is empty, the
6223
+ * hydrationComplete callback is invoked.
6224
+ */
6225
+ HydratableElementController.hydratingInstances = new Map();
6064
6226
 
6065
6227
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
6066
6228
  function createFASTElement(BaseType) {
@@ -6093,11 +6255,21 @@ function compose(type, nameOrDef) {
6093
6255
  return FASTElementDefinition.compose(this, type);
6094
6256
  }
6095
6257
  function defineAsync(type, nameOrDef) {
6096
- return __awaiter(this, void 0, void 0, function* () {
6097
- if (isFunction(type)) {
6098
- return (yield FASTElementDefinition.composeAsync(type, nameOrDef)).define().type;
6099
- }
6100
- return (yield FASTElementDefinition.composeAsync(this, type)).define().type;
6258
+ if (isFunction(type)) {
6259
+ return new Promise(resolve => {
6260
+ FASTElementDefinition.composeAsync(type, nameOrDef).then(value => {
6261
+ resolve(value);
6262
+ });
6263
+ }).then(value => {
6264
+ return value.define().type;
6265
+ });
6266
+ }
6267
+ return new Promise(resolve => {
6268
+ FASTElementDefinition.composeAsync(this, type).then(value => {
6269
+ resolve(value);
6270
+ });
6271
+ }).then(value => {
6272
+ return value.define().type;
6101
6273
  });
6102
6274
  }
6103
6275
  function define(type, nameOrDef) {
@@ -6154,4 +6326,4 @@ function customElement(nameOrDef) {
6154
6326
 
6155
6327
  DOM.setPolicy(DOMPolicy.create());
6156
6328
 
6157
- export { ArrayObserver, AttributeConfiguration, AttributeDefinition, Binding, CSSBindingDirective, CSSDirective, ChildrenDirective, Compiler, DOM, DOMAspect, ElementController, ElementStyles, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, HydratableElementController, HydrationBindingError, InlineTemplateDirective, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RenderBehavior, RenderDirective, RepeatBehavior, RepeatDirective, SlottedDirective, Sort, SourceLifetime, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, TemplateOptions, Updates, ViewTemplate, attr, booleanConverter, children, css, cssDirective, customElement, elements, emptyArray, fastElementRegistry, html, htmlDirective, lengthOf, listener, normalizeBinding$1 as normalizeBinding, nullableBooleanConverter, nullableNumberConverter, observable, oneTime, oneWay, ref, render, repeat, slotted, sortedCount, volatile, when };
6329
+ export { ArrayObserver, AttributeConfiguration, AttributeDefinition, Binding, CSSBindingDirective, CSSDirective, ChildrenDirective, Compiler, DOM, DOMAspect, ElementController, ElementStyles, ExecutionContext, FAST, FASTElement, FASTElementDefinition, HTMLBindingDirective, HTMLDirective, HTMLView, HydratableElementController, HydrationBindingError, InlineTemplateDirective, Markup, NodeObservationDirective, Observable, Parser, PropertyChangeNotifier, RefDirective, RenderBehavior, RenderDirective, RepeatBehavior, RepeatDirective, SlottedDirective, Sort, SourceLifetime, Splice, SpliceStrategy, SpliceStrategySupport, StatelessAttachedAttributeDirective, SubscriberSet, TemplateOptions, Updates, ViewTemplate, attr, booleanConverter, children, css, cssDirective, customElement, deferHydrationAttribute, elements, emptyArray, fastElementRegistry, html, htmlDirective, lengthOf, listener, needsHydrationAttribute, normalizeBinding$1 as normalizeBinding, nullableBooleanConverter, nullableNumberConverter, observable, oneTime, oneWay, ref, render, repeat, slotted, sortedCount, volatile, when };