rask-ui 0.2.1 → 0.2.3
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/dist/observation.d.ts +1 -2
- package/dist/observation.d.ts.map +1 -1
- package/dist/observation.js +3 -16
- package/dist/observation.test.js +109 -146
- package/dist/tests/observation.test.js +0 -37
- package/dist/tests/patchChildren.test.js +187 -48
- package/dist/vdom/AbstractVNode.d.ts +21 -2
- package/dist/vdom/AbstractVNode.d.ts.map +1 -1
- package/dist/vdom/AbstractVNode.js +166 -17
- package/dist/vdom/ComponentVNode.d.ts +0 -1
- package/dist/vdom/ComponentVNode.d.ts.map +1 -1
- package/dist/vdom/ComponentVNode.js +26 -18
- package/dist/vdom/ElementVNode.d.ts +0 -1
- package/dist/vdom/ElementVNode.d.ts.map +1 -1
- package/dist/vdom/ElementVNode.js +3 -11
- package/dist/vdom/FragmentVNode.d.ts +0 -1
- package/dist/vdom/FragmentVNode.d.ts.map +1 -1
- package/dist/vdom/FragmentVNode.js +3 -4
- package/dist/vdom/RootVNode.d.ts +5 -2
- package/dist/vdom/RootVNode.d.ts.map +1 -1
- package/dist/vdom/RootVNode.js +17 -5
- package/dist/vdom/TextVNode.d.ts +0 -1
- package/dist/vdom/TextVNode.d.ts.map +1 -1
- package/dist/vdom/TextVNode.js +3 -1
- package/package.json +1 -1
|
@@ -1,9 +1,77 @@
|
|
|
1
|
+
import { elementsToFragment } from "./dom-utils";
|
|
1
2
|
export class AbstractVNode {
|
|
2
3
|
key;
|
|
3
4
|
parent;
|
|
4
5
|
root;
|
|
5
6
|
elm;
|
|
6
7
|
children;
|
|
8
|
+
applyDOMOperations(operations, atVNode) {
|
|
9
|
+
if (!this.elm || !this.children) {
|
|
10
|
+
this.parent?.applyDOMOperations(operations, this);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
console.log(operations);
|
|
14
|
+
operations.forEach((operation) => {
|
|
15
|
+
switch (operation.type) {
|
|
16
|
+
case "insert": {
|
|
17
|
+
const fragment = elementsToFragment(operation.elms);
|
|
18
|
+
// Insert after afterElm (or at start if undefined)
|
|
19
|
+
if (operation.afterElm === undefined) {
|
|
20
|
+
// Insert at the start
|
|
21
|
+
this.elm.insertBefore(fragment, this.elm.firstChild);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
// Insert after afterElm
|
|
25
|
+
const target = operation.afterElm.nextSibling;
|
|
26
|
+
this.elm.insertBefore(fragment, target);
|
|
27
|
+
}
|
|
28
|
+
break;
|
|
29
|
+
}
|
|
30
|
+
case "remove": {
|
|
31
|
+
const elms = operation.elms;
|
|
32
|
+
if (elms.length === 1) {
|
|
33
|
+
this.elm.removeChild(elms[0]);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const range = new Range();
|
|
37
|
+
range.setStartBefore(elms[0]);
|
|
38
|
+
range.setEndAfter(elms[elms.length - 1]);
|
|
39
|
+
range.deleteContents();
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
case "replace": {
|
|
44
|
+
const oldElms = operation.oldElms;
|
|
45
|
+
const newElms = operation.newElms;
|
|
46
|
+
if (oldElms.length === 1) {
|
|
47
|
+
this.elm.replaceChild(elementsToFragment(newElms), oldElms[0]);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
const range = new Range();
|
|
51
|
+
range.setStartBefore(oldElms[0]);
|
|
52
|
+
range.setEndAfter(oldElms[oldElms.length - 1]);
|
|
53
|
+
range.deleteContents();
|
|
54
|
+
range.insertNode(elementsToFragment(newElms));
|
|
55
|
+
}
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
case "move": {
|
|
59
|
+
const fragment = elementsToFragment(operation.elms);
|
|
60
|
+
// Insert after afterElm (or at start if undefined)
|
|
61
|
+
if (operation.afterElm === undefined) {
|
|
62
|
+
// Insert at the start
|
|
63
|
+
this.elm.insertBefore(fragment, this.elm.firstChild);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
// Insert after afterElm
|
|
67
|
+
const target = operation.afterElm.nextSibling;
|
|
68
|
+
this.elm.insertBefore(fragment, target);
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
7
75
|
getHTMLElement() {
|
|
8
76
|
if (!this.elm || !(this.elm instanceof HTMLElement)) {
|
|
9
77
|
throw new Error("This VNode does not have an HTMLElement");
|
|
@@ -18,6 +86,7 @@ export class AbstractVNode {
|
|
|
18
86
|
return [this.elm];
|
|
19
87
|
}
|
|
20
88
|
if (!this.children) {
|
|
89
|
+
console.log("WTF", this);
|
|
21
90
|
throw new Error("This VNode has no element or children");
|
|
22
91
|
}
|
|
23
92
|
return this.children.map((child) => child.getElements()).flat();
|
|
@@ -55,52 +124,132 @@ export class AbstractVNode {
|
|
|
55
124
|
patchChildren(newChildren) {
|
|
56
125
|
const prevChildren = this.children;
|
|
57
126
|
// When there are only new children, we just mount them
|
|
58
|
-
if (
|
|
127
|
+
if (prevChildren.length === 0) {
|
|
59
128
|
newChildren.forEach((child) => child.mount(this));
|
|
60
|
-
return
|
|
129
|
+
return {
|
|
130
|
+
children: newChildren,
|
|
131
|
+
operations: [
|
|
132
|
+
{
|
|
133
|
+
type: "insert",
|
|
134
|
+
elms: newChildren.map((child) => child.getElements()).flat(),
|
|
135
|
+
},
|
|
136
|
+
],
|
|
137
|
+
};
|
|
61
138
|
}
|
|
62
139
|
// If we want to remove all children, we just unmount the previous ones
|
|
63
140
|
if (!newChildren.length && prevChildren.length) {
|
|
64
141
|
prevChildren.forEach((child) => child.unmount());
|
|
65
|
-
return
|
|
142
|
+
return {
|
|
143
|
+
children: [],
|
|
144
|
+
operations: [
|
|
145
|
+
{
|
|
146
|
+
type: "remove",
|
|
147
|
+
elms: prevChildren.map((child) => child.getElements()).flat(),
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
66
151
|
}
|
|
152
|
+
const operations = [];
|
|
67
153
|
const oldKeys = {};
|
|
154
|
+
// Build oldKeys map and handle duplicate keys
|
|
68
155
|
prevChildren.forEach((prevChild, index) => {
|
|
69
|
-
|
|
156
|
+
const key = prevChild.key || index;
|
|
157
|
+
// If key already exists, we have a duplicate - unmount the old one immediately
|
|
158
|
+
if (oldKeys[key]) {
|
|
159
|
+
oldKeys[key].unmount();
|
|
160
|
+
operations.push({ type: "remove", elms: oldKeys[key].getElements() });
|
|
161
|
+
}
|
|
162
|
+
oldKeys[key] = prevChild;
|
|
70
163
|
});
|
|
71
|
-
//
|
|
164
|
+
// Helper to get afterElm for a position in result array
|
|
165
|
+
const getAfterElm = (index, result) => {
|
|
166
|
+
let currentIndex = index;
|
|
167
|
+
let prevChild = result[--currentIndex];
|
|
168
|
+
let afterElm;
|
|
169
|
+
while (prevChild) {
|
|
170
|
+
const prevElms = prevChild.getElements();
|
|
171
|
+
afterElm = prevElms[prevElms.length - 1];
|
|
172
|
+
if (afterElm) {
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
prevChild = result[--currentIndex];
|
|
176
|
+
}
|
|
177
|
+
return afterElm;
|
|
178
|
+
};
|
|
179
|
+
// === PASS 1: Build result array (mount/patch all children) ===
|
|
72
180
|
const result = [];
|
|
181
|
+
const newChildrenMeta = [];
|
|
73
182
|
newChildren.forEach((newChild, index) => {
|
|
74
183
|
const key = newChild.key || index;
|
|
75
|
-
const
|
|
76
|
-
if (!
|
|
184
|
+
const oldChild = oldKeys[key];
|
|
185
|
+
if (!oldChild) {
|
|
77
186
|
// New child - mount and add to result
|
|
78
187
|
newChild.mount(this);
|
|
79
188
|
result.push(newChild);
|
|
189
|
+
newChildrenMeta.push({ isNew: true });
|
|
80
190
|
}
|
|
81
|
-
else if (
|
|
82
|
-
// Same instance - no patching needed, just reuse
|
|
83
|
-
result.push(prevChild);
|
|
84
|
-
delete oldKeys[key];
|
|
85
|
-
}
|
|
86
|
-
else if (this.canPatch(prevChild, newChild)) {
|
|
191
|
+
else if (this.canPatch(oldChild, newChild)) {
|
|
87
192
|
// Compatible types - patch and reuse old VNode
|
|
88
|
-
|
|
89
|
-
|
|
193
|
+
if (oldChild !== newChild) {
|
|
194
|
+
oldChild.patch(newChild);
|
|
195
|
+
}
|
|
196
|
+
result.push(oldChild);
|
|
90
197
|
delete oldKeys[key];
|
|
198
|
+
newChildrenMeta.push({ isNew: false });
|
|
91
199
|
}
|
|
92
200
|
else {
|
|
93
201
|
// Incompatible types - replace completely
|
|
94
202
|
newChild.mount(this);
|
|
95
|
-
|
|
203
|
+
oldChild.unmount();
|
|
96
204
|
result.push(newChild);
|
|
97
205
|
delete oldKeys[key];
|
|
206
|
+
newChildrenMeta.push({ isNew: true, replacedOld: oldChild });
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
// === PASS 2: Generate operations by comparing positions ===
|
|
210
|
+
result.forEach((child, newIndex) => {
|
|
211
|
+
const oldIndex = prevChildren.indexOf(child);
|
|
212
|
+
const meta = newChildrenMeta[newIndex];
|
|
213
|
+
if (meta.isNew) {
|
|
214
|
+
// New child - generate insert operation
|
|
215
|
+
const afterElm = getAfterElm(newIndex, result);
|
|
216
|
+
if (meta.replacedOld) {
|
|
217
|
+
// This is a replacement
|
|
218
|
+
const prevElms = meta.replacedOld.getElements();
|
|
219
|
+
if (prevElms.length) {
|
|
220
|
+
operations.push({
|
|
221
|
+
type: "replace",
|
|
222
|
+
oldElms: prevElms,
|
|
223
|
+
newElms: child.getElements(),
|
|
224
|
+
});
|
|
225
|
+
return;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
operations.push({
|
|
229
|
+
type: "insert",
|
|
230
|
+
elms: child.getElements(),
|
|
231
|
+
afterElm,
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
else if (oldIndex !== newIndex) {
|
|
235
|
+
// Existing child that moved - generate move operation
|
|
236
|
+
const afterElm = getAfterElm(newIndex, result);
|
|
237
|
+
operations.push({
|
|
238
|
+
type: "move",
|
|
239
|
+
elms: child.getElements(),
|
|
240
|
+
afterElm,
|
|
241
|
+
});
|
|
98
242
|
}
|
|
243
|
+
// else: child is in same position, no operation needed
|
|
99
244
|
});
|
|
100
245
|
// Unmount any old children that weren't reused
|
|
101
246
|
for (const key in oldKeys) {
|
|
102
247
|
oldKeys[key].unmount();
|
|
248
|
+
operations.push({ type: "remove", elms: oldKeys[key].getElements() });
|
|
103
249
|
}
|
|
104
|
-
return
|
|
250
|
+
return {
|
|
251
|
+
children: result,
|
|
252
|
+
operations,
|
|
253
|
+
};
|
|
105
254
|
}
|
|
106
255
|
}
|
|
@@ -40,7 +40,6 @@ export declare class ComponentVNode extends AbstractVNode {
|
|
|
40
40
|
children: VNode[];
|
|
41
41
|
instance?: ComponentInstance;
|
|
42
42
|
constructor(component: Component<any>, props: Props, children: VNode[], key?: string);
|
|
43
|
-
rerender(): void;
|
|
44
43
|
mount(parent?: VNode): Node[];
|
|
45
44
|
patch(newNode: ComponentVNode): void;
|
|
46
45
|
unmount(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ComponentVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/ComponentVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,QAAQ,EAAU,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"ComponentVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/ComponentVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,QAAQ,EAAU,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,aAAa,EAAgB,MAAM,iBAAiB,CAAC;AAG9D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGvC,MAAM,MAAM,cAAc,GACtB,KAAK,GACL,MAAM,GACN,IAAI,GACJ,MAAM,GACN,SAAS,GACT,OAAO,CAAC;AACZ,MAAM,MAAM,iBAAiB,GAAG,cAAc,GAAG,cAAc,EAAE,CAAC;AAElE;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,SAAS,KAAK,IACjC,CAAC,CAAC,KAAK,EAAE,CAAC,KAAK,MAAM,iBAAiB,CAAC,GACvC,CAAC,MAAM,MAAM,iBAAiB,CAAC,CAAC;AAEpC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,MAAM,CAAC,EAAE,KAAK,CAAC;IACf,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACtC,QAAQ,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC5B,UAAU,EAAE,KAAK,CAAC,MAAM,IAAI,CAAC,CAAC;IAC9B,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,OAAO,CAAC;IACf,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;CACnC,CAAC;AAKF,wBAAgB,mBAAmB,sBAYlC;AAED,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,IAAI,QAYrC;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,IAAI,QAYvC;AAED,qBAAa,cAAe,SAAQ,aAAa;IAC/C,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC;IAC1B,KAAK,EAAE,KAAK,CAAC;IAEb,QAAQ,EAAE,KAAK,EAAE,CAAM;IACvB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;gBAE3B,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC,EACzB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,KAAK,EAAE,EACjB,GAAG,CAAC,EAAE,MAAM;IAWd,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE;IA0I7B,KAAK,CAAC,OAAO,EAAE,cAAc;IAW7B,OAAO;CAcR"}
|
|
@@ -51,9 +51,6 @@ export class ComponentVNode extends AbstractVNode {
|
|
|
51
51
|
this.children = [];
|
|
52
52
|
this.key = key;
|
|
53
53
|
}
|
|
54
|
-
rerender() {
|
|
55
|
-
this.parent?.rerender();
|
|
56
|
-
}
|
|
57
54
|
mount(parent) {
|
|
58
55
|
this.parent = parent;
|
|
59
56
|
if (parent instanceof RootVNode) {
|
|
@@ -78,28 +75,39 @@ export class ComponentVNode extends AbstractVNode {
|
|
|
78
75
|
}
|
|
79
76
|
return normalizeChildren(renderResult);
|
|
80
77
|
};
|
|
78
|
+
let isObserverQueued = false;
|
|
81
79
|
const instance = (this.instance = {
|
|
82
80
|
parent,
|
|
83
81
|
contexts: null,
|
|
84
82
|
onCleanups: [],
|
|
85
83
|
onMounts: [],
|
|
86
84
|
observer: new Observer(() => {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const prevChildren = this.children;
|
|
90
|
-
this.children = this.patchChildren(newChildren);
|
|
91
|
-
// Typically components return a single element, which does
|
|
92
|
-
// not require the parent to apply elements to the DOM again
|
|
93
|
-
const canSelfUpdate = prevChildren.length === 1 &&
|
|
94
|
-
this.children.length === 1 &&
|
|
95
|
-
prevChildren[0] instanceof ElementVNode &&
|
|
96
|
-
this.children[0] instanceof ElementVNode &&
|
|
97
|
-
this.canPatch(prevChildren[0], this.children[0]);
|
|
98
|
-
if (!canSelfUpdate) {
|
|
99
|
-
this.parent?.rerender();
|
|
85
|
+
if (isObserverQueued) {
|
|
86
|
+
return;
|
|
100
87
|
}
|
|
101
|
-
|
|
102
|
-
this.root?.
|
|
88
|
+
isObserverQueued = true;
|
|
89
|
+
this.root?.queueObserver(() => {
|
|
90
|
+
if (instance.observer.isDisposed) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
isObserverQueued = false;
|
|
94
|
+
this.root?.setAsCurrent();
|
|
95
|
+
const newChildren = executeRender();
|
|
96
|
+
const prevChildren = this.children;
|
|
97
|
+
const { children, operations } = this.patchChildren(newChildren);
|
|
98
|
+
this.children = children;
|
|
99
|
+
// Typically components return a single element, which does
|
|
100
|
+
// not require the parent to apply elements to the DOM again
|
|
101
|
+
const canSelfUpdate = prevChildren.length === 1 &&
|
|
102
|
+
this.children.length === 1 &&
|
|
103
|
+
prevChildren[0] instanceof ElementVNode &&
|
|
104
|
+
this.children[0] instanceof ElementVNode &&
|
|
105
|
+
this.canPatch(prevChildren[0], this.children[0]);
|
|
106
|
+
if (!canSelfUpdate) {
|
|
107
|
+
this.parent?.applyDOMOperations(operations, this);
|
|
108
|
+
}
|
|
109
|
+
this.root?.clearCurrent();
|
|
110
|
+
});
|
|
103
111
|
}),
|
|
104
112
|
reactiveProps: createReactiveProps(this.props),
|
|
105
113
|
get error() {
|
|
@@ -8,7 +8,6 @@ export declare class ElementVNode extends AbstractVNode {
|
|
|
8
8
|
private ref?;
|
|
9
9
|
private eventListeners?;
|
|
10
10
|
constructor(tag: string, { ref, ...props }: Props, children: VNode[], key?: string);
|
|
11
|
-
rerender(): void;
|
|
12
11
|
mount(parent?: VNode): Node;
|
|
13
12
|
/**
|
|
14
13
|
* An ELEMENT patch goes through three operations
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ElementVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/ElementVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"ElementVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/ElementVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAgB,MAAM,iBAAiB,CAAC;AAG9D,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAWvC,qBAAa,YAAa,SAAQ,aAAa;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,KAAK,CAAC;IACb,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,GAAG,CAAC,CAA0D;IACtE,OAAO,CAAC,cAAc,CAAC,CAA6B;gBAElD,GAAG,EAAE,MAAM,EACX,EAAE,GAAG,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,EACxB,QAAQ,EAAE,KAAK,EAAE,EACjB,GAAG,CAAC,EAAE,MAAM;IASd,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;IAkC3B;;;;OAIG;IACH,KAAK,CAAC,OAAO,EAAE,YAAY;IAO3B,OAAO;IAYP,OAAO,CAAC,OAAO,CAoCb;IACF,OAAO,CAAC,UAAU;IAGlB,OAAO,CAAC,gBAAgB;CAgBzB"}
|
|
@@ -17,12 +17,6 @@ export class ElementVNode extends AbstractVNode {
|
|
|
17
17
|
this.key = key;
|
|
18
18
|
this.ref = ref;
|
|
19
19
|
}
|
|
20
|
-
rerender() {
|
|
21
|
-
const childrenElms = this.children
|
|
22
|
-
.map((child) => child.getElements())
|
|
23
|
-
.flat();
|
|
24
|
-
this.elm.replaceChildren(...childrenElms);
|
|
25
|
-
}
|
|
26
20
|
mount(parent) {
|
|
27
21
|
this.parent = parent;
|
|
28
22
|
if (parent instanceof RootVNode) {
|
|
@@ -60,11 +54,9 @@ export class ElementVNode extends AbstractVNode {
|
|
|
60
54
|
patch(newNode) {
|
|
61
55
|
this.patchProps(newNode.props);
|
|
62
56
|
this.props = newNode.props;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
.flat();
|
|
67
|
-
this.elm.replaceChildren(...childrenElms);
|
|
57
|
+
const { children, operations } = this.patchChildren(newNode.children);
|
|
58
|
+
this.children = children;
|
|
59
|
+
this.applyDOMOperations(operations);
|
|
68
60
|
}
|
|
69
61
|
unmount() {
|
|
70
62
|
this.children.forEach((child) => child.unmount());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"FragmentVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/FragmentVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAKhD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,eAAO,MAAM,QAAQ,eAAqB,CAAC;AAE3C,qBAAa,aAAc,SAAQ,aAAa;IAC9C,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;gBAED,QAAQ,EAAE,KAAK,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM;IAK3C,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE;IAW7B,
|
|
1
|
+
{"version":3,"file":"FragmentVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/FragmentVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAKhD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,eAAO,MAAM,QAAQ,eAAqB,CAAC;AAE3C,qBAAa,aAAc,SAAQ,aAAa;IAC9C,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;gBAED,QAAQ,EAAE,KAAK,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM;IAK3C,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI,EAAE;IAW7B,KAAK,CAAC,OAAO,EAAE,aAAa;IAK5B,OAAO;CAMR"}
|
|
@@ -19,11 +19,10 @@ export class FragmentVNode extends AbstractVNode {
|
|
|
19
19
|
}
|
|
20
20
|
return this.children.map((child) => child.mount(this)).flat();
|
|
21
21
|
}
|
|
22
|
-
rerender() {
|
|
23
|
-
this.parent?.rerender();
|
|
24
|
-
}
|
|
25
22
|
patch(newNode) {
|
|
26
|
-
|
|
23
|
+
const { children, operations } = this.patchChildren(newNode.children);
|
|
24
|
+
this.children = children;
|
|
25
|
+
this.applyDOMOperations(operations);
|
|
27
26
|
}
|
|
28
27
|
unmount() {
|
|
29
28
|
this.children.forEach((child) => child.unmount());
|
package/dist/vdom/RootVNode.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AbstractVNode } from "./AbstractVNode";
|
|
1
|
+
import { AbstractVNode, DOMOperation } from "./AbstractVNode";
|
|
2
2
|
import { VNode } from "./types";
|
|
3
3
|
import { ComponentInstance } from "./ComponentVNode";
|
|
4
4
|
export declare let currentRoot: RootVNode | undefined;
|
|
@@ -6,9 +6,12 @@ export declare class RootVNode extends AbstractVNode {
|
|
|
6
6
|
children: VNode[];
|
|
7
7
|
componentStack: ComponentInstance[];
|
|
8
8
|
private lifecycleQueue;
|
|
9
|
+
private hasPendingMicroTask;
|
|
10
|
+
private pendingObservers;
|
|
9
11
|
constructor(rootNode: VNode, container: HTMLElement);
|
|
10
12
|
queueMount(cb: () => void): void;
|
|
11
13
|
queueUnmount(cb: () => void): void;
|
|
14
|
+
queueObserver(cb: () => void): void;
|
|
12
15
|
flushLifecycle(): void;
|
|
13
16
|
pushComponent(instance: ComponentInstance): void;
|
|
14
17
|
popComponent(): void;
|
|
@@ -16,7 +19,7 @@ export declare class RootVNode extends AbstractVNode {
|
|
|
16
19
|
clearCurrent(): void;
|
|
17
20
|
mount(): Node | Node[];
|
|
18
21
|
patch(): void;
|
|
19
|
-
|
|
22
|
+
applyDOMOperations(operations: DOMOperation[], atVNode: VNode): void;
|
|
20
23
|
unmount(): void;
|
|
21
24
|
}
|
|
22
25
|
//# sourceMappingURL=RootVNode.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RootVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/RootVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"RootVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/RootVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAChC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAIrD,eAAO,IAAI,WAAW,EAAE,SAAS,GAAG,SAAS,CAAC;AAE9C,qBAAa,SAAU,SAAQ,aAAa;IAC1C,QAAQ,EAAE,KAAK,EAAE,CAAC;IAClB,cAAc,EAAE,iBAAiB,EAAE,CAAM;IACzC,OAAO,CAAC,cAAc,CAGpB;IACF,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,gBAAgB,CAAyB;gBAErC,QAAQ,EAAE,KAAK,EAAE,SAAS,EAAE,WAAW;IAMnD,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI;IAIzB,YAAY,CAAC,EAAE,EAAE,MAAM,IAAI;IAG3B,aAAa,CAAC,EAAE,EAAE,MAAM,IAAI;IAa5B,cAAc;IAOd,aAAa,CAAC,QAAQ,EAAE,iBAAiB;IAIzC,YAAY;IAIZ,YAAY;IAIZ,YAAY;IAMZ,KAAK,IAAI,IAAI,GAAG,IAAI,EAAE;IAGtB,KAAK,IAAI,IAAI;IACb,kBAAkB,CAAC,UAAU,EAAE,YAAY,EAAE,EAAE,OAAO,EAAE,KAAK,GAAG,IAAI;IAIpE,OAAO,IAAI,IAAI;CAChB"}
|
package/dist/vdom/RootVNode.js
CHANGED
|
@@ -9,6 +9,8 @@ export class RootVNode extends AbstractVNode {
|
|
|
9
9
|
toMount: [],
|
|
10
10
|
toUnmount: [],
|
|
11
11
|
};
|
|
12
|
+
hasPendingMicroTask = false;
|
|
13
|
+
pendingObservers = [];
|
|
12
14
|
constructor(rootNode, container) {
|
|
13
15
|
super();
|
|
14
16
|
this.elm = container;
|
|
@@ -20,6 +22,19 @@ export class RootVNode extends AbstractVNode {
|
|
|
20
22
|
queueUnmount(cb) {
|
|
21
23
|
this.lifecycleQueue.toUnmount.push(cb);
|
|
22
24
|
}
|
|
25
|
+
queueObserver(cb) {
|
|
26
|
+
this.pendingObservers.push(cb);
|
|
27
|
+
if (!this.hasPendingMicroTask) {
|
|
28
|
+
this.hasPendingMicroTask = true;
|
|
29
|
+
queueMicrotask(() => {
|
|
30
|
+
this.hasPendingMicroTask = false;
|
|
31
|
+
const pendingObservers = this.pendingObservers;
|
|
32
|
+
this.pendingObservers = [];
|
|
33
|
+
pendingObservers.forEach((pendingObserver) => pendingObserver());
|
|
34
|
+
this.flushLifecycle();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
23
38
|
flushLifecycle() {
|
|
24
39
|
this.lifecycleQueue.toUnmount.forEach((cb) => cb());
|
|
25
40
|
this.lifecycleQueue.toUnmount = [];
|
|
@@ -44,11 +59,8 @@ export class RootVNode extends AbstractVNode {
|
|
|
44
59
|
return this.children.map((childNode) => childNode.mount(this)).flat();
|
|
45
60
|
}
|
|
46
61
|
patch() { }
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
.map((child) => child.getElements())
|
|
50
|
-
.flat();
|
|
51
|
-
this.elm.replaceChildren(...childrenElms);
|
|
62
|
+
applyDOMOperations(operations, atVNode) {
|
|
63
|
+
super.applyDOMOperations(operations, atVNode);
|
|
52
64
|
this.flushLifecycle();
|
|
53
65
|
}
|
|
54
66
|
unmount() { }
|
package/dist/vdom/TextVNode.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TextVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/TextVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,qBAAa,SAAU,SAAQ,aAAa;IAC1C,IAAI,EAAE,MAAM,CAAC;gBACD,IAAI,EAAE,MAAM;IAIxB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;IAe3B,KAAK,CAAC,OAAO,EAAE,SAAS;
|
|
1
|
+
{"version":3,"file":"TextVNode.d.ts","sourceRoot":"","sources":["../../src/vdom/TextVNode.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,qBAAa,SAAU,SAAQ,aAAa;IAC1C,IAAI,EAAE,MAAM,CAAC;gBACD,IAAI,EAAE,MAAM;IAIxB,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,GAAG,IAAI;IAe3B,KAAK,CAAC,OAAO,EAAE,SAAS;IAQxB,OAAO;CAMR"}
|
package/dist/vdom/TextVNode.js
CHANGED
|
@@ -19,10 +19,12 @@ export class TextVNode extends AbstractVNode {
|
|
|
19
19
|
return textNode;
|
|
20
20
|
}
|
|
21
21
|
patch(newNode) {
|
|
22
|
+
if (newNode.text === this.text) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
22
25
|
this.text = newNode.text;
|
|
23
26
|
this.elm.textContent = this.text;
|
|
24
27
|
}
|
|
25
|
-
rerender() { }
|
|
26
28
|
unmount() {
|
|
27
29
|
this.root?.queueUnmount(() => {
|
|
28
30
|
delete this.elm;
|