@microsoft/fast-element 2.0.0-beta.1 → 2.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.json +348 -0
- package/CHANGELOG.md +114 -1
- package/dist/dts/components/attributes.d.ts +10 -0
- package/dist/dts/components/{controller.d.ts → element-controller.d.ts} +49 -25
- package/dist/dts/components/fast-definitions.d.ts +43 -9
- package/dist/dts/components/fast-element.d.ts +15 -21
- package/dist/dts/context.d.ts +157 -0
- package/dist/dts/di/di.d.ts +899 -0
- package/dist/dts/index.d.ts +2 -2
- package/dist/dts/interfaces.d.ts +45 -14
- package/dist/dts/metadata.d.ts +25 -0
- package/dist/dts/observation/arrays.d.ts +1 -1
- package/dist/dts/observation/observable.d.ts +101 -75
- package/dist/dts/pending-task.d.ts +20 -0
- package/dist/dts/platform.d.ts +7 -0
- package/dist/dts/polyfills.d.ts +1 -8
- 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 +2 -2
- package/dist/dts/styles/element-styles.d.ts +10 -17
- 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 +69 -294
- package/dist/dts/templating/children.d.ts +1 -1
- package/dist/dts/templating/compiler.d.ts +1 -2
- package/dist/dts/templating/html-directive.d.ts +93 -35
- package/dist/dts/templating/node-observation.d.ts +4 -5
- package/dist/dts/templating/ref.d.ts +5 -13
- package/dist/dts/templating/render.d.ts +272 -0
- package/dist/dts/templating/repeat.d.ts +20 -75
- package/dist/dts/templating/slotted.d.ts +1 -1
- package/dist/dts/templating/template.d.ts +12 -61
- package/dist/dts/templating/view.d.ts +77 -12
- 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} +0 -0
- package/dist/dts/utilities.d.ts +0 -18
- package/dist/esm/components/attributes.js +13 -4
- package/dist/esm/components/{controller.js → element-controller.js} +188 -109
- package/dist/esm/components/fast-definitions.js +38 -28
- package/dist/esm/components/fast-element.js +31 -12
- package/dist/esm/context.js +163 -0
- package/dist/esm/debug.js +36 -4
- package/dist/esm/di/di.js +1435 -0
- package/dist/esm/index.js +2 -1
- package/dist/esm/interfaces.js +4 -0
- package/dist/esm/metadata.js +60 -0
- package/dist/esm/observation/arrays.js +304 -3
- package/dist/esm/observation/observable.js +81 -87
- package/dist/esm/pending-task.js +16 -0
- package/dist/esm/platform.js +26 -1
- package/dist/esm/polyfills.js +1 -55
- 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.js +4 -4
- package/dist/esm/styles/element-styles.js +14 -33
- package/dist/esm/{observation/behavior.js → styles/host.js} +0 -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 +134 -414
- package/dist/esm/templating/compiler.js +30 -7
- package/dist/esm/templating/html-directive.js +100 -28
- package/dist/esm/templating/node-observation.js +9 -8
- package/dist/esm/templating/ref.js +4 -12
- package/dist/esm/templating/render.js +391 -0
- package/dist/esm/templating/repeat.js +96 -72
- package/dist/esm/templating/template.js +11 -29
- package/dist/esm/templating/view.js +107 -29
- package/dist/esm/templating/when.js +5 -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 +0 -95
- package/dist/fast-element.api.json +9278 -10745
- package/dist/fast-element.d.ts +707 -813
- package/dist/fast-element.debug.js +1229 -944
- package/dist/fast-element.debug.min.js +1 -1
- package/dist/fast-element.js +1191 -938
- package/dist/fast-element.min.js +1 -1
- package/dist/fast-element.untrimmed.d.ts +716 -824
- package/docs/api-report.md +265 -319
- package/package.json +39 -14
- package/dist/dts/hooks.d.ts +0 -20
- package/dist/dts/observation/behavior.d.ts +0 -19
- package/dist/dts/observation/splice-strategies.d.ts +0 -13
- package/dist/esm/hooks.js +0 -32
- package/dist/esm/observation/splice-strategies.js +0 -400
package/dist/esm/index.js
CHANGED
|
@@ -8,6 +8,7 @@ export * from "./observation/update-queue.js";
|
|
|
8
8
|
export * from "./styles/element-styles.js";
|
|
9
9
|
export * from "./styles/css.js";
|
|
10
10
|
export * from "./styles/css-directive.js";
|
|
11
|
+
export * from "./styles/host.js";
|
|
11
12
|
// Templating
|
|
12
13
|
export * from "./templating/dom.js";
|
|
13
14
|
export * from "./templating/template.js";
|
|
@@ -26,4 +27,4 @@ export * from "./templating/node-observation.js";
|
|
|
26
27
|
export * from "./components/fast-element.js";
|
|
27
28
|
export * from "./components/fast-definitions.js";
|
|
28
29
|
export * from "./components/attributes.js";
|
|
29
|
-
export
|
|
30
|
+
export { ElementController } from "./components/element-controller.js";
|
package/dist/esm/interfaces.js
CHANGED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { emptyArray } from "./platform.js";
|
|
2
|
+
// Tiny polyfill for TypeScript's Reflect metadata API.
|
|
3
|
+
const metadataByTarget = new Map();
|
|
4
|
+
if (!("metadata" in Reflect)) {
|
|
5
|
+
Reflect.metadata = function (key, value) {
|
|
6
|
+
return function (target) {
|
|
7
|
+
Reflect.defineMetadata(key, value, target);
|
|
8
|
+
};
|
|
9
|
+
};
|
|
10
|
+
Reflect.defineMetadata = function (key, value, target) {
|
|
11
|
+
let metadata = metadataByTarget.get(target);
|
|
12
|
+
if (metadata === void 0) {
|
|
13
|
+
metadataByTarget.set(target, (metadata = new Map()));
|
|
14
|
+
}
|
|
15
|
+
metadata.set(key, value);
|
|
16
|
+
};
|
|
17
|
+
Reflect.getOwnMetadata = function (key, target) {
|
|
18
|
+
const metadata = metadataByTarget.get(target);
|
|
19
|
+
if (metadata !== void 0) {
|
|
20
|
+
return metadata.get(key);
|
|
21
|
+
}
|
|
22
|
+
return void 0;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Provides basic metadata capabilities used by Context and Dependency Injection.
|
|
27
|
+
*/
|
|
28
|
+
export const Metadata = Object.freeze({
|
|
29
|
+
/**
|
|
30
|
+
* Gets the "design:paramtypes" metadata for the specified type.
|
|
31
|
+
* @param Type - The type to get the metadata for.
|
|
32
|
+
* @returns The metadata array or a frozen empty array if no metadata is found.
|
|
33
|
+
*/
|
|
34
|
+
getDesignParamTypes: Type => {
|
|
35
|
+
var _a;
|
|
36
|
+
return (_a = Reflect.getOwnMetadata("design:paramtypes", Type)) !== null && _a !== void 0 ? _a : emptyArray;
|
|
37
|
+
},
|
|
38
|
+
/**
|
|
39
|
+
* Gets the "annotation:paramtypes" metadata for the specified type.
|
|
40
|
+
* @param Type - The type to get the metadata for.
|
|
41
|
+
* @returns The metadata array or a frozen empty array if no metadata is found.
|
|
42
|
+
*/
|
|
43
|
+
getAnnotationParamTypes: Type => {
|
|
44
|
+
var _a;
|
|
45
|
+
return (_a = Reflect.getOwnMetadata("annotation:paramtypes", Type)) !== null && _a !== void 0 ? _a : emptyArray;
|
|
46
|
+
},
|
|
47
|
+
/**
|
|
48
|
+
*
|
|
49
|
+
* @param Type - Gets the "annotation:paramtypes" metadata for the specified type. If none is found,
|
|
50
|
+
* an empty, mutable metadata array is created and added.
|
|
51
|
+
* @returns The metadata array.
|
|
52
|
+
*/
|
|
53
|
+
getOrCreateAnnotationParamTypes(Type) {
|
|
54
|
+
let types = this.getAnnotationParamTypes(Type);
|
|
55
|
+
if (types === emptyArray) {
|
|
56
|
+
Reflect.defineMetadata("annotation:paramtypes", (types = []), Type);
|
|
57
|
+
}
|
|
58
|
+
return types;
|
|
59
|
+
},
|
|
60
|
+
});
|
|
@@ -67,10 +67,311 @@ export const SpliceStrategySupport = Object.freeze({
|
|
|
67
67
|
const reset = new Splice(0, emptyArray, 0);
|
|
68
68
|
reset.reset = true;
|
|
69
69
|
const resetSplices = [reset];
|
|
70
|
+
// Note: This function is *based* on the computation of the Levenshtein
|
|
71
|
+
// "edit" distance. The one change is that "updates" are treated as two
|
|
72
|
+
// edits - not one. With Array splices, an update is really a delete
|
|
73
|
+
// followed by an add. By retaining this, we optimize for "keeping" the
|
|
74
|
+
// maximum array items in the original array. For example:
|
|
75
|
+
//
|
|
76
|
+
// 'xxxx123' to '123yyyy'
|
|
77
|
+
//
|
|
78
|
+
// With 1-edit updates, the shortest path would be just to update all seven
|
|
79
|
+
// characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
|
|
80
|
+
// leaves the substring '123' intact.
|
|
81
|
+
function calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd) {
|
|
82
|
+
// "Deletion" columns
|
|
83
|
+
const rowCount = oldEnd - oldStart + 1;
|
|
84
|
+
const columnCount = currentEnd - currentStart + 1;
|
|
85
|
+
const distances = new Array(rowCount);
|
|
86
|
+
let north;
|
|
87
|
+
let west;
|
|
88
|
+
// "Addition" rows. Initialize null column.
|
|
89
|
+
for (let i = 0; i < rowCount; ++i) {
|
|
90
|
+
distances[i] = new Array(columnCount);
|
|
91
|
+
distances[i][0] = i;
|
|
92
|
+
}
|
|
93
|
+
// Initialize null row
|
|
94
|
+
for (let j = 0; j < columnCount; ++j) {
|
|
95
|
+
distances[0][j] = j;
|
|
96
|
+
}
|
|
97
|
+
for (let i = 1; i < rowCount; ++i) {
|
|
98
|
+
for (let j = 1; j < columnCount; ++j) {
|
|
99
|
+
if (current[currentStart + j - 1] === old[oldStart + i - 1]) {
|
|
100
|
+
distances[i][j] = distances[i - 1][j - 1];
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
north = distances[i - 1][j] + 1;
|
|
104
|
+
west = distances[i][j - 1] + 1;
|
|
105
|
+
distances[i][j] = north < west ? north : west;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return distances;
|
|
110
|
+
}
|
|
111
|
+
// This starts at the final weight, and walks "backward" by finding
|
|
112
|
+
// the minimum previous weight recursively until the origin of the weight
|
|
113
|
+
// matrix.
|
|
114
|
+
function spliceOperationsFromEditDistances(distances) {
|
|
115
|
+
let i = distances.length - 1;
|
|
116
|
+
let j = distances[0].length - 1;
|
|
117
|
+
let current = distances[i][j];
|
|
118
|
+
const edits = [];
|
|
119
|
+
while (i > 0 || j > 0) {
|
|
120
|
+
if (i === 0) {
|
|
121
|
+
edits.push(2 /* Edit.add */);
|
|
122
|
+
j--;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
if (j === 0) {
|
|
126
|
+
edits.push(3 /* Edit.delete */);
|
|
127
|
+
i--;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
const northWest = distances[i - 1][j - 1];
|
|
131
|
+
const west = distances[i - 1][j];
|
|
132
|
+
const north = distances[i][j - 1];
|
|
133
|
+
let min;
|
|
134
|
+
if (west < north) {
|
|
135
|
+
min = west < northWest ? west : northWest;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
min = north < northWest ? north : northWest;
|
|
139
|
+
}
|
|
140
|
+
if (min === northWest) {
|
|
141
|
+
if (northWest === current) {
|
|
142
|
+
edits.push(0 /* Edit.leave */);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
edits.push(1 /* Edit.update */);
|
|
146
|
+
current = northWest;
|
|
147
|
+
}
|
|
148
|
+
i--;
|
|
149
|
+
j--;
|
|
150
|
+
}
|
|
151
|
+
else if (min === west) {
|
|
152
|
+
edits.push(3 /* Edit.delete */);
|
|
153
|
+
i--;
|
|
154
|
+
current = west;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
edits.push(2 /* Edit.add */);
|
|
158
|
+
j--;
|
|
159
|
+
current = north;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return edits.reverse();
|
|
163
|
+
}
|
|
164
|
+
function sharedPrefix(current, old, searchLength) {
|
|
165
|
+
for (let i = 0; i < searchLength; ++i) {
|
|
166
|
+
if (current[i] !== old[i]) {
|
|
167
|
+
return i;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return searchLength;
|
|
171
|
+
}
|
|
172
|
+
function sharedSuffix(current, old, searchLength) {
|
|
173
|
+
let index1 = current.length;
|
|
174
|
+
let index2 = old.length;
|
|
175
|
+
let count = 0;
|
|
176
|
+
while (count < searchLength && current[--index1] === old[--index2]) {
|
|
177
|
+
count++;
|
|
178
|
+
}
|
|
179
|
+
return count;
|
|
180
|
+
}
|
|
181
|
+
function intersect(start1, end1, start2, end2) {
|
|
182
|
+
// Disjoint
|
|
183
|
+
if (end1 < start2 || end2 < start1) {
|
|
184
|
+
return -1;
|
|
185
|
+
}
|
|
186
|
+
// Adjacent
|
|
187
|
+
if (end1 === start2 || end2 === start1) {
|
|
188
|
+
return 0;
|
|
189
|
+
}
|
|
190
|
+
// Non-zero intersect, span1 first
|
|
191
|
+
if (start1 < start2) {
|
|
192
|
+
if (end1 < end2) {
|
|
193
|
+
return end1 - start2; // Overlap
|
|
194
|
+
}
|
|
195
|
+
return end2 - start2; // Contained
|
|
196
|
+
}
|
|
197
|
+
// Non-zero intersect, span2 first
|
|
198
|
+
if (end2 < end1) {
|
|
199
|
+
return end2 - start1; // Overlap
|
|
200
|
+
}
|
|
201
|
+
return end1 - start1; // Contained
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* @remarks
|
|
205
|
+
* Lacking individual splice mutation information, the minimal set of
|
|
206
|
+
* splices can be synthesized given the previous state and final state of an
|
|
207
|
+
* array. The basic approach is to calculate the edit distance matrix and
|
|
208
|
+
* choose the shortest path through it.
|
|
209
|
+
*
|
|
210
|
+
* Complexity: O(l * p)
|
|
211
|
+
* l: The length of the current array
|
|
212
|
+
* p: The length of the old array
|
|
213
|
+
*/
|
|
214
|
+
function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
|
|
215
|
+
let prefixCount = 0;
|
|
216
|
+
let suffixCount = 0;
|
|
217
|
+
const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
|
|
218
|
+
if (currentStart === 0 && oldStart === 0) {
|
|
219
|
+
prefixCount = sharedPrefix(current, old, minLength);
|
|
220
|
+
}
|
|
221
|
+
if (currentEnd === current.length && oldEnd === old.length) {
|
|
222
|
+
suffixCount = sharedSuffix(current, old, minLength - prefixCount);
|
|
223
|
+
}
|
|
224
|
+
currentStart += prefixCount;
|
|
225
|
+
oldStart += prefixCount;
|
|
226
|
+
currentEnd -= suffixCount;
|
|
227
|
+
oldEnd -= suffixCount;
|
|
228
|
+
if (currentEnd - currentStart === 0 && oldEnd - oldStart === 0) {
|
|
229
|
+
return emptyArray;
|
|
230
|
+
}
|
|
231
|
+
if (currentStart === currentEnd) {
|
|
232
|
+
const splice = new Splice(currentStart, [], 0);
|
|
233
|
+
while (oldStart < oldEnd) {
|
|
234
|
+
splice.removed.push(old[oldStart++]);
|
|
235
|
+
}
|
|
236
|
+
return [splice];
|
|
237
|
+
}
|
|
238
|
+
else if (oldStart === oldEnd) {
|
|
239
|
+
return [new Splice(currentStart, [], currentEnd - currentStart)];
|
|
240
|
+
}
|
|
241
|
+
const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
|
|
242
|
+
const splices = [];
|
|
243
|
+
let splice = void 0;
|
|
244
|
+
let index = currentStart;
|
|
245
|
+
let oldIndex = oldStart;
|
|
246
|
+
for (let i = 0; i < ops.length; ++i) {
|
|
247
|
+
switch (ops[i]) {
|
|
248
|
+
case 0 /* Edit.leave */:
|
|
249
|
+
if (splice !== void 0) {
|
|
250
|
+
splices.push(splice);
|
|
251
|
+
splice = void 0;
|
|
252
|
+
}
|
|
253
|
+
index++;
|
|
254
|
+
oldIndex++;
|
|
255
|
+
break;
|
|
256
|
+
case 1 /* Edit.update */:
|
|
257
|
+
if (splice === void 0) {
|
|
258
|
+
splice = new Splice(index, [], 0);
|
|
259
|
+
}
|
|
260
|
+
splice.addedCount++;
|
|
261
|
+
index++;
|
|
262
|
+
splice.removed.push(old[oldIndex]);
|
|
263
|
+
oldIndex++;
|
|
264
|
+
break;
|
|
265
|
+
case 2 /* Edit.add */:
|
|
266
|
+
if (splice === void 0) {
|
|
267
|
+
splice = new Splice(index, [], 0);
|
|
268
|
+
}
|
|
269
|
+
splice.addedCount++;
|
|
270
|
+
index++;
|
|
271
|
+
break;
|
|
272
|
+
case 3 /* Edit.delete */:
|
|
273
|
+
if (splice === void 0) {
|
|
274
|
+
splice = new Splice(index, [], 0);
|
|
275
|
+
}
|
|
276
|
+
splice.removed.push(old[oldIndex]);
|
|
277
|
+
oldIndex++;
|
|
278
|
+
break;
|
|
279
|
+
// no default
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (splice !== void 0) {
|
|
283
|
+
splices.push(splice);
|
|
284
|
+
}
|
|
285
|
+
return splices;
|
|
286
|
+
}
|
|
287
|
+
function merge(splice, splices) {
|
|
288
|
+
let inserted = false;
|
|
289
|
+
let insertionOffset = 0;
|
|
290
|
+
for (let i = 0; i < splices.length; i++) {
|
|
291
|
+
const current = splices[i];
|
|
292
|
+
current.index += insertionOffset;
|
|
293
|
+
if (inserted) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
const intersectCount = intersect(splice.index, splice.index + splice.removed.length, current.index, current.index + current.addedCount);
|
|
297
|
+
if (intersectCount >= 0) {
|
|
298
|
+
// Merge the two splices
|
|
299
|
+
splices.splice(i, 1);
|
|
300
|
+
i--;
|
|
301
|
+
insertionOffset -= current.addedCount - current.removed.length;
|
|
302
|
+
splice.addedCount += current.addedCount - intersectCount;
|
|
303
|
+
const deleteCount = splice.removed.length + current.removed.length - intersectCount;
|
|
304
|
+
if (!splice.addedCount && !deleteCount) {
|
|
305
|
+
// merged splice is a noop. discard.
|
|
306
|
+
inserted = true;
|
|
307
|
+
}
|
|
308
|
+
else {
|
|
309
|
+
let currentRemoved = current.removed;
|
|
310
|
+
if (splice.index < current.index) {
|
|
311
|
+
// some prefix of splice.removed is prepended to current.removed.
|
|
312
|
+
const prepend = splice.removed.slice(0, current.index - splice.index);
|
|
313
|
+
prepend.push(...currentRemoved);
|
|
314
|
+
currentRemoved = prepend;
|
|
315
|
+
}
|
|
316
|
+
if (splice.index + splice.removed.length >
|
|
317
|
+
current.index + current.addedCount) {
|
|
318
|
+
// some suffix of splice.removed is appended to current.removed.
|
|
319
|
+
const append = splice.removed.slice(current.index + current.addedCount - splice.index);
|
|
320
|
+
currentRemoved.push(...append);
|
|
321
|
+
}
|
|
322
|
+
splice.removed = currentRemoved;
|
|
323
|
+
if (current.index < splice.index) {
|
|
324
|
+
splice.index = current.index;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
else if (splice.index < current.index) {
|
|
329
|
+
// Insert splice here.
|
|
330
|
+
inserted = true;
|
|
331
|
+
splices.splice(i, 0, splice);
|
|
332
|
+
i++;
|
|
333
|
+
const offset = splice.addedCount - splice.removed.length;
|
|
334
|
+
current.index += offset;
|
|
335
|
+
insertionOffset += offset;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
if (!inserted) {
|
|
339
|
+
splices.push(splice);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
function project(array, changes) {
|
|
343
|
+
let splices = [];
|
|
344
|
+
const initialSplices = [];
|
|
345
|
+
for (let i = 0, ii = changes.length; i < ii; i++) {
|
|
346
|
+
merge(changes[i], initialSplices);
|
|
347
|
+
}
|
|
348
|
+
for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
|
|
349
|
+
const splice = initialSplices[i];
|
|
350
|
+
if (splice.addedCount === 1 && splice.removed.length === 1) {
|
|
351
|
+
if (splice.removed[0] !== array[splice.index]) {
|
|
352
|
+
splices.push(splice);
|
|
353
|
+
}
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
|
|
357
|
+
}
|
|
358
|
+
return splices;
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* A SpliceStrategy that attempts to merge all splices into the minimal set of
|
|
362
|
+
* splices needed to represent the change from the old array to the new array.
|
|
363
|
+
* @public
|
|
364
|
+
*/
|
|
70
365
|
let defaultSpliceStrategy = Object.freeze({
|
|
71
|
-
support: SpliceStrategySupport.
|
|
366
|
+
support: SpliceStrategySupport.optimized,
|
|
72
367
|
normalize(previous, current, changes) {
|
|
73
|
-
|
|
368
|
+
if (previous === void 0) {
|
|
369
|
+
if (changes === void 0) {
|
|
370
|
+
return emptyArray;
|
|
371
|
+
}
|
|
372
|
+
return changes.length > 1 ? project(current, changes) : changes;
|
|
373
|
+
}
|
|
374
|
+
return resetSplices;
|
|
74
375
|
},
|
|
75
376
|
pop(array, observer, pop, args) {
|
|
76
377
|
const notEmpty = array.length > 0;
|
|
@@ -255,7 +556,7 @@ export const ArrayObserver = Object.freeze({
|
|
|
255
556
|
* @returns The length of the array.
|
|
256
557
|
* @public
|
|
257
558
|
*/
|
|
258
|
-
export function
|
|
559
|
+
export function lengthOf(array) {
|
|
259
560
|
if (!array) {
|
|
260
561
|
return 0;
|
|
261
562
|
}
|
|
@@ -1,7 +1,22 @@
|
|
|
1
1
|
import { isFunction, isString, } from "../interfaces.js";
|
|
2
|
-
import { FAST } from "../platform.js";
|
|
2
|
+
import { createMetadataLocator, FAST } from "../platform.js";
|
|
3
3
|
import { Updates } from "./update-queue.js";
|
|
4
4
|
import { PropertyChangeNotifier, SubscriberSet } from "./notifier.js";
|
|
5
|
+
/**
|
|
6
|
+
* Describes how the source's lifetime relates to its controller's lifetime.
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export const SourceLifetime = Object.freeze({
|
|
10
|
+
/**
|
|
11
|
+
* The source to controller lifetime relationship is unknown.
|
|
12
|
+
*/
|
|
13
|
+
unknown: void 0,
|
|
14
|
+
/**
|
|
15
|
+
* The source and controller lifetimes are coupled to one another.
|
|
16
|
+
* They can/will be GC'd together.
|
|
17
|
+
*/
|
|
18
|
+
coupled: 1,
|
|
19
|
+
});
|
|
5
20
|
/**
|
|
6
21
|
* Common Observable APIs.
|
|
7
22
|
* @public
|
|
@@ -10,7 +25,6 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
|
|
|
10
25
|
const queueUpdate = Updates.enqueue;
|
|
11
26
|
const volatileRegex = /(:|&&|\|\||if)/;
|
|
12
27
|
const notifierLookup = new WeakMap();
|
|
13
|
-
const accessorLookup = new WeakMap();
|
|
14
28
|
let watcher = void 0;
|
|
15
29
|
let createArrayObserver = (array) => {
|
|
16
30
|
throw FAST.error(1101 /* Message.needsArrayObservation */);
|
|
@@ -25,19 +39,7 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
|
|
|
25
39
|
}
|
|
26
40
|
return found;
|
|
27
41
|
}
|
|
28
|
-
|
|
29
|
-
let accessors = accessorLookup.get(target);
|
|
30
|
-
if (accessors === void 0) {
|
|
31
|
-
let currentTarget = Reflect.getPrototypeOf(target);
|
|
32
|
-
while (accessors === void 0 && currentTarget !== null) {
|
|
33
|
-
accessors = accessorLookup.get(currentTarget);
|
|
34
|
-
currentTarget = Reflect.getPrototypeOf(currentTarget);
|
|
35
|
-
}
|
|
36
|
-
accessors = accessors === void 0 ? [] : accessors.slice(0);
|
|
37
|
-
accessorLookup.set(target, accessors);
|
|
38
|
-
}
|
|
39
|
-
return accessors;
|
|
40
|
-
}
|
|
42
|
+
const getAccessors = createMetadataLocator();
|
|
41
43
|
class DefaultObservableAccessor {
|
|
42
44
|
constructor(name) {
|
|
43
45
|
this.name = name;
|
|
@@ -63,10 +65,10 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
|
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
}
|
|
66
|
-
class
|
|
67
|
-
constructor(
|
|
68
|
-
super(
|
|
69
|
-
this.
|
|
68
|
+
class ExpressionNotifierImplementation extends SubscriberSet {
|
|
69
|
+
constructor(expression, initialSubscriber, isVolatileBinding = false) {
|
|
70
|
+
super(expression, initialSubscriber);
|
|
71
|
+
this.expression = expression;
|
|
70
72
|
this.isVolatileBinding = isVolatileBinding;
|
|
71
73
|
this.needsRefresh = true;
|
|
72
74
|
this.needsQueue = true;
|
|
@@ -81,6 +83,22 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
|
|
|
81
83
|
setMode(isAsync) {
|
|
82
84
|
this.isAsync = this.needsQueue = isAsync;
|
|
83
85
|
}
|
|
86
|
+
bind(controller) {
|
|
87
|
+
this.controller = controller;
|
|
88
|
+
const value = this.observe(controller.source, controller.context);
|
|
89
|
+
if (!controller.isBound && this.requiresUnbind(controller)) {
|
|
90
|
+
controller.onUnbind(this);
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
requiresUnbind(controller) {
|
|
95
|
+
return (controller.sourceLifetime !== SourceLifetime.coupled ||
|
|
96
|
+
this.first !== this.last ||
|
|
97
|
+
this.first.propertySource !== controller.source);
|
|
98
|
+
}
|
|
99
|
+
unbind(controller) {
|
|
100
|
+
this.dispose();
|
|
101
|
+
}
|
|
84
102
|
observe(source, context) {
|
|
85
103
|
if (this.needsRefresh && this.last !== null) {
|
|
86
104
|
this.dispose();
|
|
@@ -88,10 +106,19 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
|
|
|
88
106
|
const previousWatcher = watcher;
|
|
89
107
|
watcher = this.needsRefresh ? this : void 0;
|
|
90
108
|
this.needsRefresh = this.isVolatileBinding;
|
|
91
|
-
|
|
92
|
-
|
|
109
|
+
let result;
|
|
110
|
+
try {
|
|
111
|
+
result = this.expression(source, context);
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
watcher = previousWatcher;
|
|
115
|
+
}
|
|
93
116
|
return result;
|
|
94
117
|
}
|
|
118
|
+
// backwards compat with v1 kernel
|
|
119
|
+
disconnect() {
|
|
120
|
+
this.dispose();
|
|
121
|
+
}
|
|
95
122
|
dispose() {
|
|
96
123
|
if (this.last !== null) {
|
|
97
124
|
let current = this.first;
|
|
@@ -218,22 +245,22 @@ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () =>
|
|
|
218
245
|
*/
|
|
219
246
|
getAccessors,
|
|
220
247
|
/**
|
|
221
|
-
* Creates a {@link
|
|
222
|
-
* provided {@link
|
|
223
|
-
* @param
|
|
248
|
+
* Creates a {@link ExpressionNotifier} that can watch the
|
|
249
|
+
* provided {@link Expression} for changes.
|
|
250
|
+
* @param expression - The binding to observe.
|
|
224
251
|
* @param initialSubscriber - An initial subscriber to changes in the binding value.
|
|
225
252
|
* @param isVolatileBinding - Indicates whether the binding's dependency list must be re-evaluated on every value evaluation.
|
|
226
253
|
*/
|
|
227
|
-
binding(
|
|
228
|
-
return new
|
|
254
|
+
binding(expression, initialSubscriber, isVolatileBinding = this.isVolatileBinding(expression)) {
|
|
255
|
+
return new ExpressionNotifierImplementation(expression, initialSubscriber, isVolatileBinding);
|
|
229
256
|
},
|
|
230
257
|
/**
|
|
231
258
|
* Determines whether a binding expression is volatile and needs to have its dependency list re-evaluated
|
|
232
259
|
* on every evaluation of the value.
|
|
233
|
-
* @param
|
|
260
|
+
* @param expression - The binding to inspect.
|
|
234
261
|
*/
|
|
235
|
-
isVolatileBinding(
|
|
236
|
-
return volatileRegex.test(
|
|
262
|
+
isVolatileBinding(expression) {
|
|
263
|
+
return volatileRegex.test(expression.toString());
|
|
237
264
|
},
|
|
238
265
|
});
|
|
239
266
|
});
|
|
@@ -272,72 +299,39 @@ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
|
|
|
272
299
|
},
|
|
273
300
|
};
|
|
274
301
|
});
|
|
275
|
-
class DefaultExecutionContext {
|
|
276
|
-
constructor(parentSource = null, parentContext = null) {
|
|
277
|
-
this.index = 0;
|
|
278
|
-
this.length = 0;
|
|
279
|
-
this.parent = parentSource;
|
|
280
|
-
this.parentContext = parentContext;
|
|
281
|
-
}
|
|
282
|
-
get event() {
|
|
283
|
-
return contextEvent.get();
|
|
284
|
-
}
|
|
285
|
-
get isEven() {
|
|
286
|
-
return this.index % 2 === 0;
|
|
287
|
-
}
|
|
288
|
-
get isOdd() {
|
|
289
|
-
return this.index % 2 !== 0;
|
|
290
|
-
}
|
|
291
|
-
get isFirst() {
|
|
292
|
-
return this.index === 0;
|
|
293
|
-
}
|
|
294
|
-
get isInMiddle() {
|
|
295
|
-
return !this.isFirst && !this.isLast;
|
|
296
|
-
}
|
|
297
|
-
get isLast() {
|
|
298
|
-
return this.index === this.length - 1;
|
|
299
|
-
}
|
|
300
|
-
eventDetail() {
|
|
301
|
-
return this.event.detail;
|
|
302
|
-
}
|
|
303
|
-
eventTarget() {
|
|
304
|
-
return this.event.target;
|
|
305
|
-
}
|
|
306
|
-
updatePosition(index, length) {
|
|
307
|
-
this.index = index;
|
|
308
|
-
this.length = length;
|
|
309
|
-
}
|
|
310
|
-
createChildContext(parentSource) {
|
|
311
|
-
return new DefaultExecutionContext(parentSource, this);
|
|
312
|
-
}
|
|
313
|
-
createItemContext(index, length) {
|
|
314
|
-
const childContext = Object.create(this);
|
|
315
|
-
childContext.index = index;
|
|
316
|
-
childContext.length = length;
|
|
317
|
-
return childContext;
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
Observable.defineProperty(DefaultExecutionContext.prototype, "index");
|
|
321
|
-
Observable.defineProperty(DefaultExecutionContext.prototype, "length");
|
|
322
302
|
/**
|
|
323
|
-
*
|
|
303
|
+
* Provides additional contextual information available to behaviors and expressions.
|
|
324
304
|
* @public
|
|
325
305
|
*/
|
|
326
306
|
export const ExecutionContext = Object.freeze({
|
|
327
|
-
default: new DefaultExecutionContext(),
|
|
328
307
|
/**
|
|
329
|
-
*
|
|
330
|
-
* @param event - The event to set.
|
|
331
|
-
* @internal
|
|
308
|
+
* A default execution context.
|
|
332
309
|
*/
|
|
333
|
-
|
|
334
|
-
|
|
310
|
+
default: {
|
|
311
|
+
index: 0,
|
|
312
|
+
length: 0,
|
|
313
|
+
get event() {
|
|
314
|
+
return ExecutionContext.getEvent();
|
|
315
|
+
},
|
|
316
|
+
eventDetail() {
|
|
317
|
+
return this.event.detail;
|
|
318
|
+
},
|
|
319
|
+
eventTarget() {
|
|
320
|
+
return this.event.target;
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
/**
|
|
324
|
+
* Gets the current event.
|
|
325
|
+
* @returns An event object.
|
|
326
|
+
*/
|
|
327
|
+
getEvent() {
|
|
328
|
+
return contextEvent.get();
|
|
335
329
|
},
|
|
336
330
|
/**
|
|
337
|
-
*
|
|
338
|
-
* @
|
|
331
|
+
* Sets the current event.
|
|
332
|
+
* @param event - An event object.
|
|
339
333
|
*/
|
|
340
|
-
|
|
341
|
-
|
|
334
|
+
setEvent(event) {
|
|
335
|
+
contextEvent.set(event);
|
|
342
336
|
},
|
|
343
337
|
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A concrete implementation of {@link PendingTask}
|
|
3
|
+
* @beta
|
|
4
|
+
*/
|
|
5
|
+
export class PendingTaskEvent extends Event {
|
|
6
|
+
constructor(complete) {
|
|
7
|
+
super(PendingTaskEvent.type, { bubbles: true, composed: true });
|
|
8
|
+
this.complete = complete;
|
|
9
|
+
}
|
|
10
|
+
static isPendingTask(value) {
|
|
11
|
+
var _a;
|
|
12
|
+
return (value.type === PendingTaskEvent.type &&
|
|
13
|
+
typeof ((_a = value.complete) === null || _a === void 0 ? void 0 : _a.then) === "function");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
PendingTaskEvent.type = "pending-task";
|