@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
package/dist/fast-element.js
CHANGED
|
@@ -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 = {
|
|
@@ -3061,7 +3094,7 @@ class HydrationView extends DefaultExecutionContext {
|
|
|
3061
3094
|
fragment.appendChild(end);
|
|
3062
3095
|
}
|
|
3063
3096
|
bind(source, context = this) {
|
|
3064
|
-
var _b
|
|
3097
|
+
var _b;
|
|
3065
3098
|
if (this.hydrationStage !== HydrationStage.hydrated) {
|
|
3066
3099
|
this._hydrationStage = HydrationStage.hydrating;
|
|
3067
3100
|
}
|
|
@@ -3105,7 +3138,28 @@ class HydrationView extends DefaultExecutionContext {
|
|
|
3105
3138
|
if (typeof templateString !== "string") {
|
|
3106
3139
|
templateString = templateString.innerHTML;
|
|
3107
3140
|
}
|
|
3108
|
-
|
|
3141
|
+
const hostElement = ((_b = this.firstChild) === null || _b === void 0 ? void 0 : _b.getRootNode())
|
|
3142
|
+
.host;
|
|
3143
|
+
const hostName = (hostElement === null || hostElement === void 0 ? void 0 : hostElement.nodeName) || "unknown";
|
|
3144
|
+
const factoryInfo = factory;
|
|
3145
|
+
// Build detailed error message
|
|
3146
|
+
const details = [
|
|
3147
|
+
`HydrationView was unable to successfully target bindings inside "<${hostName.toLowerCase()}>".`,
|
|
3148
|
+
`\nMismatch Details:`,
|
|
3149
|
+
` - Expected target node ID: "${factory.targetNodeId}"`,
|
|
3150
|
+
` - Available target IDs: [${Object.keys(this.targets).join(", ") || "none"}]`,
|
|
3151
|
+
];
|
|
3152
|
+
if (factory.targetTagName) {
|
|
3153
|
+
details.push(` - Expected tag name: "${factory.targetTagName}"`);
|
|
3154
|
+
}
|
|
3155
|
+
if (factoryInfo.sourceAspect) {
|
|
3156
|
+
details.push(` - Source aspect: "${factoryInfo.sourceAspect}"`);
|
|
3157
|
+
}
|
|
3158
|
+
if (factoryInfo.aspectType !== undefined) {
|
|
3159
|
+
details.push(` - Aspect type: ${factoryInfo.aspectType}`);
|
|
3160
|
+
}
|
|
3161
|
+
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 ? "..." : ""}`);
|
|
3162
|
+
throw new HydrationBindingError(details.join("\n"), factory, createRangeForNodes(this.firstChild, this.lastChild).cloneContents(), templateString);
|
|
3109
3163
|
}
|
|
3110
3164
|
}
|
|
3111
3165
|
}
|
|
@@ -4689,6 +4743,13 @@ const fastElementBaseTypes = new Set();
|
|
|
4689
4743
|
* @internal
|
|
4690
4744
|
*/
|
|
4691
4745
|
const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
|
|
4746
|
+
/**
|
|
4747
|
+
* Values for the `templateOptions` property.
|
|
4748
|
+
* @alpha
|
|
4749
|
+
*/
|
|
4750
|
+
const TemplateOptions = {
|
|
4751
|
+
deferAndHydrate: "defer-and-hydrate",
|
|
4752
|
+
};
|
|
4692
4753
|
/**
|
|
4693
4754
|
* Defines metadata for a FASTElement.
|
|
4694
4755
|
* @public
|
|
@@ -4752,10 +4813,12 @@ class FASTElementDefinition {
|
|
|
4752
4813
|
* This operation is idempotent per registry.
|
|
4753
4814
|
*/
|
|
4754
4815
|
define(registry = this.registry) {
|
|
4816
|
+
var _b, _c;
|
|
4755
4817
|
const type = this.type;
|
|
4756
4818
|
if (!registry.get(this.name)) {
|
|
4757
4819
|
this.platformDefined = true;
|
|
4758
4820
|
registry.define(this.name, type, this.elementOptions);
|
|
4821
|
+
(_c = (_b = this.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementDidDefine) === null || _c === void 0 ? void 0 : _c.call(_b, this.name);
|
|
4759
4822
|
}
|
|
4760
4823
|
return this;
|
|
4761
4824
|
}
|
|
@@ -4796,15 +4859,13 @@ class FASTElementDefinition {
|
|
|
4796
4859
|
}, nameOrDef));
|
|
4797
4860
|
}
|
|
4798
4861
|
const definition = new FASTElementDefinition(type, nameOrDef);
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
}
|
|
4805
|
-
|
|
4806
|
-
resolve(definition);
|
|
4807
|
-
});
|
|
4862
|
+
Observable.getNotifier(definition).subscribe({
|
|
4863
|
+
handleChange: () => {
|
|
4864
|
+
var _b, _c;
|
|
4865
|
+
(_c = (_b = definition.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.templateDidUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, definition.name);
|
|
4866
|
+
resolve(definition);
|
|
4867
|
+
},
|
|
4868
|
+
}, "template");
|
|
4808
4869
|
});
|
|
4809
4870
|
}
|
|
4810
4871
|
}
|
|
@@ -4832,9 +4893,7 @@ FASTElementDefinition.registerAsync = (name) => __awaiter(void 0, void 0, void 0
|
|
|
4832
4893
|
if (FASTElementDefinition.isRegistered[name]) {
|
|
4833
4894
|
resolve(FASTElementDefinition.isRegistered[name]);
|
|
4834
4895
|
}
|
|
4835
|
-
Observable.getNotifier(FASTElementDefinition.isRegistered).subscribe({
|
|
4836
|
-
handleChange: () => resolve(FASTElementDefinition.isRegistered[name]),
|
|
4837
|
-
}, name);
|
|
4896
|
+
Observable.getNotifier(FASTElementDefinition.isRegistered).subscribe({ handleChange: () => resolve(FASTElementDefinition.isRegistered[name]) }, name);
|
|
4838
4897
|
});
|
|
4839
4898
|
});
|
|
4840
4899
|
Observable.defineProperty(FASTElementDefinition.prototype, "template");
|
|
@@ -5930,32 +5989,77 @@ const needsHydrationAttribute = "needs-hydration";
|
|
|
5930
5989
|
* @beta
|
|
5931
5990
|
*/
|
|
5932
5991
|
class HydratableElementController extends ElementController {
|
|
5992
|
+
/**
|
|
5993
|
+
* Adds the current element instance to the hydrating instances map
|
|
5994
|
+
*/
|
|
5995
|
+
addHydratingInstance() {
|
|
5996
|
+
if (!HydratableElementController.hydratingInstances) {
|
|
5997
|
+
return;
|
|
5998
|
+
}
|
|
5999
|
+
const name = this.definition.name;
|
|
6000
|
+
let instances = HydratableElementController.hydratingInstances.get(name);
|
|
6001
|
+
if (!instances) {
|
|
6002
|
+
instances = new Set();
|
|
6003
|
+
HydratableElementController.hydratingInstances.set(name, instances);
|
|
6004
|
+
}
|
|
6005
|
+
instances.add(this.source);
|
|
6006
|
+
}
|
|
6007
|
+
/**
|
|
6008
|
+
* Configure lifecycle callbacks for hydration events
|
|
6009
|
+
*/
|
|
6010
|
+
static config(callbacks) {
|
|
6011
|
+
HydratableElementController.lifecycleCallbacks = callbacks;
|
|
6012
|
+
return this;
|
|
6013
|
+
}
|
|
5933
6014
|
static hydrationObserverHandler(records) {
|
|
5934
6015
|
for (const record of records) {
|
|
5935
|
-
|
|
5936
|
-
|
|
6016
|
+
if (!record.target.hasAttribute(deferHydrationAttribute)) {
|
|
6017
|
+
HydratableElementController.hydrationObserver.unobserve(record.target);
|
|
6018
|
+
record.target.$fastController.connect();
|
|
6019
|
+
}
|
|
6020
|
+
}
|
|
6021
|
+
}
|
|
6022
|
+
/**
|
|
6023
|
+
* Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
|
|
6024
|
+
* Then resets the ElementController strategy to the default so that future elements
|
|
6025
|
+
* don't use the HydratableElementController.
|
|
6026
|
+
*
|
|
6027
|
+
* @param deadline - the idle deadline object
|
|
6028
|
+
*/
|
|
6029
|
+
static checkHydrationComplete(deadline) {
|
|
6030
|
+
var _a, _b, _c;
|
|
6031
|
+
if (deadline.didTimeout) {
|
|
6032
|
+
HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
|
|
6033
|
+
return;
|
|
6034
|
+
}
|
|
6035
|
+
// If there are no more hydrating instances, invoke the hydrationComplete callback
|
|
6036
|
+
if (((_a = HydratableElementController.hydratingInstances) === null || _a === void 0 ? void 0 : _a.size) === 0) {
|
|
6037
|
+
(_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.hydrationComplete) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
6038
|
+
// Reset to the default strategy after hydration is complete
|
|
6039
|
+
ElementController.setStrategy(ElementController);
|
|
5937
6040
|
}
|
|
5938
6041
|
}
|
|
5939
6042
|
static forCustomElement(element, override) {
|
|
5940
6043
|
const definition = FASTElementDefinition.getForInstance(element);
|
|
5941
|
-
if (definition
|
|
5942
|
-
definition.templateOptions === "defer-and-hydrate" &&
|
|
6044
|
+
if ((definition === null || definition === void 0 ? void 0 : definition.templateOptions) === TemplateOptions.deferAndHydrate &&
|
|
5943
6045
|
!definition.template) {
|
|
5944
|
-
element.
|
|
5945
|
-
element.
|
|
6046
|
+
element.toggleAttribute(deferHydrationAttribute, true);
|
|
6047
|
+
element.toggleAttribute(needsHydrationAttribute, true);
|
|
5946
6048
|
}
|
|
5947
6049
|
return super.forCustomElement(element, override);
|
|
5948
6050
|
}
|
|
5949
6051
|
connect() {
|
|
5950
|
-
var _a, _b;
|
|
6052
|
+
var _a, _b, _c, _d, _e;
|
|
5951
6053
|
// Initialize needsHydration on first connect
|
|
5952
|
-
|
|
5953
|
-
this.needsHydration
|
|
5954
|
-
|
|
6054
|
+
this.needsHydration =
|
|
6055
|
+
(_a = this.needsHydration) !== null && _a !== void 0 ? _a : this.source.getAttribute(needsHydrationAttribute) !== null;
|
|
6056
|
+
if (this.needsHydration) {
|
|
6057
|
+
(_c = (_b = HydratableElementController.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementWillHydrate) === null || _c === void 0 ? void 0 : _c.call(_b, this.definition.name);
|
|
5955
6058
|
}
|
|
5956
6059
|
// If the `defer-hydration` attribute exists on the source,
|
|
5957
6060
|
// wait for it to be removed before continuing connection behavior.
|
|
5958
6061
|
if (this.source.hasAttribute(deferHydrationAttribute)) {
|
|
6062
|
+
this.addHydratingInstance();
|
|
5959
6063
|
HydratableElementController.hydrationObserver.observe(this.source, {
|
|
5960
6064
|
attributeFilter: [deferHydrationAttribute],
|
|
5961
6065
|
});
|
|
@@ -5967,6 +6071,7 @@ class HydratableElementController extends ElementController {
|
|
|
5967
6071
|
// class
|
|
5968
6072
|
if (!this.needsHydration) {
|
|
5969
6073
|
super.connect();
|
|
6074
|
+
this.removeHydratingInstance();
|
|
5970
6075
|
return;
|
|
5971
6076
|
}
|
|
5972
6077
|
if (this.stage !== 3 /* Stages.disconnected */) {
|
|
@@ -5975,10 +6080,10 @@ class HydratableElementController extends ElementController {
|
|
|
5975
6080
|
this.stage = 0 /* Stages.connecting */;
|
|
5976
6081
|
this.bindObservables();
|
|
5977
6082
|
this.connectBehaviors();
|
|
5978
|
-
const element = this.source;
|
|
5979
|
-
const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
|
|
5980
6083
|
if (this.template) {
|
|
5981
6084
|
if (isHydratable(this.template)) {
|
|
6085
|
+
const element = this.source;
|
|
6086
|
+
const host = (_d = getShadowRoot(element)) !== null && _d !== void 0 ? _d : element;
|
|
5982
6087
|
let firstChild = host.firstChild;
|
|
5983
6088
|
let lastChild = host.lastChild;
|
|
5984
6089
|
if (element.shadowRoot === null) {
|
|
@@ -5993,7 +6098,7 @@ class HydratableElementController extends ElementController {
|
|
|
5993
6098
|
}
|
|
5994
6099
|
}
|
|
5995
6100
|
this.view = this.template.hydrate(firstChild, lastChild, element);
|
|
5996
|
-
(
|
|
6101
|
+
(_e = this.view) === null || _e === void 0 ? void 0 : _e.bind(this.source);
|
|
5997
6102
|
}
|
|
5998
6103
|
else {
|
|
5999
6104
|
this.renderTemplate(this.template);
|
|
@@ -6003,8 +6108,32 @@ class HydratableElementController extends ElementController {
|
|
|
6003
6108
|
this.stage = 1 /* Stages.connected */;
|
|
6004
6109
|
this.source.removeAttribute(needsHydrationAttribute);
|
|
6005
6110
|
this.needsInitialization = this.needsHydration = false;
|
|
6111
|
+
this.removeHydratingInstance();
|
|
6006
6112
|
Observable.notify(this, isConnectedPropertyName);
|
|
6007
6113
|
}
|
|
6114
|
+
/**
|
|
6115
|
+
* Removes the current element instance from the hydrating instances map
|
|
6116
|
+
*/
|
|
6117
|
+
removeHydratingInstance() {
|
|
6118
|
+
var _a, _b;
|
|
6119
|
+
if (!HydratableElementController.hydratingInstances) {
|
|
6120
|
+
return;
|
|
6121
|
+
}
|
|
6122
|
+
const name = this.definition.name;
|
|
6123
|
+
const instances = HydratableElementController.hydratingInstances.get(name);
|
|
6124
|
+
// Callback: After hydration has finished
|
|
6125
|
+
(_b = (_a = HydratableElementController.lifecycleCallbacks) === null || _a === void 0 ? void 0 : _a.elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.definition.name);
|
|
6126
|
+
if (instances) {
|
|
6127
|
+
instances.delete(this.source);
|
|
6128
|
+
if (!instances.size) {
|
|
6129
|
+
HydratableElementController.hydratingInstances.delete(name);
|
|
6130
|
+
}
|
|
6131
|
+
if (HydratableElementController.idleCallbackId) {
|
|
6132
|
+
cancelIdleCallback(HydratableElementController.idleCallbackId);
|
|
6133
|
+
}
|
|
6134
|
+
HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
|
|
6135
|
+
}
|
|
6136
|
+
}
|
|
6008
6137
|
disconnect() {
|
|
6009
6138
|
super.disconnect();
|
|
6010
6139
|
HydratableElementController.hydrationObserver.unobserve(this.source);
|
|
@@ -6014,6 +6143,20 @@ class HydratableElementController extends ElementController {
|
|
|
6014
6143
|
}
|
|
6015
6144
|
}
|
|
6016
6145
|
HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
|
|
6146
|
+
/**
|
|
6147
|
+
* An idle callback ID used to track hydration completion
|
|
6148
|
+
*/
|
|
6149
|
+
HydratableElementController.idleCallbackId = null;
|
|
6150
|
+
/**
|
|
6151
|
+
* A map of element instances by the name of the custom element they are
|
|
6152
|
+
* associated with. The key is the custom element name, and the value is the
|
|
6153
|
+
* instances of hydratable elements which currently need to be hydrated.
|
|
6154
|
+
*
|
|
6155
|
+
* When all of the instances in the set have been hydrated, the set is
|
|
6156
|
+
* cleared and removed from the map. If the map is empty, the
|
|
6157
|
+
* hydrationComplete callback is invoked.
|
|
6158
|
+
*/
|
|
6159
|
+
HydratableElementController.hydratingInstances = new Map();
|
|
6017
6160
|
|
|
6018
6161
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
6019
6162
|
function createFASTElement(BaseType) {
|
|
@@ -6117,4 +6260,4 @@ function customElement(nameOrDef) {
|
|
|
6117
6260
|
|
|
6118
6261
|
DOM.setPolicy(DOMPolicy.create());
|
|
6119
6262
|
|
|
6120
|
-
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 };
|
|
6263
|
+
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 };
|