@microsoft/fast-element 1.10.5 → 2.0.0-beta.10

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.
Files changed (122) hide show
  1. package/.eslintrc.json +1 -12
  2. package/CHANGELOG.json +629 -6
  3. package/CHANGELOG.md +152 -5
  4. package/dist/dts/components/attributes.d.ts +14 -1
  5. package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +32 -32
  6. package/dist/dts/components/fast-definitions.d.ts +51 -11
  7. package/dist/dts/components/fast-element.d.ts +18 -23
  8. package/dist/dts/context.d.ts +157 -0
  9. package/dist/{esm/observation/behavior.js → dts/debug.d.ts} +0 -0
  10. package/dist/dts/di/di.d.ts +899 -0
  11. package/dist/dts/index.d.ts +17 -16
  12. package/dist/dts/index.debug.d.ts +2 -0
  13. package/dist/dts/index.rollup.d.ts +2 -0
  14. package/dist/dts/index.rollup.debug.d.ts +3 -0
  15. package/dist/dts/interfaces.d.ts +176 -0
  16. package/dist/dts/metadata.d.ts +25 -0
  17. package/dist/dts/observation/arrays.d.ts +207 -0
  18. package/dist/dts/observation/notifier.d.ts +18 -18
  19. package/dist/dts/observation/observable.d.ts +117 -34
  20. package/dist/dts/observation/update-queue.d.ts +40 -0
  21. package/dist/dts/pending-task.d.ts +20 -0
  22. package/dist/dts/platform.d.ts +23 -66
  23. package/dist/dts/polyfills.d.ts +8 -0
  24. package/dist/dts/state/exports.d.ts +3 -0
  25. package/dist/dts/state/reactive.d.ts +8 -0
  26. package/dist/dts/state/state.d.ts +141 -0
  27. package/dist/dts/state/visitor.d.ts +6 -0
  28. package/dist/dts/state/watch.d.ts +10 -0
  29. package/dist/dts/styles/css-directive.d.ts +44 -6
  30. package/dist/dts/styles/css.d.ts +19 -3
  31. package/dist/dts/styles/element-styles.d.ts +49 -63
  32. package/dist/dts/styles/host.d.ts +68 -0
  33. package/dist/dts/templating/binding-signal.d.ts +21 -0
  34. package/dist/dts/templating/binding-two-way.d.ts +39 -0
  35. package/dist/dts/templating/binding.d.ts +101 -70
  36. package/dist/dts/templating/children.d.ts +18 -15
  37. package/dist/dts/templating/compiler.d.ts +46 -28
  38. package/dist/dts/templating/dom.d.ts +41 -0
  39. package/dist/dts/templating/html-directive.d.ts +239 -45
  40. package/dist/dts/templating/markup.d.ts +48 -0
  41. package/dist/dts/templating/node-observation.d.ts +45 -30
  42. package/dist/dts/templating/ref.d.ts +6 -20
  43. package/dist/dts/templating/render.d.ts +272 -0
  44. package/dist/dts/templating/repeat.d.ts +36 -33
  45. package/dist/dts/templating/slotted.d.ts +13 -14
  46. package/dist/dts/templating/template.d.ts +28 -22
  47. package/dist/dts/templating/view.d.ts +82 -24
  48. package/dist/dts/templating/when.d.ts +3 -3
  49. package/dist/dts/testing/exports.d.ts +3 -0
  50. package/dist/dts/testing/fakes.d.ts +4 -0
  51. package/dist/dts/testing/fixture.d.ts +84 -0
  52. package/dist/dts/testing/timeout.d.ts +7 -0
  53. package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +1 -1
  54. package/dist/dts/utilities.d.ts +22 -0
  55. package/dist/esm/components/attributes.js +38 -28
  56. package/dist/esm/components/{controller.js → element-controller.js} +150 -140
  57. package/dist/esm/components/fast-definitions.js +48 -46
  58. package/dist/esm/components/fast-element.js +31 -12
  59. package/dist/esm/context.js +163 -0
  60. package/dist/esm/debug.js +61 -0
  61. package/dist/esm/di/di.js +1435 -0
  62. package/dist/esm/index.debug.js +2 -0
  63. package/dist/esm/index.js +20 -14
  64. package/dist/esm/index.rollup.debug.js +3 -0
  65. package/dist/esm/index.rollup.js +2 -0
  66. package/dist/esm/interfaces.js +12 -1
  67. package/dist/esm/metadata.js +60 -0
  68. package/dist/esm/observation/arrays.js +570 -0
  69. package/dist/esm/observation/notifier.js +27 -35
  70. package/dist/esm/observation/observable.js +116 -149
  71. package/dist/esm/observation/update-queue.js +67 -0
  72. package/dist/esm/pending-task.js +16 -0
  73. package/dist/esm/platform.js +60 -42
  74. package/dist/esm/polyfills.js +85 -0
  75. package/dist/esm/state/exports.js +3 -0
  76. package/dist/esm/state/reactive.js +34 -0
  77. package/dist/esm/state/state.js +148 -0
  78. package/dist/esm/state/visitor.js +28 -0
  79. package/dist/esm/state/watch.js +36 -0
  80. package/dist/esm/styles/css-directive.js +29 -13
  81. package/dist/esm/styles/css.js +29 -42
  82. package/dist/esm/styles/element-styles.js +79 -104
  83. package/dist/esm/styles/host.js +1 -0
  84. package/dist/esm/templating/binding-signal.js +83 -0
  85. package/dist/esm/templating/binding-two-way.js +103 -0
  86. package/dist/esm/templating/binding.js +189 -159
  87. package/dist/esm/templating/children.js +33 -23
  88. package/dist/esm/templating/compiler.js +258 -152
  89. package/dist/esm/templating/dom.js +49 -0
  90. package/dist/esm/templating/html-directive.js +193 -36
  91. package/dist/esm/templating/markup.js +75 -0
  92. package/dist/esm/templating/node-observation.js +51 -45
  93. package/dist/esm/templating/ref.js +8 -25
  94. package/dist/esm/templating/render.js +391 -0
  95. package/dist/esm/templating/repeat.js +83 -79
  96. package/dist/esm/templating/slotted.js +23 -20
  97. package/dist/esm/templating/template.js +51 -93
  98. package/dist/esm/templating/view.js +125 -46
  99. package/dist/esm/templating/when.js +6 -4
  100. package/dist/esm/testing/exports.js +3 -0
  101. package/dist/esm/testing/fakes.js +76 -0
  102. package/dist/esm/testing/fixture.js +86 -0
  103. package/dist/esm/testing/timeout.js +24 -0
  104. package/dist/esm/utilities.js +44 -0
  105. package/dist/fast-element.api.json +12153 -5373
  106. package/dist/fast-element.d.ts +1448 -696
  107. package/dist/fast-element.debug.js +4107 -0
  108. package/dist/fast-element.debug.min.js +1 -0
  109. package/dist/fast-element.js +3817 -4029
  110. package/dist/fast-element.min.js +1 -1
  111. package/dist/fast-element.untrimmed.d.ts +2814 -0
  112. package/docs/api-report.md +567 -254
  113. package/docs/fast-element-2-changes.md +15 -0
  114. package/karma.conf.cjs +6 -17
  115. package/package.json +76 -15
  116. package/dist/dts/dom.d.ts +0 -112
  117. package/dist/dts/observation/array-change-records.d.ts +0 -48
  118. package/dist/dts/observation/array-observer.d.ts +0 -9
  119. package/dist/dts/observation/behavior.d.ts +0 -19
  120. package/dist/esm/dom.js +0 -207
  121. package/dist/esm/observation/array-change-records.js +0 -326
  122. package/dist/esm/observation/array-observer.js +0 -177
