@microsoft/fast-element 2.4.0 → 2.5.0

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": "Fri, 23 May 2025 23:51:58 GMT",
5
+ "date": "Wed, 16 Jul 2025 22:43:21 GMT",
6
+ "version": "2.5.0",
7
+ "tag": "@microsoft/fast-element_v2.5.0",
8
+ "comments": {
9
+ "minor": [
10
+ {
11
+ "author": "7559015+janechu@users.noreply.github.com",
12
+ "package": "@microsoft/fast-element",
13
+ "commit": "245c8e45d68513ee5d733340784bdaa2afcaf909",
14
+ "comment": "Update component lifecycles with async methods to allow for a deferred define step"
15
+ }
16
+ ]
17
+ }
18
+ },
19
+ {
20
+ "date": "Fri, 30 May 2025 05:02:03 GMT",
21
+ "version": "2.4.1",
22
+ "tag": "@microsoft/fast-element_v2.4.1",
23
+ "comments": {
24
+ "patch": [
25
+ {
26
+ "author": "7559015+janechu@users.noreply.github.com",
27
+ "package": "@microsoft/fast-element",
28
+ "commit": "a31632f4c15256f2fe89c8f0decaba1fa8b5905b",
29
+ "comment": "Update logic for enumerated attribute hydration markers, add view hydration as export"
30
+ }
31
+ ]
32
+ }
33
+ },
34
+ {
35
+ "date": "Fri, 23 May 2025 23:52:29 GMT",
6
36
  "version": "2.4.0",
7
37
  "tag": "@microsoft/fast-element_v2.4.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 Fri, 23 May 2025 23:51:58 GMT and should not be manually modified. -->
3
+ <!-- This log was last generated on Wed, 16 Jul 2025 22:43:21 GMT and should not be manually modified. -->
4
4
 
5
5
  <!-- Start content -->
6
6
 
7
+ ## 2.5.0
8
+
9
+ Wed, 16 Jul 2025 22:43:21 GMT
10
+
11
+ ### Minor changes
12
+
13
+ - Update component lifecycles with async methods to allow for a deferred define step (7559015+janechu@users.noreply.github.com)
14
+
15
+ ## 2.4.1
16
+
17
+ Fri, 30 May 2025 05:02:03 GMT
18
+
19
+ ### Patches
20
+
21
+ - Update logic for enumerated attribute hydration markers, add view hydration as export (7559015+janechu@users.noreply.github.com)
22
+
7
23
  ## 2.4.0
8
24
 
9
- Fri, 23 May 2025 23:51:58 GMT
25
+ Fri, 23 May 2025 23:52:29 GMT
10
26
 
11
27
  ### Minor changes
12
28
 
@@ -204,6 +204,8 @@ export declare class StyleElementStrategy implements StyleStrategy {
204
204
  addStylesTo(target: StyleTarget): void;
205
205
  removeStylesFrom(target: StyleTarget): void;
206
206
  }
207
+ export declare const deferHydrationAttribute = "defer-hydration";
208
+ export declare const needsHydrationAttribute = "needs-hydration";
207
209
  /**
208
210
  * An ElementController capable of hydrating FAST elements from
209
211
  * Declarative Shadow DOM.
@@ -219,6 +221,7 @@ export declare class HydratableElementController<TElement extends HTMLElement =
219
221
  protected needsHydration?: boolean;
220
222
  private static hydrationObserver;
221
223
  private static hydrationObserverHandler;
224
+ static forCustomElement(element: HTMLElement, override?: boolean): ElementController<HTMLElement>;
222
225
  connect(): void;
223
226
  disconnect(): void;
224
227
  static install(): void;
@@ -21,6 +21,11 @@ export interface ShadowRootOptions extends ShadowRootInit {
21
21
  */
22
22
  registry?: CustomElementRegistry;
23
23
  }
24
+ /**
25
+ * Template options.
26
+ * @alpha
27
+ */
28
+ export declare type TemplateOptions = "defer-and-hydrate";
24
29
  /**
25
30
  * Represents metadata configuration for a custom element.
26
31
  * @public
@@ -34,6 +39,11 @@ export interface PartialFASTElementDefinition {
34
39
  * The template to render for the custom element.
35
40
  */
36
41
  readonly template?: ElementViewTemplate;
