@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.
@@ -152,6 +152,39 @@ const noop = () => void 0;
152
152
  result.globalThis = result;
153
153
  }
154
154
  })();
155
+ (function requestIdleCallbackPolyfill() {
156
+ if ("requestIdleCallback" in globalThis) {
157
+ return;
158
+ }
159
+ /**
160
+ * A polyfill for requestIdleCallback that falls back to setTimeout.
161
+ *
162
+ * @param callback - The function to call when the browser is idle.
163
+ * @param options - Options object that may contain a timeout property.
164
+ * @returns An ID that can be used to cancel the callback.
165
+ * @public
166
+ */
167
+ globalThis.requestIdleCallback = function requestIdleCallback(callback, options) {
168
+ const start = Date.now();
169
+ return setTimeout(() => {
170
+ callback({
171
+ didTimeout: (options === null || options === void 0 ? void 0 : options.timeout)
172
+ ? Date.now() - start >= options.timeout
173
+ : false,
174
+ timeRemaining: () => 0,
175
+ });
176
+ }, 1);
177
+ };
178
+ /**
179
+ * A polyfill for cancelIdleCallback that falls back to clearTimeout.
180
+ *
181
+ * @param id - The ID of the callback to cancel.
182
+ * @public
183
+ */
184
+ globalThis.cancelIdleCallback = function cancelIdleCallback(id) {
185
+ clearTimeout(id);
186
+ };
187
+ })();
155
188
 
156
189
  // ensure FAST global - duplicated debug.ts
