@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.
- package/.eslintrc.json +1 -12
- package/CHANGELOG.json +629 -6
- package/CHANGELOG.md +152 -5
- package/dist/dts/components/attributes.d.ts +14 -1
- package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +32 -32
- package/dist/dts/components/fast-definitions.d.ts +51 -11
- package/dist/dts/components/fast-element.d.ts +18 -23
- package/dist/dts/context.d.ts +157 -0
- package/dist/{esm/observation/behavior.js → dts/debug.d.ts} +0 -0
- package/dist/dts/di/di.d.ts +899 -0
- package/dist/dts/index.d.ts +17 -16
- 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 +176 -0
- package/dist/dts/metadata.d.ts +25 -0
- package/dist/dts/observation/arrays.d.ts +207 -0
- package/dist/dts/observation/notifier.d.ts +18 -18
- package/dist/dts/observation/observable.d.ts +117 -34
- package/dist/dts/observation/update-queue.d.ts +40 -0
- package/dist/dts/pending-task.d.ts +20 -0
- package/dist/dts/platform.d.ts +23 -66
- package/dist/dts/polyfills.d.ts +8 -0
- package/dist/dts/state/exports.d.ts +3 -0
- package/dist/dts/state/reactive.d.ts +8 -0
- package/dist/dts/state/state.d.ts +141 -0
- package/dist/dts/state/visitor.d.ts +6 -0
- package/dist/dts/state/watch.d.ts +10 -0
- package/dist/dts/styles/css-directive.d.ts +44 -6
- package/dist/dts/styles/css.d.ts +19 -3
- package/dist/dts/styles/element-styles.d.ts +49 -63
- package/dist/dts/styles/host.d.ts +68 -0
- package/dist/dts/templating/binding-signal.d.ts +21 -0
- package/dist/dts/templating/binding-two-way.d.ts +39 -0
- package/dist/dts/templating/binding.d.ts +101 -70
- 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 +239 -45
- package/dist/dts/templating/markup.d.ts +48 -0
- package/dist/dts/templating/node-observation.d.ts +45 -30
- package/dist/dts/templating/ref.d.ts +6 -20
- package/dist/dts/templating/render.d.ts +272 -0
- package/dist/dts/templating/repeat.d.ts +36 -33
- package/dist/dts/templating/slotted.d.ts +13 -14
- package/dist/dts/templating/template.d.ts +28 -22
- package/dist/dts/templating/view.d.ts +82 -24
- package/dist/dts/templating/when.d.ts +3 -3
- package/dist/dts/testing/exports.d.ts +3 -0
- package/dist/dts/testing/fakes.d.ts +4 -0
- package/dist/dts/testing/fixture.d.ts +84 -0
- package/dist/dts/testing/timeout.d.ts +7 -0
- package/dist/{tsdoc-metadata.json → dts/tsdoc-metadata.json} +1 -1
- package/dist/dts/utilities.d.ts +22 -0
- package/dist/esm/components/attributes.js +38 -28
- package/dist/esm/components/{controller.js → element-controller.js} +150 -140
- package/dist/esm/components/fast-definitions.js +48 -46
- package/dist/esm/components/fast-element.js +31 -12
- package/dist/esm/context.js +163 -0
- package/dist/esm/debug.js +61 -0
- package/dist/esm/di/di.js +1435 -0
- package/dist/esm/index.debug.js +2 -0
- package/dist/esm/index.js +20 -14
- package/dist/esm/index.rollup.debug.js +3 -0
- package/dist/esm/index.rollup.js +2 -0
- package/dist/esm/interfaces.js +12 -1
- package/dist/esm/metadata.js +60 -0
- package/dist/esm/observation/arrays.js +570 -0
- package/dist/esm/observation/notifier.js +27 -35
- package/dist/esm/observation/observable.js +116 -149
- package/dist/esm/observation/update-queue.js +67 -0
- package/dist/esm/pending-task.js +16 -0
- package/dist/esm/platform.js +60 -42
- package/dist/esm/polyfills.js +85 -0
- package/dist/esm/state/exports.js +3 -0
- package/dist/esm/state/reactive.js +34 -0
- package/dist/esm/state/state.js +148 -0
- package/dist/esm/state/visitor.js +28 -0
- package/dist/esm/state/watch.js +36 -0
- package/dist/esm/styles/css-directive.js +29 -13
- package/dist/esm/styles/css.js +29 -42
- package/dist/esm/styles/element-styles.js +79 -104
- package/dist/esm/styles/host.js +1 -0
- package/dist/esm/templating/binding-signal.js +83 -0
- package/dist/esm/templating/binding-two-way.js +103 -0
- package/dist/esm/templating/binding.js +189 -159
- package/dist/esm/templating/children.js +33 -23
- package/dist/esm/templating/compiler.js +258 -152
- package/dist/esm/templating/dom.js +49 -0
- package/dist/esm/templating/html-directive.js +193 -36
- package/dist/esm/templating/markup.js +75 -0
- package/dist/esm/templating/node-observation.js +51 -45
- package/dist/esm/templating/ref.js +8 -25
- package/dist/esm/templating/render.js +391 -0
- package/dist/esm/templating/repeat.js +83 -79
- package/dist/esm/templating/slotted.js +23 -20
- package/dist/esm/templating/template.js +51 -93
- package/dist/esm/templating/view.js +125 -46
- package/dist/esm/templating/when.js +6 -4
- package/dist/esm/testing/exports.js +3 -0
- package/dist/esm/testing/fakes.js +76 -0
- package/dist/esm/testing/fixture.js +86 -0
- package/dist/esm/testing/timeout.js +24 -0
- package/dist/esm/utilities.js +44 -0
- package/dist/fast-element.api.json +12153 -5373
- package/dist/fast-element.d.ts +1448 -696
- package/dist/fast-element.debug.js +4107 -0
- package/dist/fast-element.debug.min.js +1 -0
- package/dist/fast-element.js +3817 -4029
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +2814 -0
- package/docs/api-report.md +567 -254
- package/docs/fast-element-2-changes.md +15 -0
- package/karma.conf.cjs +6 -17
- package/package.json +76 -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/dts/observation/behavior.d.ts +0 -19
- package/dist/esm/dom.js +0 -207
- package/dist/esm/observation/array-change-records.js +0 -326
- package/dist/esm/observation/array-observer.js +0 -177
|
@@ -1,37 +1,40 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isString } from "../interfaces.js";
|
|
2
|
+
import { HTMLDirective } from "./html-directive.js";
|
|
3
|
+
import { NodeObservationDirective } from "./node-observation.js";
|
|
4
|
+
const slotEvent = "slotchange";
|
|
3
5
|
/**
|
|
4
6
|
* The runtime behavior for slotted node observation.
|
|
5
7
|
* @public
|
|
6
8
|
*/
|
|
7
|
-
export class
|
|
8
|
-
/**
|
|
9
|
-
* Creates an instance of SlottedBehavior.
|
|
10
|
-
* @param target - The slot element target to observe.
|
|
11
|
-
* @param options - The options to use when observing the slot.
|
|
12
|
-
*/
|
|
13
|
-
constructor(target, options) {
|
|
14
|
-
super(target, options);
|
|
15
|
-
}
|
|
9
|
+
export class SlottedDirective extends NodeObservationDirective {
|
|
16
10
|
/**
|
|
17
11
|
* Begins observation of the nodes.
|
|
12
|
+
* @param target - The target to observe.
|
|
18
13
|
*/
|
|
19
|
-
observe() {
|
|
20
|
-
|
|
14
|
+
observe(target) {
|
|
15
|
+
target.addEventListener(slotEvent, this);
|
|
21
16
|
}
|
|
22
17
|
/**
|
|
23
18
|
* Disconnects observation of the nodes.
|
|
19
|
+
* @param target - The target to unobserve.
|
|
24
20
|
*/
|
|
25
|
-
disconnect() {
|
|
26
|
-
|
|
21
|
+
disconnect(target) {
|
|
22
|
+
target.removeEventListener(slotEvent, this);
|
|
27
23
|
}
|
|
28
24
|
/**
|
|
29
|
-
* Retrieves the nodes that should be assigned to the
|
|
25
|
+
* Retrieves the raw nodes that should be assigned to the source property.
|
|
26
|
+
* @param target - The target to get the node to.
|
|
30
27
|
*/
|
|
31
|
-
getNodes() {
|
|
32
|
-
return
|
|
28
|
+
getNodes(target) {
|
|
29
|
+
return target.assignedNodes(this.options);
|
|
30
|
+
}
|
|
31
|
+
/** @internal */
|
|
32
|
+
handleEvent(event) {
|
|
33
|
+
const target = event.currentTarget;
|
|
34
|
+
this.updateTarget(this.getSource(target), this.computeNodes(target));
|
|
33
35
|
}
|
|
34
36
|
}
|
|
37
|
+
HTMLDirective.define(SlottedDirective);
|
|
35
38
|
/**
|
|
36
39
|
* A directive that observes the `assignedNodes()` of a slot and updates a property
|
|
37
40
|
* whenever they change.
|
|
@@ -39,8 +42,8 @@ export class SlottedBehavior extends NodeObservationBehavior {
|
|
|
39
42
|
* @public
|
|
40
43
|
*/
|
|
41
44
|
export function slotted(propertyOrOptions) {
|
|
42
|
-
if (
|
|
45
|
+
if (isString(propertyOrOptions)) {
|
|
43
46
|
propertyOrOptions = { property: propertyOrOptions };
|
|
44
47
|
}
|
|
45
|
-
return new
|
|
48
|
+
return new SlottedDirective(propertyOrOptions);
|
|
46
49
|
}
|
|
@@ -1,86 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { HTMLBindingDirective } from "./binding.js";
|
|
1
|
+
import { isFunction, isString } from "../interfaces.js";
|
|
2
|
+
import { bind, HTMLBindingDirective, oneTime } from "./binding.js";
|
|
3
|
+
import { Compiler } from "./compiler.js";
|
|
4
|
+
import { Aspect, Binding, HTMLDirective, } from "./html-directive.js";
|
|
5
|
+
import { nextId } from "./markup.js";
|
|
7
6
|
/**
|
|
8
7
|
* A template capable of creating HTMLView instances or rendering directly to DOM.
|
|
9
8
|
* @public
|
|
10
9
|
*/
|
|
11
|
-
/* eslint-disable-next-line @typescript-eslint/no-unused-vars */
|
|
12
10
|
export class ViewTemplate {
|
|
13
11
|
/**
|
|
14
12
|
* Creates an instance of ViewTemplate.
|
|
15
13
|
* @param html - The html representing what this template will instantiate, including placeholders for directives.
|
|
16
|
-
* @param
|
|
14
|
+
* @param factories - The directives that will be connected to placeholders in the html.
|
|
17
15
|
*/
|
|
18
|
-
constructor(html,
|
|
19
|
-
this.
|
|
20
|
-
this.hasHostBehaviors = false;
|
|
21
|
-
this.fragment = null;
|
|
22
|
-
this.targetOffset = 0;
|
|
23
|
-
this.viewBehaviorFactories = null;
|
|
24
|
-
this.hostBehaviorFactories = null;
|
|
16
|
+
constructor(html, factories) {
|
|
17
|
+
this.result = null;
|
|
25
18
|
this.html = html;
|
|
26
|
-
this.
|
|
19
|
+
this.factories = factories;
|
|
27
20
|
}
|
|
28
21
|
/**
|
|
29
22
|
* Creates an HTMLView instance based on this template definition.
|
|
30
23
|
* @param hostBindingTarget - The element that host behaviors will be bound to.
|
|
31
24
|
*/
|
|
32
25
|
create(hostBindingTarget) {
|
|
33
|
-
if (this.
|
|
34
|
-
|
|
35
|
-
const html = this.html;
|
|
36
|
-
if (typeof html === "string") {
|
|
37
|
-
template = document.createElement("template");
|
|
38
|
-
template.innerHTML = DOM.createHTML(html);
|
|
39
|
-
const fec = template.content.firstElementChild;
|
|
40
|
-
if (fec !== null && fec.tagName === "TEMPLATE") {
|
|
41
|
-
template = fec;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
template = html;
|
|
46
|
-
}
|
|
47
|
-
const result = compileTemplate(template, this.directives);
|
|
48
|
-
this.fragment = result.fragment;
|
|
49
|
-
this.viewBehaviorFactories = result.viewBehaviorFactories;
|
|
50
|
-
this.hostBehaviorFactories = result.hostBehaviorFactories;
|
|
51
|
-
this.targetOffset = result.targetOffset;
|
|
52
|
-
this.behaviorCount =
|
|
53
|
-
this.viewBehaviorFactories.length + this.hostBehaviorFactories.length;
|
|
54
|
-
this.hasHostBehaviors = this.hostBehaviorFactories.length > 0;
|
|
55
|
-
}
|
|
56
|
-
const fragment = this.fragment.cloneNode(true);
|
|
57
|
-
const viewFactories = this.viewBehaviorFactories;
|
|
58
|
-
const behaviors = new Array(this.behaviorCount);
|
|
59
|
-
const walker = DOM.createTemplateWalker(fragment);
|
|
60
|
-
let behaviorIndex = 0;
|
|
61
|
-
let targetIndex = this.targetOffset;
|
|
62
|
-
let node = walker.nextNode();
|
|
63
|
-
for (let ii = viewFactories.length; behaviorIndex < ii; ++behaviorIndex) {
|
|
64
|
-
const factory = viewFactories[behaviorIndex];
|
|
65
|
-
const factoryIndex = factory.targetIndex;
|
|
66
|
-
while (node !== null) {
|
|
67
|
-
if (targetIndex === factoryIndex) {
|
|
68
|
-
behaviors[behaviorIndex] = factory.createBehavior(node);
|
|
69
|
-
break;
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
node = walker.nextNode();
|
|
73
|
-
targetIndex++;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
26
|
+
if (this.result === null) {
|
|
27
|
+
this.result = Compiler.compile(this.html, this.factories);
|
|
76
28
|
}
|
|
77
|
-
|
|
78
|
-
const hostFactories = this.hostBehaviorFactories;
|
|
79
|
-
for (let i = 0, ii = hostFactories.length; i < ii; ++i, ++behaviorIndex) {
|
|
80
|
-
behaviors[behaviorIndex] = hostFactories[i].createBehavior(hostBindingTarget);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return new HTMLView(fragment, behaviors);
|
|
29
|
+
return this.result.createView(hostBindingTarget);
|
|
84
30
|
}
|
|
85
31
|
/**
|
|
86
32
|
* Creates an HTMLView from this template, binds it to the source, and then appends it to the host.
|
|
@@ -90,14 +36,8 @@ export class ViewTemplate {
|
|
|
90
36
|
* host that the template is being attached to.
|
|
91
37
|
*/
|
|
92
38
|
render(source, host, hostBindingTarget) {
|
|
93
|
-
if (typeof host === "string") {
|
|
94
|
-
host = document.getElementById(host);
|
|
95
|
-
}
|
|
96
|
-
if (hostBindingTarget === void 0) {
|
|
97
|
-
hostBindingTarget = host;
|
|
98
|
-
}
|
|
99
39
|
const view = this.create(hostBindingTarget);
|
|
100
|
-
view.bind(source
|
|
40
|
+
view.bind(source);
|
|
101
41
|
view.appendTo(host);
|
|
102
42
|
return view;
|
|
103
43
|
}
|
|
@@ -106,8 +46,15 @@ export class ViewTemplate {
|
|
|
106
46
|
const lastAttributeNameRegex =
|
|
107
47
|
/* eslint-disable-next-line no-control-regex */
|
|
108
48
|
/([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F "'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/;
|
|
49
|
+
function createAspectedHTML(value, prevString, add) {
|
|
50
|
+
const match = lastAttributeNameRegex.exec(prevString);
|
|
51
|
+
if (match !== null) {
|
|
52
|
+
Aspect.assign(value, match[2]);
|
|
53
|
+
}
|
|
54
|
+
return value.createHTML(add);
|
|
55
|
+
}
|
|
109
56
|
/**
|
|
110
|
-
* Transforms a template literal string into a
|
|
57
|
+
* Transforms a template literal string into a ViewTemplate.
|
|
111
58
|
* @param strings - The string fragments that are interpolated with the values.
|
|
112
59
|
* @param values - The values that are interpolated with the string fragments.
|
|
113
60
|
* @remarks
|
|
@@ -116,36 +63,47 @@ const lastAttributeNameRegex =
|
|
|
116
63
|
* @public
|
|
117
64
|
*/
|
|
118
65
|
export function html(strings, ...values) {
|
|
119
|
-
const directives = [];
|
|
120
66
|
let html = "";
|
|
67
|
+
const factories = Object.create(null);
|
|
68
|
+
const add = (factory) => {
|
|
69
|
+
var _a;
|
|
70
|
+
const id = (_a = factory.id) !== null && _a !== void 0 ? _a : (factory.id = nextId());
|
|
71
|
+
factories[id] = factory;
|
|
72
|
+
return id;
|
|
73
|
+
};
|
|
121
74
|
for (let i = 0, ii = strings.length - 1; i < ii; ++i) {
|
|
122
75
|
const currentString = strings[i];
|
|
123
|
-
|
|
76
|
+
const currentValue = values[i];
|
|
77
|
+
let definition;
|
|
124
78
|
html += currentString;
|
|
125
|
-
if (
|
|
126
|
-
|
|
127
|
-
value = () => template;
|
|
128
|
-
}
|
|
129
|
-
if (typeof value === "function") {
|
|
130
|
-
value = new HTMLBindingDirective(value);
|
|
79
|
+
if (isFunction(currentValue)) {
|
|
80
|
+
html += createAspectedHTML(new HTMLBindingDirective(bind(currentValue)), currentString, add);
|
|
131
81
|
}
|
|
132
|
-
if (
|
|
82
|
+
else if (isString(currentValue)) {
|
|
133
83
|
const match = lastAttributeNameRegex.exec(currentString);
|
|
134
84
|
if (match !== null) {
|
|
135
|
-
|
|
85
|
+
const directive = new HTMLBindingDirective(oneTime(() => currentValue));
|
|
86
|
+
Aspect.assign(directive, match[2]);
|
|
87
|
+
html += directive.createHTML(add);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
html += currentValue;
|
|
136
91
|
}
|
|
137
92
|
}
|
|
138
|
-
if (
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
html +=
|
|
143
|
-
directives.push(value);
|
|
93
|
+
else if (currentValue instanceof Binding) {
|
|
94
|
+
html += createAspectedHTML(new HTMLBindingDirective(currentValue), currentString, add);
|
|
95
|
+
}
|
|
96
|
+
else if ((definition = HTMLDirective.getForInstance(currentValue)) === void 0) {
|
|
97
|
+
html += createAspectedHTML(new HTMLBindingDirective(oneTime(() => currentValue)), currentString, add);
|
|
144
98
|
}
|
|
145
99
|
else {
|
|
146
|
-
|
|
100
|
+
if (definition.aspected) {
|
|
101
|
+
html += createAspectedHTML(currentValue, currentString, add);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
html += currentValue.createHTML(add);
|
|
105
|
+
}
|
|
147
106
|
}
|
|
148
107
|
}
|
|
149
|
-
html
|
|
150
|
-
return new ViewTemplate(html, directives);
|
|
108
|
+
return new ViewTemplate(html + strings[strings.length - 1], factories);
|
|
151
109
|
}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
const
|
|
1
|
+
import { ExecutionContext, Observable, SourceLifetime, } from "../observation/observable.js";
|
|
2
|
+
function removeNodeSequence(firstNode, lastNode) {
|
|
3
|
+
const parent = firstNode.parentNode;
|
|
4
|
+
let current = firstNode;
|
|
5
|
+
let next;
|
|
6
|
+
while (current !== lastNode) {
|
|
7
|
+
next = current.nextSibling;
|
|
8
|
+
parent.removeChild(current);
|
|
9
|
+
current = next;
|
|
10
|
+
}
|
|
11
|
+
parent.removeChild(lastNode);
|
|
12
|
+
}
|
|
4
13
|
/**
|
|
5
14
|
* The standard View implementation, which also implements ElementView and SyntheticView.
|
|
6
15
|
* @public
|
|
@@ -11,20 +20,92 @@ export class HTMLView {
|
|
|
11
20
|
* @param fragment - The html fragment that contains the nodes for this view.
|
|
12
21
|
* @param behaviors - The behaviors to be applied to this view.
|
|
13
22
|
*/
|
|
14
|
-
constructor(fragment,
|
|
23
|
+
constructor(fragment, factories, targets) {
|
|
15
24
|
this.fragment = fragment;
|
|
16
|
-
this.
|
|
25
|
+
this.factories = factories;
|
|
26
|
+
this.targets = targets;
|
|
27
|
+
this.behaviors = null;
|
|
28
|
+
this.unbindables = [];
|
|
17
29
|
/**
|
|
18
30
|
* The data that the view is bound to.
|
|
19
31
|
*/
|
|
20
32
|
this.source = null;
|
|
33
|
+
/**
|
|
34
|
+
* Indicates whether the controller is bound.
|
|
35
|
+
*/
|
|
36
|
+
this.isBound = false;
|
|
37
|
+
/**
|
|
38
|
+
* Indicates how the source's lifetime relates to the controller's lifetime.
|
|
39
|
+
*/
|
|
40
|
+
this.sourceLifetime = SourceLifetime.unknown;
|
|
21
41
|
/**
|
|
22
42
|
* The execution context the view is running within.
|
|
23
43
|
*/
|
|
24
|
-
this.context =
|
|
44
|
+
this.context = this;
|
|
45
|
+
/**
|
|
46
|
+
* The index of the current item within a repeat context.
|
|
47
|
+
*/
|
|
48
|
+
this.index = 0;
|
|
49
|
+
/**
|
|
50
|
+
* The length of the current collection within a repeat context.
|
|
51
|
+
*/
|
|
52
|
+
this.length = 0;
|
|
25
53
|
this.firstChild = fragment.firstChild;
|
|
26
54
|
this.lastChild = fragment.lastChild;
|
|
27
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* The current event within an event handler.
|
|
58
|
+
*/
|
|
59
|
+
get event() {
|
|
60
|
+
return ExecutionContext.getEvent();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Indicates whether the current item within a repeat context
|
|
64
|
+
* has an even index.
|
|
65
|
+
*/
|
|
66
|
+
get isEven() {
|
|
67
|
+
return this.index % 2 === 0;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Indicates whether the current item within a repeat context
|
|
71
|
+
* has an odd index.
|
|
72
|
+
*/
|
|
73
|
+
get isOdd() {
|
|
74
|
+
return this.index % 2 !== 0;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Indicates whether the current item within a repeat context
|
|
78
|
+
* is the first item in the collection.
|
|
79
|
+
*/
|
|
80
|
+
get isFirst() {
|
|
81
|
+
return this.index === 0;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Indicates whether the current item within a repeat context
|
|
85
|
+
* is somewhere in the middle of the collection.
|
|
86
|
+
*/
|
|
87
|
+
get isInMiddle() {
|
|
88
|
+
return !this.isFirst && !this.isLast;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Indicates whether the current item within a repeat context
|
|
92
|
+
* is the last item in the collection.
|
|
93
|
+
*/
|
|
94
|
+
get isLast() {
|
|
95
|
+
return this.index === this.length - 1;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Returns the typed event detail of a custom event.
|
|
99
|
+
*/
|
|
100
|
+
eventDetail() {
|
|
101
|
+
return this.event.detail;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Returns the typed event target of the event.
|
|
105
|
+
*/
|
|
106
|
+
eventTarget() {
|
|
107
|
+
return this.event.target;
|
|
108
|
+
}
|
|
28
109
|
/**
|
|
29
110
|
* Appends the view's DOM nodes to the referenced node.
|
|
30
111
|
* @param node - The parent node to append the view's DOM nodes to.
|
|
@@ -41,8 +122,10 @@ export class HTMLView {
|
|
|
41
122
|
node.parentNode.insertBefore(this.fragment, node);
|
|
42
123
|
}
|
|
43
124
|
else {
|
|
44
|
-
const parentNode = node.parentNode;
|
|
45
125
|
const end = this.lastChild;
|
|
126
|
+
if (node.previousSibling === end)
|
|
127
|
+
return;
|
|
128
|
+
const parentNode = node.parentNode;
|
|
46
129
|
let current = this.firstChild;
|
|
47
130
|
let next;
|
|
48
131
|
while (current !== end) {
|
|
@@ -74,63 +157,64 @@ export class HTMLView {
|
|
|
74
157
|
* Once a view has been disposed, it cannot be inserted or bound again.
|
|
75
158
|
*/
|
|
76
159
|
dispose() {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
next = current.nextSibling;
|
|
83
|
-
parent.removeChild(current);
|
|
84
|
-
current = next;
|
|
85
|
-
}
|
|
86
|
-
parent.removeChild(end);
|
|
87
|
-
const behaviors = this.behaviors;
|
|
88
|
-
const oldSource = this.source;
|
|
89
|
-
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
90
|
-
behaviors[i].unbind(oldSource);
|
|
91
|
-
}
|
|
160
|
+
removeNodeSequence(this.firstChild, this.lastChild);
|
|
161
|
+
this.unbind();
|
|
162
|
+
}
|
|
163
|
+
onUnbind(behavior) {
|
|
164
|
+
this.unbindables.push(behavior);
|
|
92
165
|
}
|
|
93
166
|
/**
|
|
94
167
|
* Binds a view's behaviors to its binding source.
|
|
95
168
|
* @param source - The binding source for the view's binding behaviors.
|
|
96
169
|
* @param context - The execution context to run the behaviors within.
|
|
97
170
|
*/
|
|
98
|
-
bind(source, context) {
|
|
99
|
-
const behaviors = this.behaviors;
|
|
171
|
+
bind(source, context = this) {
|
|
100
172
|
if (this.source === source) {
|
|
101
173
|
return;
|
|
102
174
|
}
|
|
103
|
-
|
|
104
|
-
|
|
175
|
+
let behaviors = this.behaviors;
|
|
176
|
+
if (behaviors === null) {
|
|
105
177
|
this.source = source;
|
|
106
178
|
this.context = context;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
179
|
+
this.behaviors = behaviors = new Array(this.factories.length);
|
|
180
|
+
const factories = this.factories;
|
|
181
|
+
for (let i = 0, ii = factories.length; i < ii; ++i) {
|
|
182
|
+
const behavior = factories[i].createBehavior();
|
|
183
|
+
behavior.bind(this);
|
|
184
|
+
behaviors[i] = behavior;
|
|
111
185
|
}
|
|
112
186
|
}
|
|
113
187
|
else {
|
|
188
|
+
if (this.source !== null) {
|
|
189
|
+
this.evaluateUnbindables();
|
|
190
|
+
}
|
|
191
|
+
this.isBound = false;
|
|
114
192
|
this.source = source;
|
|
115
193
|
this.context = context;
|
|
116
194
|
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
117
|
-
behaviors[i].bind(
|
|
195
|
+
behaviors[i].bind(this);
|
|
118
196
|
}
|
|
119
197
|
}
|
|
198
|
+
this.isBound = true;
|
|
120
199
|
}
|
|
121
200
|
/**
|
|
122
201
|
* Unbinds a view's behaviors from its binding source.
|
|
123
202
|
*/
|
|
124
203
|
unbind() {
|
|
125
|
-
if (this.source === null) {
|
|
204
|
+
if (!this.isBound || this.source === null) {
|
|
126
205
|
return;
|
|
127
206
|
}
|
|
128
|
-
|
|
129
|
-
const oldSource = this.source;
|
|
130
|
-
for (let i = 0, ii = behaviors.length; i < ii; ++i) {
|
|
131
|
-
behaviors[i].unbind(oldSource);
|
|
132
|
-
}
|
|
207
|
+
this.evaluateUnbindables();
|
|
133
208
|
this.source = null;
|
|
209
|
+
this.context = this;
|
|
210
|
+
this.isBound = false;
|
|
211
|
+
}
|
|
212
|
+
evaluateUnbindables() {
|
|
213
|
+
const unbindables = this.unbindables;
|
|
214
|
+
for (let i = 0, ii = unbindables.length; i < ii; ++i) {
|
|
215
|
+
unbindables[i].unbind(this);
|
|
216
|
+
}
|
|
217
|
+
unbindables.length = 0;
|
|
134
218
|
}
|
|
135
219
|
/**
|
|
136
220
|
* Efficiently disposes of a contiguous range of synthetic view instances.
|
|
@@ -140,16 +224,11 @@ export class HTMLView {
|
|
|
140
224
|
if (views.length === 0) {
|
|
141
225
|
return;
|
|
142
226
|
}
|
|
143
|
-
|
|
144
|
-
range.setEndAfter(views[views.length - 1].lastChild);
|
|
145
|
-
range.deleteContents();
|
|
227
|
+
removeNodeSequence(views[0].firstChild, views[views.length - 1].lastChild);
|
|
146
228
|
for (let i = 0, ii = views.length; i < ii; ++i) {
|
|
147
|
-
|
|
148
|
-
const behaviors = view.behaviors;
|
|
149
|
-
const oldSource = view.source;
|
|
150
|
-
for (let j = 0, jj = behaviors.length; j < jj; ++j) {
|
|
151
|
-
behaviors[j].unbind(oldSource);
|
|
152
|
-
}
|
|
229
|
+
views[i].unbind();
|
|
153
230
|
}
|
|
154
231
|
}
|
|
155
232
|
}
|
|
233
|
+
Observable.defineProperty(HTMLView.prototype, "index");
|
|
234
|
+
Observable.defineProperty(HTMLView.prototype, "length");
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
+
import { isFunction } from "../interfaces.js";
|
|
1
2
|
/**
|
|
2
3
|
* A directive that enables basic conditional rendering in a template.
|
|
3
|
-
* @param
|
|
4
|
+
* @param condition - The condition to test for rendering.
|
|
4
5
|
* @param templateOrTemplateBinding - The template or a binding that gets
|
|
5
6
|
* the template to render when the condition is true.
|
|
6
7
|
* @public
|
|
7
8
|
*/
|
|
8
|
-
export function when(
|
|
9
|
-
const
|
|
9
|
+
export function when(condition, templateOrTemplateBinding) {
|
|
10
|
+
const dataBinding = isFunction(condition) ? condition : () => condition;
|
|
11
|
+
const templateBinding = isFunction(templateOrTemplateBinding)
|
|
10
12
|
? templateOrTemplateBinding
|
|
11
13
|
: () => templateOrTemplateBinding;
|
|
12
|
-
return (source, context) =>
|
|
14
|
+
return (source, context) => dataBinding(source, context) ? templateBinding(source, context) : null;
|
|
13
15
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ExecutionContext } from "../index.js";
|
|
2
|
+
export const Fake = Object.freeze({
|
|
3
|
+
executionContext(parent, parentContext) {
|
|
4
|
+
return {
|
|
5
|
+
/**
|
|
6
|
+
* The index of the current item within a repeat context.
|
|
7
|
+
*/
|
|
8
|
+
index: 0,
|
|
9
|
+
/**
|
|
10
|
+
* The length of the current collection within a repeat context.
|
|
11
|
+
*/
|
|
12
|
+
length: 0,
|
|
13
|
+
/**
|
|
14
|
+
* The parent data source within a nested context.
|
|
15
|
+
*/
|
|
16
|
+
parent: parent,
|
|
17
|
+
/**
|
|
18
|
+
* The parent execution context when in nested context scenarios.
|
|
19
|
+
*/
|
|
20
|
+
parentContext: parentContext,
|
|
21
|
+
/**
|
|
22
|
+
* The current event within an event handler.
|
|
23
|
+
*/
|
|
24
|
+
get event() {
|
|
25
|
+
return ExecutionContext.getEvent();
|
|
26
|
+
},
|
|
27
|
+
/**
|
|
28
|
+
* Indicates whether the current item within a repeat context
|
|
29
|
+
* has an even index.
|
|
30
|
+
*/
|
|
31
|
+
get isEven() {
|
|
32
|
+
return this.index % 2 === 0;
|
|
33
|
+
},
|
|
34
|
+
/**
|
|
35
|
+
* Indicates whether the current item within a repeat context
|
|
36
|
+
* has an odd index.
|
|
37
|
+
*/
|
|
38
|
+
get isOdd() {
|
|
39
|
+
return this.index % 2 !== 0;
|
|
40
|
+
},
|
|
41
|
+
/**
|
|
42
|
+
* Indicates whether the current item within a repeat context
|
|
43
|
+
* is the first item in the collection.
|
|
44
|
+
*/
|
|
45
|
+
get isFirst() {
|
|
46
|
+
return this.index === 0;
|
|
47
|
+
},
|
|
48
|
+
/**
|
|
49
|
+
* Indicates whether the current item within a repeat context
|
|
50
|
+
* is somewhere in the middle of the collection.
|
|
51
|
+
*/
|
|
52
|
+
get isInMiddle() {
|
|
53
|
+
return !this.isFirst && !this.isLast;
|
|
54
|
+
},
|
|
55
|
+
/**
|
|
56
|
+
* Indicates whether the current item within a repeat context
|
|
57
|
+
* is the last item in the collection.
|
|
58
|
+
*/
|
|
59
|
+
get isLast() {
|
|
60
|
+
return this.index === this.length - 1;
|
|
61
|
+
},
|
|
62
|
+
/**
|
|
63
|
+
* Returns the typed event detail of a custom event.
|
|
64
|
+
*/
|
|
65
|
+
eventDetail() {
|
|
66
|
+
return this.event.detail;
|
|
67
|
+
},
|
|
68
|
+
/**
|
|
69
|
+
* Returns the typed event target of the event.
|
|
70
|
+
*/
|
|
71
|
+
eventTarget() {
|
|
72
|
+
return this.event.target;
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
},
|
|
76
|
+
});
|