@@ -1,186 +1,292 @@
1
- import { _interpolationEnd, _interpolationStart, DOM } from "../dom.js";
2
- import { HTMLBindingDirective } from "./binding.js";
3
- let sharedContext = null;
4
- class CompilationContext {
5
- addFactory(factory) {
6
- factory.targetIndex = this.targetIndex;
7
- this.behaviorFactories.push(factory);
8
- }
9
- captureContentBinding(directive) {
10
- directive.targetAtContent();
11
- this.addFactory(directive);
12
- }
13
- reset() {
14
- this.behaviorFactories = [];
15
- this.targetIndex = -1;
16
- }
17
- release() {
18
- /* eslint-disable-next-line @typescript-eslint/no-this-alias */
19
- sharedContext = this;
20
- }
21
- static borrow(directives) {
22
- const shareable = sharedContext || new CompilationContext();
23
- shareable.directives = directives;
24
- shareable.reset();
25
- sharedContext = null;
26
- return shareable;
1
+ import { isFunction, isString } from "../interfaces.js";
2
+ import { FAST } from "../platform.js";
3
+ import { Parser } from "./markup.js";
4
+ import { HTMLBindingDirective, oneTime } from "./binding.js";
5
+ import { Aspect } from "./html-directive.js";
6
+ import { HTMLView } from "./view.js";
7
+ const targetIdFrom = (parentId, nodeIndex) => `${parentId}.${nodeIndex}`;
8
+ const descriptorCache = {};
9
+ // used to prevent creating lots of objects just to track node and index while compiling
10
+ const next = {
11
+ index: 0,
12
+ node: null,
13
+ };
14
+ function tryWarn(name) {
15
+ if (!name.startsWith("fast-")) {
16
+ FAST.warn(1204 /* Message.hostBindingWithoutHost */, { name });
27
17
  }
28
18
  }
29
- function createAggregateBinding(parts) {
30
- if (parts.length === 1) {
31
- return parts[0];
19
+ const warningHost = new Proxy(document.createElement("div"), {
20
+ get(target, property) {
21
+ tryWarn(property);
22
+ const value = Reflect.get(target, property);
23
+ return isFunction(value) ? value.bind(target) : value;
24
+ },
25
+ set(target, property, value) {
26
+ tryWarn(property);
27
+ return Reflect.set(target, property, value);
28
+ },
29
+ });
30
+ class CompilationContext {
31
+ constructor(fragment, directives) {
32
+ this.fragment = fragment;
33
+ this.directives = directives;
34
+ this.proto = null;
35
+ this.nodeIds = new Set();
36
+ this.descriptors = {};
37
+ this.factories = [];
32
38
  }
33
- let targetName;
34
- const partCount = parts.length;
35
- const finalParts = parts.map((x) => {
36
- if (typeof x === "string") {
37
- return () => x;
38
- }
39
- targetName = x.targetName || targetName;
40
- return x.binding;
41
- });
42
- const binding = (scope, context) => {
43
- let output = "";
44
- for (let i = 0; i < partCount; ++i) {
45
- output += finalParts[i](scope, context);
46
- }
47
- return output;
48
- };
49
- const directive = new HTMLBindingDirective(binding);
50
- directive.targetName = targetName;
51
- return directive;
52
- }
53
- const interpolationEndLength = _interpolationEnd.length;
54
- function parseContent(context, value) {
55
- const valueParts = value.split(_interpolationStart);
56
- if (valueParts.length === 1) {
57
- return null;
39
+ addFactory(factory, parentId, nodeId, targetIndex) {
40
+ if (!this.nodeIds.has(nodeId)) {
41
+ this.nodeIds.add(nodeId);
42
+ this.addTargetDescriptor(parentId, nodeId, targetIndex);
43
+ }
44
+ factory.nodeId = nodeId;
45
+ this.factories.push(factory);
46
+ }
47
+ freeze() {
48
+ this.proto = Object.create(null, this.descriptors);
49
+ return this;
58
50
  }
59
- const bindingParts = [];
60
- for (let i = 0, ii = valueParts.length; i < ii; ++i) {
61
- const current = valueParts[i];
62
- const index = current.indexOf(_interpolationEnd);
63
- let literal;
64
- if (index === -1) {
65
- literal = current;
51
+ addTargetDescriptor(parentId, targetId, targetIndex) {
52
+ const descriptors = this.descriptors;
53
+ if (targetId === "r" || // root
54
+ targetId === "h" || // host
55
+ descriptors[targetId]) {
56
+ return;
66
57
  }
67
- else {
68
- const directiveIndex = parseInt(current.substring(0, index));
69
- bindingParts.push(context.directives[directiveIndex]);
70
- literal = current.substring(index + interpolationEndLength);
58
+ if (!descriptors[parentId]) {
59
+ const index = parentId.lastIndexOf(".");
60
+ const grandparentId = parentId.substring(0, index);
61
+ const childIndex = parseInt(parentId.substring(index + 1));
62
+ this.addTargetDescriptor(grandparentId, parentId, childIndex);
63
+ }
64
+ let descriptor = descriptorCache[targetId];
65
+ if (!descriptor) {
66
+ const field = `_${targetId}`;
67
+ descriptorCache[targetId] = descriptor = {
68
+ get() {
69
+ var _a;
70
+ return ((_a = this[field]) !== null && _a !== void 0 ? _a : (this[field] = this[parentId].childNodes[targetIndex]));
71
+ },
72
+ };
71
73
  }
72
- if (literal !== "") {
73
- bindingParts.push(literal);
74
+ descriptors[targetId] = descriptor;
75
+ }
76
+ createView(hostBindingTarget) {
77
+ const fragment = this.fragment.cloneNode(true);
78
+ const targets = Object.create(this.proto);
79
+ targets.r = fragment;
80
+ targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : warningHost;
81
+ for (const id of this.nodeIds) {
82
+ targets[id]; // trigger locator
74
83
  }
84
+ return new HTMLView(fragment, this.factories, targets);
75
85
  }
76
- return bindingParts;
77
86
  }
78
- function compileAttributes(context, node, includeBasicValues = false) {
87
+ function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBasicValues = false) {
79
88
  const attributes = node.attributes;
89
+ const directives = context.directives;
80
90
  for (let i = 0, ii = attributes.length; i < ii; ++i) {
81
91
  const attr = attributes[i];
82
92
  const attrValue = attr.value;
83
- const parseResult = parseContent(context, attrValue);
93
+ const parseResult = Parser.parse(attrValue, directives);
84
94
  let result = null;
85
95
  if (parseResult === null) {
86
96
  if (includeBasicValues) {
87
- result = new HTMLBindingDirective(() => attrValue);
88
- result.targetName = attr.name;
97
+ result = new HTMLBindingDirective(oneTime(() => attrValue));
98
+ Aspect.assign(result, attr.name);
89
99
  }
90
100
  }
91
101
  else {
92
- result = createAggregateBinding(parseResult);
102
+ /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
103
+ result = Compiler.aggregate(parseResult);
93
104
  }
94
105
  if (result !== null) {
95
106
  node.removeAttributeNode(attr);
96
107
  i--;
97
108
  ii--;
98
- context.addFactory(result);
109
+ context.addFactory(result, parentId, nodeId, nodeIndex);
99
110
  }
100
111
  }
101
112
  }
102
- function compileContent(context, node, walker) {
103
- const parseResult = parseContent(context, node.textContent);
104
- if (parseResult !== null) {
105
- let lastNode = node;
106
- for (let i = 0, ii = parseResult.length; i < ii; ++i) {
107
- const currentPart = parseResult[i];
108
- const currentNode = i === 0
109
- ? node
110
- : lastNode.parentNode.insertBefore(document.createTextNode(""), lastNode.nextSibling);
111
- if (typeof currentPart === "string") {
112
- currentNode.textContent = currentPart;
113
- }
114
- else {
115
- currentNode.textContent = " ";
116
- context.captureContentBinding(currentPart);
117
- }
118
- lastNode = currentNode;
119
- context.targetIndex++;
120
- if (currentNode !== node) {
121
- walker.nextNode();
122
- }
113
+ function compileContent(context, node, parentId, nodeId, nodeIndex) {
114
+ const parseResult = Parser.parse(node.textContent, context.directives);
115
+ if (parseResult === null) {
116
+ next.node = node.nextSibling;
117
+ next.index = nodeIndex + 1;
118
+ return next;
119
+ }
120
+ let currentNode;
121
+ let lastNode = (currentNode = node);
122
+ for (let i = 0, ii = parseResult.length; i < ii; ++i) {
123
+ const currentPart = parseResult[i];
124
+ if (i !== 0) {
125
+ nodeIndex++;
126
+ nodeId = targetIdFrom(parentId, nodeIndex);
127
+ currentNode = lastNode.parentNode.insertBefore(document.createTextNode(""), lastNode.nextSibling);
128
+ }
129
+ if (isString(currentPart)) {
130
+ currentNode.textContent = currentPart;
131
+ }
132
+ else {
133
+ currentNode.textContent = " ";
134
+ Aspect.assign(currentPart);
135
+ context.addFactory(currentPart, parentId, nodeId, nodeIndex);
123
136
  }
124
- context.targetIndex--;
137
+ lastNode = currentNode;
138
+ }
139
+ next.index = nodeIndex + 1;
140
+ next.node = lastNode.nextSibling;
141
+ return next;
142
+ }
143
+ function compileChildren(context, parent, parentId) {
144
+ let nodeIndex = 0;
145
+ let childNode = parent.firstChild;
146
+ while (childNode) {
147
+ /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
148
+ const result = compileNode(context, parentId, childNode, nodeIndex);
149
+ childNode = result.node;
150
+ nodeIndex = result.index;
125
151
  }
126
152
  }
153
+ function compileNode(context, parentId, node, nodeIndex) {
154
+ const nodeId = targetIdFrom(parentId, nodeIndex);
155
+ switch (node.nodeType) {
156
+ case 1: // element node
157
+ compileAttributes(context, parentId, node, nodeId, nodeIndex);
158
+ compileChildren(context, node, nodeId);
159
+ break;
160
+ case 3: // text node
161
+ return compileContent(context, node, parentId, nodeId, nodeIndex);
162
+ case 8: // comment
163
+ const parts = Parser.parse(node.data, context.directives);
164
+ if (parts !== null) {
165
+ context.addFactory(
166
+ /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
167
+ Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
168
+ }
169
+ break;
170
+ }
171
+ next.index = nodeIndex + 1;
172
+ next.node = node.nextSibling;
173
+ return next;
174
+ }
175
+ function isMarker(node, directives) {
176
+ return (node &&
177
+ node.nodeType == 8 &&
178
+ Parser.parse(node.data, directives) !== null);
179
+ }
180
+ const templateTag = "TEMPLATE";
181
+ const policyOptions = { createHTML: html => html };
182
+ let htmlPolicy = globalThis.trustedTypes
183
+ ? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
184
+ : policyOptions;
185
+ const fastHTMLPolicy = htmlPolicy;
127
186
  /**
128
- * Compiles a template and associated directives into a raw compilation
129
- * result which include a cloneable DocumentFragment and factories capable
130
- * of attaching runtime behavior to nodes within the fragment.
131
- * @param template - The template to compile.
132
- * @param directives - The directives referenced by the template.
133
- * @remarks
134
- * The template that is provided for compilation is altered in-place
135
- * and cannot be compiled again. If the original template must be preserved,
136
- * it is recommended that you clone the original and pass the clone to this API.
187
+ * Common APIs related to compilation.
137
188
  * @public
138
189
  */
139
- export function compileTemplate(template, directives) {
140
- const fragment = template.content;
141
- // https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
142
- document.adoptNode(fragment);
143
- const context = CompilationContext.borrow(directives);
144
- compileAttributes(context, template, true);
145
- const hostBehaviorFactories = context.behaviorFactories;
146
- context.reset();
147
- const walker = DOM.createTemplateWalker(fragment);
148
- let node;
149
- while ((node = walker.nextNode())) {
150
- context.targetIndex++;
151
- switch (node.nodeType) {
152
- case 1: // element node
153
- compileAttributes(context, node);
154
- break;
155
- case 3: // text node
156
- compileContent(context, node, walker);
157
- break;
158
- case 8: // comment
159
- if (DOM.isMarker(node)) {
160
- context.addFactory(directives[DOM.extractDirectiveIndexFromMarker(node)]);
161
- }
190
+ export const Compiler = {
191
+ /**
192
+ * Sets the HTML trusted types policy used by the compiler.
193
+ * @param policy - The policy to set for HTML.
194
+ * @remarks
195
+ * This API can only be called once, for security reasons. It should be
196
+ * called by the application developer at the start of their program.
197
+ */
198
+ setHTMLPolicy(policy) {
199
+ if (htmlPolicy !== fastHTMLPolicy) {
200
+ throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
162
201
  }
163
- }
164
- let targetOffset = 0;
165
- if (
166
- // If the first node in a fragment is a marker, that means it's an unstable first node,
167
- // because something like a when, repeat, etc. could add nodes before the marker.
168
- // To mitigate this, we insert a stable first node. However, if we insert a node,
169
- // that will alter the result of the TreeWalker. So, we also need to offset the target index.
170
- DOM.isMarker(fragment.firstChild) ||
171
- // Or if there is only one node and a directive, it means the template's content
172
- // is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
173
- // the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
174
- (fragment.childNodes.length === 1 && directives.length)) {
175
- fragment.insertBefore(document.createComment(""), fragment.firstChild);
176
- targetOffset = -1;
177
- }
178
- const viewBehaviorFactories = context.behaviorFactories;
179
- context.release();
180
- return {
181
- fragment,
182
- viewBehaviorFactories,
183
- hostBehaviorFactories,
184
- targetOffset,
185
- };
186
- }
202
+ htmlPolicy = policy;
203
+ },
204
+ /**
205
+ * Compiles a template and associated directives into a compilation
206
+ * result which can be used to create views.
207
+ * @param html - The html string or template element to compile.
208
+ * @param directives - The directives referenced by the template.
209
+ * @remarks
210
+ * The template that is provided for compilation is altered in-place
211
+ * and cannot be compiled again. If the original template must be preserved,
212
+ * it is recommended that you clone the original and pass the clone to this API.
213
+ * @public
214
+ */
215
+ compile(html, directives) {
216
+ let template;
217
+ if (isString(html)) {
218
+ template = document.createElement(templateTag);
219
+ template.innerHTML = htmlPolicy.createHTML(html);
220
+ const fec = template.content.firstElementChild;
221
+ if (fec !== null && fec.tagName === templateTag) {
222
+ template = fec;
223
+ }
224
+ }
225
+ else {
226
+ template = html;
227
+ }
228
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
229
+ const fragment = document.adoptNode(template.content);
230
+ const context = new CompilationContext(fragment, directives);
231
+ compileAttributes(context, "", template, /* host */ "h", 0, true);
232
+ if (
233
+ // If the first node in a fragment is a marker, that means it's an unstable first node,
234
+ // because something like a when, repeat, etc. could add nodes before the marker.
235
+ // To mitigate this, we insert a stable first node. However, if we insert a node,
236
+ // that will alter the result of the TreeWalker. So, we also need to offset the target index.
237
+ isMarker(fragment.firstChild, directives) ||
238
+ // Or if there is only one node and a directive, it means the template's content
239
+ // is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
240
+ // the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
241
+ (fragment.childNodes.length === 1 && Object.keys(directives).length > 0)) {
242
+ fragment.insertBefore(document.createComment(""), fragment.firstChild);
243
+ }
244
+ compileChildren(context, fragment, /* root */ "r");
245
+ next.node = null; // prevent leaks
246
+ return context.freeze();
247
+ },
248
+ /**
249
+ * Sets the default compilation strategy that will be used by the ViewTemplate whenever
250
+ * it needs to compile a view preprocessed with the html template function.
251
+ * @param strategy - The compilation strategy to use when compiling templates.
252
+ */
253
+ setDefaultStrategy(strategy) {
254
+ this.compile = strategy;
255
+ },
256
+ /**
257
+ * Aggregates an array of strings and directives into a single directive.
258
+ * @param parts - A heterogeneous array of static strings interspersed with
259
+ * directives.
260
+ * @returns A single inline directive that aggregates the behavior of all the parts.
261
+ */
262
+ aggregate(parts) {
263
+ if (parts.length === 1) {
264
+ return parts[0];
265
+ }
266
+ let sourceAspect;
267
+ let binding;
268
+ let isVolatile = false;
269
+ const partCount = parts.length;
270
+ const finalParts = parts.map((x) => {
271
+ if (isString(x)) {
272
+ return () => x;
273
+ }
274
+ sourceAspect = x.sourceAspect || sourceAspect;
275
+ binding = x.dataBinding || binding;
276
+ isVolatile = isVolatile || x.dataBinding.isVolatile;
277
+ return x.dataBinding.evaluate;
278
+ });
279
+ const expression = (scope, context) => {
280
+ let output = "";
281
+ for (let i = 0; i < partCount; ++i) {
282
+ output += finalParts[i](scope, context);
283
+ }
284
+ return output;
285
+ };
286
+ binding.evaluate = expression;
287
+ binding.isVolatile = isVolatile;
288
+ const directive = new HTMLBindingDirective(binding);
289
+ Aspect.assign(directive, sourceAspect);
290
+ return directive;
291
+ },
292
+ };
@@ -0,0 +1,49 @@
1
+ import { Updates } from "../observation/update-queue.js";
2
+ /**
3
+ * Common DOM APIs.
4
+ * @public
5
+ */
6
+ export const DOM = Object.freeze({
7
+ /**
8
+ * @deprecated
9
+ * Use Updates.enqueue().
10
+ */
11
+ queueUpdate: Updates.enqueue,
12
+ /**
13
+ * @deprecated
14
+ * Use Updates.next()
15
+ */
16
+ nextUpdate: Updates.next,
17
+ /**
18
+ * @deprecated
19
+ * Use Updates.process()
20
+ */
21
+ processUpdates: Updates.process,
22
+ /**
23
+ * Sets an attribute value on an element.
24
+ * @param element - The element to set the attribute value on.
25
+ * @param attributeName - The attribute name to set.
26
+ * @param value - The value of the attribute to set.
27
+ * @remarks
28
+ * If the value is `null` or `undefined`, the attribute is removed, otherwise
29
+ * it is set to the provided value using the standard `setAttribute` API.
30
+ */
31
+ setAttribute(element, attributeName, value) {
32
+ value === null || value === undefined
33
+ ? element.removeAttribute(attributeName)
34
+ : element.setAttribute(attributeName, value);
35
+ },
36
+ /**
37
+ * Sets a boolean attribute value.
38
+ * @param element - The element to set the boolean attribute value on.
39
+ * @param attributeName - The attribute name to set.
40
+ * @param value - The value of the attribute to set.
41
+ * @remarks
42
+ * If the value is true, the attribute is added; otherwise it is removed.
43
+ */
44
+ setBooleanAttribute(element, attributeName, value) {
45
+ value
46
+ ? element.setAttribute(attributeName, "")
47
+ : element.removeAttribute(attributeName);
48
+ },
49
+ });