42
+ /**
43
+ * Options controlling how the template will be created.
44
+ * @alpha
45
+ */
46
+ readonly templateOptions?: TemplateOptions;
37
47
  /**
38
48
  * The styles to associate with the custom element.
39
49
  */
@@ -94,6 +104,11 @@ export declare class FASTElementDefinition<TType extends Constructable<HTMLEleme
94
104
  * The template to render for the custom element.
95
105
  */
96
106
  template?: ElementViewTemplate;
107
+ /**
108
+ * The template options.
109
+ * @alpha
110
+ */
111
+ templateOptions?: TemplateOptions;
97
112
  /**
98
113
  * The styles to associate with the custom element.
99
114
  */
@@ -110,6 +125,10 @@ export declare class FASTElementDefinition<TType extends Constructable<HTMLEleme
110
125
  * The registry to register this component in by default.
111
126
  */
112
127
  readonly registry: CustomElementRegistry;
128
+ /**
129
+ * The definition has been registered to the FAST element registry.
130
+ */
131
+ static isRegistered: Record<string, Function>;
113
132
  private constructor();
114
133
  /**
115
134
  * Defines a custom element based on this definition.
@@ -141,4 +160,20 @@ export declare class FASTElementDefinition<TType extends Constructable<HTMLEleme
141
160
  * @param instance - The custom element instance to retrieve the definition for.
142
161
  */
143
162
  static readonly getForInstance: (object: any) => FASTElementDefinition<Constructable<HTMLElement>> | undefined;
163
+ /**
164
+ * Indicates when a custom elements definition has been registered with the fastElementRegistry.
165
+ * @param name - The name of the defined custom element.
166
+ * @alpha
167
+ */
168
+ static registerAsync: (name: string) => Promise<Function>;
169
+ /**
170
+ * Creates an instance of FASTElementDefinition asynchronously. This option assumes
171
+ * that a template and shadowOptions will be provided and completes when those requirements
172
+ * are met.
173
+ * @param type - The type this definition is being created for.
174
+ * @param nameOrDef - The name of the element to define or a config object
175
+ * that describes the element to define.
176
+ * @alpha
177
+ */
178
+ static composeAsync<TType extends Constructable<HTMLElement> = Constructable<HTMLElement>>(type: TType, nameOrDef?: string | PartialFASTElementDefinition): Promise<FASTElementDefinition<TType>>;
144
179
  }
@@ -47,6 +47,8 @@ export interface FASTElement extends HTMLElement {
47
47
  }
48
48
  declare function compose<TType extends Constructable<HTMLElement> = Constructable<HTMLElement>>(this: TType, nameOrDef: string | PartialFASTElementDefinition): FASTElementDefinition<TType>;
49
49
  declare function compose<TType extends Constructable<HTMLElement> = Constructable<HTMLElement>>(type: TType, nameOrDef?: string | PartialFASTElementDefinition): FASTElementDefinition<TType>;
50
+ declare function defineAsync<TType extends Constructable<HTMLElement> = Constructable<HTMLElement>>(this: TType, nameOrDef: string | PartialFASTElementDefinition): Promise<FASTElementDefinition<TType>>;
51
+ declare function defineAsync<TType extends Constructable<HTMLElement> = Constructable<HTMLElement>>(type: TType, nameOrDef?: string | PartialFASTElementDefinition): Promise<FASTElementDefinition<TType>>;
50
52
  declare function define<TType extends Constructable<HTMLElement> = Constructable<HTMLElement>>(this: TType, nameOrDef: string | PartialFASTElementDefinition): TType;
51
53
  declare function define<TType extends Constructable<HTMLElement> = Constructable<HTMLElement>>(type: TType, nameOrDef?: string | PartialFASTElementDefinition): TType;
52
54
  declare function from<TBase extends typeof HTMLElement>(BaseType: TBase): new () => InstanceType<TBase> & FASTElement;
@@ -59,6 +61,7 @@ export declare const FASTElement: {
59
61
  new (): FASTElement;
60
62
  define: typeof define;
61
63
  compose: typeof compose;
64
+ defineAsync: typeof defineAsync;
62
65
  from: typeof from;
63
66
  };
