@microsoft/fast-element 1.10.0 → 2.0.0-beta.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.
Files changed (97) hide show
  1. package/.eslintrc.json +1 -12
  2. package/CHANGELOG.json +396 -1
  3. package/CHANGELOG.md +68 -2
  4. package/README.md +2 -2
  5. package/dist/dts/components/attributes.d.ts +4 -1
  6. package/dist/dts/components/controller.d.ts +12 -11
  7. package/dist/dts/components/fast-definitions.d.ts +8 -2
  8. package/dist/dts/components/fast-element.d.ts +5 -4
  9. package/dist/dts/debug.d.ts +1 -0
  10. package/dist/dts/hooks.d.ts +20 -0
  11. package/dist/dts/index.d.ts +16 -15
  12. package/dist/dts/index.debug.d.ts +2 -0
  13. package/dist/dts/index.rollup.d.ts +2 -0
  14. package/dist/dts/index.rollup.debug.d.ts +3 -0
  15. package/dist/dts/interfaces.d.ts +144 -0
  16. package/dist/dts/observation/arrays.d.ts +207 -0
  17. package/dist/dts/observation/behavior.d.ts +5 -5
  18. package/dist/dts/observation/notifier.d.ts +18 -18
  19. package/dist/dts/observation/observable.d.ts +86 -29
  20. package/dist/dts/observation/splice-strategies.d.ts +13 -0
  21. package/dist/dts/observation/update-queue.d.ts +40 -0
  22. package/dist/dts/platform.d.ts +18 -67
  23. package/dist/dts/polyfills.d.ts +8 -0
  24. package/dist/dts/styles/css-directive.d.ts +43 -5
  25. package/dist/dts/styles/css.d.ts +19 -3
  26. package/dist/dts/styles/element-styles.d.ts +42 -62
  27. package/dist/dts/templating/binding.d.ts +320 -64
  28. package/dist/dts/templating/children.d.ts +18 -15
  29. package/dist/dts/templating/compiler.d.ts +47 -28
  30. package/dist/dts/templating/dom.d.ts +41 -0
  31. package/dist/dts/templating/html-directive.d.ts +179 -43
  32. package/dist/dts/templating/markup.d.ts +48 -0
  33. package/dist/dts/templating/node-observation.d.ts +45 -29
  34. package/dist/dts/templating/ref.d.ts +6 -12
  35. package/dist/dts/templating/repeat.d.ts +72 -14
  36. package/dist/dts/templating/slotted.d.ts +13 -14
  37. package/dist/dts/templating/template.d.ts +78 -23
  38. package/dist/dts/templating/view.d.ts +16 -23
  39. package/dist/dts/utilities.d.ts +40 -0
  40. package/dist/esm/components/attributes.js +25 -24
  41. package/dist/esm/components/controller.js +77 -57
  42. package/dist/esm/components/fast-definitions.js +14 -22
  43. package/dist/esm/debug.js +29 -0
  44. package/dist/esm/hooks.js +32 -0
  45. package/dist/esm/index.debug.js +2 -0
  46. package/dist/esm/index.js +19 -14
  47. package/dist/esm/index.rollup.debug.js +3 -0
  48. package/dist/esm/index.rollup.js +2 -0
  49. package/dist/esm/interfaces.js +8 -1
  50. package/dist/esm/observation/arrays.js +269 -0
  51. package/dist/esm/observation/notifier.js +75 -83
  52. package/dist/esm/observation/observable.js +80 -107
  53. package/dist/esm/observation/{array-change-records.js → splice-strategies.js} +136 -62
  54. package/dist/esm/observation/update-queue.js +67 -0
  55. package/dist/esm/platform.js +36 -42
  56. package/dist/esm/polyfills.js +85 -0
  57. package/dist/esm/styles/css-directive.js +29 -13
  58. package/dist/esm/styles/css.js +27 -40
  59. package/dist/esm/styles/element-styles.js +65 -104
  60. package/dist/esm/templating/binding.js +465 -155
  61. package/dist/esm/templating/children.js +33 -23
  62. package/dist/esm/templating/compiler.js +235 -152
  63. package/dist/esm/templating/dom.js +49 -0
  64. package/dist/esm/templating/html-directive.js +125 -40
  65. package/dist/esm/templating/markup.js +75 -0
  66. package/dist/esm/templating/node-observation.js +50 -45
  67. package/dist/esm/templating/ref.js +7 -16
  68. package/dist/esm/templating/repeat.js +38 -43
  69. package/dist/esm/templating/slotted.js +23 -20
  70. package/dist/esm/templating/template.js +71 -95
  71. package/dist/esm/templating/view.js +44 -43
  72. package/dist/esm/templating/when.js +2 -1
  73. package/dist/esm/utilities.js +139 -0
  74. package/dist/fast-element.api.json +14062 -5235
  75. package/dist/fast-element.d.ts +1434 -579
  76. package/dist/fast-element.debug.js +3824 -0
  77. package/dist/fast-element.debug.min.js +1 -0
  78. package/dist/fast-element.js +3565 -4014
  79. package/dist/fast-element.min.js +1 -1
  80. package/dist/fast-element.untrimmed.d.ts +2908 -0
  81. package/dist/tsdoc-metadata.json +1 -1
  82. package/docs/api-report.md +590 -231
  83. package/docs/fast-element-2-changes.md +15 -0
  84. package/docs/guide/declaring-templates.md +5 -4
  85. package/docs/guide/defining-elements.md +3 -2
  86. package/docs/guide/leveraging-css.md +1 -0
  87. package/docs/guide/next-steps.md +3 -2
  88. package/docs/guide/observables-and-state.md +2 -1
  89. package/docs/guide/using-directives.md +2 -1
  90. package/docs/guide/working-with-shadow-dom.md +1 -0
  91. package/karma.conf.cjs +6 -17
  92. package/package.json +48 -14
  93. package/dist/dts/dom.d.ts +0 -112
  94. package/dist/dts/observation/array-change-records.d.ts +0 -48
  95. package/dist/dts/observation/array-observer.d.ts +0 -9
  96. package/dist/esm/dom.js +0 -207
  97. package/dist/esm/observation/array-observer.js +0 -173
