@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
|
@@ -75,6 +75,44 @@ switch (kernelMode) {
|
|
|
75
75
|
});
|
|
76
76
|
break;
|
|
77
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Warning and error messages.
|
|
80
|
+
* @internal
|
|
81
|
+
*/
|
|
82
|
+
var Message;
|
|
83
|
+
(function (Message) {
|
|
84
|
+
// 1000 - 1100 Kernel
|
|
85
|
+
// 1101 - 1200 Observation
|
|
86
|
+
Message[Message["needsArrayObservation"] = 1101] = "needsArrayObservation";
|
|
87
|
+
// 1201 - 1300 Templating
|
|
88
|
+
Message[Message["onlySetDOMPolicyOnce"] = 1201] = "onlySetDOMPolicyOnce";
|
|
89
|
+
Message[Message["bindingInnerHTMLRequiresTrustedTypes"] = 1202] = "bindingInnerHTMLRequiresTrustedTypes";
|
|
90
|
+
Message[Message["twoWayBindingRequiresObservables"] = 1203] = "twoWayBindingRequiresObservables";
|
|
91
|
+
Message[Message["hostBindingWithoutHost"] = 1204] = "hostBindingWithoutHost";
|
|
92
|
+
Message[Message["unsupportedBindingBehavior"] = 1205] = "unsupportedBindingBehavior";
|
|
93
|
+
Message[Message["directCallToHTMLTagNotAllowed"] = 1206] = "directCallToHTMLTagNotAllowed";
|
|
94
|
+
Message[Message["onlySetTemplatePolicyOnce"] = 1207] = "onlySetTemplatePolicyOnce";
|
|
95
|
+
Message[Message["cannotSetTemplatePolicyAfterCompilation"] = 1208] = "cannotSetTemplatePolicyAfterCompilation";
|
|
96
|
+
Message[Message["blockedByDOMPolicy"] = 1209] = "blockedByDOMPolicy";
|
|
97
|
+
// 1301 - 1400 Styles
|
|
98
|
+
// 1401 - 1500 Components
|
|
99
|
+
Message[Message["missingElementDefinition"] = 1401] = "missingElementDefinition";
|
|
100
|
+
// 1501 - 1600 Context and Dependency Injection
|
|
101
|
+
Message[Message["noRegistrationForContext"] = 1501] = "noRegistrationForContext";
|
|
102
|
+
Message[Message["noFactoryForResolver"] = 1502] = "noFactoryForResolver";
|
|
103
|
+
Message[Message["invalidResolverStrategy"] = 1503] = "invalidResolverStrategy";
|
|
104
|
+
Message[Message["cannotAutoregisterDependency"] = 1504] = "cannotAutoregisterDependency";
|
|
105
|
+
Message[Message["cannotResolveKey"] = 1505] = "cannotResolveKey";
|
|
106
|
+
Message[Message["cannotConstructNativeFunction"] = 1506] = "cannotConstructNativeFunction";
|
|
107
|
+
Message[Message["cannotJITRegisterNonConstructor"] = 1507] = "cannotJITRegisterNonConstructor";
|
|
108
|
+
Message[Message["cannotJITRegisterIntrinsic"] = 1508] = "cannotJITRegisterIntrinsic";
|
|
109
|
+
Message[Message["cannotJITRegisterInterface"] = 1509] = "cannotJITRegisterInterface";
|
|
110
|
+
Message[Message["invalidResolver"] = 1510] = "invalidResolver";
|
|
111
|
+
Message[Message["invalidKey"] = 1511] = "invalidKey";
|
|
112
|
+
Message[Message["noDefaultResolver"] = 1512] = "noDefaultResolver";
|
|
113
|
+
Message[Message["cyclicDependency"] = 1513] = "cyclicDependency";
|
|
114
|
+
Message[Message["connectUpdateRequiresController"] = 1514] = "connectUpdateRequiresController";
|
|
115
|
+
})(Message || (Message = {}));
|
|
78
116
|
/**
|
|
79
117
|
* Determines whether or not an object is a function.
|
|
80
118
|
* @public
|
|
@@ -120,6 +158,39 @@ var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof win
|
|
|
120
158
|
result.globalThis = result;
|
|
121
159
|
}
|
|
122
160
|
})();
|
|
161
|
+
(function requestIdleCallbackPolyfill() {
|
|
162
|
+
if ("requestIdleCallback" in globalThis) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* A polyfill for requestIdleCallback that falls back to setTimeout.
|
|
167
|
+
*
|
|
168
|
+
* @param callback - The function to call when the browser is idle.
|
|
169
|
+
* @param options - Options object that may contain a timeout property.
|
|
170
|
+
* @returns An ID that can be used to cancel the callback.
|
|
171
|
+
* @public
|
|
172
|
+
*/
|
|
173
|
+
globalThis.requestIdleCallback = function requestIdleCallback(callback, options) {
|
|
174
|
+
const start = Date.now();
|
|
175
|
+
return setTimeout(() => {
|
|
176
|
+
callback({
|
|
177
|
+
didTimeout: (options === null || options === void 0 ? void 0 : options.timeout)
|
|
178
|
+
? Date.now() - start >= options.timeout
|
|
179
|
+
: false,
|
|
180
|
+
timeRemaining: () => 0,
|
|
181
|
+
});
|
|
182
|
+
}, 1);
|
|
183
|
+
};
|
|
184
|
+
/**
|
|
185
|
+
* A polyfill for cancelIdleCallback that falls back to clearTimeout.
|
|
186
|
+
*
|
|
187
|
+
* @param id - The ID of the callback to cancel.
|
|
188
|
+
* @public
|
|
189
|
+
*/
|
|
190
|
+
globalThis.cancelIdleCallback = function cancelIdleCallback(id) {
|
|
191
|
+
clearTimeout(id);
|
|
192
|
+
};
|
|
193
|
+
})();
|
|
123
194
|
|
|
124
195
|
// ensure FAST global - duplicated debug.ts
|
|
125
196
|
const propConfig = {
|
|
@@ -284,7 +355,7 @@ const DOM = Object.freeze({
|
|
|
284
355
|
*/
|
|
285
356
|
setPolicy(value) {
|
|
286
357
|
if (defaultPolicy !== fastPolicy) {
|
|
287
|
-
throw FAST.error(
|
|
358
|
+
throw FAST.error(Message.onlySetDOMPolicyOnce);
|
|
288
359
|
}
|
|
289
360
|
defaultPolicy = value;
|
|
290
361
|
},
|
|
@@ -317,72 +388,6 @@ const DOM = Object.freeze({
|
|
|
317
388
|
},
|
|
318
389
|
});
|
|
319
390
|
|
|
320
|
-
/**
|
|
321
|
-
* The default UpdateQueue.
|
|
322
|
-
* @public
|
|
323
|
-
*/
|
|
324
|
-
const Updates = FAST.getById(KernelServiceId.updateQueue, () => {
|
|
325
|
-
const tasks = [];
|
|
326
|
-
const pendingErrors = [];
|
|
327
|
-
const rAF = globalThis.requestAnimationFrame;
|
|
328
|
-
let updateAsync = true;
|
|
329
|
-
function throwFirstError() {
|
|
330
|
-
if (pendingErrors.length) {
|
|
331
|
-
throw pendingErrors.shift();
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
function tryRunTask(task) {
|
|
335
|
-
try {
|
|
336
|
-
task.call();
|
|
337
|
-
}
|
|
338
|
-
catch (error) {
|
|
339
|
-
if (updateAsync) {
|
|
340
|
-
pendingErrors.push(error);
|
|
341
|
-
setTimeout(throwFirstError, 0);
|
|
342
|
-
}
|
|
343
|
-
else {
|
|
344
|
-
tasks.length = 0;
|
|
345
|
-
throw error;
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
function process() {
|
|
350
|
-
const capacity = 1024;
|
|
351
|
-
let index = 0;
|
|
352
|
-
while (index < tasks.length) {
|
|
353
|
-
tryRunTask(tasks[index]);
|
|
354
|
-
index++;
|
|
355
|
-
// Prevent leaking memory for long chains of recursive calls to `enqueue`.
|
|
356
|
-
// If we call `enqueue` within a task scheduled by `enqueue`, the queue will
|
|
357
|
-
// grow, but to avoid an O(n) walk for every task we execute, we don't
|
|
358
|
-
// shift tasks off the queue after they have been executed.
|
|
359
|
-
// Instead, we periodically shift 1024 tasks off the queue.
|
|
360
|
-
if (index > capacity) {
|
|
361
|
-
// Manually shift all values starting at the index back to the
|
|
362
|
-
// beginning of the queue.
|
|
363
|
-
for (let scan = 0, newLength = tasks.length - index; scan < newLength; scan++) {
|
|
364
|
-
tasks[scan] = tasks[scan + index];
|
|
365
|
-
}
|
|
366
|
-
tasks.length -= index;
|
|
367
|
-
index = 0;
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
tasks.length = 0;
|
|
371
|
-
}
|
|
372
|
-
function enqueue(callable) {
|
|
373
|
-
tasks.push(callable);
|
|
374
|
-
if (tasks.length < 2) {
|
|
375
|
-
updateAsync ? rAF(process) : process();
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
return Object.freeze({
|
|
379
|
-
enqueue,
|
|
380
|
-
next: () => new Promise(enqueue),
|
|
381
|
-
process,
|
|
382
|
-
setMode: (isAsync) => (updateAsync = isAsync),
|
|
383
|
-
});
|
|
384
|
-
});
|
|
385
|
-
|
|
386
391
|
/**
|
|
387
392
|
* An implementation of {@link Notifier} that efficiently keeps track of
|
|
388
393
|
* subscribers interested in a specific change notification on an
|
|
@@ -548,6 +553,72 @@ class PropertyChangeNotifier {
|
|
|
548
553
|
}
|
|
549
554
|
}
|
|
550
555
|
|
|
556
|
+
/**
|
|
557
|
+
* The default UpdateQueue.
|
|
558
|
+
* @public
|
|
559
|
+
*/
|
|
560
|
+
const Updates = FAST.getById(KernelServiceId.updateQueue, () => {
|
|
561
|
+
const tasks = [];
|
|
562
|
+
const pendingErrors = [];
|
|
563
|
+
const rAF = globalThis.requestAnimationFrame;
|
|
564
|
+
let updateAsync = true;
|
|
565
|
+
function throwFirstError() {
|
|
566
|
+
if (pendingErrors.length) {
|
|
567
|
+
throw pendingErrors.shift();
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function tryRunTask(task) {
|
|
571
|
+
try {
|
|
572
|
+
task.call();
|
|
573
|
+
}
|
|
574
|
+
catch (error) {
|
|
575
|
+
if (updateAsync) {
|
|
576
|
+
pendingErrors.push(error);
|
|
577
|
+
setTimeout(throwFirstError, 0);
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
tasks.length = 0;
|
|
581
|
+
throw error;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
function process() {
|
|
586
|
+
const capacity = 1024;
|
|
587
|
+
let index = 0;
|
|
588
|
+
while (index < tasks.length) {
|
|
589
|
+
tryRunTask(tasks[index]);
|
|
590
|
+
index++;
|
|
591
|
+
// Prevent leaking memory for long chains of recursive calls to `enqueue`.
|
|
592
|
+
// If we call `enqueue` within a task scheduled by `enqueue`, the queue will
|
|
593
|
+
// grow, but to avoid an O(n) walk for every task we execute, we don't
|
|
594
|
+
// shift tasks off the queue after they have been executed.
|
|
595
|
+
// Instead, we periodically shift 1024 tasks off the queue.
|
|
596
|
+
if (index > capacity) {
|
|
597
|
+
// Manually shift all values starting at the index back to the
|
|
598
|
+
// beginning of the queue.
|
|
599
|
+
for (let scan = 0, newLength = tasks.length - index; scan < newLength; scan++) {
|
|
600
|
+
tasks[scan] = tasks[scan + index];
|
|
601
|
+
}
|
|
602
|
+
tasks.length -= index;
|
|
603
|
+
index = 0;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
tasks.length = 0;
|
|
607
|
+
}
|
|
608
|
+
function enqueue(callable) {
|
|
609
|
+
tasks.push(callable);
|
|
610
|
+
if (tasks.length < 2) {
|
|
611
|
+
updateAsync ? rAF(process) : process();
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return Object.freeze({
|
|
615
|
+
enqueue,
|
|
616
|
+
next: () => new Promise(enqueue),
|
|
617
|
+
process,
|
|
618
|
+
setMode: (isAsync) => (updateAsync = isAsync),
|
|
619
|
+
});
|
|
620
|
+
});
|
|
621
|
+
|
|
551
622
|
/**
|
|
552
623
|
* Describes how the source's lifetime relates to its controller's lifetime.
|
|
553
624
|
* @public
|
|
@@ -573,7 +644,7 @@ const Observable = FAST.getById(KernelServiceId.observable, () => {
|
|
|
573
644
|
const notifierLookup = new WeakMap();
|
|
574
645
|
let watcher = void 0;
|
|
575
646
|
let createArrayObserver = (array) => {
|
|
576
|
-
throw FAST.error(
|
|
647
|
+
throw FAST.error(Message.needsArrayObservation);
|
|
577
648
|
};
|
|
578
649
|
function getNotifier(source) {
|
|
579
650
|
var _a;
|
|
@@ -939,97 +1010,6 @@ function oneTime(expression, policy) {
|
|
|
939
1010
|
return new OneTimeBinding(expression, policy);
|
|
940
1011
|
}
|
|
941
1012
|
|
|
942
|
-
let DefaultStyleStrategy;
|
|
943
|
-
function reduceStyles(styles) {
|
|
944
|
-
return styles
|
|
945
|
-
.map((x) => x instanceof ElementStyles ? reduceStyles(x.styles) : [x])
|
|
946
|
-
.reduce((prev, curr) => prev.concat(curr), []);
|
|
947
|
-
}
|
|
948
|
-
/**
|
|
949
|
-
* Represents styles that can be applied to a custom element.
|
|
950
|
-
* @public
|
|
951
|
-
*/
|
|
952
|
-
class ElementStyles {
|
|
953
|
-
/**
|
|
954
|
-
* Creates an instance of ElementStyles.
|
|
955
|
-
* @param styles - The styles that will be associated with elements.
|
|
956
|
-
*/
|
|
957
|
-
constructor(styles) {
|
|
958
|
-
this.styles = styles;
|
|
959
|
-
this.targets = new WeakSet();
|
|
960
|
-
this._strategy = null;
|
|
961
|
-
this.behaviors = styles
|
|
962
|
-
.map((x) => x instanceof ElementStyles ? x.behaviors : null)
|
|
963
|
-
.reduce((prev, curr) => (curr === null ? prev : prev === null ? curr : prev.concat(curr)), null);
|
|
964
|
-
}
|
|
965
|
-
/**
|
|
966
|
-
* Gets the StyleStrategy associated with these element styles.
|
|
967
|
-
*/
|
|
968
|
-
get strategy() {
|
|
969
|
-
if (this._strategy === null) {
|
|
970
|
-
this.withStrategy(DefaultStyleStrategy);
|
|
971
|
-
}
|
|
972
|
-
return this._strategy;
|
|
973
|
-
}
|
|
974
|
-
/** @internal */
|
|
975
|
-
addStylesTo(target) {
|
|
976
|
-
this.strategy.addStylesTo(target);
|
|
977
|
-
this.targets.add(target);
|
|
978
|
-
}
|
|
979
|
-
/** @internal */
|
|
980
|
-
removeStylesFrom(target) {
|
|
981
|
-
this.strategy.removeStylesFrom(target);
|
|
982
|
-
this.targets.delete(target);
|
|
983
|
-
}
|
|
984
|
-
/** @internal */
|
|
985
|
-
isAttachedTo(target) {
|
|
986
|
-
return this.targets.has(target);
|
|
987
|
-
}
|
|
988
|
-
/**
|
|
989
|
-
* Associates behaviors with this set of styles.
|
|
990
|
-
* @param behaviors - The behaviors to associate.
|
|
991
|
-
*/
|
|
992
|
-
withBehaviors(...behaviors) {
|
|
993
|
-
this.behaviors =
|
|
994
|
-
this.behaviors === null ? behaviors : this.behaviors.concat(behaviors);
|
|
995
|
-
return this;
|
|
996
|
-
}
|
|
997
|
-
/**
|
|
998
|
-
* Sets the strategy that handles adding/removing these styles for an element.
|
|
999
|
-
* @param strategy - The strategy to use.
|
|
1000
|
-
*/
|
|
1001
|
-
withStrategy(Strategy) {
|
|
1002
|
-
this._strategy = new Strategy(reduceStyles(this.styles));
|
|
1003
|
-
return this;
|
|
1004
|
-
}
|
|
1005
|
-
/**
|
|
1006
|
-
* Sets the default strategy type to use when creating style strategies.
|
|
1007
|
-
* @param Strategy - The strategy type to construct.
|
|
1008
|
-
*/
|
|
1009
|
-
static setDefaultStrategy(Strategy) {
|
|
1010
|
-
DefaultStyleStrategy = Strategy;
|
|
1011
|
-
}
|
|
1012
|
-
/**
|
|
1013
|
-
* Normalizes a set of composable style options.
|
|
1014
|
-
* @param styles - The style options to normalize.
|
|
1015
|
-
* @returns A singular ElementStyles instance or undefined.
|
|
1016
|
-
*/
|
|
1017
|
-
static normalize(styles) {
|
|
1018
|
-
return styles === void 0
|
|
1019
|
-
? void 0
|
|
1020
|
-
: Array.isArray(styles)
|
|
1021
|
-
? new ElementStyles(styles)
|
|
1022
|
-
: styles instanceof ElementStyles
|
|
1023
|
-
? styles
|
|
1024
|
-
: new ElementStyles([styles]);
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
/**
|
|
1028
|
-
* Indicates whether the DOM supports the adoptedStyleSheets feature.
|
|
1029
|
-
*/
|
|
1030
|
-
ElementStyles.supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
|
|
1031
|
-
"replace" in CSSStyleSheet.prototype;
|
|
1032
|
-
|
|
1033
1013
|
const registry$1 = createTypeRegistry();
|
|
1034
1014
|
/**
|
|
1035
1015
|
* Instructs the css engine to provide dynamic styles or
|
|
@@ -1133,22 +1113,113 @@ class CSSBindingDirective {
|
|
|
1133
1113
|
}
|
|
1134
1114
|
CSSDirective.define(CSSBindingDirective);
|
|
1135
1115
|
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1116
|
+
let DefaultStyleStrategy;
|
|
1117
|
+
function reduceStyles(styles) {
|
|
1118
|
+
return styles
|
|
1119
|
+
.map((x) => x instanceof ElementStyles ? reduceStyles(x.styles) : [x])
|
|
1120
|
+
.reduce((prev, curr) => prev.concat(curr), []);
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* Represents styles that can be applied to a custom element.
|
|
1124
|
+
* @public
|
|
1125
|
+
*/
|
|
1126
|
+
class ElementStyles {
|
|
1127
|
+
/**
|
|
1128
|
+
* Gets the StyleStrategy associated with these element styles.
|
|
1129
|
+
*/
|
|
1130
|
+
get strategy() {
|
|
1131
|
+
if (this._strategy === null) {
|
|
1132
|
+
this.withStrategy(DefaultStyleStrategy);
|
|
1133
|
+
}
|
|
1134
|
+
return this._strategy;
|
|
1135
|
+
}
|
|
1136
|
+
/**
|
|
1137
|
+
* Creates an instance of ElementStyles.
|
|
1138
|
+
* @param styles - The styles that will be associated with elements.
|
|
1139
|
+
*/
|
|
1140
|
+
constructor(styles) {
|
|
1141
|
+
this.styles = styles;
|
|
1142
|
+
this.targets = new WeakSet();
|
|
1143
|
+
this._strategy = null;
|
|
1144
|
+
this.behaviors = styles
|
|
1145
|
+
.map((x) => x instanceof ElementStyles ? x.behaviors : null)
|
|
1146
|
+
.reduce((prev, curr) => (curr === null ? prev : prev === null ? curr : prev.concat(curr)), null);
|
|
1147
|
+
}
|
|
1148
|
+
/** @internal */
|
|
1149
|
+
addStylesTo(target) {
|
|
1150
|
+
this.strategy.addStylesTo(target);
|
|
1151
|
+
this.targets.add(target);
|
|
1152
|
+
}
|
|
1153
|
+
/** @internal */
|
|
1154
|
+
removeStylesFrom(target) {
|
|
1155
|
+
this.strategy.removeStylesFrom(target);
|
|
1156
|
+
this.targets.delete(target);
|
|
1157
|
+
}
|
|
1158
|
+
/** @internal */
|
|
1159
|
+
isAttachedTo(target) {
|
|
1160
|
+
return this.targets.has(target);
|
|
1161
|
+
}
|
|
1162
|
+
/**
|
|
1163
|
+
* Associates behaviors with this set of styles.
|
|
1164
|
+
* @param behaviors - The behaviors to associate.
|
|
1165
|
+
*/
|
|
1166
|
+
withBehaviors(...behaviors) {
|
|
1167
|
+
this.behaviors =
|
|
1168
|
+
this.behaviors === null ? behaviors : this.behaviors.concat(behaviors);
|
|
1169
|
+
return this;
|
|
1170
|
+
}
|
|
1171
|
+
/**
|
|
1172
|
+
* Sets the strategy that handles adding/removing these styles for an element.
|
|
1173
|
+
* @param strategy - The strategy to use.
|
|
1174
|
+
*/
|
|
1175
|
+
withStrategy(Strategy) {
|
|
1176
|
+
this._strategy = new Strategy(reduceStyles(this.styles));
|
|
1177
|
+
return this;
|
|
1178
|
+
}
|
|
1179
|
+
/**
|
|
1180
|
+
* Sets the default strategy type to use when creating style strategies.
|
|
1181
|
+
* @param Strategy - The strategy type to construct.
|
|
1182
|
+
*/
|
|
1183
|
+
static setDefaultStrategy(Strategy) {
|
|
1184
|
+
DefaultStyleStrategy = Strategy;
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* Normalizes a set of composable style options.
|
|
1188
|
+
* @param styles - The style options to normalize.
|
|
1189
|
+
* @returns A singular ElementStyles instance or undefined.
|
|
1190
|
+
*/
|
|
1191
|
+
static normalize(styles) {
|
|
1192
|
+
return styles === void 0
|
|
1193
|
+
? void 0
|
|
1194
|
+
: Array.isArray(styles)
|
|
1195
|
+
? new ElementStyles(styles)
|
|
1196
|
+
: styles instanceof ElementStyles
|
|
1197
|
+
? styles
|
|
1198
|
+
: new ElementStyles([styles]);
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
/**
|
|
1202
|
+
* Indicates whether the DOM supports the adoptedStyleSheets feature.
|
|
1203
|
+
*/
|
|
1204
|
+
ElementStyles.supportsAdoptedStyleSheets = Array.isArray(document.adoptedStyleSheets) &&
|
|
1205
|
+
"replace" in CSSStyleSheet.prototype;
|
|
1206
|
+
|
|
1207
|
+
const marker$1 = `${Math.random().toString(36).substring(2, 8)}`;
|
|
1208
|
+
let varId = 0;
|
|
1209
|
+
const nextCSSVariable = () => `--v${marker$1}${++varId}`;
|
|
1210
|
+
function collectStyles(strings, values) {
|
|
1211
|
+
const styles = [];
|
|
1212
|
+
let cssString = "";
|
|
1213
|
+
const behaviors = [];
|
|
1214
|
+
const add = (behavior) => {
|
|
1215
|
+
behaviors.push(behavior);
|
|
1216
|
+
};
|
|
1217
|
+
for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
|
|
1218
|
+
cssString += strings[i];
|
|
1219
|
+
let value = values[i];
|
|
1220
|
+
if (isFunction(value)) {
|
|
1221
|
+
value = new CSSBindingDirective(oneWay(value), nextCSSVariable()).createCSS(add);
|
|
1222
|
+
}
|
|
1152
1223
|
else if (value instanceof Binding) {
|
|
1153
1224
|
value = new CSSBindingDirective(value, nextCSSVariable()).createCSS(add);
|
|
1154
1225
|
}
|
|
@@ -1225,111 +1296,12 @@ css.partial = (strings, ...values) => {
|
|
|
1225
1296
|
return new CSSPartial(styles, behaviors);
|
|
1226
1297
|
};
|
|
1227
1298
|
|
|
1228
|
-
const bindingStartMarker = /fe-b\$\$start\$\$(\d+)\$\$(.+)\$\$fe-b/;
|
|
1229
|
-
const bindingEndMarker = /fe-b\$\$end\$\$(\d+)\$\$(.+)\$\$fe-b/;
|
|
1230
|
-
const repeatViewStartMarker = /fe-repeat\$\$start\$\$(\d+)\$\$fe-repeat/;
|
|
1231
|
-
const repeatViewEndMarker = /fe-repeat\$\$end\$\$(\d+)\$\$fe-repeat/;
|
|
1232
|
-
const elementBoundaryStartMarker = /^(?:.{0,1000})fe-eb\$\$start\$\$(.+?)\$\$fe-eb/;
|
|
1233
|
-
const elementBoundaryEndMarker = /fe-eb\$\$end\$\$(.{0,1000})\$\$fe-eb(?:.{0,1000})$/;
|
|
1234
|
-
function isComment$1(node) {
|
|
1235
|
-
return node && node.nodeType === Node.COMMENT_NODE;
|
|
1236
|
-
}
|
|
1237
|
-
/**
|
|
1238
|
-
* Markup utilities to aid in template hydration.
|
|
1239
|
-
* @internal
|
|
1240
|
-
*/
|
|
1241
|
-
const HydrationMarkup = Object.freeze({
|
|
1242
|
-
attributeMarkerName: "data-fe-b",
|
|
1243
|
-
attributeBindingSeparator: " ",
|
|
1244
|
-
contentBindingStartMarker(index, uniqueId) {
|
|
1245
|
-
return `fe-b$$start$$${index}$$${uniqueId}$$fe-b`;
|
|
1246
|
-
},
|
|
1247
|
-
contentBindingEndMarker(index, uniqueId) {
|
|
1248
|
-
return `fe-b$$end$$${index}$$${uniqueId}$$fe-b`;
|
|
1249
|
-
},
|
|
1250
|
-
repeatStartMarker(index) {
|
|
1251
|
-
return `fe-repeat$$start$$${index}$$fe-repeat`;
|
|
1252
|
-
},
|
|
1253
|
-
repeatEndMarker(index) {
|
|
1254
|
-
return `fe-repeat$$end$$${index}$$fe-repeat`;
|
|
1255
|
-
},
|
|
1256
|
-
isContentBindingStartMarker(content) {
|
|
1257
|
-
return bindingStartMarker.test(content);
|
|
1258
|
-
},
|
|
1259
|
-
isContentBindingEndMarker(content) {
|
|
1260
|
-
return bindingEndMarker.test(content);
|
|
1261
|
-
},
|
|
1262
|
-
isRepeatViewStartMarker(content) {
|
|
1263
|
-
return repeatViewStartMarker.test(content);
|
|
1264
|
-
},
|
|
1265
|
-
isRepeatViewEndMarker(content) {
|
|
1266
|
-
return repeatViewEndMarker.test(content);
|
|
1267
|
-
},
|
|
1268
|
-
isElementBoundaryStartMarker(node) {
|
|
1269
|
-
return isComment$1(node) && elementBoundaryStartMarker.test(node.data.trim());
|
|
1270
|
-
},
|
|
1271
|
-
isElementBoundaryEndMarker(node) {
|
|
1272
|
-
return isComment$1(node) && elementBoundaryEndMarker.test(node.data);
|
|
1273
|
-
},
|
|
1274
|
-
/**
|
|
1275
|
-
* Returns the indexes of the ViewBehaviorFactories affecting
|
|
1276
|
-
* attributes for the element, or null if no factories were found.
|
|
1277
|
-
*/
|
|
1278
|
-
parseAttributeBinding(node) {
|
|
1279
|
-
const attr = node.getAttribute(this.attributeMarkerName);
|
|
1280
|
-
return attr === null
|
|
1281
|
-
? attr
|
|
1282
|
-
: attr.split(this.attributeBindingSeparator).map(i => parseInt(i));
|
|
1283
|
-
},
|
|
1284
|
-
/**
|
|
1285
|
-
* Parses the ViewBehaviorFactory index from string data. Returns
|
|
1286
|
-
* the binding index or null if the index cannot be retrieved.
|
|
1287
|
-
*/
|
|
1288
|
-
parseContentBindingStartMarker(content) {
|
|
1289
|
-
return parseIndexAndIdMarker(bindingStartMarker, content);
|
|
1290
|
-
},
|
|
1291
|
-
parseContentBindingEndMarker(content) {
|
|
1292
|
-
return parseIndexAndIdMarker(bindingEndMarker, content);
|
|
1293
|
-
},
|
|
1294
|
-
/**
|
|
1295
|
-
* Parses the index of a repeat directive from a content string.
|
|
1296
|
-
*/
|
|
1297
|
-
parseRepeatStartMarker(content) {
|
|
1298
|
-
return parseIntMarker(repeatViewStartMarker, content);
|
|
1299
|
-
},
|
|
1300
|
-
parseRepeatEndMarker(content) {
|
|
1301
|
-
return parseIntMarker(repeatViewEndMarker, content);
|
|
1302
|
-
},
|
|
1303
|
-
/**
|
|
1304
|
-
* Parses element Id from element boundary markers
|
|
1305
|
-
*/
|
|
1306
|
-
parseElementBoundaryStartMarker(content) {
|
|
1307
|
-
return parseStringMarker(elementBoundaryStartMarker, content.trim());
|
|
1308
|
-
},
|
|
1309
|
-
parseElementBoundaryEndMarker(content) {
|
|
1310
|
-
return parseStringMarker(elementBoundaryEndMarker, content);
|
|
1311
|
-
},
|
|
1312
|
-
});
|
|
1313
|
-
function parseIntMarker(regex, content) {
|
|
1314
|
-
const match = regex.exec(content);
|
|
1315
|
-
return match === null ? match : parseInt(match[1]);
|
|
1316
|
-
}
|
|
1317
|
-
function parseStringMarker(regex, content) {
|
|
1318
|
-
const match = regex.exec(content);
|
|
1319
|
-
return match === null ? match : match[1];
|
|
1320
|
-
}
|
|
1321
|
-
function parseIndexAndIdMarker(regex, content) {
|
|
1322
|
-
const match = regex.exec(content);
|
|
1323
|
-
return match === null ? match : [parseInt(match[1]), match[2]];
|
|
1324
|
-
}
|
|
1325
1299
|
/**
|
|
1326
|
-
*
|
|
1300
|
+
* A unique per-session random marker string used to create placeholder tokens in HTML.
|
|
1301
|
+
* Bindings embedded in template literals are replaced with interpolation markers
|
|
1302
|
+
* of the form `fast-xxxxxx{id}fast-xxxxxx` so the compiler can later locate them in the
|
|
1303
|
+
* parsed DOM and associate each marker with its ViewBehaviorFactory.
|
|
1327
1304
|
*/
|
|
1328
|
-
const Hydratable = Symbol.for("fe-hydration");
|
|
1329
|
-
function isHydratable(value) {
|
|
1330
|
-
return value[Hydratable] === Hydratable;
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
1305
|
const marker = `fast-${Math.random().toString(36).substring(2, 8)}`;
|
|
1334
1306
|
const interpolationStart = `${marker}{`;
|
|
1335
1307
|
const interpolationEnd = `}${marker}`;
|
|
@@ -1381,6 +1353,8 @@ const Parser = Object.freeze({
|
|
|
1381
1353
|
* directives or null if no directives are found in the string.
|
|
1382
1354
|
*/
|
|
1383
1355
|
parse(value, factories) {
|
|
1356
|
+
// Split on the interpolation start marker. If there's only one part,
|
|
1357
|
+
// no placeholders exist and we return null to signal "no directives here."
|
|
1384
1358
|
const parts = value.split(interpolationStart);
|
|
1385
1359
|
if (parts.length === 1) {
|
|
1386
1360
|
return null;
|
|
@@ -1434,7 +1408,13 @@ const HTMLDirective = Object.freeze({
|
|
|
1434
1408
|
return type;
|
|
1435
1409
|
},
|
|
1436
1410
|
/**
|
|
1437
|
-
*
|
|
1411
|
+
* Determines the DOM aspect type for a directive based on attribute name prefix.
|
|
1412
|
+
* The prefix convention maps to aspect types as follows:
|
|
1413
|
+
* - No prefix (e.g. "class") → DOMAspect.attribute
|
|
1414
|
+
* - ":" prefix (e.g. ":value") → DOMAspect.property (":classList" → DOMAspect.tokenList)
|
|
1415
|
+
* - "?" prefix (e.g. "?disabled") → DOMAspect.booleanAttribute
|
|
1416
|
+
* - `@` prefix (e.g. `@click`) → DOMAspect.event
|
|
1417
|
+
* - Falsy or absent value → DOMAspect.content (see remarks)
|
|
1438
1418
|
* @param directive - The directive to assign the aspect to.
|
|
1439
1419
|
* @param value - The value to base the aspect determination on.
|
|
1440
1420
|
* @remarks
|
|
@@ -1500,21 +1480,294 @@ class StatelessAttachedAttributeDirective {
|
|
|
1500
1480
|
}
|
|
1501
1481
|
makeSerializationNoop(StatelessAttachedAttributeDirective);
|
|
1502
1482
|
|
|
1503
|
-
|
|
1504
|
-
|
|
1483
|
+
const selectElements = (value) => value.nodeType === 1;
|
|
1484
|
+
/**
|
|
1485
|
+
* Creates a function that can be used to filter a Node array, selecting only elements.
|
|
1486
|
+
* @param selector - An optional selector to restrict the filter to.
|
|
1487
|
+
* @public
|
|
1488
|
+
*/
|
|
1489
|
+
const elements = (selector) => selector
|
|
1490
|
+
? value => value.nodeType === 1 && value.matches(selector)
|
|
1491
|
+
: selectElements;
|
|
1492
|
+
/**
|
|
1493
|
+
* A base class for node observation.
|
|
1494
|
+
* @public
|
|
1495
|
+
* @remarks
|
|
1496
|
+
* Internally used by the SlottedDirective and the ChildrenDirective.
|
|
1497
|
+
*/
|
|
1498
|
+
class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
1505
1499
|
/**
|
|
1506
|
-
* The
|
|
1500
|
+
* The unique id of the factory.
|
|
1507
1501
|
*/
|
|
1508
|
-
|
|
1502
|
+
get id() {
|
|
1503
|
+
return this._id;
|
|
1504
|
+
}
|
|
1505
|
+
set id(value) {
|
|
1506
|
+
this._id = value;
|
|
1507
|
+
this._controllerProperty = `${value}-c`;
|
|
1508
|
+
}
|
|
1509
1509
|
/**
|
|
1510
|
-
*
|
|
1510
|
+
* Bind this behavior to the source.
|
|
1511
|
+
* @param source - The source to bind to.
|
|
1512
|
+
* @param context - The execution context that the binding is operating within.
|
|
1513
|
+
* @param targets - The targets that behaviors in a view can attach to.
|
|
1511
1514
|
*/
|
|
1512
|
-
|
|
1515
|
+
bind(controller) {
|
|
1516
|
+
const target = controller.targets[this.targetNodeId];
|
|
1517
|
+
target[this._controllerProperty] = controller;
|
|
1518
|
+
this.updateTarget(controller.source, this.computeNodes(target));
|
|
1519
|
+
this.observe(target);
|
|
1520
|
+
controller.onUnbind(this);
|
|
1521
|
+
}
|
|
1513
1522
|
/**
|
|
1514
|
-
*
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1523
|
+
* Unbinds this behavior from the source.
|
|
1524
|
+
* @param source - The source to unbind from.
|
|
1525
|
+
* @param context - The execution context that the binding is operating within.
|
|
1526
|
+
* @param targets - The targets that behaviors in a view can attach to.
|
|
1527
|
+
*/
|
|
1528
|
+
unbind(controller) {
|
|
1529
|
+
const target = controller.targets[this.targetNodeId];
|
|
1530
|
+
this.updateTarget(controller.source, emptyArray);
|
|
1531
|
+
this.disconnect(target);
|
|
1532
|
+
target[this._controllerProperty] = null;
|
|
1533
|
+
}
|
|
1534
|
+
/**
|
|
1535
|
+
* Gets the data source for the target.
|
|
1536
|
+
* @param target - The target to get the source for.
|
|
1537
|
+
* @returns The source.
|
|
1538
|
+
*/
|
|
1539
|
+
getSource(target) {
|
|
1540
|
+
return target[this._controllerProperty].source;
|
|
1541
|
+
}
|
|
1542
|
+
/**
|
|
1543
|
+
* Updates the source property with the computed nodes.
|
|
1544
|
+
* @param source - The source object to assign the nodes property to.
|
|
1545
|
+
* @param value - The nodes to assign to the source object property.
|
|
1546
|
+
*/
|
|
1547
|
+
updateTarget(source, value) {
|
|
1548
|
+
source[this.options.property] = value;
|
|
1549
|
+
}
|
|
1550
|
+
/**
|
|
1551
|
+
* Computes the set of nodes that should be assigned to the source property.
|
|
1552
|
+
* @param target - The target to compute the nodes for.
|
|
1553
|
+
* @returns The computed nodes.
|
|
1554
|
+
* @remarks
|
|
1555
|
+
* Applies filters if provided.
|
|
1556
|
+
*/
|
|
1557
|
+
computeNodes(target) {
|
|
1558
|
+
let nodes = this.getNodes(target);
|
|
1559
|
+
if ("filter" in this.options) {
|
|
1560
|
+
nodes = nodes.filter(this.options.filter);
|
|
1561
|
+
}
|
|
1562
|
+
return nodes;
|
|
1563
|
+
}
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
/**
|
|
1567
|
+
* Regex patterns for parsing hydration markers embedded as HTML comments by the SSR renderer.
|
|
1568
|
+
* Each marker type encodes factory indices so the client can map markers back to ViewBehaviorFactories.
|
|
1569
|
+
*
|
|
1570
|
+
* Content binding markers bracket text/template content:
|
|
1571
|
+
* <!-- fe-b$$start$$<factoryIndex>$$<uniqueId>$$fe-b -->
|
|
1572
|
+
* ...content...
|
|
1573
|
+
* <!-- fe-b$$end$$<factoryIndex>$$<uniqueId>$$fe-b -->
|
|
1574
|
+
*
|
|
1575
|
+
* Repeat markers bracket each repeated item:
|
|
1576
|
+
* <!-- fe-repeat$$start$$<itemIndex>$$fe-repeat -->
|
|
1577
|
+
* <!-- fe-repeat$$end$$<itemIndex>$$fe-repeat -->
|
|
1578
|
+
*
|
|
1579
|
+
* Element boundary markers demarcate nested custom elements so parent walkers can skip them:
|
|
1580
|
+
* <!-- fe-eb$$start$$<elementId>$$fe-eb -->
|
|
1581
|
+
* <!-- fe-eb$$end$$<elementId>$$fe-eb -->
|
|
1582
|
+
*/
|
|
1583
|
+
const bindingStartMarker = /fe-b\$\$start\$\$(\d+)\$\$(.+)\$\$fe-b/;
|
|
1584
|
+
const bindingEndMarker = /fe-b\$\$end\$\$(\d+)\$\$(.+)\$\$fe-b/;
|
|
1585
|
+
const repeatViewStartMarker = /fe-repeat\$\$start\$\$(\d+)\$\$fe-repeat/;
|
|
1586
|
+
const repeatViewEndMarker = /fe-repeat\$\$end\$\$(\d+)\$\$fe-repeat/;
|
|
1587
|
+
const elementBoundaryStartMarker = /^(?:.{0,1000})fe-eb\$\$start\$\$(.+?)\$\$fe-eb/;
|
|
1588
|
+
const elementBoundaryEndMarker = /fe-eb\$\$end\$\$(.{0,1000})\$\$fe-eb(?:.{0,1000})$/;
|
|
1589
|
+
function isComment$1(node) {
|
|
1590
|
+
return node && node.nodeType === Node.COMMENT_NODE;
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* Markup utilities to aid in template hydration.
|
|
1594
|
+
* @internal
|
|
1595
|
+
*/
|
|
1596
|
+
const HydrationMarkup = Object.freeze({
|
|
1597
|
+
attributeMarkerName: "data-fe-b",
|
|
1598
|
+
compactAttributeMarkerName: "data-fe-c",
|
|
1599
|
+
attributeBindingSeparator: " ",
|
|
1600
|
+
contentBindingStartMarker(index, uniqueId) {
|
|
1601
|
+
return `fe-b$$start$$${index}$$${uniqueId}$$fe-b`;
|
|
1602
|
+
},
|
|
1603
|
+
contentBindingEndMarker(index, uniqueId) {
|
|
1604
|
+
return `fe-b$$end$$${index}$$${uniqueId}$$fe-b`;
|
|
1605
|
+
},
|
|
1606
|
+
repeatStartMarker(index) {
|
|
1607
|
+
return `fe-repeat$$start$$${index}$$fe-repeat`;
|
|
1608
|
+
},
|
|
1609
|
+
repeatEndMarker(index) {
|
|
1610
|
+
return `fe-repeat$$end$$${index}$$fe-repeat`;
|
|
1611
|
+
},
|
|
1612
|
+
isContentBindingStartMarker(content) {
|
|
1613
|
+
return bindingStartMarker.test(content);
|
|
1614
|
+
},
|
|
1615
|
+
isContentBindingEndMarker(content) {
|
|
1616
|
+
return bindingEndMarker.test(content);
|
|
1617
|
+
},
|
|
1618
|
+
isRepeatViewStartMarker(content) {
|
|
1619
|
+
return repeatViewStartMarker.test(content);
|
|
1620
|
+
},
|
|
1621
|
+
isRepeatViewEndMarker(content) {
|
|
1622
|
+
return repeatViewEndMarker.test(content);
|
|
1623
|
+
},
|
|
1624
|
+
isElementBoundaryStartMarker(node) {
|
|
1625
|
+
return isComment$1(node) && elementBoundaryStartMarker.test(node.data.trim());
|
|
1626
|
+
},
|
|
1627
|
+
isElementBoundaryEndMarker(node) {
|
|
1628
|
+
return isComment$1(node) && elementBoundaryEndMarker.test(node.data);
|
|
1629
|
+
},
|
|
1630
|
+
/**
|
|
1631
|
+
* Returns the indexes of the ViewBehaviorFactories affecting
|
|
1632
|
+
* attributes for the element, or null if no factories were found.
|
|
1633
|
+
*
|
|
1634
|
+
* This method parses the space-separated format: `data-fe-b="0 1 2"`.
|
|
1635
|
+
*/
|
|
1636
|
+
parseAttributeBinding(node) {
|
|
1637
|
+
const attr = node.getAttribute(this.attributeMarkerName);
|
|
1638
|
+
return attr === null
|
|
1639
|
+
? attr
|
|
1640
|
+
: attr.split(this.attributeBindingSeparator).map(i => parseInt(i));
|
|
1641
|
+
},
|
|
1642
|
+
/**
|
|
1643
|
+
* Returns the indexes of the ViewBehaviorFactories affecting
|
|
1644
|
+
* attributes for the element, or null if no factories were found.
|
|
1645
|
+
*
|
|
1646
|
+
* This method parses the enumerated format: `data-fe-b-0`, `data-fe-b-1`, `data-fe-b-2`.
|
|
1647
|
+
* This is an alternative format that uses separate attributes for each binding index.
|
|
1648
|
+
*/
|
|
1649
|
+
parseEnumeratedAttributeBinding(node) {
|
|
1650
|
+
const attrs = [];
|
|
1651
|
+
const prefixLength = this.attributeMarkerName.length + 1;
|
|
1652
|
+
const prefix = `${this.attributeMarkerName}-`;
|
|
1653
|
+
for (const attr of node.getAttributeNames()) {
|
|
1654
|
+
if (attr.startsWith(prefix)) {
|
|
1655
|
+
const count = Number(attr.slice(prefixLength));
|
|
1656
|
+
if (!Number.isNaN(count)) {
|
|
1657
|
+
attrs.push(count);
|
|
1658
|
+
}
|
|
1659
|
+
else {
|
|
1660
|
+
throw FAST.error(1601 /* invalidAttributeMarkerName */, {
|
|
1661
|
+
name: attr,
|
|
1662
|
+
expectedFormat: `${prefix}<number>`,
|
|
1663
|
+
});
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
}
|
|
1667
|
+
return attrs.length === 0 ? null : attrs;
|
|
1668
|
+
},
|
|
1669
|
+
/**
|
|
1670
|
+
* Returns the indexes of the ViewBehaviorFactories affecting
|
|
1671
|
+
* attributes for the element, or null if no factories were found.
|
|
1672
|
+
*
|
|
1673
|
+
* This method parses the compact format: `data-fe-c-{index}-{count}`.
|
|
1674
|
+
*/
|
|
1675
|
+
parseCompactAttributeBinding(node) {
|
|
1676
|
+
const prefix = `${this.compactAttributeMarkerName}-`;
|
|
1677
|
+
const attrName = node.getAttributeNames().find(name => name.startsWith(prefix));
|
|
1678
|
+
if (!attrName) {
|
|
1679
|
+
return null;
|
|
1680
|
+
}
|
|
1681
|
+
const suffix = attrName.slice(prefix.length);
|
|
1682
|
+
const parts = suffix.split("-");
|
|
1683
|
+
const startIndex = parseInt(parts[0], 10);
|
|
1684
|
+
const count = parseInt(parts[1], 10);
|
|
1685
|
+
if (parts.length !== 2 ||
|
|
1686
|
+
Number.isNaN(startIndex) ||
|
|
1687
|
+
Number.isNaN(count) ||
|
|
1688
|
+
startIndex < 0 ||
|
|
1689
|
+
count < 1) {
|
|
1690
|
+
throw FAST.error(1604 /* invalidCompactAttributeMarkerName */, {
|
|
1691
|
+
name: attrName,
|
|
1692
|
+
expectedFormat: `${this.compactAttributeMarkerName}-{index}-{count}`,
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
const indexes = [];
|
|
1696
|
+
for (let i = 0; i < count; i++) {
|
|
1697
|
+
indexes.push(startIndex + i);
|
|
1698
|
+
}
|
|
1699
|
+
return indexes;
|
|
1700
|
+
},
|
|
1701
|
+
/**
|
|
1702
|
+
* Parses the ViewBehaviorFactory index from string data. Returns
|
|
1703
|
+
* the binding index or null if the index cannot be retrieved.
|
|
1704
|
+
*/
|
|
1705
|
+
parseContentBindingStartMarker(content) {
|
|
1706
|
+
return parseIndexAndIdMarker(bindingStartMarker, content);
|
|
1707
|
+
},
|
|
1708
|
+
parseContentBindingEndMarker(content) {
|
|
1709
|
+
return parseIndexAndIdMarker(bindingEndMarker, content);
|
|
1710
|
+
},
|
|
1711
|
+
/**
|
|
1712
|
+
* Parses the index of a repeat directive from a content string.
|
|
1713
|
+
*/
|
|
1714
|
+
parseRepeatStartMarker(content) {
|
|
1715
|
+
return parseIntMarker(repeatViewStartMarker, content);
|
|
1716
|
+
},
|
|
1717
|
+
parseRepeatEndMarker(content) {
|
|
1718
|
+
return parseIntMarker(repeatViewEndMarker, content);
|
|
1719
|
+
},
|
|
1720
|
+
/**
|
|
1721
|
+
* Parses element Id from element boundary markers
|
|
1722
|
+
*/
|
|
1723
|
+
parseElementBoundaryStartMarker(content) {
|
|
1724
|
+
return parseStringMarker(elementBoundaryStartMarker, content.trim());
|
|
1725
|
+
},
|
|
1726
|
+
parseElementBoundaryEndMarker(content) {
|
|
1727
|
+
return parseStringMarker(elementBoundaryEndMarker, content);
|
|
1728
|
+
},
|
|
1729
|
+
});
|
|
1730
|
+
function parseIntMarker(regex, content) {
|
|
1731
|
+
const match = regex.exec(content);
|
|
1732
|
+
return match === null ? match : parseInt(match[1]);
|
|
1733
|
+
}
|
|
1734
|
+
function parseStringMarker(regex, content) {
|
|
1735
|
+
const match = regex.exec(content);
|
|
1736
|
+
return match === null ? match : match[1];
|
|
1737
|
+
}
|
|
1738
|
+
function parseIndexAndIdMarker(regex, content) {
|
|
1739
|
+
const match = regex.exec(content);
|
|
1740
|
+
return match === null ? match : [parseInt(match[1]), match[2]];
|
|
1741
|
+
}
|
|
1742
|
+
/**
|
|
1743
|
+
* @internal
|
|
1744
|
+
*/
|
|
1745
|
+
const Hydratable = Symbol.for("fe-hydration");
|
|
1746
|
+
/** @beta */
|
|
1747
|
+
function isHydratable(value) {
|
|
1748
|
+
return value[Hydratable] === Hydratable;
|
|
1749
|
+
}
|
|
1750
|
+
/**
|
|
1751
|
+
* The attribute used to defer hydration of an element.
|
|
1752
|
+
* @beta
|
|
1753
|
+
*/
|
|
1754
|
+
const deferHydrationAttribute = "defer-hydration";
|
|
1755
|
+
|
|
1756
|
+
class HydrationTargetElementError extends Error {
|
|
1757
|
+
constructor(
|
|
1758
|
+
/**
|
|
1759
|
+
* The error message
|
|
1760
|
+
*/
|
|
1761
|
+
message,
|
|
1762
|
+
/**
|
|
1763
|
+
* The Compiled View Behavior Factories that belong to the view.
|
|
1764
|
+
*/
|
|
1765
|
+
factories,
|
|
1766
|
+
/**
|
|
1767
|
+
* The node to target factory.
|
|
1768
|
+
*/
|
|
1769
|
+
node) {
|
|
1770
|
+
super(message);
|
|
1518
1771
|
this.factories = factories;
|
|
1519
1772
|
this.node = node;
|
|
1520
1773
|
}
|
|
@@ -1544,7 +1797,23 @@ function isShadowRoot(node) {
|
|
|
1544
1797
|
return node instanceof DocumentFragment && "mode" in node;
|
|
1545
1798
|
}
|
|
1546
1799
|
/**
|
|
1547
|
-
* Maps
|
|
1800
|
+
* Maps compiled ViewBehaviorFactory IDs to their corresponding DOM nodes in the
|
|
1801
|
+
* server-rendered shadow root. Uses a TreeWalker to scan the existing DOM between
|
|
1802
|
+
* firstNode and lastNode, parsing hydration markers to build the targets map.
|
|
1803
|
+
*
|
|
1804
|
+
* For element nodes: parses `data-fe-b` (or variant) attributes to identify which
|
|
1805
|
+
* factories target each element, then removes the marker attribute.
|
|
1806
|
+
*
|
|
1807
|
+
* For comment nodes: parses content binding markers (`fe-b$$start/end$$`) to find
|
|
1808
|
+
* the DOM range controlled by each content binding. Single text nodes become the
|
|
1809
|
+
* direct target; multi-node ranges are stored in boundaries for structural directives.
|
|
1810
|
+
* Element boundary markers (`fe-eb$$start/end$$`) cause the walker to skip over
|
|
1811
|
+
* nested custom elements that handle their own hydration.
|
|
1812
|
+
*
|
|
1813
|
+
* Host bindings (targetNodeId='h') appear at the start of the factories array but
|
|
1814
|
+
* have no SSR markers — getHydrationIndexOffset() computes how many to skip so that
|
|
1815
|
+
* marker indices align with the correct non-host factories.
|
|
1816
|
+
*
|
|
1548
1817
|
* @param firstNode - The first node of the view.
|
|
1549
1818
|
* @param lastNode - The last node of the view.
|
|
1550
1819
|
* @param factories - The Compiled View Behavior Factories that belong to the view.
|
|
@@ -1553,6 +1822,7 @@ function isShadowRoot(node) {
|
|
|
1553
1822
|
function buildViewBindingTargets(firstNode, lastNode, factories) {
|
|
1554
1823
|
const range = createRangeForNodes(firstNode, lastNode);
|
|
1555
1824
|
const treeRoot = range.commonAncestorContainer;
|
|
1825
|
+
const hydrationIndexOffset = getHydrationIndexOffset(factories);
|
|
1556
1826
|
const walker = document.createTreeWalker(treeRoot, NodeFilter.SHOW_ELEMENT + NodeFilter.SHOW_COMMENT + NodeFilter.SHOW_TEXT, {
|
|
1557
1827
|
acceptNode(node) {
|
|
1558
1828
|
return range.comparePoint(node, 0) === 0
|
|
@@ -1566,11 +1836,11 @@ function buildViewBindingTargets(firstNode, lastNode, factories) {
|
|
|
1566
1836
|
while (node !== null) {
|
|
1567
1837
|
switch (node.nodeType) {
|
|
1568
1838
|
case Node.ELEMENT_NODE: {
|
|
1569
|
-
targetElement(node, factories, targets);
|
|
1839
|
+
targetElement(node, factories, targets, hydrationIndexOffset);
|
|
1570
1840
|
break;
|
|
1571
1841
|
}
|
|
1572
1842
|
case Node.COMMENT_NODE: {
|
|
1573
|
-
targetComment(node, walker, factories, targets, boundaries);
|
|
1843
|
+
targetComment(node, walker, factories, targets, boundaries, hydrationIndexOffset);
|
|
1574
1844
|
break;
|
|
1575
1845
|
}
|
|
1576
1846
|
}
|
|
@@ -1579,20 +1849,22 @@ function buildViewBindingTargets(firstNode, lastNode, factories) {
|
|
|
1579
1849
|
range.detach();
|
|
1580
1850
|
return { targets, boundaries };
|
|
1581
1851
|
}
|
|
1582
|
-
function targetElement(node, factories, targets) {
|
|
1852
|
+
function targetElement(node, factories, targets, hydrationIndexOffset) {
|
|
1853
|
+
var _a, _b;
|
|
1583
1854
|
// Check for attributes and map any factories.
|
|
1584
|
-
const attrFactoryIds = HydrationMarkup.parseAttributeBinding(node);
|
|
1855
|
+
const attrFactoryIds = (_b = (_a = HydrationMarkup.parseAttributeBinding(node)) !== null && _a !== void 0 ? _a : HydrationMarkup.parseEnumeratedAttributeBinding(node)) !== null && _b !== void 0 ? _b : HydrationMarkup.parseCompactAttributeBinding(node);
|
|
1585
1856
|
if (attrFactoryIds !== null) {
|
|
1586
1857
|
for (const id of attrFactoryIds) {
|
|
1587
|
-
|
|
1858
|
+
const factory = factories[id + hydrationIndexOffset];
|
|
1859
|
+
if (!factory) {
|
|
1588
1860
|
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);
|
|
1589
1861
|
}
|
|
1590
|
-
targetFactory(
|
|
1862
|
+
targetFactory(factory, node, targets);
|
|
1591
1863
|
}
|
|
1592
1864
|
node.removeAttribute(HydrationMarkup.attributeMarkerName);
|
|
1593
1865
|
}
|
|
1594
1866
|
}
|
|
1595
|
-
function targetComment(node, walker, factories, targets, boundaries) {
|
|
1867
|
+
function targetComment(node, walker, factories, targets, boundaries, hydrationIndexOffset) {
|
|
1596
1868
|
if (HydrationMarkup.isElementBoundaryStartMarker(node)) {
|
|
1597
1869
|
skipToElementBoundaryEndMarker(node, walker);
|
|
1598
1870
|
return;
|
|
@@ -1603,7 +1875,7 @@ function targetComment(node, walker, factories, targets, boundaries) {
|
|
|
1603
1875
|
return;
|
|
1604
1876
|
}
|
|
1605
1877
|
const [index, id] = parsed;
|
|
1606
|
-
const factory = factories[index];
|
|
1878
|
+
const factory = factories[index + hydrationIndexOffset];
|
|
1607
1879
|
const nodes = [];
|
|
1608
1880
|
let current = walker.nextSibling();
|
|
1609
1881
|
node.data = "";
|
|
@@ -1667,6 +1939,23 @@ function skipToElementBoundaryEndMarker(node, walker) {
|
|
|
1667
1939
|
current = walker.nextSibling();
|
|
1668
1940
|
}
|
|
1669
1941
|
}
|
|
1942
|
+
/**
|
|
1943
|
+
* Counts how many factories at the start of the array are host bindings (targetNodeId='h').
|
|
1944
|
+
* Host bindings target the custom element itself and are not represented by SSR markers,
|
|
1945
|
+
* so the marker indices must be offset by this count to align with the correct factory.
|
|
1946
|
+
*/
|
|
1947
|
+
function getHydrationIndexOffset(factories) {
|
|
1948
|
+
let offset = 0;
|
|
1949
|
+
for (let i = 0, ii = factories.length; i < ii; ++i) {
|
|
1950
|
+
if (factories[i].targetNodeId === "h") {
|
|
1951
|
+
offset++;
|
|
1952
|
+
}
|
|
1953
|
+
else {
|
|
1954
|
+
break;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
return offset;
|
|
1958
|
+
}
|
|
1670
1959
|
function targetFactory(factory, node, targets) {
|
|
1671
1960
|
if (factory.targetNodeId === undefined) {
|
|
1672
1961
|
// Dev error, this shouldn't ever be thrown
|
|
@@ -1675,7 +1964,7 @@ function targetFactory(factory, node, targets) {
|
|
|
1675
1964
|
targets[factory.targetNodeId] = node;
|
|
1676
1965
|
}
|
|
1677
1966
|
|
|
1678
|
-
var _a;
|
|
1967
|
+
var _a$1;
|
|
1679
1968
|
function removeNodeSequence(firstNode, lastNode) {
|
|
1680
1969
|
const parent = firstNode.parentNode;
|
|
1681
1970
|
let current = firstNode;
|
|
@@ -1850,6 +2139,17 @@ class HTMLView extends DefaultExecutionContext {
|
|
|
1850
2139
|
}
|
|
1851
2140
|
/**
|
|
1852
2141
|
* Binds a view's behaviors to its binding source.
|
|
2142
|
+
*
|
|
2143
|
+
* On the first call, this iterates through all compiled factories, calling
|
|
2144
|
+
* createBehavior() on each to produce a ViewBehavior instance (e.g., an
|
|
2145
|
+
* HTMLBindingDirective), and then immediately binds it. This is where event
|
|
2146
|
+
* listeners are registered, expression observers are created, and initial
|
|
2147
|
+
* DOM values are set.
|
|
2148
|
+
*
|
|
2149
|
+
* On subsequent calls with a new source, existing behaviors are re-bound
|
|
2150
|
+
* to the new data source, which re-evaluates all binding expressions and
|
|
2151
|
+
* updates the DOM accordingly.
|
|
2152
|
+
*
|
|
1853
2153
|
* @param source - The binding source for the view's binding behaviors.
|
|
1854
2154
|
* @param context - The execution context to run the behaviors within.
|
|
1855
2155
|
*/
|
|
@@ -1859,6 +2159,8 @@ class HTMLView extends DefaultExecutionContext {
|
|
|
1859
2159
|
}
|
|
1860
2160
|
let behaviors = this.behaviors;
|
|
1861
2161
|
if (behaviors === null) {
|
|
2162
|
+
// First bind: create behaviors from factories and bind each one.
|
|
2163
|
+
// The view (this) acts as the ViewController, providing targets and source.
|
|
1862
2164
|
this.source = source;
|
|
1863
2165
|
this.context = context;
|
|
1864
2166
|
this.behaviors = behaviors = new Array(this.factories.length);
|
|
@@ -1951,13 +2253,22 @@ class HydrationBindingError extends Error {
|
|
|
1951
2253
|
}
|
|
1952
2254
|
}
|
|
1953
2255
|
class HydrationView extends DefaultExecutionContext {
|
|
2256
|
+
get hydrationStage() {
|
|
2257
|
+
return this._hydrationStage;
|
|
2258
|
+
}
|
|
2259
|
+
get targets() {
|
|
2260
|
+
return this._targets;
|
|
2261
|
+
}
|
|
2262
|
+
get bindingViewBoundaries() {
|
|
2263
|
+
return this._bindingViewBoundaries;
|
|
2264
|
+
}
|
|
1954
2265
|
constructor(firstChild, lastChild, sourceTemplate, hostBindingTarget) {
|
|
1955
2266
|
super();
|
|
1956
2267
|
this.firstChild = firstChild;
|
|
1957
2268
|
this.lastChild = lastChild;
|
|
1958
2269
|
this.sourceTemplate = sourceTemplate;
|
|
1959
2270
|
this.hostBindingTarget = hostBindingTarget;
|
|
1960
|
-
this[_a] = Hydratable;
|
|
2271
|
+
this[_a$1] = Hydratable;
|
|
1961
2272
|
this.context = this;
|
|
1962
2273
|
this.source = null;
|
|
1963
2274
|
this.isBound = false;
|
|
@@ -1970,15 +2281,6 @@ class HydrationView extends DefaultExecutionContext {
|
|
|
1970
2281
|
this._targets = {};
|
|
1971
2282
|
this.factories = sourceTemplate.compile().factories;
|
|
1972
2283
|
}
|
|
1973
|
-
get hydrationStage() {
|
|
1974
|
-
return this._hydrationStage;
|
|
1975
|
-
}
|
|
1976
|
-
get targets() {
|
|
1977
|
-
return this._targets;
|
|
1978
|
-
}
|
|
1979
|
-
get bindingViewBoundaries() {
|
|
1980
|
-
return this._bindingViewBoundaries;
|
|
1981
|
-
}
|
|
1982
2284
|
/**
|
|
1983
2285
|
* no-op. Hydrated views are don't need to be moved from a documentFragment
|
|
1984
2286
|
* to the target node.
|
|
@@ -2033,7 +2335,7 @@ class HydrationView extends DefaultExecutionContext {
|
|
|
2033
2335
|
fragment.appendChild(end);
|
|
2034
2336
|
}
|
|
2035
2337
|
bind(source, context = this) {
|
|
2036
|
-
var _b
|
|
2338
|
+
var _b;
|
|
2037
2339
|
if (this.hydrationStage !== HydrationStage.hydrated) {
|
|
2038
2340
|
this._hydrationStage = HydrationStage.hydrating;
|
|
2039
2341
|
}
|
|
@@ -2077,7 +2379,28 @@ class HydrationView extends DefaultExecutionContext {
|
|
|
2077
2379
|
if (typeof templateString !== "string") {
|
|
2078
2380
|
templateString = templateString.innerHTML;
|
|
2079
2381
|
}
|
|
2080
|
-
|
|
2382
|
+
const hostElement = ((_b = this.firstChild) === null || _b === void 0 ? void 0 : _b.getRootNode())
|
|
2383
|
+
.host;
|
|
2384
|
+
const hostName = (hostElement === null || hostElement === void 0 ? void 0 : hostElement.nodeName) || "unknown";
|
|
2385
|
+
const factoryInfo = factory;
|
|
2386
|
+
// Build detailed error message
|
|
2387
|
+
const details = [
|
|
2388
|
+
`HydrationView was unable to successfully target bindings inside "<${hostName.toLowerCase()}>".`,
|
|
2389
|
+
`\nMismatch Details:`,
|
|
2390
|
+
` - Expected target node ID: "${factory.targetNodeId}"`,
|
|
2391
|
+
` - Available target IDs: [${Object.keys(this.targets).join(", ") || "none"}]`,
|
|
2392
|
+
];
|
|
2393
|
+
if (factory.targetTagName) {
|
|
2394
|
+
details.push(` - Expected tag name: "${factory.targetTagName}"`);
|
|
2395
|
+
}
|
|
2396
|
+
if (factoryInfo.sourceAspect) {
|
|
2397
|
+
details.push(` - Source aspect: "${factoryInfo.sourceAspect}"`);
|
|
2398
|
+
}
|
|
2399
|
+
if (factoryInfo.aspectType !== undefined) {
|
|
2400
|
+
details.push(` - Aspect type: ${factoryInfo.aspectType}`);
|
|
2401
|
+
}
|
|
2402
|
+
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 ? "..." : ""}`);
|
|
2403
|
+
throw new HydrationBindingError(details.join("\n"), factory, createRangeForNodes(this.firstChild, this.lastChild).cloneContents(), templateString);
|
|
2081
2404
|
}
|
|
2082
2405
|
}
|
|
2083
2406
|
}
|
|
@@ -2123,12 +2446,20 @@ class HydrationView extends DefaultExecutionContext {
|
|
|
2123
2446
|
unbindables.length = 0;
|
|
2124
2447
|
}
|
|
2125
2448
|
}
|
|
2126
|
-
_a = Hydratable;
|
|
2449
|
+
_a$1 = Hydratable;
|
|
2127
2450
|
makeSerializationNoop(HydrationView);
|
|
2128
2451
|
|
|
2129
2452
|
function isContentTemplate(value) {
|
|
2130
2453
|
return value.create !== undefined;
|
|
2131
2454
|
}
|
|
2455
|
+
/**
|
|
2456
|
+
* Sink function for DOMAspect.content bindings (text content interpolation).
|
|
2457
|
+
* Handles two cases:
|
|
2458
|
+
* - If the value is a ContentTemplate (has a create() method), it composes a child
|
|
2459
|
+
* view into the DOM, managing view lifecycle (create/reuse/remove/bind).
|
|
2460
|
+
* - If the value is a primitive, it sets target.textContent directly, first removing
|
|
2461
|
+
* any previously composed view.
|
|
2462
|
+
*/
|
|
2132
2463
|
function updateContent(target, aspect, value, controller) {
|
|
2133
2464
|
// If there's no actual value, then this equates to the
|
|
2134
2465
|
// empty string for the purposes of content bindings.
|
|
@@ -2197,6 +2528,12 @@ function updateContent(target, aspect, value, controller) {
|
|
|
2197
2528
|
target.textContent = value;
|
|
2198
2529
|
}
|
|
2199
2530
|
}
|
|
2531
|
+
/**
|
|
2532
|
+
* Sink function for DOMAspect.tokenList bindings (e.g., :classList).
|
|
2533
|
+
* Uses a versioning scheme to efficiently track which CSS classes were added
|
|
2534
|
+
* in the current update vs. the previous one. Classes from the previous version
|
|
2535
|
+
* that aren't present in the new value are automatically removed.
|
|
2536
|
+
*/
|
|
2200
2537
|
function updateTokenList(target, aspect, value) {
|
|
2201
2538
|
var _a;
|
|
2202
2539
|
const lookup = `${this.id}-t`;
|
|
@@ -2229,6 +2566,12 @@ function updateTokenList(target, aspect, value) {
|
|
|
2229
2566
|
}
|
|
2230
2567
|
}
|
|
2231
2568
|
}
|
|
2569
|
+
/**
|
|
2570
|
+
* Maps each DOMAspect type to its corresponding DOM update ("sink") function.
|
|
2571
|
+
* When a binding value changes, the sink function for the binding's aspect type
|
|
2572
|
+
* is called to push the new value into the DOM. Events are handled separately
|
|
2573
|
+
* via addEventListener in bind(), so the event sink is a no-op.
|
|
2574
|
+
*/
|
|
2232
2575
|
const sinkLookup = {
|
|
2233
2576
|
[DOMAspect.attribute]: DOM.setAttribute,
|
|
2234
2577
|
[DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
|
|
@@ -2238,7 +2581,18 @@ const sinkLookup = {
|
|
|
2238
2581
|
[DOMAspect.event]: () => void 0,
|
|
2239
2582
|
};
|
|
2240
2583
|
/**
|
|
2241
|
-
*
|
|
2584
|
+
* The central binding directive that bridges data expressions and DOM updates.
|
|
2585
|
+
*
|
|
2586
|
+
* HTMLBindingDirective fulfills three roles simultaneously:
|
|
2587
|
+
* - **HTMLDirective**: Produces placeholder HTML via createHTML() during template authoring.
|
|
2588
|
+
* - **ViewBehaviorFactory**: Creates behaviors (returns itself) during view creation.
|
|
2589
|
+
* - **ViewBehavior / EventListener**: Attaches to a DOM node during bind, manages
|
|
2590
|
+
* expression observers for reactive updates, and handles DOM events directly.
|
|
2591
|
+
*
|
|
2592
|
+
* The aspectType (set by HTMLDirective.assignAspect during template processing)
|
|
2593
|
+
* determines which DOM "sink" function is used to apply values — e.g.,
|
|
2594
|
+
* setAttribute for attributes, addEventListener for events, textContent for content.
|
|
2595
|
+
*
|
|
2242
2596
|
* @public
|
|
2243
2597
|
*/
|
|
2244
2598
|
class HTMLBindingDirective {
|
|
@@ -2270,14 +2624,25 @@ class HTMLBindingDirective {
|
|
|
2270
2624
|
const sink = sinkLookup[this.aspectType];
|
|
2271
2625
|
const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
|
|
2272
2626
|
if (!sink) {
|
|
2273
|
-
throw FAST.error(
|
|
2627
|
+
throw FAST.error(Message.unsupportedBindingBehavior);
|
|
2274
2628
|
}
|
|
2275
2629
|
this.data = `${this.id}-d`;
|
|
2276
2630
|
this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
|
|
2277
2631
|
}
|
|
2278
2632
|
return this;
|
|
2279
2633
|
}
|
|
2280
|
-
/**
|
|
2634
|
+
/**
|
|
2635
|
+
* Attaches this binding to its target DOM node.
|
|
2636
|
+
* - For events: stores the controller reference on the target element and registers
|
|
2637
|
+
* this directive as the EventListener via addEventListener. The directive's
|
|
2638
|
+
* handleEvent() method will be called when the event fires.
|
|
2639
|
+
* - For content bindings: registers an unbind handler, then falls through to the
|
|
2640
|
+
* default path.
|
|
2641
|
+
* - For all non-event bindings: creates (or reuses) an ExpressionObserver, evaluates
|
|
2642
|
+
* the binding expression, and applies the result to the DOM via the updateTarget
|
|
2643
|
+
* sink function. The observer will call handleChange() on future data changes.
|
|
2644
|
+
* @internal
|
|
2645
|
+
*/
|
|
2281
2646
|
bind(controller) {
|
|
2282
2647
|
var _a;
|
|
2283
2648
|
const target = controller.targets[this.targetNodeId];
|
|
@@ -2292,7 +2657,7 @@ class HTMLBindingDirective {
|
|
|
2292
2657
|
case DOMAspect.content:
|
|
2293
2658
|
controller.onUnbind(this);
|
|
2294
2659
|
// intentional fall through
|
|
2295
|
-
default:
|
|
2660
|
+
default: {
|
|
2296
2661
|
const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
|
|
2297
2662
|
observer.target = target;
|
|
2298
2663
|
observer.controller = controller;
|
|
@@ -2305,6 +2670,7 @@ class HTMLBindingDirective {
|
|
|
2305
2670
|
}
|
|
2306
2671
|
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
2307
2672
|
break;
|
|
2673
|
+
}
|
|
2308
2674
|
}
|
|
2309
2675
|
}
|
|
2310
2676
|
/** @internal */
|
|
@@ -2316,7 +2682,14 @@ class HTMLBindingDirective {
|
|
|
2316
2682
|
view.needsBindOnly = true;
|
|
2317
2683
|
}
|
|
2318
2684
|
}
|
|
2319
|
-
/**
|
|
2685
|
+
/**
|
|
2686
|
+
* Implements the EventListener interface. When a DOM event fires on the target
|
|
2687
|
+
* element, this method retrieves the ViewController stored on the element,
|
|
2688
|
+
* sets the event on the ExecutionContext so `c.event` is available to the
|
|
2689
|
+
* binding expression, and evaluates the expression. If the expression returns
|
|
2690
|
+
* anything other than `true`, the event's default action is prevented.
|
|
2691
|
+
* @internal
|
|
2692
|
+
*/
|
|
2320
2693
|
handleEvent(event) {
|
|
2321
2694
|
const controller = event.currentTarget[this.data];
|
|
2322
2695
|
if (controller.isBound) {
|
|
@@ -2328,15 +2701,38 @@ class HTMLBindingDirective {
|
|
|
2328
2701
|
}
|
|
2329
2702
|
}
|
|
2330
2703
|
}
|
|
2331
|
-
/**
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2704
|
+
/**
|
|
2705
|
+
* Called by the ExpressionObserver when a tracked dependency changes.
|
|
2706
|
+
* Re-evaluates the binding expression via observer.bind() and pushes
|
|
2707
|
+
* the new value to the DOM through the updateTarget sink function.
|
|
2708
|
+
* This is the reactive update path that keeps the DOM in sync with data.
|
|
2709
|
+
*
|
|
2710
|
+
* Guards against stale notifications: when a view is unbound (e.g., after
|
|
2711
|
+
* a parent `when` directive tears down a child element), coupled-lifetime
|
|
2712
|
+
* observers may still hold active subscriptions. If a property change fires
|
|
2713
|
+
* on the source element while the view is inactive, this guard prevents
|
|
2714
|
+
* the binding expression from evaluating with a null source.
|
|
2715
|
+
* @internal
|
|
2716
|
+
*/
|
|
2717
|
+
handleChange(binding, observer) {
|
|
2718
|
+
const controller = observer.controller;
|
|
2719
|
+
// https://github.com/microsoft/fast/issues/7444
|
|
2720
|
+
// This guard will be reconsidered in the next major version.
|
|
2721
|
+
if (!controller.isBound) {
|
|
2722
|
+
return;
|
|
2723
|
+
}
|
|
2724
|
+
const target = observer.target;
|
|
2335
2725
|
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
2336
2726
|
}
|
|
2337
2727
|
}
|
|
2338
2728
|
HTMLDirective.define(HTMLBindingDirective, { aspected: true });
|
|
2339
2729
|
|
|
2730
|
+
/**
|
|
2731
|
+
* Builds a hierarchical node ID by appending the child index to the parent's ID.
|
|
2732
|
+
* For example, the third child of root is "r.2", and its first child is "r.2.0".
|
|
2733
|
+
* These IDs are used as property names on the targets prototype so that each
|
|
2734
|
+
* binding's target DOM node can be lazily resolved via a chain of childNodes lookups.
|
|
2735
|
+
*/
|
|
2340
2736
|
const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
|
|
2341
2737
|
const descriptorCache = {};
|
|
2342
2738
|
// used to prevent creating lots of objects just to track node and index while compiling
|
|
@@ -2346,7 +2742,7 @@ const next = {
|
|
|
2346
2742
|
};
|
|
2347
2743
|
function tryWarn(name) {
|
|
2348
2744
|
if (!name.startsWith("fast-")) {
|
|
2349
|
-
FAST.warn(
|
|
2745
|
+
FAST.warn(Message.hostBindingWithoutHost, { name });
|
|
2350
2746
|
}
|
|
2351
2747
|
}
|
|
2352
2748
|
const warningHost = new Proxy(document.createElement("div"), {
|
|
@@ -2386,6 +2782,13 @@ class CompilationContext {
|
|
|
2386
2782
|
this.proto = Object.create(null, this.descriptors);
|
|
2387
2783
|
return this;
|
|
2388
2784
|
}
|
|
2785
|
+
/**
|
|
2786
|
+
* Registers a lazy getter on the targets prototype that resolves a DOM node
|
|
2787
|
+
* by navigating from its parent's childNodes at the given index. Getters are
|
|
2788
|
+
* chained: accessing targets["r.0.2"] first resolves targets["r.0"] (which
|
|
2789
|
+
* resolves targets["r"]), then returns childNodes[2]. Results are cached so
|
|
2790
|
+
* each node is resolved at most once per view instance.
|
|
2791
|
+
*/
|
|
2389
2792
|
addTargetDescriptor(parentId, targetId, targetIndex) {
|
|
2390
2793
|
const descriptors = this.descriptors;
|
|
2391
2794
|
if (targetId === "r" || // root
|
|
@@ -2396,7 +2799,7 @@ class CompilationContext {
|
|
|
2396
2799
|
if (!descriptors[parentId]) {
|
|
2397
2800
|
const index = parentId.lastIndexOf(".");
|
|
2398
2801
|
const grandparentId = parentId.substring(0, index);
|
|
2399
|
-
const childIndex = parseInt(parentId.substring(index + 1));
|
|
2802
|
+
const childIndex = parseInt(parentId.substring(index + 1), 10);
|
|
2400
2803
|
this.addTargetDescriptor(grandparentId, parentId, childIndex);
|
|
2401
2804
|
}
|
|
2402
2805
|
let descriptor = descriptorCache[targetId];
|
|
@@ -2411,13 +2814,20 @@ class CompilationContext {
|
|
|
2411
2814
|
}
|
|
2412
2815
|
descriptors[targetId] = descriptor;
|
|
2413
2816
|
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Creates a new HTMLView by cloning the compiled DocumentFragment and building
|
|
2819
|
+
* a targets object. The targets prototype contains lazy getters that resolve
|
|
2820
|
+
* each binding's target DOM node via childNodes traversal. Accessing every
|
|
2821
|
+
* registered nodeId eagerly triggers the getter chain so all nodes are resolved
|
|
2822
|
+
* before behaviors are bound.
|
|
2823
|
+
*/
|
|
2414
2824
|
createView(hostBindingTarget) {
|
|
2415
2825
|
const fragment = this.fragment.cloneNode(true);
|
|
2416
2826
|
const targets = Object.create(this.proto);
|
|
2417
|
-
targets.r = fragment;
|
|
2418
|
-
targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
|
|
2827
|
+
targets.r = fragment; // root — the cloned DocumentFragment
|
|
2828
|
+
targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost; // host — the custom element
|
|
2419
2829
|
for (const id of this.nodeIds) {
|
|
2420
|
-
targets
|
|
2830
|
+
Reflect.get(targets, id); // trigger lazy getter to resolve and cache the DOM node
|
|
2421
2831
|
}
|
|
2422
2832
|
return new HTMLView(fragment, this.factories, targets);
|
|
2423
2833
|
}
|
|
@@ -2437,7 +2847,6 @@ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBa
|
|
|
2437
2847
|
}
|
|
2438
2848
|
}
|
|
2439
2849
|
else {
|
|
2440
|
-
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
2441
2850
|
result = Compiler.aggregate(parseResult, context.policy);
|
|
2442
2851
|
}
|
|
2443
2852
|
if (result !== null) {
|
|
@@ -2482,7 +2891,6 @@ function compileChildren(context, parent, parentId) {
|
|
|
2482
2891
|
let nodeIndex = 0;
|
|
2483
2892
|
let childNode = parent.firstChild;
|
|
2484
2893
|
while (childNode) {
|
|
2485
|
-
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
2486
2894
|
const result = compileNode(context, parentId, childNode, nodeIndex);
|
|
2487
2895
|
childNode = result.node;
|
|
2488
2896
|
nodeIndex = result.index;
|
|
@@ -2497,14 +2905,14 @@ function compileNode(context, parentId, node, nodeIndex) {
|
|
|
2497
2905
|
break;
|
|
2498
2906
|
case 3: // text node
|
|
2499
2907
|
return compileContent(context, node, parentId, nodeId, nodeIndex);
|
|
2500
|
-
case 8:
|
|
2908
|
+
case 8: {
|
|
2909
|
+
// comment
|
|
2501
2910
|
const parts = Parser.parse(node.data, context.directives);
|
|
2502
2911
|
if (parts !== null) {
|
|
2503
|
-
context.addFactory(
|
|
2504
|
-
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
2505
|
-
Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
|
|
2912
|
+
context.addFactory(Compiler.aggregate(parts), parentId, nodeId, nodeIndex, null);
|
|
2506
2913
|
}
|
|
2507
2914
|
break;
|
|
2915
|
+
}
|
|
2508
2916
|
}
|
|
2509
2917
|
next.index = nodeIndex + 1;
|
|
2510
2918
|
next.node = node.nextSibling;
|
|
@@ -2512,7 +2920,7 @@ function compileNode(context, parentId, node, nodeIndex) {
|
|
|
2512
2920
|
}
|
|
2513
2921
|
function isMarker(node, directives) {
|
|
2514
2922
|
return (node &&
|
|
2515
|
-
node.nodeType
|
|
2923
|
+
node.nodeType === 8 &&
|
|
2516
2924
|
Parser.parse(node.data, directives) !== null);
|
|
2517
2925
|
}
|
|
2518
2926
|
const templateTag = "TEMPLATE";
|
|
@@ -2614,631 +3022,634 @@ const Compiler = {
|
|
|
2614
3022
|
},
|
|
2615
3023
|
};
|
|
2616
3024
|
|
|
2617
|
-
// Much thanks to LitHTML for working this out!
|
|
2618
|
-
const lastAttributeNameRegex =
|
|
2619
|
-
/* eslint-disable-next-line no-control-regex, max-len */
|
|
2620
|
-
/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
|
|
2621
|
-
const noFactories = Object.create(null);
|
|
2622
3025
|
/**
|
|
2623
|
-
*
|
|
3026
|
+
* The runtime behavior for template references.
|
|
2624
3027
|
* @public
|
|
2625
3028
|
*/
|
|
2626
|
-
class
|
|
2627
|
-
/**
|
|
2628
|
-
* Creates an instance of InlineTemplateDirective.
|
|
2629
|
-
* @param template - The template to inline.
|
|
2630
|
-
*/
|
|
2631
|
-
constructor(html, factories = noFactories) {
|
|
2632
|
-
this.html = html;
|
|
2633
|
-
this.factories = factories;
|
|
2634
|
-
}
|
|
3029
|
+
class RefDirective extends StatelessAttachedAttributeDirective {
|
|
2635
3030
|
/**
|
|
2636
|
-
*
|
|
2637
|
-
* @param
|
|
3031
|
+
* Bind this behavior.
|
|
3032
|
+
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
2638
3033
|
*/
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
for (const key in factories) {
|
|
2642
|
-
add(factories[key]);
|
|
2643
|
-
}
|
|
2644
|
-
return this.html;
|
|
3034
|
+
bind(controller) {
|
|
3035
|
+
controller.source[this.options] = controller.targets[this.targetNodeId];
|
|
2645
3036
|
}
|
|
2646
3037
|
}
|
|
3038
|
+
HTMLDirective.define(RefDirective);
|
|
2647
3039
|
/**
|
|
2648
|
-
*
|
|
3040
|
+
* A directive that observes the updates a property with a reference to the element.
|
|
3041
|
+
* @param propertyName - The name of the property to assign the reference to.
|
|
3042
|
+
* @public
|
|
2649
3043
|
*/
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
const match = lastAttributeNameRegex.exec(prevString);
|
|
2655
|
-
if (match !== null) {
|
|
2656
|
-
HTMLDirective.assignAspect(value, match[2]);
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
return value.createHTML(add);
|
|
2660
|
-
}
|
|
3044
|
+
const ref = (propertyName) => new RefDirective(propertyName);
|
|
3045
|
+
|
|
3046
|
+
const booleanMode = "boolean";
|
|
3047
|
+
const reflectMode = "reflect";
|
|
2661
3048
|
/**
|
|
2662
|
-
*
|
|
3049
|
+
* Metadata used to configure a custom attribute's behavior.
|
|
2663
3050
|
* @public
|
|
2664
3051
|
*/
|
|
2665
|
-
|
|
3052
|
+
const AttributeConfiguration = Object.freeze({
|
|
2666
3053
|
/**
|
|
2667
|
-
*
|
|
2668
|
-
* @param html - The html representing what this template will instantiate, including placeholders for directives.
|
|
2669
|
-
* @param factories - The directives that will be connected to placeholders in the html.
|
|
2670
|
-
* @param policy - The security policy to use when compiling this template.
|
|
3054
|
+
* Locates all attribute configurations associated with a type.
|
|
2671
3055
|
*/
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
3056
|
+
locate: createMetadataLocator(),
|
|
3057
|
+
});
|
|
3058
|
+
/**
|
|
3059
|
+
* A {@link ValueConverter} that converts to and from `boolean` values.
|
|
3060
|
+
* @remarks
|
|
3061
|
+
* Used automatically when the `boolean` {@link AttributeMode} is selected.
|
|
3062
|
+
* @public
|
|
3063
|
+
*/
|
|
3064
|
+
const booleanConverter = {
|
|
3065
|
+
toView(value) {
|
|
3066
|
+
return value ? "true" : "false";
|
|
3067
|
+
},
|
|
3068
|
+
fromView(value) {
|
|
3069
|
+
return !(value === null ||
|
|
3070
|
+
value === void 0 ||
|
|
3071
|
+
value === "false" ||
|
|
3072
|
+
value === false ||
|
|
3073
|
+
value === 0);
|
|
3074
|
+
},
|
|
3075
|
+
};
|
|
3076
|
+
function toNumber(value) {
|
|
3077
|
+
if (value === null || value === undefined) {
|
|
3078
|
+
return null;
|
|
2677
3079
|
}
|
|
3080
|
+
const number = value * 1;
|
|
3081
|
+
return isNaN(number) ? null : number;
|
|
3082
|
+
}
|
|
3083
|
+
/**
|
|
3084
|
+
* A {@link ValueConverter} that converts to and from `number` values.
|
|
3085
|
+
* @remarks
|
|
3086
|
+
* This converter allows for nullable numbers, returning `null` if the
|
|
3087
|
+
* input was `null`, `undefined`, or `NaN`.
|
|
3088
|
+
* @public
|
|
3089
|
+
*/
|
|
3090
|
+
const nullableNumberConverter = {
|
|
3091
|
+
toView(value) {
|
|
3092
|
+
const output = toNumber(value);
|
|
3093
|
+
return output ? output.toString() : output;
|
|
3094
|
+
},
|
|
3095
|
+
fromView: toNumber,
|
|
3096
|
+
};
|
|
3097
|
+
/**
|
|
3098
|
+
* An implementation of {@link Accessor} that supports reactivity,
|
|
3099
|
+
* change callbacks, attribute reflection, and type conversion for
|
|
3100
|
+
* custom elements.
|
|
3101
|
+
* @public
|
|
3102
|
+
*/
|
|
3103
|
+
class AttributeDefinition {
|
|
2678
3104
|
/**
|
|
2679
|
-
*
|
|
3105
|
+
* Creates an instance of AttributeDefinition.
|
|
3106
|
+
* @param Owner - The class constructor that owns this attribute.
|
|
3107
|
+
* @param name - The name of the property associated with the attribute.
|
|
3108
|
+
* @param attribute - The name of the attribute in HTML.
|
|
3109
|
+
* @param mode - The {@link AttributeMode} that describes the behavior of this attribute.
|
|
3110
|
+
* @param converter - A {@link ValueConverter} that integrates with the property getter/setter
|
|
3111
|
+
* to convert values to and from a DOM string.
|
|
2680
3112
|
*/
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
3113
|
+
constructor(Owner, name, attribute = name.toLowerCase(), mode = reflectMode, converter) {
|
|
3114
|
+
this.guards = new Set();
|
|
3115
|
+
this.Owner = Owner;
|
|
3116
|
+
this.name = name;
|
|
3117
|
+
this.attribute = attribute;
|
|
3118
|
+
this.mode = mode;
|
|
3119
|
+
this.converter = converter;
|
|
3120
|
+
this.fieldName = `_${name}`;
|
|
3121
|
+
this.callbackName = `${name}Changed`;
|
|
3122
|
+
this.hasCallback = this.callbackName in Owner.prototype;
|
|
3123
|
+
if (mode === booleanMode && converter === void 0) {
|
|
3124
|
+
this.converter = booleanConverter;
|
|
2684
3125
|
}
|
|
2685
|
-
return this.result;
|
|
2686
3126
|
}
|
|
2687
3127
|
/**
|
|
2688
|
-
*
|
|
2689
|
-
* @param
|
|
3128
|
+
* Sets the value of the attribute/property on the source element.
|
|
3129
|
+
* @param source - The source element to access.
|
|
3130
|
+
* @param newValue - The value to set the attribute/property to.
|
|
2690
3131
|
*/
|
|
2691
|
-
|
|
2692
|
-
|
|
3132
|
+
setValue(source, newValue) {
|
|
3133
|
+
const oldValue = source[this.fieldName];
|
|
3134
|
+
const converter = this.converter;
|
|
3135
|
+
if (converter !== void 0) {
|
|
3136
|
+
newValue = converter.fromView(newValue);
|
|
3137
|
+
}
|
|
3138
|
+
if (oldValue !== newValue) {
|
|
3139
|
+
source[this.fieldName] = newValue;
|
|
3140
|
+
this.tryReflectToAttribute(source);
|
|
3141
|
+
if (this.hasCallback) {
|
|
3142
|
+
source[this.callbackName](oldValue, newValue);
|
|
3143
|
+
}
|
|
3144
|
+
source.$fastController.notify(this.name);
|
|
3145
|
+
}
|
|
2693
3146
|
}
|
|
2694
3147
|
/**
|
|
2695
|
-
*
|
|
3148
|
+
* Gets the value of the attribute/property on the source element.
|
|
3149
|
+
* @param source - The source element to access.
|
|
2696
3150
|
*/
|
|
2697
|
-
|
|
2698
|
-
|
|
3151
|
+
getValue(source) {
|
|
3152
|
+
Observable.track(source, this.name);
|
|
3153
|
+
return source[this.fieldName];
|
|
2699
3154
|
}
|
|
2700
|
-
/**
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
* @remarks
|
|
2705
|
-
* The DOMPolicy can only be set once for a template and cannot be
|
|
2706
|
-
* set after the template is compiled.
|
|
2707
|
-
*/
|
|
2708
|
-
withPolicy(policy) {
|
|
2709
|
-
if (this.result) {
|
|
2710
|
-
throw FAST.error(1208 /* Message.cannotSetTemplatePolicyAfterCompilation */);
|
|
3155
|
+
/** @internal */
|
|
3156
|
+
onAttributeChangedCallback(element, value) {
|
|
3157
|
+
if (this.guards.has(element)) {
|
|
3158
|
+
return;
|
|
2711
3159
|
}
|
|
2712
|
-
|
|
2713
|
-
|
|
3160
|
+
this.guards.add(element);
|
|
3161
|
+
this.setValue(element, value);
|
|
3162
|
+
this.guards.delete(element);
|
|
3163
|
+
}
|
|
3164
|
+
tryReflectToAttribute(element) {
|
|
3165
|
+
const mode = this.mode;
|
|
3166
|
+
const guards = this.guards;
|
|
3167
|
+
if (guards.has(element) || mode === "fromView") {
|
|
3168
|
+
return;
|
|
2714
3169
|
}
|
|
2715
|
-
|
|
2716
|
-
|
|
3170
|
+
Updates.enqueue(() => {
|
|
3171
|
+
guards.add(element);
|
|
3172
|
+
const latestValue = element[this.fieldName];
|
|
3173
|
+
switch (mode) {
|
|
3174
|
+
case reflectMode:
|
|
3175
|
+
const converter = this.converter;
|
|
3176
|
+
DOM.setAttribute(element, this.attribute, converter !== void 0 ? converter.toView(latestValue) : latestValue);
|
|
3177
|
+
break;
|
|
3178
|
+
case booleanMode:
|
|
3179
|
+
DOM.setBooleanAttribute(element, this.attribute, latestValue);
|
|
3180
|
+
break;
|
|
3181
|
+
}
|
|
3182
|
+
guards.delete(element);
|
|
3183
|
+
});
|
|
2717
3184
|
}
|
|
2718
3185
|
/**
|
|
2719
|
-
*
|
|
2720
|
-
* @param
|
|
2721
|
-
* @param
|
|
2722
|
-
* @
|
|
2723
|
-
* host that the template is being attached to.
|
|
3186
|
+
* Collects all attribute definitions associated with the owner.
|
|
3187
|
+
* @param Owner - The class constructor to collect attribute for.
|
|
3188
|
+
* @param attributeLists - Any existing attributes to collect and merge with those associated with the owner.
|
|
3189
|
+
* @internal
|
|
2724
3190
|
*/
|
|
2725
|
-
|
|
2726
|
-
const
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
static create(strings, values, policy) {
|
|
2745
|
-
let html = "";
|
|
2746
|
-
const factories = Object.create(null);
|
|
2747
|
-
const add = (factory) => {
|
|
2748
|
-
var _a;
|
|
2749
|
-
const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
|
|
2750
|
-
factories[id] = factory;
|
|
2751
|
-
return id;
|
|
2752
|
-
};
|
|
2753
|
-
for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
|
|
2754
|
-
const currentString = strings[i];
|
|
2755
|
-
let currentValue = values[i];
|
|
2756
|
-
let definition;
|
|
2757
|
-
html += currentString;
|
|
2758
|
-
if (isFunction(currentValue)) {
|
|
2759
|
-
currentValue = new HTMLBindingDirective(oneWay(currentValue));
|
|
2760
|
-
}
|
|
2761
|
-
else if (currentValue instanceof Binding) {
|
|
2762
|
-
currentValue = new HTMLBindingDirective(currentValue);
|
|
2763
|
-
}
|
|
2764
|
-
else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
|
|
2765
|
-
const staticValue = currentValue;
|
|
2766
|
-
currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
|
|
2767
|
-
}
|
|
2768
|
-
html += createHTML(currentValue, currentString, add, definition);
|
|
2769
|
-
}
|
|
2770
|
-
return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
|
|
3191
|
+
static collect(Owner, ...attributeLists) {
|
|
3192
|
+
const attributes = [];
|
|
3193
|
+
attributeLists.push(AttributeConfiguration.locate(Owner));
|
|
3194
|
+
for (let i = 0, ii = attributeLists.length; i < ii; ++i) {
|
|
3195
|
+
const list = attributeLists[i];
|
|
3196
|
+
if (list === void 0) {
|
|
3197
|
+
continue;
|
|
3198
|
+
}
|
|
3199
|
+
for (let j = 0, jj = list.length; j < jj; ++j) {
|
|
3200
|
+
const config = list[j];
|
|
3201
|
+
if (isString(config)) {
|
|
3202
|
+
attributes.push(new AttributeDefinition(Owner, config));
|
|
3203
|
+
}
|
|
3204
|
+
else {
|
|
3205
|
+
attributes.push(new AttributeDefinition(Owner, config.property, config.attribute, config.mode, config.converter));
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
return attributes;
|
|
2771
3210
|
}
|
|
2772
3211
|
}
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
return ViewTemplate.create(strings, values);
|
|
3212
|
+
function attr(configOrTarget, prop) {
|
|
3213
|
+
let config;
|
|
3214
|
+
function decorator($target, $prop) {
|
|
3215
|
+
if (arguments.length > 1) {
|
|
3216
|
+
// Non invocation:
|
|
3217
|
+
// - @attr
|
|
3218
|
+
// Invocation with or w/o opts:
|
|
3219
|
+
// - @attr()
|
|
3220
|
+
// - @attr({...opts})
|
|
3221
|
+
config.property = $prop;
|
|
3222
|
+
}
|
|
3223
|
+
AttributeConfiguration.locate($target.constructor).push(config);
|
|
2786
3224
|
}
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
/**
|
|
2794
|
-
* The runtime behavior for template references.
|
|
2795
|
-
* @public
|
|
2796
|
-
*/
|
|
2797
|
-
class RefDirective extends StatelessAttachedAttributeDirective {
|
|
2798
|
-
/**
|
|
2799
|
-
* Bind this behavior.
|
|
2800
|
-
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
2801
|
-
*/
|
|
2802
|
-
bind(controller) {
|
|
2803
|
-
controller.source[this.options] = controller.targets[this.targetNodeId];
|
|
3225
|
+
if (arguments.length > 1) {
|
|
3226
|
+
// Non invocation:
|
|
3227
|
+
// - @attr
|
|
3228
|
+
config = {};
|
|
3229
|
+
decorator(configOrTarget, prop);
|
|
3230
|
+
return;
|
|
2804
3231
|
}
|
|
3232
|
+
// Invocation with or w/o opts:
|
|
3233
|
+
// - @attr()
|
|
3234
|
+
// - @attr({...opts})
|
|
3235
|
+
config = configOrTarget === void 0 ? {} : configOrTarget;
|
|
3236
|
+
return decorator;
|
|
2805
3237
|
}
|
|
2806
|
-
|
|
3238
|
+
|
|
3239
|
+
var __awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3240
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3241
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
3242
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
3243
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
3244
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
3245
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
3246
|
+
});
|
|
3247
|
+
};
|
|
3248
|
+
var _a;
|
|
3249
|
+
const defaultShadowOptions = { mode: "open" };
|
|
3250
|
+
const defaultElementOptions = {};
|
|
3251
|
+
const fastElementBaseTypes = new Set();
|
|
2807
3252
|
/**
|
|
2808
|
-
*
|
|
2809
|
-
* @
|
|
2810
|
-
* @public
|
|
3253
|
+
* The FAST custom element registry
|
|
3254
|
+
* @internal
|
|
2811
3255
|
*/
|
|
2812
|
-
const
|
|
2813
|
-
|
|
2814
|
-
const selectElements = (value) => value.nodeType === 1;
|
|
3256
|
+
const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
|
|
2815
3257
|
/**
|
|
2816
|
-
*
|
|
2817
|
-
* @
|
|
2818
|
-
* @public
|
|
3258
|
+
* Values for the `templateOptions` property.
|
|
3259
|
+
* @alpha
|
|
2819
3260
|
*/
|
|
2820
|
-
const
|
|
2821
|
-
|
|
2822
|
-
|
|
3261
|
+
const TemplateOptions = {
|
|
3262
|
+
deferAndHydrate: "defer-and-hydrate",
|
|
3263
|
+
};
|
|
2823
3264
|
/**
|
|
2824
|
-
*
|
|
3265
|
+
* Defines metadata for a FASTElement.
|
|
2825
3266
|
* @public
|
|
2826
|
-
* @remarks
|
|
2827
|
-
* Internally used by the SlottedDirective and the ChildrenDirective.
|
|
2828
3267
|
*/
|
|
2829
|
-
class
|
|
3268
|
+
class FASTElementDefinition {
|
|
2830
3269
|
/**
|
|
2831
|
-
*
|
|
3270
|
+
* Indicates if this element has been defined in at least one registry.
|
|
2832
3271
|
*/
|
|
2833
|
-
get
|
|
2834
|
-
return this.
|
|
2835
|
-
}
|
|
2836
|
-
set id(value) {
|
|
2837
|
-
this._id = value;
|
|
2838
|
-
this._controllerProperty = `${value}-c`;
|
|
3272
|
+
get isDefined() {
|
|
3273
|
+
return this.platformDefined;
|
|
2839
3274
|
}
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
this.
|
|
2850
|
-
this.
|
|
2851
|
-
|
|
3275
|
+
constructor(type, nameOrConfig = type.definition) {
|
|
3276
|
+
var _b;
|
|
3277
|
+
this.platformDefined = false;
|
|
3278
|
+
if (isString(nameOrConfig)) {
|
|
3279
|
+
nameOrConfig = { name: nameOrConfig };
|
|
3280
|
+
}
|
|
3281
|
+
this.type = type;
|
|
3282
|
+
this.name = nameOrConfig.name;
|
|
3283
|
+
this.template = nameOrConfig.template;
|
|
3284
|
+
this.templateOptions = nameOrConfig.templateOptions;
|
|
3285
|
+
this.registry = (_b = nameOrConfig.registry) !== null && _b !== void 0 ? _b : customElements;
|
|
3286
|
+
const proto = type.prototype;
|
|
3287
|
+
const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
|
|
3288
|
+
const observedAttributes = new Array(attributes.length);
|
|
3289
|
+
const propertyLookup = {};
|
|
3290
|
+
const attributeLookup = {};
|
|
3291
|
+
for (let i = 0, ii = attributes.length; i < ii; ++i) {
|
|
3292
|
+
const current = attributes[i];
|
|
3293
|
+
observedAttributes[i] = current.attribute;
|
|
3294
|
+
propertyLookup[current.name] = current;
|
|
3295
|
+
attributeLookup[current.attribute] = current;
|
|
3296
|
+
Observable.defineProperty(proto, current);
|
|
3297
|
+
}
|
|
3298
|
+
Reflect.defineProperty(type, "observedAttributes", {
|
|
3299
|
+
value: observedAttributes,
|
|
3300
|
+
enumerable: true,
|
|
3301
|
+
});
|
|
3302
|
+
this.attributes = attributes;
|
|
3303
|
+
this.propertyLookup = propertyLookup;
|
|
3304
|
+
this.attributeLookup = attributeLookup;
|
|
3305
|
+
this.shadowOptions =
|
|
3306
|
+
nameOrConfig.shadowOptions === void 0
|
|
3307
|
+
? defaultShadowOptions
|
|
3308
|
+
: nameOrConfig.shadowOptions === null
|
|
3309
|
+
? void 0
|
|
3310
|
+
: Object.assign(Object.assign({}, defaultShadowOptions), nameOrConfig.shadowOptions);
|
|
3311
|
+
this.elementOptions =
|
|
3312
|
+
nameOrConfig.elementOptions === void 0
|
|
3313
|
+
? defaultElementOptions
|
|
3314
|
+
: Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
|
|
3315
|
+
this.styles = ElementStyles.normalize(nameOrConfig.styles);
|
|
3316
|
+
fastElementRegistry.register(this);
|
|
3317
|
+
Observable.defineProperty(_a.isRegistered, this.name);
|
|
3318
|
+
_a.isRegistered[this.name] = this.type;
|
|
2852
3319
|
}
|
|
2853
3320
|
/**
|
|
2854
|
-
*
|
|
2855
|
-
* @param
|
|
2856
|
-
* @
|
|
2857
|
-
*
|
|
3321
|
+
* Defines a custom element based on this definition.
|
|
3322
|
+
* @param registry - The element registry to define the element in.
|
|
3323
|
+
* @remarks
|
|
3324
|
+
* This operation is idempotent per registry.
|
|
2858
3325
|
*/
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
this.
|
|
2862
|
-
this.
|
|
2863
|
-
|
|
3326
|
+
define(registry = this.registry) {
|
|
3327
|
+
var _b, _c;
|
|
3328
|
+
const type = this.type;
|
|
3329
|
+
if (!registry.get(this.name)) {
|
|
3330
|
+
this.platformDefined = true;
|
|
3331
|
+
registry.define(this.name, type, this.elementOptions);
|
|
3332
|
+
(_c = (_b = this.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.elementDidDefine) === null || _c === void 0 ? void 0 : _c.call(_b, this.name);
|
|
3333
|
+
}
|
|
3334
|
+
return this;
|
|
2864
3335
|
}
|
|
2865
3336
|
/**
|
|
2866
|
-
*
|
|
2867
|
-
* @param
|
|
2868
|
-
* @
|
|
3337
|
+
* Creates an instance of FASTElementDefinition.
|
|
3338
|
+
* @param type - The type this definition is being created for.
|
|
3339
|
+
* @param nameOrDef - The name of the element to define or a config object
|
|
3340
|
+
* that describes the element to define.
|
|
2869
3341
|
*/
|
|
2870
|
-
|
|
2871
|
-
|
|
3342
|
+
static compose(type, nameOrDef) {
|
|
3343
|
+
if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
|
|
3344
|
+
return new _a(class extends type {
|
|
3345
|
+
}, nameOrDef);
|
|
3346
|
+
}
|
|
3347
|
+
return new _a(type, nameOrDef);
|
|
2872
3348
|
}
|
|
2873
3349
|
/**
|
|
2874
|
-
*
|
|
2875
|
-
* @param
|
|
2876
|
-
* @
|
|
3350
|
+
* Registers a FASTElement base type.
|
|
3351
|
+
* @param type - The type to register as a base type.
|
|
3352
|
+
* @internal
|
|
2877
3353
|
*/
|
|
2878
|
-
|
|
2879
|
-
|
|
3354
|
+
static registerBaseType(type) {
|
|
3355
|
+
fastElementBaseTypes.add(type);
|
|
2880
3356
|
}
|
|
2881
3357
|
/**
|
|
2882
|
-
*
|
|
2883
|
-
*
|
|
2884
|
-
*
|
|
2885
|
-
* @
|
|
2886
|
-
*
|
|
3358
|
+
* Creates an instance of FASTElementDefinition asynchronously. This option assumes
|
|
3359
|
+
* that a template and shadowOptions will be provided and completes when those requirements
|
|
3360
|
+
* are met.
|
|
3361
|
+
* @param type - The type this definition is being created for.
|
|
3362
|
+
* @param nameOrDef - The name of the element to define or a config object
|
|
3363
|
+
* that describes the element to define.
|
|
3364
|
+
* @alpha
|
|
2887
3365
|
*/
|
|
2888
|
-
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
2893
|
-
|
|
3366
|
+
static composeAsync(type, nameOrDef) {
|
|
3367
|
+
return new Promise(resolve => {
|
|
3368
|
+
if (fastElementBaseTypes.has(type) || fastElementRegistry.getByType(type)) {
|
|
3369
|
+
resolve(new _a(class extends type {
|
|
3370
|
+
}, nameOrDef));
|
|
3371
|
+
}
|
|
3372
|
+
const definition = new _a(type, nameOrDef);
|
|
3373
|
+
Observable.getNotifier(definition).subscribe({
|
|
3374
|
+
handleChange: () => {
|
|
3375
|
+
var _b, _c;
|
|
3376
|
+
(_c = (_b = definition.lifecycleCallbacks) === null || _b === void 0 ? void 0 : _b.templateDidUpdate) === null || _c === void 0 ? void 0 : _c.call(_b, definition.name);
|
|
3377
|
+
resolve(definition);
|
|
3378
|
+
},
|
|
3379
|
+
}, "template");
|
|
3380
|
+
});
|
|
2894
3381
|
}
|
|
2895
3382
|
}
|
|
2896
|
-
|
|
2897
|
-
const slotEvent = "slotchange";
|
|
3383
|
+
_a = FASTElementDefinition;
|
|
2898
3384
|
/**
|
|
2899
|
-
* The
|
|
2900
|
-
* @public
|
|
3385
|
+
* The definition has been registered to the FAST element registry.
|
|
2901
3386
|
*/
|
|
2902
|
-
|
|
2903
|
-
/**
|
|
2904
|
-
* Begins observation of the nodes.
|
|
2905
|
-
* @param target - The target to observe.
|
|
2906
|
-
*/
|
|
2907
|
-
observe(target) {
|
|
2908
|
-
target.addEventListener(slotEvent, this);
|
|
2909
|
-
}
|
|
2910
|
-
/**
|
|
2911
|
-
* Disconnects observation of the nodes.
|
|
2912
|
-
* @param target - The target to unobserve.
|
|
2913
|
-
*/
|
|
2914
|
-
disconnect(target) {
|
|
2915
|
-
target.removeEventListener(slotEvent, this);
|
|
2916
|
-
}
|
|
2917
|
-
/**
|
|
2918
|
-
* Retrieves the raw nodes that should be assigned to the source property.
|
|
2919
|
-
* @param target - The target to get the node to.
|
|
2920
|
-
*/
|
|
2921
|
-
getNodes(target) {
|
|
2922
|
-
return target.assignedNodes(this.options);
|
|
2923
|
-
}
|
|
2924
|
-
/** @internal */
|
|
2925
|
-
handleEvent(event) {
|
|
2926
|
-
const target = event.currentTarget;
|
|
2927
|
-
this.updateTarget(this.getSource(target), this.computeNodes(target));
|
|
2928
|
-
}
|
|
2929
|
-
}
|
|
2930
|
-
HTMLDirective.define(SlottedDirective);
|
|
3387
|
+
FASTElementDefinition.isRegistered = {};
|
|
2931
3388
|
/**
|
|
2932
|
-
*
|
|
2933
|
-
*
|
|
2934
|
-
* @param propertyOrOptions - The options used to configure slotted node observation.
|
|
2935
|
-
* @public
|
|
3389
|
+
* Gets the element definition associated with the specified type.
|
|
3390
|
+
* @param type - The custom element type to retrieve the definition for.
|
|
2936
3391
|
*/
|
|
2937
|
-
|
|
2938
|
-
if (isString(propertyOrOptions)) {
|
|
2939
|
-
propertyOrOptions = { property: propertyOrOptions };
|
|
2940
|
-
}
|
|
2941
|
-
return new SlottedDirective(propertyOrOptions);
|
|
2942
|
-
}
|
|
2943
|
-
|
|
2944
|
-
const booleanMode = "boolean";
|
|
2945
|
-
const reflectMode = "reflect";
|
|
3392
|
+
FASTElementDefinition.getByType = fastElementRegistry.getByType;
|
|
2946
3393
|
/**
|
|
2947
|
-
*
|
|
2948
|
-
* @
|
|
3394
|
+
* Gets the element definition associated with the instance.
|
|
3395
|
+
* @param instance - The custom element instance to retrieve the definition for.
|
|
2949
3396
|
*/
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
3397
|
+
FASTElementDefinition.getForInstance = fastElementRegistry.getForInstance;
|
|
3398
|
+
/**
|
|
3399
|
+
* Indicates when a custom elements definition has been registered with the fastElementRegistry.
|
|
3400
|
+
* @param name - The name of the defined custom element.
|
|
3401
|
+
* @alpha
|
|
3402
|
+
*/
|
|
3403
|
+
FASTElementDefinition.registerAsync = (name) => __awaiter(void 0, void 0, void 0, function* () {
|
|
3404
|
+
return new Promise(resolve => {
|
|
3405
|
+
if (_a.isRegistered[name]) {
|
|
3406
|
+
resolve(_a.isRegistered[name]);
|
|
3407
|
+
}
|
|
3408
|
+
Observable.getNotifier(_a.isRegistered).subscribe({ handleChange: () => resolve(_a.isRegistered[name]) }, name);
|
|
3409
|
+
});
|
|
2955
3410
|
});
|
|
3411
|
+
Observable.defineProperty(FASTElementDefinition.prototype, "template");
|
|
3412
|
+
|
|
3413
|
+
// Much thanks to LitHTML for working this out!
|
|
3414
|
+
const lastAttributeNameRegex =
|
|
3415
|
+
/* eslint-disable-next-line no-control-regex, max-len */
|
|
3416
|
+
/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
|
|
3417
|
+
const noFactories = Object.create(null);
|
|
2956
3418
|
/**
|
|
2957
|
-
*
|
|
2958
|
-
* @remarks
|
|
2959
|
-
* Used automatically when the `boolean` {@link AttributeMode} is selected.
|
|
3419
|
+
* Inlines a template into another template.
|
|
2960
3420
|
* @public
|
|
2961
3421
|
*/
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
3422
|
+
class InlineTemplateDirective {
|
|
3423
|
+
/**
|
|
3424
|
+
* Creates an instance of InlineTemplateDirective.
|
|
3425
|
+
* @param template - The template to inline.
|
|
3426
|
+
*/
|
|
3427
|
+
constructor(html, factories = noFactories) {
|
|
3428
|
+
this.html = html;
|
|
3429
|
+
this.factories = factories;
|
|
3430
|
+
}
|
|
3431
|
+
/**
|
|
3432
|
+
* Creates HTML to be used within a template.
|
|
3433
|
+
* @param add - Can be used to add behavior factories to a template.
|
|
3434
|
+
*/
|
|
3435
|
+
createHTML(add) {
|
|
3436
|
+
const factories = this.factories;
|
|
3437
|
+
for (const key in factories) {
|
|
3438
|
+
add(factories[key]);
|
|
3439
|
+
}
|
|
3440
|
+
return this.html;
|
|
2979
3441
|
}
|
|
2980
|
-
const number = value * 1;
|
|
2981
|
-
return isNaN(number) ? null : number;
|
|
2982
3442
|
}
|
|
2983
3443
|
/**
|
|
2984
|
-
*
|
|
2985
|
-
* @remarks
|
|
2986
|
-
* This converter allows for nullable numbers, returning `null` if the
|
|
2987
|
-
* input was `null`, `undefined`, or `NaN`.
|
|
2988
|
-
* @public
|
|
3444
|
+
* An empty template partial.
|
|
2989
3445
|
*/
|
|
2990
|
-
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
3446
|
+
InlineTemplateDirective.empty = new InlineTemplateDirective("");
|
|
3447
|
+
HTMLDirective.define(InlineTemplateDirective);
|
|
3448
|
+
function createHTML(value, prevString, add, definition = HTMLDirective.getForInstance(value)) {
|
|
3449
|
+
if (definition.aspected) {
|
|
3450
|
+
const match = lastAttributeNameRegex.exec(prevString);
|
|
3451
|
+
if (match !== null) {
|
|
3452
|
+
HTMLDirective.assignAspect(value, match[2]);
|
|
3453
|
+
}
|
|
3454
|
+
}
|
|
3455
|
+
return value.createHTML(add);
|
|
3456
|
+
}
|
|
2997
3457
|
/**
|
|
2998
|
-
*
|
|
2999
|
-
* change callbacks, attribute reflection, and type conversion for
|
|
3000
|
-
* custom elements.
|
|
3458
|
+
* A template capable of creating HTMLView instances or rendering directly to DOM.
|
|
3001
3459
|
* @public
|
|
3002
3460
|
*/
|
|
3003
|
-
class
|
|
3461
|
+
class ViewTemplate {
|
|
3004
3462
|
/**
|
|
3005
|
-
* Creates an instance of
|
|
3006
|
-
* @param
|
|
3007
|
-
* @param
|
|
3008
|
-
* @param
|
|
3009
|
-
* @param mode - The {@link AttributeMode} that describes the behavior of this attribute.
|
|
3010
|
-
* @param converter - A {@link ValueConverter} that integrates with the property getter/setter
|
|
3011
|
-
* to convert values to and from a DOM string.
|
|
3463
|
+
* Creates an instance of ViewTemplate.
|
|
3464
|
+
* @param html - The html representing what this template will instantiate, including placeholders for directives.
|
|
3465
|
+
* @param factories - The directives that will be connected to placeholders in the html.
|
|
3466
|
+
* @param policy - The security policy to use when compiling this template.
|
|
3012
3467
|
*/
|
|
3013
|
-
constructor(
|
|
3014
|
-
this.
|
|
3015
|
-
this.
|
|
3016
|
-
this.
|
|
3017
|
-
this.
|
|
3018
|
-
this.mode = mode;
|
|
3019
|
-
this.converter = converter;
|
|
3020
|
-
this.fieldName = `_${name}`;
|
|
3021
|
-
this.callbackName = `${name}Changed`;
|
|
3022
|
-
this.hasCallback = this.callbackName in Owner.prototype;
|
|
3023
|
-
if (mode === booleanMode && converter === void 0) {
|
|
3024
|
-
this.converter = booleanConverter;
|
|
3025
|
-
}
|
|
3468
|
+
constructor(html, factories = {}, policy) {
|
|
3469
|
+
this.policy = policy;
|
|
3470
|
+
this.result = null;
|
|
3471
|
+
this.html = html;
|
|
3472
|
+
this.factories = factories;
|
|
3026
3473
|
}
|
|
3027
3474
|
/**
|
|
3028
|
-
*
|
|
3029
|
-
* @param source - The source element to access.
|
|
3030
|
-
* @param value - The value to set the attribute/property to.
|
|
3475
|
+
* @internal
|
|
3031
3476
|
*/
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3035
|
-
if (converter !== void 0) {
|
|
3036
|
-
newValue = converter.fromView(newValue);
|
|
3037
|
-
}
|
|
3038
|
-
if (oldValue !== newValue) {
|
|
3039
|
-
source[this.fieldName] = newValue;
|
|
3040
|
-
this.tryReflectToAttribute(source);
|
|
3041
|
-
if (this.hasCallback) {
|
|
3042
|
-
source[this.callbackName](oldValue, newValue);
|
|
3043
|
-
}
|
|
3044
|
-
source.$fastController.notify(this.name);
|
|
3477
|
+
compile() {
|
|
3478
|
+
if (this.result === null) {
|
|
3479
|
+
this.result = Compiler.compile(this.html, this.factories, this.policy);
|
|
3045
3480
|
}
|
|
3481
|
+
return this.result;
|
|
3046
3482
|
}
|
|
3047
3483
|
/**
|
|
3048
|
-
*
|
|
3049
|
-
* @param
|
|
3484
|
+
* Creates an HTMLView instance based on this template definition.
|
|
3485
|
+
* @param hostBindingTarget - The element that host behaviors will be bound to.
|
|
3050
3486
|
*/
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
return source[this.fieldName];
|
|
3487
|
+
create(hostBindingTarget) {
|
|
3488
|
+
return this.compile().createView(hostBindingTarget);
|
|
3054
3489
|
}
|
|
3055
|
-
/**
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
this.guards.add(element);
|
|
3061
|
-
this.setValue(element, value);
|
|
3062
|
-
this.guards.delete(element);
|
|
3490
|
+
/**
|
|
3491
|
+
* Returns a directive that can inline the template.
|
|
3492
|
+
*/
|
|
3493
|
+
inline() {
|
|
3494
|
+
return new InlineTemplateDirective(isString(this.html) ? this.html : this.html.innerHTML, this.factories);
|
|
3063
3495
|
}
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3496
|
+
/**
|
|
3497
|
+
* Sets the DOMPolicy for this template.
|
|
3498
|
+
* @param policy - The policy to associated with this template.
|
|
3499
|
+
* @returns The modified template instance.
|
|
3500
|
+
* @remarks
|
|
3501
|
+
* The DOMPolicy can only be set once for a template and cannot be
|
|
3502
|
+
* set after the template is compiled.
|
|
3503
|
+
*/
|
|
3504
|
+
withPolicy(policy) {
|
|
3505
|
+
if (this.result) {
|
|
3506
|
+
throw FAST.error(Message.cannotSetTemplatePolicyAfterCompilation);
|
|
3069
3507
|
}
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3074
|
-
|
|
3075
|
-
const converter = this.converter;
|
|
3076
|
-
DOM.setAttribute(element, this.attribute, converter !== void 0 ? converter.toView(latestValue) : latestValue);
|
|
3077
|
-
break;
|
|
3078
|
-
case booleanMode:
|
|
3079
|
-
DOM.setBooleanAttribute(element, this.attribute, latestValue);
|
|
3080
|
-
break;
|
|
3081
|
-
}
|
|
3082
|
-
guards.delete(element);
|
|
3083
|
-
});
|
|
3508
|
+
if (this.policy) {
|
|
3509
|
+
throw FAST.error(Message.onlySetTemplatePolicyOnce);
|
|
3510
|
+
}
|
|
3511
|
+
this.policy = policy;
|
|
3512
|
+
return this;
|
|
3084
3513
|
}
|
|
3085
3514
|
/**
|
|
3086
|
-
*
|
|
3087
|
-
* @param
|
|
3088
|
-
* @param
|
|
3089
|
-
* @
|
|
3515
|
+
* Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
|
|
3516
|
+
* @param source - The data source to bind the template to.
|
|
3517
|
+
* @param host - The Element where the template will be rendered.
|
|
3518
|
+
* @param hostBindingTarget - An HTML element to target the host bindings at if different from the
|
|
3519
|
+
* host that the template is being attached to.
|
|
3090
3520
|
*/
|
|
3091
|
-
|
|
3092
|
-
const
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3521
|
+
render(source, host, hostBindingTarget) {
|
|
3522
|
+
const view = this.create(hostBindingTarget);
|
|
3523
|
+
view.bind(source);
|
|
3524
|
+
view.appendTo(host);
|
|
3525
|
+
return view;
|
|
3526
|
+
}
|
|
3527
|
+
/**
|
|
3528
|
+
* Processes the tagged template literal's static strings and interpolated values and
|
|
3529
|
+
* creates a ViewTemplate.
|
|
3530
|
+
*
|
|
3531
|
+
* For each interpolated value:
|
|
3532
|
+
* 1. Functions (binding expressions, e.g., `x => x.name`) → wrapped in a one-way HTMLBindingDirective
|
|
3533
|
+
* 2. Binding instances → wrapped in an HTMLBindingDirective
|
|
3534
|
+
* 3. HTMLDirective instances → used as-is
|
|
3535
|
+
* 4. Static values (strings, numbers) → wrapped in a one-time HTMLBindingDirective
|
|
3536
|
+
*
|
|
3537
|
+
* Each directive's createHTML() is called with an `add` callback that registers
|
|
3538
|
+
* the factory in the factories record under a unique ID and returns that ID.
|
|
3539
|
+
* The directive inserts a placeholder marker (e.g., `fast-abc123{0}fast-abc123`) into
|
|
3540
|
+
* the HTML string so the compiler can later find and associate it with the factory.
|
|
3541
|
+
*
|
|
3542
|
+
* Aspect detection happens here too: the `lastAttributeNameRegex` checks whether
|
|
3543
|
+
* the placeholder appears inside an attribute value, and if so, assignAspect()
|
|
3544
|
+
* sets the correct DOMAspect (attribute, property, event, etc.) based on the
|
|
3545
|
+
* attribute name prefix.
|
|
3546
|
+
*
|
|
3547
|
+
* @param strings - The static strings to create the template with.
|
|
3548
|
+
* @param values - The dynamic values to create the template with.
|
|
3549
|
+
* @param policy - The DOMPolicy to associated with the template.
|
|
3550
|
+
* @returns A ViewTemplate.
|
|
3551
|
+
* @remarks
|
|
3552
|
+
* This API should not be used directly under normal circumstances because constructing
|
|
3553
|
+
* a template in this way, if not done properly, can open up the application to XSS
|
|
3554
|
+
* attacks. When using this API, provide a strong DOMPolicy that can properly sanitize
|
|
3555
|
+
* and also be sure to manually sanitize all static strings particularly if they can
|
|
3556
|
+
* come from user input.
|
|
3557
|
+
*/
|
|
3558
|
+
static create(strings, values, policy) {
|
|
3559
|
+
let html = "";
|
|
3560
|
+
const factories = Object.create(null);
|
|
3561
|
+
const add = (factory) => {
|
|
3562
|
+
var _a;
|
|
3563
|
+
const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
|
|
3564
|
+
factories[id] = factory;
|
|
3565
|
+
return id;
|
|
3566
|
+
};
|
|
3567
|
+
for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
|
|
3568
|
+
const currentString = strings[i];
|
|
3569
|
+
let currentValue = values[i];
|
|
3570
|
+
let definition;
|
|
3571
|
+
html += currentString;
|
|
3572
|
+
if (isFunction(currentValue)) {
|
|
3573
|
+
currentValue = new HTMLBindingDirective(oneWay(currentValue));
|
|
3098
3574
|
}
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
if (isString(config)) {
|
|
3102
|
-
attributes.push(new AttributeDefinition(Owner, config));
|
|
3103
|
-
}
|
|
3104
|
-
else {
|
|
3105
|
-
attributes.push(new AttributeDefinition(Owner, config.property, config.attribute, config.mode, config.converter));
|
|
3106
|
-
}
|
|
3575
|
+
else if (currentValue instanceof Binding) {
|
|
3576
|
+
currentValue = new HTMLBindingDirective(currentValue);
|
|
3107
3577
|
}
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
}
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
if (arguments.length > 1) {
|
|
3116
|
-
// Non invocation:
|
|
3117
|
-
// - @attr
|
|
3118
|
-
// Invocation with or w/o opts:
|
|
3119
|
-
// - @attr()
|
|
3120
|
-
// - @attr({...opts})
|
|
3121
|
-
config.property = $prop;
|
|
3122
|
-
}
|
|
3123
|
-
AttributeConfiguration.locate($target.constructor).push(config);
|
|
3124
|
-
}
|
|
3125
|
-
if (arguments.length > 1) {
|
|
3126
|
-
// Non invocation:
|
|
3127
|
-
// - @attr
|
|
3128
|
-
config = {};
|
|
3129
|
-
decorator(configOrTarget, prop);
|
|
3130
|
-
return;
|
|
3578
|
+
else if (!(definition = HTMLDirective.getForInstance(currentValue))) {
|
|
3579
|
+
const staticValue = currentValue;
|
|
3580
|
+
currentValue = new HTMLBindingDirective(oneTime(() => staticValue));
|
|
3581
|
+
}
|
|
3582
|
+
html += createHTML(currentValue, currentString, add, definition);
|
|
3583
|
+
}
|
|
3584
|
+
return new ViewTemplate(html + strings[strings.length - 1], factories, policy);
|
|
3131
3585
|
}
|
|
3132
|
-
// Invocation with or w/o opts:
|
|
3133
|
-
// - @attr()
|
|
3134
|
-
// - @attr({...opts})
|
|
3135
|
-
config = configOrTarget === void 0 ? {} : configOrTarget;
|
|
3136
|
-
return decorator;
|
|
3137
3586
|
}
|
|
3138
|
-
|
|
3139
|
-
const defaultShadowOptions = { mode: "open" };
|
|
3140
|
-
const defaultElementOptions = {};
|
|
3141
|
-
const fastElementBaseTypes = new Set();
|
|
3142
|
-
const fastElementRegistry = FAST.getById(KernelServiceId.elementRegistry, () => createTypeRegistry());
|
|
3587
|
+
makeSerializationNoop(ViewTemplate);
|
|
3143
3588
|
/**
|
|
3144
|
-
*
|
|
3589
|
+
* Transforms a template literal string into a ViewTemplate.
|
|
3590
|
+
* @param strings - The string fragments that are interpolated with the values.
|
|
3591
|
+
* @param values - The values that are interpolated with the string fragments.
|
|
3592
|
+
* @remarks
|
|
3593
|
+
* The html helper supports interpolation of strings, numbers, binding expressions,
|
|
3594
|
+
* other template instances, and Directive instances.
|
|
3145
3595
|
* @public
|
|
3146
3596
|
*/
|
|
3147
|
-
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
this.platformDefined = false;
|
|
3151
|
-
if (isString(nameOrConfig)) {
|
|
3152
|
-
nameOrConfig = { name: nameOrConfig };
|
|
3153
|
-
}
|
|
3154
|
-
this.type = type;
|
|
3155
|
-
this.name = nameOrConfig.name;
|
|
3156
|
-
this.template = nameOrConfig.template;
|
|
3157
|
-
this.registry = (_a = nameOrConfig.registry) !== null && _a !== void 0 ? _a : customElements;
|
|
3158
|
-
const proto = type.prototype;
|
|
3159
|
-
const attributes = AttributeDefinition.collect(type, nameOrConfig.attributes);
|
|
3160
|
-
const observedAttributes = new Array(attributes.length);
|
|
3161
|
-
const propertyLookup = {};
|
|
3162
|
-
const attributeLookup = {};
|
|
3163
|
-
for (let i = 0, ii = attributes.length; i < ii; ++i) {
|
|
3164
|
-
const current = attributes[i];
|
|
3165
|
-
observedAttributes[i] = current.attribute;
|
|
3166
|
-
propertyLookup[current.name] = current;
|
|
3167
|
-
attributeLookup[current.attribute] = current;
|
|
3168
|
-
Observable.defineProperty(proto, current);
|
|
3169
|
-
}
|
|
3170
|
-
Reflect.defineProperty(type, "observedAttributes", {
|
|
3171
|
-
value: observedAttributes,
|
|
3172
|
-
enumerable: true,
|
|
3173
|
-
});
|
|
3174
|
-
this.attributes = attributes;
|
|
3175
|
-
this.propertyLookup = propertyLookup;
|
|
3176
|
-
this.attributeLookup = attributeLookup;
|
|
3177
|
-
this.shadowOptions =
|
|
3178
|
-
nameOrConfig.shadowOptions === void 0
|
|
3179
|
-
? defaultShadowOptions
|
|
3180
|
-
: nameOrConfig.shadowOptions === null
|
|
3181
|
-
? void 0
|
|
3182
|
-
: Object.assign(Object.assign({}, defaultShadowOptions), nameOrConfig.shadowOptions);
|
|
3183
|
-
this.elementOptions =
|
|
3184
|
-
nameOrConfig.elementOptions === void 0
|
|
3185
|
-
? defaultElementOptions
|
|
3186
|
-
: Object.assign(Object.assign({}, defaultElementOptions), nameOrConfig.elementOptions);
|
|
3187
|
-
this.styles = ElementStyles.normalize(nameOrConfig.styles);
|
|
3188
|
-
fastElementRegistry.register(this);
|
|
3597
|
+
const html = ((strings, ...values) => {
|
|
3598
|
+
if (Array.isArray(strings) && Array.isArray(strings.raw)) {
|
|
3599
|
+
return ViewTemplate.create(strings, values);
|
|
3189
3600
|
}
|
|
3601
|
+
throw FAST.error(Message.directCallToHTMLTagNotAllowed);
|
|
3602
|
+
});
|
|
3603
|
+
html.partial = (html) => {
|
|
3604
|
+
return new InlineTemplateDirective(html);
|
|
3605
|
+
};
|
|
3606
|
+
|
|
3607
|
+
const slotEvent = "slotchange";
|
|
3608
|
+
/**
|
|
3609
|
+
* The runtime behavior for slotted node observation.
|
|
3610
|
+
* @public
|
|
3611
|
+
*/
|
|
3612
|
+
class SlottedDirective extends NodeObservationDirective {
|
|
3190
3613
|
/**
|
|
3191
|
-
*
|
|
3614
|
+
* Begins observation of the nodes.
|
|
3615
|
+
* @param target - The target to observe.
|
|
3192
3616
|
*/
|
|
3193
|
-
|
|
3194
|
-
|
|
3617
|
+
observe(target) {
|
|
3618
|
+
target.addEventListener(slotEvent, this);
|
|
3195
3619
|
}
|
|
3196
3620
|
/**
|
|
3197
|
-
*
|
|
3198
|
-
* @param
|
|
3199
|
-
* @remarks
|
|
3200
|
-
* This operation is idempotent per registry.
|
|
3621
|
+
* Disconnects observation of the nodes.
|
|
3622
|
+
* @param target - The target to unobserve.
|
|
3201
3623
|
*/
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
if (!registry.get(this.name)) {
|
|
3205
|
-
this.platformDefined = true;
|
|
3206
|
-
registry.define(this.name, type, this.elementOptions);
|
|
3207
|
-
}
|
|
3208
|
-
return this;
|
|
3624
|
+
disconnect(target) {
|
|
3625
|
+
target.removeEventListener(slotEvent, this);
|
|
3209
3626
|
}
|
|
3210
3627
|
/**
|
|
3211
|
-
*
|
|
3212
|
-
* @param
|
|
3213
|
-
* @param nameOrDef - The name of the element to define or a config object
|
|
3214
|
-
* that describes the element to define.
|
|
3628
|
+
* Retrieves the raw nodes that should be assigned to the source property.
|
|
3629
|
+
* @param target - The target to get the node to.
|
|
3215
3630
|
*/
|
|
3216
|
-
|
|
3217
|
-
|
|
3218
|
-
return new FASTElementDefinition(class extends type {
|
|
3219
|
-
}, nameOrDef);
|
|
3220
|
-
}
|
|
3221
|
-
return new FASTElementDefinition(type, nameOrDef);
|
|
3631
|
+
getNodes(target) {
|
|
3632
|
+
return target.assignedNodes(this.options);
|
|
3222
3633
|
}
|
|
3223
|
-
/**
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
*/
|
|
3228
|
-
static registerBaseType(type) {
|
|
3229
|
-
fastElementBaseTypes.add(type);
|
|
3634
|
+
/** @internal */
|
|
3635
|
+
handleEvent(event) {
|
|
3636
|
+
const target = event.currentTarget;
|
|
3637
|
+
this.updateTarget(this.getSource(target), this.computeNodes(target));
|
|
3230
3638
|
}
|
|
3231
3639
|
}
|
|
3640
|
+
HTMLDirective.define(SlottedDirective);
|
|
3232
3641
|
/**
|
|
3233
|
-
*
|
|
3234
|
-
*
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
/**
|
|
3238
|
-
* Gets the element definition associated with the instance.
|
|
3239
|
-
* @param instance - The custom element instance to retrieve the definition for.
|
|
3642
|
+
* A directive that observes the `assignedNodes()` of a slot and updates a property
|
|
3643
|
+
* whenever they change.
|
|
3644
|
+
* @param propertyOrOptions - The options used to configure slotted node observation.
|
|
3645
|
+
* @public
|
|
3240
3646
|
*/
|
|
3241
|
-
|
|
3647
|
+
function slotted(propertyOrOptions) {
|
|
3648
|
+
if (isString(propertyOrOptions)) {
|
|
3649
|
+
propertyOrOptions = { property: propertyOrOptions };
|
|
3650
|
+
}
|
|
3651
|
+
return new SlottedDirective(propertyOrOptions);
|
|
3652
|
+
}
|
|
3242
3653
|
|
|
3243
3654
|
/**
|
|
3244
3655
|
* An extension of MutationObserver that supports unobserving nodes.
|
|
@@ -3346,92 +3757,33 @@ function getShadowRoot(element) {
|
|
|
3346
3757
|
return (_b = (_a = element.shadowRoot) !== null && _a !== void 0 ? _a : shadowRoots.get(element)) !== null && _b !== void 0 ? _b : null;
|
|
3347
3758
|
}
|
|
3348
3759
|
let elementControllerStrategy;
|
|
3760
|
+
/**
|
|
3761
|
+
* The various lifecycle stages of an ElementController.
|
|
3762
|
+
* @public
|
|
3763
|
+
*/
|
|
3764
|
+
var Stages;
|
|
3765
|
+
(function (Stages) {
|
|
3766
|
+
/** The element is in the process of connecting. */
|
|
3767
|
+
Stages[Stages["connecting"] = 0] = "connecting";
|
|
3768
|
+
/** The element is connected. */
|
|
3769
|
+
Stages[Stages["connected"] = 1] = "connected";
|
|
3770
|
+
/** The element is in the process of disconnecting. */
|
|
3771
|
+
Stages[Stages["disconnecting"] = 2] = "disconnecting";
|
|
3772
|
+
/** The element is disconnected. */
|
|
3773
|
+
Stages[Stages["disconnected"] = 3] = "disconnected";
|
|
3774
|
+
})(Stages || (Stages = {}));
|
|
3349
3775
|
/**
|
|
3350
3776
|
* Controls the lifecycle and rendering of a `FASTElement`.
|
|
3351
3777
|
* @public
|
|
3352
3778
|
*/
|
|
3353
3779
|
class ElementController extends PropertyChangeNotifier {
|
|
3354
|
-
/**
|
|
3355
|
-
* Creates a Controller to control the specified element.
|
|
3356
|
-
* @param element - The element to be controlled by this controller.
|
|
3357
|
-
* @param definition - The element definition metadata that instructs this
|
|
3358
|
-
* controller in how to handle rendering and other platform integrations.
|
|
3359
|
-
* @internal
|
|
3360
|
-
*/
|
|
3361
|
-
constructor(element, definition) {
|
|
3362
|
-
super(element);
|
|
3363
|
-
this.boundObservables = null;
|
|
3364
|
-
this.needsInitialization = true;
|
|
3365
|
-
this.hasExistingShadowRoot = false;
|
|
3366
|
-
this._template = null;
|
|
3367
|
-
this.stage = 3 /* Stages.disconnected */;
|
|
3368
|
-
/**
|
|
3369
|
-
* A guard against connecting behaviors multiple times
|
|
3370
|
-
* during connect in scenarios where a behavior adds
|
|
3371
|
-
* another behavior during it's connectedCallback
|
|
3372
|
-
*/
|
|
3373
|
-
this.guardBehaviorConnection = false;
|
|
3374
|
-
this.behaviors = null;
|
|
3375
|
-
/**
|
|
3376
|
-
* Tracks whether behaviors are connected so that
|
|
3377
|
-
* behaviors cant be connected multiple times
|
|
3378
|
-
*/
|
|
3379
|
-
this.behaviorsConnected = false;
|
|
3380
|
-
this._mainStyles = null;
|
|
3381
|
-
/**
|
|
3382
|
-
* This allows Observable.getNotifier(...) to return the Controller
|
|
3383
|
-
* when the notifier for the Controller itself is being requested. The
|
|
3384
|
-
* result is that the Observable system does not need to create a separate
|
|
3385
|
-
* instance of Notifier for observables on the Controller. The component and
|
|
3386
|
-
* the controller will now share the same notifier, removing one-object construct
|
|
3387
|
-
* per web component instance.
|
|
3388
|
-
*/
|
|
3389
|
-
this.$fastController = this;
|
|
3390
|
-
/**
|
|
3391
|
-
* The view associated with the custom element.
|
|
3392
|
-
* @remarks
|
|
3393
|
-
* If `null` then the element is managing its own rendering.
|
|
3394
|
-
*/
|
|
3395
|
-
this.view = null;
|
|
3396
|
-
this.source = element;
|
|
3397
|
-
this.definition = definition;
|
|
3398
|
-
const shadowOptions = definition.shadowOptions;
|
|
3399
|
-
if (shadowOptions !== void 0) {
|
|
3400
|
-
let shadowRoot = element.shadowRoot;
|
|
3401
|
-
if (shadowRoot) {
|
|
3402
|
-
this.hasExistingShadowRoot = true;
|
|
3403
|
-
}
|
|
3404
|
-
else {
|
|
3405
|
-
shadowRoot = element.attachShadow(shadowOptions);
|
|
3406
|
-
if (shadowOptions.mode === "closed") {
|
|
3407
|
-
shadowRoots.set(element, shadowRoot);
|
|
3408
|
-
}
|
|
3409
|
-
}
|
|
3410
|
-
}
|
|
3411
|
-
// Capture any observable values that were set by the binding engine before
|
|
3412
|
-
// the browser upgraded the element. Then delete the property since it will
|
|
3413
|
-
// shadow the getter/setter that is required to make the observable operate.
|
|
3414
|
-
// Later, in the connect callback, we'll re-apply the values.
|
|
3415
|
-
const accessors = Observable.getAccessors(element);
|
|
3416
|
-
if (accessors.length > 0) {
|
|
3417
|
-
const boundObservables = (this.boundObservables = Object.create(null));
|
|
3418
|
-
for (let i = 0, ii = accessors.length; i < ii; ++i) {
|
|
3419
|
-
const propertyName = accessors[i].name;
|
|
3420
|
-
const value = element[propertyName];
|
|
3421
|
-
if (value !== void 0) {
|
|
3422
|
-
delete element[propertyName];
|
|
3423
|
-
boundObservables[propertyName] = value;
|
|
3424
|
-
}
|
|
3425
|
-
}
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
3780
|
/**
|
|
3429
3781
|
* Indicates whether or not the custom element has been
|
|
3430
3782
|
* connected to the document.
|
|
3431
3783
|
*/
|
|
3432
3784
|
get isConnected() {
|
|
3433
3785
|
Observable.track(this, isConnectedPropertyName);
|
|
3434
|
-
return this.stage ===
|
|
3786
|
+
return this.stage === Stages.connected;
|
|
3435
3787
|
}
|
|
3436
3788
|
/**
|
|
3437
3789
|
* The context the expression is evaluated against.
|
|
@@ -3484,6 +3836,28 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3484
3836
|
this.renderTemplate(value);
|
|
3485
3837
|
}
|
|
3486
3838
|
}
|
|
3839
|
+
/**
|
|
3840
|
+
* The shadow root options for the component.
|
|
3841
|
+
*/
|
|
3842
|
+
get shadowOptions() {
|
|
3843
|
+
return this._shadowRootOptions;
|
|
3844
|
+
}
|
|
3845
|
+
set shadowOptions(value) {
|
|
3846
|
+
// options on the shadowRoot can only be set once
|
|
3847
|
+
if (this._shadowRootOptions === void 0 && value !== void 0) {
|
|
3848
|
+
this._shadowRootOptions = value;
|
|
3849
|
+
let shadowRoot = this.source.shadowRoot;
|
|
3850
|
+
if (shadowRoot) {
|
|
3851
|
+
this.hasExistingShadowRoot = true;
|
|
3852
|
+
}
|
|
3853
|
+
else {
|
|
3854
|
+
shadowRoot = this.source.attachShadow(value);
|
|
3855
|
+
if (value.mode === "closed") {
|
|
3856
|
+
shadowRoots.set(this.source, shadowRoot);
|
|
3857
|
+
}
|
|
3858
|
+
}
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3487
3861
|
/**
|
|
3488
3862
|
* The main set of styles used for the component, independent
|
|
3489
3863
|
* of any dynamically added styles.
|
|
@@ -3502,19 +3876,103 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3502
3876
|
this._mainStyles = (_a = definition.styles) !== null && _a !== void 0 ? _a : null;
|
|
3503
3877
|
}
|
|
3504
3878
|
}
|
|
3505
|
-
return this._mainStyles;
|
|
3506
|
-
}
|
|
3507
|
-
set mainStyles(value) {
|
|
3508
|
-
if (this._mainStyles === value) {
|
|
3509
|
-
return;
|
|
3510
|
-
}
|
|
3511
|
-
if (this._mainStyles !== null) {
|
|
3512
|
-
this.removeStyles(this._mainStyles);
|
|
3513
|
-
}
|
|
3514
|
-
this._mainStyles = value;
|
|
3515
|
-
if (!this.needsInitialization) {
|
|
3516
|
-
this.addStyles(value);
|
|
3517
|
-
}
|
|
3879
|
+
return this._mainStyles;
|
|
3880
|
+
}
|
|
3881
|
+
set mainStyles(value) {
|
|
3882
|
+
if (this._mainStyles === value) {
|
|
3883
|
+
return;
|
|
3884
|
+
}
|
|
3885
|
+
if (this._mainStyles !== null) {
|
|
3886
|
+
this.removeStyles(this._mainStyles);
|
|
3887
|
+
}
|
|
3888
|
+
this._mainStyles = value;
|
|
3889
|
+
if (!this.needsInitialization) {
|
|
3890
|
+
this.addStyles(value);
|
|
3891
|
+
}
|
|
3892
|
+
}
|
|
3893
|
+
/**
|
|
3894
|
+
* Creates a Controller to control the specified element.
|
|
3895
|
+
* @param element - The element to be controlled by this controller.
|
|
3896
|
+
* @param definition - The element definition metadata that instructs this
|
|
3897
|
+
* controller in how to handle rendering and other platform integrations.
|
|
3898
|
+
* @internal
|
|
3899
|
+
*/
|
|
3900
|
+
constructor(element, definition) {
|
|
3901
|
+
super(element);
|
|
3902
|
+
/**
|
|
3903
|
+
* A map of observable properties that were set on the element before upgrade.
|
|
3904
|
+
*/
|
|
3905
|
+
this.boundObservables = null;
|
|
3906
|
+
/**
|
|
3907
|
+
* Indicates whether the controller needs to perform initial rendering.
|
|
3908
|
+
*/
|
|
3909
|
+
this.needsInitialization = true;
|
|
3910
|
+
/**
|
|
3911
|
+
* Indicates whether the element has an existing shadow root (e.g. from declarative shadow DOM).
|
|
3912
|
+
*/
|
|
3913
|
+
this.hasExistingShadowRoot = false;
|
|
3914
|
+
/**
|
|
3915
|
+
* The template used to render the component.
|
|
3916
|
+
*/
|
|
3917
|
+
this._template = null;
|
|
3918
|
+
/**
|
|
3919
|
+
* The current lifecycle stage of the controller.
|
|
3920
|
+
*/
|
|
3921
|
+
this.stage = Stages.disconnected;
|
|
3922
|
+
/**
|
|
3923
|
+
* A guard against connecting behaviors multiple times
|
|
3924
|
+
* during connect in scenarios where a behavior adds
|
|
3925
|
+
* another behavior during it's connectedCallback
|
|
3926
|
+
*/
|
|
3927
|
+
this.guardBehaviorConnection = false;
|
|
3928
|
+
/**
|
|
3929
|
+
* The behaviors associated with the component.
|
|
3930
|
+
*/
|
|
3931
|
+
this.behaviors = null;
|
|
3932
|
+
/**
|
|
3933
|
+
* Tracks whether behaviors are connected so that
|
|
3934
|
+
* behaviors cant be connected multiple times
|
|
3935
|
+
*/
|
|
3936
|
+
this.behaviorsConnected = false;
|
|
3937
|
+
/**
|
|
3938
|
+
* The main set of styles used for the component, independent of any
|
|
3939
|
+
* dynamically added styles.
|
|
3940
|
+
*/
|
|
3941
|
+
this._mainStyles = null;
|
|
3942
|
+
/**
|
|
3943
|
+
* This allows Observable.getNotifier(...) to return the Controller
|
|
3944
|
+
* when the notifier for the Controller itself is being requested. The
|
|
3945
|
+
* result is that the Observable system does not need to create a separate
|
|
3946
|
+
* instance of Notifier for observables on the Controller. The component and
|
|
3947
|
+
* the controller will now share the same notifier, removing one-object construct
|
|
3948
|
+
* per web component instance.
|
|
3949
|
+
*/
|
|
3950
|
+
this.$fastController = this;
|
|
3951
|
+
/**
|
|
3952
|
+
* The view associated with the custom element.
|
|
3953
|
+
* @remarks
|
|
3954
|
+
* If `null` then the element is managing its own rendering.
|
|
3955
|
+
*/
|
|
3956
|
+
this.view = null;
|
|
3957
|
+
this.source = element;
|
|
3958
|
+
this.definition = definition;
|
|
3959
|
+
this.shadowOptions = definition.shadowOptions;
|
|
3960
|
+
// Capture any observable values that were set by the binding engine before
|
|
3961
|
+
// the browser upgraded the element. Then delete the property since it will
|
|
3962
|
+
// shadow the getter/setter that is required to make the observable operate.
|
|
3963
|
+
// Later, in the connect callback, we'll re-apply the values.
|
|
3964
|
+
const accessors = Observable.getAccessors(element);
|
|
3965
|
+
if (accessors.length > 0) {
|
|
3966
|
+
const boundObservables = (this.boundObservables = Object.create(null));
|
|
3967
|
+
for (let i = 0, ii = accessors.length; i < ii; ++i) {
|
|
3968
|
+
const propertyName = accessors[i].name;
|
|
3969
|
+
const value = element[propertyName];
|
|
3970
|
+
if (value !== void 0) {
|
|
3971
|
+
delete element[propertyName];
|
|
3972
|
+
boundObservables[propertyName] = value;
|
|
3973
|
+
}
|
|
3974
|
+
}
|
|
3975
|
+
}
|
|
3518
3976
|
}
|
|
3519
3977
|
/**
|
|
3520
3978
|
* Registers an unbind handler with the controller.
|
|
@@ -3537,7 +3995,7 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3537
3995
|
behavior.addedCallback && behavior.addedCallback(this);
|
|
3538
3996
|
if (behavior.connectedCallback &&
|
|
3539
3997
|
!this.guardBehaviorConnection &&
|
|
3540
|
-
(this.stage ===
|
|
3998
|
+
(this.stage === Stages.connected || this.stage === Stages.connecting)) {
|
|
3541
3999
|
behavior.connectedCallback(this);
|
|
3542
4000
|
}
|
|
3543
4001
|
}
|
|
@@ -3561,7 +4019,7 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3561
4019
|
}
|
|
3562
4020
|
if (count === 1 || force) {
|
|
3563
4021
|
targetBehaviors.delete(behavior);
|
|
3564
|
-
if (behavior.disconnectedCallback && this.stage !==
|
|
4022
|
+
if (behavior.disconnectedCallback && this.stage !== Stages.disconnected) {
|
|
3565
4023
|
behavior.disconnectedCallback(this);
|
|
3566
4024
|
}
|
|
3567
4025
|
behavior.removedCallback && behavior.removedCallback(this);
|
|
@@ -3622,10 +4080,10 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3622
4080
|
* Runs connected lifecycle behavior on the associated element.
|
|
3623
4081
|
*/
|
|
3624
4082
|
connect() {
|
|
3625
|
-
if (this.stage !==
|
|
4083
|
+
if (this.stage !== Stages.disconnected) {
|
|
3626
4084
|
return;
|
|
3627
4085
|
}
|
|
3628
|
-
this.stage =
|
|
4086
|
+
this.stage = Stages.connecting;
|
|
3629
4087
|
this.bindObservables();
|
|
3630
4088
|
this.connectBehaviors();
|
|
3631
4089
|
if (this.needsInitialization) {
|
|
@@ -3636,9 +4094,12 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3636
4094
|
else if (this.view !== null) {
|
|
3637
4095
|
this.view.bind(this.source);
|
|
3638
4096
|
}
|
|
3639
|
-
this.stage =
|
|
4097
|
+
this.stage = Stages.connected;
|
|
3640
4098
|
Observable.notify(this, isConnectedPropertyName);
|
|
3641
4099
|
}
|
|
4100
|
+
/**
|
|
4101
|
+
* Binds any observables that were set before upgrade.
|
|
4102
|
+
*/
|
|
3642
4103
|
bindObservables() {
|
|
3643
4104
|
if (this.boundObservables !== null) {
|
|
3644
4105
|
const element = this.source;
|
|
@@ -3651,6 +4112,9 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3651
4112
|
this.boundObservables = null;
|
|
3652
4113
|
}
|
|
3653
4114
|
}
|
|
4115
|
+
/**
|
|
4116
|
+
* Connects any existing behaviors on the associated element.
|
|
4117
|
+
*/
|
|
3654
4118
|
connectBehaviors() {
|
|
3655
4119
|
if (this.behaviorsConnected === false) {
|
|
3656
4120
|
const behaviors = this.behaviors;
|
|
@@ -3664,6 +4128,9 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3664
4128
|
this.behaviorsConnected = true;
|
|
3665
4129
|
}
|
|
3666
4130
|
}
|
|
4131
|
+
/**
|
|
4132
|
+
* Disconnects any behaviors on the associated element.
|
|
4133
|
+
*/
|
|
3667
4134
|
disconnectBehaviors() {
|
|
3668
4135
|
if (this.behaviorsConnected === true) {
|
|
3669
4136
|
const behaviors = this.behaviors;
|
|
@@ -3679,16 +4146,16 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3679
4146
|
* Runs disconnected lifecycle behavior on the associated element.
|
|
3680
4147
|
*/
|
|
3681
4148
|
disconnect() {
|
|
3682
|
-
if (this.stage !==
|
|
4149
|
+
if (this.stage !== Stages.connected) {
|
|
3683
4150
|
return;
|
|
3684
4151
|
}
|
|
3685
|
-
this.stage =
|
|
4152
|
+
this.stage = Stages.disconnecting;
|
|
3686
4153
|
Observable.notify(this, isConnectedPropertyName);
|
|
3687
4154
|
if (this.view !== null) {
|
|
3688
4155
|
this.view.unbind();
|
|
3689
4156
|
}
|
|
3690
4157
|
this.disconnectBehaviors();
|
|
3691
|
-
this.stage =
|
|
4158
|
+
this.stage = Stages.disconnected;
|
|
3692
4159
|
}
|
|
3693
4160
|
/**
|
|
3694
4161
|
* Runs the attribute changed callback for the associated element.
|
|
@@ -3711,11 +4178,18 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3711
4178
|
* Only emits events if connected.
|
|
3712
4179
|
*/
|
|
3713
4180
|
emit(type, detail, options) {
|
|
3714
|
-
if (this.stage ===
|
|
4181
|
+
if (this.stage === Stages.connected) {
|
|
3715
4182
|
return this.source.dispatchEvent(new CustomEvent(type, Object.assign(Object.assign({ detail }, defaultEventOptions), options)));
|
|
3716
4183
|
}
|
|
3717
4184
|
return false;
|
|
3718
4185
|
}
|
|
4186
|
+
/**
|
|
4187
|
+
* Renders the provided template to the element.
|
|
4188
|
+
*
|
|
4189
|
+
* @param template - The template to render.
|
|
4190
|
+
* @remarks
|
|
4191
|
+
* If `null` is provided, any existing view will be removed.
|
|
4192
|
+
*/
|
|
3719
4193
|
renderTemplate(template) {
|
|
3720
4194
|
var _a;
|
|
3721
4195
|
// When getting the host to render to, we start by looking
|
|
@@ -3745,20 +4219,33 @@ class ElementController extends PropertyChangeNotifier {
|
|
|
3745
4219
|
/**
|
|
3746
4220
|
* Locates or creates a controller for the specified element.
|
|
3747
4221
|
* @param element - The element to return the controller for.
|
|
4222
|
+
* @param override - Reset the controller even if one has been defined.
|
|
3748
4223
|
* @remarks
|
|
3749
4224
|
* The specified element must have a {@link FASTElementDefinition}
|
|
3750
4225
|
* registered either through the use of the {@link customElement}
|
|
3751
4226
|
* decorator or a call to `FASTElement.define`.
|
|
3752
4227
|
*/
|
|
3753
|
-
static forCustomElement(element) {
|
|
4228
|
+
static forCustomElement(element, override = false) {
|
|
3754
4229
|
const controller = element.$fastController;
|
|
3755
|
-
if (controller !== void 0) {
|
|
4230
|
+
if (controller !== void 0 && !override) {
|
|
3756
4231
|
return controller;
|
|
3757
4232
|
}
|
|
3758
4233
|
const definition = FASTElementDefinition.getForInstance(element);
|
|
3759
4234
|
if (definition === void 0) {
|
|
3760
|
-
throw FAST.error(
|
|
4235
|
+
throw FAST.error(Message.missingElementDefinition);
|
|
3761
4236
|
}
|
|
4237
|
+
Observable.getNotifier(definition).subscribe({
|
|
4238
|
+
handleChange: () => {
|
|
4239
|
+
ElementController.forCustomElement(element, true);
|
|
4240
|
+
element.$fastController.connect();
|
|
4241
|
+
},
|
|
4242
|
+
}, "template");
|
|
4243
|
+
Observable.getNotifier(definition).subscribe({
|
|
4244
|
+
handleChange: () => {
|
|
4245
|
+
ElementController.forCustomElement(element, true);
|
|
4246
|
+
element.$fastController.connect();
|
|
4247
|
+
},
|
|
4248
|
+
}, "shadowOptions");
|
|
3762
4249
|
return (element.$fastController = new elementControllerStrategy(element, definition));
|
|
3763
4250
|
}
|
|
3764
4251
|
/**
|
|
@@ -3890,7 +4377,10 @@ if (ElementStyles.supportsAdoptedStyleSheets) {
|
|
|
3890
4377
|
else {
|
|
3891
4378
|
ElementStyles.setDefaultStrategy(StyleElementStrategy);
|
|
3892
4379
|
}
|
|
3893
|
-
|
|
4380
|
+
/**
|
|
4381
|
+
* The attribute used to indicate that an element needs hydration.
|
|
4382
|
+
* @public
|
|
4383
|
+
*/
|
|
3894
4384
|
const needsHydrationAttribute = "needs-hydration";
|
|
3895
4385
|
/**
|
|
3896
4386
|
* An ElementController capable of hydrating FAST elements from
|
|
@@ -3899,22 +4389,90 @@ const needsHydrationAttribute = "needs-hydration";
|
|
|
3899
4389
|
* @beta
|
|
3900
4390
|
*/
|
|
3901
4391
|
class HydratableElementController extends ElementController {
|
|
4392
|
+
/**
|
|
4393
|
+
* {@inheritdoc ElementController.shadowOptions}
|
|
4394
|
+
*/
|
|
4395
|
+
get shadowOptions() {
|
|
4396
|
+
return super.shadowOptions;
|
|
4397
|
+
}
|
|
4398
|
+
set shadowOptions(value) {
|
|
4399
|
+
super.shadowOptions = value;
|
|
4400
|
+
if ((this.hasExistingShadowRoot || (value !== void 0 && !this.template)) &&
|
|
4401
|
+
this.definition.templateOptions === TemplateOptions.deferAndHydrate) {
|
|
4402
|
+
this.source.toggleAttribute(deferHydrationAttribute, true);
|
|
4403
|
+
this.source.toggleAttribute(needsHydrationAttribute, true);
|
|
4404
|
+
}
|
|
4405
|
+
}
|
|
4406
|
+
/**
|
|
4407
|
+
* Adds the current element instance to the hydrating instances map
|
|
4408
|
+
*/
|
|
4409
|
+
addHydratingInstance() {
|
|
4410
|
+
if (!HydratableElementController.hydratingInstances) {
|
|
4411
|
+
return;
|
|
4412
|
+
}
|
|
4413
|
+
const name = this.definition.name;
|
|
4414
|
+
let instances = HydratableElementController.hydratingInstances.get(name);
|
|
4415
|
+
if (!instances) {
|
|
4416
|
+
instances = new Set();
|
|
4417
|
+
HydratableElementController.hydratingInstances.set(name, instances);
|
|
4418
|
+
}
|
|
4419
|
+
instances.add(this.source);
|
|
4420
|
+
}
|
|
4421
|
+
/**
|
|
4422
|
+
* Configure lifecycle callbacks for hydration events
|
|
4423
|
+
*/
|
|
4424
|
+
static config(callbacks) {
|
|
4425
|
+
HydratableElementController.lifecycleCallbacks = callbacks;
|
|
4426
|
+
return this;
|
|
4427
|
+
}
|
|
3902
4428
|
static hydrationObserverHandler(records) {
|
|
3903
4429
|
for (const record of records) {
|
|
3904
|
-
|
|
3905
|
-
|
|
4430
|
+
if (!record.target.hasAttribute(deferHydrationAttribute)) {
|
|
4431
|
+
HydratableElementController.hydrationObserver.unobserve(record.target);
|
|
4432
|
+
record.target.$fastController.connect();
|
|
4433
|
+
}
|
|
4434
|
+
}
|
|
4435
|
+
}
|
|
4436
|
+
/**
|
|
4437
|
+
* Checks to see if hydration is complete and if so, invokes the hydrationComplete callback.
|
|
4438
|
+
* Then resets the ElementController strategy to the default so that future elements
|
|
4439
|
+
* don't use the HydratableElementController.
|
|
4440
|
+
*
|
|
4441
|
+
* @param deadline - the idle deadline object
|
|
4442
|
+
*/
|
|
4443
|
+
static checkHydrationComplete(deadline) {
|
|
4444
|
+
var _a, _b, _c;
|
|
4445
|
+
if (deadline.didTimeout) {
|
|
4446
|
+
HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
|
|
4447
|
+
return;
|
|
4448
|
+
}
|
|
4449
|
+
// If there are no more hydrating instances, invoke the hydrationComplete callback
|
|
4450
|
+
if (((_a = HydratableElementController.hydratingInstances) === null || _a === void 0 ? void 0 : _a.size) === 0) {
|
|
4451
|
+
try {
|
|
4452
|
+
(_c = (_b = HydratableElementController.lifecycleCallbacks).hydrationComplete) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
4453
|
+
}
|
|
4454
|
+
catch (_d) {
|
|
4455
|
+
// A lifecycle callback must never prevent post-hydration cleanup.
|
|
4456
|
+
}
|
|
4457
|
+
// Reset to the default strategy after hydration is complete
|
|
4458
|
+
ElementController.setStrategy(ElementController);
|
|
3906
4459
|
}
|
|
3907
4460
|
}
|
|
4461
|
+
/**
|
|
4462
|
+
* Runs connected lifecycle behavior on the associated element.
|
|
4463
|
+
*/
|
|
3908
4464
|
connect() {
|
|
3909
|
-
var _a, _b;
|
|
4465
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
3910
4466
|
// Initialize needsHydration on first connect
|
|
3911
|
-
|
|
3912
|
-
this.needsHydration
|
|
3913
|
-
|
|
4467
|
+
this.needsHydration =
|
|
4468
|
+
(_a = this.needsHydration) !== null && _a !== void 0 ? _a : this.source.hasAttribute(needsHydrationAttribute);
|
|
4469
|
+
if (this.needsHydration) {
|
|
4470
|
+
this.addHydratingInstance();
|
|
3914
4471
|
}
|
|
3915
4472
|
// If the `defer-hydration` attribute exists on the source,
|
|
3916
4473
|
// wait for it to be removed before continuing connection behavior.
|
|
3917
4474
|
if (this.source.hasAttribute(deferHydrationAttribute)) {
|
|
4475
|
+
this.addHydratingInstance();
|
|
3918
4476
|
HydratableElementController.hydrationObserver.observe(this.source, {
|
|
3919
4477
|
attributeFilter: [deferHydrationAttribute],
|
|
3920
4478
|
});
|
|
@@ -3926,18 +4484,34 @@ class HydratableElementController extends ElementController {
|
|
|
3926
4484
|
// class
|
|
3927
4485
|
if (!this.needsHydration) {
|
|
3928
4486
|
super.connect();
|
|
4487
|
+
this.removeHydratingInstance();
|
|
3929
4488
|
return;
|
|
3930
4489
|
}
|
|
3931
|
-
if (this.stage !==
|
|
4490
|
+
if (this.stage !== Stages.disconnected) {
|
|
3932
4491
|
return;
|
|
3933
4492
|
}
|
|
3934
|
-
|
|
4493
|
+
if (!HydratableElementController.hydrationStarted) {
|
|
4494
|
+
HydratableElementController.hydrationStarted = true;
|
|
4495
|
+
try {
|
|
4496
|
+
(_c = (_b = HydratableElementController.lifecycleCallbacks).hydrationStarted) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
4497
|
+
}
|
|
4498
|
+
catch (_h) {
|
|
4499
|
+
// A lifecycle callback must never prevent hydration.
|
|
4500
|
+
}
|
|
4501
|
+
}
|
|
4502
|
+
try {
|
|
4503
|
+
(_e = (_d = HydratableElementController.lifecycleCallbacks).elementWillHydrate) === null || _e === void 0 ? void 0 : _e.call(_d, this.source);
|
|
4504
|
+
}
|
|
4505
|
+
catch (_j) {
|
|
4506
|
+
// A lifecycle callback must never prevent hydration.
|
|
4507
|
+
}
|
|
4508
|
+
this.stage = Stages.connecting;
|
|
3935
4509
|
this.bindObservables();
|
|
3936
4510
|
this.connectBehaviors();
|
|
3937
|
-
const element = this.source;
|
|
3938
|
-
const host = (_a = getShadowRoot(element)) !== null && _a !== void 0 ? _a : element;
|
|
3939
4511
|
if (this.template) {
|
|
3940
4512
|
if (isHydratable(this.template)) {
|
|
4513
|
+
const element = this.source;
|
|
4514
|
+
const host = (_f = getShadowRoot(element)) !== null && _f !== void 0 ? _f : element;
|
|
3941
4515
|
let firstChild = host.firstChild;
|
|
3942
4516
|
let lastChild = host.lastChild;
|
|
3943
4517
|
if (element.shadowRoot === null) {
|
|
@@ -3952,27 +4526,86 @@ class HydratableElementController extends ElementController {
|
|
|
3952
4526
|
}
|
|
3953
4527
|
}
|
|
3954
4528
|
this.view = this.template.hydrate(firstChild, lastChild, element);
|
|
3955
|
-
(
|
|
4529
|
+
(_g = this.view) === null || _g === void 0 ? void 0 : _g.bind(this.source);
|
|
3956
4530
|
}
|
|
3957
4531
|
else {
|
|
3958
4532
|
this.renderTemplate(this.template);
|
|
3959
4533
|
}
|
|
3960
4534
|
}
|
|
3961
4535
|
this.addStyles(this.mainStyles);
|
|
3962
|
-
this.stage =
|
|
4536
|
+
this.stage = Stages.connected;
|
|
3963
4537
|
this.source.removeAttribute(needsHydrationAttribute);
|
|
3964
4538
|
this.needsInitialization = this.needsHydration = false;
|
|
4539
|
+
this.removeHydratingInstance();
|
|
3965
4540
|
Observable.notify(this, isConnectedPropertyName);
|
|
3966
4541
|
}
|
|
4542
|
+
/**
|
|
4543
|
+
* Removes the current element instance from the hydrating instances map
|
|
4544
|
+
*/
|
|
4545
|
+
removeHydratingInstance() {
|
|
4546
|
+
var _a, _b;
|
|
4547
|
+
if (!HydratableElementController.hydratingInstances) {
|
|
4548
|
+
return;
|
|
4549
|
+
}
|
|
4550
|
+
try {
|
|
4551
|
+
(_b = (_a = HydratableElementController.lifecycleCallbacks).elementDidHydrate) === null || _b === void 0 ? void 0 : _b.call(_a, this.source);
|
|
4552
|
+
}
|
|
4553
|
+
catch (_c) {
|
|
4554
|
+
// A lifecycle callback must never prevent hydration.
|
|
4555
|
+
}
|
|
4556
|
+
const name = this.definition.name;
|
|
4557
|
+
const instances = HydratableElementController.hydratingInstances.get(name);
|
|
4558
|
+
if (instances) {
|
|
4559
|
+
instances.delete(this.source);
|
|
4560
|
+
if (!instances.size) {
|
|
4561
|
+
HydratableElementController.hydratingInstances.delete(name);
|
|
4562
|
+
}
|
|
4563
|
+
if (HydratableElementController.idleCallbackId) {
|
|
4564
|
+
cancelIdleCallback(HydratableElementController.idleCallbackId);
|
|
4565
|
+
}
|
|
4566
|
+
HydratableElementController.idleCallbackId = requestIdleCallback(HydratableElementController.checkHydrationComplete, { timeout: 50 });
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4569
|
+
/**
|
|
4570
|
+
* Unregisters the hydration observer when the element is disconnected.
|
|
4571
|
+
*/
|
|
3967
4572
|
disconnect() {
|
|
3968
4573
|
super.disconnect();
|
|
3969
4574
|
HydratableElementController.hydrationObserver.unobserve(this.source);
|
|
3970
4575
|
}
|
|
4576
|
+
/**
|
|
4577
|
+
* Sets the ElementController strategy to HydratableElementController.
|
|
4578
|
+
* @remarks
|
|
4579
|
+
* This method is typically called during application startup to enable
|
|
4580
|
+
* hydration support for FAST elements.
|
|
4581
|
+
*/
|
|
3971
4582
|
static install() {
|
|
3972
4583
|
ElementController.setStrategy(HydratableElementController);
|
|
3973
4584
|
}
|
|
3974
4585
|
}
|
|
3975
4586
|
HydratableElementController.hydrationObserver = new UnobservableMutationObserver(HydratableElementController.hydrationObserverHandler);
|
|
4587
|
+
/**
|
|
4588
|
+
* Lifecycle callbacks for hydration events
|
|
4589
|
+
*/
|
|
4590
|
+
HydratableElementController.lifecycleCallbacks = {};
|
|
4591
|
+
/**
|
|
4592
|
+
* Whether the hydrationStarted callback has already been invoked.
|
|
4593
|
+
*/
|
|
4594
|
+
HydratableElementController.hydrationStarted = false;
|
|
4595
|
+
/**
|
|
4596
|
+
* An idle callback ID used to track hydration completion
|
|
4597
|
+
*/
|
|
4598
|
+
HydratableElementController.idleCallbackId = null;
|
|
4599
|
+
/**
|
|
4600
|
+
* A map of element instances by the name of the custom element they are
|
|
4601
|
+
* associated with. The key is the custom element name, and the value is the
|
|
4602
|
+
* instances of hydratable elements which currently need to be hydrated.
|
|
4603
|
+
*
|
|
4604
|
+
* When all of the instances in the set have been hydrated, the set is
|
|
4605
|
+
* cleared and removed from the map. If the map is empty, the
|
|
4606
|
+
* hydrationComplete callback is invoked.
|
|
4607
|
+
*/
|
|
4608
|
+
HydratableElementController.hydratingInstances = new Map();
|
|
3976
4609
|
|
|
3977
4610
|
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
3978
4611
|
function createFASTElement(BaseType) {
|
|
@@ -4004,6 +4637,24 @@ function compose(type, nameOrDef) {
|
|
|
4004
4637
|
}
|
|
4005
4638
|
return FASTElementDefinition.compose(this, type);
|
|
4006
4639
|
}
|
|
4640
|
+
function defineAsync(type, nameOrDef) {
|
|
4641
|
+
if (isFunction(type)) {
|
|
4642
|
+
return new Promise(resolve => {
|
|
4643
|
+
FASTElementDefinition.composeAsync(type, nameOrDef).then(value => {
|
|
4644
|
+
resolve(value);
|
|
4645
|
+
});
|
|
4646
|
+
}).then(value => {
|
|
4647
|
+
return value.define().type;
|
|
4648
|
+
});
|
|
4649
|
+
}
|
|
4650
|
+
return new Promise(resolve => {
|
|
4651
|
+
FASTElementDefinition.composeAsync(this, type).then(value => {
|
|
4652
|
+
resolve(value);
|
|
4653
|
+
});
|
|
4654
|
+
}).then(value => {
|
|
4655
|
+
return value.define().type;
|
|
4656
|
+
});
|
|
4657
|
+
}
|
|
4007
4658
|
function define(type, nameOrDef) {
|
|
4008
4659
|
if (isFunction(type)) {
|
|
4009
4660
|
return FASTElementDefinition.compose(type, nameOrDef).define().type;
|
|
@@ -4037,6 +4688,11 @@ const FASTElement = Object.assign(createFASTElement(HTMLElement), {
|
|
|
4037
4688
|
* @public
|
|
4038
4689
|
*/
|
|
4039
4690
|
compose,
|
|
4691
|
+
/**
|
|
4692
|
+
* Defines metadata for a FASTElement which can be used after it has been resolved to define the element.
|
|
4693
|
+
* @alpha
|
|
4694
|
+
*/
|
|
4695
|
+
defineAsync,
|
|
4040
4696
|
});
|
|
4041
4697
|
|
|
4042
4698
|
function staticallyCompose(item) {
|
|
@@ -4646,7 +5302,7 @@ const AccordionExpandMode = {
|
|
|
4646
5302
|
};
|
|
4647
5303
|
const tagName$E = `${FluentDesignSystem.prefix}-accordion`;
|
|
4648
5304
|
|
|
4649
|
-
function requestIdleCallback(callback, options) {
|
|
5305
|
+
function requestIdleCallback$1(callback, options) {
|
|
4650
5306
|
if ("requestIdleCallback" in globalThis) {
|
|
4651
5307
|
return globalThis.requestIdleCallback(callback, options);
|
|
4652
5308
|
}
|
|
@@ -4666,13 +5322,13 @@ function waitForConnectedDescendants(target, callback, options) {
|
|
|
4666
5322
|
const scheduleCheck = (deadline) => {
|
|
4667
5323
|
if (target.querySelector(selector) === null || deadline && deadline.timeRemaining() <= 0) {
|
|
4668
5324
|
if (useIdleCallback) {
|
|
4669
|
-
requestIdleCallback(callback, { timeout });
|
|
5325
|
+
requestIdleCallback$1(callback, { timeout });
|
|
4670
5326
|
} else {
|
|
4671
5327
|
callback();
|
|
4672
5328
|
}
|
|
4673
5329
|
return;
|
|
4674
5330
|
}
|
|
4675
|
-
requestIdleCallback(scheduleCheck, { timeout });
|
|
5331
|
+
requestIdleCallback$1(scheduleCheck, { timeout });
|
|
4676
5332
|
};
|
|
4677
5333
|
scheduleCheck();
|
|
4678
5334
|
}
|
|
@@ -6443,7 +7099,7 @@ class CompoundButton extends Button {
|
|
|
6443
7099
|
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}}`;
|
|
6444
7100
|
|
|
6445
7101
|
function buttonTemplate(options = {}) {
|
|
6446
|
-
return html`<template
|
|
7102
|
+
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>`;
|
|
6447
7103
|
}
|
|
6448
7104
|
const template$y = buttonTemplate();
|
|
6449
7105
|
|
|
@@ -6597,19 +7253,6 @@ var __decorateClass$B = (decorators, target, key, kind) => {
|
|
|
6597
7253
|
class Dialog extends FASTElement {
|
|
6598
7254
|
constructor() {
|
|
6599
7255
|
super(...arguments);
|
|
6600
|
-
this.type = DialogType.modal;
|
|
6601
|
-
/**
|
|
6602
|
-
* Method to emit an event before the dialog's open state changes
|
|
6603
|
-
* HTML spec proposal: https://github.com/whatwg/html/issues/9733
|
|
6604
|
-
*
|
|
6605
|
-
* @public
|
|
6606
|
-
*/
|
|
6607
|
-
this.emitBeforeToggle = () => {
|
|
6608
|
-
this.$emit("beforetoggle", {
|
|
6609
|
-
oldState: this.dialog.open ? "open" : "closed",
|
|
6610
|
-
newState: this.dialog.open ? "closed" : "open"
|
|
6611
|
-
});
|
|
6612
|
-
};
|
|
6613
7256
|
/**
|
|
6614
7257
|
* Method to emit an event after the dialog's open state changes
|
|
6615
7258
|
* HTML spec proposal: https://github.com/whatwg/html/issues/9733
|
|
@@ -6623,11 +7266,48 @@ class Dialog extends FASTElement {
|
|
|
6623
7266
|
});
|
|
6624
7267
|
};
|
|
6625
7268
|
}
|
|
6626
|
-
|
|
6627
|
-
this.
|
|
7269
|
+
get dialogDescribedby() {
|
|
7270
|
+
if (this.dialog) {
|
|
7271
|
+
return this.ariaDescribedby;
|
|
7272
|
+
}
|
|
6628
7273
|
}
|
|
6629
|
-
|
|
6630
|
-
this.
|
|
7274
|
+
get dialogLabel() {
|
|
7275
|
+
if (this.dialog) {
|
|
7276
|
+
return this.ariaLabel;
|
|
7277
|
+
}
|
|
7278
|
+
}
|
|
7279
|
+
get dialogLabelledby() {
|
|
7280
|
+
if (this.dialog) {
|
|
7281
|
+
return this.ariaLabelledby;
|
|
7282
|
+
}
|
|
7283
|
+
}
|
|
7284
|
+
get dialogModal() {
|
|
7285
|
+
if (this.dialog && this.type !== DialogType.nonModal) {
|
|
7286
|
+
return true;
|
|
7287
|
+
}
|
|
7288
|
+
}
|
|
7289
|
+
get dialogRole() {
|
|
7290
|
+
if (this.dialog && this.type === DialogType.alert) {
|
|
7291
|
+
return "alertdialog";
|
|
7292
|
+
}
|
|
7293
|
+
}
|
|
7294
|
+
connectedCallback() {
|
|
7295
|
+
super.connectedCallback();
|
|
7296
|
+
Updates.enqueue(() => {
|
|
7297
|
+
this.type = this.type ?? DialogType.modal;
|
|
7298
|
+
});
|
|
7299
|
+
}
|
|
7300
|
+
/**
|
|
7301
|
+
* Method to emit an event before the dialog's open state changes
|
|
7302
|
+
* HTML spec proposal: https://github.com/whatwg/html/issues/9733
|
|
7303
|
+
*
|
|
7304
|
+
* @public
|
|
7305
|
+
*/
|
|
7306
|
+
emitBeforeToggle() {
|
|
7307
|
+
this.$emit("beforetoggle", {
|
|
7308
|
+
oldState: this.dialog.open ? "open" : "closed",
|
|
7309
|
+
newState: this.dialog.open ? "closed" : "open"
|
|
7310
|
+
});
|
|
6631
7311
|
}
|
|
6632
7312
|
/**
|
|
6633
7313
|
* Method to show the dialog
|
|
@@ -6668,26 +7348,6 @@ class Dialog extends FASTElement {
|
|
|
6668
7348
|
}
|
|
6669
7349
|
return true;
|
|
6670
7350
|
}
|
|
6671
|
-
/**
|
|
6672
|
-
* Updates the internal dialog element's attributes based on its type.
|
|
6673
|
-
*
|
|
6674
|
-
* @internal
|
|
6675
|
-
*/
|
|
6676
|
-
updateDialogAttributes() {
|
|
6677
|
-
if (!this.dialog) {
|
|
6678
|
-
return;
|
|
6679
|
-
}
|
|
6680
|
-
if (this.type === DialogType.alert) {
|
|
6681
|
-
this.dialog.setAttribute("role", "alertdialog");
|
|
6682
|
-
} else {
|
|
6683
|
-
this.dialog.removeAttribute("role");
|
|
6684
|
-
}
|
|
6685
|
-
if (this.type !== DialogType.nonModal) {
|
|
6686
|
-
this.dialog.setAttribute("aria-modal", "true");
|
|
6687
|
-
} else {
|
|
6688
|
-
this.dialog.removeAttribute("aria-modal");
|
|
6689
|
-
}
|
|
6690
|
-
}
|
|
6691
7351
|
}
|
|
6692
7352
|
__decorateClass$B([
|
|
6693
7353
|
observable
|
|
@@ -6704,8 +7364,23 @@ __decorateClass$B([
|
|
|
6704
7364
|
__decorateClass$B([
|
|
6705
7365
|
attr
|
|
6706
7366
|
], Dialog.prototype, "type", 2);
|
|
7367
|
+
__decorateClass$B([
|
|
7368
|
+
volatile
|
|
7369
|
+
], Dialog.prototype, "dialogDescribedby", 1);
|
|
7370
|
+
__decorateClass$B([
|
|
7371
|
+
volatile
|
|
7372
|
+
], Dialog.prototype, "dialogLabel", 1);
|
|
7373
|
+
__decorateClass$B([
|
|
7374
|
+
volatile
|
|
7375
|
+
], Dialog.prototype, "dialogLabelledby", 1);
|
|
7376
|
+
__decorateClass$B([
|
|
7377
|
+
volatile
|
|
7378
|
+
], Dialog.prototype, "dialogModal", 1);
|
|
7379
|
+
__decorateClass$B([
|
|
7380
|
+
volatile
|
|
7381
|
+
], Dialog.prototype, "dialogRole", 1);
|
|
6707
7382
|
|
|
6708
|
-
const template$w = html`<dialog class=dialog part=dialog aria-describedby=${(x) => x.
|
|
7383
|
+
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>`;
|
|
6709
7384
|
|
|
6710
7385
|
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}}}}`;
|
|
6711
7386
|
|
|
@@ -6905,7 +7580,6 @@ var __decorateClass$y = (decorators, target, key, kind) => {
|
|
|
6905
7580
|
class Drawer extends FASTElement {
|
|
6906
7581
|
constructor() {
|
|
6907
7582
|
super(...arguments);
|
|
6908
|
-
this.type = DrawerType.modal;
|
|
6909
7583
|
this.position = DrawerPosition.start;
|
|
6910
7584
|
this.size = DrawerSize.medium;
|
|
6911
7585
|
/**
|
|
@@ -6933,27 +7607,37 @@ class Drawer extends FASTElement {
|
|
|
6933
7607
|
});
|
|
6934
7608
|
};
|
|
6935
7609
|
}
|
|
6936
|
-
|
|
6937
|
-
if (
|
|
6938
|
-
return;
|
|
7610
|
+
get dialogDescribedby() {
|
|
7611
|
+
if (this.dialog) {
|
|
7612
|
+
return this.ariaDescribedby;
|
|
6939
7613
|
}
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
|
|
6944
|
-
this.dialog.removeAttribute("aria-modal");
|
|
7614
|
+
}
|
|
7615
|
+
get dialogLabel() {
|
|
7616
|
+
if (this.dialog) {
|
|
7617
|
+
return this.ariaLabel;
|
|
6945
7618
|
}
|
|
6946
7619
|
}
|
|
6947
|
-
|
|
7620
|
+
get dialogLabelledby() {
|
|
7621
|
+
if (this.dialog) {
|
|
7622
|
+
return this.ariaLabelledby;
|
|
7623
|
+
}
|
|
7624
|
+
}
|
|
7625
|
+
get dialogModal() {
|
|
7626
|
+
if (this.dialog && this.type === DrawerType.modal) {
|
|
7627
|
+
return true;
|
|
7628
|
+
}
|
|
7629
|
+
}
|
|
7630
|
+
get dialogRole() {
|
|
7631
|
+
if (this.dialog && this.type === DrawerType.modal) {
|
|
7632
|
+
return "dialog";
|
|
7633
|
+
}
|
|
7634
|
+
return this.role;
|
|
7635
|
+
}
|
|
6948
7636
|
connectedCallback() {
|
|
6949
7637
|
super.connectedCallback();
|
|
6950
|
-
|
|
6951
|
-
|
|
6952
|
-
|
|
6953
|
-
/** @internal */
|
|
6954
|
-
disconnectedCallback() {
|
|
6955
|
-
super.disconnectedCallback();
|
|
6956
|
-
this.roleAttrObserver.disconnect();
|
|
7638
|
+
Updates.enqueue(() => {
|
|
7639
|
+
this.type = this.type ?? DrawerType.modal;
|
|
7640
|
+
});
|
|
6957
7641
|
}
|
|
6958
7642
|
/**
|
|
6959
7643
|
* Method to show the drawer
|
|
@@ -7001,24 +7685,6 @@ class Drawer extends FASTElement {
|
|
|
7001
7685
|
cancelHandler() {
|
|
7002
7686
|
this.hide();
|
|
7003
7687
|
}
|
|
7004
|
-
observeRoleAttr() {
|
|
7005
|
-
if (this.roleAttrObserver) {
|
|
7006
|
-
return;
|
|
7007
|
-
}
|
|
7008
|
-
this.roleAttrObserver = new MutationObserver(() => {
|
|
7009
|
-
this.updateDialogRole();
|
|
7010
|
-
});
|
|
7011
|
-
this.roleAttrObserver.observe(this, {
|
|
7012
|
-
attributes: true,
|
|
7013
|
-
attributeFilter: ["role"]
|
|
7014
|
-
});
|
|
7015
|
-
}
|
|
7016
|
-
updateDialogRole() {
|
|
7017
|
-
if (!this.dialog) {
|
|
7018
|
-
return;
|
|
7019
|
-
}
|
|
7020
|
-
this.dialog.role = this.type === DrawerType.modal ? "dialog" : this.role;
|
|
7021
|
-
}
|
|
7022
7688
|
}
|
|
7023
7689
|
__decorateClass$y([
|
|
7024
7690
|
attr
|
|
@@ -7032,17 +7698,35 @@ __decorateClass$y([
|
|
|
7032
7698
|
__decorateClass$y([
|
|
7033
7699
|
attr
|
|
7034
7700
|
], Drawer.prototype, "position", 2);
|
|
7701
|
+
__decorateClass$y([
|
|
7702
|
+
observable
|
|
7703
|
+
], Drawer.prototype, "role", 2);
|
|
7035
7704
|
__decorateClass$y([
|
|
7036
7705
|
attr({ attribute: "size" })
|
|
7037
7706
|
], Drawer.prototype, "size", 2);
|
|
7038
7707
|
__decorateClass$y([
|
|
7039
7708
|
observable
|
|
7040
7709
|
], Drawer.prototype, "dialog", 2);
|
|
7710
|
+
__decorateClass$y([
|
|
7711
|
+
volatile
|
|
7712
|
+
], Drawer.prototype, "dialogDescribedby", 1);
|
|
7713
|
+
__decorateClass$y([
|
|
7714
|
+
volatile
|
|
7715
|
+
], Drawer.prototype, "dialogLabel", 1);
|
|
7716
|
+
__decorateClass$y([
|
|
7717
|
+
volatile
|
|
7718
|
+
], Drawer.prototype, "dialogLabelledby", 1);
|
|
7719
|
+
__decorateClass$y([
|
|
7720
|
+
volatile
|
|
7721
|
+
], Drawer.prototype, "dialogModal", 1);
|
|
7722
|
+
__decorateClass$y([
|
|
7723
|
+
volatile
|
|
7724
|
+
], Drawer.prototype, "dialogRole", 1);
|
|
7041
7725
|
|
|
7042
7726
|
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}}}`;
|
|
7043
7727
|
|
|
7044
7728
|
function drawerTemplate() {
|
|
7045
|
-
return html`<dialog class=dialog part=dialog aria-describedby=${(x) => x.
|
|
7729
|
+
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>`;
|
|
7046
7730
|
}
|
|
7047
7731
|
const template$t = drawerTemplate();
|
|
7048
7732
|
|
|
@@ -9462,6 +10146,9 @@ const _BaseMenuList = class _BaseMenuList extends FASTElement {
|
|
|
9462
10146
|
*/
|
|
9463
10147
|
disconnectedCallback() {
|
|
9464
10148
|
super.disconnectedCallback();
|
|
10149
|
+
Array.from(this.children).forEach((child) => {
|
|
10150
|
+
Observable.getNotifier(child).unsubscribe(this, "hidden");
|
|
10151
|
+
});
|
|
9465
10152
|
this.menuChildren = void 0;
|
|
9466
10153
|
this.removeEventListener("change", this.changedMenuItemHandler);
|
|
9467
10154
|
}
|
|
@@ -9483,6 +10170,9 @@ const _BaseMenuList = class _BaseMenuList extends FASTElement {
|
|
|
9483
10170
|
}
|
|
9484
10171
|
setItems() {
|
|
9485
10172
|
const children = Array.from(this.children);
|
|
10173
|
+
children.forEach((child) => {
|
|
10174
|
+
Observable.getNotifier(child).subscribe(this, "hidden");
|
|
10175
|
+
});
|
|
9486
10176
|
this.menuChildren = children.filter((child) => !child.hasAttribute("hidden"));
|
|
9487
10177
|
this.menuItems = this.menuChildren?.filter(this.isMenuItemElement);
|
|
9488
10178
|
const indent = this.menuItems?.reduce((accum, current) => {
|
|
@@ -13721,6 +14411,8 @@ class BaseTree extends FASTElement {
|
|
|
13721
14411
|
if (item?.childTreeItems?.length) {
|
|
13722
14412
|
if (!item.expanded) {
|
|
13723
14413
|
item.expanded = true;
|
|
14414
|
+
} else {
|
|
14415
|
+
return true;
|
|
13724
14416
|
}
|
|
13725
14417
|
}
|
|
13726
14418
|
return;
|
|
@@ -13845,7 +14537,7 @@ class Tree extends BaseTree {
|
|
|
13845
14537
|
this.fg = new FocusGroup(this, this.fgItems, {
|
|
13846
14538
|
definition: {
|
|
13847
14539
|
behavior: "menu",
|
|
13848
|
-
axis:
|
|
14540
|
+
axis: void 0,
|
|
13849
14541
|
memory: false
|
|
13850
14542
|
}
|
|
13851
14543
|
});
|
|
@@ -13884,7 +14576,7 @@ __decorateClass$2([
|
|
|
13884
14576
|
|
|
13885
14577
|
const styles$1 = css`${display("block")} :host{outline:none}`;
|
|
13886
14578
|
|
|
13887
|
-
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>`;
|
|
14579
|
+
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>`;
|
|
13888
14580
|
|
|
13889
14581
|
const definition$1 = Tree.compose({
|
|
13890
14582
|
name: tagName$1,
|