@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.
- package/CHANGELOG.md +15 -2
- package/custom-elements.json +10 -49
- package/dist/esm/compound-button/compound-button.template.d.ts +3 -1
- package/dist/esm/compound-button/compound-button.template.js +7 -2
- package/dist/esm/compound-button/compound-button.template.js.map +1 -1
- package/dist/esm/dialog/dialog.d.ts +37 -9
- package/dist/esm/dialog/dialog.js +87 -45
- package/dist/esm/dialog/dialog.js.map +1 -1
- package/dist/esm/dialog/dialog.template.js +5 -3
- package/dist/esm/dialog/dialog.template.js.map +1 -1
- package/dist/esm/drawer/drawer.d.ts +36 -7
- package/dist/esm/drawer/drawer.js +74 -42
- package/dist/esm/drawer/drawer.js.map +1 -1
- package/dist/esm/drawer/drawer.template.js +5 -3
- package/dist/esm/drawer/drawer.template.js.map +1 -1
- package/dist/esm/menu-list/menu-list.base.js +7 -1
- package/dist/esm/menu-list/menu-list.base.js.map +1 -1
- package/dist/esm/tree/tree.base.js +3 -0
- package/dist/esm/tree/tree.base.js.map +1 -1
- package/dist/esm/tree/tree.js +1 -1
- package/dist/esm/tree/tree.js.map +1 -1
- package/dist/esm/tree/tree.template.js +1 -1
- package/dist/web-components-all.js +1731 -1039
- package/dist/web-components-all.min.js +17 -13
- package/dist/web-components.d.ts +74 -16
- package/dist/web-components.js +1731 -1039
- package/dist/web-components.min.js +21 -17
- package/package.json +1 -3
package/dist/web-components.js
CHANGED
|
@@ -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(
|
|
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(
|
|
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
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1494
|
-
|
|
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
|
|
1490
|
+
* The unique id of the factory.
|
|
1497
1491
|
*/
|
|
1498
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
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
|
-
*
|
|
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(
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
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(
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
*
|
|
3016
|
+
* The runtime behavior for template references.
|
|
2614
3017
|
* @public
|
|
2615
3018
|
*/
|
|
2616
|
-
class
|
|
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
|
-
*
|
|
2627
|
-
* @param
|
|
3021
|
+
* Bind this behavior.
|
|
3022
|
+
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
2628
3023
|
*/
|
|
2629
|
-
|
|
2630
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
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
|
-
*
|
|
3039
|
+
* Metadata used to configure a custom attribute's behavior.
|
|
2653
3040
|
* @public
|
|
2654
3041
|
*/
|
|
2655
|
-
|
|
3042
|
+
const AttributeConfiguration = Object.freeze({
|
|
2656
3043
|
/**
|
|
2657
|
-
*
|
|
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
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
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
|
-
*
|
|
2679
|
-
* @param
|
|
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
|
-
|
|
2682
|
-
|
|
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
|
-
*
|
|
3138
|
+
* Gets the value of the attribute/property on the source element.
|
|
3139
|
+
* @param source - The source element to access.
|
|
2686
3140
|
*/
|
|
2687
|
-
|
|
2688
|
-
|
|
3141
|
+
getValue(source) {
|
|
3142
|
+
Observable.track(source, this.name);
|
|
3143
|
+
return source[this.fieldName];
|
|
2689
3144
|
}
|
|
2690
|
-
/**
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
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
|
-
|
|
2703
|
-
|
|
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
|
-
|
|
2706
|
-
|
|
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
|
-
*
|
|
2710
|
-
* @param
|
|
2711
|
-
* @param
|
|
2712
|
-
* @
|
|
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
|
-
|
|
2716
|
-
const
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
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
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
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
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
2799
|
-
* @
|
|
2800
|
-
* @public
|
|
3243
|
+
* The FAST custom element registry
|
|
3244
|
+
* @internal
|
|
2801
3245
|
*/
|
|
2802
|
-
const
|
|
2803
|
-
|
|
2804
|
-
const selectElements = (value) => value.nodeType === 1;
|
|
3246
|
+
const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
|
|
2805
3247
|
/**
|
|
2806
|
-
*
|
|
2807
|
-
* @
|
|
2808
|
-
* @public
|
|
3248
|
+
* Values for the `templateOptions` property.
|
|
3249
|
+
* @alpha
|
|
2809
3250
|
*/
|
|
2810
|
-
const
|
|
2811
|
-
|
|
2812
|
-
|
|
3251
|
+
const TemplateOptions = {
|
|
3252
|
+
deferAndHydrate: "defer-and-hydrate",
|
|
3253
|
+
};
|
|
2813
3254
|
/**
|
|
2814
|
-
*
|
|
3255
|
+
* Defines metadata for a FASTElement.
|
|
2815
3256
|
* @public
|
|
2816
|
-
* @remarks
|
|
2817
|
-
* Internally used by the SlottedDirective and the ChildrenDirective.
|
|
2818
3257
|
*/
|
|
2819
|
-
class
|
|
3258
|
+
class FASTElementDefinition {
|
|
2820
3259
|
/**
|
|
2821
|
-
*
|
|
3260
|
+
* Indicates if this element has been defined in at least one registry.
|
|
2822
3261
|
*/
|
|
2823
|
-
get
|
|
2824
|
-
return this.
|
|
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
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
this.
|
|
2840
|
-
this.
|
|
2841
|
-
|
|
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
|
-
*
|
|
2845
|
-
* @param
|
|
2846
|
-
* @
|
|
2847
|
-
*
|
|
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
|
-
|
|
2850
|
-
|
|
2851
|
-
this.
|
|
2852
|
-
this.
|
|
2853
|
-
|
|
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
|
-
*
|
|
2857
|
-
* @param
|
|
2858
|
-
* @
|
|
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
|
-
|
|
2861
|
-
|
|
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
|
-
*
|
|
2865
|
-
* @param
|
|
2866
|
-
* @
|
|
3340
|
+
* Registers a FASTElement base type.
|
|
3341
|
+
* @param type - The type to register as a base type.
|
|
3342
|
+
* @internal
|
|
2867
3343
|
*/
|
|
2868
|
-
|
|
2869
|
-
|
|
3344
|
+
static registerBaseType(type) {
|
|
3345
|
+
fastElementBaseTypes.add(type);
|
|
2870
3346
|
}
|
|
2871
3347
|
/**
|
|
2872
|
-
*
|
|
2873
|
-
*
|
|
2874
|
-
*
|
|
2875
|
-
* @
|
|
2876
|
-
*
|
|
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
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
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
|
|
2890
|
-
* @public
|
|
3375
|
+
* The definition has been registered to the FAST element registry.
|
|
2891
3376
|
*/
|
|
2892
|
-
|
|
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
|
-
*
|
|
2923
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
2938
|
-
* @
|
|
3384
|
+
* Gets the element definition associated with the instance.
|
|
3385
|
+
* @param instance - The custom element instance to retrieve the definition for.
|
|
2939
3386
|
*/
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
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
|
-
*
|
|
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
|
|
3451
|
+
class ViewTemplate {
|
|
2994
3452
|
/**
|
|
2995
|
-
* Creates an instance of
|
|
2996
|
-
* @param
|
|
2997
|
-
* @param
|
|
2998
|
-
* @param
|
|
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(
|
|
3004
|
-
this.
|
|
3005
|
-
this.
|
|
3006
|
-
this.
|
|
3007
|
-
this.
|
|
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
|
-
*
|
|
3019
|
-
* @param source - The source element to access.
|
|
3020
|
-
* @param value - The value to set the attribute/property to.
|
|
3465
|
+
* @internal
|
|
3021
3466
|
*/
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
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
|
-
*
|
|
3039
|
-
* @param
|
|
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
|
-
|
|
3042
|
-
|
|
3043
|
-
return source[this.fieldName];
|
|
3477
|
+
create(hostBindingTarget) {
|
|
3478
|
+
return this.compile().createView(hostBindingTarget);
|
|
3044
3479
|
}
|
|
3045
|
-
/**
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
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
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
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
|
-
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
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
|
-
*
|
|
3077
|
-
* @param
|
|
3078
|
-
* @param
|
|
3079
|
-
* @
|
|
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
|
-
|
|
3082
|
-
const
|
|
3083
|
-
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
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
|
-
|
|
3090
|
-
|
|
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
|
-
|
|
3100
|
-
|
|
3101
|
-
}
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
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
|
-
*
|
|
3604
|
+
* Begins observation of the nodes.
|
|
3605
|
+
* @param target - The target to observe.
|
|
3182
3606
|
*/
|
|
3183
|
-
|
|
3184
|
-
|
|
3607
|
+
observe(target) {
|
|
3608
|
+
target.addEventListener(slotEvent, this);
|
|
3185
3609
|
}
|
|
3186
3610
|
/**
|
|
3187
|
-
*
|
|
3188
|
-
* @param
|
|
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
|
-
|
|
3193
|
-
|
|
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
|
-
*
|
|
3202
|
-
* @param
|
|
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
|
-
|
|
3207
|
-
|
|
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
|
-
|
|
3215
|
-
|
|
3216
|
-
|
|
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
|
-
*
|
|
3224
|
-
*
|
|
3225
|
-
|
|
3226
|
-
|
|
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
|
-
|
|
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 ===
|
|
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 ===
|
|
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 !==
|
|
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 !==
|
|
4073
|
+
if (this.stage !== Stages.disconnected) {
|
|
3616
4074
|
return;
|
|
3617
4075
|
}
|
|
3618
|
-
this.stage =
|
|
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 =
|
|
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 !==
|
|
4139
|
+
if (this.stage !== Stages.connected) {
|
|
3673
4140
|
return;
|
|
3674
4141
|
}
|
|
3675
|
-
this.stage =
|
|
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 =
|
|
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 ===
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
3895
|
-
|
|
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
|
-
|
|
3902
|
-
this.needsHydration
|
|
3903
|
-
|
|
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 !==
|
|
4480
|
+
if (this.stage !== Stages.disconnected) {
|
|
3922
4481
|
return;
|
|
3923
4482
|
}
|
|
3924
|
-
|
|
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
|
-
(
|
|
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 =
|
|
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
|
|
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
|
-
|
|
6304
|
-
this.
|
|
6946
|
+
get dialogDescribedby() {
|
|
6947
|
+
if (this.dialog) {
|
|
6948
|
+
return this.ariaDescribedby;
|
|
6949
|
+
}
|
|
6305
6950
|
}
|
|
6306
|
-
|
|
6307
|
-
this.
|
|
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.
|
|
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
|
-
|
|
6604
|
-
if (
|
|
6605
|
-
return;
|
|
7277
|
+
get dialogDescribedby() {
|
|
7278
|
+
if (this.dialog) {
|
|
7279
|
+
return this.ariaDescribedby;
|
|
6606
7280
|
}
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
this.dialog.removeAttribute("aria-modal");
|
|
7281
|
+
}
|
|
7282
|
+
get dialogLabel() {
|
|
7283
|
+
if (this.dialog) {
|
|
7284
|
+
return this.ariaLabel;
|
|
6612
7285
|
}
|
|
6613
7286
|
}
|
|
6614
|
-
|
|
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
|
-
|
|
6618
|
-
|
|
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.
|
|
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:
|
|
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,
|