@microsoft/fast-element 1.10.1 → 2.0.0-beta.2
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/.eslintrc.json +1 -12
- package/CHANGELOG.json +432 -1
- package/CHANGELOG.md +74 -2
- package/README.md +2 -2
- package/dist/dts/components/attributes.d.ts +4 -1
- package/dist/dts/components/controller.d.ts +12 -11
- package/dist/dts/components/fast-definitions.d.ts +10 -2
- package/dist/dts/components/fast-element.d.ts +12 -5
- package/dist/dts/context.d.ts +157 -0
- package/dist/dts/debug.d.ts +1 -0
- package/dist/dts/hooks.d.ts +20 -0
- package/dist/dts/index.d.ts +16 -15
- package/dist/dts/index.debug.d.ts +2 -0
- package/dist/dts/index.rollup.d.ts +2 -0
- package/dist/dts/index.rollup.debug.d.ts +3 -0
- package/dist/dts/interfaces.d.ts +144 -0
- package/dist/dts/metadata.d.ts +25 -0
- package/dist/dts/observation/arrays.d.ts +207 -0
- package/dist/dts/observation/behavior.d.ts +4 -4
- package/dist/dts/observation/notifier.d.ts +18 -18
- package/dist/dts/observation/observable.d.ts +56 -18
- package/dist/dts/observation/splice-strategies.d.ts +13 -0
- package/dist/dts/observation/update-queue.d.ts +40 -0
- package/dist/dts/platform.d.ts +18 -67
- package/dist/dts/polyfills.d.ts +8 -0
- package/dist/dts/styles/css-directive.d.ts +43 -5
- package/dist/dts/styles/css.d.ts +19 -3
- package/dist/dts/styles/element-styles.d.ts +42 -62
- package/dist/dts/templating/binding-signal.d.ts +38 -0
- package/dist/dts/templating/binding-two-way.d.ts +56 -0
- package/dist/dts/templating/binding.d.ts +233 -65
- package/dist/dts/templating/children.d.ts +18 -15
- package/dist/dts/templating/compiler.d.ts +46 -28
- package/dist/dts/templating/dom.d.ts +41 -0
- package/dist/dts/templating/html-directive.d.ts +179 -43
- package/dist/dts/templating/markup.d.ts +48 -0
- package/dist/dts/templating/node-observation.d.ts +45 -29
- package/dist/dts/templating/ref.d.ts +6 -12
- package/dist/dts/templating/repeat.d.ts +26 -14
- package/dist/dts/templating/slotted.d.ts +13 -14
- package/dist/dts/templating/template.d.ts +27 -21
- package/dist/dts/templating/view.d.ts +15 -22
- package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +1 -1
- package/dist/dts/utilities.d.ts +40 -0
- package/dist/esm/components/attributes.js +25 -24
- package/dist/esm/components/controller.js +77 -57
- package/dist/esm/components/fast-definitions.js +16 -22
- package/dist/esm/components/fast-element.js +10 -2
- package/dist/esm/context.js +159 -0
- package/dist/esm/debug.js +29 -0
- package/dist/esm/hooks.js +32 -0
- package/dist/esm/index.debug.js +2 -0
- package/dist/esm/index.js +19 -14
- package/dist/esm/index.rollup.debug.js +3 -0
- package/dist/esm/index.rollup.js +2 -0
- package/dist/esm/interfaces.js +8 -1
- package/dist/esm/metadata.js +60 -0
- package/dist/esm/observation/arrays.js +269 -0
- package/dist/esm/observation/notifier.js +75 -83
- package/dist/esm/observation/observable.js +93 -68
- package/dist/esm/observation/{array-change-records.js → splice-strategies.js} +136 -62
- package/dist/esm/observation/update-queue.js +67 -0
- package/dist/esm/platform.js +36 -42
- package/dist/esm/polyfills.js +85 -0
- package/dist/esm/styles/css-directive.js +29 -13
- package/dist/esm/styles/css.js +27 -40
- package/dist/esm/styles/element-styles.js +65 -104
- package/dist/esm/templating/binding-signal.js +84 -0
- package/dist/esm/templating/binding-two-way.js +76 -0
- package/dist/esm/templating/binding.js +306 -153
- package/dist/esm/templating/children.js +33 -23
- package/dist/esm/templating/compiler.js +235 -152
- package/dist/esm/templating/dom.js +49 -0
- package/dist/esm/templating/html-directive.js +125 -40
- package/dist/esm/templating/markup.js +75 -0
- package/dist/esm/templating/node-observation.js +50 -45
- package/dist/esm/templating/ref.js +7 -16
- package/dist/esm/templating/repeat.js +39 -36
- package/dist/esm/templating/slotted.js +23 -20
- package/dist/esm/templating/template.js +51 -95
- package/dist/esm/templating/view.js +44 -43
- package/dist/esm/templating/when.js +2 -1
- package/dist/esm/utilities.js +139 -0
- package/dist/fast-element.api.json +11789 -5377
- package/dist/fast-element.d.ts +1177 -531
- package/dist/fast-element.debug.js +3717 -0
- package/dist/fast-element.debug.min.js +1 -0
- package/dist/fast-element.js +3466 -4022
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +2697 -0
- package/docs/api-report.md +472 -219
- package/docs/fast-element-2-changes.md +15 -0
- package/docs/guide/declaring-templates.md +5 -4
- package/docs/guide/defining-elements.md +3 -2
- package/docs/guide/leveraging-css.md +1 -0
- package/docs/guide/next-steps.md +3 -2
- package/docs/guide/observables-and-state.md +2 -1
- package/docs/guide/using-directives.md +2 -1
- package/docs/guide/working-with-shadow-dom.md +1 -0
- package/karma.conf.cjs +6 -17
- package/package.json +65 -15
- package/dist/dts/dom.d.ts +0 -112
- package/dist/dts/observation/array-change-records.d.ts +0 -48
- package/dist/dts/observation/array-observer.d.ts +0 -9
- package/dist/esm/dom.js +0 -207
- package/dist/esm/observation/array-observer.js +0 -173
|
@@ -1,186 +1,269 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { isString } from "../interfaces.js";
|
|
2
|
+
import { FAST } from "../platform.js";
|
|
3
|
+
import { Parser } from "./markup.js";
|
|
4
|
+
import { bind, 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
|
+
};
|
|
4
14
|
class CompilationContext {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
this.
|
|
15
|
+
constructor(fragment, directives) {
|
|
16
|
+
this.fragment = fragment;
|
|
17
|
+
this.directives = directives;
|
|
18
|
+
this.proto = null;
|
|
19
|
+
this.nodeIds = new Set();
|
|
20
|
+
this.descriptors = {};
|
|
21
|
+
this.factories = [];
|
|
8
22
|
}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
this.
|
|
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;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function createAggregateBinding(parts) {
|
|
30
|
-
if (parts.length === 1) {
|
|
31
|
-
return parts[0];
|
|
23
|
+
addFactory(factory, parentId, nodeId, targetIndex) {
|
|
24
|
+
if (!this.nodeIds.has(nodeId)) {
|
|
25
|
+
this.nodeIds.add(nodeId);
|
|
26
|
+
this.addTargetDescriptor(parentId, nodeId, targetIndex);
|
|
27
|
+
}
|
|
28
|
+
factory.nodeId = nodeId;
|
|
29
|
+
this.factories.push(factory);
|
|
32
30
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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;
|
|
31
|
+
freeze() {
|
|
32
|
+
this.proto = Object.create(null, this.descriptors);
|
|
33
|
+
return this;
|
|
58
34
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
literal = current;
|
|
35
|
+
addTargetDescriptor(parentId, targetId, targetIndex) {
|
|
36
|
+
const descriptors = this.descriptors;
|
|
37
|
+
if (targetId === "r" || // root
|
|
38
|
+
targetId === "h" || // host
|
|
39
|
+
descriptors[targetId]) {
|
|
40
|
+
return;
|
|
66
41
|
}
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
42
|
+
if (!descriptors[parentId]) {
|
|
43
|
+
const index = parentId.lastIndexOf(".");
|
|
44
|
+
const grandparentId = parentId.substring(0, index);
|
|
45
|
+
const childIndex = parseInt(parentId.substring(index + 1));
|
|
46
|
+
this.addTargetDescriptor(grandparentId, parentId, childIndex);
|
|
71
47
|
}
|
|
72
|
-
|
|
73
|
-
|
|
48
|
+
let descriptor = descriptorCache[targetId];
|
|
49
|
+
if (!descriptor) {
|
|
50
|
+
const field = `_${targetId}`;
|
|
51
|
+
descriptorCache[targetId] = descriptor = {
|
|
52
|
+
get() {
|
|
53
|
+
var _a;
|
|
54
|
+
return ((_a = this[field]) !== null && _a !== void 0 ? _a : (this[field] = this[parentId].childNodes[targetIndex]));
|
|
55
|
+
},
|
|
56
|
+
};
|
|
74
57
|
}
|
|
58
|
+
descriptors[targetId] = descriptor;
|
|
59
|
+
}
|
|
60
|
+
createView(hostBindingTarget) {
|
|
61
|
+
const fragment = this.fragment.cloneNode(true);
|
|
62
|
+
const targets = Object.create(this.proto);
|
|
63
|
+
targets.r = fragment;
|
|
64
|
+
targets.h = hostBindingTarget !== null && hostBindingTarget !== void 0 ? hostBindingTarget : fragment;
|
|
65
|
+
for (const id of this.nodeIds) {
|
|
66
|
+
targets[id]; // trigger locator
|
|
67
|
+
}
|
|
68
|
+
return new HTMLView(fragment, this.factories, targets);
|
|
75
69
|
}
|
|
76
|
-
return bindingParts;
|
|
77
70
|
}
|
|
78
|
-
function compileAttributes(context, node, includeBasicValues = false) {
|
|
71
|
+
function compileAttributes(context, parentId, node, nodeId, nodeIndex, includeBasicValues = false) {
|
|
79
72
|
const attributes = node.attributes;
|
|
73
|
+
const directives = context.directives;
|
|
80
74
|
for (let i = 0, ii = attributes.length; i < ii; ++i) {
|
|
81
75
|
const attr = attributes[i];
|
|
82
76
|
const attrValue = attr.value;
|
|
83
|
-
const parseResult =
|
|
77
|
+
const parseResult = Parser.parse(attrValue, directives);
|
|
84
78
|
let result = null;
|
|
85
79
|
if (parseResult === null) {
|
|
86
80
|
if (includeBasicValues) {
|
|
87
|
-
result =
|
|
88
|
-
result
|
|
81
|
+
result = bind(() => attrValue, oneTime);
|
|
82
|
+
Aspect.assign(result, attr.name);
|
|
89
83
|
}
|
|
90
84
|
}
|
|
91
85
|
else {
|
|
92
|
-
|
|
86
|
+
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
87
|
+
result = Compiler.aggregate(parseResult);
|
|
93
88
|
}
|
|
94
89
|
if (result !== null) {
|
|
95
90
|
node.removeAttributeNode(attr);
|
|
96
91
|
i--;
|
|
97
92
|
ii--;
|
|
98
|
-
context.addFactory(result);
|
|
93
|
+
context.addFactory(result, parentId, nodeId, nodeIndex);
|
|
99
94
|
}
|
|
100
95
|
}
|
|
101
96
|
}
|
|
102
|
-
function compileContent(context, node,
|
|
103
|
-
const parseResult =
|
|
104
|
-
if (parseResult
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
97
|
+
function compileContent(context, node, parentId, nodeId, nodeIndex) {
|
|
98
|
+
const parseResult = Parser.parse(node.textContent, context.directives);
|
|
99
|
+
if (parseResult === null) {
|
|
100
|
+
next.node = node.nextSibling;
|
|
101
|
+
next.index = nodeIndex + 1;
|
|
102
|
+
return next;
|
|
103
|
+
}
|
|
104
|
+
let currentNode;
|
|
105
|
+
let lastNode = (currentNode = node);
|
|
106
|
+
for (let i = 0, ii = parseResult.length; i < ii; ++i) {
|
|
107
|
+
const currentPart = parseResult[i];
|
|
108
|
+
if (i !== 0) {
|
|
109
|
+
nodeIndex++;
|
|
110
|
+
nodeId = targetIdFrom(parentId, nodeIndex);
|
|
111
|
+
currentNode = lastNode.parentNode.insertBefore(document.createTextNode(""), lastNode.nextSibling);
|
|
112
|
+
}
|
|
113
|
+
if (isString(currentPart)) {
|
|
114
|
+
currentNode.textContent = currentPart;
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
currentNode.textContent = " ";
|
|
118
|
+
context.addFactory(currentPart, parentId, nodeId, nodeIndex);
|
|
123
119
|
}
|
|
124
|
-
|
|
120
|
+
lastNode = currentNode;
|
|
125
121
|
}
|
|
122
|
+
next.index = nodeIndex + 1;
|
|
123
|
+
next.node = lastNode.nextSibling;
|
|
124
|
+
return next;
|
|
126
125
|
}
|
|
126
|
+
function compileChildren(context, parent, parentId) {
|
|
127
|
+
let nodeIndex = 0;
|
|
128
|
+
let childNode = parent.firstChild;
|
|
129
|
+
while (childNode) {
|
|
130
|
+
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
131
|
+
const result = compileNode(context, parentId, childNode, nodeIndex);
|
|
132
|
+
childNode = result.node;
|
|
133
|
+
nodeIndex = result.index;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function compileNode(context, parentId, node, nodeIndex) {
|
|
137
|
+
const nodeId = targetIdFrom(parentId, nodeIndex);
|
|
138
|
+
switch (node.nodeType) {
|
|
139
|
+
case 1: // element node
|
|
140
|
+
compileAttributes(context, parentId, node, nodeId, nodeIndex);
|
|
141
|
+
compileChildren(context, node, nodeId);
|
|
142
|
+
break;
|
|
143
|
+
case 3: // text node
|
|
144
|
+
return compileContent(context, node, parentId, nodeId, nodeIndex);
|
|
145
|
+
case 8: // comment
|
|
146
|
+
const parts = Parser.parse(node.data, context.directives);
|
|
147
|
+
if (parts !== null) {
|
|
148
|
+
context.addFactory(
|
|
149
|
+
/* eslint-disable-next-line @typescript-eslint/no-use-before-define */
|
|
150
|
+
Compiler.aggregate(parts), parentId, nodeId, nodeIndex);
|
|
151
|
+
}
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
next.index = nodeIndex + 1;
|
|
155
|
+
next.node = node.nextSibling;
|
|
156
|
+
return next;
|
|
157
|
+
}
|
|
158
|
+
function isMarker(node, directives) {
|
|
159
|
+
return (node &&
|
|
160
|
+
node.nodeType == 8 &&
|
|
161
|
+
Parser.parse(node.data, directives) !== null);
|
|
162
|
+
}
|
|
163
|
+
const templateTag = "TEMPLATE";
|
|
164
|
+
const policyOptions = { createHTML: html => html };
|
|
165
|
+
let htmlPolicy = globalThis.trustedTypes
|
|
166
|
+
? globalThis.trustedTypes.createPolicy("fast-html", policyOptions)
|
|
167
|
+
: policyOptions;
|
|
168
|
+
const fastHTMLPolicy = htmlPolicy;
|
|
127
169
|
/**
|
|
128
|
-
*
|
|
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.
|
|
170
|
+
* Common APIs related to compilation.
|
|
137
171
|
* @public
|
|
138
172
|
*/
|
|
139
|
-
export
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
-
}
|
|
173
|
+
export const Compiler = {
|
|
174
|
+
/**
|
|
175
|
+
* Sets the HTML trusted types policy used by the compiler.
|
|
176
|
+
* @param policy - The policy to set for HTML.
|
|
177
|
+
* @remarks
|
|
178
|
+
* This API can only be called once, for security reasons. It should be
|
|
179
|
+
* called by the application developer at the start of their program.
|
|
180
|
+
*/
|
|
181
|
+
setHTMLPolicy(policy) {
|
|
182
|
+
if (htmlPolicy !== fastHTMLPolicy) {
|
|
183
|
+
throw FAST.error(1201 /* Message.onlySetHTMLPolicyOnce */);
|
|
162
184
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
185
|
+
htmlPolicy = policy;
|
|
186
|
+
},
|
|
187
|
+
/**
|
|
188
|
+
* Compiles a template and associated directives into a compilation
|
|
189
|
+
* result which can be used to create views.
|
|
190
|
+
* @param html - The html string or template element to compile.
|
|
191
|
+
* @param directives - The directives referenced by the template.
|
|
192
|
+
* @remarks
|
|
193
|
+
* The template that is provided for compilation is altered in-place
|
|
194
|
+
* and cannot be compiled again. If the original template must be preserved,
|
|
195
|
+
* it is recommended that you clone the original and pass the clone to this API.
|
|
196
|
+
* @public
|
|
197
|
+
*/
|
|
198
|
+
compile(html, directives) {
|
|
199
|
+
let template;
|
|
200
|
+
if (isString(html)) {
|
|
201
|
+
template = document.createElement(templateTag);
|
|
202
|
+
template.innerHTML = htmlPolicy.createHTML(html);
|
|
203
|
+
const fec = template.content.firstElementChild;
|
|
204
|
+
if (fec !== null && fec.tagName === templateTag) {
|
|
205
|
+
template = fec;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
template = html;
|
|
210
|
+
}
|
|
211
|
+
// https://bugs.chromium.org/p/chromium/issues/detail?id=1111864
|
|
212
|
+
const fragment = document.adoptNode(template.content);
|
|
213
|
+
const context = new CompilationContext(fragment, directives);
|
|
214
|
+
compileAttributes(context, "", template, /* host */ "h", 0, true);
|
|
215
|
+
if (
|
|
216
|
+
// If the first node in a fragment is a marker, that means it's an unstable first node,
|
|
217
|
+
// because something like a when, repeat, etc. could add nodes before the marker.
|
|
218
|
+
// To mitigate this, we insert a stable first node. However, if we insert a node,
|
|
219
|
+
// that will alter the result of the TreeWalker. So, we also need to offset the target index.
|
|
220
|
+
isMarker(fragment.firstChild, directives) ||
|
|
221
|
+
// Or if there is only one node and a directive, it means the template's content
|
|
222
|
+
// is *only* the directive. In that case, HTMLView.dispose() misses any nodes inserted by
|
|
223
|
+
// the directive. Inserting a new node ensures proper disposal of nodes added by the directive.
|
|
224
|
+
(fragment.childNodes.length === 1 && Object.keys(directives).length > 0)) {
|
|
225
|
+
fragment.insertBefore(document.createComment(""), fragment.firstChild);
|
|
226
|
+
}
|
|
227
|
+
compileChildren(context, fragment, /* root */ "r");
|
|
228
|
+
next.node = null; // prevent leaks
|
|
229
|
+
return context.freeze();
|
|
230
|
+
},
|
|
231
|
+
/**
|
|
232
|
+
* Sets the default compilation strategy that will be used by the ViewTemplate whenever
|
|
233
|
+
* it needs to compile a view preprocessed with the html template function.
|
|
234
|
+
* @param strategy - The compilation strategy to use when compiling templates.
|
|
235
|
+
*/
|
|
236
|
+
setDefaultStrategy(strategy) {
|
|
237
|
+
this.compile = strategy;
|
|
238
|
+
},
|
|
239
|
+
/**
|
|
240
|
+
* Aggregates an array of strings and directives into a single directive.
|
|
241
|
+
* @param parts - A heterogeneous array of static strings interspersed with
|
|
242
|
+
* directives.
|
|
243
|
+
* @returns A single inline directive that aggregates the behavior of all the parts.
|
|
244
|
+
*/
|
|
245
|
+
aggregate(parts) {
|
|
246
|
+
if (parts.length === 1) {
|
|
247
|
+
return parts[0];
|
|
248
|
+
}
|
|
249
|
+
let sourceAspect;
|
|
250
|
+
const partCount = parts.length;
|
|
251
|
+
const finalParts = parts.map((x) => {
|
|
252
|
+
if (isString(x)) {
|
|
253
|
+
return () => x;
|
|
254
|
+
}
|
|
255
|
+
sourceAspect = x.sourceAspect || sourceAspect;
|
|
256
|
+
return x.binding;
|
|
257
|
+
});
|
|
258
|
+
const binding = (scope, context) => {
|
|
259
|
+
let output = "";
|
|
260
|
+
for (let i = 0; i < partCount; ++i) {
|
|
261
|
+
output += finalParts[i](scope, context);
|
|
262
|
+
}
|
|
263
|
+
return output;
|
|
264
|
+
};
|
|
265
|
+
const directive = bind(binding);
|
|
266
|
+
Aspect.assign(directive, sourceAspect);
|
|
267
|
+
return directive;
|
|
268
|
+
},
|
|
269
|
+
};
|
|
@@ -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
|
+
});
|
|
@@ -1,64 +1,149 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createTypeRegistry } from "../platform.js";
|
|
2
|
+
import { Markup } from "./markup.js";
|
|
3
|
+
const registry = createTypeRegistry();
|
|
2
4
|
/**
|
|
3
5
|
* Instructs the template engine to apply behavior to a node.
|
|
4
6
|
* @public
|
|
5
7
|
*/
|
|
6
|
-
export
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
export const HTMLDirective = Object.freeze({
|
|
9
|
+
/**
|
|
10
|
+
* Gets the directive definition associated with the instance.
|
|
11
|
+
* @param instance - The directive instance to retrieve the definition for.
|
|
12
|
+
*/
|
|
13
|
+
getForInstance: registry.getForInstance,
|
|
14
|
+
/**
|
|
15
|
+
* Gets the directive definition associated with the specified type.
|
|
16
|
+
* @param type - The directive type to retrieve the definition for.
|
|
17
|
+
*/
|
|
18
|
+
getByType: registry.getByType,
|
|
19
|
+
/**
|
|
20
|
+
* Defines an HTMLDirective based on the options.
|
|
21
|
+
* @param type - The type to define as a directive.
|
|
22
|
+
* @param options - Options that specify the directive's application.
|
|
23
|
+
*/
|
|
24
|
+
define(type, options) {
|
|
25
|
+
options = options || {};
|
|
26
|
+
options.type = type;
|
|
27
|
+
registry.register(options);
|
|
28
|
+
return type;
|
|
29
|
+
},
|
|
30
|
+
});
|
|
14
31
|
/**
|
|
15
|
-
*
|
|
32
|
+
* Decorator: Defines an HTMLDirective.
|
|
33
|
+
* @param options - Provides options that specify the directive's application.
|
|
16
34
|
* @public
|
|
17
35
|
*/
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
* @param index - The index of the directive within the template.
|
|
24
|
-
*/
|
|
25
|
-
this.createPlaceholder = DOM.createInterpolationPlaceholder;
|
|
26
|
-
}
|
|
36
|
+
export function htmlDirective(options) {
|
|
37
|
+
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
38
|
+
return function (type) {
|
|
39
|
+
HTMLDirective.define(type, options);
|
|
40
|
+
};
|
|
27
41
|
}
|
|
28
42
|
/**
|
|
29
|
-
*
|
|
43
|
+
* The type of HTML aspect to target.
|
|
30
44
|
* @public
|
|
31
45
|
*/
|
|
32
|
-
export
|
|
46
|
+
export const Aspect = Object.freeze({
|
|
47
|
+
/**
|
|
48
|
+
* Not aspected.
|
|
49
|
+
*/
|
|
50
|
+
none: 0,
|
|
51
|
+
/**
|
|
52
|
+
* An attribute.
|
|
53
|
+
*/
|
|
54
|
+
attribute: 1,
|
|
55
|
+
/**
|
|
56
|
+
* A boolean attribute.
|
|
57
|
+
*/
|
|
58
|
+
booleanAttribute: 2,
|
|
59
|
+
/**
|
|
60
|
+
* A property.
|
|
61
|
+
*/
|
|
62
|
+
property: 3,
|
|
63
|
+
/**
|
|
64
|
+
* Content
|
|
65
|
+
*/
|
|
66
|
+
content: 4,
|
|
67
|
+
/**
|
|
68
|
+
* A token list.
|
|
69
|
+
*/
|
|
70
|
+
tokenList: 5,
|
|
71
|
+
/**
|
|
72
|
+
* An event.
|
|
73
|
+
*/
|
|
74
|
+
event: 6,
|
|
33
75
|
/**
|
|
34
76
|
*
|
|
35
|
-
* @param
|
|
36
|
-
* @param
|
|
37
|
-
* @param options - Options to pass to the behavior during creation.
|
|
77
|
+
* @param directive - The directive to assign the aspect to.
|
|
78
|
+
* @param value - The value to base the aspect determination on.
|
|
38
79
|
*/
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
80
|
+
assign(directive, value) {
|
|
81
|
+
directive.sourceAspect = value;
|
|
82
|
+
if (!value) {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
switch (value[0]) {
|
|
86
|
+
case ":":
|
|
87
|
+
directive.targetAspect = value.substring(1);
|
|
88
|
+
switch (directive.targetAspect) {
|
|
89
|
+
case "innerHTML":
|
|
90
|
+
directive.aspectType = Aspect.property;
|
|
91
|
+
break;
|
|
92
|
+
case "classList":
|
|
93
|
+
directive.aspectType = Aspect.tokenList;
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
directive.aspectType = Aspect.property;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
break;
|
|
100
|
+
case "?":
|
|
101
|
+
directive.targetAspect = value.substring(1);
|
|
102
|
+
directive.aspectType = Aspect.booleanAttribute;
|
|
103
|
+
break;
|
|
104
|
+
case "@":
|
|
105
|
+
directive.targetAspect = value.substring(1);
|
|
106
|
+
directive.aspectType = Aspect.event;
|
|
107
|
+
break;
|
|
108
|
+
default:
|
|
109
|
+
if (value === "class") {
|
|
110
|
+
directive.targetAspect = "className";
|
|
111
|
+
directive.aspectType = Aspect.property;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
directive.targetAspect = value;
|
|
115
|
+
directive.aspectType = Aspect.attribute;
|
|
116
|
+
}
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
/**
|
|
122
|
+
* A base class used for attribute directives that don't need internal state.
|
|
123
|
+
* @public
|
|
124
|
+
*/
|
|
125
|
+
export class StatelessAttachedAttributeDirective {
|
|
126
|
+
/**
|
|
127
|
+
* Creates an instance of RefDirective.
|
|
128
|
+
* @param options - The options to use in configuring the directive.
|
|
129
|
+
*/
|
|
130
|
+
constructor(options) {
|
|
43
131
|
this.options = options;
|
|
44
132
|
}
|
|
45
133
|
/**
|
|
46
|
-
* Creates a
|
|
47
|
-
* @param
|
|
48
|
-
* @remarks
|
|
49
|
-
* Creates a custom attribute placeholder.
|
|
134
|
+
* Creates a behavior.
|
|
135
|
+
* @param targets - The targets available for behaviors to be attached to.
|
|
50
136
|
*/
|
|
51
|
-
|
|
52
|
-
return
|
|
137
|
+
createBehavior(targets) {
|
|
138
|
+
return this;
|
|
53
139
|
}
|
|
54
140
|
/**
|
|
55
|
-
* Creates a
|
|
56
|
-
* @param
|
|
141
|
+
* Creates a placeholder string based on the directive's index within the template.
|
|
142
|
+
* @param index - The index of the directive within the template.
|
|
57
143
|
* @remarks
|
|
58
|
-
* Creates
|
|
59
|
-
* and passes the target and options to that `behavior`'s constructor.
|
|
144
|
+
* Creates a custom attribute placeholder.
|
|
60
145
|
*/
|
|
61
|
-
|
|
62
|
-
return
|
|
146
|
+
createHTML(add) {
|
|
147
|
+
return Markup.attribute(add(this));
|
|
63
148
|
}
|
|
64
149
|
}
|