@microsoft/fast-element 2.7.0 → 2.8.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/CHANGELOG.json +31 -1
- package/CHANGELOG.md +18 -2
- package/dist/dts/components/element-controller.d.ts +56 -0
- package/dist/dts/components/fast-definitions.d.ts +31 -2
- package/dist/dts/index.d.ts +2 -2
- package/dist/esm/components/element-controller.js +98 -14
- package/dist/esm/components/fast-definitions.js +17 -12
- package/dist/esm/index.js +1 -1
- package/dist/esm/polyfills.js +33 -0
- package/dist/esm/templating/view.js +23 -2
- package/dist/fast-element.api.json +431 -2
- package/dist/fast-element.debug.js +171 -28
- package/dist/fast-element.debug.min.js +2 -2
- package/dist/fast-element.js +171 -28
- package/dist/fast-element.min.js +2 -2
- package/dist/fast-element.untrimmed.d.ts +90 -2
- package/docs/api-report.api.md +23 -1
- package/package.json +1 -1
|
@@ -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 = {
|
|
@@ -3128,7 +3161,7 @@ class HydrationView extends DefaultExecutionContext {
|
|
|
3128
3161
|
fragment.appendChild(end);
|
|
3129
3162
|
}
|
|
3130
3163
|
bind(source, context = this) {
|
|
3131
|
-
var _b
|
|
3164
|
+
var _b;
|
|
3132
3165
|
if (this.hydrationStage !== HydrationStage.hydrated) {
|
|
3133
3166
|
this._hydrationStage = HydrationStage.hydrating;
|
|
3134
3167
|
}
|
|
@@ -3172,7 +3205,28 @@ class HydrationView extends DefaultExecutionContext {
|
|
|
3172
3205
|
if (typeof templateString !== "string") {
|
|
3173
3206
|
templateString = templateString.innerHTML;
|
|
3174
3207
|
}
|
|
3175
|
-
|
|
3208
|
+
const hostElement = ((_b = this.firstChild) === null || _b === void 0 ? void 0 : _b.getRootNode())
|
|
3209
|
+
.host;
|
|
3210
|
+
const hostName = (hostElement === null || hostElement === void 0 ? void 0 : hostElement.nodeName) || "unknown";
|
|
3211
|
+
const factoryInfo = factory;
|
|
3212
|
+
// Build detailed error message
|
|
3213
|
+
const details = [
|
|
3214
|
+
`HydrationView was unable to successfully target bindings inside "<${hostName.toLowerCase()}>".`,
|
|
3215
|
+
`\nMismatch Details:`,
|
|
3216
|
+
` - Expected target node ID: "${factory.targetNodeId}"`,
|
|
3217
|
+
` - Available target IDs: [${Object.keys(this.targets).join(", ") || "none"}]`,
|
|
3218
|
+
];
|
|
3219
|
+
if (factory.targetTagName) {
|
|
3220
|
+
details.push(` - Expected tag name: "${factory.targetTagName}"`);
|
|
3221
|
+
}
|
|
3222
|
+
if (factoryInfo.sourceAspect) {
|
|
3223
|
+
details.push(` - Source aspect: "${factoryInfo.sourceAspect}"`);
|
|
3224
|
+
}
|
|
3225
|
+
if (factoryInfo.aspectType !== undefined) {
|
|
3226
|
+
details.push(` - Aspect type: ${factoryInfo.aspectType}`);
|
|
3227
|
+
}
|
|
3228
|
+
details.push(`\nThis usually means:`, ` 1. The server-rendered HTML doesn't match the client template`, ` 2. The hydration markers are missing or corrupted`, ` 3. The DOM structure was modified before hydration`, `\nTemplate: ${templateString.slice(0, 200)}${templateString.length > 200 ? "..." : ""}`);
|
|
3229
|
+
throw new HydrationBindingError(details.join("\n"), factory, createRangeForNodes(this.firstChild, this.lastChild).cloneContents(), templateString);
|
|
3176
3230
|
}
|
|
3177
3231
|
}
|
|
3178
3232
|
}
|
|
@@ -4756,6 +4810,13 @@ const fastElementBaseTypes = new Set();
|
|
|
4756
4810
|
* @internal
|
|
4757
4811
|
*/
|
|
4758
4812
|
const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
|
|
4813
|
+
/**
|
|
4814
|
+
* Values for the `templateOptions` property.
|
|
4815
|
+
* @alpha
|
|
4816
|
+
*/
|
|
4817
|
+
const TemplateOptions = {
|
|
4818
|
+
deferAndHydrate: "defer-and-hydrate",
|
|
4819
|
+
};
|
|
4759
4820
|
/**
|
|
4760
4821
|
* Defines metadata for a FASTElement.
|
|
4761
4822
|
* @public
|
|
@@ -4819,10 +4880,12 @@ class FASTElementDefinition {
|
|
|
4819
4880
|
* This operation is idempotent per registry.
|
|
4820
4881
|
*/
|
|
4821
4882
|
define(registry = this.registry) {
|
|
4883
|
+
var _b, _c;
|
|
4822
4884
|
const type = this.type;
|
|
4823
4885
|
if (!registry.get(this.name)) {
|
|
4824
4886
|
this.platformDefined = true;
|
|
4825
4887
|
registry.define(this.name, type, this.elementOptions);
|
|
4888
|
+
(_c = (_b = this.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementDidDefine) === null || _c === void 0 ? void 0 : _c.call(_b, this.name);
|
|
4826
4889
|
}
|
|
4827
4890
|
return this;
|
|
4828
4891
|
}
|
|
@@ -4863,15 +4926,13 @@ class FASTElementDefinition {
|
|
|
4863
4926
|
}, nameOrDef));
|
|
4864
4927
|
}
|
|
4865
4928
|
const definition = new FASTElementDefinition(type, nameOrDef);
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
}
|
|
4872
|
-
|
|
4873
|
-
resolve(definition);
|
|
4874
|
-
});
|
|
4929
|
+
Observable.getNotifier(definition).subscribe({
|
|
4930
|
+
handleChange: () => {
|
|
4931
|
+
var _b, _c;
|
|
4932
|
+
(_c = (_b = definition.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.templateDidUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, definition.name);
|
|
4933
|
+
resolve(definition);
|
|
4934
|
+
},
|
|
4935
|
+
}, "template");
|
|
4875
4936
|
});
|
|
4876
4937
|
}
|
|
4877
4938
|
}
|
|
@@ -4899,9 +4960,7 @@ FASTElementDefinition.registerAsync = (name) => __awaiter(void 0, void 0, void 0
|
|
|
4899
4960
|
if (FASTElementDefinition.isRegistered[name]) {
|
|
4900
4961
|
resolve(FASTElementDefinition.isRegistered[name]);
|
|
4901
4962
|
}
|
|
4902
|
-
Observable.getNotifier(FASTElementDefinition.isRegistered).subscribe({
|
|
4903
|
-
handleChange: () => resolve(FASTElementDefinition.isRegistered[name]),
|
|
4904
|
-
}, name);
|
|
4963
|
+
Observable.getNotifier(FASTElementDefinition.isRegistered).subscribe({ handleChange: () => resolve(FASTElementDefinition.isRegistered[name]) }, name);
|
|
4905
4964
|
});
|
|
4906
4965
|
});
|
|
4907
4966
|
Observable.defineProperty(FASTElementDefinition.prototype, "template");
|
|
@@ -5997,32 +6056,77 @@ const needsHydrationAttribute = "needs-hydration";
|
|
|
5997
6056
|
* @beta
|
|
5998
6057
|
*/
|
|
5999
6058
|
class HydratableElementController extends ElementController {
|
|
6059
|
+
/**
|
|
6060
|
+
* Adds the current element instance to the hydrating instances map
|
|
6061
|
+
*/
|
|
6062
|
+
addHydratingInstance() {
|
|
6063
|
+
if (!HydratableElementController.hydratingInstances) {
|
|
6064
|
+
return;
|
|
6065
|
+
}
|
|
6066
|
+
const name = this.definition.name;
|
|
6067
|
+
let instances = HydratableElementController.hydratingInstances.get(name);
|
|
6068
|
+
if (!instances) {
|
|
6069
|
+
instances = new Set();
|
|
6070
|
+
HydratableElementController.hydratingInstances.set(name, instances);
|
|
6071
|
+
}
|
|
6072
|
+
instances.add(this.source);
|
|
6073
|
+
}
|
|
6074
|
+
/**
|
|
6075
|
+
* Configure lifecycle callbacks for hydration events
|
|
6076
|
+
*/
|
|
6077
|
+
static config(callbacks) {
|
|
6078
|
+
HydratableElementController.lifecycleCallbacks = callbacks;
|
|
6079
|
+
return this;
|
|
6080
|
+
}
|
|
6000
6081
|
static hydrationObserverHandler(records) {
|
|
6001
6082
|
for (const record of records) {
|
|
6002
|
-
|
|
6003
|
-
|
|
6083
|
+
if (!record.target.hasAttribute(deferHydrationAttribute)) {
|
|
6084
|
+
HydratableElementController.hydrationObserver.unobserve(record.target);
|
|
6085
|
+
record.target.$fastController.connect();
|
|
6086
|
+
}
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
/**
|
|
6090
|
+
* Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
|
|
6091
|
+
* Then resets the ElementController strategy to the default so that future elements
|
|
6092
|
+
* don't use the HydratableElementController.
|
|
6093
|
+
*
|
|
6094
|
+
* @param deadline - the idle deadline object
|
|
6095
|
+
*/
|
|
6096
|
+
static checkHydrationComplete(deadline) {
|
|
6097
|
+
var _a, _b, _c;
|
|
6098
|
+
if (deadline.didTimeout) {
|
|
6099
|
+
HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
|
|
6100
|
+
return;
|
|
6101
|
+
}
|
|
6102
|
+
// If there are no more hydrating instances, invoke the hydrationComplete callback
|
|
6103
|
+
if (((_a = HydratableElementController.hydratingInstances) === null || _a === void 0 ? void 0 : _a.size) === 0) {
|
|
6104
|
+
(_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.hydrationComplete) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
6105
|
+
// Reset to the default strategy after hydration is complete
|
|
6106
|
+
ElementController.setStrategy(ElementController);
|
|
6004
6107
|
}
|
|
6005
6108
|
}
|
|
6006
6109
|
static forCustomElement(element, override) {
|
|
6007
6110
|
const definition = FASTElementDefinition.getForInstance(element);
|
|
6008
|
-
if (definition
|
|
6009
|
-
definition.templateOptions === "defer-and-hydrate" &&
|
|
6111
|
+
if ((definition === null || definition === void 0 ? void 0 : definition.templateOptions) === TemplateOptions.deferAndHydrate &&
|
|
6010
6112
|
!definition.template) {
|
|
6011
|
-
element.
|
|
6012
|
-
element.
|
|
6113
|
+
element.toggleAttribute(deferHydrationAttribute, true);
|
|
6114
|
+
element.toggleAttribute(needsHydrationAttribute, true);
|
|
6013
6115
|
}
|
|
6014
6116
|
return super.forCustomElement(element, override);
|
|
6015
6117
|
}
|
|
6016
6118
|
connect() {
|
|
6017
|
-
var _a, _b;
|
|
6119
|
+
var _a, _b, _c, _d, _e;
|
|
6018
6120
|
// Initialize needsHydration on first connect
|
|
6019
|
-
|
|
6020
|
-
this.needsHydration
|
|
6021
|
-
|
|
6121
|
+
this.needsHydration =
|
|
6122
|
+
(_a = this.needsHydration) !== null && _a !== void 0 ? _a : this.source.getAttribute(needsHydrationAttribute) !== null;
|
|
6123
|
+
if (this.needsHydration) {
|
|
6124
|
+
(_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementWillHydrate) === null || _c === void 0 ? void 0 : _c.call(_b, this.definition.name);
|
|
6022
6125
|
}
|
|
6023
6126
|
// If the `defer-hydration` attribute exists on the source,
|
|
6024
6127
|
// wait for it to be removed before continuing connection behavior.
|
|
6025
6128
|
if (this.source.hasAttribute(deferHydrationAttribute)) {
|
|
6129
|
+
this.addHydratingInstance();
|
|
6026
6130
|
HydratableElementController.hydrationObserver.observe(this.source, {
|
|
6027
6131
|
attributeFilter: [deferHydrationAttribute],
|
|
6028
6132
|
});
|
|
@@ -6034,6 +6138,7 @@ class HydratableElementController extends ElementController {
|
|
|
6034
6138
|
// class
|
|
6035
6139
|
if (!this.needsHydration) {
|
|
6036
6140
|
super.connect();
|
|
6141
|
+
this.removeHydratingInstance();
|
|
6037
6142
|
return;
|
|
6038
6143
|
}
|
|
6039
6144
|
if (this.stage !== 3 /* Stages.disconnected */) {
|
|
@@ -6042,10 +6147,10 @@ class HydratableElementController extends ElementController {
|
|
|
6042
6147
|
this.stage = 0 /* Stages.connecting */;
|
|
6043
6148
|
this.bindObservables();
|
|
6044
6149
|
this.connectBehaviors();
|
|
6045
|
-
const element = this.source;
|
|
6046
|
-
const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
|
|
6047
6150
|
if (this.template) {
|
|
6048
6151
|
if (isHydratable(this.template)) {
|
|
6152
|
+
const element = this.source;
|
|
6153
|
+
const host = (_d = getShadowRoot(element)) !== null && _d !== void 0 ? _d : element;
|
|
6049
6154
|
let firstChild = host.firstChild;
|
|
6050
6155
|
let lastChild = host.lastChild;
|
|
6051
6156
|
if (element.shadowRoot === null) {
|
|
@@ -6060,7 +6165,7 @@ class HydratableElementController extends ElementController {
|
|
|
6060
6165
|
}
|
|
6061
6166
|
}
|
|
6062
6167
|
this.view = this.template.hydrate(firstChild, lastChild, element);
|
|
6063
|
-
(
|
|
6168
|
+
(_e = this.view) === null || _e === void 0 ? void 0 : _e.bind(this.source);
|
|
6064
6169
|
}
|
|
6065
6170
|
else {
|
|
6066
6171
|
this.renderTemplate(this.template);
|
|
@@ -6070,8 +6175,32 @@ class HydratableElementController extends ElementController {
|
|
|
6070
6175
|
this.stage = 1 /* Stages.connected */;
|
|
6071
6176
|
this.source.removeAttribute(needsHydrationAttribute);
|
|
6072
6177
|
this.needsInitialization = this.needsHydration = false;
|
|
6178
|
+
this.removeHydratingInstance();
|
|
6073
6179
|
Observable.notify(this, isConnectedPropertyName);
|
|
6074
6180
|
}
|
|
6181
|
+
/**
|
|
6182
|
+
* Removes the current element instance from the hydrating instances map
|
|
6183
|
+
*/
|
|
6184
|
+
removeHydratingInstance() {
|
|
6185
|
+
var _a, _b;
|
|
6186
|
+
if (!HydratableElementController.hydratingInstances) {
|
|
6187
|
+
return;
|
|
6188
|
+
}
|
|
6189
|
+
const name = this.definition.name;
|
|
6190
|
+
const instances = HydratableElementController.hydratingInstances.get(name);
|
|
6191
|
+
// Callback: After hydration has finished
|
|
6192
|
+
(_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
|
|
6193
|
+
if (instances) {
|
|
6194
|
+
instances.delete(this.source);
|
|
6195
|
+
if (!instances.size) {
|
|
6196
|
+
HydratableElementController.hydratingInstances.delete(name);
|
|
6197
|
+
}
|
|
6198
|
+
if (HydratableElementController.idleCallbackId) {
|
|
6199
|
+
cancelIdleCallback(HydratableElementController.idleCallbackId);
|
|
6200
|
+
}
|
|
6201
|
+
HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
|
|
6202
|
+
}
|
|
6203
|
+
}
|
|
6075
6204
|
disconnect() {
|
|
6076
6205
|
super.disconnect();
|
|
6077
6206
|
HydratableElementController.hydrationObserver.unobserve(this.source);
|
|
@@ -6081,6 +6210,20 @@ class HydratableElementController extends ElementController {
|
|
|
6081
6210
|
}
|
|
6082
6211
|
}
|
|
6083
6212
|
HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
|
|
6213
|
+
/**
|
|
6214
|
+
* An idle callback ID used to track hydration completion
|
|
6215
|
+
*/
|
|
6216
|
+
HydratableElementController.idleCallbackId = null;
|
|
6217
|
+
/**
|
|
6218
|
+
* A map of element instances by the name of the custom element they are
|
|
6219
|
+
* associated with. The key is the custom element name, and the value is the
|
|
6220
|
+
* instances of hydratable elements which currently need to be hydrated.
|
|
6221
|
+
*
|
|
6222
|
+
* When all of the instances in the set have been hydrated, the set is
|
|
6223
|
+
* cleared and removed from the map. If the map is empty, the
|
|
6224
|
+
* hydrationComplete callback is invoked.
|
|
6225
|
+
*/
|
|
6226
|
+
HydratableElementController.hydratingInstances = new Map();
|
|
6084
6227
|
|
|
6085
6228
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
6086
6229
|
function createFASTElement(BaseType) {
|
|
@@ -6184,4 +6327,4 @@ function customElement(nameOrDef) {
|
|
|
6184
6327
|
|
|
6185
6328
|
DOM.setPolicy(DOMPolicy.create());
|
|
6186
6329
|
|
|
6187
|
-
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, 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 };
|
|
6330
|
+
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 };
|