157
190
  const propConfig = {
@@ -5464,10 +5497,25 @@ class ElementController extends PropertyChangeNotifier {
5464
5497
  */
5465
5498
  constructor(element, definition) {
5466
5499
  super(element);
5500
+ /**
5501
+ * A map of observable properties that were set on the element before upgrade.
5502
+ */
5467
5503
  this.boundObservables = null;
5504
+ /**
5505
+ * Indicates whether the controller needs to perform initial rendering.
5506
+ */
5468
5507
  this.needsInitialization = true;
5508
+ /**
5509
+ * Indicates whether the element has an existing shadow root (e.g. from declarative shadow DOM).
5510
+ */
5469
5511
  this.hasExistingShadowRoot = false;
5512
+ /**
5513
+ * The template used to render the component.
5514
+ */
5470
5515
  this._template = null;
5516
+ /**
5517
+ * The current lifecycle stage of the controller.
5518
+ */
5471
5519
  this.stage = 3 /* Stages.disconnected */;
5472
5520
  /**
5473
5521
  * A guard against connecting behaviors multiple times
@@ -5475,12 +5523,19 @@ class ElementController extends PropertyChangeNotifier {
5475
5523
  * another behavior during it's connectedCallback
5476
5524
  */
5477
5525
  this.guardBehaviorConnection = false;
5526
+ /**
5527
+ * The behaviors associated with the component.
5528
+ */
5478
5529
  this.behaviors = null;
5479
5530
  /**
5480
5531
  * Tracks whether behaviors are connected so that
5481
5532
  * behaviors cant be connected multiple times
5482
5533
  */
5483
5534
  this.behaviorsConnected = false;
5535
+ /**
5536
+ * The main set of styles used for the component, independent of any
5537
+ * dynamically added styles.
5538
+ */
5484
5539
  this._mainStyles = null;
5485
5540
  /**
5486
5541
  * This allows Observable.getNotifier(...) to return the Controller
@@ -5576,6 +5631,9 @@ class ElementController extends PropertyChangeNotifier {
5576
5631
  this.renderTemplate(value);
5577
5632
  }
5578
5633
  }
5634
+ /**
5635
+ * The shadow root options for the component.
5636
+ */
5579
5637
  get shadowOptions() {
5580
5638
  return this._shadowRootOptions;
5581
5639
  }
@@ -5750,6 +5808,9 @@ class ElementController extends PropertyChangeNotifier {
5750
5808
  this.stage = 1 /* Stages.connected */;
5751
5809
  Observable.notify(this, isConnectedPropertyName);
5752
5810
  }
5811
+ /**
5812
+ * Binds any observables that were set before upgrade.
5813
+ */
5753
5814
  bindObservables() {
5754
5815
  if (this.boundObservables !== null) {
5755
5816
  const element = this.source;
@@ -5762,6 +5823,9 @@ class ElementController extends PropertyChangeNotifier {
5762
5823
  this.boundObservables = null;
5763
5824
  }
5764
5825
  }
5826
+ /**
5827
+ * Connects any existing behaviors on the associated element.
5828
+ */
5765
5829
  connectBehaviors() {
5766
5830
  if (this.behaviorsConnected === false) {
5767
5831
  const behaviors = this.behaviors;
@@ -5775,6 +5839,9 @@ class ElementController extends PropertyChangeNotifier {
5775
5839
  this.behaviorsConnected = true;
5776
5840
  }
5777
5841
  }
5842
+ /**
5843
+ * Disconnects any behaviors on the associated element.
5844
+ */
5778
5845
  disconnectBehaviors() {
5779
5846
  if (this.behaviorsConnected === true) {
5780
5847
  const behaviors = this.behaviors;
@@ -5827,6 +5894,13 @@ class ElementController extends PropertyChangeNotifier {
5827
5894
  }
5828
5895
  return false;
5829
5896
  }
5897
+ /**
5898
+ * Renders the provided template to the element.
5899
+ *
5900
+ * @param template - The template to render.
5901
+ * @remarks
5902
+ * If `null` is provided, any existing view will be removed.
5903
+ */
5830
5904
  renderTemplate(template) {
5831
5905
  var _a;
5832
5906
  // When getting the host to render to, we start by looking
@@ -6014,7 +6088,15 @@ if (ElementStyles.supportsAdoptedStyleSheets) {
6014
6088
  else {
6015
6089
  ElementStyles.setDefaultStrategy(StyleElementStrategy);
6016
6090
  }
6091
+ /**
6092
+ * The attribute used to defer hydration of an element.
6093
+ * @public
6094
+ */
6017
6095
  const deferHydrationAttribute = "defer-hydration";
6096
+ /**
6097
+ * The attribute used to indicate that an element needs hydration.
6098
+ * @public
6099
+ */
6018
6100
  const needsHydrationAttribute = "needs-hydration";
6019
6101
  /**
6020
6102
  * An ElementController capable of hydrating FAST elements from
@@ -6023,6 +6105,35 @@ const needsHydrationAttribute = "needs-hydration";
6023
6105
  * @beta
6024
6106
  */
6025
6107
  class HydratableElementController extends ElementController {
6108
+ /**
6109
+ * {@inheritdoc ElementController.shadowOptions}
6110
+ */
6111
+ get shadowOptions() {
6112
+ return super.shadowOptions;
6113
+ }
6114
+ set shadowOptions(value) {
6115
+ super.shadowOptions = value;
6116
+ if (this.hasExistingShadowRoot &&
6117
+ this.definition.templateOptions === TemplateOptions.deferAndHydrate) {
6118
+ this.source.toggleAttribute(deferHydrationAttribute, true);
6119
+ this.source.toggleAttribute(needsHydrationAttribute, true);
6120
+ }
6121
+ }
6122
+ /**
6123
+ * Adds the current element instance to the hydrating instances map
6124
+ */
6125
+ addHydratingInstance() {
6126
+ if (!HydratableElementController.hydratingInstances) {
6127
+ return;
6128
+ }
6129
+ const name = this.definition.name;
6130
+ let instances = HydratableElementController.hydratingInstances.get(name);
6131
+ if (!instances) {
6132
+ instances = new Set();
6133
+ HydratableElementController.hydratingInstances.set(name, instances);
6134
+ }
6135
+ instances.add(this.source);
6136
+ }
6026
6137
  /**
6027
6138
  * Configure lifecycle callbacks for hydration events
6028
6139
  */
@@ -6032,38 +6143,47 @@ class HydratableElementController extends ElementController {
6032
6143
  }
6033
6144
  static hydrationObserverHandler(records) {
6034
6145
  for (const record of records) {
6035
- HydratableElementController.hydrationObserver.unobserve(record.target);
6036
- record.target.$fastController.connect();
6146
+ if (!record.target.hasAttribute(deferHydrationAttribute)) {
6147
+ HydratableElementController.hydrationObserver.unobserve(record.target);
6148
+ record.target.$fastController.connect();
6149
+ }
6037
6150
  }
6038
6151
  }
6039
6152
  /**
6040
- * Checks if all elements have completed hydration and dispatches event if complete
6153
+ * Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
6154
+ * Then resets the ElementController strategy to the default so that future elements
6155
+ * don't use the HydratableElementController.
6156
+ *
6157
+ * @param deadline - the idle deadline object
6041
6158
  */
6042
- static checkHydrationComplete() {
6043
- var _a, _b;
6044
- if (!document.querySelector(`[${needsHydrationAttribute}]`)) {
6045
- (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.hydrationComplete) === null || _b === void 0 ? void 0 : _b.call(_a);
6159
+ static checkHydrationComplete(deadline) {
6160
+ var _a, _b, _c;
6161
+ if (deadline.didTimeout) {
6162
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
6163
+ return;
6046
6164
  }
6047
- }
6048
- static forCustomElement(element, override) {
6049
- const definition = FASTElementDefinition.getForInstance(element);
6050
- if ((definition === null || definition === void 0 ? void 0 : definition.templateOptions) === TemplateOptions.deferAndHydrate &&
6051
- !definition.template) {
6052
- element.toggleAttribute(deferHydrationAttribute, true);
6053
- element.toggleAttribute(needsHydrationAttribute, true);
6165
+ // If there are no more hydrating instances, invoke the hydrationComplete callback
6166
+ if (((_a = HydratableElementController.hydratingInstances) === null || _a === void 0 ? void 0 : _a.size) === 0) {
6167
+ (_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.hydrationComplete) === null || _c === void 0 ? void 0 : _c.call(_b);
6168
+ // Reset to the default strategy after hydration is complete
6169
+ ElementController.setStrategy(ElementController);
6054
6170
  }
6055
- return super.forCustomElement(element, override);
6056
6171
  }
6172
+ /**
6173
+ * Runs connected lifecycle behavior on the associated element.
6174
+ */
6057
6175
  connect() {
6058
- var _a, _b, _c, _d, _e, _f;
6176
+ var _a, _b, _c, _d, _e;
6059
6177
  // Initialize needsHydration on first connect
6060
- if (this.needsHydration === undefined) {
6061
- this.needsHydration =
6062
- this.source.getAttribute(needsHydrationAttribute) !== null;
6178
+ this.needsHydration =
6179
+ (_a = this.needsHydration) !== null && _a !== void 0 ? _a : this.source.hasAttribute(needsHydrationAttribute);
6180
+ if (this.needsHydration) {
6181
+ (_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementWillHydrate) === null || _c === void 0 ? void 0 : _c.call(_b, this.definition.name);
6063
6182
  }
6064
6183
  // If the `defer-hydration` attribute exists on the source,
6065
6184
  // wait for it to be removed before continuing connection behavior.
6066
6185
  if (this.source.hasAttribute(deferHydrationAttribute)) {
6186
+ this.addHydratingInstance();
6067
6187
  HydratableElementController.hydrationObserver.observe(this.source, {
6068
6188
  attributeFilter: [deferHydrationAttribute],
6069
6189
  });
@@ -6075,20 +6195,19 @@ class HydratableElementController extends ElementController {
6075
6195
  // class
6076
6196
  if (!this.needsHydration) {
6077
6197
  super.connect();
6198
+ this.removeHydratingInstance();
6078
6199
  return;
6079
6200
  }
6080
6201
  if (this.stage !== 3 /* Stages.disconnected */) {
6081
6202
  return;
6082
6203
  }
6083
- // Callback: Before hydration has started
6084
- (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementWillHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
6085
6204
  this.stage = 0 /* Stages.connecting */;
6086
6205
  this.bindObservables();
6087
6206
  this.connectBehaviors();
6088
- const element = this.source;
6089
- const host = (_c = getShadowRoot(element)) !== null && _c !== void 0 ? _c : element;
6090
6207
  if (this.template) {
6091
6208
  if (isHydratable(this.template)) {
6209
+ const element = this.source;
6210
+ const host = (_d = getShadowRoot(element)) !== null && _d !== void 0 ? _d : element;
6092
6211
  let firstChild = host.firstChild;
6093
6212
  let lastChild = host.lastChild;
6094
6213
  if (element.shadowRoot === null) {
@@ -6103,7 +6222,7 @@ class HydratableElementController extends ElementController {
6103
6222
  }
6104
6223
  }
6105
6224
  this.view = this.template.hydrate(firstChild, lastChild, element);
6106
- (_d = this.view) === null || _d === void 0 ? void 0 : _d.bind(this.source);
6225
+ (_e = this.view) === null || _e === void 0 ? void 0 : _e.bind(this.source);
6107
6226
  }
6108
6227
  else {
6109
6228
  this.renderTemplate(this.template);
@@ -6113,21 +6232,64 @@ class HydratableElementController extends ElementController {
6113
6232
  this.stage = 1 /* Stages.connected */;
6114
6233
  this.source.removeAttribute(needsHydrationAttribute);
6115
6234
  this.needsInitialization = this.needsHydration = false;
6235
+ this.removeHydratingInstance();
6116
6236
  Observable.notify(this, isConnectedPropertyName);
6237
+ }
6238
+ /**
6239
+ * Removes the current element instance from the hydrating instances map
6240
+ */
6241
+ removeHydratingInstance() {
6242
+ var _a, _b;
6243
+ if (!HydratableElementController.hydratingInstances) {
6244
+ return;
6245
+ }
6246
+ const name = this.definition.name;
6247
+ const instances = HydratableElementController.hydratingInstances.get(name);
6117
6248
  // Callback: After hydration has finished
6118
- (_f = (_e = HydratableElementController.lifecycleCallbacks) === null || _e === void 0 ? void 0 : _e.elementDidHydrate) === null || _f === void 0 ? void 0 : _f.call(_e, this.definition.name);
6119
- // Check if hydration is complete after this element is hydrated
6120
- HydratableElementController.checkHydrationComplete();
6249
+ (_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
6250
+ if (instances) {
6251
+ instances.delete(this.source);
6252
+ if (!instances.size) {
6253
+ HydratableElementController.hydratingInstances.delete(name);
6254
+ }
6255
+ if (HydratableElementController.idleCallbackId) {
6256
+ cancelIdleCallback(HydratableElementController.idleCallbackId);
6257
+ }
6258
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
6259
+ }
6121
6260
  }
6261
+ /**
6262
+ * Unregisters the hydration observer when the element is disconnected.
6263
+ */
6122
6264
  disconnect() {
6123
6265
  super.disconnect();
6124
6266
  HydratableElementController.hydrationObserver.unobserve(this.source);
6125
6267
  }
6268
+ /**
6269
+ * Sets the ElementController strategy to HydratableElementController.
6270
+ * @remarks
6271
+ * This method is typically called during application startup to enable
6272
+ * hydration support for FAST elements.
6273
+ */
6126
6274
  static install() {
6127
6275
  ElementController.setStrategy(HydratableElementController);
6128
6276
  }
6129
6277
  }
6130
6278
  HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
6279
+ /**
6280
+ * An idle callback ID used to track hydration completion
6281
+ */
6282
+ HydratableElementController.idleCallbackId = null;
6283
+ /**
6284
+ * A map of element instances by the name of the custom element they are
6285
+ * associated with. The key is the custom element name, and the value is the
6286
+ * instances of hydratable elements which currently need to be hydrated.
6287
+ *
6288
+ * When all of the instances in the set have been hydrated, the set is
6289
+ * cleared and removed from the map. If the map is empty, the
6290
+ * hydrationComplete callback is invoked.
6291
+ */
6292
+ HydratableElementController.hydratingInstances = new Map();
6131
6293
 
6132
6294
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
6133
6295
  function createFASTElement(BaseType) {
@@ -6160,11 +6322,21 @@ function compose(type, nameOrDef) {
6160
6322
  return FASTElementDefinition.compose(this, type);
6161
6323
  }
6162
6324
  function defineAsync(type, nameOrDef) {
6163
- return __awaiter(this, void 0, void 0, function* () {
6164
- if (isFunction(type)) {
6165
- return (yield FASTElementDefinition.composeAsync(type, nameOrDef)).define().type;
6166
- }
6167
- return (yield FASTElementDefinition.composeAsync(this, type)).define().type;
6325
+ if (isFunction(type)) {
6326
+ return new Promise(resolve => {
6327
+ FASTElementDefinition.composeAsync(type, nameOrDef).then(value => {
6328
+ resolve(value);
6329
+ });
6330
+ }).then(value => {
6331
+ return value.define().type;
6332
+ });
6333
+ }
6334
+ return new Promise(resolve => {
6335
+ FASTElementDefinition.composeAsync(this, type).then(value => {
6336
+ resolve(value);
6337
+ });
6338
+ }).then(value => {
6339
+ return value.define().type;
6168
6340
  });
6169
6341
  }
6170
6342
  function define(type, nameOrDef) {
@@ -6221,4 +6393,4 @@ function customElement(nameOrDef) {
6221
6393
 
6222
6394
  DOM.setPolicy(DOMPolicy.create());
6223
6395
 
6224
- 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 };
6396
+ 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 };