64
67
  /**
@@ -29,6 +29,6 @@ export { ElementView, HTMLView, SyntheticView, View, HydratableView, HydrationBi
29
29
  export { elements, ElementsFilter, NodeBehaviorOptions, NodeObservationDirective, } from "./templating/node-observation.js";
30
30
  export { render, RenderBehavior, RenderDirective } from "./templating/render.js";
31
31
  export { customElement, FASTElement } from "./components/fast-element.js";
32
- export { FASTElementDefinition, PartialFASTElementDefinition, ShadowRootOptions, fastElementRegistry, TypeRegistry, } from "./components/fast-definitions.js";
32
+ export { FASTElementDefinition, PartialFASTElementDefinition, ShadowRootOptions, fastElementRegistry, TemplateOptions, TypeRegistry, } from "./components/fast-definitions.js";
33
33
  export { attr, AttributeConfiguration, AttributeDefinition, AttributeMode, booleanConverter, DecoratorAttributeConfiguration, nullableBooleanConverter, nullableNumberConverter, ValueConverter, } from "./components/attributes.js";
34
34
  export { ElementController, ElementControllerStrategy, HydratableElementController, } from "./components/element-controller.js";
@@ -582,8 +582,8 @@ if (ElementStyles.supportsAdoptedStyleSheets) {
582
582
  else {
583
583
  ElementStyles.setDefaultStrategy(StyleElementStrategy);
584
584
  }
585
- const deferHydrationAttribute = "defer-hydration";
586
- const needsHydrationAttribute = "needs-hydration";
585
+ export const deferHydrationAttribute = "defer-hydration";
586
+ export const needsHydrationAttribute = "needs-hydration";
587
587
  /**
588
588
  * An ElementController capable of hydrating FAST elements from
589
589
  * Declarative Shadow DOM.
@@ -597,6 +597,16 @@ export class HydratableElementController extends ElementController {
597
597
  record.target.$fastController.connect();
598
598
  }
599
599
  }
600
+ static forCustomElement(element, override) {
601
+ const definition = FASTElementDefinition.getForInstance(element);
602
+ if (definition !== undefined &&
603
+ definition.templateOptions === "defer-and-hydrate" &&
604
+ !definition.template) {
605
+ element.setAttribute(deferHydrationAttribute, "");
606
+ element.setAttribute(needsHydrationAttribute, "");
607
+ }
608
+ return super.forCustomElement(element, override);
609
+ }
600
610
  connect() {
601
611
  var _a, _b;
602
612
  // Initialize needsHydration on first connect
@@ -1,3 +1,13 @@
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
+ var _a;
1
11
  import { isString, KernelServiceId } from "../interfaces.js";
2
12
  import { Observable } from "../observation/observable.js";
3
13
  import { createTypeRegistry, FAST } from "../platform.js";
@@ -17,7 +27,7 @@ export const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry,
17
27
  */
