@microsoft/fast-element 2.0.0-beta.9 → 2.0.1
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 -1
- package/CHANGELOG.json +518 -0
- package/CHANGELOG.md +181 -1
- package/README.md +1 -9
- package/api-extractor.context.json +14 -0
- package/api-extractor.di.json +14 -0
- package/dist/context/context.api.json +1068 -0
- package/dist/di/di.api.json +4929 -0
- package/dist/dts/binding/binding.d.ts +49 -0
- package/dist/dts/binding/normalize.d.ts +9 -0
- package/dist/dts/binding/one-time.d.ts +11 -0
- package/dist/dts/binding/one-way.d.ts +20 -0
- package/dist/dts/{templating/binding-signal.d.ts → binding/signal.d.ts} +19 -4
- package/dist/dts/{templating/binding-two-way.d.ts → binding/two-way.d.ts} +9 -5
- package/dist/dts/components/attributes.d.ts +7 -1
- package/dist/dts/components/element-controller.d.ts +104 -8
- package/dist/dts/components/element-hydration.d.ts +2 -0
- package/dist/dts/components/fast-definitions.d.ts +6 -0
- package/dist/dts/components/hydration.d.ts +56 -0
- package/dist/dts/components/install-hydration.d.ts +1 -0
- package/dist/dts/context.d.ts +29 -15
- package/dist/dts/di/di.d.ts +0 -5
- package/dist/dts/dom-policy.d.ts +83 -0
- package/dist/dts/dom.d.ts +100 -0
- package/dist/dts/hydration/target-builder.d.ts +63 -0
- package/dist/dts/index.d.ts +33 -26
- package/dist/dts/index.rollup.d.ts +0 -1
- package/dist/dts/index.rollup.debug.d.ts +0 -1
- package/dist/dts/interfaces.d.ts +32 -82
- package/dist/dts/metadata.d.ts +6 -5
- package/dist/dts/observation/arrays.d.ts +1 -1
- package/dist/dts/observation/observable.bench.d.ts +18 -0
- package/dist/dts/observation/observable.d.ts +5 -5
- package/dist/dts/pending-task.d.ts +19 -7
- package/dist/dts/platform.d.ts +11 -2
- package/dist/dts/polyfills.d.ts +0 -8
- package/dist/dts/styles/css-binding-directive.d.ts +60 -0
- package/dist/dts/styles/css.d.ts +9 -7
- package/dist/dts/styles/element-styles.d.ts +1 -14
- package/dist/dts/styles/host.d.ts +2 -5
- package/dist/dts/styles/style-strategy.d.ts +42 -0
- package/dist/dts/templating/compiler.d.ts +11 -13
- package/dist/dts/templating/{binding.d.ts → html-binding-directive.d.ts} +21 -41
- package/dist/dts/templating/html-directive.d.ts +44 -140
- package/dist/dts/templating/install-hydratable-view-templates.d.ts +1 -0
- package/dist/dts/templating/node-observation.d.ts +11 -1
- package/dist/dts/templating/ref.d.ts +4 -0
- package/dist/dts/templating/render.bench.d.ts +3 -0
- package/dist/dts/templating/render.d.ts +49 -9
- package/dist/dts/templating/repeat-basic-reverse.bench.d.ts +3 -0
- package/dist/dts/templating/repeat-basic-shift.bench.d.ts +3 -0
- package/dist/dts/templating/repeat.d.ts +31 -9
- package/dist/dts/templating/template.d.ts +97 -12
- package/dist/dts/templating/view.d.ts +146 -29
- package/dist/dts/templating/when-basic.bench.d.ts +3 -0
- package/dist/dts/templating/when-conditional.bench.d.ts +3 -0
- package/dist/dts/templating/when-switch.bench.d.ts +3 -0
- package/dist/dts/templating/when.d.ts +3 -1
- package/dist/dts/testing/fakes.d.ts +12 -1
- package/dist/dts/tsdoc-metadata.json +1 -1
- package/dist/dts/utilities.d.ts +55 -1
- package/dist/esm/binding/binding.js +18 -0
- package/dist/esm/binding/normalize.js +17 -0
- package/dist/esm/binding/one-time.js +21 -0
- package/dist/esm/binding/one-way.js +30 -0
- package/dist/esm/{templating/binding-signal.js → binding/signal.js} +22 -6
- package/dist/esm/{templating/binding-two-way.js → binding/two-way.js} +18 -12
- package/dist/esm/components/attributes.js +19 -6
- package/dist/esm/components/element-controller.js +319 -49
- package/dist/esm/components/element-hydration.js +2 -0
- package/dist/esm/components/fast-definitions.js +12 -4
- package/dist/esm/components/fast-element.js +3 -1
- package/dist/esm/components/hydration.js +104 -0
- package/dist/esm/components/install-hydration.js +3 -0
- package/dist/esm/context.js +26 -4
- package/dist/esm/debug.js +8 -2
- package/dist/esm/di/di.js +9 -12
- package/dist/esm/dom-policy.js +345 -0
- package/dist/esm/dom.js +101 -0
- package/dist/esm/hydration/target-builder.js +175 -0
- package/dist/esm/index.js +34 -25
- package/dist/esm/index.rollup.debug.js +3 -1
- package/dist/esm/index.rollup.js +3 -1
- package/dist/esm/interfaces.js +51 -3
- package/dist/esm/metadata.js +11 -8
- package/dist/esm/observation/arrays.js +1 -1
- package/dist/esm/observation/observable.bench.js +79 -0
- package/dist/esm/observation/observable.js +20 -15
- package/dist/esm/observation/update-queue.js +2 -2
- package/dist/esm/pending-task.js +13 -1
- package/dist/esm/platform.js +12 -2
- package/dist/esm/polyfills.js +3 -61
- package/dist/esm/styles/css-binding-directive.js +76 -0
- package/dist/esm/styles/css.js +14 -7
- package/dist/esm/styles/element-styles.js +0 -33
- package/dist/esm/styles/style-strategy.js +1 -0
- package/dist/esm/templating/children.js +8 -4
- package/dist/esm/templating/compiler.js +37 -44
- package/dist/esm/templating/html-binding-directive.js +218 -0
- package/dist/esm/templating/html-directive.js +25 -152
- package/dist/esm/templating/install-hydratable-view-templates.js +17 -0
- package/dist/esm/templating/node-observation.js +14 -8
- package/dist/esm/templating/ref.js +1 -1
- package/dist/esm/templating/render.bench.js +56 -0
- package/dist/esm/templating/render.js +74 -30
- package/dist/esm/templating/repeat-basic-reverse.bench.js +43 -0
- package/dist/esm/templating/repeat-basic-shift.bench.js +43 -0
- package/dist/esm/templating/repeat.js +116 -17
- package/dist/esm/templating/template.js +135 -60
- package/dist/esm/templating/view.js +254 -34
- package/dist/esm/templating/when-basic.bench.js +36 -0
- package/dist/esm/templating/when-conditional.bench.js +39 -0
- package/dist/esm/templating/when-switch.bench.js +68 -0
- package/dist/esm/templating/when.js +12 -5
- package/dist/esm/testing/fakes.js +32 -1
- package/dist/esm/testing/fixture.js +1 -1
- package/dist/esm/utilities.js +97 -1
- package/dist/fast-element.api.json +9789 -5667
- package/dist/fast-element.d.ts +813 -2392
- package/dist/fast-element.debug.js +2788 -974
- package/dist/fast-element.debug.min.js +3 -1
- package/dist/fast-element.js +2641 -833
- package/dist/fast-element.min.js +3 -1
- package/dist/fast-element.untrimmed.d.ts +662 -314
- package/docs/{api-report.md → api-report.api.md} +238 -151
- package/docs/context/api-report.api.md +69 -0
- package/docs/di/api-report.api.md +315 -0
- package/karma.conf.cjs +2 -1
- package/package.json +59 -47
- package/scripts/run-api-extractor.js +51 -0
- package/scripts/run-benchmarks.js +46 -0
- package/tensile.config.js +12 -0
- package/dist/dts/templating/dom.d.ts +0 -41
- package/dist/esm/templating/binding.js +0 -282
- package/dist/esm/templating/dom.js +0 -49
- package/docs/guide/declaring-templates.md +0 -230
- package/docs/guide/defining-elements.md +0 -214
- package/docs/guide/leveraging-css.md +0 -253
- package/docs/guide/next-steps.md +0 -13
- package/docs/guide/observables-and-state.md +0 -213
- package/docs/guide/using-directives.md +0 -576
- package/docs/guide/working-with-shadow-dom.md +0 -296
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import { isHydratable } from "../components/hydration.js";
|
|
2
|
+
import { DOM, DOMAspect } from "../dom.js";
|
|
3
|
+
import "../interfaces.js";
|
|
4
|
+
import { ExecutionContext, } from "../observation/observable.js";
|
|
5
|
+
import { FAST } from "../platform.js";
|
|
6
|
+
import { HTMLDirective, } from "./html-directive.js";
|
|
7
|
+
import { Markup } from "./markup.js";
|
|
8
|
+
import { HydrationStage } from "./view.js";
|
|
9
|
+
function isContentTemplate(value) {
|
|
10
|
+
return value.create !== undefined;
|
|
11
|
+
}
|
|
12
|
+
function updateContent(target, aspect, value, controller) {
|
|
13
|
+
// If there's no actual value, then this equates to the
|
|
14
|
+
// empty string for the purposes of content bindings.
|
|
15
|
+
if (value === null || value === undefined) {
|
|
16
|
+
value = "";
|
|
17
|
+
}
|
|
18
|
+
// If the value has a "create" method, then it's a ContentTemplate.
|
|
19
|
+
if (isContentTemplate(value)) {
|
|
20
|
+
target.textContent = "";
|
|
21
|
+
let view = target.$fastView;
|
|
22
|
+
// If there's no previous view that we might be able to
|
|
23
|
+
// reuse then create a new view from the template.
|
|
24
|
+
if (view === void 0) {
|
|
25
|
+
if (isHydratable(controller) &&
|
|
26
|
+
isHydratable(value) &&
|
|
27
|
+
controller.bindingViewBoundaries[this.targetNodeId] !== undefined &&
|
|
28
|
+
controller.hydrationStage !== HydrationStage.hydrated) {
|
|
29
|
+
const viewNodes = controller.bindingViewBoundaries[this.targetNodeId];
|
|
30
|
+
view = value.hydrate(viewNodes.first, viewNodes.last);
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
view = value.create();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// If there is a previous view, but it wasn't created
|
|
38
|
+
// from the same template as the new value, then we
|
|
39
|
+
// need to remove the old view if it's still in the DOM
|
|
40
|
+
// and create a new view from the template.
|
|
41
|
+
if (target.$fastTemplate !== value) {
|
|
42
|
+
if (view.isComposed) {
|
|
43
|
+
view.remove();
|
|
44
|
+
view.unbind();
|
|
45
|
+
}
|
|
46
|
+
view = value.create();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// It's possible that the value is the same as the previous template
|
|
50
|
+
// and that there's actually no need to compose it.
|
|
51
|
+
if (!view.isComposed) {
|
|
52
|
+
view.isComposed = true;
|
|
53
|
+
view.bind(controller.source, controller.context);
|
|
54
|
+
view.insertBefore(target);
|
|
55
|
+
target.$fastView = view;
|
|
56
|
+
target.$fastTemplate = value;
|
|
57
|
+
}
|
|
58
|
+
else if (view.needsBindOnly) {
|
|
59
|
+
view.needsBindOnly = false;
|
|
60
|
+
view.bind(controller.source, controller.context);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const view = target.$fastView;
|
|
65
|
+
// If there is a view and it's currently composed into
|
|
66
|
+
// the DOM, then we need to remove it.
|
|
67
|
+
if (view !== void 0 && view.isComposed) {
|
|
68
|
+
view.isComposed = false;
|
|
69
|
+
view.remove();
|
|
70
|
+
if (view.needsBindOnly) {
|
|
71
|
+
view.needsBindOnly = false;
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
view.unbind();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
target.textContent = value;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function updateTokenList(target, aspect, value) {
|
|
81
|
+
var _a;
|
|
82
|
+
const lookup = `${this.id}-t`;
|
|
83
|
+
const state = (_a = target[lookup]) !== null && _a !== void 0 ? _a : (target[lookup] = { v: 0, cv: Object.create(null) });
|
|
84
|
+
const classVersions = state.cv;
|
|
85
|
+
let version = state.v;
|
|
86
|
+
const tokenList = target[aspect];
|
|
87
|
+
// Add the classes, tracking the version at which they were added.
|
|
88
|
+
if (value !== null && value !== undefined && value.length) {
|
|
89
|
+
const names = value.split(/\s+/);
|
|
90
|
+
for (let i = 0, ii = names.length; i < ii; ++i) {
|
|
91
|
+
const currentName = names[i];
|
|
92
|
+
if (currentName === "") {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
classVersions[currentName] = version;
|
|
96
|
+
tokenList.add(currentName);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
state.v = version + 1;
|
|
100
|
+
// If this is the first call to add classes, there's no need to remove old ones.
|
|
101
|
+
if (version === 0) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
// Remove classes from the previous version.
|
|
105
|
+
version -= 1;
|
|
106
|
+
for (const name in classVersions) {
|
|
107
|
+
if (classVersions[name] === version) {
|
|
108
|
+
tokenList.remove(name);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const sinkLookup = {
|
|
113
|
+
[DOMAspect.attribute]: DOM.setAttribute,
|
|
114
|
+
[DOMAspect.booleanAttribute]: DOM.setBooleanAttribute,
|
|
115
|
+
[DOMAspect.property]: (t, a, v) => (t[a] = v),
|
|
116
|
+
[DOMAspect.content]: updateContent,
|
|
117
|
+
[DOMAspect.tokenList]: updateTokenList,
|
|
118
|
+
[DOMAspect.event]: () => void 0,
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* A directive that applies bindings.
|
|
122
|
+
* @public
|
|
123
|
+
*/
|
|
124
|
+
export class HTMLBindingDirective {
|
|
125
|
+
/**
|
|
126
|
+
* Creates an instance of HTMLBindingDirective.
|
|
127
|
+
* @param dataBinding - The binding configuration to apply.
|
|
128
|
+
*/
|
|
129
|
+
constructor(dataBinding) {
|
|
130
|
+
this.dataBinding = dataBinding;
|
|
131
|
+
this.updateTarget = null;
|
|
132
|
+
/**
|
|
133
|
+
* The type of aspect to target.
|
|
134
|
+
*/
|
|
135
|
+
this.aspectType = DOMAspect.content;
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Creates HTML to be used within a template.
|
|
139
|
+
* @param add - Can be used to add behavior factories to a template.
|
|
140
|
+
*/
|
|
141
|
+
createHTML(add) {
|
|
142
|
+
return Markup.interpolation(add(this));
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Creates a behavior.
|
|
146
|
+
*/
|
|
147
|
+
createBehavior() {
|
|
148
|
+
var _a;
|
|
149
|
+
if (this.updateTarget === null) {
|
|
150
|
+
const sink = sinkLookup[this.aspectType];
|
|
151
|
+
const policy = (_a = this.dataBinding.policy) !== null && _a !== void 0 ? _a : this.policy;
|
|
152
|
+
if (!sink) {
|
|
153
|
+
throw FAST.error(1205 /* Message.unsupportedBindingBehavior */);
|
|
154
|
+
}
|
|
155
|
+
this.data = `${this.id}-d`;
|
|
156
|
+
this.updateTarget = policy.protect(this.targetTagName, this.aspectType, this.targetAspect, sink);
|
|
157
|
+
}
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
/** @internal */
|
|
161
|
+
bind(controller) {
|
|
162
|
+
var _a;
|
|
163
|
+
const target = controller.targets[this.targetNodeId];
|
|
164
|
+
const isHydrating = isHydratable(controller) &&
|
|
165
|
+
controller.hydrationStage &&
|
|
166
|
+
controller.hydrationStage !== HydrationStage.hydrated;
|
|
167
|
+
switch (this.aspectType) {
|
|
168
|
+
case DOMAspect.event:
|
|
169
|
+
target[this.data] = controller;
|
|
170
|
+
target.addEventListener(this.targetAspect, this, this.dataBinding.options);
|
|
171
|
+
break;
|
|
172
|
+
case DOMAspect.content:
|
|
173
|
+
controller.onUnbind(this);
|
|
174
|
+
// intentional fall through
|
|
175
|
+
default:
|
|
176
|
+
const observer = (_a = target[this.data]) !== null && _a !== void 0 ? _a : (target[this.data] = this.dataBinding.createObserver(this, this));
|
|
177
|
+
observer.target = target;
|
|
178
|
+
observer.controller = controller;
|
|
179
|
+
if (isHydrating &&
|
|
180
|
+
(this.aspectType === DOMAspect.attribute ||
|
|
181
|
+
this.aspectType === DOMAspect.booleanAttribute)) {
|
|
182
|
+
observer.bind(controller);
|
|
183
|
+
// Skip updating target during bind for attributes
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
/** @internal */
|
|
191
|
+
unbind(controller) {
|
|
192
|
+
const target = controller.targets[this.targetNodeId];
|
|
193
|
+
const view = target.$fastView;
|
|
194
|
+
if (view !== void 0 && view.isComposed) {
|
|
195
|
+
view.unbind();
|
|
196
|
+
view.needsBindOnly = true;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
/** @internal */
|
|
200
|
+
handleEvent(event) {
|
|
201
|
+
const controller = event.currentTarget[this.data];
|
|
202
|
+
if (controller.isBound) {
|
|
203
|
+
ExecutionContext.setEvent(event);
|
|
204
|
+
const result = this.dataBinding.evaluate(controller.source, controller.context);
|
|
205
|
+
ExecutionContext.setEvent(null);
|
|
206
|
+
if (result !== true) {
|
|
207
|
+
event.preventDefault();
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
/** @internal */
|
|
212
|
+
handleChange(binding, observer) {
|
|
213
|
+
const target = observer.target;
|
|
214
|
+
const controller = observer.controller;
|
|
215
|
+
this.updateTarget(target, this.targetAspect, observer.bind(controller), controller);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
HTMLDirective.define(HTMLBindingDirective, { aspected: true });
|
|
@@ -1,67 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createTypeRegistry } from "../platform.js";
|
|
3
|
-
import { Markup
|
|
4
|
-
/**
|
|
5
|
-
* Bridges between ViewBehaviors and HostBehaviors, enabling a host to
|
|
6
|
-
* control ViewBehaviors.
|
|
7
|
-
* @public
|
|
8
|
-
*/
|
|
9
|
-
export const ViewBehaviorOrchestrator = Object.freeze({
|
|
10
|
-
/**
|
|
11
|
-
* Creates a ViewBehaviorOrchestrator.
|
|
12
|
-
* @param source - The source to to associate behaviors with.
|
|
13
|
-
* @returns A ViewBehaviorOrchestrator.
|
|
14
|
-
*/
|
|
15
|
-
create(source) {
|
|
16
|
-
const behaviors = [];
|
|
17
|
-
const targets = {};
|
|
18
|
-
let unbindables = null;
|
|
19
|
-
let isConnected = false;
|
|
20
|
-
return {
|
|
21
|
-
source,
|
|
22
|
-
context: ExecutionContext.default,
|
|
23
|
-
targets,
|
|
24
|
-
get isBound() {
|
|
25
|
-
return isConnected;
|
|
26
|
-
},
|
|
27
|
-
addBehaviorFactory(factory, target) {
|
|
28
|
-
const nodeId = factory.nodeId || (factory.nodeId = nextId());
|
|
29
|
-
factory.id || (factory.id = nextId());
|
|
30
|
-
this.addTarget(nodeId, target);
|
|
31
|
-
this.addBehavior(factory.createBehavior());
|
|
32
|
-
},
|
|
33
|
-
addTarget(nodeId, target) {
|
|
34
|
-
targets[nodeId] = target;
|
|
35
|
-
},
|
|
36
|
-
addBehavior(behavior) {
|
|
37
|
-
behaviors.push(behavior);
|
|
38
|
-
if (isConnected) {
|
|
39
|
-
behavior.bind(this);
|
|
40
|
-
}
|
|
41
|
-
},
|
|
42
|
-
onUnbind(unbindable) {
|
|
43
|
-
if (unbindables === null) {
|
|
44
|
-
unbindables = [];
|
|
45
|
-
}
|
|
46
|
-
unbindables.push(unbindable);
|
|
47
|
-
},
|
|
48
|
-
connectedCallback(controller) {
|
|
49
|
-
if (!isConnected) {
|
|
50
|
-
isConnected = true;
|
|
51
|
-
behaviors.forEach(x => x.bind(this));
|
|
52
|
-
}
|
|
53
|
-
},
|
|
54
|
-
disconnectedCallback(controller) {
|
|
55
|
-
if (isConnected) {
|
|
56
|
-
isConnected = false;
|
|
57
|
-
if (unbindables !== null) {
|
|
58
|
-
unbindables.forEach(x => x.unbind(this));
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
},
|
|
64
|
-
});
|
|
1
|
+
import { DOMAspect } from "../dom.js";
|
|
2
|
+
import { createTypeRegistry, makeSerializationNoop } from "../platform.js";
|
|
3
|
+
import { Markup } from "./markup.js";
|
|
65
4
|
const registry = createTypeRegistry();
|
|
66
5
|
/**
|
|
67
6
|
* Instructs the template engine to apply behavior to a node.
|
|
@@ -89,67 +28,6 @@ export const HTMLDirective = Object.freeze({
|
|
|
89
28
|
registry.register(options);
|
|
90
29
|
return type;
|
|
91
30
|
},
|
|
92
|
-
});
|
|
93
|
-
/**
|
|
94
|
-
* Decorator: Defines an HTMLDirective.
|
|
95
|
-
* @param options - Provides options that specify the directive's application.
|
|
96
|
-
* @public
|
|
97
|
-
*/
|
|
98
|
-
export function htmlDirective(options) {
|
|
99
|
-
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
100
|
-
return function (type) {
|
|
101
|
-
HTMLDirective.define(type, options);
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Captures a binding expression along with related information and capabilities.
|
|
106
|
-
*
|
|
107
|
-
* @public
|
|
108
|
-
*/
|
|
109
|
-
export class Binding {
|
|
110
|
-
/**
|
|
111
|
-
* Creates a binding.
|
|
112
|
-
* @param evaluate - Evaluates the binding.
|
|
113
|
-
* @param isVolatile - Indicates whether the binding is volatile.
|
|
114
|
-
*/
|
|
115
|
-
constructor(evaluate, isVolatile = false) {
|
|
116
|
-
this.evaluate = evaluate;
|
|
117
|
-
this.isVolatile = isVolatile;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
/**
|
|
121
|
-
* The type of HTML aspect to target.
|
|
122
|
-
* @public
|
|
123
|
-
*/
|
|
124
|
-
export const Aspect = Object.freeze({
|
|
125
|
-
/**
|
|
126
|
-
* Not aspected.
|
|
127
|
-
*/
|
|
128
|
-
none: 0,
|
|
129
|
-
/**
|
|
130
|
-
* An attribute.
|
|
131
|
-
*/
|
|
132
|
-
attribute: 1,
|
|
133
|
-
/**
|
|
134
|
-
* A boolean attribute.
|
|
135
|
-
*/
|
|
136
|
-
booleanAttribute: 2,
|
|
137
|
-
/**
|
|
138
|
-
* A property.
|
|
139
|
-
*/
|
|
140
|
-
property: 3,
|
|
141
|
-
/**
|
|
142
|
-
* Content
|
|
143
|
-
*/
|
|
144
|
-
content: 4,
|
|
145
|
-
/**
|
|
146
|
-
* A token list.
|
|
147
|
-
*/
|
|
148
|
-
tokenList: 5,
|
|
149
|
-
/**
|
|
150
|
-
* An event.
|
|
151
|
-
*/
|
|
152
|
-
event: 6,
|
|
153
31
|
/**
|
|
154
32
|
*
|
|
155
33
|
* @param directive - The directive to assign the aspect to.
|
|
@@ -157,48 +35,46 @@ export const Aspect = Object.freeze({
|
|
|
157
35
|
* @remarks
|
|
158
36
|
* If a falsy value is provided, then the content aspect will be assigned.
|
|
159
37
|
*/
|
|
160
|
-
|
|
38
|
+
assignAspect(directive, value) {
|
|
161
39
|
if (!value) {
|
|
162
|
-
directive.aspectType =
|
|
40
|
+
directive.aspectType = DOMAspect.content;
|
|
163
41
|
return;
|
|
164
42
|
}
|
|
165
43
|
directive.sourceAspect = value;
|
|
166
44
|
switch (value[0]) {
|
|
167
45
|
case ":":
|
|
168
46
|
directive.targetAspect = value.substring(1);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
case "classList":
|
|
174
|
-
directive.aspectType = Aspect.tokenList;
|
|
175
|
-
break;
|
|
176
|
-
default:
|
|
177
|
-
directive.aspectType = Aspect.property;
|
|
178
|
-
break;
|
|
179
|
-
}
|
|
47
|
+
directive.aspectType =
|
|
48
|
+
directive.targetAspect === "classList"
|
|
49
|
+
? DOMAspect.tokenList
|
|
50
|
+
: DOMAspect.property;
|
|
180
51
|
break;
|
|
181
52
|
case "?":
|
|
182
53
|
directive.targetAspect = value.substring(1);
|
|
183
|
-
directive.aspectType =
|
|
54
|
+
directive.aspectType = DOMAspect.booleanAttribute;
|
|
184
55
|
break;
|
|
185
56
|
case "@":
|
|
186
57
|
directive.targetAspect = value.substring(1);
|
|
187
|
-
directive.aspectType =
|
|
58
|
+
directive.aspectType = DOMAspect.event;
|
|
188
59
|
break;
|
|
189
60
|
default:
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
directive.aspectType = Aspect.property;
|
|
193
|
-
}
|
|
194
|
-
else {
|
|
195
|
-
directive.targetAspect = value;
|
|
196
|
-
directive.aspectType = Aspect.attribute;
|
|
197
|
-
}
|
|
61
|
+
directive.targetAspect = value;
|
|
62
|
+
directive.aspectType = DOMAspect.attribute;
|
|
198
63
|
break;
|
|
199
64
|
}
|
|
200
65
|
},
|
|
201
66
|
});
|
|
67
|
+
/**
|
|
68
|
+
* Decorator: Defines an HTMLDirective.
|
|
69
|
+
* @param options - Provides options that specify the directive's application.
|
|
70
|
+
* @public
|
|
71
|
+
*/
|
|
72
|
+
export function htmlDirective(options) {
|
|
73
|
+
/* eslint-disable-next-line @typescript-eslint/explicit-function-return-type */
|
|
74
|
+
return function (type) {
|
|
75
|
+
HTMLDirective.define(type, options);
|
|
76
|
+
};
|
|
77
|
+
}
|
|
202
78
|
/**
|
|
203
79
|
* A base class used for attribute directives that don't need internal state.
|
|
204
80
|
* @public
|
|
@@ -210,10 +86,6 @@ export class StatelessAttachedAttributeDirective {
|
|
|
210
86
|
*/
|
|
211
87
|
constructor(options) {
|
|
212
88
|
this.options = options;
|
|
213
|
-
/**
|
|
214
|
-
* The unique id of the factory.
|
|
215
|
-
*/
|
|
216
|
-
this.id = nextId();
|
|
217
89
|
}
|
|
218
90
|
/**
|
|
219
91
|
* Creates a placeholder string based on the directive's index within the template.
|
|
@@ -232,3 +104,4 @@ export class StatelessAttachedAttributeDirective {
|
|
|
232
104
|
return this;
|
|
233
105
|
}
|
|
234
106
|
}
|
|
107
|
+
makeSerializationNoop(StatelessAttachedAttributeDirective);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Hydratable } from "../components/hydration.js";
|
|
2
|
+
import { ViewTemplate } from "./template.js";
|
|
3
|
+
import { HydrationView } from "./view.js";
|
|
4
|
+
// Configure ViewTemplate to be hydratable by attaching a symbol identifier
|
|
5
|
+
// and a hydrate method. Augmenting the hydration features is done by
|
|
6
|
+
// property assignment instead of class extension to better allow the
|
|
7
|
+
// hydration feature to be tree-shaken.
|
|
8
|
+
Object.defineProperties(ViewTemplate.prototype, {
|
|
9
|
+
[Hydratable]: { value: Hydratable, enumerable: false, configurable: false },
|
|
10
|
+
hydrate: {
|
|
11
|
+
value: function (firstChild, lastChild, hostBindingTarget) {
|
|
12
|
+
return new HydrationView(firstChild, lastChild, this, hostBindingTarget);
|
|
13
|
+
},
|
|
14
|
+
enumerable: true,
|
|
15
|
+
configurable: false,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
@@ -16,9 +16,15 @@ export const elements = (selector) => selector
|
|
|
16
16
|
* Internally used by the SlottedDirective and the ChildrenDirective.
|
|
17
17
|
*/
|
|
18
18
|
export class NodeObservationDirective extends StatelessAttachedAttributeDirective {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
/**
|
|
20
|
+
* The unique id of the factory.
|
|
21
|
+
*/
|
|
22
|
+
get id() {
|
|
23
|
+
return this._id;
|
|
24
|
+
}
|
|
25
|
+
set id(value) {
|
|
26
|
+
this._id = value;
|
|
27
|
+
this._controllerProperty = `${value}-c`;
|
|
22
28
|
}
|
|
23
29
|
/**
|
|
24
30
|
* Bind this behavior to the source.
|
|
@@ -27,8 +33,8 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
|
|
|
27
33
|
* @param targets - The targets that behaviors in a view can attach to.
|
|
28
34
|
*/
|
|
29
35
|
bind(controller) {
|
|
30
|
-
const target = controller.targets[this.
|
|
31
|
-
target[this.
|
|
36
|
+
const target = controller.targets[this.targetNodeId];
|
|
37
|
+
target[this._controllerProperty] = controller;
|
|
32
38
|
this.updateTarget(controller.source, this.computeNodes(target));
|
|
33
39
|
this.observe(target);
|
|
34
40
|
controller.onUnbind(this);
|
|
@@ -40,10 +46,10 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
|
|
|
40
46
|
* @param targets - The targets that behaviors in a view can attach to.
|
|
41
47
|
*/
|
|
42
48
|
unbind(controller) {
|
|
43
|
-
const target = controller.targets[this.
|
|
49
|
+
const target = controller.targets[this.targetNodeId];
|
|
44
50
|
this.updateTarget(controller.source, emptyArray);
|
|
45
51
|
this.disconnect(target);
|
|
46
|
-
target[this.
|
|
52
|
+
target[this._controllerProperty] = null;
|
|
47
53
|
}
|
|
48
54
|
/**
|
|
49
55
|
* Gets the data source for the target.
|
|
@@ -51,7 +57,7 @@ export class NodeObservationDirective extends StatelessAttachedAttributeDirectiv
|
|
|
51
57
|
* @returns The source.
|
|
52
58
|
*/
|
|
53
59
|
getSource(target) {
|
|
54
|
-
return target[this.
|
|
60
|
+
return target[this._controllerProperty].source;
|
|
55
61
|
}
|
|
56
62
|
/**
|
|
57
63
|
* Updates the source property with the computed nodes.
|
|
@@ -9,7 +9,7 @@ export class RefDirective extends StatelessAttachedAttributeDirective {
|
|
|
9
9
|
* @param controller - The view controller that manages the lifecycle of this behavior.
|
|
10
10
|
*/
|
|
11
11
|
bind(controller) {
|
|
12
|
-
controller.source[this.options] = controller.targets[this.
|
|
12
|
+
controller.source[this.options] = controller.targets[this.targetNodeId];
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
15
|
HTMLDirective.define(RefDirective);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { attr, css, FASTElement, html, oneTime, repeat } from "../index.js";
|
|
11
|
+
import { data } from "../__test__/utilities.js";
|
|
12
|
+
const xItemTemplate = html `
|
|
13
|
+
<div @click="${x => x.onClick}" class="item">${x => x.value}</div>
|
|
14
|
+
`;
|
|
15
|
+
const styles = css `
|
|
16
|
+
.item {
|
|
17
|
+
display: flex;
|
|
18
|
+
}
|
|
19
|
+
`;
|
|
20
|
+
class XItem extends FASTElement {
|
|
21
|
+
onClick(e) {
|
|
22
|
+
console.log(e.type);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
__decorate([
|
|
26
|
+
attr,
|
|
27
|
+
__metadata("design:type", Object)
|
|
28
|
+
], XItem.prototype, "value", void 0);
|
|
29
|
+
XItem.define({
|
|
30
|
+
name: "x-item",
|
|
31
|
+
template: xItemTemplate,
|
|
32
|
+
styles,
|
|
33
|
+
});
|
|
34
|
+
const xAppTemplate = html `
|
|
35
|
+
<div id="test-container">
|
|
36
|
+
${repeat(x => x.items, html `
|
|
37
|
+
<x-item :value="${oneTime((x) => x.label)}"></x-item>
|
|
38
|
+
`)}
|
|
39
|
+
</div>
|
|
40
|
+
`;
|
|
41
|
+
class XApp extends FASTElement {
|
|
42
|
+
constructor() {
|
|
43
|
+
super(...arguments);
|
|
44
|
+
this.items = data;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
XApp.define({
|
|
48
|
+
name: "x-app",
|
|
49
|
+
template: xAppTemplate,
|
|
50
|
+
});
|
|
51
|
+
const itemRenderer = () => {
|
|
52
|
+
const testRender = document.createElement("x-app");
|
|
53
|
+
return testRender;
|
|
54
|
+
};
|
|
55
|
+
export default itemRenderer;
|
|
56
|
+
export { tests } from "@tensile-perf/web-components";
|