vasille 5.1.9 → 6.0.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/dev/components.js +2 -2
- package/lib/dev/node.js +4 -4
- package/lib/dev/runner.js +5 -8
- package/lib/dev/state.js +1 -0
- package/lib/dev/views.js +46 -26
- package/lib/functional/safety.js +0 -1
- package/lib/index.js +3 -7
- package/lib/models/array-model.js +320 -33
- package/lib/models/listener.js +20 -38
- package/lib/models/map-model.js +82 -17
- package/lib/models/set-model.js +79 -17
- package/lib/node/node.js +75 -73
- package/lib/node/watch.js +1 -1
- package/lib/runner/web/binding/attribute.js +4 -4
- package/lib/runner/web/binding/class.js +2 -2
- package/lib/runner/web/binding/property.js +1 -1
- package/lib/runner/web/binding/style.js +3 -3
- package/lib/runner/web/runner.js +37 -9
- package/package.json +1 -1
- package/types/dev/components.d.ts +1 -1
- package/types/dev/index.d.ts +1 -1
- package/types/dev/inspectable.d.ts +4 -0
- package/types/dev/models.d.ts +1 -1
- package/types/dev/node.d.ts +2 -2
- package/types/dev/runner.d.ts +2 -3
- package/types/dev/views.d.ts +18 -13
- package/types/index.d.ts +3 -8
- package/types/models/array-model.d.ts +50 -4
- package/types/models/listener.d.ts +7 -28
- package/types/models/map-model.d.ts +25 -4
- package/types/models/set-model.d.ts +25 -6
- package/types/node/node.d.ts +26 -30
- package/types/runner/web/binding/attribute.d.ts +2 -2
- package/types/runner/web/binding/class.d.ts +5 -5
- package/types/runner/web/binding/property.d.ts +2 -2
- package/types/runner/web/binding/style.d.ts +3 -3
- package/types/runner/web/runner.d.ts +6 -2
- package/lib/models/model.js +0 -1
- package/lib/views/array-view.js +0 -17
- package/lib/views/base-view.js +0 -38
- package/lib/views/map-view.js +0 -14
- package/lib/views/repeat-node.js +0 -54
- package/lib/views/set-view.js +0 -17
- package/types/models/model.d.ts +0 -9
- package/types/views/array-view.d.ts +0 -13
- package/types/views/base-view.d.ts +0 -27
- package/types/views/map-view.d.ts +0 -11
- package/types/views/repeat-node.d.ts +0 -23
- package/types/views/set-view.d.ts +0 -12
package/lib/dev/components.js
CHANGED
|
@@ -70,9 +70,9 @@ export class DevSwitchedNode extends SwitchedNode {
|
|
|
70
70
|
time: Date.now(),
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
|
-
destroy() {
|
|
73
|
+
destroy(keepNodes) {
|
|
74
74
|
this.runner.inspector.destroy({ id: this.id, time: Date.now() });
|
|
75
|
-
super.destroy();
|
|
75
|
+
super.destroy(keepNodes);
|
|
76
76
|
}
|
|
77
77
|
newChild(index) {
|
|
78
78
|
const frag = new DevFragment(this.runner, null, null, "Case", { index });
|
package/lib/dev/node.js
CHANGED
|
@@ -43,17 +43,17 @@ export class DevFragment extends Fragment {
|
|
|
43
43
|
time: Date.now(),
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
-
destroy() {
|
|
46
|
+
destroy(keepNodes) {
|
|
47
47
|
this.inspector.destroy({ id: this.id, time: Date.now() });
|
|
48
|
-
super.destroy();
|
|
48
|
+
super.destroy(keepNodes);
|
|
49
49
|
}
|
|
50
|
-
|
|
50
|
+
push(node) {
|
|
51
51
|
if ("id" in node && typeof node.id == "number") {
|
|
52
52
|
this.inspector.setElementParent({
|
|
53
53
|
parent: this.id,
|
|
54
54
|
child: node.id,
|
|
55
55
|
});
|
|
56
56
|
}
|
|
57
|
-
super.
|
|
57
|
+
super.push(node);
|
|
58
58
|
}
|
|
59
59
|
}
|
package/lib/dev/runner.js
CHANGED
|
@@ -25,9 +25,9 @@ export class DevTextNode extends TextNode {
|
|
|
25
25
|
position: usage,
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
|
-
destroy() {
|
|
28
|
+
destroy(keepNodes) {
|
|
29
29
|
this.runner.inspector.destroy({ id: this.id, time: Date.now() });
|
|
30
|
-
super.destroy();
|
|
30
|
+
super.destroy(keepNodes);
|
|
31
31
|
}
|
|
32
32
|
compose() {
|
|
33
33
|
super.compose();
|
|
@@ -110,16 +110,13 @@ export class DevTag extends Tag {
|
|
|
110
110
|
}
|
|
111
111
|
super.applyOptions(options);
|
|
112
112
|
}
|
|
113
|
-
|
|
114
|
-
return this.node;
|
|
115
|
-
}
|
|
116
|
-
destroy() {
|
|
113
|
+
destroy(keepNodes) {
|
|
117
114
|
this.runner.inspector.destroy({ id: this.id, time: Date.now() });
|
|
118
|
-
super.destroy();
|
|
115
|
+
super.destroy(keepNodes);
|
|
119
116
|
}
|
|
120
117
|
compose() {
|
|
121
118
|
super.compose();
|
|
122
|
-
Object.defineProperty(this.
|
|
119
|
+
Object.defineProperty(this.node, "vasille", { value: this.id, configurable: false, enumerable: false });
|
|
123
120
|
}
|
|
124
121
|
}
|
|
125
122
|
export class DevRunner extends Runner {
|
package/lib/dev/state.js
CHANGED
package/lib/dev/views.js
CHANGED
|
@@ -1,62 +1,82 @@
|
|
|
1
|
-
import { ArrayView } from "../
|
|
2
|
-
import { MapView } from "../
|
|
3
|
-
import { SetView } from "../
|
|
1
|
+
import { ArrayView, SinglePassArrayView } from "../models/array-model.js";
|
|
2
|
+
import { MapView } from "../models/map-model.js";
|
|
3
|
+
import { SetView } from "../models/set-model.js";
|
|
4
|
+
import { Reference } from "../value/reference.js";
|
|
4
5
|
import { provideId, toDevObject } from "./inspectable.js";
|
|
5
6
|
import { DevFragment } from "./node.js";
|
|
7
|
+
import { DevReference } from "./state.js";
|
|
8
|
+
function createDevFragment(runner, host) {
|
|
9
|
+
const frag = new DevFragment(runner, null, null, "Fragment", {});
|
|
10
|
+
runner.inspector.setElementParent({ parent: host.id, child: frag.id });
|
|
11
|
+
return frag;
|
|
12
|
+
}
|
|
6
13
|
export class DevArrayView extends ArrayView {
|
|
7
14
|
id;
|
|
8
|
-
constructor(
|
|
9
|
-
super(
|
|
15
|
+
constructor(runner, model, slot, usage, indexDeclaration) {
|
|
16
|
+
super(runner, model, slot, v => (indexDeclaration ? new DevReference(v, indexDeclaration, runner.inspector) : new Reference(v)), runner => createDevFragment(runner, this));
|
|
10
17
|
this.id = provideId();
|
|
11
18
|
runner.inspector.createComponent({
|
|
12
19
|
id: this.id,
|
|
13
20
|
name: "ArrayView",
|
|
14
|
-
props: toDevObject(
|
|
21
|
+
props: toDevObject({ model }),
|
|
22
|
+
usage: usage,
|
|
23
|
+
time: Date.now(),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class DevSinglePassArrayView extends SinglePassArrayView {
|
|
28
|
+
id;
|
|
29
|
+
constructor(runner, model, key, slot, usage, valueDeclaration, indexDeclaration) {
|
|
30
|
+
super(runner, model, key, slot, v => new DevReference(v, valueDeclaration, runner.inspector), v => new DevReference(v, indexDeclaration, runner.inspector), runner => createDevFragment(runner, this));
|
|
31
|
+
this.id = provideId();
|
|
32
|
+
runner.inspector.createComponent({
|
|
33
|
+
id: this.id,
|
|
34
|
+
name: "SinglePassArrayView",
|
|
35
|
+
props: toDevObject({ model }),
|
|
15
36
|
usage: usage,
|
|
16
37
|
time: Date.now(),
|
|
17
38
|
});
|
|
18
39
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
40
|
+
}
|
|
41
|
+
export class DevMultiPassArrayView extends SinglePassArrayView {
|
|
42
|
+
id;
|
|
43
|
+
constructor(runner, model, key, slot, usage, valueDeclaration, indexDeclaration) {
|
|
44
|
+
super(runner, model, key, slot, v => new DevReference(v, valueDeclaration, runner.inspector), v => new DevReference(v, indexDeclaration, runner.inspector), runner => createDevFragment(runner, this));
|
|
45
|
+
this.id = provideId();
|
|
46
|
+
runner.inspector.createComponent({
|
|
47
|
+
id: this.id,
|
|
48
|
+
name: "MultiPassArrayView",
|
|
49
|
+
props: toDevObject({ model }),
|
|
50
|
+
usage: usage,
|
|
51
|
+
time: Date.now(),
|
|
52
|
+
});
|
|
23
53
|
}
|
|
24
54
|
}
|
|
25
55
|
export class DevSetView extends SetView {
|
|
26
56
|
id;
|
|
27
|
-
constructor(
|
|
28
|
-
super(
|
|
57
|
+
constructor(runner, model, slot, usage) {
|
|
58
|
+
super(runner, model, slot, runner => createDevFragment(runner, this));
|
|
29
59
|
this.id = provideId();
|
|
30
60
|
runner.inspector.createComponent({
|
|
31
61
|
id: this.id,
|
|
32
62
|
name: "SetView",
|
|
33
|
-
props: toDevObject(
|
|
63
|
+
props: toDevObject({ model }),
|
|
34
64
|
usage: usage,
|
|
35
65
|
time: Date.now(),
|
|
36
66
|
});
|
|
37
67
|
}
|
|
38
|
-
newChild(_id, item) {
|
|
39
|
-
const frag = new DevFragment(this.runner, null, null, "SetViewItem", { item });
|
|
40
|
-
this.runner.inspector.setElementParent({ parent: this.id, child: frag.id });
|
|
41
|
-
return frag;
|
|
42
|
-
}
|
|
43
68
|
}
|
|
44
69
|
export class DevMapView extends MapView {
|
|
45
70
|
id;
|
|
46
|
-
constructor(
|
|
47
|
-
super(
|
|
71
|
+
constructor(runner, model, slot, usage, valueDeclaration) {
|
|
72
|
+
super(runner, model, slot, v => (valueDeclaration ? new DevReference(v, valueDeclaration, runner.inspector) : new Reference(v)), runner => createDevFragment(runner, this));
|
|
48
73
|
this.id = provideId();
|
|
49
74
|
runner.inspector.createComponent({
|
|
50
75
|
id: this.id,
|
|
51
76
|
name: "MapView",
|
|
52
|
-
props: toDevObject(
|
|
77
|
+
props: toDevObject({ model }),
|
|
53
78
|
usage: usage,
|
|
54
79
|
time: Date.now(),
|
|
55
80
|
});
|
|
56
81
|
}
|
|
57
|
-
newChild(key, value) {
|
|
58
|
-
const frag = new DevFragment(this.runner, null, null, "MapViewItem", { key, value });
|
|
59
|
-
this.runner.inspector.setElementParent({ parent: this.id, child: frag.id });
|
|
60
|
-
return frag;
|
|
61
|
-
}
|
|
62
82
|
}
|
package/lib/functional/safety.js
CHANGED
package/lib/index.js
CHANGED
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
export { Reactive } from "./core/core.js";
|
|
2
2
|
export { IValue } from "./core/ivalue.js";
|
|
3
3
|
export { reportError, setErrorHandler, safe } from "./functional/safety.js";
|
|
4
|
-
export { ArrayModel } from "./models/array-model.js";
|
|
4
|
+
export { ArrayModel, SinglePassArrayView, ArrayView } from "./models/array-model.js";
|
|
5
5
|
export { Listener } from "./models/listener.js";
|
|
6
|
-
export { MapModel } from "./models/map-model.js";
|
|
7
|
-
export { SetModel } from "./models/set-model.js";
|
|
6
|
+
export { MapModel, MapView } from "./models/map-model.js";
|
|
7
|
+
export { SetModel, SetView } from "./models/set-model.js";
|
|
8
8
|
export { App, Portal } from "./node/app.js";
|
|
9
9
|
export { Fragment, Tag, TextNode, SwitchedNode } from "./node/node.js";
|
|
10
10
|
export { Expression } from "./value/expression.js";
|
|
11
11
|
export { Reference } from "./value/reference.js";
|
|
12
|
-
export { ArrayView } from "./views/array-view.js";
|
|
13
|
-
export { BaseView } from "./views/base-view.js";
|
|
14
|
-
export { MapView } from "./views/map-view.js";
|
|
15
|
-
export { SetView } from "./views/set-view.js";
|
|
16
12
|
export { userError } from "./core/errors.js";
|
|
17
13
|
export { Watch } from "./node/watch.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Fragment } from "../node/node.js";
|
|
1
2
|
import { Listener } from "./listener.js";
|
|
2
3
|
/**
|
|
3
4
|
* Model based on Array class
|
|
@@ -11,12 +12,10 @@ export class ArrayModel extends Array {
|
|
|
11
12
|
* @param ctx lifetime context of model
|
|
12
13
|
*/
|
|
13
14
|
constructor(data, ctx) {
|
|
14
|
-
super();
|
|
15
|
+
super(typeof data === "number" ? data : 0);
|
|
15
16
|
this.listener = new Listener();
|
|
16
17
|
if (data instanceof Array) {
|
|
17
|
-
|
|
18
|
-
super.push(data[i]);
|
|
19
|
-
}
|
|
18
|
+
super.push(...data);
|
|
20
19
|
}
|
|
21
20
|
ctx?.bind(this);
|
|
22
21
|
}
|
|
@@ -37,10 +36,9 @@ export class ArrayModel extends Array {
|
|
|
37
36
|
end = this.length;
|
|
38
37
|
}
|
|
39
38
|
for (let i = start; i < end; i++) {
|
|
40
|
-
this.listener.emitRemoved(this[i], this[i]);
|
|
41
39
|
this[i] = value;
|
|
42
|
-
this.listener.emitAdded(value, value);
|
|
43
40
|
}
|
|
41
|
+
this.listener.emit(0, this.length, this);
|
|
44
42
|
return this;
|
|
45
43
|
}
|
|
46
44
|
/**
|
|
@@ -50,9 +48,8 @@ export class ArrayModel extends Array {
|
|
|
50
48
|
pop() {
|
|
51
49
|
/* istanbul ignore else */
|
|
52
50
|
if (this.length > 0) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
return v;
|
|
51
|
+
this.listener.emit(this.length - 1, 1);
|
|
52
|
+
return super.pop();
|
|
56
53
|
}
|
|
57
54
|
}
|
|
58
55
|
/**
|
|
@@ -61,10 +58,8 @@ export class ArrayModel extends Array {
|
|
|
61
58
|
* @return {number} new length of the array
|
|
62
59
|
*/
|
|
63
60
|
push(...items) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
super.push(item);
|
|
67
|
-
});
|
|
61
|
+
this.listener.emit(this.length, 0, items);
|
|
62
|
+
super.push(...items);
|
|
68
63
|
return this.length;
|
|
69
64
|
}
|
|
70
65
|
/**
|
|
@@ -74,9 +69,8 @@ export class ArrayModel extends Array {
|
|
|
74
69
|
shift() {
|
|
75
70
|
/* istanbul ignore else */
|
|
76
71
|
if (this.length > 0) {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
return v;
|
|
72
|
+
this.listener.emit(0, 1);
|
|
73
|
+
return super.shift();
|
|
80
74
|
}
|
|
81
75
|
}
|
|
82
76
|
/**
|
|
@@ -89,18 +83,8 @@ export class ArrayModel extends Array {
|
|
|
89
83
|
splice(start, deleteCount, ...items) {
|
|
90
84
|
start = Math.min(start, this.length);
|
|
91
85
|
deleteCount = typeof deleteCount === "number" ? deleteCount : this.length - start;
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
const index = start + deleteCount - i - 1;
|
|
95
|
-
/* istanbul ignore else */
|
|
96
|
-
if (this[index] !== undefined) {
|
|
97
|
-
this.listener.emitRemoved(this[index], this[index]);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
for (let i = 0; i < items.length; i++) {
|
|
101
|
-
this.listener.emitAdded(before, items[i]);
|
|
102
|
-
}
|
|
103
|
-
return new ArrayModel(super.splice(start, deleteCount, ...items));
|
|
86
|
+
this.listener.emit(start, deleteCount, items);
|
|
87
|
+
return super.splice(start, deleteCount, ...items);
|
|
104
88
|
}
|
|
105
89
|
/**
|
|
106
90
|
* Calls Array.unshift and notify about changed
|
|
@@ -108,14 +92,11 @@ export class ArrayModel extends Array {
|
|
|
108
92
|
* @return {number} the length after prepending
|
|
109
93
|
*/
|
|
110
94
|
unshift(...items) {
|
|
111
|
-
|
|
112
|
-
this.listener.emitAdded(this[i], items[i]);
|
|
113
|
-
}
|
|
95
|
+
this.listener.emit(0, 0, items);
|
|
114
96
|
return super.unshift(...items);
|
|
115
97
|
}
|
|
116
98
|
replace(at, with_) {
|
|
117
|
-
this.listener.
|
|
118
|
-
this.listener.emitRemoved(this[at], this[at]);
|
|
99
|
+
this.listener.emit(at, 1, [with_]);
|
|
119
100
|
this[at] = with_;
|
|
120
101
|
return this;
|
|
121
102
|
}
|
|
@@ -123,3 +104,309 @@ export class ArrayModel extends Array {
|
|
|
123
104
|
this.splice(0);
|
|
124
105
|
}
|
|
125
106
|
}
|
|
107
|
+
class BaseArrayView extends Fragment {
|
|
108
|
+
cache = [];
|
|
109
|
+
unmount(keepStructure) {
|
|
110
|
+
super.unmount(keepStructure);
|
|
111
|
+
this.cache.forEach(item => item.frag.unmount(true));
|
|
112
|
+
}
|
|
113
|
+
remount() {
|
|
114
|
+
for (let i = this.cache.length - 1; i >= 0; i--) {
|
|
115
|
+
this.cache[i].frag.remount();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
destroy(keepNodes) {
|
|
119
|
+
for (let i = this.cache.length - 1; i >= 0; i--) {
|
|
120
|
+
this.cache[i]?.frag.destroy(keepNodes);
|
|
121
|
+
}
|
|
122
|
+
super.destroy(keepNodes);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export class ArrayView extends BaseArrayView {
|
|
126
|
+
model;
|
|
127
|
+
slot;
|
|
128
|
+
ref;
|
|
129
|
+
frag;
|
|
130
|
+
apply;
|
|
131
|
+
constructor(runner, model, slot, ref, frag) {
|
|
132
|
+
super(runner);
|
|
133
|
+
this.model = model;
|
|
134
|
+
this.slot = slot;
|
|
135
|
+
this.ref = ref;
|
|
136
|
+
this.frag = frag;
|
|
137
|
+
}
|
|
138
|
+
compose() {
|
|
139
|
+
const view = this;
|
|
140
|
+
function apply(index, remove, values) {
|
|
141
|
+
const children = view.cache;
|
|
142
|
+
const toInsert = [];
|
|
143
|
+
const prev = children[index - 1]?.frag;
|
|
144
|
+
const next = children[index + remove]?.frag;
|
|
145
|
+
const length = values?.length || 0;
|
|
146
|
+
const lastIndex = length - 1;
|
|
147
|
+
// create new fragments
|
|
148
|
+
for (let i = 0; i < length; i++) {
|
|
149
|
+
toInsert[i] = {
|
|
150
|
+
frag: view.frag(view.runner),
|
|
151
|
+
index: view.ref(i + index),
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// locate new fragments
|
|
155
|
+
for (let i = 0; i < length; i++) {
|
|
156
|
+
toInsert[i].frag.link(view, i === 0 ? prev : toInsert[i - 1].frag, i === lastIndex ? next : toInsert[i + 1].frag);
|
|
157
|
+
}
|
|
158
|
+
// destroy removed nodes
|
|
159
|
+
for (let i = 0; i < remove; i++) {
|
|
160
|
+
children[index + i]?.frag.destroy();
|
|
161
|
+
}
|
|
162
|
+
// modify the cache
|
|
163
|
+
children.splice(index, remove, ...toInsert);
|
|
164
|
+
// update indexes of affected items in the cache
|
|
165
|
+
for (let i = index + length; i < children.length; i++) {
|
|
166
|
+
children[i].index.V = i;
|
|
167
|
+
}
|
|
168
|
+
// render new fragments content in reverse order
|
|
169
|
+
for (let i = lastIndex; i >= 0; i--) {
|
|
170
|
+
view.slot(toInsert[i].frag, values[i], toInsert[i].index);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
apply(0, 0, this.model);
|
|
174
|
+
this.model.listener.on(apply);
|
|
175
|
+
this.apply = apply;
|
|
176
|
+
}
|
|
177
|
+
destroy(keepNodes) {
|
|
178
|
+
/* istanbul ignore else */
|
|
179
|
+
if (this.apply) {
|
|
180
|
+
this.model.listener.off(this.apply);
|
|
181
|
+
}
|
|
182
|
+
super.destroy(keepNodes);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export class DiffingArrayView extends BaseArrayView {
|
|
186
|
+
model;
|
|
187
|
+
key;
|
|
188
|
+
slot;
|
|
189
|
+
vRef;
|
|
190
|
+
iRef;
|
|
191
|
+
frag;
|
|
192
|
+
match;
|
|
193
|
+
constructor(runner, model, key, slot, vRef, iRef, frag) {
|
|
194
|
+
super(runner);
|
|
195
|
+
this.model = model;
|
|
196
|
+
this.key = key;
|
|
197
|
+
this.slot = slot;
|
|
198
|
+
this.vRef = vRef;
|
|
199
|
+
this.iRef = iRef;
|
|
200
|
+
this.frag = frag;
|
|
201
|
+
}
|
|
202
|
+
addChild(newCache, key, modelItem, prev, next) {
|
|
203
|
+
const frag = this.frag(this.runner);
|
|
204
|
+
const index = this.iRef(newCache.length);
|
|
205
|
+
const value = this.vRef(modelItem);
|
|
206
|
+
const cache = {
|
|
207
|
+
frag: frag,
|
|
208
|
+
key: key,
|
|
209
|
+
value: value,
|
|
210
|
+
moved: false,
|
|
211
|
+
unmounted: false,
|
|
212
|
+
index,
|
|
213
|
+
};
|
|
214
|
+
frag.link(this, prev, next);
|
|
215
|
+
this.slot(frag, value, index);
|
|
216
|
+
newCache.push(cache);
|
|
217
|
+
return cache;
|
|
218
|
+
}
|
|
219
|
+
destroy(keepNodes) {
|
|
220
|
+
/* istanbul ignore else */
|
|
221
|
+
if (this.match) {
|
|
222
|
+
this.model.off(this.match);
|
|
223
|
+
}
|
|
224
|
+
super.destroy(keepNodes);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
export class SinglePassArrayView extends DiffingArrayView {
|
|
228
|
+
existing = new Map();
|
|
229
|
+
compose() {
|
|
230
|
+
const view = this;
|
|
231
|
+
const { existing } = this;
|
|
232
|
+
function match(model) {
|
|
233
|
+
const children = view.cache;
|
|
234
|
+
const newCache = [];
|
|
235
|
+
const unmounted = new Set();
|
|
236
|
+
const modelLength = model.length;
|
|
237
|
+
const cacheLength = children.length;
|
|
238
|
+
let modelIndex = 0;
|
|
239
|
+
let cacheIndex = 0;
|
|
240
|
+
while (modelIndex <= modelLength &&
|
|
241
|
+
cacheIndex <= cacheLength &&
|
|
242
|
+
!(modelIndex == modelLength && cacheIndex == cacheLength)) {
|
|
243
|
+
const modelItem = model[modelIndex];
|
|
244
|
+
const cacheItem = children[cacheIndex];
|
|
245
|
+
const key = modelItem ? view.key(modelItem) : null;
|
|
246
|
+
const prev = newCache[modelIndex - 1];
|
|
247
|
+
let present;
|
|
248
|
+
// ideal case, a match
|
|
249
|
+
if (key === cacheItem?.key) {
|
|
250
|
+
cacheItem.value.V = modelItem;
|
|
251
|
+
cacheItem.index.V = modelIndex;
|
|
252
|
+
newCache.push(cacheItem);
|
|
253
|
+
modelIndex++;
|
|
254
|
+
cacheIndex++;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
// skip already unmounted items
|
|
258
|
+
if (cacheItem?.moved || !modelItem) {
|
|
259
|
+
cacheIndex++;
|
|
260
|
+
/* istanbul ignore else */
|
|
261
|
+
if (cacheItem) {
|
|
262
|
+
if (cacheItem.moved) {
|
|
263
|
+
cacheItem.moved = false;
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
cacheItem.frag.unlink();
|
|
267
|
+
unmounted.add(cacheItem);
|
|
268
|
+
cacheItem.unmounted = true;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// the item is present in another position, move it
|
|
273
|
+
else if ((present = existing.get(key))) {
|
|
274
|
+
const frag = present.frag;
|
|
275
|
+
const presentIndex = present.index;
|
|
276
|
+
const presentValue = present.value;
|
|
277
|
+
const indexDiff = presentIndex.V - cacheIndex;
|
|
278
|
+
const removeLimit = modelLength < cacheLength ? cacheLength - modelLength + 2 : 4;
|
|
279
|
+
// remove items between if less than 5
|
|
280
|
+
if (indexDiff <= removeLimit && indexDiff > 0) {
|
|
281
|
+
while (cacheIndex < presentIndex.V) {
|
|
282
|
+
const item = children[cacheIndex];
|
|
283
|
+
item.frag.unmount(false);
|
|
284
|
+
item.unmounted = true;
|
|
285
|
+
unmounted.add(item);
|
|
286
|
+
cacheIndex++;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
if (!present.unmounted) {
|
|
291
|
+
frag.unmount(false);
|
|
292
|
+
}
|
|
293
|
+
frag.link(view, prev?.frag, cacheItem?.frag);
|
|
294
|
+
frag.remount();
|
|
295
|
+
presentIndex.V = modelIndex;
|
|
296
|
+
presentValue.V = modelItem;
|
|
297
|
+
present.moved = indexDiff > 0;
|
|
298
|
+
newCache.push(present);
|
|
299
|
+
modelIndex++;
|
|
300
|
+
// if was unmounted, remove it from the unmounted set
|
|
301
|
+
if (present.unmounted) {
|
|
302
|
+
unmounted.delete(present);
|
|
303
|
+
present.unmounted = false;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// add missing items
|
|
308
|
+
else {
|
|
309
|
+
/* istanbul ignore else */
|
|
310
|
+
if (key !== null && modelItem) {
|
|
311
|
+
existing.set(key, view.addChild(newCache, key, modelItem, prev?.frag, cacheItem?.frag));
|
|
312
|
+
modelIndex++;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
// destroy removed nodes
|
|
317
|
+
unmounted.forEach(item => {
|
|
318
|
+
existing.delete(item.key);
|
|
319
|
+
item.frag.destroy();
|
|
320
|
+
});
|
|
321
|
+
view.cache = newCache;
|
|
322
|
+
}
|
|
323
|
+
match(this.model.V);
|
|
324
|
+
this.model.on(match);
|
|
325
|
+
this.match = match;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
// export class MultiPassArrayView<
|
|
329
|
+
// T,
|
|
330
|
+
// Node,
|
|
331
|
+
// Element,
|
|
332
|
+
// TagOptions extends object,
|
|
333
|
+
// Runner extends IRunner<Node, Element, TagOptions>,
|
|
334
|
+
// > extends DiffingArrayView<T, Node, Element, TagOptions, Runner> {
|
|
335
|
+
// public override compose() {
|
|
336
|
+
// const view = this;
|
|
337
|
+
//
|
|
338
|
+
// function match(model: T[]) {
|
|
339
|
+
// const children = view.cache;
|
|
340
|
+
// const newCache: KeyedCacheItem<T, Node, Element, TagOptions, Runner>[] = [];
|
|
341
|
+
// const modelKeys = new Set<number | string>();
|
|
342
|
+
// const modelData: { item: T; key: number | string }[] = [];
|
|
343
|
+
//
|
|
344
|
+
// // pass 1, transform model data to keyed data
|
|
345
|
+
// for (let i = 0; i < model.length; i++) {
|
|
346
|
+
// const modelItem = model[i] as T;
|
|
347
|
+
// const key = view.key(modelItem);
|
|
348
|
+
// modelKeys.add(key);
|
|
349
|
+
// modelData.push({ item: modelItem, key });
|
|
350
|
+
// }
|
|
351
|
+
//
|
|
352
|
+
// let gt = 0,
|
|
353
|
+
// lt = 0;
|
|
354
|
+
//
|
|
355
|
+
// // pass 2, destroy unexisting items
|
|
356
|
+
// for (let i = 0; i < children.length; i++) {
|
|
357
|
+
// const cacheItem = children[i]!;
|
|
358
|
+
// const key = cacheItem.key;
|
|
359
|
+
//
|
|
360
|
+
// if (!modelKeys.has(key)) {
|
|
361
|
+
// const { next, prev } = cacheItem.frag;
|
|
362
|
+
//
|
|
363
|
+
// if (prev) {
|
|
364
|
+
// prev.next = next;
|
|
365
|
+
// }
|
|
366
|
+
// if (next) {
|
|
367
|
+
// next.prev = prev;
|
|
368
|
+
// }
|
|
369
|
+
//
|
|
370
|
+
// cacheItem.unmounted = true;
|
|
371
|
+
// cacheItem.frag.destroy();
|
|
372
|
+
// }
|
|
373
|
+
// }
|
|
374
|
+
//
|
|
375
|
+
// const modelLength = model.length;
|
|
376
|
+
// const cacheLength = children.length;
|
|
377
|
+
// let modelIndex = 0;
|
|
378
|
+
// let cacheIndex = 0;
|
|
379
|
+
//
|
|
380
|
+
// // pass 3, create new items
|
|
381
|
+
// while (modelIndex < modelLength && cacheIndex <= cacheLength) {
|
|
382
|
+
// const modelItem = modelData[modelIndex]!;
|
|
383
|
+
// const cacheItem = children[cacheIndex];
|
|
384
|
+
// const key = modelItem.key;
|
|
385
|
+
//
|
|
386
|
+
// // ideal case, a match
|
|
387
|
+
// if (key === cacheItem?.key) {
|
|
388
|
+
// cacheItem.value.V = modelItem.item;
|
|
389
|
+
// cacheItem.index.V = modelIndex;
|
|
390
|
+
// newCache.push(cacheItem);
|
|
391
|
+
// modelIndex++;
|
|
392
|
+
// cacheIndex++;
|
|
393
|
+
// }
|
|
394
|
+
// // skip unmounted items in step 2
|
|
395
|
+
// else if (cacheItem?.unmounted) {
|
|
396
|
+
// cacheIndex++;
|
|
397
|
+
// }
|
|
398
|
+
// // add missing items
|
|
399
|
+
// else {
|
|
400
|
+
// view.addChild(newCache, key, modelItem.item, newCache[modelIndex - 1]?.frag, cacheItem?.frag);
|
|
401
|
+
// modelIndex++;
|
|
402
|
+
// }
|
|
403
|
+
// }
|
|
404
|
+
//
|
|
405
|
+
// view.cache = newCache;
|
|
406
|
+
// }
|
|
407
|
+
//
|
|
408
|
+
// match(this.model.V);
|
|
409
|
+
// this.model.on(match);
|
|
410
|
+
// this.match = match;
|
|
411
|
+
// }
|
|
412
|
+
// }
|