18
28
  export class FASTElementDefinition {
19
29
  constructor(type, nameOrConfig = type.definition) {
20
- var _a;
30
+ var _b;
21
31
  this.platformDefined = false;
22
32
  if (isString(nameOrConfig)) {
23
33
  nameOrConfig = { name: nameOrConfig };
@@ -25,7 +35,8 @@ export class FASTElementDefinition {
25
35
  this.type = type;
26
36
  this.name = nameOrConfig.name;
27
37
  this.template = nameOrConfig.template;
28
- this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
38
+ this.templateOptions = nameOrConfig.templateOptions;
39
+ this.registry = (_b = nameOrConfig.registry) !== null && _b !== void 0 ? _b : customElements;
29
40
  const proto = type.prototype;
30
41
  const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
31
42
  const observedAttributes = new Array(attributes.length);
@@ -57,6 +68,8 @@ export class FASTElementDefinition {
57
68
  : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
58
69
  this.styles = ElementStyles.normalize(nameOrConfig.styles);
59
70
  fastElementRegistry.register(this);
71
+ Observable.defineProperty(FASTElementDefinition.isRegistered, this.name);
72
+ FASTElementDefinition.isRegistered[this.name] = this.type;
60
73
  }
61
74
  /**
62
75
  * Indicates if this element has been defined in at least one registry.
@@ -99,7 +112,44 @@ export class FASTElementDefinition {
99
112
  static registerBaseType(type) {
100
113
  fastElementBaseTypes.add(type);
101
114
  }
115
+ /**
116
+ * Creates an instance of FASTElementDefinition asynchronously. This option assumes
117
+ * that a template and shadowOptions will be provided and completes when those requirements
118
+ * are met.
119
+ * @param type - The type this definition is being created for.
120
+ * @param nameOrDef - The name of the element to define or a config object
121
+ * that describes the element to define.
122
+ * @alpha
123
+ */
124
+ static composeAsync(type, nameOrDef) {
125
+ return new Promise(resolve => {
126
+ if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
127
+ resolve(new FASTElementDefinition(class extends type {
128
+ }, nameOrDef));
129
+ }
130
+ const definition = new FASTElementDefinition(type, nameOrDef);
131
+ Promise.all([
132
+ new Promise(resolve => {
133
+ Observable.getNotifier(definition).subscribe({
134
+ handleChange: () => resolve(),
135
+ }, "template");
136
+ }),
137
+ new Promise(resolve => {
138
+ Observable.getNotifier(definition).subscribe({
139
+ handleChange: () => resolve(),
140
+ }, "shadowOptions");
141
+ }),
142
+ ]).then(() => {
143
+ resolve(definition);
144
+ });
145
+ });
146
+ }
102
147
  }
148
+ _a = FASTElementDefinition;
149
+ /**
150
+ * The definition has been registered to the FAST element registry.
151
+ */
152
+ FASTElementDefinition.isRegistered = {};
103
153
  /**
104
154
  * Gets the element definition associated with the specified type.
105
155
  * @param type - The custom element type to retrieve the definition for.
@@ -110,5 +160,20 @@ FASTElementDefinition.getByType = fastElementRegistry.getByType;
110
160
  * @param instance - The custom element instance to retrieve the definition for.
111
161
  */
112
162
  FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
163
+ /**
164
+ * Indicates when a custom elements definition has been registered with the fastElementRegistry.
165
+ * @param name - The name of the defined custom element.
166
+ * @alpha
167
+ */
168
+ FASTElementDefinition.registerAsync = (name) => __awaiter(void 0, void 0, void 0, function* () {
169
+ return new Promise(resolve => {
170
+ if (FASTElementDefinition.isRegistered[name]) {
171
+ resolve(FASTElementDefinition.isRegistered[name]);
172
+ }
173
+ Observable.getNotifier(FASTElementDefinition.isRegistered).subscribe({
174
+ handleChange: () => resolve(FASTElementDefinition.isRegistered[name]),
175
+ }, name);
176
+ });
177
+ });
113
178
  Observable.defineProperty(FASTElementDefinition.prototype, "template");
114
179
  Observable.defineProperty(FASTElementDefinition.prototype, "shadowOptions");
@@ -31,6 +31,24 @@ function compose(type, nameOrDef) {
31
31
  }
32
32
  return FASTElementDefinition.compose(this, type);
33
33
  }
34
+ function defineAsync(type, nameOrDef) {
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;
50
+ });
51
+ }
34
52
  function define(type, nameOrDef) {
35
53
  if (isFunction(type)) {
36
54
  return FASTElementDefinition.compose(type, nameOrDef).define().type;
@@ -64,6 +82,11 @@ export const FASTElement = Object.assign(createFASTElement(HTMLElement), {
64
82
  * @public
65
83
  */
66
84
  compose,
85
+ /**
86
+ * Defines metadata for a FASTElement which can be used after it has been resolved to define the element.
87
+ * @alpha
88
+ */
89
+ defineAsync,
67
90
  });
68
91
  /**
69
92
  * Decorator: Defines a platform custom element based on `FASTElement`.
@@ -62,11 +62,20 @@ export const HydrationMarkup = Object.freeze({
62
62
  */
63
63
  parseEnumeratedAttributeBinding(node) {
64
64
  const attrs = [];
65
- let count = 0;
66
- while (node.hasAttribute(`${this.attributeMarkerName}-${count}`)) {
67
- attrs.push(count++);
65
+ const prefixLength = this.attributeMarkerName.length + 1;
66
+ const prefix = `${this.attributeMarkerName}-`;
67
+ for (const attr of node.getAttributeNames()) {
68
+ if (attr.startsWith(prefix)) {
69
+ const count = Number(attr.slice(prefixLength));
70
+ if (!Number.isNaN(count)) {
71
+ attrs.push(count);
72
+ }
73
+ else {
74
+ throw new Error(`Invalid attribute marker name: ${attr}. Expected format is ${prefix}<number>.`);
75
+ }
76
+ }
68
77
  }
69
- return count === 0 ? null : attrs;
78
+ return attrs.length === 0 ? null : attrs;
70
79
  },
71
80
  /**
72
81
  * Parses the ViewBehaviorFactory index from string data. Returns