@fluentui/web-components 3.0.0-rc.18 → 3.0.0-rc.19

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.
@@ -65,6 +65,44 @@ switch (kernelMode) {
65
65
  });
66
66
  break;
67
67
  }
68
+ /**
69
+ * Warning and error messages.
70
+ * @internal
71
+ */
72
+ var Message;
73
+ (function (Message) {
74
+ // 1000 - 1100 Kernel
75
+ // 1101 - 1200 Observation
76
+ Message[Message["needsArrayObservation"] = 1101] = "needsArrayObservation";
77
+ // 1201 - 1300 Templating
78
+ Message[Message["onlySetDOMPolicyOnce"] = 1201] = "onlySetDOMPolicyOnce";
79
+ Message[Message["bindingInnerHTMLRequiresTrustedTypes"] = 1202] = "bindingInnerHTMLRequiresTrustedTypes";
80
+ Message[Message["twoWayBindingRequiresObservables"] = 1203] = "twoWayBindingRequiresObservables";
81
+ Message[Message["hostBindingWithoutHost"] = 1204] = "hostBindingWithoutHost";
82
+ Message[Message["unsupportedBindingBehavior"] = 1205] = "unsupportedBindingBehavior";
83
+ Message[Message["directCallToHTMLTagNotAllowed"] = 1206] = "directCallToHTMLTagNotAllowed";
84
+ Message[Message["onlySetTemplatePolicyOnce"] = 1207] = "onlySetTemplatePolicyOnce";
85
+ Message[Message["cannotSetTemplatePolicyAfterCompilation"] = 1208] = "cannotSetTemplatePolicyAfterCompilation";
86
+ Message[Message["blockedByDOMPolicy"] = 1209] = "blockedByDOMPolicy";
87
+ // 1301 - 1400 Styles
88
+ // 1401 - 1500 Components
89
+ Message[Message["missingElementDefinition"] = 1401] = "missingElementDefinition";
90
+ // 1501 - 1600 Context and Dependency Injection
91
+ Message[Message["noRegistrationForContext"] = 1501] = "noRegistrationForContext";
92
+ Message[Message["noFactoryForResolver"] = 1502] = "noFactoryForResolver";
93
+ Message[Message["invalidResolverStrategy"] = 1503] = "invalidResolverStrategy";
94
+ Message[Message["cannotAutoregisterDependency"] = 1504] = "cannotAutoregisterDependency";
95
+ Message[Message["cannotResolveKey"] = 1505] = "cannotResolveKey";
96
+ Message[Message["cannotConstructNativeFunction"] = 1506] = "cannotConstructNativeFunction";
97
+ Message[Message["cannotJITRegisterNonConstructor"] = 1507] = "cannotJITRegisterNonConstructor";
98
+ Message[Message["cannotJITRegisterIntrinsic"] = 1508] = "cannotJITRegisterIntrinsic";
99
+ Message[Message["cannotJITRegisterInterface"] = 1509] = "cannotJITRegisterInterface";
100
+ Message[Message["invalidResolver"] = 1510] = "invalidResolver";
101
+ Message[Message["invalidKey"] = 1511] = "invalidKey";
102
+ Message[Message["noDefaultResolver"] = 1512] = "noDefaultResolver";
103
+ Message[Message["cyclicDependency"] = 1513] = "cyclicDependency";
104
+ Message[Message["connectUpdateRequiresController"] = 1514] = "connectUpdateRequiresController";
105
+ })(Message || (Message = {}));
68
106
  /**
69
107
  * Determines whether or not an object is a function.
70
108
  * @public
@@ -110,6 +148,39 @@ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof win
110
148
  result.globalThis = result;
111
149
  }
112
150
  })();
151
+ (function requestIdleCallbackPolyfill() {
152
+ if ("requestIdleCallback" in globalThis) {
153
+ return;
154
+ }
155
+ /**
156
+ * A polyfill for requestIdleCallback that falls back to setTimeout.
157
+ *
158
+ * @param callback - The function to call when the browser is idle.
159
+ * @param options - Options object that may contain a timeout property.
160
+ * @returns An ID that can be used to cancel the callback.
161
+ * @public
162
+ */
163
+ globalThis.requestIdleCallback = function requestIdleCallback(callback, options) {
164
+ const start = Date.now();
165
+ return setTimeout(() => {
166
+ callback({
167
+ didTimeout: (options === null || options === void 0 ? void 0 : options.timeout)
168
+ ? Date.now() - start >= options.timeout
169
+ : false,
170
+ timeRemaining: () => 0,
171
+ });
172
+ }, 1);
173
+ };
174
+ /**
175
+ * A polyfill for cancelIdleCallback that falls back to clearTimeout.
176
+ *
177
+ * @param id - The ID of the callback to cancel.
178
+ * @public
179
+ */
180
+ globalThis.cancelIdleCallback = function cancelIdleCallback(id) {
181
+ clearTimeout(id);
182
+ };
183
+ })();
113
184
 
114
185
  // ensure FAST global - duplicated debug.ts
115
186
  const propConfig = {
@@ -274,7 +345,7 @@ const DOM = Object.freeze({
274
345
  */
275
346
  setPolicy(value) {
276
347
  if (defaultPolicy !== fastPolicy) {
277
- throw FAST.error(1201 /* Message.onlySetDOMPolicyOnce */);
348
+ throw FAST.error(Message.onlySetDOMPolicyOnce);
278
349
  }
279
350
  defaultPolicy = value;
280
351
  },
@@ -307,72 +378,6 @@ const DOM = Object.freeze({
307
378
  },
308
379
  });
309
380
 
310
- /**
311
- * The default UpdateQueue.
312
- * @public
313
- */
314
- const Updates = FAST.getById(KernelServiceId.updateQueue, () => {
315
- const tasks = [];
316
- const pendingErrors = [];
317
- const rAF = globalThis.requestAnimationFrame;
318
- let updateAsync = true;
319
- function throwFirstError() {
320
- if (pendingErrors.length) {
321
- throw pendingErrors.shift();
322
- }
323
- }
324
- function tryRunTask(task) {
325
- try {
326
- task.call();
327
- }
328
- catch (error) {
329
- if (updateAsync) {
330
- pendingErrors.push(error);
331
- setTimeout(throwFirstError, 0);
332
- }
333
- else {
334
- tasks.length = 0;
335
- throw error;
336
- }
337
- }
338
- }
339
- function process() {
340
- const capacity = 1024;
341
- let index = 0;
342
- while (index < tasks.length) {
343
- tryRunTask(tasks[index]);
344
- index++;
345
- // Prevent leaking memory for long chains of recursive calls to `enqueue`.
346
- // If we call `enqueue` within a task scheduled by `enqueue`, the queue will
347
- // grow, but to avoid an O(n) walk for every task we execute, we don't
348
- // shift tasks off the queue after they have been executed.
349
- // Instead, we periodically shift 1024 tasks off the queue.
350
- if (index > capacity) {
351
- // Manually shift all values starting at the index back to the
352
- // beginning of the queue.
353
- for (let scan = 0, newLength = tasks.length - index; scan < newLength; scan++) {
354
- tasks[scan] = tasks[scan + index];
355
- }
356
- tasks.length -= index;
357
- index = 0;
358
- }
359
- }
360
- tasks.length = 0;
361
- }
362
- function enqueue(callable) {
363
- tasks.push(callable);
364
- if (tasks.length < 2) {
365
- updateAsync ? rAF(process) : process();
366
- }
367
- }
368
- return Object.freeze({
369
- enqueue,
370
- next: () => new Promise(enqueue),
371
- process,
372
- setMode: (isAsync) => (updateAsync = isAsync),
373
- });
374
- });
375
-
376
381
  /**
377
382
  * An implementation of {@link Notifier} that efficiently keeps track of
378
383
  * subscribers interested in a specific change notification on an
@@ -538,6 +543,72 @@ class PropertyChangeNotifier {
538
543
  }
539
544
  }
540
545
 
546
+ /**
547
+ * The default UpdateQueue.
548
+ * @public
549
+ */
550
+ const Updates = FAST.getById(KernelServiceId.updateQueue, () => {
551
+ const tasks = [];
552
+ const pendingErrors = [];
553
+ const rAF = globalThis.requestAnimationFrame;
554
+ let updateAsync = true;
555
+ function throwFirstError() {
556
+ if (pendingErrors.length) {
557
+ throw pendingErrors.shift();
558
+ }
559
+ }
560
+ function tryRunTask(task) {
561
+ try {
562
+ task.call();
563
+ }
564
+ catch (error) {
565
+ if (updateAsync) {
566
+ pendingErrors.push(error);
567
+ setTimeout(throwFirstError, 0);
568
+ }
569
+ else {
570
+ tasks.length = 0;
571
+ throw error;
572
+ }
573
+ }
574
+ }
575
+ function process() {
576
+ const capacity = 1024;
577
+ let index = 0;
578
+ while (index < tasks.length) {
579
+ tryRunTask(tasks[index]);
580
+ index++;
581
+ // Prevent leaking memory for long chains of recursive calls to `enqueue`.
582
+ // If we call `enqueue` within a task scheduled by `enqueue`, the queue will
583
+ // grow, but to avoid an O(n) walk for every task we execute, we don't
584
+ // shift tasks off the queue after they have been executed.
585
+ // Instead, we periodically shift 1024 tasks off the queue.
586
+ if (index > capacity) {
587
+ // Manually shift all values starting at the index back to the
588
+ // beginning of the queue.
589
+ for (let scan = 0, newLength = tasks.length - index; scan < newLength; scan++) {
590
+ tasks[scan] = tasks[scan + index];
591
+ }
592
+ tasks.length -= index;
593
+ index = 0;
594
+ }
595
+ }
596
+ tasks.length = 0;
597
+ }
598
+ function enqueue(callable) {
599
+ tasks.push(callable);
600
+ if (tasks.length < 2) {
601
+ updateAsync ? rAF(process) : process();
602
+ }
603
+ }
604
+ return Object.freeze({
605
+ enqueue,
606
+ next: () => new Promise(enqueue),
607
+ process,
608
+ setMode: (isAsync) => (updateAsync = isAsync),
609
+ });
610
+ });
611
+
541
612
  /**
542
613
  * Describes how the source's lifetime relates to its controller's lifetime.
543
614
  * @public
@@ -563,7 +634,7 @@ const Observable = FAST.getById(KernelServiceId.observable, () => {
563
634
  const notifierLookup = new WeakMap();
564
635
  let watcher = void 0;
565
636
  let createArrayObserver = (array) => {
566
- throw FAST.error(1101 /* Message.needsArrayObservation */);
637
+ throw FAST.error(Message.needsArrayObservation);
567
638
  };
