@3sln/dodo 0.0.3 → 0.0.5
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/README.md +6 -1
- package/index.js +128 -7
- package/package.json +6 -2
- package/src/html.js +119 -117
- package/src/scheduler.js +48 -48
- package/src/vdom.js +274 -161
- package/dodo.js +0 -698
package/src/vdom.js
CHANGED
|
@@ -32,32 +32,35 @@ class VNode {
|
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function defaultShouldUpdate(a, b) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
if (a === b) return false;
|
|
36
|
+
if (a && b && typeof a === 'object' && typeof b === 'object') {
|
|
37
|
+
if (a.constructor !== b.constructor) return true;
|
|
38
|
+
|
|
39
|
+
if (Array.isArray(a)) {
|
|
40
|
+
if (a.length !== b.length) return true;
|
|
41
|
+
for (let j = 0; j < a.length; j++) {
|
|
42
|
+
if (a[j] !== b[j]) return true;
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
47
|
+
if (a.constructor === Object) {
|
|
48
|
+
const keysA = Object.keys(a);
|
|
49
|
+
if (keysA.length !== Object.keys(b).length) return true;
|
|
50
|
+
for (const key of keysA) {
|
|
51
|
+
if (!b.hasOwnProperty(key) || a[key] !== b[key]) return true;
|
|
52
|
+
}
|
|
53
|
+
return false;
|
|
55
54
|
}
|
|
56
|
-
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
function isIterable(x) {
|
|
60
|
-
return
|
|
60
|
+
return (
|
|
61
|
+
Array.isArray(x) ||
|
|
62
|
+
(x != null && typeof x[Symbol.iterator] === 'function' && typeof x !== 'string')
|
|
63
|
+
);
|
|
61
64
|
}
|
|
62
65
|
|
|
63
66
|
function alias(f) {
|
|
@@ -71,8 +74,10 @@ function special(o) {
|
|
|
71
74
|
function newElementNamespace(parentNode, newNodeTag) {
|
|
72
75
|
if (parentNode.namespaceURI === 'http://www.w3.org/1999/xhtml') {
|
|
73
76
|
switch (newNodeTag) {
|
|
74
|
-
case 'svg':
|
|
75
|
-
|
|
77
|
+
case 'svg':
|
|
78
|
+
return 'http://www.w3.org/2000/svg';
|
|
79
|
+
case 'math':
|
|
80
|
+
return 'http://www.w3.org/1998/Math/MathML';
|
|
76
81
|
}
|
|
77
82
|
}
|
|
78
83
|
return parentNode.namespaceURI ?? parentNode.host?.namespaceURI ?? 'http://www.w3.org/1999/xhtml';
|
|
@@ -80,7 +85,7 @@ function newElementNamespace(parentNode, newNodeTag) {
|
|
|
80
85
|
|
|
81
86
|
function createElementNode(parentNode, tag, vdom) {
|
|
82
87
|
const el = parentNode.ownerDocument.createElementNS(newElementNamespace(parentNode, tag), tag);
|
|
83
|
-
el[NODE_STATE] = {
|
|
88
|
+
el[NODE_STATE] = {originalProps: {}, newVdom: vdom};
|
|
84
89
|
return el;
|
|
85
90
|
}
|
|
86
91
|
|
|
@@ -117,10 +122,10 @@ function removePathFromFocusWithinSet(set, newPath) {
|
|
|
117
122
|
|
|
118
123
|
function installFocusTrackingForDocument(doc) {
|
|
119
124
|
const focusWithinSet = new Set();
|
|
120
|
-
doc.addEventListener('focusin',
|
|
125
|
+
doc.addEventListener('focusin', event => {
|
|
121
126
|
addPathToFocusWithinSet(focusWithinSet, event.composedPath());
|
|
122
127
|
});
|
|
123
|
-
doc.addEventListener('focusout',
|
|
128
|
+
doc.addEventListener('focusout', event => {
|
|
124
129
|
const newPath = event.relatedTarget ? event.composedPath() : [];
|
|
125
130
|
removePathFromFocusWithinSet(focusWithinSet, newPath);
|
|
126
131
|
});
|
|
@@ -129,27 +134,50 @@ function installFocusTrackingForDocument(doc) {
|
|
|
129
134
|
return focusWithinSet;
|
|
130
135
|
}
|
|
131
136
|
|
|
132
|
-
export default
|
|
137
|
+
export default userSettings => {
|
|
133
138
|
const shouldUpdate = userSettings?.shouldUpdate ?? defaultShouldUpdate;
|
|
134
|
-
const isMap = userSettings?.isMap ?? (
|
|
135
|
-
const mapIter = userSettings?.mapIter ?? (
|
|
139
|
+
const isMap = userSettings?.isMap ?? (x => x?.constructor === Object);
|
|
140
|
+
const mapIter = userSettings?.mapIter ?? (m => Object.entries(m));
|
|
136
141
|
const mapGet = userSettings?.mapGet ?? ((m, k) => m[k]);
|
|
137
142
|
const mapMerge = userSettings?.mapMerge ?? ((...maps) => Object.assign({}, ...maps));
|
|
138
|
-
const newMap = userSettings?.newMap ?? (
|
|
139
|
-
const mapPut =
|
|
143
|
+
const newMap = userSettings?.newMap ?? (obj => ({...obj}));
|
|
144
|
+
const mapPut =
|
|
145
|
+
userSettings?.mapPut ??
|
|
146
|
+
((m, k, v) => {
|
|
147
|
+
m[k] = v;
|
|
148
|
+
return m;
|
|
149
|
+
});
|
|
140
150
|
const isSeq = userSettings?.isSeq ?? isIterable;
|
|
141
|
-
const seqIter = userSettings?.seqIter ?? (
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
const
|
|
151
|
+
const seqIter = userSettings?.seqIter ?? (s => s);
|
|
152
|
+
const convertName = userSettings?.convertName ?? (x => x);
|
|
153
|
+
const convertTagName = userSettings?.convertTagName ?? convertName;
|
|
154
|
+
const convertPropName = userSettings?.convertPropName ?? convertName;
|
|
155
|
+
const convertStyleName = userSettings?.convertStyleName ?? convertName;
|
|
156
|
+
const convertDataName = userSettings?.convertDataName ?? convertName;
|
|
157
|
+
const convertClassName = userSettings?.convertClassName ?? convertName;
|
|
158
|
+
const convertHookName = userSettings?.convertHookName ?? convertName;
|
|
147
159
|
const listenerKey = userSettings?.listenerKey ?? 'listener';
|
|
148
160
|
const captureKey = userSettings?.captureKey ?? 'capture';
|
|
149
161
|
const passiveKey = userSettings?.passiveKey ?? 'passive';
|
|
150
162
|
|
|
163
|
+
const EMPTY_MAP = newMap({});
|
|
164
|
+
|
|
165
|
+
function toIterator(iterableOrIterator) {
|
|
166
|
+
if (iterableOrIterator == null) return [][Symbol.iterator]();
|
|
167
|
+
if (typeof iterableOrIterator[Symbol.iterator] === 'function') {
|
|
168
|
+
return iterableOrIterator[Symbol.iterator]();
|
|
169
|
+
}
|
|
170
|
+
if (typeof iterableOrIterator.next === 'function') {
|
|
171
|
+
return iterableOrIterator;
|
|
172
|
+
}
|
|
173
|
+
return [iterableOrIterator][Symbol.iterator]();
|
|
174
|
+
}
|
|
175
|
+
|
|
151
176
|
function flattenSeqIntoArray(array, items, excludeFalsey) {
|
|
152
|
-
|
|
177
|
+
const iterator = toIterator(seqIter(items));
|
|
178
|
+
let result;
|
|
179
|
+
while (!(result = iterator.next()).done) {
|
|
180
|
+
const item = result.value;
|
|
153
181
|
if (excludeFalsey && (item == null || item == false)) {
|
|
154
182
|
continue;
|
|
155
183
|
}
|
|
@@ -157,7 +185,7 @@ export default (userSettings) => {
|
|
|
157
185
|
if (!isSeq(item)) {
|
|
158
186
|
array.push(item);
|
|
159
187
|
} else {
|
|
160
|
-
flattenSeqIntoArray(array, item);
|
|
188
|
+
flattenSeqIntoArray(array, item, excludeFalsey);
|
|
161
189
|
}
|
|
162
190
|
}
|
|
163
191
|
}
|
|
@@ -176,7 +204,7 @@ export default (userSettings) => {
|
|
|
176
204
|
if (!isSeq(item)) {
|
|
177
205
|
array.push(item);
|
|
178
206
|
} else {
|
|
179
|
-
|
|
207
|
+
flattenSeqIntoArray(array, seqIter(item));
|
|
180
208
|
}
|
|
181
209
|
}
|
|
182
210
|
}
|
|
@@ -189,37 +217,52 @@ export default (userSettings) => {
|
|
|
189
217
|
function h(tag, props, ...children) {
|
|
190
218
|
if (!isMap(props)) {
|
|
191
219
|
children.unshift(props);
|
|
192
|
-
props =
|
|
220
|
+
props = EMPTY_MAP;
|
|
193
221
|
}
|
|
194
|
-
return new VNode(ELEMENT_NODE, convertTagName(tag), [props ??
|
|
222
|
+
return new VNode(ELEMENT_NODE, convertTagName(tag), [props ?? EMPTY_MAP, ...children]);
|
|
195
223
|
}
|
|
196
224
|
|
|
197
225
|
function reconcileElementStyling(target, oldStyling, newStyling) {
|
|
198
226
|
const style = target.style;
|
|
199
|
-
|
|
227
|
+
const newStylingIterator = toIterator(mapIter(newStyling));
|
|
228
|
+
let result;
|
|
229
|
+
while (!(result = newStylingIterator.next()).done) {
|
|
230
|
+
const [name, value] = result.value;
|
|
200
231
|
style.setProperty(convertStyleName(name), value);
|
|
201
232
|
}
|
|
202
|
-
|
|
233
|
+
const oldStylingIterator = toIterator(mapIter(oldStyling));
|
|
234
|
+
while (!(result = oldStylingIterator.next()).done) {
|
|
235
|
+
const [name] = result.value;
|
|
203
236
|
if (mapGet(newStyling, name) !== undefined) continue;
|
|
204
237
|
style.removeProperty(convertStyleName(name));
|
|
205
238
|
}
|
|
206
239
|
}
|
|
207
240
|
|
|
208
241
|
function reconcileElementAttributes(target, oldAttrs, newAttrs) {
|
|
209
|
-
|
|
242
|
+
const newAttrsIterator = toIterator(mapIter(newAttrs));
|
|
243
|
+
let result;
|
|
244
|
+
while (!(result = newAttrsIterator.next()).done) {
|
|
245
|
+
const [name, value] = result.value;
|
|
210
246
|
target.setAttribute(convertPropName(name), value);
|
|
211
247
|
}
|
|
212
|
-
|
|
248
|
+
const oldAttrsIterator = toIterator(mapIter(oldAttrs));
|
|
249
|
+
while (!(result = oldAttrsIterator.next()).done) {
|
|
250
|
+
const [name] = result.value;
|
|
213
251
|
if (mapGet(newAttrs, name) !== undefined) continue;
|
|
214
252
|
target.removeAttribute(convertPropName(name));
|
|
215
253
|
}
|
|
216
254
|
}
|
|
217
255
|
|
|
218
256
|
function reconcileElementDataset(target, oldDataset, newDataset) {
|
|
219
|
-
|
|
257
|
+
const newDatasetIterator = toIterator(mapIter(newDataset));
|
|
258
|
+
let result;
|
|
259
|
+
while (!(result = newDatasetIterator.next()).done) {
|
|
260
|
+
const [name, value] = result.value;
|
|
220
261
|
target.dataset[convertDataName(name)] = value;
|
|
221
262
|
}
|
|
222
|
-
|
|
263
|
+
const oldDatasetIterator = toIterator(mapIter(oldDataset));
|
|
264
|
+
while (!(result = oldDatasetIterator.next()).done) {
|
|
265
|
+
const [name] = result.value;
|
|
223
266
|
if (mapGet(newDataset, name) !== undefined) continue;
|
|
224
267
|
delete target.dataset[convertDataName(name)];
|
|
225
268
|
}
|
|
@@ -240,136 +283,160 @@ export default (userSettings) => {
|
|
|
240
283
|
function reconcileElementProps(target, props) {
|
|
241
284
|
const nodeState = target[NODE_STATE];
|
|
242
285
|
const isHtml = target.namespaceURI === 'http://www.w3.org/1999/xhtml';
|
|
243
|
-
const oldProps = nodeState.vdom?.args[0] ??
|
|
286
|
+
const oldProps = nodeState.vdom?.args[0] ?? EMPTY_MAP;
|
|
244
287
|
|
|
245
288
|
// Handle new and changed props
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
289
|
+
const propsIterator = toIterator(mapIter(props));
|
|
290
|
+
let result;
|
|
291
|
+
while (!(result = propsIterator.next()).done) {
|
|
292
|
+
const [name, newValue] = result.value;
|
|
293
|
+
const propName = convertPropName(name);
|
|
294
|
+
const oldValue = mapGet(oldProps, name);
|
|
295
|
+
|
|
296
|
+
if (Object.is(newValue, oldValue)) continue;
|
|
297
|
+
|
|
298
|
+
switch (propName) {
|
|
299
|
+
case '$styling': {
|
|
300
|
+
if (!isMap(newValue)) throw new Error('invalid value for styling prop');
|
|
301
|
+
reconcileElementStyling(target, oldValue ?? EMPTY_MAP, newValue ?? EMPTY_MAP);
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
case '$classes': {
|
|
305
|
+
if (!isSeq(newValue)) throw new Error('invalid value for classes prop');
|
|
306
|
+
reconcileElementClasses(target, oldValue ?? [], newValue ?? []);
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
case '$attrs': {
|
|
310
|
+
if (!isMap(newValue)) throw new Error('invalid value for attrs prop');
|
|
311
|
+
reconcileElementAttributes(target, oldValue ?? EMPTY_MAP, newValue ?? EMPTY_MAP);
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
case '$dataset': {
|
|
315
|
+
if (!isMap(newValue)) throw new Error('invalid value for dataset prop');
|
|
316
|
+
reconcileElementDataset(target, oldValue ?? EMPTY_MAP, newValue ?? EMPTY_MAP);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
default: {
|
|
320
|
+
if (isHtml) {
|
|
321
|
+
const originalProps = nodeState.originalProps;
|
|
322
|
+
if (!(propName in originalProps)) {
|
|
323
|
+
originalProps[propName] = target[propName];
|
|
267
324
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
325
|
+
if (newValue === undefined) {
|
|
326
|
+
target[propName] = originalProps[propName];
|
|
327
|
+
} else {
|
|
328
|
+
target[propName] = newValue;
|
|
272
329
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
if (newValue === undefined) {
|
|
280
|
-
target[propName] = originalProps[propName];
|
|
281
|
-
} else {
|
|
282
|
-
target[propName] = newValue;
|
|
283
|
-
}
|
|
284
|
-
} else {
|
|
285
|
-
if (newValue === undefined) {
|
|
286
|
-
target.removeAttribute(propName);
|
|
287
|
-
} else {
|
|
288
|
-
target.setAttribute(propName, newValue);
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
break;
|
|
330
|
+
} else {
|
|
331
|
+
if (newValue === undefined) {
|
|
332
|
+
target.removeAttribute(propName);
|
|
333
|
+
} else {
|
|
334
|
+
target.setAttribute(propName, newValue);
|
|
292
335
|
}
|
|
336
|
+
}
|
|
337
|
+
break;
|
|
293
338
|
}
|
|
339
|
+
}
|
|
294
340
|
}
|
|
295
341
|
|
|
296
342
|
// Handle removed props
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
target.removeAttribute(propName);
|
|
323
|
-
}
|
|
324
|
-
break;
|
|
343
|
+
const oldPropsIterator = toIterator(mapIter(oldProps));
|
|
344
|
+
while (!(result = oldPropsIterator.next()).done) {
|
|
345
|
+
const [name, oldValue] = result.value;
|
|
346
|
+
if (mapGet(props, name) !== undefined) continue; // it wasn't removed
|
|
347
|
+
|
|
348
|
+
const propName = convertPropName(name);
|
|
349
|
+
switch (propName) {
|
|
350
|
+
case '$styling':
|
|
351
|
+
reconcileElementStyling(target, oldValue ?? EMPTY_MAP, EMPTY_MAP);
|
|
352
|
+
break;
|
|
353
|
+
case '$classes':
|
|
354
|
+
reconcileElementClasses(target, oldValue ?? [], []);
|
|
355
|
+
break;
|
|
356
|
+
case '$attrs':
|
|
357
|
+
reconcileElementAttributes(target, oldValue ?? EMPTY_MAP, EMPTY_MAP);
|
|
358
|
+
break;
|
|
359
|
+
case '$dataset':
|
|
360
|
+
reconcileElementDataset(target, oldValue ?? EMPTY_MAP, EMPTY_MAP);
|
|
361
|
+
break;
|
|
362
|
+
default: {
|
|
363
|
+
if (isHtml) {
|
|
364
|
+
const originalProps = nodeState.originalProps;
|
|
365
|
+
if (propName in originalProps) {
|
|
366
|
+
target[propName] = originalProps[propName];
|
|
367
|
+
delete originalProps[propName];
|
|
325
368
|
}
|
|
369
|
+
} else {
|
|
370
|
+
target.removeAttribute(propName);
|
|
371
|
+
}
|
|
372
|
+
break;
|
|
326
373
|
}
|
|
374
|
+
}
|
|
327
375
|
}
|
|
328
376
|
}
|
|
329
377
|
|
|
330
378
|
function reconcileListeners(target, hooks) {
|
|
331
379
|
const state = target[NODE_STATE];
|
|
332
380
|
if (!state.vdom) {
|
|
333
|
-
|
|
334
|
-
|
|
381
|
+
const hooksIterator = toIterator(mapIter(hooks));
|
|
382
|
+
let result;
|
|
383
|
+
while (!(result = hooksIterator.next()).done) {
|
|
384
|
+
const [name, listener] = result.value;
|
|
385
|
+
const hookName = convertHookName(name);
|
|
386
|
+
if (hookName[0] === '$') continue;
|
|
335
387
|
if (typeof listener === 'function') {
|
|
336
|
-
target.addEventListener(
|
|
388
|
+
target.addEventListener(hookName, listener);
|
|
337
389
|
} else if (listener != null) {
|
|
338
|
-
target.addEventListener(
|
|
339
|
-
capture: !!mapGet(listener, captureKey),
|
|
340
|
-
passive: !!mapGet(listener, passiveKey)
|
|
390
|
+
target.addEventListener(hookName, mapGet(listener, listenerKey), {
|
|
391
|
+
capture: !!mapGet(listener, captureKey),
|
|
392
|
+
passive: !!mapGet(listener, passiveKey),
|
|
341
393
|
});
|
|
342
394
|
}
|
|
343
395
|
}
|
|
344
396
|
} else {
|
|
345
|
-
const oldHooks = state.vdom.hooks ??
|
|
346
|
-
|
|
347
|
-
|
|
397
|
+
const oldHooks = state.vdom.hooks ?? EMPTY_MAP;
|
|
398
|
+
const hooksIterator = toIterator(mapIter(hooks));
|
|
399
|
+
let result;
|
|
400
|
+
while (!(result = hooksIterator.next()).done) {
|
|
401
|
+
const [name, listener] = result.value;
|
|
402
|
+
const hookName = convertHookName(name);
|
|
403
|
+
if (hookName[0] === '$') continue;
|
|
348
404
|
const oldListener = mapGet(oldHooks, name);
|
|
349
405
|
if (listener === oldListener) continue;
|
|
350
406
|
|
|
351
407
|
if (typeof oldListener === 'function') {
|
|
352
|
-
target.removeEventListener(
|
|
408
|
+
target.removeEventListener(hookName, oldListener);
|
|
353
409
|
} else if (oldListener != null) {
|
|
354
|
-
target.removeEventListener(
|
|
410
|
+
target.removeEventListener(
|
|
411
|
+
hookName,
|
|
412
|
+
mapGet(oldListener, listenerKey),
|
|
413
|
+
!!mapGet(oldListener, captureKey),
|
|
414
|
+
);
|
|
355
415
|
}
|
|
356
|
-
|
|
416
|
+
|
|
357
417
|
if (typeof listener === 'function') {
|
|
358
|
-
target.addEventListener(
|
|
418
|
+
target.addEventListener(hookName, listener);
|
|
359
419
|
} else if (listener != null) {
|
|
360
|
-
target.addEventListener(
|
|
361
|
-
capture: !!mapGet(listener, captureKey),
|
|
362
|
-
passive: !!mapGet(listener, passiveKey)
|
|
420
|
+
target.addEventListener(hookName, mapGet(listener, listenerKey), {
|
|
421
|
+
capture: !!mapGet(listener, captureKey),
|
|
422
|
+
passive: !!mapGet(listener, passiveKey),
|
|
363
423
|
});
|
|
364
424
|
}
|
|
365
425
|
}
|
|
366
|
-
|
|
367
|
-
|
|
426
|
+
const oldHooksIterator = toIterator(mapIter(oldHooks));
|
|
427
|
+
while (!(result = oldHooksIterator.next()).done) {
|
|
428
|
+
const [name] = result.value;
|
|
429
|
+
const hookName = convertHookName(name);
|
|
430
|
+
if (hookName[0] === '$' || mapGet(hooks, name) !== undefined) continue;
|
|
368
431
|
const oldListener = mapGet(oldHooks, name);
|
|
369
432
|
if (typeof oldListener === 'function') {
|
|
370
|
-
target.removeEventListener(
|
|
433
|
+
target.removeEventListener(hookName, oldListener);
|
|
371
434
|
} else if (oldListener != null) {
|
|
372
|
-
target.removeEventListener(
|
|
435
|
+
target.removeEventListener(
|
|
436
|
+
hookName,
|
|
437
|
+
mapGet(oldListener, listenerKey),
|
|
438
|
+
!!mapGet(oldListener, captureKey),
|
|
439
|
+
);
|
|
373
440
|
}
|
|
374
441
|
}
|
|
375
442
|
}
|
|
@@ -384,7 +451,7 @@ export default (userSettings) => {
|
|
|
384
451
|
switch (newVdom.type) {
|
|
385
452
|
case ELEMENT_NODE: {
|
|
386
453
|
reconcileElementProps(target, args[0]);
|
|
387
|
-
reconcileElementChildren(target, args.slice(1));
|
|
454
|
+
reconcileElementChildren(target, flattenVNodeChildren(args.slice(1)));
|
|
388
455
|
break;
|
|
389
456
|
}
|
|
390
457
|
case OPAQUE_NODE: {
|
|
@@ -395,9 +462,9 @@ export default (userSettings) => {
|
|
|
395
462
|
const innerVdom = newVdom.tag.apply(undefined, newVdom.args);
|
|
396
463
|
if (innerVdom === undefined || innerVdom === null) break;
|
|
397
464
|
if (isSeq(innerVdom)) {
|
|
398
|
-
reconcileElementChildren(target, innerVdom);
|
|
465
|
+
reconcileElementChildren(target, flattenSeq(innerVdom));
|
|
399
466
|
} else {
|
|
400
|
-
reconcileElementChildren(target, [innerVdom]);
|
|
467
|
+
reconcileElementChildren(target, flattenVNodeChildren([innerVdom]));
|
|
401
468
|
}
|
|
402
469
|
break;
|
|
403
470
|
}
|
|
@@ -412,7 +479,7 @@ export default (userSettings) => {
|
|
|
412
479
|
}
|
|
413
480
|
|
|
414
481
|
if (newVdom.hooks || oldVdom?.hooks) {
|
|
415
|
-
reconcileListeners(target, newVdom.hooks ??
|
|
482
|
+
reconcileListeners(target, newVdom.hooks ?? EMPTY_MAP);
|
|
416
483
|
}
|
|
417
484
|
try {
|
|
418
485
|
newVdom.hooks?.$update?.(target, newVdom, oldVdom);
|
|
@@ -459,7 +526,6 @@ export default (userSettings) => {
|
|
|
459
526
|
function cleanupTarget(target) {
|
|
460
527
|
const state = target[NODE_STATE];
|
|
461
528
|
if (state) {
|
|
462
|
-
|
|
463
529
|
switch (state.vdom.type) {
|
|
464
530
|
case ELEMENT_NODE:
|
|
465
531
|
reconcileElementProps(target, {});
|
|
@@ -491,8 +557,7 @@ export default (userSettings) => {
|
|
|
491
557
|
}
|
|
492
558
|
}
|
|
493
559
|
|
|
494
|
-
function reconcileElementChildren(target,
|
|
495
|
-
const newChildren = flattenVNodeChildren(childrenIterable);
|
|
560
|
+
function reconcileElementChildren(target, newChildren) {
|
|
496
561
|
const oldNodesToRemove = new Set(target.childNodes);
|
|
497
562
|
const oldVNodeNodesPool = new Map();
|
|
498
563
|
const oldTextNodesPool = [];
|
|
@@ -502,14 +567,21 @@ export default (userSettings) => {
|
|
|
502
567
|
oldTextNodesPool.push(oldChild);
|
|
503
568
|
continue;
|
|
504
569
|
}
|
|
505
|
-
|
|
570
|
+
|
|
571
|
+
const oldChildState = oldChild[NODE_STATE];
|
|
572
|
+
const vdom = oldChildState?.vdom;
|
|
506
573
|
if (vdom === undefined) {
|
|
574
|
+
if (oldChildState?.newVdom) {
|
|
575
|
+
throw new Error(
|
|
576
|
+
'Attempt to reconcile against a target while already working on a reconciliation against that same target, this is not allowed',
|
|
577
|
+
);
|
|
578
|
+
}
|
|
507
579
|
continue;
|
|
508
580
|
}
|
|
509
581
|
|
|
510
582
|
let oldNodesPoolForTag = oldVNodeNodesPool.get(vdom.tag);
|
|
511
583
|
if (!oldNodesPoolForTag) {
|
|
512
|
-
oldNodesPoolForTag = {
|
|
584
|
+
oldNodesPoolForTag = {nodesForKey: new Map(), nodesWithoutKey: []};
|
|
513
585
|
oldVNodeNodesPool.set(vdom.tag, oldNodesPoolForTag);
|
|
514
586
|
}
|
|
515
587
|
|
|
@@ -540,6 +612,9 @@ export default (userSettings) => {
|
|
|
540
612
|
newDomNode = pool.shift();
|
|
541
613
|
const state = newDomNode[NODE_STATE];
|
|
542
614
|
if (shouldUpdate(state.vdom.args, newVdom.args)) {
|
|
615
|
+
if (state.vdom.hooks || newVdom.hooks) {
|
|
616
|
+
reconcileListeners(target, newVdom.hooks);
|
|
617
|
+
}
|
|
543
618
|
state.newVdom = newVdom;
|
|
544
619
|
}
|
|
545
620
|
} else {
|
|
@@ -551,6 +626,9 @@ export default (userSettings) => {
|
|
|
551
626
|
newDomNode = unkeyedOldNode;
|
|
552
627
|
const state = newDomNode[NODE_STATE];
|
|
553
628
|
if (shouldUpdate(state.vdom.args, newVdom.args)) {
|
|
629
|
+
if (state.vdom.hooks || newVdom.hooks) {
|
|
630
|
+
reconcileListeners(target, newVdom.hooks);
|
|
631
|
+
}
|
|
554
632
|
state.newVdom = newVdom;
|
|
555
633
|
}
|
|
556
634
|
} else {
|
|
@@ -583,7 +661,11 @@ export default (userSettings) => {
|
|
|
583
661
|
const newChild = newDomChildren[i];
|
|
584
662
|
const existingChildAtPosition = target.childNodes[i];
|
|
585
663
|
if (newChild !== existingChildAtPosition) {
|
|
586
|
-
(newChild.isConnected ? moveBefore : insertBefore).call(
|
|
664
|
+
(newChild.isConnected ? moveBefore : insertBefore).call(
|
|
665
|
+
target,
|
|
666
|
+
newChild,
|
|
667
|
+
existingChildAtPosition,
|
|
668
|
+
);
|
|
587
669
|
}
|
|
588
670
|
const state = newChild[NODE_STATE];
|
|
589
671
|
if (state?.newVdom) {
|
|
@@ -668,14 +750,17 @@ export default (userSettings) => {
|
|
|
668
750
|
}
|
|
669
751
|
|
|
670
752
|
if (isSeq(vdom)) {
|
|
671
|
-
reconcileElementChildren(target,
|
|
753
|
+
reconcileElementChildren(target, flattenSeq(vdom));
|
|
672
754
|
return;
|
|
673
755
|
}
|
|
674
756
|
|
|
675
757
|
if (vdom instanceof VNode) {
|
|
676
758
|
if (state) {
|
|
677
759
|
if (state.vdom.type === vdom.type) {
|
|
678
|
-
if (
|
|
760
|
+
if (
|
|
761
|
+
shouldUpdate(state.vdom.args, vdom.args) ||
|
|
762
|
+
shouldUpdate(state.vdom.hooks, vdom.hooks)
|
|
763
|
+
) {
|
|
679
764
|
state.newVdom = vdom;
|
|
680
765
|
reconcileNode(target);
|
|
681
766
|
}
|
|
@@ -687,13 +772,18 @@ export default (userSettings) => {
|
|
|
687
772
|
switch (vdom.type) {
|
|
688
773
|
case ELEMENT_NODE:
|
|
689
774
|
case OPAQUE_NODE:
|
|
690
|
-
if (
|
|
775
|
+
if (
|
|
776
|
+
0 !==
|
|
777
|
+
target.nodeName.localeCompare(convertTagName(vdom.tag), undefined, {
|
|
778
|
+
sensitivity: 'base',
|
|
779
|
+
})
|
|
780
|
+
) {
|
|
691
781
|
throw new Error('incompatible target for vdom');
|
|
692
782
|
}
|
|
693
783
|
break;
|
|
694
784
|
}
|
|
695
785
|
|
|
696
|
-
target[NODE_STATE] = {
|
|
786
|
+
target[NODE_STATE] = {originalProps: {}, newVdom: vdom};
|
|
697
787
|
try {
|
|
698
788
|
vdom.hooks?.$attach?.(target);
|
|
699
789
|
if (vdom.type === SPECIAL_NODE) {
|
|
@@ -709,9 +799,32 @@ export default (userSettings) => {
|
|
|
709
799
|
throw new Error('invalid vdom');
|
|
710
800
|
}
|
|
711
801
|
|
|
712
|
-
return {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
802
|
+
return {
|
|
803
|
+
h,
|
|
804
|
+
alias,
|
|
805
|
+
special,
|
|
806
|
+
reconcile,
|
|
807
|
+
settings: {
|
|
808
|
+
shouldUpdate,
|
|
809
|
+
isMap,
|
|
810
|
+
mapIter,
|
|
811
|
+
mapGet,
|
|
812
|
+
mapMerge,
|
|
813
|
+
newMap,
|
|
814
|
+
mapPut,
|
|
815
|
+
isSeq,
|
|
816
|
+
flattenSeq,
|
|
817
|
+
seqIter,
|
|
818
|
+
convertTagName,
|
|
819
|
+
convertPropName,
|
|
820
|
+
convertStyleName,
|
|
821
|
+
convertDataName,
|
|
822
|
+
convertClassName,
|
|
823
|
+
convertHookName,
|
|
824
|
+
convertName,
|
|
825
|
+
listenerKey,
|
|
826
|
+
captureKey,
|
|
827
|
+
passiveKey,
|
|
828
|
+
},
|
|
829
|
+
};
|
|
830
|
+
};
|