@@ -1,28 +1,27 @@
1
- import { DOM } from "../dom.js";
1
+ import { isFunction, isString, } from "../interfaces.js";
2
2
  import { FAST } from "../platform.js";
3
+ import { Updates } from "./update-queue.js";
3
4
  import { PropertyChangeNotifier, SubscriberSet } from "./notifier.js";
4
5
  /**
5
6
  * Common Observable APIs.
6
7
  * @public
7
8
  */
8
- export const Observable = FAST.getById(2 /* observable */, () => {
9
+ export const Observable = FAST.getById(2 /* KernelServiceId.observable */, () => {
10
+ const queueUpdate = Updates.enqueue;
9
11
  const volatileRegex = /(:|&&|\|\||if)/;
10
12
  const notifierLookup = new WeakMap();
11
13
  const accessorLookup = new WeakMap();
12
- const queueUpdate = DOM.queueUpdate;
13
14
  let watcher = void 0;
14
15
  let createArrayObserver = (array) => {
15
- throw new Error("Must call enableArrayObservation before observing arrays.");
16
+ throw FAST.error(1101 /* Message.needsArrayObservation */);
16
17
  };
17
18
  function getNotifier(source) {
18
- let found = source.$fastController || notifierLookup.get(source);
19
+ var _a;
20
+ let found = (_a = source.$fastController) !== null && _a !== void 0 ? _a : notifierLookup.get(source);
19
21
  if (found === void 0) {
20
- if (Array.isArray(source)) {
21
- found = createArrayObserver(source);
22
- }
23
- else {
24
- notifierLookup.set(source, (found = new PropertyChangeNotifier(source)));
25
- }
22
+ Array.isArray(source)
23
+ ? (found = createArrayObserver(source))
24
+ : notifierLookup.set(source, (found = new PropertyChangeNotifier(source)));
26
25
  }
27
26
  return found;
28
27
  }
@@ -34,12 +33,7 @@ export const Observable = FAST.getById(2 /* observable */, () => {
34
33
  accessors = accessorLookup.get(currentTarget);
35
34
  currentTarget = Reflect.getPrototypeOf(currentTarget);
36
35
  }
37
- if (accessors === void 0) {
38
- accessors = [];
39
- }
40
- else {
41
- accessors = accessors.slice(0);
42
- }
36
+ accessors = accessors === void 0 ? [] : accessors.slice(0);
43
37
  accessorLookup.set(target, accessors);
44
38
  }
45
39
  return accessors;
@@ -62,7 +56,7 @@ export const Observable = FAST.getById(2 /* observable */, () => {
62
56
  if (oldValue !== newValue) {
63
57
  source[field] = newValue;
64
58
  const callback = source[this.callback];
65
- if (typeof callback === "function") {
59
+ if (isFunction(callback)) {
66
60
  callback.call(source, oldValue, newValue);
67
61
  }
68
62
  getNotifier(source).notify(this.name);
@@ -76,6 +70,7 @@ export const Observable = FAST.getById(2 /* observable */, () => {
76
70
  this.isVolatileBinding = isVolatileBinding;
77
71
  this.needsRefresh = true;
78
72
  this.needsQueue = true;
73
+ this.isAsync = true;
79
74
  this.first = this;
80
75
  this.last = null;
81
76
  this.propertySource = void 0;
@@ -83,18 +78,21 @@ export const Observable = FAST.getById(2 /* observable */, () => {
83
78
  this.notifier = void 0;
84
79
  this.next = void 0;
85
80
  }
81
+ setMode(isAsync) {
82
+ this.isAsync = this.needsQueue = isAsync;
83
+ }
86
84
  observe(source, context) {
87
85
  if (this.needsRefresh && this.last !== null) {
88
- this.disconnect();
86
+ this.dispose();
89
87
  }
90
88
  const previousWatcher = watcher;
91
89
  watcher = this.needsRefresh ? this : void 0;
92
90
  this.needsRefresh = this.isVolatileBinding;
93
- const result = this.binding(source, context);
91
+ const result = this.binding(source, context !== null && context !== void 0 ? context : ExecutionContext.default);
94
92
  watcher = previousWatcher;
95
93
  return result;
96
94
  }
97
- disconnect() {
95
+ dispose() {
98
96
  if (this.last !== null) {
99
97
  let current = this.first;
100
98
  while (current !== void 0) {
@@ -102,7 +100,7 @@ export const Observable = FAST.getById(2 /* observable */, () => {
102
100
  current = current.next;
103
101
  }
104
102
  this.last = null;
105
- this.needsRefresh = this.needsQueue = true;
103
+ this.needsRefresh = this.needsQueue = this.isAsync;
106
104
  }
107
105
  }
108
106
  watch(propertySource, propertyName) {
@@ -122,7 +120,7 @@ export const Observable = FAST.getById(2 /* observable */, () => {
122
120
  watcher = void 0;
123
121
  /* eslint-disable-next-line */
124
122
  prevValue = prev.propertySource[prev.propertyName];
125
- /* eslint-disable-next-line @typescript-eslint/no-this-alias */
123
+ /* eslint-disable-next-line */
126
124
  watcher = this;
127
125
  if (propertySource === prevValue) {
128
126
  this.needsRefresh = true;
@@ -137,33 +135,22 @@ export const Observable = FAST.getById(2 /* observable */, () => {
137
135
  this.needsQueue = false;
138
136
  queueUpdate(this);
139
137
  }
138
+ else if (!this.isAsync) {
139
+ this.call();
140
+ }
140
141
  }
141
142
  call() {
142
143
  if (this.last !== null) {
143
- this.needsQueue = true;
144
+ this.needsQueue = this.isAsync;
144
145
  this.notify(this);
145
146
  }
146
147
  }
147
- records() {
148
+ *records() {
148
149
  let next = this.first;
149
- return {
150
- next: () => {
151
- const current = next;
152
- if (current === undefined) {
153
- return { value: void 0, done: true };
154
- }
155
- else {
156
- next = next.next;
157
- return {
158
- value: current,
159
- done: false,
160
- };
161
- }
162
- },
163
- [Symbol.iterator]: function () {
164
- return this;
165
- },
166
- };
150
+ while (next !== void 0) {
151
+ yield next;
152
+ next = next.next;
153
+ }
167
154
  }
168
155
  }
169
156
  return Object.freeze({
@@ -185,18 +172,14 @@ export const Observable = FAST.getById(2 /* observable */, () => {
185
172
  * @param propertyName - The property to track as changed.
186
173
  */
187
174
  track(source, propertyName) {
188
- if (watcher !== void 0) {
189
- watcher.watch(source, propertyName);
190
- }
175
+ watcher && watcher.watch(source, propertyName);
191
176
  },
192
177
  /**
193
178
  * Notifies watchers that the currently executing property getter or function is volatile
194
179
  * with respect to its observable dependencies.
195
180
  */
196
181
  trackVolatile() {
197
- if (watcher !== void 0) {
198
- watcher.needsRefresh = true;
199
- }
182
+ watcher && (watcher.needsRefresh = true);
200
183
  },
201
184
  /**
202
185
  * Notifies subscribers of a source object of changes.
@@ -204,6 +187,7 @@ export const Observable = FAST.getById(2 /* observable */, () => {
204
187
  * @param args - The change args to pass to subscribers.
205
188
  */
206
189
  notify(source, args) {
190
+ /* eslint-disable-next-line @typescript-eslint/no-use-before-define */
207
191
  getNotifier(source).notify(args);
208
192
  },
209
193
  /**
@@ -213,16 +197,16 @@ export const Observable = FAST.getById(2 /* observable */, () => {
213
197
  * or a custom accessor that specifies the property name and accessor implementation.
214
198
  */
215
199
  defineProperty(target, nameOrAccessor) {
216
- if (typeof nameOrAccessor === "string") {
200
+ if (isString(nameOrAccessor)) {
217
201
  nameOrAccessor = new DefaultObservableAccessor(nameOrAccessor);
218
202
  }
219
203
  getAccessors(target).push(nameOrAccessor);
220
204
  Reflect.defineProperty(target, nameOrAccessor.name, {
221
205
  enumerable: true,
222
- get: function () {
206
+ get() {
223
207
  return nameOrAccessor.getValue(this);
224
208
  },
225
- set: function (newValue) {
209
+ set(newValue) {
226
210
  nameOrAccessor.setValue(this, newValue);
227
211
  },
228
212
  });
@@ -271,13 +255,13 @@ export function observable(target, nameOrAccessor) {
271
255
  */
272
256
  export function volatile(target, name, descriptor) {
273
257
  return Object.assign({}, descriptor, {
274
- get: function () {
258
+ get() {
275
259
  Observable.trackVolatile();
276
260
  return descriptor.get.apply(this);
277
261
  },
278
262
  });
279
263
  }
280
- const contextEvent = FAST.getById(3 /* contextEvent */, () => {
264
+ const contextEvent = FAST.getById(3 /* KernelServiceId.contextEvent */, () => {
281
265
  let current = null;
282
266
  return {
283
267
  get() {
@@ -288,83 +272,72 @@ const contextEvent = FAST.getById(3 /* contextEvent */, () => {
288
272
  },
289
273
  };
290
274
  });
291
- /**
292
- * Provides additional contextual information available to behaviors and expressions.
293
- * @public
294
- */
295
- export class ExecutionContext {
296
- constructor() {
297
- /**
298
- * The index of the current item within a repeat context.
299
- */
275
+ class DefaultExecutionContext {
276
+ constructor(parentSource = null, parentContext = null) {
300
277
  this.index = 0;
301
- /**
302
- * The length of the current collection within a repeat context.
303
- */
304
278
  this.length = 0;
305
- /**
306
- * The parent data object within a repeat context.
307
- */
308
- this.parent = null;
309
- /**
310
- * The parent execution context when in nested context scenarios.
311
- */
312
- this.parentContext = null;
279
+ this.parent = parentSource;
280
+ this.parentContext = parentContext;
313
281
  }
314
- /**
315
- * The current event within an event handler.
316
- */
317
282
  get event() {
318
283
  return contextEvent.get();
319
284
  }
320
- /**
321
- * Indicates whether the current item within a repeat context
322
- * has an even index.
323
- */
324
285
  get isEven() {
325
286
  return this.index % 2 === 0;
326
287
  }
327
- /**
328
- * Indicates whether the current item within a repeat context
329
- * has an odd index.
330
- */
331
288
  get isOdd() {
332
289
  return this.index % 2 !== 0;
333
290
  }
334
- /**
335
- * Indicates whether the current item within a repeat context
336
- * is the first item in the collection.
337
- */
338
291
  get isFirst() {
339
292
  return this.index === 0;
340
293
  }
341
- /**
342
- * Indicates whether the current item within a repeat context
343
- * is somewhere in the middle of the collection.
344
- */
345
294
  get isInMiddle() {
346
295
  return !this.isFirst && !this.isLast;
347
296
  }
348
- /**
349
- * Indicates whether the current item within a repeat context
350
- * is the last item in the collection.
351
- */
352
297
  get isLast() {
353
298
  return this.index === this.length - 1;
354
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
+ /**
323
+ * The common execution context APIs.
324
+ * @public
325
+ */
326
+ export const ExecutionContext = Object.freeze({
327
+ default: new DefaultExecutionContext(),
355
328
  /**
356
329
  * Sets the event for the current execution context.
357
330
  * @param event - The event to set.
358
331
  * @internal
359
332
  */
360
- static setEvent(event) {
333
+ setEvent(event) {
361
334
  contextEvent.set(event);
362
- }
363
- }
364
- Observable.defineProperty(ExecutionContext.prototype, "index");
365
- Observable.defineProperty(ExecutionContext.prototype, "length");
366
- /**
367
- * The default execution context used in binding expressions.
368
- * @public
369
- */
370
- export const defaultExecutionContext = Object.seal(new ExecutionContext());
335
+ },
336
+ /**
337
+ * Creates a new root execution context.
338
+ * @returns A new execution context.
339
+ */
340
+ create() {
341
+ return new DefaultExecutionContext();
342
+ },
343
+ });
@@ -1,23 +1,12 @@
1
1
  import { emptyArray } from "../platform.js";
2
- /** @internal */
3
- export function newSplice(index, removed, addedCount) {
4
- return {
5
- index: index,
6
- removed: removed,
7
- addedCount: addedCount,
8
- };
9
- }
10
- const EDIT_LEAVE = 0;
11
- const EDIT_UPDATE = 1;
12
- const EDIT_ADD = 2;
13
- const EDIT_DELETE = 3;
2
+ import { Splice, SpliceStrategy, SpliceStrategySupport, } from "./arrays.js";
14
3
  // Note: This function is *based* on the computation of the Levenshtein
15
4
  // "edit" distance. The one change is that "updates" are treated as two
16
5
  // edits - not one. With Array splices, an update is really a delete
17
6
  // followed by an add. By retaining this, we optimize for "keeping" the
18
7
  // maximum array items in the original array. For example:
19
8
  //
20
- // 'xxxx123' -> '123yyyy'
9
+ // 'xxxx123' to '123yyyy'
21
10
  //
22
11
  // With 1-edit updates, the shortest path would be just to update all seven
23
12
  // characters. With 2-edit updates, we delete 4, leave 3, and add 4. This
@@ -62,12 +51,12 @@ function spliceOperationsFromEditDistances(distances) {
62
51
  const edits = [];
63
52
  while (i > 0 || j > 0) {
64
53
  if (i === 0) {
65
- edits.push(EDIT_ADD);
54
+ edits.push(2 /* Edit.add */);
66
55
  j--;
67
56
  continue;
68
57
  }
69
58
  if (j === 0) {
70
- edits.push(EDIT_DELETE);
59
+ edits.push(3 /* Edit.delete */);
71
60
  i--;
72
61
  continue;
73
62
  }
@@ -83,28 +72,27 @@ function spliceOperationsFromEditDistances(distances) {
83
72
  }
84
73
  if (min === northWest) {
85
74
  if (northWest === current) {
86
- edits.push(EDIT_LEAVE);
75
+ edits.push(0 /* Edit.leave */);
87
76
  }
88
77
  else {
89
- edits.push(EDIT_UPDATE);
78
+ edits.push(1 /* Edit.update */);
90
79
  current = northWest;
91
80
  }
92
81
  i--;
93
82
  j--;
94
83
  }
95
84
  else if (min === west) {
96
- edits.push(EDIT_DELETE);
85
+ edits.push(3 /* Edit.delete */);
97
86
  i--;
98
87
  current = west;
99
88
  }
100
89
  else {
101
- edits.push(EDIT_ADD);
90
+ edits.push(2 /* Edit.add */);
102
91
  j--;
103
92
  current = north;
104
93
  }
105
94
  }
106
- edits.reverse();
107
- return edits;
95
+ return edits.reverse();
108
96
  }
109
97
  function sharedPrefix(current, old, searchLength) {
110
98
  for (let i = 0; i < searchLength; ++i) {
@@ -146,20 +134,6 @@ function intersect(start1, end1, start2, end2) {
146
134
  return end1 - start1; // Contained
147
135
  }
148
136
  /**
149
- * Splice Projection functions:
150
- *
151
- * A splice map is a representation of how a previous array of items
152
- * was transformed into a new array of items. Conceptually it is a list of
153
- * tuples of
154
- *
155
- * <index, removed, addedCount>
156
- *
157
- * which are kept in ascending index order of. The tuple represents that at
158
- * the |index|, |removed| sequence of items were removed, and counting forward
159
- * from |index|, |addedCount| items were added.
160
- */
161
- /**
162
- * @internal
163
137
  * @remarks
164
138
  * Lacking individual splice mutation information, the minimal set of
165
139
  * splices can be synthesized given the previous state and final state of an
@@ -170,7 +144,7 @@ function intersect(start1, end1, start2, end2) {
170
144
  * l: The length of the current array
171
145
  * p: The length of the old array
172
146
  */
173
- export function calcSplices(current, currentStart, currentEnd, old, oldStart, oldEnd) {
147
+ function calc(current, currentStart, currentEnd, old, oldStart, oldEnd) {
174
148
  let prefixCount = 0;
175
149
  let suffixCount = 0;
176
150
  const minLength = Math.min(currentEnd - currentStart, oldEnd - oldStart);
@@ -188,14 +162,14 @@ export function calcSplices(current, currentStart, currentEnd, old, oldStart, ol
188
162
  return emptyArray;
189
163
  }
190
164
  if (currentStart === currentEnd) {
191
- const splice = newSplice(currentStart, [], 0);
165
+ const splice = new Splice(currentStart, [], 0);
192
166
  while (oldStart < oldEnd) {
193
167
  splice.removed.push(old[oldStart++]);
194
168
  }
195
169
  return [splice];
196
170
  }
197
171
  else if (oldStart === oldEnd) {
198
- return [newSplice(currentStart, [], currentEnd - currentStart)];
172
+ return [new Splice(currentStart, [], currentEnd - currentStart)];
199
173
  }
200
174
  const ops = spliceOperationsFromEditDistances(calcEditDistances(current, currentStart, currentEnd, old, oldStart, oldEnd));
201
175
  const splices = [];
@@ -204,7 +178,7 @@ export function calcSplices(current, currentStart, currentEnd, old, oldStart, ol
204
178
  let oldIndex = oldStart;
205
179
  for (let i = 0; i < ops.length; ++i) {
206
180
  switch (ops[i]) {
207
- case EDIT_LEAVE:
181
+ case 0 /* Edit.leave */:
208
182
  if (splice !== void 0) {
209
183
  splices.push(splice);
210
184
  splice = void 0;
@@ -212,25 +186,25 @@ export function calcSplices(current, currentStart, currentEnd, old, oldStart, ol
212
186
  index++;
213
187
  oldIndex++;
214
188
  break;
215
- case EDIT_UPDATE:
189
+ case 1 /* Edit.update */:
216
190
  if (splice === void 0) {
217
- splice = newSplice(index, [], 0);
191
+ splice = new Splice(index, [], 0);
218
192
  }
219
193
  splice.addedCount++;
220
194
  index++;
221
195
  splice.removed.push(old[oldIndex]);
222
196
  oldIndex++;
223
197
  break;
224
- case EDIT_ADD:
198
+ case 2 /* Edit.add */:
225
199
  if (splice === void 0) {
226
- splice = newSplice(index, [], 0);
200
+ splice = new Splice(index, [], 0);
227
201
  }
228
202
  splice.addedCount++;
229
203
  index++;
230
204
  break;
231
- case EDIT_DELETE:
205
+ case 3 /* Edit.delete */:
232
206
  if (splice === void 0) {
233
- splice = newSplice(index, [], 0);
207
+ splice = new Splice(index, [], 0);
234
208
  }
235
209
  splice.removed.push(old[oldIndex]);
236
210
  oldIndex++;
@@ -243,9 +217,7 @@ export function calcSplices(current, currentStart, currentEnd, old, oldStart, ol
243
217
  }
244
218
  return splices;
245
219
  }
246
- const $push = Array.prototype.push;
247
- function mergeSplice(splices, index, removed, addedCount) {
248
- const splice = newSplice(index, removed, addedCount);
220
+ function merge(splice, splices) {
249
221
  let inserted = false;
250
222
  let insertionOffset = 0;
251
223
  for (let i = 0; i < splices.length; i++) {
@@ -271,14 +243,14 @@ function mergeSplice(splices, index, removed, addedCount) {
271
243
  if (splice.index < current.index) {
272
244
  // some prefix of splice.removed is prepended to current.removed.
273
245
  const prepend = splice.removed.slice(0, current.index - splice.index);
274
- $push.apply(prepend, currentRemoved);
246
+ prepend.push(...currentRemoved);
275
247
  currentRemoved = prepend;
276
248
  }
277
249
  if (splice.index + splice.removed.length >
278
250
  current.index + current.addedCount) {
279
251
  // some suffix of splice.removed is appended to current.removed.
280
252
  const append = splice.removed.slice(current.index + current.addedCount - splice.index);
281
- $push.apply(currentRemoved, append);
253
+ currentRemoved.push(...append);
282
254
  }
283
255
  splice.removed = currentRemoved;
284
256
  if (current.index < splice.index) {
@@ -300,18 +272,12 @@ function mergeSplice(splices, index, removed, addedCount) {
300
272
  splices.push(splice);
301
273
  }
302
274
  }
303
- function createInitialSplices(changeRecords) {
304
- const splices = [];
305
- for (let i = 0, ii = changeRecords.length; i < ii; i++) {
306
- const record = changeRecords[i];
307
- mergeSplice(splices, record.index, record.removed, record.addedCount);
308
- }
309
- return splices;
310
- }
311
- /** @internal */
312
- export function projectArraySplices(array, changeRecords) {
275
+ function project(array, changes) {
313
276
  let splices = [];
314
- const initialSplices = createInitialSplices(changeRecords);
277
+ const initialSplices = [];
278
+ for (let i = 0, ii = changes.length; i < ii; i++) {
279
+ merge(changes[i], initialSplices);
280
+ }
315
281
  for (let i = 0, ii = initialSplices.length; i < ii; ++i) {
316
282
  const splice = initialSplices[i];
317
283
  if (splice.addedCount === 1 && splice.removed.length === 1) {
@@ -320,7 +286,115 @@ export function projectArraySplices(array, changeRecords) {
320
286
  }
321
287
  continue;
322
288
  }
323
- splices = splices.concat(calcSplices(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
289
+ splices = splices.concat(calc(array, splice.index, splice.index + splice.addedCount, splice.removed, 0, splice.removed.length));
324
290
  }
325
291
  return splices;
326
292
  }
293
+ /**
294
+ * A SpliceStrategy that attempts to merge all splices into the minimal set of
295
+ * splices needed to represent the change from the old array to the new array.
296
+ * @public
297
+ */
298
+ export const mergeSpliceStrategy = Object.freeze({
299
+ support: SpliceStrategySupport.optimized,
300
+ normalize(previous, current, changes) {
301
+ if (previous === void 0) {
302
+ if (changes === void 0) {
303
+ return emptyArray;
304
+ }
305
+ return changes.length > 1 ? project(current, changes) : changes;
306
+ }
307
+ return calc(current, 0, current.length, previous, 0, previous.length);
308
+ },
309
+ pop(array, observer, pop, args) {
310
+ const notEmpty = array.length > 0;
311
+ const result = pop.apply(array, args);
312
+ if (notEmpty) {
313
+ observer.addSplice(new Splice(array.length, [result], 0));
314
+ }
315
+ return result;
316
+ },
317
+ push(array, observer, push, args) {
318
+ const result = push.apply(array, args);
319
+ observer.addSplice(new Splice(array.length - args.length, [], args.length).adjustTo(array));
320
+ return result;
321
+ },
322
+ reverse(array, observer, reverse, args) {
323
+ observer.flush();
324
+ const oldArray = array.slice();
325
+ const result = reverse.apply(array, args);
326
+ observer.reset(oldArray);
327
+ return result;
328
+ },
329
+ shift(array, observer, shift, args) {
330
+ const notEmpty = array.length > 0;
331
+ const result = shift.apply(array, args);
332
+ if (notEmpty) {
333
+ observer.addSplice(new Splice(0, [result], 0));
334
+ }
335
+ return result;
336
+ },
337
+ sort(array, observer, sort, args) {
338
+ observer.flush();
339
+ const oldArray = array.slice();
340
+ const result = sort.apply(array, args);
341
+ observer.reset(oldArray);
342
+ return result;
343
+ },
344
+ splice(array, observer, splice, args) {
345
+ const result = splice.apply(array, args);
346
+ observer.addSplice(new Splice(+args[0], result, args.length > 2 ? args.length - 2 : 0).adjustTo(array));
347
+ return result;
348
+ },
349
+ unshift(array, observer, unshift, args) {
350
+ const result = unshift.apply(array, args);
351
+ observer.addSplice(new Splice(0, [], args.length).adjustTo(array));
352
+ return result;
353
+ },
354
+ });
355
+ /**
356
+ * A splice strategy that doesn't create splices, but instead
357
+ * tracks every change as a full array reset.
358
+ * @public
359
+ */
360
+ export const resetSpliceStrategy = Object.freeze({
361
+ support: SpliceStrategySupport.reset,
362
+ normalize(previous, current, changes) {
363
+ return SpliceStrategy.reset;
364
+ },
365
+ pop(array, observer, pop, args) {
366
+ const result = pop.apply(array, args);
367
+ observer.reset(array);
368
+ return result;
369
+ },
370
+ push(array, observer, push, args) {
371
+ const result = push.apply(array, args);
372
+ observer.reset(array);
373
+ return result;
374
+ },
375
+ reverse(array, observer, reverse, args) {
376
+ const result = reverse.apply(array, args);
377
+ observer.reset(array);
378
+ return result;
379
+ },
380
+ shift(array, observer, shift, args) {
381
+ const result = shift.apply(array, args);
382
+ observer.reset(array);
383
+ return result;
384
+ },
385
+ sort(array, observer, sort, args) {
386
+ const result = sort.apply(array, args);
387
+ observer.reset(array);
388
+ return result;
389
+ },
390
+ splice(array, observer, splice, args) {
391
+ const result = splice.apply(array, args);
392
+ observer.reset(array);
393
+ return result;
394
+ },
395
+ unshift(array, observer, unshift, args) {
396
+ const result = unshift.apply(array, args);
397
+ observer.reset(array);
398
+ return result;
399
+ },
400
+ });