568
639
  function getNotifier(source) {
569
640
  var _a;
@@ -929,97 +1000,6 @@ function oneTime(expression, policy) {
929
1000
  return new OneTimeBinding(expression, policy);
930
1001
  }
931
1002
 
932
- let DefaultStyleStrategy;
933
- function reduceStyles(styles) {
934
- return styles
935
- .map((x) => x instanceof ElementStyles ? reduceStyles(x.styles) : [x])
936
- .reduce((prev, curr) => prev.concat(curr), []);
937
- }
938
- /**
939
- * Represents styles that can be applied to a custom element.
940
- * @public
941
- */
942
- class ElementStyles {
943
- /**
944
- * Creates an instance of ElementStyles.
945
- * @param styles - The styles that will be associated with elements.
946
- */
947
- constructor(styles) {
948
- this.styles = styles;
949
- this.targets = new WeakSet();
950
- this._strategy = null;
951
- this.behaviors = styles
952
- .map((x) => x instanceof ElementStyles ? x.behaviors : null)
953
- .reduce((prev, curr) => (curr === null ? prev : prev === null ? curr : prev.concat(curr)), null);
954
- }
955
- /**
956
- * Gets the StyleStrategy associated with these element styles.
957
- */
958
- get strategy() {
959
- if (this._strategy === null) {
960
- this.withStrategy(DefaultStyleStrategy);
961
- }
962
- return this._strategy;
963
- }
964
- /** @internal */
965
- addStylesTo(target) {
966
- this.strategy.addStylesTo(target);
967
- this.targets.add(target);
968
- }
969
- /** @internal */
970
- removeStylesFrom(target) {
971
- this.strategy.removeStylesFrom(target);
972
- this.targets.delete(target);
973
- }
974
- /** @internal */
975
- isAttachedTo(target) {
976
- return this.targets.has(target);
977
- }
978
- /**
979
- * Associates behaviors with this set of styles.
980
- * @param behaviors - The behaviors to associate.
981
- */
982
- withBehaviors(...behaviors) {
983
- this.behaviors =
984
- this.behaviors === null ? behaviors : this.behaviors.concat(behaviors);
985
- return this;
986
- }
987
- /**
988
- * Sets the strategy that handles adding/removing these styles for an element.
989
- * @param strategy - The strategy to use.
990
- */
991
- withStrategy(Strategy) {
992
- this._strategy = new Strategy(reduceStyles(this.styles));
993
- return this;
994
- }
995
- /**
996
- * Sets the default strategy type to use when creating style strategies.
997
- * @param Strategy - The strategy type to construct.
998
- */
999
- static setDefaultStrategy(Strategy) {
1000
- DefaultStyleStrategy = Strategy;
1001
- }
1002
- /**
1003
- * Normalizes a set of composable style options.
1004
- * @param styles - The style options to normalize.
1005
- * @returns A singular ElementStyles instance or undefined.
1006
- */
1007
- static normalize(styles) {
1008
- return styles === void 0
1009
- ? void 0
1010
- : Array.isArray(styles)
1011
- ? new ElementStyles(styles)
1012
- : styles instanceof ElementStyles
1013
- ? styles
1014
- : new ElementStyles([styles]);
1015
- }
1016
- }
1017
- /**
1018
- * Indicates whether the DOM supports the adoptedStyleSheets feature.
1019
- */
1020
- ElementStyles.supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
1021
- "replace" in CSSStyleSheet.prototype;
1022
-
1023
1003
  const registry$1 = createTypeRegistry();
1024
1004
  /**
1025
1005
  * Instructs the css engine to provide dynamic styles or
@@ -1123,22 +1103,113 @@ class CSSBindingDirective {
1123
1103
  }
1124
1104
  CSSDirective.define(CSSBindingDirective);
1125
1105
 
1126
- const marker$1 = `${Math.random().toString(36).substring(2, 8)}`;
1127
- let varId = 0;
1128
- const nextCSSVariable = () => `--v${marker$1}${++varId}`;
1129
- function collectStyles(strings, values) {
1130
- const styles = [];
1131
- let cssString = "";
1132
- const behaviors = [];
1133
- const add = (behavior) => {
1134
- behaviors.push(behavior);
1135
- };
1136
- for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
1137
- cssString += strings[i];
1138
- let value = values[i];
1139
- if (isFunction(value)) {
1140
- value = new CSSBindingDirective(oneWay(value), nextCSSVariable()).createCSS(add);
1141
- }
1106
+ let DefaultStyleStrategy;
1107
+ function reduceStyles(styles) {
1108
+ return styles
1109
+ .map((x) => x instanceof ElementStyles ? reduceStyles(x.styles) : [x])
1110
+ .reduce((prev, curr) => prev.concat(curr), []);
1111
+ }
1112
+ /**
1113
+ * Represents styles that can be applied to a custom element.
1114
+ * @public
1115
+ */
1116
+ class ElementStyles {
1117
+ /**
1118
+ * Gets the StyleStrategy associated with these element styles.
1119
+ */
1120
+ get strategy() {
1121
+ if (this._strategy === null) {
1122
+ this.withStrategy(DefaultStyleStrategy);
1123
+ }
1124
+ return this._strategy;
1125
+ }
1126
+ /**
1127
+ * Creates an instance of ElementStyles.
1128
+ * @param styles - The styles that will be associated with elements.
1129
+ */
1130
+ constructor(styles) {
1131
+ this.styles = styles;
1132
+ this.targets = new WeakSet();
1133
+ this._strategy = null;
1134
+ this.behaviors = styles
1135
+ .map((x) => x instanceof ElementStyles ? x.behaviors : null)
1136
+ .reduce((prev, curr) => (curr === null ? prev : prev === null ? curr : prev.concat(curr)), null);
1137
+ }
1138
+ /** @internal */
1139
+ addStylesTo(target) {
1140
+ this.strategy.addStylesTo(target);
1141
+ this.targets.add(target);
1142
+ }
1143
+ /** @internal */
1144
+ removeStylesFrom(target) {
1145
+ this.strategy.removeStylesFrom(target);
1146
+ this.targets.delete(target);
1147
+ }
1148
+ /** @internal */
1149
+ isAttachedTo(target) {
1150
+ return this.targets.has(target);
1151
+ }
1152
+ /**
1153
+ * Associates behaviors with this set of styles.
1154
+ * @param behaviors - The behaviors to associate.
1155
+ */
1156
+ withBehaviors(...behaviors) {
1157
+ this.behaviors =
1158
+ this.behaviors === null ? behaviors : this.behaviors.concat(behaviors);
1159
+ return this;
1160
+ }
1161
+ /**
1162
+ * Sets the strategy that handles adding/removing these styles for an element.
1163
+ * @param strategy - The strategy to use.
1164
+ */
1165
+ withStrategy(Strategy) {
1166
+ this._strategy = new Strategy(reduceStyles(this.styles));
1167
+ return this;
1168
+ }
1169
+ /**
1170
+ * Sets the default strategy type to use when creating style strategies.
1171
+ * @param Strategy - The strategy type to construct.
1172
+ */
1173
+ static setDefaultStrategy(Strategy) {
1174
+ DefaultStyleStrategy = Strategy;
1175
+ }
1176
+ /**
1177
+ * Normalizes a set of composable style options.
1178
+ * @param styles - The style options to normalize.
1179
+ * @returns A singular ElementStyles instance or undefined.
1180
+ */
1181
+ static normalize(styles) {
1182
+ return styles === void 0
1183
+ ? void 0
1184
+ : Array.isArray(styles)
1185
+ ? new ElementStyles(styles)
1186
+ : styles instanceof ElementStyles
1187
+ ? styles
1188
+ : new ElementStyles([styles]);
1189
+ }
1190
+ }
1191
+ /**
1192
+ * Indicates whether the DOM supports the adoptedStyleSheets feature.
1193
+ */
1194
+ ElementStyles.supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
1195
+ "replace" in CSSStyleSheet.prototype;
1196
+
1197
+ const marker$1 = `${Math.random().toString(36).substring(2, 8)}`;
1198
+ let varId = 0;
1199
+ const nextCSSVariable = () => `--v${marker$1}${++varId}`;
1200
+ function collectStyles(strings, values) {
1201
+ const styles = [];
1202
+ let cssString = "";
1203
+ const behaviors = [];
1204
+ const add = (behavior) => {
1205
+ behaviors.push(behavior);
1206
+ };
1207
+ for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
1208
+ cssString += strings[i];
1209
+ let value = values[i];
1210
+ if (isFunction(value)) {
1211
+ value = new CSSBindingDirective(oneWay(value), nextCSSVariable()).createCSS(add);
1212
+ }
1142
1213
  else if (value instanceof Binding) {
1143
1214
  value = new CSSBindingDirective(value, nextCSSVariable()).createCSS(add);
1144
1215
  }
@@ -1215,111 +1286,12 @@ css.partial = (strings, ...values) => {
1215
1286
  return new CSSPartial(styles, behaviors);
1216
1287
  };
1217
1288
 
1218
- const bindingStartMarker = /fe-b\$\$start\$\$(\d+)\$\$(.+)\$\$fe-b/;
1219
- const bindingEndMarker = /fe-b\$\$end\$\$(\d+)\$\$(.+)\$\$fe-b/;
1220
- const repeatViewStartMarker = /fe-repeat\$\$start\$\$(\d+)\$\$fe-repeat/;
1221
- const repeatViewEndMarker = /fe-repeat\$\$end\$\$(\d+)\$\$fe-repeat/;
1222
- const elementBoundaryStartMarker = /^(?:.{0,1000})fe-eb\$\$start\$\$(.+?)\$\$fe-eb/;
1223
- const elementBoundaryEndMarker = /fe-eb\$\$end\$\$(.{0,1000})\$\$fe-eb(?:.{0,1000})$/;
1224
- function isComment$1(node) {
1225
- return node && node.nodeType === Node.COMMENT_NODE;
1226
- }
1227
- /**
1228
- * Markup utilities to aid in template hydration.
1229
- * @internal
1230
- */
1231
- const HydrationMarkup = Object.freeze({
1232
- attributeMarkerName: "data-fe-b",
1233
- attributeBindingSeparator: " ",
1234
- contentBindingStartMarker(index, uniqueId) {
1235
- return `fe-b$$start$$${index}$$${uniqueId}$$fe-b`;
1236
- },
1237
- contentBindingEndMarker(index, uniqueId) {
1238
- return `fe-b$$end$$${index}$$${uniqueId}$$fe-b`;
1239
- },
1240
- repeatStartMarker(index) {
1241
- return `fe-repeat$$start$$${index}$$fe-repeat`;
1242
- },
1243
- repeatEndMarker(index) {
1244
- return `fe-repeat$$end$$${index}$$fe-repeat`;
1245
- },
1246
- isContentBindingStartMarker(content) {
1247
- return bindingStartMarker.test(content);
1248
- },
1249
- isContentBindingEndMarker(content) {
1250
- return bindingEndMarker.test(content);
1251
- },
1252
- isRepeatViewStartMarker(content) {
1253
- return repeatViewStartMarker.test(content);
1254
- },
1255
- isRepeatViewEndMarker(content) {
1256
- return repeatViewEndMarker.test(content);
1257
- },
1258
- isElementBoundaryStartMarker(node) {
1259
- return isComment$1(node) && elementBoundaryStartMarker.test(node.data.trim());
1260
- },
1261
- isElementBoundaryEndMarker(node) {
1262
- return isComment$1(node) && elementBoundaryEndMarker.test(node.data);
1263
- },
1264
- /**
1265
- * Returns the indexes of the ViewBehaviorFactories affecting
1266
- * attributes for the element, or null if no factories were found.
1267
- */
1268
- parseAttributeBinding(node) {
1269
- const attr = node.getAttribute(this.attributeMarkerName);
1270
- return attr === null
1271
- ? attr
1272
- : attr.split(this.attributeBindingSeparator).map(i => parseInt(i));
1273
- },
1274
- /**
1275
- * Parses the ViewBehaviorFactory index from string data. Returns
1276
- * the binding index or null if the index cannot be retrieved.
1277
- */
1278
- parseContentBindingStartMarker(content) {
1279
- return parseIndexAndIdMarker(bindingStartMarker, content);
1280
- },
1281
- parseContentBindingEndMarker(content) {
1282
- return parseIndexAndIdMarker(bindingEndMarker, content);
1283
- },
1284
- /**
1285
- * Parses the index of a repeat directive from a content string.
1286
- */
1287
- parseRepeatStartMarker(content) {
1288
- return parseIntMarker(repeatViewStartMarker, content);
1289
- },
1290
- parseRepeatEndMarker(content) {
1291
- return parseIntMarker(repeatViewEndMarker, content);
1292
- },
1293
- /**
1294
- * Parses element Id from element boundary markers
1295
- */
1296
- parseElementBoundaryStartMarker(content) {
1297
- return parseStringMarker(elementBoundaryStartMarker, content.trim());
1298
- },
1299
- parseElementBoundaryEndMarker(content) {
1300
- return parseStringMarker(elementBoundaryEndMarker, content);
1301
- },
1302
- });
1303
- function parseIntMarker(regex, content) {
1304
- const match = regex.exec(content);
1305
- return match === null ? match : parseInt(match[1]);
1306
- }
1307
- function parseStringMarker(regex, content) {
1308
- const match = regex.exec(content);
1309
- return match === null ? match : match[1];
1310
- }
1311
- function parseIndexAndIdMarker(regex, content) {
1312
- const match = regex.exec(content);
1313
- return match === null ? match : [parseInt(match[1]), match[2]];
1314
- }
1315
1289
  /**
1316
- * @internal
1290
+ * A unique per-session random marker string used to create placeholder tokens in HTML.
1291
+ * Bindings embedded in template literals are replaced with interpolation markers
1292
+ * of the form `fast-xxxxxx{id}fast-xxxxxx` so the compiler can later locate them in the
1293
+ * parsed DOM and associate each marker with its ViewBehaviorFactory.
1317
1294
  */
1318
- const Hydratable = Symbol.for("fe-hydration");
1319
- function isHydratable(value) {
1320
- return value[Hydratable] === Hydratable;
1321
- }
1322
-
1323
1295
  const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
1324
1296
  const interpolationStart = `${marker}{`;
1325
1297
  const interpolationEnd = `}${marker}`;
@@ -1371,6 +1343,8 @@ const Parser = Object.freeze({
1371
1343
  * directives or null if no directives are found in the string.
1372
1344
  */
1373
1345
  parse(value, factories) {
1346
+ // Split on the interpolation start marker. If there's only one part,
1347
+ // no placeholders exist and we return null to signal "no directives here."
1374
1348
  const parts = value.split(interpolationStart);
1375
1349
  if (parts.length === 1) {
1376
1350
  return null;
@@ -1424,7 +1398,13 @@ const HTMLDirective = Object.freeze({
1424
1398
  return type;
1425
1399
  },
1426
1400
  /**
1427
- *
1401
+ * Determines the DOM aspect type for a directive based on attribute name prefix.
1402
+ * The prefix convention maps to aspect types as follows:
1403
+ * - No prefix (e.g. "class") → DOMAspect.attribute
1404
+ * - ":" prefix (e.g. ":value") → DOMAspect.property (":classList" → DOMAspect.tokenList)
1405
+ * - "?" prefix (e.g. "?disabled") → DOMAspect.booleanAttribute
1406
+ * - `@` prefix (e.g. `@click`) → DOMAspect.event
1407
+ * - Falsy or absent value → DOMAspect.content (see remarks)
1428
1408
  * @param directive - The directive to assign the aspect to.
1429
1409
  * @param value - The value to base the aspect determination on.
1430
1410
  * @remarks
@@ -1490,21 +1470,294 @@ class StatelessAttachedAttributeDirective {
1490
1470
  }
1491
1471
  makeSerializationNoop(StatelessAttachedAttributeDirective);
1492
1472
 
1493
- class HydrationTargetElementError extends Error {
1494
- constructor(
1473
+ const selectElements = (value) => value.nodeType === 1;
1474
+ /**
1475
+ * Creates a function that can be used to filter a Node array, selecting only elements.
1476
+ * @param selector - An optional selector to restrict the filter to.
1477
+ * @public
1478
+ */
1479
+ const elements = (selector) => selector
1480
+ ? value => value.nodeType === 1 && value.matches(selector)
1481
+ : selectElements;
1482
+ /**
1483
+ * A base class for node observation.
1484
+ * @public
1485
+ * @remarks
1486
+ * Internally used by the SlottedDirective and the ChildrenDirective.
1487
+ */
1488
+ class NodeObservationDirective extends StatelessAttachedAttributeDirective {
1495
1489
  /**
1496
- * The error message
1490
+ * The unique id of the factory.
1497
1491
  */
1498
- message,
1492
+ get id() {
1493
+ return this._id;
1494
+ }
1495
+ set id(value) {
1496
+ this._id = value;
1497
+ this._controllerProperty = `${value}-c`;
1498
+ }
1499
1499
  /**
1500
- * The Compiled View Behavior Factories that belong to the view.
1500
+ * Bind this behavior to the source.
1501
+ * @param source - The source to bind to.
1502
+ * @param context - The execution context that the binding is operating within.
1503
+ * @param targets - The targets that behaviors in a view can attach to.
1501
1504
  */
1502
- factories,
1505
+ bind(controller) {
1506
+ const target = controller.targets[this.targetNodeId];
1507
+ target[this._controllerProperty] = controller;
1508
+ this.updateTarget(controller.source, this.computeNodes(target));
1509
+ this.observe(target);
1510
+ controller.onUnbind(this);
1511
+ }
1503
1512
  /**
1504
- * The node to target factory.
1505
- */
1506
- node) {
1507
- super(message);
1513
+ * Unbinds this behavior from the source.
1514
+ * @param source - The source to unbind from.
1515
+ * @param context - The execution context that the binding is operating within.
1516
+ * @param targets - The targets that behaviors in a view can attach to.
1517
+ */
1518
+ unbind(controller) {
1519
+ const target = controller.targets[this.targetNodeId];
1520
+ this.updateTarget(controller.source, emptyArray);
1521
+ this.disconnect(target);
1522
+ target[this._controllerProperty] = null;
1523
+ }
1524
+ /**
1525
+ * Gets the data source for the target.
1526
+ * @param target - The target to get the source for.
1527
+ * @returns The source.
1528
+ */
1529
+ getSource(target) {
1530
+ return target[this._controllerProperty].source;
1531
+ }
1532
+ /**
1533
+ * Updates the source property with the computed nodes.
1534
+ * @param source - The source object to assign the nodes property to.
1535
+ * @param value - The nodes to assign to the source object property.
1536
+ */
1537
+ updateTarget(source, value) {
1538
+ source[this.options.property] = value;
1539
+ }
1540
+ /**
1541
+ * Computes the set of nodes that should be assigned to the source property.
1542
+ * @param target - The target to compute the nodes for.
1543
+ * @returns The computed nodes.
1544
+ * @remarks
1545
+ * Applies filters if provided.
1546
+ */
1547
+ computeNodes(target) {
1548
+ let nodes = this.getNodes(target);
1549
+ if ("filter" in this.options) {
1550
+ nodes = nodes.filter(this.options.filter);
1551
+ }
1552
+ return nodes;
1553
+ }
1554
+ }
1555
+
1556
+ /**
1557
+ * Regex patterns for parsing hydration markers embedded as HTML comments by the SSR renderer.
1558
+ * Each marker type encodes factory indices so the client can map markers back to ViewBehaviorFactories.
1559
+ *
1560
+ * Content binding markers bracket text/template content:
1561
+ * <!-- fe-b$$start$$<factoryIndex>$$<uniqueId>$$fe-b -->
1562
+ * ...content...
1563
+ * <!-- fe-b$$end$$<factoryIndex>$$<uniqueId>$$fe-b -->
1564
+ *
1565
+ * Repeat markers bracket each repeated item:
1566
+ * <!-- fe-repeat$$start$$<itemIndex>$$fe-repeat -->
1567
+ * <!-- fe-repeat$$end$$<itemIndex>$$fe-repeat -->
1568
+ *
1569
+ * Element boundary markers demarcate nested custom elements so parent walkers can skip them:
1570
+ * <!-- fe-eb$$start$$<elementId>$$fe-eb -->
1571
+ * <!-- fe-eb$$end$$<elementId>$$fe-eb -->
1572
+ */
1573
+ const bindingStartMarker = /fe-b\$\$start\$\$(\d+)\$\$(.+)\$\$fe-b/;
1574
+ const bindingEndMarker = /fe-b\$\$end\$\$(\d+)\$\$(.+)\$\$fe-b/;
1575
+ const repeatViewStartMarker = /fe-repeat\$\$start\$\$(\d+)\$\$fe-repeat/;
1576
+ const repeatViewEndMarker = /fe-repeat\$\$end\$\$(\d+)\$\$fe-repeat/;
1577
+ const elementBoundaryStartMarker = /^(?:.{0,1000})fe-eb\$\$start\$\$(.+?)\$\$fe-eb/;
1578
+ const elementBoundaryEndMarker = /fe-eb\$\$end\$\$(.{0,1000})\$\$fe-eb(?:.{0,1000})$/;
1579
+ function isComment$1(node) {
1580
+ return node && node.nodeType === Node.COMMENT_NODE;
1581
+ }
1582
+ /**
1583
+ * Markup utilities to aid in template hydration.
1584
+ * @internal
1585
+ */
1586
+ const HydrationMarkup = Object.freeze({
1587
+ attributeMarkerName: "data-fe-b",
1588
+ compactAttributeMarkerName: "data-fe-c",
1589
+ attributeBindingSeparator: " ",
1590
+ contentBindingStartMarker(index, uniqueId) {
1591
+ return `fe-b$$start$$${index}$$${uniqueId}$$fe-b`;
1592
+ },
1593
+ contentBindingEndMarker(index, uniqueId) {
1594
+ return `fe-b$$end$$${index}$$${uniqueId}$$fe-b`;
1595
+ },
1596
+ repeatStartMarker(index) {
1597
+ return `fe-repeat$$start$$${index}$$fe-repeat`;
1598
+ },
1599
+ repeatEndMarker(index) {
1600
+ return `fe-repeat$$end$$${index}$$fe-repeat`;
1601
+ },
1602
+ isContentBindingStartMarker(content) {
1603
+ return bindingStartMarker.test(content);
1604
+ },
1605
+ isContentBindingEndMarker(content) {
1606
+ return bindingEndMarker.test(content);
1607
+ },
1608
+ isRepeatViewStartMarker(content) {
1609
+ return repeatViewStartMarker.test(content);
1610
+ },
1611
+ isRepeatViewEndMarker(content) {
1612
+ return repeatViewEndMarker.test(content);
1613
+ },
1614
+ isElementBoundaryStartMarker(node) {
1615
+ return isComment$1(node) && elementBoundaryStartMarker.test(node.data.trim());
1616
+ },
1617
+ isElementBoundaryEndMarker(node) {
1618
+ return isComment$1(node) && elementBoundaryEndMarker.test(node.data);
1619
+ },
1620
+ /**
1621
+ * Returns the indexes of the ViewBehaviorFactories affecting
1622
+ * attributes for the element, or null if no factories were found.
1623
+ *
1624
+ * This method parses the space-separated format: `data-fe-b="0 1 2"`.
1625
+ */
1626
+ parseAttributeBinding(node) {
1627
+ const attr = node.getAttribute(this.attributeMarkerName);
1628
+ return attr === null
1629
+ ? attr
1630
+ : attr.split(this.attributeBindingSeparator).map(i => parseInt(i));
1631
+ },
1632
+ /**
1633
+ * Returns the indexes of the ViewBehaviorFactories affecting
1634
+ * attributes for the element, or null if no factories were found.
1635
+ *
1636
+ * This method parses the enumerated format: `data-fe-b-0`, `data-fe-b-1`, `data-fe-b-2`.
1637
+ * This is an alternative format that uses separate attributes for each binding index.
1638
+ */
1639
+ parseEnumeratedAttributeBinding(node) {
1640
+ const attrs = [];
1641
+ const prefixLength = this.attributeMarkerName.length + 1;
1642
+ const prefix = `${this.attributeMarkerName}-`;
1643
+ for (const attr of node.getAttributeNames()) {
1644
+ if (attr.startsWith(prefix)) {
1645
+ const count = Number(attr.slice(prefixLength));
1646
+ if (!Number.isNaN(count)) {
1647
+ attrs.push(count);
1648
+ }
1649
+ else {
1650
+ throw FAST.error(1601 /* invalidAttributeMarkerName */, {
1651
+ name: attr,
1652
+ expectedFormat: `${prefix}<number>`,
1653
+ });
1654
+ }
1655
+ }
1656
+ }
1657
+ return attrs.length === 0 ? null : attrs;
1658
+ },
1659
+ /**
1660
+ * Returns the indexes of the ViewBehaviorFactories affecting
1661
+ * attributes for the element, or null if no factories were found.
1662
+ *
1663
+ * This method parses the compact format: `data-fe-c-{index}-{count}`.
1664
+ */
1665
+ parseCompactAttributeBinding(node) {
1666
+ const prefix = `${this.compactAttributeMarkerName}-`;
1667
+ const attrName = node.getAttributeNames().find(name => name.startsWith(prefix));
1668
+ if (!attrName) {
1669
+ return null;
1670
+ }
1671
+ const suffix = attrName.slice(prefix.length);
1672
+ const parts = suffix.split("-");
1673
+ const startIndex = parseInt(parts[0], 10);
1674
+ const count = parseInt(parts[1], 10);
1675
+ if (parts.length !== 2 ||
1676
+ Number.isNaN(startIndex) ||
1677
+ Number.isNaN(count) ||
1678
+ startIndex < 0 ||
1679
+ count < 1) {
1680
+ throw FAST.error(1604 /* invalidCompactAttributeMarkerName */, {
1681
+ name: attrName,
1682
+ expectedFormat: `${this.compactAttributeMarkerName}-{index}-{count}`,
1683
+ });
1684
+ }
1685
+ const indexes = [];
1686
+ for (let i = 0; i < count; i++) {
1687
+ indexes.push(startIndex + i);
1688
+ }
1689
+ return indexes;
1690
+ },
1691
+ /**
1692
+ * Parses the ViewBehaviorFactory index from string data. Returns
1693
+ * the binding index or null if the index cannot be retrieved.
1694
+ */
1695
+ parseContentBindingStartMarker(content) {
1696
+ return parseIndexAndIdMarker(bindingStartMarker, content);
1697
+ },
1698
+ parseContentBindingEndMarker(content) {
1699
+ return parseIndexAndIdMarker(bindingEndMarker, content);
1700
+ },
1701
+ /**
1702
+ * Parses the index of a repeat directive from a content string.
1703
+ */
1704
+ parseRepeatStartMarker(content) {
1705
+ return parseIntMarker(repeatViewStartMarker, content);
1706
+ },
1707
+ parseRepeatEndMarker(content) {
1708
+ return parseIntMarker(repeatViewEndMarker, content);
1709
+ },
1710
+ /**
1711
+ * Parses element Id from element boundary markers
1712
+ */
1713
+ parseElementBoundaryStartMarker(content) {
1714
+ return parseStringMarker(elementBoundaryStartMarker, content.trim());
1715
+ },
1716
+ parseElementBoundaryEndMarker(content) {
1717
+ return parseStringMarker(elementBoundaryEndMarker, content);
1718
+ },
1719
+ });
1720
+ function parseIntMarker(regex, content) {
1721
+ const match = regex.exec(content);
1722
+ return match === null ? match : parseInt(match[1]);
1723
+ }
1724
+ function parseStringMarker(regex, content) {
1725
+ const match = regex.exec(content);
1726
+ return match === null ? match : match[1];
1727
+ }
1728
+ function parseIndexAndIdMarker(regex, content) {
1729
+ const match = regex.exec(content);
1730
+ return match === null ? match : [parseInt(match[1]), match[2]];
1731
+ }
1732
+ /**
1733
+ * @internal
1734
+ */
1735
+ const Hydratable = Symbol.for("fe-hydration");
1736
+ /** @beta */
1737
+ function isHydratable(value) {
1738
+ return value[Hydratable] === Hydratable;
1739
+ }
1740
+ /**
1741
+ * The attribute used to defer hydration of an element.
1742
+ * @beta
1743
+ */
1744
+ const deferHydrationAttribute = "defer-hydration";
1745
+
1746
+ class HydrationTargetElementError extends Error {
1747
+ constructor(
1748
+ /**
1749
+ * The error message
1750
+ */
1751
+ message,
1752
+ /**
1753
+ * The Compiled View Behavior Factories that belong to the view.
1754
+ */
1755
+ factories,
1756
+ /**
1757
+ * The node to target factory.
1758
+ */
1759
+ node) {
1760
+ super(message);
1508
1761
  this.factories = factories;
1509
1762
  this.node = node;
1510
1763
  }
@@ -1534,7 +1787,23 @@ function isShadowRoot(node) {
1534
1787
  return node instanceof DocumentFragment && "mode" in node;
1535
1788
  }
1536
1789
  /**
1537
- * Maps {@link CompiledViewBehaviorFactory} ids to the corresponding node targets for the view.
1790
+ * Maps compiled ViewBehaviorFactory IDs to their corresponding DOM nodes in the
1791
+ * server-rendered shadow root. Uses a TreeWalker to scan the existing DOM between
1792
+ * firstNode and lastNode, parsing hydration markers to build the targets map.
1793
+ *
1794
+ * For element nodes: parses `data-fe-b` (or variant) attributes to identify which
1795
+ * factories target each element, then removes the marker attribute.
1796
+ *
1797
+ * For comment nodes: parses content binding markers (`fe-b$$start/end$$`) to find
1798
+ * the DOM range controlled by each content binding. Single text nodes become the
1799
+ * direct target; multi-node ranges are stored in boundaries for structural directives.
1800
+ * Element boundary markers (`fe-eb$$start/end$$`) cause the walker to skip over
1801
+ * nested custom elements that handle their own hydration.
1802
+ *
1803
+ * Host bindings (targetNodeId='h') appear at the start of the factories array but
1804
+ * have no SSR markers — getHydrationIndexOffset() computes how many to skip so that
1805
+ * marker indices align with the correct non-host factories.
1806
+ *
1538
1807
  * @param firstNode - The first node of the view.
1539
1808
  * @param lastNode - The last node of the view.
1540
1809
  * @param factories - The Compiled View Behavior Factories that belong to the view.
@@ -1543,6 +1812,7 @@ function isShadowRoot(node) {
1543
1812
  function buildViewBindingTargets(firstNode, lastNode, factories) {
1544
1813
  const range = createRangeForNodes(firstNode, lastNode);
1545
1814
  const treeRoot = range.commonAncestorContainer;
1815
+ const hydrationIndexOffset = getHydrationIndexOffset(factories);
1546
1816
  const walker = document.createTreeWalker(treeRoot, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_COMMENT + NodeFilter.SHOW_TEXT, {
1547
1817
  acceptNode(node) {
1548
1818
  return range.comparePoint(node, 0) === 0
@@ -1556,11 +1826,11 @@ function buildViewBindingTargets(firstNode, lastNode, factories) {
1556
1826
  while (node !== null) {
1557
1827
  switch (node.nodeType) {
1558
1828
  case Node.ELEMENT_NODE: {
1559
- targetElement(node, factories, targets);
1829
+ targetElement(node, factories, targets, hydrationIndexOffset);
1560
1830
  break;
1561
1831
  }
1562
1832
  case Node.COMMENT_NODE: {
1563
- targetComment(node, walker, factories, targets, boundaries);
1833
+ targetComment(node, walker, factories, targets, boundaries, hydrationIndexOffset);
1564
1834
  break;
1565
1835
  }
1566
1836
  }
@@ -1569,20 +1839,22 @@ function buildViewBindingTargets(firstNode, lastNode, factories) {
1569
1839
  range.detach();
1570
1840
  return { targets, boundaries };
1571
1841
  }
1572
- function targetElement(node, factories, targets) {
1842
+ function targetElement(node, factories, targets, hydrationIndexOffset) {
1843
+ var _a, _b;
1573
1844
  // Check for attributes and map any factories.
1574
- const attrFactoryIds = HydrationMarkup.parseAttributeBinding(node);
1845
+ const attrFactoryIds = (_b = (_a = HydrationMarkup.parseAttributeBinding(node)) !== null && _a !== void 0 ? _a : HydrationMarkup.parseEnumeratedAttributeBinding(node)) !== null && _b !== void 0 ? _b : HydrationMarkup.parseCompactAttributeBinding(node);
1575
1846
  if (attrFactoryIds !== null) {
1576
1847
  for (const id of attrFactoryIds) {
1577
- if (!factories[id]) {
1848
+ const factory = factories[id + hydrationIndexOffset];
1849
+ if (!factory) {
1578
1850
  throw new HydrationTargetElementError(`HydrationView was unable to successfully target factory on ${node.nodeName} inside ${node.getRootNode().host.nodeName}. This likely indicates a template mismatch between SSR rendering and hydration.`, factories, node);
1579
1851
  }
1580
- targetFactory(factories[id], node, targets);
1852
+ targetFactory(factory, node, targets);
1581
1853
  }
1582
1854
  node.removeAttribute(HydrationMarkup.attributeMarkerName);
1583
1855
  }
1584
1856
  }
1585
- function targetComment(node, walker, factories, targets, boundaries) {
1857
+ function targetComment(node, walker, factories, targets, boundaries, hydrationIndexOffset) {
1586
1858
  if (HydrationMarkup.isElementBoundaryStartMarker(node)) {
1587
1859
  skipToElementBoundaryEndMarker(node, walker);
1588
1860
  return;
@@ -1593,7 +1865,7 @@ function targetComment(node, walker, factories, targets, boundaries) {
1593
1865
  return;
1594
1866
  }
1595
1867
  const [index, id] = parsed;
1596
- const factory = factories[index];
1868
+ const factory = factories[index + hydrationIndexOffset];
1597
1869
  const nodes = [];
1598
1870
  let current = walker.nextSibling();
1599
1871
  node.data = "";
@@ -1657,6 +1929,23 @@ function skipToElementBoundaryEndMarker(node, walker) {
1657
1929
  current = walker.nextSibling();
1658
1930
  }
1659
1931
  }
1932
+ /**
1933
+ * Counts how many factories at the start of the array are host bindings (targetNodeId='h').
1934
+ * Host bindings target the custom element itself and are not represented by SSR markers,
1935
+ * so the marker indices must be offset by this count to align with the correct factory.
1936
+ */
1937
+ function getHydrationIndexOffset(factories) {
1938
+ let offset = 0;
1939
+ for (let i = 0, ii = factories.length; i < ii; ++i) {
1940
+ if (factories[i].targetNodeId === "h") {
1941
+ offset++;
1942
+ }
1943
+ else {
1944
+ break;
1945
+ }
1946
+ }
1947
+ return offset;
1948
+ }
1660
1949
  function targetFactory(factory, node, targets) {
1661
1950
  if (factory.targetNodeId === undefined) {
1662
1951
  // Dev error, this shouldn't ever be thrown
@@ -1665,7 +1954,7 @@ function targetFactory(factory, node, targets) {
1665
1954
  targets[factory.targetNodeId] = node;
1666
1955
  }
1667
1956
 
1668
- var _a;
1957
+ var _a$1;
1669
1958
  function removeNodeSequence(firstNode, lastNode) {
1670
1959
  const parent = firstNode.parentNode;
1671
1960
  let current = firstNode;
@@ -1840,6 +2129,17 @@ class HTMLView extends DefaultExecutionContext {
1840
2129
  }
1841
2130
  /**
1842
2131
  * Binds a view's behaviors to its binding source.
2132
+ *
2133
+ * On the first call, this iterates through all compiled factories, calling
2134
+ * createBehavior() on each to produce a ViewBehavior instance (e.g., an
2135
+ * HTMLBindingDirective), and then immediately binds it. This is where event
2136
+ * listeners are registered, expression observers are created, and initial
2137
+ * DOM values are set.
2138
+ *
2139
+ * On subsequent calls with a new source, existing behaviors are re-bound
2140
+ * to the new data source, which re-evaluates all binding expressions and
2141
+ * updates the DOM accordingly.
2142
+ *
1843
2143
  * @param source - The binding source for the view's binding behaviors.
1844
2144
  * @param context - The execution context to run the behaviors within.
1845
2145
  */
@@ -1849,6 +2149,8 @@ class HTMLView extends DefaultExecutionContext {
1849
2149
  }
1850
2150
  let behaviors = this.behaviors;
1851
2151
  if (behaviors === null) {
2152
+ // First bind: create behaviors from factories and bind each one.
2153
+ // The view (this) acts as the ViewController, providing targets and source.
1852
2154
  this.source = source;
1853
2155
  this.context = context;
1854
2156
  this.behaviors = behaviors = new Array(this.factories.length);
@@ -1941,13 +2243,22 @@ class HydrationBindingError extends Error {
1941
2243
  }
1942
2244
  }
1943
2245
  class HydrationView extends DefaultExecutionContext {
2246
+ get hydrationStage() {
2247
+ return this._hydrationStage;
2248
+ }
2249
+ get targets() {
2250
+ return this._targets;
2251
+ }
2252
+ get bindingViewBoundaries() {
2253
+ return this._bindingViewBoundaries;
2254
+ }
1944
2255
  constructor(firstChild, lastChild, sourceTemplate, hostBindingTarget) {
1945
2256
  super();
1946
2257
  this.firstChild = firstChild;
1947
2258
  this.lastChild = lastChild;
1948
2259
  this.sourceTemplate = sourceTemplate;
1949
2260
  this.hostBindingTarget = hostBindingTarget;
1950
- this[_a] = Hydratable;
2261
+ this[_a$1] = Hydratable;
1951
2262
  this.context = this;
1952
2263
  this.source = null;
1953
2264
  this.isBound = false;
@@ -1960,15 +2271,6 @@ class HydrationView extends DefaultExecutionContext {
1960
2271
  this._targets = {};
1961
2272
  this.factories = sourceTemplate.compile().factories;
1962
2273
  }
1963
- get hydrationStage() {
1964
- return this._hydrationStage;
1965
- }
1966
- get targets() {
1967
- return this._targets;
1968
- }
1969
- get bindingViewBoundaries() {
1970
- return this._bindingViewBoundaries;
1971
- }
1972
2274
  /**
1973
2275
  * no-op. Hydrated views are don't need to be moved from a documentFragment
1974
2276
  * to the target node.
@@ -2023,7 +2325,7 @@ class HydrationView extends DefaultExecutionContext {
2023
2325
  fragment.appendChild(end);
2024
2326
  }
2025
2327
  bind(source, context = this) {
2026
- var _b, _c;
2328
+ var _b;
2027
2329
  if (this.hydrationStage !== HydrationStage.hydrated) {
2028
2330
  this._hydrationStage = HydrationStage.hydrating;
2029
2331
  }
@@ -2067,7 +2369,28 @@ class HydrationView extends DefaultExecutionContext {
2067
2369
  if (typeof templateString !== "string") {
2068
2370
  templateString = templateString.innerHTML;
2069
2371
  }
2070
- throw new HydrationBindingError(`HydrationView was unable to successfully target bindings inside "${(_c = ((_b = this.firstChild) === null || _b === void 0 ? void 0 : _b.getRootNode()).host) === null || _c === void 0 ? void 0 : _c.nodeName}".`, factory, createRangeForNodes(this.firstChild, this.lastChild).cloneContents(), templateString);
2372
+ const hostElement = ((_b = this.firstChild) === null || _b === void 0 ? void 0 : _b.getRootNode())
2373
+ .host;
2374
+ const hostName = (hostElement === null || hostElement === void 0 ? void 0 : hostElement.nodeName) || "unknown";
2375
+ const factoryInfo = factory;
2376
+ // Build detailed error message
2377
+ const details = [
2378
+ `HydrationView was unable to successfully target bindings inside "<${hostName.toLowerCase()}>".`,
2379
+ `\nMismatch Details:`,
2380
+ ` - Expected target node ID: "${factory.targetNodeId}"`,
2381
+ ` - Available target IDs: [${Object.keys(this.targets).join(", ") || "none"}]`,
2382
+ ];
2383
+ if (factory.targetTagName) {
2384
+ details.push(` - Expected tag name: "${factory.targetTagName}"`);
2385
+ }
2386
+ if (factoryInfo.sourceAspect) {
2387
+ details.push(` - Source aspect: "${factoryInfo.sourceAspect}"`);
2388
+ }
2389
+ if (factoryInfo.aspectType !== undefined) {
2390
+ details.push(` - Aspect type: ${factoryInfo.aspectType}`);
2391
+ }
2392
+ 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 ? "..." : ""}`);
2393
+ throw new HydrationBindingError(details.join("\n"), factory, createRangeForNodes(this.firstChild, this.lastChild).cloneContents(), templateString);
2071
2394
  }
2072
2395
  }
2073
2396
  }
@@ -2113,12 +2436,20 @@ class HydrationView extends DefaultExecutionContext {
2113
2436
  unbindables.length = 0;
2114
2437
  }
2115
2438
  }
2116
- _a = Hydratable;
2439
+ _a$1 = Hydratable;
2117
2440
  makeSerializationNoop(HydrationView);
2118
2441
 
2119
2442
  function isContentTemplate(value) {
2120
2443
  return value.create !== undefined;
2121
2444
  }
2445
+ /**
2446
+ * Sink function for DOMAspect.content bindings (text content interpolation).
2447
+ * Handles two cases:
2448
+ * - If the value is a ContentTemplate (has a create() method), it composes a child
2449
+ * view into the DOM, managing view lifecycle (create/reuse/remove/bind).
2450
+ * - If the value is a primitive, it sets target.textContent directly, first removing
2451
+ * any previously composed view.
2452
+ */
2122
2453
  function updateContent(target, aspect, value, controller) {
2123
2454
  // If there's no actual value, then this equates to the
2124
2455
  // empty string for the purposes of content bindings.
@@ -2187,6 +2518,12 @@ function updateContent(target, aspect, value, controller) {
2187
2518
  target.textContent = value;
2188
2519
  }
2189
2520
  }
2521
+ /**
2522
+ * Sink function for DOMAspect.tokenList bindings (e.g., :classList).
2523
+ * Uses a versioning scheme to efficiently track which CSS classes were added
2524
+ * in the current update vs. the previous one. Classes from the previous version
2525
+ * that aren't present in the new value are automatically removed.
2526
+ */
2190
2527
  function updateTokenList(target, aspect, value) {
2191
2528
  var _a;
2192
2529
  const lookup = `${this.id}-t`;
@@ -2219,6 +2556,12 @@ function updateTokenList(target, aspect, value) {
2219
2556
  }
2220
2557
  }
2221
2558
  }
2559
+ /**
2560
+ * Maps each DOMAspect type to its corresponding DOM update ("sink") function.
2561
+ * When a binding value changes, the sink function for the binding's aspect type
2562
+ * is called to push the new value into the DOM. Events are handled separately
2563
+ * via addEventListener in bind(), so the event sink is a no-op.
2564
+ */
2222
2565
  const sinkLookup = {
2223
2566
  [DOMAspect.attribute]: DOM.setAttribute,
2224
2567
  [DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
@@ -2228,7 +2571,18 @@ const sinkLookup = {
2228
2571
  [DOMAspect.event]: () => void 0,
2229
2572
  };
2230
2573
  /**
2231
- * A directive that applies bindings.
2574
+ * The central binding directive that bridges data expressions and DOM updates.
2575
+ *
2576
+ * HTMLBindingDirective fulfills three roles simultaneously:
2577
+ * - **HTMLDirective**: Produces placeholder HTML via createHTML() during template authoring.
2578
+ * - **ViewBehaviorFactory**: Creates behaviors (returns itself) during view creation.
2579
+ * - **ViewBehavior / EventListener**: Attaches to a DOM node during bind, manages
2580
+ * expression observers for reactive updates, and handles DOM events directly.
2581
+ *
2582
+ * The aspectType (set by HTMLDirective.assignAspect during template processing)
2583
+ * determines which DOM "sink" function is used to apply values — e.g.,
2584
+ * setAttribute for attributes, addEventListener for events, textContent for content.
2585
+ *
2232
2586
  * @public
2233
2587
  */
2234
2588
  class HTMLBindingDirective {
@@ -2260,14 +2614,25 @@ class HTMLBindingDirective {
2260
2614
  const sink = sinkLookup[this.aspectType];
2261
2615
  const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
2262
2616
  if (!sink) {
2263
- throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
2617
+ throw FAST.error(Message.unsupportedBindingBehavior);
2264
2618
  }
2265
2619
  this.data = `${this.id}-d`;
2266
2620
  this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
2267
2621
  }
2268
2622
  return this;
2269
2623
  }
2270
- /** @internal */
2624
+ /**
2625
+ * Attaches this binding to its target DOM node.
2626
+ * - For events: stores the controller reference on the target element and registers
2627
+ * this directive as the EventListener via addEventListener. The directive's
2628
+ * handleEvent() method will be called when the event fires.
2629
+ * - For content bindings: registers an unbind handler, then falls through to the
2630
+ * default path.
2631
+ * - For all non-event bindings: creates (or reuses) an ExpressionObserver, evaluates
2632
+ * the binding expression, and applies the result to the DOM via the updateTarget
2633
+ * sink function. The observer will call handleChange() on future data changes.
2634
+ * @internal
2635
+ */
2271
2636
  bind(controller) {
2272
2637
  var _a;
2273
2638
  const target = controller.targets[this.targetNodeId];
@@ -2282,7 +2647,7 @@ class HTMLBindingDirective {
2282
2647
  case DOMAspect.content:
2283
2648
  controller.onUnbind(this);
2284
2649
  // intentional fall through
2285
- default:
2650
+ default: {
2286
2651
  const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
2287
2652
  observer.target = target;
2288
2653
  observer.controller = controller;
@@ -2295,6 +2660,7 @@ class HTMLBindingDirective {
2295
2660
  }
2296
2661
  this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2297
2662
  break;
2663
+ }
2298
2664
  }
2299
2665
  }
2300
2666
  /** @internal */
@@ -2306,7 +2672,14 @@ class HTMLBindingDirective {
2306
2672
  view.needsBindOnly = true;
2307
2673
  }
2308
2674
  }
2309
- /** @internal */
2675
+ /**
2676
+ * Implements the EventListener interface. When a DOM event fires on the target
2677
+ * element, this method retrieves the ViewController stored on the element,
2678
+ * sets the event on the ExecutionContext so `c.event` is available to the
2679
+ * binding expression, and evaluates the expression. If the expression returns
2680
+ * anything other than `true`, the event's default action is prevented.
2681
+ * @internal
2682
+ */
2310
2683
  handleEvent(event) {
2311
2684
  const controller = event.currentTarget[this.data];
2312
2685
  if (controller.isBound) {
@@ -2318,15 +2691,38 @@ class HTMLBindingDirective {
2318
2691
  }
2319
2692
  }
2320
2693
  }
2321
- /** @internal */
2322
- handleChange(binding, observer) {
2323
- const target = observer.target;
2324
- const controller = observer.controller;
2694
+ /**
2695
+ * Called by the ExpressionObserver when a tracked dependency changes.
2696
+ * Re-evaluates the binding expression via observer.bind() and pushes
2697
+ * the new value to the DOM through the updateTarget sink function.
2698
+ * This is the reactive update path that keeps the DOM in sync with data.
2699
+ *
2700
+ * Guards against stale notifications: when a view is unbound (e.g., after
2701
+ * a parent `when` directive tears down a child element), coupled-lifetime
2702
+ * observers may still hold active subscriptions. If a property change fires
2703
+ * on the source element while the view is inactive, this guard prevents
2704
+ * the binding expression from evaluating with a null source.
2705
+ * @internal
2706
+ */
2707
+ handleChange(binding, observer) {
2708
+ const controller = observer.controller;
2709
+ // https://github.com/microsoft/fast/issues/7444
2710
+ // This guard will be reconsidered in the next major version.
2711
+ if (!controller.isBound) {
2712
+ return;
2713
+ }
2714
+ const target = observer.target;
2325
2715
  this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
2326
2716
  }
2327
2717
  }
2328
2718
  HTMLDirective.define(HTMLBindingDirective, { aspected: true });
2329
2719
 
2720
+ /**
2721
+ * Builds a hierarchical node ID by appending the child index to the parent's ID.
2722
+ * For example, the third child of root is "r.2", and its first child is "r.2.0".
2723
+ * These IDs are used as property names on the targets prototype so that each
2724
+ * binding's target DOM node can be lazily resolved via a chain of childNodes lookups.
2725
+ */
2330
2726
  const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
2331
2727
  const descriptorCache = {};
2332
2728
  // used to prevent creating lots of objects just to track node and index while compiling
@@ -2336,7 +2732,7 @@ const next = {
2336
2732
  };
2337
2733
  function tryWarn(name) {
2338
2734
  if (!name.startsWith("fast-")) {
2339
- FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
2735
+ FAST.warn(Message.hostBindingWithoutHost, { name });
2340
2736
  }
2341
2737
  }
2342
2738
  const warningHost = new Proxy(document.createElement("div"), {
@@ -2376,6 +2772,13 @@ class CompilationContext {
2376
2772
  this.proto = Object.create(null, this.descriptors);
2377
2773
  return this;
2378
2774
  }
2775
+ /**
2776
+ * Registers a lazy getter on the targets prototype that resolves a DOM node
2777
+ * by navigating from its parent's childNodes at the given index. Getters are
2778
+ * chained: accessing targets["r.0.2"] first resolves targets["r.0"] (which
2779
+ * resolves targets["r"]), then returns childNodes[2]. Results are cached so
2780
+ * each node is resolved at most once per view instance.
2781
+ */
2379
2782
  addTargetDescriptor(parentId, targetId, targetIndex) {
2380
2783
  const descriptors = this.descriptors;
2381
2784
  if (targetId === "r" || // root
@@ -2386,7 +2789,7 @@ class CompilationContext {
2386
2789
  if (!descriptors[parentId]) {
2387
2790
  const index = parentId.lastIndexOf(".");
2388
2791
  const grandparentId = parentId.substring(0, index);
2389
- const childIndex = parseInt(parentId.substring(index + 1));
2792
+ const childIndex = parseInt(parentId.substring(index + 1), 10);
2390
2793
  this.addTargetDescriptor(grandparentId, parentId, childIndex);
2391
2794
  }
2392
2795
  let descriptor = descriptorCache[targetId];
@@ -2401,13 +2804,20 @@ class CompilationContext {
2401
2804
  }
2402
2805
  descriptors[targetId] = descriptor;
2403
2806
  }
2807
+ /**
2808
+ * Creates a new HTMLView by cloning the compiled DocumentFragment and building
2809
+ * a targets object. The targets prototype contains lazy getters that resolve
2810
+ * each binding's target DOM node via childNodes traversal. Accessing every
2811
+ * registered nodeId eagerly triggers the getter chain so all nodes are resolved
2812
+ * before behaviors are bound.
2813
+ */
2404
2814
  createView(hostBindingTarget) {
2405
2815
  const fragment = this.fragment.cloneNode(true);
2406
2816
  const targets = Object.create(this.proto);
2407
- targets.r = fragment;
2408
- targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
2817
+ targets.r = fragment; // root — the cloned DocumentFragment
2818
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost; // host — the custom element
2409
2819
  for (const id of this.nodeIds) {
2410
- targets[id]; // trigger locator
2820
+ Reflect.get(targets, id); // trigger lazy getter to resolve and cache the DOM node
2411
2821
  }
2412
2822
  return new HTMLView(fragment, this.factories, targets);
2413
2823
  }
@@ -2427,7 +2837,6 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
2427
2837
  }
2428
2838
  }
2429
2839
  else {
2430
- /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2431
2840
  result = Compiler.aggregate(parseResult, context.policy);
2432
2841
  }
2433
2842
  if (result !== null) {
@@ -2472,7 +2881,6 @@ function compileChildren(context, parent, parentId) {
2472
2881
  let nodeIndex = 0;
2473
2882
  let childNode = parent.firstChild;
2474
2883
  while (childNode) {
2475
- /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2476
2884
  const result = compileNode(context, parentId, childNode, nodeIndex);
2477
2885
  childNode = result.node;
2478
2886
  nodeIndex = result.index;
@@ -2487,14 +2895,14 @@ function compileNode(context, parentId, node, nodeIndex) {
2487
2895
  break;
2488
2896
  case 3: // text node
2489
2897
  return compileContent(context, node, parentId, nodeId, nodeIndex);
2490
- case 8: // comment
2898
+ case 8: {
2899
+ // comment
2491
2900
  const parts = Parser.parse(node.data, context.directives);
2492
2901
  if (parts !== null) {
2493
- context.addFactory(
2494
- /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
2495
- Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
2902
+ context.addFactory(Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
2496
2903
  }
2497
2904
  break;
2905
+ }
2498
2906
  }
2499
2907
  next.index = nodeIndex + 1;
2500
2908
  next.node = node.nextSibling;
@@ -2502,7 +2910,7 @@ function compileNode(context, parentId, node, nodeIndex) {
2502
2910
  }
2503
2911
  function isMarker(node, directives) {
2504
2912
  return (node &&
2505
- node.nodeType == 8 &&
2913
+ node.nodeType === 8 &&
2506
2914
  Parser.parse(node.data, directives) !== null);
2507
2915
  }
2508
2916
  const templateTag = "TEMPLATE";
@@ -2604,631 +3012,634 @@ const Compiler = {
2604
3012
  },
2605
3013
  };
2606
3014
 
2607
- // Much thanks to LitHTML for working this out!
2608
- const lastAttributeNameRegex =
2609
- /* eslint-disable-next-line no-control-regex, max-len */
2610
- /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
2611
- const noFactories = Object.create(null);
2612
3015
  /**
2613
- * Inlines a template into another template.
3016
+ * The runtime behavior for template references.
2614
3017
  * @public
2615
3018
  */
2616
- class InlineTemplateDirective {
2617
- /**
2618
- * Creates an instance of InlineTemplateDirective.
2619
- * @param template - The template to inline.
2620
- */
2621
- constructor(html, factories = noFactories) {
2622
- this.html = html;
2623
- this.factories = factories;
2624
- }
3019
+ class RefDirective extends StatelessAttachedAttributeDirective {
2625
3020
  /**
2626
- * Creates HTML to be used within a template.
2627
- * @param add - Can be used to add behavior factories to a template.
3021
+ * Bind this behavior.
3022
+ * @param controller - The view controller that manages the lifecycle of this behavior.
2628
3023
  */
2629
- createHTML(add) {
2630
- const factories = this.factories;
2631
- for (const key in factories) {
2632
- add(factories[key]);
2633
- }
2634
- return this.html;
3024
+ bind(controller) {
3025
+ controller.source[this.options] = controller.targets[this.targetNodeId];
2635
3026
  }
2636
3027
  }
3028
+ HTMLDirective.define(RefDirective);
2637
3029
  /**
2638
- * An empty template partial.
3030
+ * A directive that observes the updates a property with a reference to the element.
3031
+ * @param propertyName - The name of the property to assign the reference to.
3032
+ * @public
2639
3033
  */
2640
- InlineTemplateDirective.empty = new InlineTemplateDirective("");
2641
- HTMLDirective.define(InlineTemplateDirective);
2642
- function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
2643
- if (definition.aspected) {
2644
- const match = lastAttributeNameRegex.exec(prevString);
2645
- if (match !== null) {
2646
- HTMLDirective.assignAspect(value, match[2]);
2647
- }
2648
- }
2649
- return value.createHTML(add);
2650
- }
3034
+ const ref = (propertyName) => new RefDirective(propertyName);
3035
+
3036
+ const booleanMode = "boolean";
3037
+ const reflectMode = "reflect";
2651
3038
  /**
2652
- * A template capable of creating HTMLView instances or rendering directly to DOM.
3039
+ * Metadata used to configure a custom attribute's behavior.
2653
3040
  * @public
2654
3041
  */
2655
- class ViewTemplate {
3042
+ const AttributeConfiguration = Object.freeze({
2656
3043
  /**
2657
- * Creates an instance of ViewTemplate.
2658
- * @param html - The html representing what this template will instantiate, including placeholders for directives.
2659
- * @param factories - The directives that will be connected to placeholders in the html.
2660
- * @param policy - The security policy to use when compiling this template.
3044
+ * Locates all attribute configurations associated with a type.
2661
3045
  */
2662
- constructor(html, factories = {}, policy) {
2663
- this.policy = policy;
2664
- this.result = null;
2665
- this.html = html;
2666
- this.factories = factories;
3046
+ locate: createMetadataLocator(),
3047
+ });
3048
+ /**
3049
+ * A {@link ValueConverter} that converts to and from `boolean` values.
3050
+ * @remarks
3051
+ * Used automatically when the `boolean` {@link AttributeMode} is selected.
3052
+ * @public
3053
+ */
3054
+ const booleanConverter = {
3055
+ toView(value) {
3056
+ return value ? "true" : "false";
3057
+ },
3058
+ fromView(value) {
3059
+ return !(value === null ||
3060
+ value === void 0 ||
3061
+ value === "false" ||
3062
+ value === false ||
3063
+ value === 0);
3064
+ },
3065
+ };
3066
+ function toNumber(value) {
3067
+ if (value === null || value === undefined) {
3068
+ return null;
2667
3069
  }
3070
+ const number = value * 1;
3071
+ return isNaN(number) ? null : number;
3072
+ }
3073
+ /**
3074
+ * A {@link ValueConverter} that converts to and from `number` values.
3075
+ * @remarks
3076
+ * This converter allows for nullable numbers, returning `null` if the
3077
+ * input was `null`, `undefined`, or `NaN`.
3078
+ * @public
3079
+ */
3080
+ const nullableNumberConverter = {
3081
+ toView(value) {
3082
+ const output = toNumber(value);
3083
+ return output ? output.toString() : output;
3084
+ },
3085
+ fromView: toNumber,
3086
+ };
3087
+ /**
3088
+ * An implementation of {@link Accessor} that supports reactivity,
3089
+ * change callbacks, attribute reflection, and type conversion for
3090
+ * custom elements.
3091
+ * @public
3092
+ */
3093
+ class AttributeDefinition {
2668
3094
  /**
2669
- * @internal
3095
+ * Creates an instance of AttributeDefinition.
3096
+ * @param Owner - The class constructor that owns this attribute.
3097
+ * @param name - The name of the property associated with the attribute.
3098
+ * @param attribute - The name of the attribute in HTML.
3099
+ * @param mode - The {@link AttributeMode} that describes the behavior of this attribute.
3100
+ * @param converter - A {@link ValueConverter} that integrates with the property getter/setter
3101
+ * to convert values to and from a DOM string.
2670
3102
  */
2671
- compile() {
2672
- if (this.result === null) {
2673
- this.result = Compiler.compile(this.html, this.factories, this.policy);
3103
+ constructor(Owner, name, attribute = name.toLowerCase(), mode = reflectMode, converter) {
3104
+ this.guards = new Set();
3105
+ this.Owner = Owner;
3106
+ this.name = name;
3107
+ this.attribute = attribute;
3108
+ this.mode = mode;
3109
+ this.converter = converter;
3110
+ this.fieldName = `_${name}`;
3111
+ this.callbackName = `${name}Changed`;
3112
+ this.hasCallback = this.callbackName in Owner.prototype;
3113
+ if (mode === booleanMode && converter === void 0) {
3114
+ this.converter = booleanConverter;
2674
3115
  }
2675
- return this.result;
2676
3116
  }
2677
3117
  /**
2678
- * Creates an HTMLView instance based on this template definition.
2679
- * @param hostBindingTarget - The element that host behaviors will be bound to.
3118
+ * Sets the value of the attribute/property on the source element.
3119
+ * @param source - The source element to access.
3120
+ * @param newValue - The value to set the attribute/property to.
2680
3121
  */
2681
- create(hostBindingTarget) {
2682
- return this.compile().createView(hostBindingTarget);
3122
+ setValue(source, newValue) {
3123
+ const oldValue = source[this.fieldName];
3124
+ const converter = this.converter;
3125
+ if (converter !== void 0) {
3126
+ newValue = converter.fromView(newValue);
3127
+ }
3128
+ if (oldValue !== newValue) {
3129
+ source[this.fieldName] = newValue;
3130
+ this.tryReflectToAttribute(source);
3131
+ if (this.hasCallback) {
3132
+ source[this.callbackName](oldValue, newValue);
3133
+ }
3134
+ source.$fastController.notify(this.name);
3135
+ }
2683
3136
  }
2684
3137
  /**
2685
- * Returns a directive that can inline the template.
3138
+ * Gets the value of the attribute/property on the source element.
3139
+ * @param source - The source element to access.
2686
3140
  */
2687
- inline() {
2688
- return new InlineTemplateDirective(isString(this.html) ? this.html : this.html.innerHTML, this.factories);
3141
+ getValue(source) {
3142
+ Observable.track(source, this.name);
3143
+ return source[this.fieldName];
2689
3144
  }
2690
- /**
2691
- * Sets the DOMPolicy for this template.
2692
- * @param policy - The policy to associated with this template.
2693
- * @returns The modified template instance.
2694
- * @remarks
2695
- * The DOMPolicy can only be set once for a template and cannot be
2696
- * set after the template is compiled.
2697
- */
2698
- withPolicy(policy) {
2699
- if (this.result) {
2700
- throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
3145
+ /** @internal */
3146
+ onAttributeChangedCallback(element, value) {
3147
+ if (this.guards.has(element)) {
3148
+ return;
2701
3149
  }
2702
- if (this.policy) {
2703
- throw FAST.error(1207 /* Message.onlySetTemplatePolicyOnce */);
3150
+ this.guards.add(element);
3151
+ this.setValue(element, value);
3152
+ this.guards.delete(element);
3153
+ }
3154
+ tryReflectToAttribute(element) {
3155
+ const mode = this.mode;
3156
+ const guards = this.guards;
3157
+ if (guards.has(element) || mode === "fromView") {
3158
+ return;
2704
3159
  }
2705
- this.policy = policy;
2706
- return this;
3160
+ Updates.enqueue(() => {
3161
+ guards.add(element);
3162
+ const latestValue = element[this.fieldName];
3163
+ switch (mode) {
3164
+ case reflectMode:
3165
+ const converter = this.converter;
3166
+ DOM.setAttribute(element, this.attribute, converter !== void 0 ? converter.toView(latestValue) : latestValue);
3167
+ break;
3168
+ case booleanMode:
3169
+ DOM.setBooleanAttribute(element, this.attribute, latestValue);
3170
+ break;
3171
+ }
3172
+ guards.delete(element);
3173
+ });
2707
3174
  }
2708
3175
  /**
2709
- * Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
2710
- * @param source - The data source to bind the template to.
2711
- * @param host - The Element where the template will be rendered.
2712
- * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
2713
- * host that the template is being attached to.
3176
+ * Collects all attribute definitions associated with the owner.
3177
+ * @param Owner - The class constructor to collect attribute for.
3178
+ * @param attributeLists - Any existing attributes to collect and merge with those associated with the owner.
3179
+ * @internal
2714
3180
  */
2715
- render(source, host, hostBindingTarget) {
2716
- const view = this.create(hostBindingTarget);
2717
- view.bind(source);
2718
- view.appendTo(host);
2719
- return view;
2720
- }
2721
- /**
2722
- * Creates a template based on a set of static strings and dynamic values.
2723
- * @param strings - The static strings to create the template with.
2724
- * @param values - The dynamic values to create the template with.
2725
- * @param policy - The DOMPolicy to associated with the template.
2726
- * @returns A ViewTemplate.
2727
- * @remarks
2728
- * This API should not be used directly under normal circumstances because constructing
2729
- * a template in this way, if not done properly, can open up the application to XSS
2730
- * attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
2731
- * and also be sure to manually sanitize all static strings particularly if they can
2732
- * come from user input.
2733
- */
2734
- static create(strings, values, policy) {
2735
- let html = "";
2736
- const factories = Object.create(null);
2737
- const add = (factory) => {
2738
- var _a;
2739
- const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
2740
- factories[id] = factory;
2741
- return id;
2742
- };
2743
- for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
2744
- const currentString = strings[i];
2745
- let currentValue = values[i];
2746
- let definition;
2747
- html += currentString;
2748
- if (isFunction(currentValue)) {
2749
- currentValue = new HTMLBindingDirective(oneWay(currentValue));
2750
- }
2751
- else if (currentValue instanceof Binding) {
2752
- currentValue = new HTMLBindingDirective(currentValue);
2753
- }
2754
- else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
2755
- const staticValue = currentValue;
2756
- currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
2757
- }
2758
- html += createHTML(currentValue, currentString, add, definition);
2759
- }
2760
- return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
3181
+ static collect(Owner, ...attributeLists) {
3182
+ const attributes = [];
3183
+ attributeLists.push(AttributeConfiguration.locate(Owner));
3184
+ for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
3185
+ const list = attributeLists[i];
3186
+ if (list === void 0) {
3187
+ continue;
3188
+ }
3189
+ for (let j = 0, jj = list.length; j < jj; ++j) {
3190
+ const config = list[j];
3191
+ if (isString(config)) {
3192
+ attributes.push(new AttributeDefinition(Owner, config));
3193
+ }
3194
+ else {
3195
+ attributes.push(new AttributeDefinition(Owner, config.property, config.attribute, config.mode, config.converter));
3196
+ }
3197
+ }
3198
+ }
3199
+ return attributes;
2761
3200
  }
2762
3201
  }
2763
- makeSerializationNoop(ViewTemplate);
2764
- /**
2765
- * Transforms a template literal string into a ViewTemplate.
2766
- * @param strings - The string fragments that are interpolated with the values.
2767
- * @param values - The values that are interpolated with the string fragments.
2768
- * @remarks
2769
- * The html helper supports interpolation of strings, numbers, binding expressions,
2770
- * other template instances, and Directive instances.
2771
- * @public
2772
- */
2773
- const html = ((strings, ...values) => {
2774
- if (Array.isArray(strings) && Array.isArray(strings.raw)) {
2775
- return ViewTemplate.create(strings, values);
3202
+ function attr(configOrTarget, prop) {
3203
+ let config;
3204
+ function decorator($target, $prop) {
3205
+ if (arguments.length > 1) {
3206
+ // Non invocation:
3207
+ // - @attr
3208
+ // Invocation with or w/o opts:
3209
+ // - @attr()
3210
+ // - @attr({...opts})
3211
+ config.property = $prop;
3212
+ }
3213
+ AttributeConfiguration.locate($target.constructor).push(config);
2776
3214
  }
2777
- throw FAST.error(1206 /* Message.directCallToHTMLTagNotAllowed */);
2778
- });
2779
- html.partial = (html) => {
2780
- return new InlineTemplateDirective(html);
2781
- };
2782
-
2783
- /**
2784
- * The runtime behavior for template references.
2785
- * @public
2786
- */
2787
- class RefDirective extends StatelessAttachedAttributeDirective {
2788
- /**
2789
- * Bind this behavior.
2790
- * @param controller - The view controller that manages the lifecycle of this behavior.
2791
- */
2792
- bind(controller) {
2793
- controller.source[this.options] = controller.targets[this.targetNodeId];
3215
+ if (arguments.length > 1) {
3216
+ // Non invocation:
3217
+ // - @attr
3218
+ config = {};
3219
+ decorator(configOrTarget, prop);
3220
+ return;
2794
3221
  }
3222
+ // Invocation with or w/o opts:
3223
+ // - @attr()
3224
+ // - @attr({...opts})
3225
+ config = configOrTarget === void 0 ? {} : configOrTarget;
3226
+ return decorator;
2795
3227
  }
2796
- HTMLDirective.define(RefDirective);
3228
+
3229
+ var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
3230
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3231
+ return new (P || (P = Promise))(function (resolve, reject) {
3232
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
3233
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
3234
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
3235
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
3236
+ });
3237
+ };
3238
+ var _a;
3239
+ const defaultShadowOptions = { mode: "open" };
3240
+ const defaultElementOptions = {};
3241
+ const fastElementBaseTypes = new Set();
2797
3242
  /**
2798
- * A directive that observes the updates a property with a reference to the element.
2799
- * @param propertyName - The name of the property to assign the reference to.
2800
- * @public
3243
+ * The FAST custom element registry
3244
+ * @internal
2801
3245
  */
2802
- const ref = (propertyName) => new RefDirective(propertyName);
2803
-
2804
- const selectElements = (value) => value.nodeType === 1;
3246
+ const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
2805
3247
  /**
2806
- * Creates a function that can be used to filter a Node array, selecting only elements.
2807
- * @param selector - An optional selector to restrict the filter to.
2808
- * @public
3248
+ * Values for the `templateOptions` property.
3249
+ * @alpha
2809
3250
  */
2810
- const elements = (selector) => selector
2811
- ? value => value.nodeType === 1 && value.matches(selector)
2812
- : selectElements;
3251
+ const TemplateOptions = {
3252
+ deferAndHydrate: "defer-and-hydrate",
3253
+ };
2813
3254
  /**
2814
- * A base class for node observation.
3255
+ * Defines metadata for a FASTElement.
2815
3256
  * @public
2816
- * @remarks
2817
- * Internally used by the SlottedDirective and the ChildrenDirective.
2818
3257
  */
2819
- class NodeObservationDirective extends StatelessAttachedAttributeDirective {
3258
+ class FASTElementDefinition {
2820
3259
  /**
2821
- * The unique id of the factory.
3260
+ * Indicates if this element has been defined in at least one registry.
2822
3261
  */
2823
- get id() {
2824
- return this._id;
2825
- }
2826
- set id(value) {
2827
- this._id = value;
2828
- this._controllerProperty = `${value}-c`;
3262
+ get isDefined() {
3263
+ return this.platformDefined;
2829
3264
  }
2830
- /**
2831
- * Bind this behavior to the source.
2832
- * @param source - The source to bind to.
2833
- * @param context - The execution context that the binding is operating within.
2834
- * @param targets - The targets that behaviors in a view can attach to.
2835
- */
2836
- bind(controller) {
2837
- const target = controller.targets[this.targetNodeId];
2838
- target[this._controllerProperty] = controller;
2839
- this.updateTarget(controller.source, this.computeNodes(target));
2840
- this.observe(target);
2841
- controller.onUnbind(this);
3265
+ constructor(type, nameOrConfig = type.definition) {
3266
+ var _b;
3267
+ this.platformDefined = false;
3268
+ if (isString(nameOrConfig)) {
3269
+ nameOrConfig = { name: nameOrConfig };
3270
+ }
3271
+ this.type = type;
3272
+ this.name = nameOrConfig.name;
3273
+ this.template = nameOrConfig.template;
3274
+ this.templateOptions = nameOrConfig.templateOptions;
3275
+ this.registry = (_b = nameOrConfig.registry) !== null && _b !== void 0 ? _b : customElements;
3276
+ const proto = type.prototype;
3277
+ const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3278
+ const observedAttributes = new Array(attributes.length);
3279
+ const propertyLookup = {};
3280
+ const attributeLookup = {};
3281
+ for (let i = 0, ii = attributes.length; i < ii; ++i) {
3282
+ const current = attributes[i];
3283
+ observedAttributes[i] = current.attribute;
3284
+ propertyLookup[current.name] = current;
3285
+ attributeLookup[current.attribute] = current;
3286
+ Observable.defineProperty(proto, current);
3287
+ }
3288
+ Reflect.defineProperty(type, "observedAttributes", {
3289
+ value: observedAttributes,
3290
+ enumerable: true,
3291
+ });
3292
+ this.attributes = attributes;
3293
+ this.propertyLookup = propertyLookup;
3294
+ this.attributeLookup = attributeLookup;
3295
+ this.shadowOptions =
3296
+ nameOrConfig.shadowOptions === void 0
3297
+ ? defaultShadowOptions
3298
+ : nameOrConfig.shadowOptions === null
3299
+ ? void 0
3300
+ : Object.assign(Object.assign({}, defaultShadowOptions), nameOrConfig.shadowOptions);
3301
+ this.elementOptions =
3302
+ nameOrConfig.elementOptions === void 0
3303
+ ? defaultElementOptions
3304
+ : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
3305
+ this.styles = ElementStyles.normalize(nameOrConfig.styles);
3306
+ fastElementRegistry.register(this);
3307
+ Observable.defineProperty(_a.isRegistered, this.name);
3308
+ _a.isRegistered[this.name] = this.type;
2842
3309
  }
2843
3310
  /**
2844
- * Unbinds this behavior from the source.
2845
- * @param source - The source to unbind from.
2846
- * @param context - The execution context that the binding is operating within.
2847
- * @param targets - The targets that behaviors in a view can attach to.
3311
+ * Defines a custom element based on this definition.
3312
+ * @param registry - The element registry to define the element in.
3313
+ * @remarks
3314
+ * This operation is idempotent per registry.
2848
3315
  */
2849
- unbind(controller) {
2850
- const target = controller.targets[this.targetNodeId];
2851
- this.updateTarget(controller.source, emptyArray);
2852
- this.disconnect(target);
2853
- target[this._controllerProperty] = null;
3316
+ define(registry = this.registry) {
3317
+ var _b, _c;
3318
+ const type = this.type;
3319
+ if (!registry.get(this.name)) {
3320
+ this.platformDefined = true;
3321
+ registry.define(this.name, type, this.elementOptions);
3322
+ (_c = (_b = this.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementDidDefine) === null || _c === void 0 ? void 0 : _c.call(_b, this.name);
3323
+ }
3324
+ return this;
2854
3325
  }
2855
3326
  /**
2856
- * Gets the data source for the target.
2857
- * @param target - The target to get the source for.
2858
- * @returns The source.
3327
+ * Creates an instance of FASTElementDefinition.
3328
+ * @param type - The type this definition is being created for.
3329
+ * @param nameOrDef - The name of the element to define or a config object
3330
+ * that describes the element to define.
2859
3331
  */
2860
- getSource(target) {
2861
- return target[this._controllerProperty].source;
3332
+ static compose(type, nameOrDef) {
3333
+ if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
3334
+ return new _a(class extends type {
3335
+ }, nameOrDef);
3336
+ }
3337
+ return new _a(type, nameOrDef);
2862
3338
  }
2863
3339
  /**
2864
- * Updates the source property with the computed nodes.
2865
- * @param source - The source object to assign the nodes property to.
2866
- * @param value - The nodes to assign to the source object property.
3340
+ * Registers a FASTElement base type.
3341
+ * @param type - The type to register as a base type.
3342
+ * @internal
2867
3343
  */
2868
- updateTarget(source, value) {
2869
- source[this.options.property] = value;
3344
+ static registerBaseType(type) {
3345
+ fastElementBaseTypes.add(type);
2870
3346
  }
2871
3347
  /**
2872
- * Computes the set of nodes that should be assigned to the source property.
2873
- * @param target - The target to compute the nodes for.
2874
- * @returns The computed nodes.
2875
- * @remarks
2876
- * Applies filters if provided.
3348
+ * Creates an instance of FASTElementDefinition asynchronously. This option assumes
3349
+ * that a template and shadowOptions will be provided and completes when those requirements
3350
+ * are met.
3351
+ * @param type - The type this definition is being created for.
3352
+ * @param nameOrDef - The name of the element to define or a config object
3353
+ * that describes the element to define.
3354
+ * @alpha
2877
3355
  */
2878
- computeNodes(target) {
2879
- let nodes = this.getNodes(target);
2880
- if ("filter" in this.options) {
2881
- nodes = nodes.filter(this.options.filter);
2882
- }
2883
- return nodes;
3356
+ static composeAsync(type, nameOrDef) {
3357
+ return new Promise(resolve => {
3358
+ if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
3359
+ resolve(new _a(class extends type {
3360
+ }, nameOrDef));
3361
+ }
3362
+ const definition = new _a(type, nameOrDef);
3363
+ Observable.getNotifier(definition).subscribe({
3364
+ handleChange: () => {
3365
+ var _b, _c;
3366
+ (_c = (_b = definition.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.templateDidUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, definition.name);
3367
+ resolve(definition);
3368
+ },
3369
+ }, "template");
3370
+ });
2884
3371
  }
2885
3372
  }
2886
-
2887
- const slotEvent = "slotchange";
3373
+ _a = FASTElementDefinition;
2888
3374
  /**
2889
- * The runtime behavior for slotted node observation.
2890
- * @public
3375
+ * The definition has been registered to the FAST element registry.
2891
3376
  */
2892
- class SlottedDirective extends NodeObservationDirective {
2893
- /**
2894
- * Begins observation of the nodes.
2895
- * @param target - The target to observe.
2896
- */
2897
- observe(target) {
2898
- target.addEventListener(slotEvent, this);
2899
- }
2900
- /**
2901
- * Disconnects observation of the nodes.
2902
- * @param target - The target to unobserve.
2903
- */
2904
- disconnect(target) {
2905
- target.removeEventListener(slotEvent, this);
2906
- }
2907
- /**
2908
- * Retrieves the raw nodes that should be assigned to the source property.
2909
- * @param target - The target to get the node to.
2910
- */
2911
- getNodes(target) {
2912
- return target.assignedNodes(this.options);
2913
- }
2914
- /** @internal */
2915
- handleEvent(event) {
2916
- const target = event.currentTarget;
2917
- this.updateTarget(this.getSource(target), this.computeNodes(target));
2918
- }
2919
- }
2920
- HTMLDirective.define(SlottedDirective);
3377
+ FASTElementDefinition.isRegistered = {};
2921
3378
  /**
2922
- * A directive that observes the `assignedNodes()` of a slot and updates a property
2923
- * whenever they change.
2924
- * @param propertyOrOptions - The options used to configure slotted node observation.
2925
- * @public
3379
+ * Gets the element definition associated with the specified type.
3380
+ * @param type - The custom element type to retrieve the definition for.
2926
3381
  */
2927
- function slotted(propertyOrOptions) {
2928
- if (isString(propertyOrOptions)) {
2929
- propertyOrOptions = { property: propertyOrOptions };
2930
- }
2931
- return new SlottedDirective(propertyOrOptions);
2932
- }
2933
-
2934
- const booleanMode = "boolean";
2935
- const reflectMode = "reflect";
3382
+ FASTElementDefinition.getByType = fastElementRegistry.getByType;
2936
3383
  /**
2937
- * Metadata used to configure a custom attribute's behavior.
2938
- * @public
3384
+ * Gets the element definition associated with the instance.
3385
+ * @param instance - The custom element instance to retrieve the definition for.
2939
3386
  */
2940
- const AttributeConfiguration = Object.freeze({
2941
- /**
2942
- * Locates all attribute configurations associated with a type.
2943
- */
2944
- locate: createMetadataLocator(),
3387
+ FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
3388
+ /**
3389
+ * Indicates when a custom elements definition has been registered with the fastElementRegistry.
3390
+ * @param name - The name of the defined custom element.
3391
+ * @alpha
3392
+ */
3393
+ FASTElementDefinition.registerAsync = (name) => __awaiter(void 0, void 0, void 0, function* () {
3394
+ return new Promise(resolve => {
3395
+ if (_a.isRegistered[name]) {
3396
+ resolve(_a.isRegistered[name]);
3397
+ }
3398
+ Observable.getNotifier(_a.isRegistered).subscribe({ handleChange: () => resolve(_a.isRegistered[name]) }, name);
3399
+ });
2945
3400
  });
3401
+ Observable.defineProperty(FASTElementDefinition.prototype, "template");
3402
+
3403
+ // Much thanks to LitHTML for working this out!
3404
+ const lastAttributeNameRegex =
3405
+ /* eslint-disable-next-line no-control-regex, max-len */
3406
+ /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
3407
+ const noFactories = Object.create(null);
2946
3408
  /**
2947
- * A {@link ValueConverter} that converts to and from `boolean` values.
2948
- * @remarks
2949
- * Used automatically when the `boolean` {@link AttributeMode} is selected.
3409
+ * Inlines a template into another template.
2950
3410
  * @public
2951
3411
  */
2952
- const booleanConverter = {
2953
- toView(value) {
2954
- return value ? "true" : "false";
2955
- },
2956
- fromView(value) {
2957
- return value === null ||
2958
- value === void 0 ||
2959
- value === "false" ||
2960
- value === false ||
2961
- value === 0
2962
- ? false
2963
- : true;
2964
- },
2965
- };
2966
- function toNumber(value) {
2967
- if (value === null || value === undefined) {
2968
- return null;
3412
+ class InlineTemplateDirective {
3413
+ /**
3414
+ * Creates an instance of InlineTemplateDirective.
3415
+ * @param template - The template to inline.
3416
+ */
3417
+ constructor(html, factories = noFactories) {
3418
+ this.html = html;
3419
+ this.factories = factories;
3420
+ }
3421
+ /**
3422
+ * Creates HTML to be used within a template.
3423
+ * @param add - Can be used to add behavior factories to a template.
3424
+ */
3425
+ createHTML(add) {
3426
+ const factories = this.factories;
3427
+ for (const key in factories) {
3428
+ add(factories[key]);
3429
+ }
3430
+ return this.html;
2969
3431
  }
2970
- const number = value * 1;
2971
- return isNaN(number) ? null : number;
2972
3432
  }
2973
3433
  /**
2974
- * A {@link ValueConverter} that converts to and from `number` values.
2975
- * @remarks
2976
- * This converter allows for nullable numbers, returning `null` if the
2977
- * input was `null`, `undefined`, or `NaN`.
2978
- * @public
3434
+ * An empty template partial.
2979
3435
  */
2980
- const nullableNumberConverter = {
2981
- toView(value) {
2982
- const output = toNumber(value);
2983
- return output ? output.toString() : output;
2984
- },
2985
- fromView: toNumber,
2986
- };
3436
+ InlineTemplateDirective.empty = new InlineTemplateDirective("");
3437
+ HTMLDirective.define(InlineTemplateDirective);
3438
+ function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
3439
+ if (definition.aspected) {
3440
+ const match = lastAttributeNameRegex.exec(prevString);
3441
+ if (match !== null) {
3442
+ HTMLDirective.assignAspect(value, match[2]);
3443
+ }
3444
+ }
3445
+ return value.createHTML(add);
3446
+ }
2987
3447
  /**
2988
- * An implementation of {@link Accessor} that supports reactivity,
2989
- * change callbacks, attribute reflection, and type conversion for
2990
- * custom elements.
3448
+ * A template capable of creating HTMLView instances or rendering directly to DOM.
2991
3449
  * @public
2992
3450
  */
2993
- class AttributeDefinition {
3451
+ class ViewTemplate {
2994
3452
  /**
2995
- * Creates an instance of AttributeDefinition.
2996
- * @param Owner - The class constructor that owns this attribute.
2997
- * @param name - The name of the property associated with the attribute.
2998
- * @param attribute - The name of the attribute in HTML.
2999
- * @param mode - The {@link AttributeMode} that describes the behavior of this attribute.
3000
- * @param converter - A {@link ValueConverter} that integrates with the property getter/setter
3001
- * to convert values to and from a DOM string.
3453
+ * Creates an instance of ViewTemplate.
3454
+ * @param html - The html representing what this template will instantiate, including placeholders for directives.
3455
+ * @param factories - The directives that will be connected to placeholders in the html.
3456
+ * @param policy - The security policy to use when compiling this template.
3002
3457
  */
3003
- constructor(Owner, name, attribute = name.toLowerCase(), mode = reflectMode, converter) {
3004
- this.guards = new Set();
3005
- this.Owner = Owner;
3006
- this.name = name;
3007
- this.attribute = attribute;
3008
- this.mode = mode;
3009
- this.converter = converter;
3010
- this.fieldName = `_${name}`;
3011
- this.callbackName = `${name}Changed`;
3012
- this.hasCallback = this.callbackName in Owner.prototype;
3013
- if (mode === booleanMode && converter === void 0) {
3014
- this.converter = booleanConverter;
3015
- }
3458
+ constructor(html, factories = {}, policy) {
3459
+ this.policy = policy;
3460
+ this.result = null;
3461
+ this.html = html;
3462
+ this.factories = factories;
3016
3463
  }
3017
3464
  /**
3018
- * Sets the value of the attribute/property on the source element.
3019
- * @param source - The source element to access.
3020
- * @param value - The value to set the attribute/property to.
3465
+ * @internal
3021
3466
  */
3022
- setValue(source, newValue) {
3023
- const oldValue = source[this.fieldName];
3024
- const converter = this.converter;
3025
- if (converter !== void 0) {
3026
- newValue = converter.fromView(newValue);
3027
- }
3028
- if (oldValue !== newValue) {
3029
- source[this.fieldName] = newValue;
3030
- this.tryReflectToAttribute(source);
3031
- if (this.hasCallback) {
3032
- source[this.callbackName](oldValue, newValue);
3033
- }
3034
- source.$fastController.notify(this.name);
3467
+ compile() {
3468
+ if (this.result === null) {
3469
+ this.result = Compiler.compile(this.html, this.factories, this.policy);
3035
3470
  }
3471
+ return this.result;
3036
3472
  }
3037
3473
  /**
3038
- * Gets the value of the attribute/property on the source element.
3039
- * @param source - The source element to access.
3474
+ * Creates an HTMLView instance based on this template definition.
3475
+ * @param hostBindingTarget - The element that host behaviors will be bound to.
3040
3476
  */
3041
- getValue(source) {
3042
- Observable.track(source, this.name);
3043
- return source[this.fieldName];
3477
+ create(hostBindingTarget) {
3478
+ return this.compile().createView(hostBindingTarget);
3044
3479
  }
3045
- /** @internal */
3046
- onAttributeChangedCallback(element, value) {
3047
- if (this.guards.has(element)) {
3048
- return;
3049
- }
3050
- this.guards.add(element);
3051
- this.setValue(element, value);
3052
- this.guards.delete(element);
3480
+ /**
3481
+ * Returns a directive that can inline the template.
3482
+ */
3483
+ inline() {
3484
+ return new InlineTemplateDirective(isString(this.html) ? this.html : this.html.innerHTML, this.factories);
3053
3485
  }
3054
- tryReflectToAttribute(element) {
3055
- const mode = this.mode;
3056
- const guards = this.guards;
3057
- if (guards.has(element) || mode === "fromView") {
3058
- return;
3486
+ /**
3487
+ * Sets the DOMPolicy for this template.
3488
+ * @param policy - The policy to associated with this template.
3489
+ * @returns The modified template instance.
3490
+ * @remarks
3491
+ * The DOMPolicy can only be set once for a template and cannot be
3492
+ * set after the template is compiled.
3493
+ */
3494
+ withPolicy(policy) {
3495
+ if (this.result) {
3496
+ throw FAST.error(Message.cannotSetTemplatePolicyAfterCompilation);
3059
3497
  }
3060
- Updates.enqueue(() => {
3061
- guards.add(element);
3062
- const latestValue = element[this.fieldName];
3063
- switch (mode) {
3064
- case reflectMode:
3065
- const converter = this.converter;
3066
- DOM.setAttribute(element, this.attribute, converter !== void 0 ? converter.toView(latestValue) : latestValue);
3067
- break;
3068
- case booleanMode:
3069
- DOM.setBooleanAttribute(element, this.attribute, latestValue);
3070
- break;
3071
- }
3072
- guards.delete(element);
3073
- });
3498
+ if (this.policy) {
3499
+ throw FAST.error(Message.onlySetTemplatePolicyOnce);
3500
+ }
3501
+ this.policy = policy;
3502
+ return this;
3074
3503
  }
3075
3504
  /**
3076
- * Collects all attribute definitions associated with the owner.
3077
- * @param Owner - The class constructor to collect attribute for.
3078
- * @param attributeLists - Any existing attributes to collect and merge with those associated with the owner.
3079
- * @internal
3505
+ * Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
3506
+ * @param source - The data source to bind the template to.
3507
+ * @param host - The Element where the template will be rendered.
3508
+ * @param hostBindingTarget - An HTML element to target the host bindings at if different from the
3509
+ * host that the template is being attached to.
3080
3510
  */
3081
- static collect(Owner, ...attributeLists) {
3082
- const attributes = [];
3083
- attributeLists.push(AttributeConfiguration.locate(Owner));
3084
- for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
3085
- const list = attributeLists[i];
3086
- if (list === void 0) {
3087
- continue;
3511
+ render(source, host, hostBindingTarget) {
3512
+ const view = this.create(hostBindingTarget);
3513
+ view.bind(source);
3514
+ view.appendTo(host);
3515
+ return view;
3516
+ }
3517
+ /**
3518
+ * Processes the tagged template literal's static strings and interpolated values and
3519
+ * creates a ViewTemplate.
3520
+ *
3521
+ * For each interpolated value:
3522
+ * 1. Functions (binding expressions, e.g., `x => x.name`) → wrapped in a one-way HTMLBindingDirective
3523
+ * 2. Binding instances → wrapped in an HTMLBindingDirective
3524
+ * 3. HTMLDirective instances → used as-is
3525
+ * 4. Static values (strings, numbers) → wrapped in a one-time HTMLBindingDirective
3526
+ *
3527
+ * Each directive's createHTML() is called with an `add` callback that registers
3528
+ * the factory in the factories record under a unique ID and returns that ID.
3529
+ * The directive inserts a placeholder marker (e.g., `fast-abc123{0}fast-abc123`) into
3530
+ * the HTML string so the compiler can later find and associate it with the factory.
3531
+ *
3532
+ * Aspect detection happens here too: the `lastAttributeNameRegex` checks whether
3533
+ * the placeholder appears inside an attribute value, and if so, assignAspect()
3534
+ * sets the correct DOMAspect (attribute, property, event, etc.) based on the
3535
+ * attribute name prefix.
3536
+ *
3537
+ * @param strings - The static strings to create the template with.
3538
+ * @param values - The dynamic values to create the template with.
3539
+ * @param policy - The DOMPolicy to associated with the template.
3540
+ * @returns A ViewTemplate.
3541
+ * @remarks
3542
+ * This API should not be used directly under normal circumstances because constructing
3543
+ * a template in this way, if not done properly, can open up the application to XSS
3544
+ * attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
3545
+ * and also be sure to manually sanitize all static strings particularly if they can
3546
+ * come from user input.
3547
+ */
3548
+ static create(strings, values, policy) {
3549
+ let html = "";
3550
+ const factories = Object.create(null);
3551
+ const add = (factory) => {
3552
+ var _a;
3553
+ const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
3554
+ factories[id] = factory;
3555
+ return id;
3556
+ };
3557
+ for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
3558
+ const currentString = strings[i];
3559
+ let currentValue = values[i];
3560
+ let definition;
3561
+ html += currentString;
3562
+ if (isFunction(currentValue)) {
3563
+ currentValue = new HTMLBindingDirective(oneWay(currentValue));
3088
3564
  }
3089
- for (let j = 0, jj = list.length; j < jj; ++j) {
3090
- const config = list[j];
3091
- if (isString(config)) {
3092
- attributes.push(new AttributeDefinition(Owner, config));
3093
- }
3094
- else {
3095
- attributes.push(new AttributeDefinition(Owner, config.property, config.attribute, config.mode, config.converter));
3096
- }
3565
+ else if (currentValue instanceof Binding) {
3566
+ currentValue = new HTMLBindingDirective(currentValue);
3097
3567
  }
3098
- }
3099
- return attributes;
3100
- }
3101
- }
3102
- function attr(configOrTarget, prop) {
3103
- let config;
3104
- function decorator($target, $prop) {
3105
- if (arguments.length > 1) {
3106
- // Non invocation:
3107
- // - @attr
3108
- // Invocation with or w/o opts:
3109
- // - @attr()
3110
- // - @attr({...opts})
3111
- config.property = $prop;
3112
- }
3113
- AttributeConfiguration.locate($target.constructor).push(config);
3114
- }
3115
- if (arguments.length > 1) {
3116
- // Non invocation:
3117
- // - @attr
3118
- config = {};
3119
- decorator(configOrTarget, prop);
3120
- return;
3568
+ else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
3569
+ const staticValue = currentValue;
3570
+ currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
3571
+ }
3572
+ html += createHTML(currentValue, currentString, add, definition);
3573
+ }
3574
+ return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
3121
3575
  }
3122
- // Invocation with or w/o opts:
3123
- // - @attr()
3124
- // - @attr({...opts})
3125
- config = configOrTarget === void 0 ? {} : configOrTarget;
3126
- return decorator;
3127
3576
  }
3128
-
3129
- const defaultShadowOptions = { mode: "open" };
3130
- const defaultElementOptions = {};
3131
- const fastElementBaseTypes = new Set();
3132
- const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
3577
+ makeSerializationNoop(ViewTemplate);
3133
3578
  /**
3134
- * Defines metadata for a FASTElement.
3579
+ * Transforms a template literal string into a ViewTemplate.
3580
+ * @param strings - The string fragments that are interpolated with the values.
3581
+ * @param values - The values that are interpolated with the string fragments.
3582
+ * @remarks
3583
+ * The html helper supports interpolation of strings, numbers, binding expressions,
3584
+ * other template instances, and Directive instances.
3135
3585
  * @public
3136
3586
  */
3137
- class FASTElementDefinition {
3138
- constructor(type, nameOrConfig = type.definition) {
3139
- var _a;
3140
- this.platformDefined = false;
3141
- if (isString(nameOrConfig)) {
3142
- nameOrConfig = { name: nameOrConfig };
3143
- }
3144
- this.type = type;
3145
- this.name = nameOrConfig.name;
3146
- this.template = nameOrConfig.template;
3147
- this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
3148
- const proto = type.prototype;
3149
- const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
3150
- const observedAttributes = new Array(attributes.length);
3151
- const propertyLookup = {};
3152
- const attributeLookup = {};
3153
- for (let i = 0, ii = attributes.length; i < ii; ++i) {
3154
- const current = attributes[i];
3155
- observedAttributes[i] = current.attribute;
3156
- propertyLookup[current.name] = current;
3157
- attributeLookup[current.attribute] = current;
3158
- Observable.defineProperty(proto, current);
3159
- }
3160
- Reflect.defineProperty(type, "observedAttributes", {
3161
- value: observedAttributes,
3162
- enumerable: true,
3163
- });
3164
- this.attributes = attributes;
3165
- this.propertyLookup = propertyLookup;
3166
- this.attributeLookup = attributeLookup;
3167
- this.shadowOptions =
3168
- nameOrConfig.shadowOptions === void 0
3169
- ? defaultShadowOptions
3170
- : nameOrConfig.shadowOptions === null
3171
- ? void 0
3172
- : Object.assign(Object.assign({}, defaultShadowOptions), nameOrConfig.shadowOptions);
3173
- this.elementOptions =
3174
- nameOrConfig.elementOptions === void 0
3175
- ? defaultElementOptions
3176
- : Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
3177
- this.styles = ElementStyles.normalize(nameOrConfig.styles);
3178
- fastElementRegistry.register(this);
3587
+ const html = ((strings, ...values) => {
3588
+ if (Array.isArray(strings) && Array.isArray(strings.raw)) {
3589
+ return ViewTemplate.create(strings, values);
3179
3590
  }
3591
+ throw FAST.error(Message.directCallToHTMLTagNotAllowed);
3592
+ });
3593
+ html.partial = (html) => {
3594
+ return new InlineTemplateDirective(html);
3595
+ };
3596
+
3597
+ const slotEvent = "slotchange";
3598
+ /**
3599
+ * The runtime behavior for slotted node observation.
3600
+ * @public
3601
+ */
3602
+ class SlottedDirective extends NodeObservationDirective {
3180
3603
  /**
3181
- * Indicates if this element has been defined in at least one registry.
3604
+ * Begins observation of the nodes.
3605
+ * @param target - The target to observe.
3182
3606
  */
3183
- get isDefined() {
3184
- return this.platformDefined;
3607
+ observe(target) {
3608
+ target.addEventListener(slotEvent, this);
3185
3609
  }
3186
3610
  /**
3187
- * Defines a custom element based on this definition.
3188
- * @param registry - The element registry to define the element in.
3189
- * @remarks
3190
- * This operation is idempotent per registry.
3611
+ * Disconnects observation of the nodes.
3612
+ * @param target - The target to unobserve.
3191
3613
  */
3192
- define(registry = this.registry) {
3193
- const type = this.type;
3194
- if (!registry.get(this.name)) {
3195
- this.platformDefined = true;
3196
- registry.define(this.name, type, this.elementOptions);
3197
- }
3198
- return this;
3614
+ disconnect(target) {
3615
+ target.removeEventListener(slotEvent, this);
3199
3616
  }
3200
3617
  /**
3201
- * Creates an instance of FASTElementDefinition.
3202
- * @param type - The type this definition is being created for.
3203
- * @param nameOrDef - The name of the element to define or a config object
3204
- * that describes the element to define.
3618
+ * Retrieves the raw nodes that should be assigned to the source property.
3619
+ * @param target - The target to get the node to.
3205
3620
  */
3206
- static compose(type, nameOrDef) {
3207
- if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
3208
- return new FASTElementDefinition(class extends type {
3209
- }, nameOrDef);
3210
- }
3211
- return new FASTElementDefinition(type, nameOrDef);
3621
+ getNodes(target) {
3622
+ return target.assignedNodes(this.options);
3212
3623
  }
3213
- /**
3214
- * Registers a FASTElement base type.
3215
- * @param type - The type to register as a base type.
3216
- * @internal
3217
- */
3218
- static registerBaseType(type) {
3219
- fastElementBaseTypes.add(type);
3624
+ /** @internal */
3625
+ handleEvent(event) {
3626
+ const target = event.currentTarget;
3627
+ this.updateTarget(this.getSource(target), this.computeNodes(target));
3220
3628
  }
3221
3629
  }
3630
+ HTMLDirective.define(SlottedDirective);
3222
3631
  /**
3223
- * Gets the element definition associated with the specified type.
3224
- * @param type - The custom element type to retrieve the definition for.
3225
- */
3226
- FASTElementDefinition.getByType = fastElementRegistry.getByType;
3227
- /**
3228
- * Gets the element definition associated with the instance.
3229
- * @param instance - The custom element instance to retrieve the definition for.
3632
+ * A directive that observes the `assignedNodes()` of a slot and updates a property
3633
+ * whenever they change.
3634
+ * @param propertyOrOptions - The options used to configure slotted node observation.
3635
+ * @public
3230
3636
  */
3231
- FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
3637
+ function slotted(propertyOrOptions) {
3638
+ if (isString(propertyOrOptions)) {
3639
+ propertyOrOptions = { property: propertyOrOptions };
3640
+ }
3641
+ return new SlottedDirective(propertyOrOptions);
3642
+ }
3232
3643
 
3233
3644
  /**
3234
3645
  * An extension of MutationObserver that supports unobserving nodes.
@@ -3336,92 +3747,33 @@ function getShadowRoot(element) {
3336
3747
  return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
3337
3748
  }
3338
3749
  let elementControllerStrategy;
3750
+ /**
3751
+ * The various lifecycle stages of an ElementController.
3752
+ * @public
3753
+ */
3754
+ var Stages;
3755
+ (function (Stages) {
3756
+ /** The element is in the process of connecting. */
3757
+ Stages[Stages["connecting"] = 0] = "connecting";
3758
+ /** The element is connected. */
3759
+ Stages[Stages["connected"] = 1] = "connected";
3760
+ /** The element is in the process of disconnecting. */
3761
+ Stages[Stages["disconnecting"] = 2] = "disconnecting";
3762
+ /** The element is disconnected. */
3763
+ Stages[Stages["disconnected"] = 3] = "disconnected";
3764
+ })(Stages || (Stages = {}));
3339
3765
  /**
3340
3766
  * Controls the lifecycle and rendering of a `FASTElement`.
3341
3767
  * @public
3342
3768
  */
3343
3769
  class ElementController extends PropertyChangeNotifier {
3344
- /**
3345
- * Creates a Controller to control the specified element.
3346
- * @param element - The element to be controlled by this controller.
3347
- * @param definition - The element definition metadata that instructs this
3348
- * controller in how to handle rendering and other platform integrations.
3349
- * @internal
3350
- */
3351
- constructor(element, definition) {
3352
- super(element);
3353
- this.boundObservables = null;
3354
- this.needsInitialization = true;
3355
- this.hasExistingShadowRoot = false;
3356
- this._template = null;
3357
- this.stage = 3 /* Stages.disconnected */;
3358
- /**
3359
- * A guard against connecting behaviors multiple times
3360
- * during connect in scenarios where a behavior adds
3361
- * another behavior during it's connectedCallback
3362
- */
3363
- this.guardBehaviorConnection = false;
3364
- this.behaviors = null;
3365
- /**
3366
- * Tracks whether behaviors are connected so that
3367
- * behaviors cant be connected multiple times
3368
- */
3369
- this.behaviorsConnected = false;
3370
- this._mainStyles = null;
3371
- /**
3372
- * This allows Observable.getNotifier(...) to return the Controller
3373
- * when the notifier for the Controller itself is being requested. The
3374
- * result is that the Observable system does not need to create a separate
3375
- * instance of Notifier for observables on the Controller. The component and
3376
- * the controller will now share the same notifier, removing one-object construct
3377
- * per web component instance.
3378
- */
3379
- this.$fastController = this;
3380
- /**
3381
- * The view associated with the custom element.
3382
- * @remarks
3383
- * If `null` then the element is managing its own rendering.
3384
- */
3385
- this.view = null;
3386
- this.source = element;
3387
- this.definition = definition;
3388
- const shadowOptions = definition.shadowOptions;
3389
- if (shadowOptions !== void 0) {
3390
- let shadowRoot = element.shadowRoot;
3391
- if (shadowRoot) {
3392
- this.hasExistingShadowRoot = true;
3393
- }
3394
- else {
3395
- shadowRoot = element.attachShadow(shadowOptions);
3396
- if (shadowOptions.mode === "closed") {
3397
- shadowRoots.set(element, shadowRoot);
3398
- }
3399
- }
3400
- }
3401
- // Capture any observable values that were set by the binding engine before
3402
- // the browser upgraded the element. Then delete the property since it will
3403
- // shadow the getter/setter that is required to make the observable operate.
3404
- // Later, in the connect callback, we'll re-apply the values.
3405
- const accessors = Observable.getAccessors(element);
3406
- if (accessors.length > 0) {
3407
- const boundObservables = (this.boundObservables = Object.create(null));
3408
- for (let i = 0, ii = accessors.length; i < ii; ++i) {
3409
- const propertyName = accessors[i].name;
3410
- const value = element[propertyName];
3411
- if (value !== void 0) {
3412
- delete element[propertyName];
3413
- boundObservables[propertyName] = value;
3414
- }
3415
- }
3416
- }
3417
- }
3418
3770
  /**
3419
3771
  * Indicates whether or not the custom element has been
3420
3772
  * connected to the document.
3421
3773
  */
3422
3774
  get isConnected() {
3423
3775
  Observable.track(this, isConnectedPropertyName);
3424
- return this.stage === 1 /* Stages.connected */;
3776
+ return this.stage === Stages.connected;
3425
3777
  }
3426
3778
  /**
3427
3779
  * The context the expression is evaluated against.
@@ -3474,6 +3826,28 @@ class ElementController extends PropertyChangeNotifier {
3474
3826
  this.renderTemplate(value);
3475
3827
  }
3476
3828
  }
3829
+ /**
3830
+ * The shadow root options for the component.
3831
+ */
3832
+ get shadowOptions() {
3833
+ return this._shadowRootOptions;
3834
+ }
3835
+ set shadowOptions(value) {
3836
+ // options on the shadowRoot can only be set once
3837
+ if (this._shadowRootOptions === void 0 && value !== void 0) {
3838
+ this._shadowRootOptions = value;
3839
+ let shadowRoot = this.source.shadowRoot;
3840
+ if (shadowRoot) {
3841
+ this.hasExistingShadowRoot = true;
3842
+ }
3843
+ else {
3844
+ shadowRoot = this.source.attachShadow(value);
3845
+ if (value.mode === "closed") {
3846
+ shadowRoots.set(this.source, shadowRoot);
3847
+ }
3848
+ }
3849
+ }
3850
+ }
3477
3851
  /**
3478
3852
  * The main set of styles used for the component, independent
3479
3853
  * of any dynamically added styles.
@@ -3492,19 +3866,103 @@ class ElementController extends PropertyChangeNotifier {
3492
3866
  this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
3493
3867
  }
3494
3868
  }
3495
- return this._mainStyles;
3496
- }
3497
- set mainStyles(value) {
3498
- if (this._mainStyles === value) {
3499
- return;
3500
- }
3501
- if (this._mainStyles !== null) {
3502
- this.removeStyles(this._mainStyles);
3503
- }
3504
- this._mainStyles = value;
3505
- if (!this.needsInitialization) {
3506
- this.addStyles(value);
3507
- }
3869
+ return this._mainStyles;
3870
+ }
3871
+ set mainStyles(value) {
3872
+ if (this._mainStyles === value) {
3873
+ return;
3874
+ }
3875
+ if (this._mainStyles !== null) {
3876
+ this.removeStyles(this._mainStyles);
3877
+ }
3878
+ this._mainStyles = value;
3879
+ if (!this.needsInitialization) {
3880
+ this.addStyles(value);
3881
+ }
3882
+ }
3883
+ /**
3884
+ * Creates a Controller to control the specified element.
3885
+ * @param element - The element to be controlled by this controller.
3886
+ * @param definition - The element definition metadata that instructs this
3887
+ * controller in how to handle rendering and other platform integrations.
3888
+ * @internal
3889
+ */
3890
+ constructor(element, definition) {
3891
+ super(element);
3892
+ /**
3893
+ * A map of observable properties that were set on the element before upgrade.
3894
+ */
3895
+ this.boundObservables = null;
3896
+ /**
3897
+ * Indicates whether the controller needs to perform initial rendering.
3898
+ */
3899
+ this.needsInitialization = true;
3900
+ /**
3901
+ * Indicates whether the element has an existing shadow root (e.g. from declarative shadow DOM).
3902
+ */
3903
+ this.hasExistingShadowRoot = false;
3904
+ /**
3905
+ * The template used to render the component.
3906
+ */
3907
+ this._template = null;
3908
+ /**
3909
+ * The current lifecycle stage of the controller.
3910
+ */
3911
+ this.stage = Stages.disconnected;
3912
+ /**
3913
+ * A guard against connecting behaviors multiple times
3914
+ * during connect in scenarios where a behavior adds
3915
+ * another behavior during it's connectedCallback
3916
+ */
3917
+ this.guardBehaviorConnection = false;
3918
+ /**
3919
+ * The behaviors associated with the component.
3920
+ */
3921
+ this.behaviors = null;
3922
+ /**
3923
+ * Tracks whether behaviors are connected so that
3924
+ * behaviors cant be connected multiple times
3925
+ */
3926
+ this.behaviorsConnected = false;
3927
+ /**
3928
+ * The main set of styles used for the component, independent of any
3929
+ * dynamically added styles.
3930
+ */
3931
+ this._mainStyles = null;
3932
+ /**
3933
+ * This allows Observable.getNotifier(...) to return the Controller
3934
+ * when the notifier for the Controller itself is being requested. The
3935
+ * result is that the Observable system does not need to create a separate
3936
+ * instance of Notifier for observables on the Controller. The component and
3937
+ * the controller will now share the same notifier, removing one-object construct
3938
+ * per web component instance.
3939
+ */
3940
+ this.$fastController = this;
3941
+ /**
3942
+ * The view associated with the custom element.
3943
+ * @remarks
3944
+ * If `null` then the element is managing its own rendering.
3945
+ */
3946
+ this.view = null;
3947
+ this.source = element;
3948
+ this.definition = definition;
3949
+ this.shadowOptions = definition.shadowOptions;
3950
+ // Capture any observable values that were set by the binding engine before
3951
+ // the browser upgraded the element. Then delete the property since it will
3952
+ // shadow the getter/setter that is required to make the observable operate.
3953
+ // Later, in the connect callback, we'll re-apply the values.
3954
+ const accessors = Observable.getAccessors(element);
3955
+ if (accessors.length > 0) {
3956
+ const boundObservables = (this.boundObservables = Object.create(null));
3957
+ for (let i = 0, ii = accessors.length; i < ii; ++i) {
3958
+ const propertyName = accessors[i].name;
3959
+ const value = element[propertyName];
3960
+ if (value !== void 0) {
3961
+ delete element[propertyName];
3962
+ boundObservables[propertyName] = value;
3963
+ }
3964
+ }
3965
+ }
3508
3966
  }
3509
3967
  /**
3510
3968
  * Registers an unbind handler with the controller.
@@ -3527,7 +3985,7 @@ class ElementController extends PropertyChangeNotifier {
3527
3985
  behavior.addedCallback && behavior.addedCallback(this);
3528
3986
  if (behavior.connectedCallback &&
3529
3987
  !this.guardBehaviorConnection &&
3530
- (this.stage === 1 /* Stages.connected */ || this.stage === 0 /* Stages.connecting */)) {
3988
+ (this.stage === Stages.connected || this.stage === Stages.connecting)) {
3531
3989
  behavior.connectedCallback(this);
3532
3990
  }
3533
3991
  }
@@ -3551,7 +4009,7 @@ class ElementController extends PropertyChangeNotifier {
3551
4009
  }
3552
4010
  if (count === 1 || force) {
3553
4011
  targetBehaviors.delete(behavior);
3554
- if (behavior.disconnectedCallback && this.stage !== 3 /* Stages.disconnected */) {
4012
+ if (behavior.disconnectedCallback && this.stage !== Stages.disconnected) {
3555
4013
  behavior.disconnectedCallback(this);
3556
4014
  }
3557
4015
  behavior.removedCallback && behavior.removedCallback(this);
@@ -3612,10 +4070,10 @@ class ElementController extends PropertyChangeNotifier {
3612
4070
  * Runs connected lifecycle behavior on the associated element.
3613
4071
  */
3614
4072
  connect() {
3615
- if (this.stage !== 3 /* Stages.disconnected */) {
4073
+ if (this.stage !== Stages.disconnected) {
3616
4074
  return;
3617
4075
  }
3618
- this.stage = 0 /* Stages.connecting */;
4076
+ this.stage = Stages.connecting;
3619
4077
  this.bindObservables();
3620
4078
  this.connectBehaviors();
3621
4079
  if (this.needsInitialization) {
@@ -3626,9 +4084,12 @@ class ElementController extends PropertyChangeNotifier {
3626
4084
  else if (this.view !== null) {
3627
4085
  this.view.bind(this.source);
3628
4086
  }
3629
- this.stage = 1 /* Stages.connected */;
4087
+ this.stage = Stages.connected;
3630
4088
  Observable.notify(this, isConnectedPropertyName);
3631
4089
  }
4090
+ /**
4091
+ * Binds any observables that were set before upgrade.
4092
+ */
3632
4093
  bindObservables() {
3633
4094
  if (this.boundObservables !== null) {
3634
4095
  const element = this.source;
@@ -3641,6 +4102,9 @@ class ElementController extends PropertyChangeNotifier {
3641
4102
  this.boundObservables = null;
3642
4103
  }
3643
4104
  }
4105
+ /**
4106
+ * Connects any existing behaviors on the associated element.
4107
+ */
3644
4108
  connectBehaviors() {
3645
4109
  if (this.behaviorsConnected === false) {
3646
4110
  const behaviors = this.behaviors;
@@ -3654,6 +4118,9 @@ class ElementController extends PropertyChangeNotifier {
3654
4118
  this.behaviorsConnected = true;
3655
4119
  }
3656
4120
  }
4121
+ /**
4122
+ * Disconnects any behaviors on the associated element.
4123
+ */
3657
4124
  disconnectBehaviors() {
3658
4125
  if (this.behaviorsConnected === true) {
3659
4126
  const behaviors = this.behaviors;
@@ -3669,16 +4136,16 @@ class ElementController extends PropertyChangeNotifier {
3669
4136
  * Runs disconnected lifecycle behavior on the associated element.
3670
4137
  */
3671
4138
  disconnect() {
3672
- if (this.stage !== 1 /* Stages.connected */) {
4139
+ if (this.stage !== Stages.connected) {
3673
4140
  return;
3674
4141
  }
3675
- this.stage = 2 /* Stages.disconnecting */;
4142
+ this.stage = Stages.disconnecting;
3676
4143
  Observable.notify(this, isConnectedPropertyName);
3677
4144
  if (this.view !== null) {
3678
4145
  this.view.unbind();
3679
4146
  }
3680
4147
  this.disconnectBehaviors();
3681
- this.stage = 3 /* Stages.disconnected */;
4148
+ this.stage = Stages.disconnected;
3682
4149
  }
3683
4150
  /**
3684
4151
  * Runs the attribute changed callback for the associated element.
@@ -3701,11 +4168,18 @@ class ElementController extends PropertyChangeNotifier {
3701
4168
  * Only emits events if connected.
3702
4169
  */
3703
4170
  emit(type, detail, options) {
3704
- if (this.stage === 1 /* Stages.connected */) {
4171
+ if (this.stage === Stages.connected) {
3705
4172
  return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
3706
4173
  }
3707
4174
  return false;
3708
4175
  }
4176
+ /**
4177
+ * Renders the provided template to the element.
4178
+ *
4179
+ * @param template - The template to render.
4180
+ * @remarks
4181
+ * If `null` is provided, any existing view will be removed.
4182
+ */
3709
4183
  renderTemplate(template) {
3710
4184
  var _a;
3711
4185
  // When getting the host to render to, we start by looking
@@ -3735,20 +4209,33 @@ class ElementController extends PropertyChangeNotifier {
3735
4209
  /**
3736
4210
  * Locates or creates a controller for the specified element.
3737
4211
  * @param element - The element to return the controller for.
4212
+ * @param override - Reset the controller even if one has been defined.
3738
4213
  * @remarks
3739
4214
  * The specified element must have a {@link FASTElementDefinition}
3740
4215
  * registered either through the use of the {@link customElement}
3741
4216
  * decorator or a call to `FASTElement.define`.
3742
4217
  */
3743
- static forCustomElement(element) {
4218
+ static forCustomElement(element, override = false) {
3744
4219
  const controller = element.$fastController;
3745
- if (controller !== void 0) {
4220
+ if (controller !== void 0 && !override) {
3746
4221
  return controller;
3747
4222
  }
3748
4223
  const definition = FASTElementDefinition.getForInstance(element);
3749
4224
  if (definition === void 0) {
3750
- throw FAST.error(1401 /* Message.missingElementDefinition */);
4225
+ throw FAST.error(Message.missingElementDefinition);
3751
4226
  }
4227
+ Observable.getNotifier(definition).subscribe({
4228
+ handleChange: () => {
4229
+ ElementController.forCustomElement(element, true);
4230
+ element.$fastController.connect();
4231
+ },
4232
+ }, "template");
4233
+ Observable.getNotifier(definition).subscribe({
4234
+ handleChange: () => {
4235
+ ElementController.forCustomElement(element, true);
4236
+ element.$fastController.connect();
4237
+ },
4238
+ }, "shadowOptions");
3752
4239
  return (element.$fastController = new elementControllerStrategy(element, definition));
3753
4240
  }
3754
4241
  /**
@@ -3880,7 +4367,10 @@ if (ElementStyles.supportsAdoptedStyleSheets) {
3880
4367
  else {
3881
4368
  ElementStyles.setDefaultStrategy(StyleElementStrategy);
3882
4369
  }
3883
- const deferHydrationAttribute = "defer-hydration";
4370
+ /**
4371
+ * The attribute used to indicate that an element needs hydration.
4372
+ * @public
4373
+ */
3884
4374
  const needsHydrationAttribute = "needs-hydration";
3885
4375
  /**
3886
4376
  * An ElementController capable of hydrating FAST elements from
@@ -3889,22 +4379,90 @@ const needsHydrationAttribute = "needs-hydration";
3889
4379
  * @beta
3890
4380
  */
3891
4381
  class HydratableElementController extends ElementController {
4382
+ /**
4383
+ * {@inheritdoc ElementController.shadowOptions}
4384
+ */
4385
+ get shadowOptions() {
4386
+ return super.shadowOptions;
4387
+ }
4388
+ set shadowOptions(value) {
4389
+ super.shadowOptions = value;
4390
+ if ((this.hasExistingShadowRoot || (value !== void 0 && !this.template)) &&
4391
+ this.definition.templateOptions === TemplateOptions.deferAndHydrate) {
4392
+ this.source.toggleAttribute(deferHydrationAttribute, true);
4393
+ this.source.toggleAttribute(needsHydrationAttribute, true);
4394
+ }
4395
+ }
4396
+ /**
4397
+ * Adds the current element instance to the hydrating instances map
4398
+ */
4399
+ addHydratingInstance() {
4400
+ if (!HydratableElementController.hydratingInstances) {
4401
+ return;
4402
+ }
4403
+ const name = this.definition.name;
4404
+ let instances = HydratableElementController.hydratingInstances.get(name);
4405
+ if (!instances) {
4406
+ instances = new Set();
4407
+ HydratableElementController.hydratingInstances.set(name, instances);
4408
+ }
4409
+ instances.add(this.source);
4410
+ }
4411
+ /**
4412
+ * Configure lifecycle callbacks for hydration events
4413
+ */
4414
+ static config(callbacks) {
4415
+ HydratableElementController.lifecycleCallbacks = callbacks;
4416
+ return this;
4417
+ }
3892
4418
  static hydrationObserverHandler(records) {
3893
4419
  for (const record of records) {
3894
- HydratableElementController.hydrationObserver.unobserve(record.target);
3895
- record.target.$fastController.connect();
4420
+ if (!record.target.hasAttribute(deferHydrationAttribute)) {
4421
+ HydratableElementController.hydrationObserver.unobserve(record.target);
4422
+ record.target.$fastController.connect();
4423
+ }
4424
+ }
4425
+ }
4426
+ /**
4427
+ * Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
4428
+ * Then resets the ElementController strategy to the default so that future elements
4429
+ * don't use the HydratableElementController.
4430
+ *
4431
+ * @param deadline - the idle deadline object
4432
+ */
4433
+ static checkHydrationComplete(deadline) {
4434
+ var _a, _b, _c;
4435
+ if (deadline.didTimeout) {
4436
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
4437
+ return;
4438
+ }
4439
+ // If there are no more hydrating instances, invoke the hydrationComplete callback
4440
+ if (((_a = HydratableElementController.hydratingInstances) === null || _a === void 0 ? void 0 : _a.size) === 0) {
4441
+ try {
4442
+ (_c = (_b = HydratableElementController.lifecycleCallbacks).hydrationComplete) === null || _c === void 0 ? void 0 : _c.call(_b);
4443
+ }
4444
+ catch (_d) {
4445
+ // A lifecycle callback must never prevent post-hydration cleanup.
4446
+ }
4447
+ // Reset to the default strategy after hydration is complete
4448
+ ElementController.setStrategy(ElementController);
3896
4449
  }
3897
4450
  }
4451
+ /**
4452
+ * Runs connected lifecycle behavior on the associated element.
4453
+ */
3898
4454
  connect() {
3899
- var _a, _b;
4455
+ var _a, _b, _c, _d, _e, _f, _g;
3900
4456
  // Initialize needsHydration on first connect
3901
- if (this.needsHydration === undefined) {
3902
- this.needsHydration =
3903
- this.source.getAttribute(needsHydrationAttribute) !== null;
4457
+ this.needsHydration =
4458
+ (_a = this.needsHydration) !== null && _a !== void 0 ? _a : this.source.hasAttribute(needsHydrationAttribute);
4459
+ if (this.needsHydration) {
4460
+ this.addHydratingInstance();
3904
4461
  }
3905
4462
  // If the `defer-hydration` attribute exists on the source,
3906
4463
  // wait for it to be removed before continuing connection behavior.
3907
4464
  if (this.source.hasAttribute(deferHydrationAttribute)) {
4465
+ this.addHydratingInstance();
3908
4466
  HydratableElementController.hydrationObserver.observe(this.source, {
3909
4467
  attributeFilter: [deferHydrationAttribute],
3910
4468
  });
@@ -3916,18 +4474,34 @@ class HydratableElementController extends ElementController {
3916
4474
  // class
3917
4475
  if (!this.needsHydration) {
3918
4476
  super.connect();
4477
+ this.removeHydratingInstance();
3919
4478
  return;
3920
4479
  }
3921
- if (this.stage !== 3 /* Stages.disconnected */) {
4480
+ if (this.stage !== Stages.disconnected) {
3922
4481
  return;
3923
4482
  }
3924
- this.stage = 0 /* Stages.connecting */;
4483
+ if (!HydratableElementController.hydrationStarted) {
4484
+ HydratableElementController.hydrationStarted = true;
4485
+ try {
4486
+ (_c = (_b = HydratableElementController.lifecycleCallbacks).hydrationStarted) === null || _c === void 0 ? void 0 : _c.call(_b);
4487
+ }
4488
+ catch (_h) {
4489
+ // A lifecycle callback must never prevent hydration.
4490
+ }
4491
+ }
4492
+ try {
4493
+ (_e = (_d = HydratableElementController.lifecycleCallbacks).elementWillHydrate) === null || _e === void 0 ? void 0 : _e.call(_d, this.source);
4494
+ }
4495
+ catch (_j) {
4496
+ // A lifecycle callback must never prevent hydration.
4497
+ }
4498
+ this.stage = Stages.connecting;
3925
4499
  this.bindObservables();
3926
4500
  this.connectBehaviors();
3927
- const element = this.source;
3928
- const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
3929
4501
  if (this.template) {
3930
4502
  if (isHydratable(this.template)) {
4503
+ const element = this.source;
4504
+ const host = (_f = getShadowRoot(element)) !== null && _f !== void 0 ? _f : element;
3931
4505
  let firstChild = host.firstChild;
3932
4506
  let lastChild = host.lastChild;
3933
4507
  if (element.shadowRoot === null) {
@@ -3942,27 +4516,86 @@ class HydratableElementController extends ElementController {
3942
4516
  }
3943
4517
  }
3944
4518
  this.view = this.template.hydrate(firstChild, lastChild, element);
3945
- (_b = this.view) === null || _b === void 0 ? void 0 : _b.bind(this.source);
4519
+ (_g = this.view) === null || _g === void 0 ? void 0 : _g.bind(this.source);
3946
4520
  }
3947
4521
  else {
3948
4522
  this.renderTemplate(this.template);
3949
4523
  }
3950
4524
  }
3951
4525
  this.addStyles(this.mainStyles);
3952
- this.stage = 1 /* Stages.connected */;
4526
+ this.stage = Stages.connected;
3953
4527
  this.source.removeAttribute(needsHydrationAttribute);
3954
4528
  this.needsInitialization = this.needsHydration = false;
4529
+ this.removeHydratingInstance();
3955
4530
  Observable.notify(this, isConnectedPropertyName);
3956
4531
  }
4532
+ /**
4533
+ * Removes the current element instance from the hydrating instances map
4534
+ */
4535
+ removeHydratingInstance() {
4536
+ var _a, _b;
4537
+ if (!HydratableElementController.hydratingInstances) {
4538
+ return;
4539
+ }
4540
+ try {
4541
+ (_b = (_a = HydratableElementController.lifecycleCallbacks).elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.source);
4542
+ }
4543
+ catch (_c) {
4544
+ // A lifecycle callback must never prevent hydration.
4545
+ }
4546
+ const name = this.definition.name;
4547
+ const instances = HydratableElementController.hydratingInstances.get(name);
4548
+ if (instances) {
4549
+ instances.delete(this.source);
4550
+ if (!instances.size) {
4551
+ HydratableElementController.hydratingInstances.delete(name);
4552
+ }
4553
+ if (HydratableElementController.idleCallbackId) {
4554
+ cancelIdleCallback(HydratableElementController.idleCallbackId);
4555
+ }
4556
+ HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
4557
+ }
4558
+ }
4559
+ /**
4560
+ * Unregisters the hydration observer when the element is disconnected.
4561
+ */
3957
4562
  disconnect() {
3958
4563
  super.disconnect();
3959
4564
  HydratableElementController.hydrationObserver.unobserve(this.source);
3960
4565
  }
4566
+ /**
4567
+ * Sets the ElementController strategy to HydratableElementController.
4568
+ * @remarks
4569
+ * This method is typically called during application startup to enable
4570
+ * hydration support for FAST elements.
4571
+ */
3961
4572
  static install() {
3962
4573
  ElementController.setStrategy(HydratableElementController);
3963
4574
  }
3964
4575
  }
3965
4576
  HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
4577
+ /**
4578
+ * Lifecycle callbacks for hydration events
4579
+ */
4580
+ HydratableElementController.lifecycleCallbacks = {};
4581
+ /**
4582
+ * Whether the hydrationStarted callback has already been invoked.
4583
+ */
4584
+ HydratableElementController.hydrationStarted = false;
4585
+ /**
4586
+ * An idle callback ID used to track hydration completion
4587
+ */
4588
+ HydratableElementController.idleCallbackId = null;
4589
+ /**
4590
+ * A map of element instances by the name of the custom element they are
4591
+ * associated with. The key is the custom element name, and the value is the
4592
+ * instances of hydratable elements which currently need to be hydrated.
4593
+ *
4594
+ * When all of the instances in the set have been hydrated, the set is
4595
+ * cleared and removed from the map. If the map is empty, the
4596
+ * hydrationComplete callback is invoked.
4597
+ */
4598
+ HydratableElementController.hydratingInstances = new Map();
3966
4599
 
3967
4600
  /* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
3968
4601
  function createFASTElement(BaseType) {
@@ -3994,6 +4627,24 @@ function compose(type, nameOrDef) {
3994
4627
  }
3995
4628
  return FASTElementDefinition.compose(this, type);
3996
4629
  }
4630
+ function defineAsync(type, nameOrDef) {
4631
+ if (isFunction(type)) {
4632
+ return new Promise(resolve => {
4633
+ FASTElementDefinition.composeAsync(type, nameOrDef).then(value => {
4634
+ resolve(value);
4635
+ });
4636
+ }).then(value => {
4637
+ return value.define().type;
4638
+ });
4639
+ }
4640
+ return new Promise(resolve => {
4641
+ FASTElementDefinition.composeAsync(this, type).then(value => {
4642
+ resolve(value);
4643
+ });
4644
+ }).then(value => {
4645
+ return value.define().type;
4646
+ });
4647
+ }
3997
4648
  function define(type, nameOrDef) {
3998
4649
  if (isFunction(type)) {
3999
4650
  return FASTElementDefinition.compose(type, nameOrDef).define().type;
@@ -4027,6 +4678,11 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
4027
4678
  * @public
4028
4679
  */
4029
4680
  compose,
4681
+ /**
4682
+ * Defines metadata for a FASTElement which can be used after it has been resolved to define the element.
4683
+ * @alpha
4684
+ */
4685
+ defineAsync,
4030
4686
  });
4031
4687
 
4032
4688
  function staticallyCompose(item) {
@@ -4414,7 +5070,7 @@ const AccordionExpandMode = {
4414
5070
  };
4415
5071
  const tagName$E = `${FluentDesignSystem.prefix}-accordion`;
4416
5072
 
4417
- function requestIdleCallback(callback, options) {
5073
+ function requestIdleCallback$1(callback, options) {
4418
5074
  if ("requestIdleCallback" in globalThis) {
4419
5075
  return globalThis.requestIdleCallback(callback, options);
4420
5076
  }
@@ -4434,13 +5090,13 @@ function waitForConnectedDescendants(target, callback, options) {
4434
5090
  const scheduleCheck = (deadline) => {
4435
5091
  if (target.querySelector(selector) === null || deadline && deadline.timeRemaining() <= 0) {
4436
5092
  if (useIdleCallback) {
4437
- requestIdleCallback(callback, { timeout });
5093
+ requestIdleCallback$1(callback, { timeout });
4438
5094
  } else {
4439
5095
  callback();
4440
5096
  }
4441
5097
  return;
4442
5098
  }
4443
- requestIdleCallback(scheduleCheck, { timeout });
5099
+ requestIdleCallback$1(scheduleCheck, { timeout });
4444
5100
  };
4445
5101
  scheduleCheck();
4446
5102
  }
@@ -6146,7 +6802,7 @@ class CompoundButton extends Button {
6146
6802
  const styles$x = css`${styles$C} :host,:host(:is([size])){gap:12px;height:auto;padding-top:14px;padding-inline:12px;padding-bottom:16px;font-size:${fontSizeBase300};line-height:${lineHeightBase300}}.content{display:flex;flex-direction:column;text-align:start}::slotted([slot='description']){color:${colorNeutralForeground2};line-height:100%;font-size:${fontSizeBase200};font-weight:${fontWeightRegular}}::slotted(svg),:host([size='large']) ::slotted(svg){font-size:40px;height:40px;width:40px}:host(:hover) ::slotted([slot='description']){color:${colorNeutralForeground2Hover}}:host(:active) ::slotted([slot='description']){color:${colorNeutralForeground2Pressed}}:host(:is([appearance='primary'],[appearance='primary']:is(:hover,:active))) ::slotted([slot='description']){color:${colorNeutralForegroundOnBrand}}:host(:is([appearance='transparent'],[appearance='subtle'],[appearance='subtle']:is(:hover,:active))) ::slotted([slot='description']){color:${colorNeutralForeground2}}:host([appearance='transparent']:hover) ::slotted([slot='description']){color:${colorNeutralForeground2BrandHover}}:host([appearance='transparent']:active) ::slotted([slot='description']){color:${colorNeutralForeground2BrandPressed}}:host(:is(:disabled,:disabled[appearance],[disabled-focusable],[disabled-focusable][appearance])) ::slotted([slot='description']){color:${colorNeutralForegroundDisabled}}:host([size='small']){padding:8px;padding-bottom:10px}:host([icon-only]){min-width:52px;max-width:52px;padding:${spacingHorizontalSNudge}}:host([icon-only][size='small']){min-width:48px;max-width:48px;padding:${spacingHorizontalXS}}:host([icon-only][size='large']){min-width:56px;max-width:56px;padding:${spacingHorizontalS}}:host([size='large']){padding-top:18px;padding-inline:16px;padding-bottom:20px;font-size:${fontSizeBase400};line-height:${lineHeightBase400}}:host([size='large']) ::slotted([slot='description']){font-size:${fontSizeBase300}}@media (forced-colors:active){:host([appearance='primary']:not(:hover,:focus-visible,:disabled,[disabled-focusable])) ::slotted([slot='description']){color:HighlightText}}`;
6147
6803
 
6148
6804
  function buttonTemplate(options = {}) {
6149
- return html`<template ?disabled=${(x) => x.disabled} tabindex=${(x) => x.disabled ? null : x.tabIndex ?? 0}>${startSlotTemplate(options)} <span class=content part=content><slot ${slotted("defaultSlottedContent")}></slot><slot name=description></slot></span>${endSlotTemplate(options)}</template>`;
6805
+ return html`<template @click=${(x, c) => x.clickHandler(c.event)} @keypress=${(x, c) => x.keypressHandler(c.event)}>${startSlotTemplate(options)} <span class=content part=content><slot ${slotted("defaultSlottedContent")}></slot><slot name=description></slot></span>${endSlotTemplate(options)}</template>`;
6150
6806
  }
6151
6807
  const template$y = buttonTemplate();
6152
6808
 
@@ -6274,19 +6930,6 @@ var __decorateClass$B = (decorators, target, key, kind) => {
6274
6930
  class Dialog extends FASTElement {
6275
6931
  constructor() {
6276
6932
  super(...arguments);
6277
- this.type = DialogType.modal;
6278
- /**
6279
- * Method to emit an event before the dialog's open state changes
6280
- * HTML spec proposal: https://github.com/whatwg/html/issues/9733
6281
- *
6282
- * @public
6283
- */
6284
- this.emitBeforeToggle = () => {
6285
- this.$emit("beforetoggle", {
6286
- oldState: this.dialog.open ? "open" : "closed",
6287
- newState: this.dialog.open ? "closed" : "open"
6288
- });
6289
- };
6290
6933
  /**
6291
6934
  * Method to emit an event after the dialog's open state changes
6292
6935
  * HTML spec proposal: https://github.com/whatwg/html/issues/9733
@@ -6300,11 +6943,48 @@ class Dialog extends FASTElement {
6300
6943
  });
6301
6944
  };
6302
6945
  }
6303
- dialogChanged() {
6304
- this.updateDialogAttributes();
6946
+ get dialogDescribedby() {
6947
+ if (this.dialog) {
6948
+ return this.ariaDescribedby;
6949
+ }
6305
6950
  }
6306
- typeChanged(prev, next) {
6307
- this.updateDialogAttributes();
6951
+ get dialogLabel() {
6952
+ if (this.dialog) {
6953
+ return this.ariaLabel;
6954
+ }
6955
+ }
6956
+ get dialogLabelledby() {
6957
+ if (this.dialog) {
6958
+ return this.ariaLabelledby;
6959
+ }
6960
+ }
6961
+ get dialogModal() {
6962
+ if (this.dialog && this.type !== DialogType.nonModal) {
6963
+ return true;
6964
+ }
6965
+ }
6966
+ get dialogRole() {
6967
+ if (this.dialog && this.type === DialogType.alert) {
6968
+ return "alertdialog";
6969
+ }
6970
+ }
6971
+ connectedCallback() {
6972
+ super.connectedCallback();
6973
+ Updates.enqueue(() => {
6974
+ this.type = this.type ?? DialogType.modal;
6975
+ });
6976
+ }
6977
+ /**
6978
+ * Method to emit an event before the dialog's open state changes
6979
+ * HTML spec proposal: https://github.com/whatwg/html/issues/9733
6980
+ *
6981
+ * @public
6982
+ */
6983
+ emitBeforeToggle() {
6984
+ this.$emit("beforetoggle", {
6985
+ oldState: this.dialog.open ? "open" : "closed",
6986
+ newState: this.dialog.open ? "closed" : "open"
6987
+ });
6308
6988
  }
6309
6989
  /**
6310
6990
  * Method to show the dialog
@@ -6345,26 +7025,6 @@ class Dialog extends FASTElement {
6345
7025
  }
6346
7026
  return true;
6347
7027
  }
6348
- /**
6349
- * Updates the internal dialog element's attributes based on its type.
6350
- *
6351
- * @internal
6352
- */
6353
- updateDialogAttributes() {
6354
- if (!this.dialog) {
6355
- return;
6356
- }
6357
- if (this.type === DialogType.alert) {
6358
- this.dialog.setAttribute("role", "alertdialog");
6359
- } else {
6360
- this.dialog.removeAttribute("role");
6361
- }
6362
- if (this.type !== DialogType.nonModal) {
6363
- this.dialog.setAttribute("aria-modal", "true");
6364
- } else {
6365
- this.dialog.removeAttribute("aria-modal");
6366
- }
6367
- }
6368
7028
  }
6369
7029
  __decorateClass$B([
6370
7030
  observable
@@ -6381,8 +7041,23 @@ __decorateClass$B([
6381
7041
  __decorateClass$B([
6382
7042
  attr
6383
7043
  ], Dialog.prototype, "type", 2);
7044
+ __decorateClass$B([
7045
+ volatile
7046
+ ], Dialog.prototype, "dialogDescribedby", 1);
7047
+ __decorateClass$B([
7048
+ volatile
7049
+ ], Dialog.prototype, "dialogLabel", 1);
7050
+ __decorateClass$B([
7051
+ volatile
7052
+ ], Dialog.prototype, "dialogLabelledby", 1);
7053
+ __decorateClass$B([
7054
+ volatile
7055
+ ], Dialog.prototype, "dialogModal", 1);
7056
+ __decorateClass$B([
7057
+ volatile
7058
+ ], Dialog.prototype, "dialogRole", 1);
6384
7059
 
6385
- const template$w = html`<dialog class=dialog part=dialog aria-describedby=${(x) => x.ariaDescribedby} aria-labelledby=${(x) => x.ariaLabelledby} aria-label=${(x) => x.ariaLabel} @click=${(x, c) => x.clickHandler(c.event)} @cancel=${(x) => x.hide()} ${ref("dialog")}><slot></slot></dialog>`;
7060
+ const template$w = html`<dialog class=dialog part=dialog aria-modal=${(x) => x.dialogModal} aria-describedby=${(x) => x.dialogDescribedby} aria-labelledby=${(x) => x.dialogLabelledby} aria-label=${(x) => x.dialogLabel} role=${(x) => x.dialogRole} @click=${(x, c) => x.clickHandler(c.event)} @cancel=${(x) => x.hide()} ${ref("dialog")}><slot></slot></dialog>`;
6386
7061
 
6387
7062
  const styles$v = css`@layer base{:host{--dialog-backdrop:${colorBackgroundOverlay};--dialog-starting-scale:0.85}::backdrop{background:var(--dialog-backdrop,rgba(0,0,0,0.4))}dialog{background:${colorNeutralBackground1};border-radius:${borderRadiusXLarge};border:none;box-shadow:${shadow64};color:${colorNeutralForeground1};max-height:100vh;padding:0;width:100%;max-width:600px}:host([type='non-modal']) dialog{inset:0;position:fixed;z-index:2;overflow:auto}@supports (max-height:1dvh){dialog{max-height:100dvh}}}@layer animations{@media (prefers-reduced-motion:no-preference){dialog,::backdrop{transition:display allow-discrete,opacity,overlay allow-discrete,scale;transition-duration:${durationGentle};transition-timing-function:${curveDecelerateMid};opacity:0}::backdrop{transition-timing-function:${curveLinear}}[open],[open]::backdrop{opacity:1}dialog:not([open]){scale:var(--dialog-starting-scale);transition-timing-function:${curveAccelerateMid}}}@starting-style{[open],[open]::backdrop{opacity:0}dialog{scale:var(--dialog-starting-scale)}}}@media (forced-colors:active){@layer base{dialog{border:${strokeWidthThin} solid ${colorTransparentStroke}}}}`;
6388
7063
 
@@ -6572,7 +7247,6 @@ var __decorateClass$y = (decorators, target, key, kind) => {
6572
7247
  class Drawer extends FASTElement {
6573
7248
  constructor() {
6574
7249
  super(...arguments);
6575
- this.type = DrawerType.modal;
6576
7250
  this.position = DrawerPosition.start;
6577
7251
  this.size = DrawerSize.medium;
6578
7252
  /**
@@ -6600,27 +7274,37 @@ class Drawer extends FASTElement {
6600
7274
  });
6601
7275
  };
6602
7276
  }
6603
- typeChanged() {
6604
- if (!this.dialog) {
6605
- return;
7277
+ get dialogDescribedby() {
7278
+ if (this.dialog) {
7279
+ return this.ariaDescribedby;
6606
7280
  }
6607
- this.updateDialogRole();
6608
- if (this.type === DrawerType.modal) {
6609
- this.dialog.setAttribute("aria-modal", "true");
6610
- } else {
6611
- this.dialog.removeAttribute("aria-modal");
7281
+ }
7282
+ get dialogLabel() {
7283
+ if (this.dialog) {
7284
+ return this.ariaLabel;
6612
7285
  }
6613
7286
  }
6614
- /** @internal */
7287
+ get dialogLabelledby() {
7288
+ if (this.dialog) {
7289
+ return this.ariaLabelledby;
7290
+ }
7291
+ }
7292
+ get dialogModal() {
7293
+ if (this.dialog && this.type === DrawerType.modal) {
7294
+ return true;
7295
+ }
7296
+ }
7297
+ get dialogRole() {
7298
+ if (this.dialog && this.type === DrawerType.modal) {
7299
+ return "dialog";
7300
+ }
7301
+ return this.role;
7302
+ }
6615
7303
  connectedCallback() {
6616
7304
  super.connectedCallback();
6617
- this.typeChanged();
6618
- this.observeRoleAttr();
6619
- }
6620
- /** @internal */
6621
- disconnectedCallback() {
6622
- super.disconnectedCallback();
6623
- this.roleAttrObserver.disconnect();
7305
+ Updates.enqueue(() => {
7306
+ this.type = this.type ?? DrawerType.modal;
7307
+ });
6624
7308
  }
6625
7309
  /**
6626
7310
  * Method to show the drawer
@@ -6668,24 +7352,6 @@ class Drawer extends FASTElement {
6668
7352
  cancelHandler() {
6669
7353
  this.hide();
6670
7354
  }
6671
- observeRoleAttr() {
6672
- if (this.roleAttrObserver) {
6673
- return;
6674
- }
6675
- this.roleAttrObserver = new MutationObserver(() => {
6676
- this.updateDialogRole();
6677
- });
6678
- this.roleAttrObserver.observe(this, {
6679
- attributes: true,
6680
- attributeFilter: ["role"]
6681
- });
6682
- }
6683
- updateDialogRole() {
6684
- if (!this.dialog) {
6685
- return;
6686
- }
6687
- this.dialog.role = this.type === DrawerType.modal ? "dialog" : this.role;
6688
- }
6689
7355
  }
6690
7356
  __decorateClass$y([
6691
7357
  attr
@@ -6699,17 +7365,35 @@ __decorateClass$y([
6699
7365
  __decorateClass$y([
6700
7366
  attr
6701
7367
  ], Drawer.prototype, "position", 2);
7368
+ __decorateClass$y([
7369
+ observable
7370
+ ], Drawer.prototype, "role", 2);
6702
7371
  __decorateClass$y([
6703
7372
  attr({ attribute: "size" })
6704
7373
  ], Drawer.prototype, "size", 2);
6705
7374
  __decorateClass$y([
6706
7375
  observable
6707
7376
  ], Drawer.prototype, "dialog", 2);
7377
+ __decorateClass$y([
7378
+ volatile
7379
+ ], Drawer.prototype, "dialogDescribedby", 1);
7380
+ __decorateClass$y([
7381
+ volatile
7382
+ ], Drawer.prototype, "dialogLabel", 1);
7383
+ __decorateClass$y([
7384
+ volatile
7385
+ ], Drawer.prototype, "dialogLabelledby", 1);
7386
+ __decorateClass$y([
7387
+ volatile
7388
+ ], Drawer.prototype, "dialogModal", 1);
7389
+ __decorateClass$y([
7390
+ volatile
7391
+ ], Drawer.prototype, "dialogRole", 1);
6708
7392
 
6709
7393
  const styles$s = css`${display("block")} :host{--dialog-backdrop:${colorBackgroundOverlay}}:host([type='non-modal']) dialog[open]::backdrop{display:none}:host([type='non-modal']) dialog{position:fixed;top:0;bottom:0}:host([type='inline']){height:100%;width:fit-content}:host([type='inline']) dialog[open]{box-shadow:none;position:relative}:host([size='small']) dialog{width:320px;max-width:320px}:host([size='large']) dialog{width:940px;max-width:940px}:host([size='full']) dialog{width:100%;max-width:100%}:host([position='end']) dialog{margin-inline-start:auto;margin-inline-end:0}dialog{box-sizing:border-box;z-index:var(--drawer-elevation,1000);font-size:${fontSizeBase300};line-height:${lineHeightBase300};font-family:${fontFamilyBase};font-weight:${fontWeightRegular};color:${colorNeutralForeground1};max-width:var(--drawer-width,592px);max-height:100vh;height:100%;margin-inline-start:0;margin-inline-end:auto;border-inline-end-color:${colorTransparentStroke};border-inline-start-color:var(--drawer-separator,${colorTransparentStroke});outline:none;top:0;bottom:0;width:var(--drawer-width,592px);border-radius:0;padding:0;max-width:var(--drawer-width,592px);box-shadow:${shadow64};border:${strokeWidthThin} solid ${colorTransparentStroke};background:${colorNeutralBackground1}}dialog::backdrop{background:var(--dialog-backdrop)}@layer animations{@media (prefers-reduced-motion:no-preference){dialog{transition:display allow-discrete,opacity,overlay allow-discrete,transform;transition-duration:${durationGentle};transition-timing-function:${curveDecelerateMid}}:host dialog:not([open]){transform:translateX(-100%);transition-timing-function:${curveAccelerateMid}}:host([position='end']) dialog:not([open]){transform:translateX(100%);transition-timing-function:${curveAccelerateMid}}dialog[open]{transform:translateX(0)}dialog::backdrop{transition:display allow-discrete,opacity,overlay allow-discrete,scale;transition-duration:${durationGentle};transition-timing-function:${curveDecelerateMid};background:var(--dialog-backdrop,${colorBackgroundOverlay});opacity:0}dialog[open]::backdrop{opacity:1}dialog::backdrop{transition-timing-function:${curveLinear}}}@starting-style{dialog[open]{transform:translateX(-100%)}:host([position='end']) dialog[open]{transform:translateX(100%)}dialog[open]::backdrop{opacity:0}}}`;
6710
7394
 
6711
7395
  function drawerTemplate() {
6712
- return html`<dialog class=dialog part=dialog aria-describedby=${(x) => x.ariaDescribedby} aria-labelledby=${(x) => x.ariaLabelledby} aria-label=${(x) => x.ariaLabel} size=${(x) => x.size} position=${(x) => x.position} @click=${(x, c) => x.clickHandler(c.event)} @cancel=${(x) => x.cancelHandler()} ${ref("dialog")}><slot></slot></dialog>`;
7396
+ return html`<dialog class=dialog part=dialog aria-describedby=${(x) => x.dialogDescribedby} aria-labelledby=${(x) => x.dialogLabelledby} aria-label=${(x) => x.dialogLabel} aria-modal=${(x) => x.dialogModal} role=${(x) => x.dialogRole} size=${(x) => x.size} position=${(x) => x.position} @click=${(x, c) => x.clickHandler(c.event)} @cancel=${(x) => x.cancelHandler()} ${ref("dialog")}><slot></slot></dialog>`;
6713
7397
  }
6714
7398
  const template$t = drawerTemplate();
6715
7399
 
@@ -9091,6 +9775,9 @@ const _BaseMenuList = class _BaseMenuList extends FASTElement {
9091
9775
  */
9092
9776
  disconnectedCallback() {
9093
9777
  super.disconnectedCallback();
9778
+ Array.from(this.children).forEach((child) => {
9779
+ Observable.getNotifier(child).unsubscribe(this, "hidden");
9780
+ });
9094
9781
  this.menuChildren = void 0;
9095
9782
  this.removeEventListener("change", this.changedMenuItemHandler);
9096
9783
  }
@@ -9112,6 +9799,9 @@ const _BaseMenuList = class _BaseMenuList extends FASTElement {
9112
9799
  }
9113
9800
  setItems() {
9114
9801
  const children = Array.from(this.children);
9802
+ children.forEach((child) => {
9803
+ Observable.getNotifier(child).subscribe(this, "hidden");
9804
+ });
9115
9805
  this.menuChildren = children.filter((child) => !child.hasAttribute("hidden"));
9116
9806
  this.menuItems = this.menuChildren?.filter(this.isMenuItemElement);
9117
9807
  const indent = this.menuItems?.reduce((accum, current) => {
@@ -13235,6 +13925,8 @@ class BaseTree extends FASTElement {
13235
13925
  if (item?.childTreeItems?.length) {
13236
13926
  if (!item.expanded) {
13237
13927
  item.expanded = true;
13928
+ } else {
13929
+ return true;
13238
13930
  }
13239
13931
  }
13240
13932
  return;
@@ -13359,7 +14051,7 @@ class Tree extends BaseTree {
13359
14051
  this.fg = new FocusGroup(this, this.fgItems, {
13360
14052
  definition: {
13361
14053
  behavior: "menu",
13362
- axis: "block",
14054
+ axis: void 0,
13363
14055
  memory: false
13364
14056
  }
13365
14057
  });
@@ -13398,7 +14090,7 @@ __decorateClass$2([
13398
14090
 
13399
14091
  const styles$1 = css`${display("block")} :host{outline:none}`;
13400
14092
 
13401
- const template$1 = html`<template focusgroup="menu nowrap nomemory" @click=${(x, c) => x.clickHandler(c.event)} @keydown=${(x, c) => x.keydownHandler(c.event)} @change=${(x, c) => x.changeHandler(c.event)} @toggle=${(x, c) => x.itemToggleHandler()}><slot ${ref("defaultSlot")} @slotchange=${(x) => x.handleDefaultSlotChange()}></slot></template>`;
14093
+ const template$1 = html`<template focusgroup="menu inline block nowrap nomemory" @click=${(x, c) => x.clickHandler(c.event)} @keydown=${(x, c) => x.keydownHandler(c.event)} @change=${(x, c) => x.changeHandler(c.event)} @toggle=${(x, c) => x.itemToggleHandler()}><slot ${ref("defaultSlot")} @slotchange=${(x) => x.handleDefaultSlotChange()}></slot></template>`;
13402
14094
 
13403
14095
  const definition$1 = Tree.compose({
13404
14096
  name: tagName$1,