@3sln/dodo 0.0.7 → 0.0.9
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/index.js +3 -2
- package/package.json +1 -1
- package/src/scheduler.js +66 -60
- package/src/vdom.js +7 -5
package/index.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import vdomFactory from './src/vdom.js';
|
|
2
2
|
import htmlFactory from './src/html.js';
|
|
3
|
-
import
|
|
3
|
+
import schedulerFactory from './src/scheduler.js';
|
|
4
4
|
|
|
5
5
|
function dodoFactory(userSettings) {
|
|
6
6
|
const vdomInstance = vdomFactory(userSettings);
|
|
7
7
|
const htmlInstance = htmlFactory(vdomInstance);
|
|
8
|
+
const schedulerInstance = schedulerFactory(userSettings);
|
|
8
9
|
return {
|
|
9
10
|
...vdomInstance,
|
|
10
11
|
...htmlInstance,
|
|
11
|
-
...
|
|
12
|
+
...schedulerInstance,
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
|
package/package.json
CHANGED
package/src/scheduler.js
CHANGED
|
@@ -1,79 +1,85 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
export default function factory({window} = {}) {
|
|
2
|
+
const requestAnimationFrame = window?.requestAnimationFrame ?? globalThis.requestAnimationFrame;
|
|
3
|
+
const cancelAnimationFrame = window?.cancelAnimationFrame ?? globalThis.cancelAnimationFrame;
|
|
4
|
+
let scheduled = false;
|
|
5
|
+
let frameId = 0;
|
|
6
|
+
let queue = [];
|
|
4
7
|
|
|
5
|
-
const FRAME_BUDGET = 10; // ms
|
|
6
|
-
const CHUNK_SIZE = 100;
|
|
8
|
+
const FRAME_BUDGET = 10; // ms
|
|
9
|
+
const CHUNK_SIZE = 100;
|
|
7
10
|
|
|
8
|
-
function _runTasks(tasks) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
function _runTasks(tasks) {
|
|
12
|
+
for (const f of tasks) {
|
|
13
|
+
try {
|
|
14
|
+
f();
|
|
15
|
+
} catch (err) {
|
|
16
|
+
console.error('Error in scheduled function:', err);
|
|
17
|
+
}
|
|
14
18
|
}
|
|
15
19
|
}
|
|
16
|
-
}
|
|
17
20
|
|
|
18
|
-
function runQueue() {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
function runQueue() {
|
|
22
|
+
const startTime = performance.now();
|
|
23
|
+
frameId = 0;
|
|
21
24
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
while (queue.length > 0) {
|
|
26
|
+
const chunk = queue.splice(0, CHUNK_SIZE);
|
|
27
|
+
_runTasks(chunk);
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
if (performance.now() - startTime > FRAME_BUDGET && queue.length > 0) {
|
|
30
|
+
frameId = requestAnimationFrame(runQueue);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
29
33
|
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
scheduled = false;
|
|
33
|
-
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
export function schedule(f, {signal} = {}) {
|
|
37
|
-
if (signal?.aborted) {
|
|
38
|
-
return;
|
|
35
|
+
scheduled = false;
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
38
|
+
// Schedules a function to be executed on the next animation frame.
|
|
39
|
+
function schedule(f, {signal} = {}) {
|
|
40
|
+
if (signal?.aborted) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let task = f;
|
|
45
|
+
// If a signal is provided, wrap the task to check for abortion before execution.
|
|
46
|
+
if (signal) {
|
|
47
|
+
task = () => {
|
|
48
|
+
if (!signal.aborted) {
|
|
49
|
+
f();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
50
53
|
|
|
51
|
-
|
|
54
|
+
queue.push(task);
|
|
52
55
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
if (!scheduled) {
|
|
57
|
+
scheduled = true;
|
|
58
|
+
frameId = requestAnimationFrame(runQueue);
|
|
59
|
+
}
|
|
56
60
|
}
|
|
57
|
-
}
|
|
58
61
|
|
|
59
|
-
// Immediately runs all queued tasks synchronously.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
// Immediately runs all queued tasks synchronously.
|
|
63
|
+
function flush() {
|
|
64
|
+
if (frameId) {
|
|
65
|
+
cancelAnimationFrame(frameId);
|
|
66
|
+
}
|
|
67
|
+
const toRun = queue;
|
|
68
|
+
queue = [];
|
|
69
|
+
_runTasks(toRun);
|
|
70
|
+
scheduled = false;
|
|
71
|
+
frameId = 0;
|
|
63
72
|
}
|
|
64
|
-
const toRun = queue;
|
|
65
|
-
queue = [];
|
|
66
|
-
_runTasks(toRun);
|
|
67
|
-
scheduled = false;
|
|
68
|
-
frameId = 0;
|
|
69
|
-
}
|
|
70
73
|
|
|
71
|
-
// Clears all pending tasks.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
// Clears all pending tasks.
|
|
75
|
+
function clear() {
|
|
76
|
+
if (frameId) {
|
|
77
|
+
cancelAnimationFrame(frameId);
|
|
78
|
+
}
|
|
79
|
+
queue = [];
|
|
80
|
+
scheduled = false;
|
|
81
|
+
frameId = 0;
|
|
75
82
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
frameId = 0;
|
|
83
|
+
|
|
84
|
+
return {schedule, flush, clear};
|
|
79
85
|
}
|
package/src/vdom.js
CHANGED
|
@@ -395,7 +395,8 @@ export default userSettings => {
|
|
|
395
395
|
}
|
|
396
396
|
} else {
|
|
397
397
|
const oldHooks = state.vdom.hooks ?? EMPTY_MAP;
|
|
398
|
-
const
|
|
398
|
+
const newHooks = hooks ?? EMPTY_MAP;
|
|
399
|
+
const hooksIterator = toIterator(mapIter(newHooks));
|
|
399
400
|
let result;
|
|
400
401
|
while (!(result = hooksIterator.next()).done) {
|
|
401
402
|
const [name, listener] = result.value;
|
|
@@ -427,7 +428,7 @@ export default userSettings => {
|
|
|
427
428
|
while (!(result = oldHooksIterator.next()).done) {
|
|
428
429
|
const [name] = result.value;
|
|
429
430
|
const hookName = convertHookName(name);
|
|
430
|
-
if (hookName[0] === '$' || mapGet(
|
|
431
|
+
if (hookName[0] === '$' || mapGet(newHooks, name) !== undefined) continue;
|
|
431
432
|
const oldListener = mapGet(oldHooks, name);
|
|
432
433
|
if (typeof oldListener === 'function') {
|
|
433
434
|
target.removeEventListener(hookName, oldListener);
|
|
@@ -459,10 +460,10 @@ export default userSettings => {
|
|
|
459
460
|
break;
|
|
460
461
|
}
|
|
461
462
|
case ALIAS_NODE: {
|
|
462
|
-
const innerVdom = newVdom.tag.apply(
|
|
463
|
+
const innerVdom = newVdom.tag.apply(target, newVdom.args);
|
|
463
464
|
if (innerVdom === undefined || innerVdom === null) break;
|
|
464
465
|
if (isSeq(innerVdom)) {
|
|
465
|
-
reconcileElementChildren(target, flattenSeq(innerVdom));
|
|
466
|
+
reconcileElementChildren(target, flattenSeq(innerVdom, true));
|
|
466
467
|
} else {
|
|
467
468
|
reconcileElementChildren(target, flattenVNodeChildren([innerVdom]));
|
|
468
469
|
}
|
|
@@ -651,6 +652,7 @@ export default userSettings => {
|
|
|
651
652
|
target.removeChild(nodeToRemove);
|
|
652
653
|
}
|
|
653
654
|
|
|
655
|
+
const window = userSettings?.window ?? target.ownerDocument.defaultView;
|
|
654
656
|
const moveBefore = window.Element.prototype.moveBefore;
|
|
655
657
|
const insertBefore = window.Element.prototype.insertBefore;
|
|
656
658
|
if (target.isConnected) {
|
|
@@ -748,7 +750,7 @@ export default userSettings => {
|
|
|
748
750
|
}
|
|
749
751
|
|
|
750
752
|
if (isSeq(vdom)) {
|
|
751
|
-
reconcileElementChildren(target, flattenSeq(vdom));
|
|
753
|
+
reconcileElementChildren(target, flattenSeq(vdom, true));
|
|
752
754
|
return;
|
|
753
755
|
}
|
|
754
756
|
|