@ryupold/vode 1.1.9 → 1.3.0
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 +57 -3
- package/dist/vode.js +233 -112
- package/dist/vode.min.js +1 -1
- package/dist/vode.min.mjs +1 -1
- package/dist/vode.mjs +232 -116
- package/index.ts +5 -1
- package/package.json +4 -4
- package/src/merge-class.ts +62 -0
- package/src/state-context.ts +228 -0
- package/src/vode-tags.ts +204 -203
- package/src/vode.ts +154 -164
- package/tsconfig.json +1 -2
package/dist/vode.mjs
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
// src/vode.ts
|
|
2
|
+
var globals = {
|
|
3
|
+
currentViewTransition: undefined,
|
|
4
|
+
requestAnimationFrame: window.requestAnimationFrame ? window.requestAnimationFrame.bind(window) : (cb) => cb(),
|
|
5
|
+
startViewTransition: document.startViewTransition ? document.startViewTransition.bind(document) : null
|
|
6
|
+
};
|
|
2
7
|
function vode(tag, props, ...children) {
|
|
3
8
|
if (!tag)
|
|
4
9
|
throw new Error("first argument to vode() must be a tag name or a vode");
|
|
@@ -17,12 +22,16 @@ function app(container, state, dom, ...initialPatches) {
|
|
|
17
22
|
if (typeof dom !== "function")
|
|
18
23
|
throw new Error("third argument to app() must be a function that returns a vode");
|
|
19
24
|
const _vode = {};
|
|
20
|
-
_vode.
|
|
25
|
+
_vode.syncRenderer = globals.requestAnimationFrame;
|
|
26
|
+
_vode.asyncRenderer = globals.startViewTransition;
|
|
27
|
+
_vode.qSync = null;
|
|
28
|
+
_vode.qAsync = null;
|
|
29
|
+
_vode.stats = { lastSyncRenderTime: 0, lastAsyncRenderTime: 0, syncRenderCount: 0, asyncRenderCount: 0, liveEffectCount: 0, patchCount: 0, syncRenderPatchCount: 0, asyncRenderPatchCount: 0 };
|
|
21
30
|
Object.defineProperty(state, "patch", {
|
|
22
31
|
enumerable: false,
|
|
23
32
|
configurable: true,
|
|
24
33
|
writable: false,
|
|
25
|
-
value: async (action) => {
|
|
34
|
+
value: async (action, isAsync) => {
|
|
26
35
|
if (!action || typeof action !== "function" && typeof action !== "object")
|
|
27
36
|
return;
|
|
28
37
|
_vode.stats.patchCount++;
|
|
@@ -34,13 +43,13 @@ function app(container, state, dom, ...initialPatches) {
|
|
|
34
43
|
while (v.done === false) {
|
|
35
44
|
_vode.stats.liveEffectCount++;
|
|
36
45
|
try {
|
|
37
|
-
_vode.patch(v.value);
|
|
46
|
+
_vode.patch(v.value, isAsync);
|
|
38
47
|
v = await generator.next();
|
|
39
48
|
} finally {
|
|
40
49
|
_vode.stats.liveEffectCount--;
|
|
41
50
|
}
|
|
42
51
|
}
|
|
43
|
-
_vode.patch(v.value);
|
|
52
|
+
_vode.patch(v.value, isAsync);
|
|
44
53
|
} finally {
|
|
45
54
|
_vode.stats.liveEffectCount--;
|
|
46
55
|
}
|
|
@@ -48,60 +57,96 @@ function app(container, state, dom, ...initialPatches) {
|
|
|
48
57
|
_vode.stats.liveEffectCount++;
|
|
49
58
|
try {
|
|
50
59
|
const nextState = await action;
|
|
51
|
-
_vode.patch(nextState);
|
|
60
|
+
_vode.patch(nextState, isAsync);
|
|
52
61
|
} finally {
|
|
53
62
|
_vode.stats.liveEffectCount--;
|
|
54
63
|
}
|
|
55
64
|
} else if (Array.isArray(action)) {
|
|
56
|
-
if (
|
|
57
|
-
|
|
58
|
-
_vode.patch(
|
|
59
|
-
|
|
60
|
-
_vode.patch(action[0](_vode.state));
|
|
65
|
+
if (action.length > 0) {
|
|
66
|
+
for (const p of action) {
|
|
67
|
+
_vode.patch(p, !document.hidden && !!_vode.asyncRenderer);
|
|
68
|
+
}
|
|
61
69
|
} else {
|
|
62
|
-
_vode.
|
|
70
|
+
_vode.qSync = mergeState(_vode.qSync || {}, _vode.qAsync, false);
|
|
71
|
+
_vode.qAsync = null;
|
|
72
|
+
globals.currentViewTransition?.skipTransition();
|
|
73
|
+
_vode.stats.syncRenderPatchCount++;
|
|
74
|
+
_vode.renderSync();
|
|
63
75
|
}
|
|
64
76
|
} else if (typeof action === "function") {
|
|
65
|
-
_vode.patch(action(_vode.state));
|
|
77
|
+
_vode.patch(action(_vode.state), isAsync);
|
|
66
78
|
} else {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
_vode.
|
|
79
|
+
if (isAsync) {
|
|
80
|
+
_vode.stats.asyncRenderPatchCount++;
|
|
81
|
+
_vode.qAsync = mergeState(_vode.qAsync || {}, action, false);
|
|
82
|
+
await _vode.renderAsync();
|
|
83
|
+
} else {
|
|
84
|
+
_vode.stats.syncRenderPatchCount++;
|
|
85
|
+
_vode.qSync = mergeState(_vode.qSync || {}, action, false);
|
|
86
|
+
_vode.renderSync();
|
|
87
|
+
}
|
|
71
88
|
}
|
|
72
89
|
}
|
|
73
90
|
});
|
|
74
|
-
|
|
91
|
+
function renderDom(isAsync) {
|
|
92
|
+
const sw = Date.now();
|
|
93
|
+
const vom = dom(_vode.state);
|
|
94
|
+
_vode.vode = render(_vode.state, _vode.patch, container.parentElement, 0, _vode.vode, vom);
|
|
95
|
+
if (container.tagName.toUpperCase() !== vom[0].toUpperCase()) {
|
|
96
|
+
container = _vode.vode.node;
|
|
97
|
+
container._vode = _vode;
|
|
98
|
+
}
|
|
99
|
+
if (!isAsync) {
|
|
100
|
+
_vode.stats.lastSyncRenderTime = Date.now() - sw;
|
|
101
|
+
_vode.stats.syncRenderCount++;
|
|
102
|
+
_vode.isRendering = false;
|
|
103
|
+
if (_vode.qSync)
|
|
104
|
+
_vode.renderSync();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const sr = renderDom.bind(null, false);
|
|
108
|
+
const ar = renderDom.bind(null, true);
|
|
109
|
+
Object.defineProperty(_vode, "renderSync", {
|
|
75
110
|
enumerable: false,
|
|
76
111
|
configurable: true,
|
|
77
112
|
writable: false,
|
|
78
|
-
value: () =>
|
|
79
|
-
if (_vode.isRendering || !_vode.
|
|
113
|
+
value: () => {
|
|
114
|
+
if (_vode.isRendering || !_vode.qSync)
|
|
80
115
|
return;
|
|
81
116
|
_vode.isRendering = true;
|
|
117
|
+
_vode.state = mergeState(_vode.state, _vode.qSync, true);
|
|
118
|
+
_vode.qSync = null;
|
|
119
|
+
_vode.syncRenderer(sr);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
Object.defineProperty(_vode, "renderAsync", {
|
|
123
|
+
enumerable: false,
|
|
124
|
+
configurable: true,
|
|
125
|
+
writable: false,
|
|
126
|
+
value: async () => {
|
|
127
|
+
if (_vode.isAnimating || !_vode.qAsync)
|
|
128
|
+
return;
|
|
129
|
+
await globals.currentViewTransition?.updateCallbackDone;
|
|
130
|
+
if (_vode.isAnimating || !_vode.qAsync || document.hidden)
|
|
131
|
+
return;
|
|
132
|
+
_vode.isAnimating = true;
|
|
82
133
|
const sw = Date.now();
|
|
83
134
|
try {
|
|
84
|
-
_vode.state = mergeState(_vode.state, _vode.
|
|
85
|
-
_vode.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
if (container.tagName.toUpperCase() !== vom[0].toUpperCase()) {
|
|
89
|
-
container = _vode.vode.node;
|
|
90
|
-
container._vode = _vode;
|
|
91
|
-
}
|
|
135
|
+
_vode.state = mergeState(_vode.state, _vode.qAsync, true);
|
|
136
|
+
_vode.qAsync = null;
|
|
137
|
+
globals.currentViewTransition = _vode.asyncRenderer(ar);
|
|
138
|
+
await globals.currentViewTransition?.updateCallbackDone;
|
|
92
139
|
} finally {
|
|
93
|
-
_vode.
|
|
94
|
-
_vode.stats.
|
|
95
|
-
_vode.
|
|
96
|
-
if (_vode.q) {
|
|
97
|
-
_vode.render();
|
|
98
|
-
}
|
|
140
|
+
_vode.stats.lastAsyncRenderTime = Date.now() - sw;
|
|
141
|
+
_vode.stats.asyncRenderCount++;
|
|
142
|
+
_vode.isAnimating = false;
|
|
99
143
|
}
|
|
100
|
-
|
|
144
|
+
if (_vode.qAsync)
|
|
145
|
+
_vode.renderAsync();
|
|
146
|
+
}
|
|
101
147
|
});
|
|
102
148
|
_vode.patch = state.patch;
|
|
103
149
|
_vode.state = state;
|
|
104
|
-
_vode.q = null;
|
|
105
150
|
const root = container;
|
|
106
151
|
root._vode = _vode;
|
|
107
152
|
_vode.vode = render(state, _vode.patch, container.parentElement, Array.from(container.parentElement.children).indexOf(container), hydrate(container, true), dom(state));
|
|
@@ -175,49 +220,6 @@ function props(vode2) {
|
|
|
175
220
|
}
|
|
176
221
|
return;
|
|
177
222
|
}
|
|
178
|
-
function mergeClass(a, b) {
|
|
179
|
-
if (!a)
|
|
180
|
-
return b;
|
|
181
|
-
if (!b)
|
|
182
|
-
return a;
|
|
183
|
-
if (typeof a === "string" && typeof b === "string") {
|
|
184
|
-
const aSplit = a.split(" ");
|
|
185
|
-
const bSplit = b.split(" ");
|
|
186
|
-
const classSet = new Set([...aSplit, ...bSplit]);
|
|
187
|
-
return Array.from(classSet).join(" ").trim();
|
|
188
|
-
} else if (typeof a === "string" && Array.isArray(b)) {
|
|
189
|
-
const classSet = new Set([...b, ...a.split(" ")]);
|
|
190
|
-
return Array.from(classSet).join(" ").trim();
|
|
191
|
-
} else if (Array.isArray(a) && typeof b === "string") {
|
|
192
|
-
const classSet = new Set([...a, ...b.split(" ")]);
|
|
193
|
-
return Array.from(classSet).join(" ").trim();
|
|
194
|
-
} else if (Array.isArray(a) && Array.isArray(b)) {
|
|
195
|
-
const classSet = new Set([...a, ...b]);
|
|
196
|
-
return Array.from(classSet).join(" ").trim();
|
|
197
|
-
} else if (typeof a === "string" && typeof b === "object") {
|
|
198
|
-
return { [a]: true, ...b };
|
|
199
|
-
} else if (typeof a === "object" && typeof b === "string") {
|
|
200
|
-
return { ...a, [b]: true };
|
|
201
|
-
} else if (typeof a === "object" && typeof b === "object") {
|
|
202
|
-
return { ...a, ...b };
|
|
203
|
-
} else if (typeof a === "object" && Array.isArray(b)) {
|
|
204
|
-
const aa = { ...a };
|
|
205
|
-
for (const item of b) {
|
|
206
|
-
aa[item] = true;
|
|
207
|
-
}
|
|
208
|
-
return aa;
|
|
209
|
-
} else if (Array.isArray(a) && typeof b === "object") {
|
|
210
|
-
const aa = {};
|
|
211
|
-
for (const item of a) {
|
|
212
|
-
aa[item] = true;
|
|
213
|
-
}
|
|
214
|
-
for (const bKey of Object.keys(b)) {
|
|
215
|
-
aa[bKey] = b[bKey];
|
|
216
|
-
}
|
|
217
|
-
return aa;
|
|
218
|
-
}
|
|
219
|
-
throw new Error(`cannot merge classes of ${a} (${typeof a}) and ${b} (${typeof b})`);
|
|
220
|
-
}
|
|
221
223
|
function children(vode2) {
|
|
222
224
|
const start = childrenStart(vode2);
|
|
223
225
|
if (start > 0) {
|
|
@@ -269,7 +271,7 @@ function mergeState(target, source, allowDeletion) {
|
|
|
269
271
|
}
|
|
270
272
|
return target;
|
|
271
273
|
}
|
|
272
|
-
function render(state, patch, parent, childIndex, oldVode, newVode,
|
|
274
|
+
function render(state, patch, parent, childIndex, oldVode, newVode, xmlns) {
|
|
273
275
|
newVode = remember(state, newVode, oldVode);
|
|
274
276
|
const isNoVode = !newVode || typeof newVode === "number" || typeof newVode === "boolean";
|
|
275
277
|
if (newVode === oldVode || !oldVode && isNoVode) {
|
|
@@ -313,15 +315,15 @@ function render(state, patch, parent, childIndex, oldVode, newVode, svg) {
|
|
|
313
315
|
return text;
|
|
314
316
|
}
|
|
315
317
|
if (isNode && (!oldNode || oldIsText || oldVode[0] !== newVode[0])) {
|
|
316
|
-
svg = svg || newVode[0] === "svg";
|
|
317
|
-
const newNode = svg ? document.createElementNS("http://www.w3.org/2000/svg", newVode[0]) : document.createElement(newVode[0]);
|
|
318
|
-
newVode.node = newNode;
|
|
319
318
|
const newvode = newVode;
|
|
320
319
|
if (1 in newvode) {
|
|
321
320
|
newvode[1] = remember(state, newvode[1], undefined);
|
|
322
321
|
}
|
|
323
322
|
const properties = props(newVode);
|
|
324
|
-
|
|
323
|
+
xmlns = properties?.xmlns || xmlns;
|
|
324
|
+
const newNode = xmlns ? document.createElementNS(xmlns, newVode[0]) : document.createElement(newVode[0]);
|
|
325
|
+
newVode.node = newNode;
|
|
326
|
+
patchProperties(state, patch, newNode, undefined, properties);
|
|
325
327
|
if (oldNode) {
|
|
326
328
|
oldNode.onUnmount && patch(oldNode.onUnmount(oldNode));
|
|
327
329
|
oldNode.replaceWith(newNode);
|
|
@@ -336,7 +338,7 @@ function render(state, patch, parent, childIndex, oldVode, newVode, svg) {
|
|
|
336
338
|
if (newChildren) {
|
|
337
339
|
for (let i = 0;i < newChildren.length; i++) {
|
|
338
340
|
const child2 = newChildren[i];
|
|
339
|
-
const attached = render(state, patch, newNode, i, undefined, child2,
|
|
341
|
+
const attached = render(state, patch, newNode, i, undefined, child2, xmlns);
|
|
340
342
|
newVode[properties ? i + 2 : i + 1] = attached;
|
|
341
343
|
}
|
|
342
344
|
}
|
|
@@ -344,7 +346,6 @@ function render(state, patch, parent, childIndex, oldVode, newVode, svg) {
|
|
|
344
346
|
return newVode;
|
|
345
347
|
}
|
|
346
348
|
if (!oldIsText && isNode && oldVode[0] === newVode[0]) {
|
|
347
|
-
svg = svg || newVode[0] === "svg";
|
|
348
349
|
newVode.node = oldNode;
|
|
349
350
|
const newvode = newVode;
|
|
350
351
|
const oldvode = oldVode;
|
|
@@ -354,12 +355,12 @@ function render(state, patch, parent, childIndex, oldVode, newVode, svg) {
|
|
|
354
355
|
newvode[1] = remember(state, newvode[1], oldvode[1]);
|
|
355
356
|
if (prev !== newvode[1]) {
|
|
356
357
|
const properties = props(newVode);
|
|
357
|
-
patchProperties(patch, oldNode, props(oldVode), properties
|
|
358
|
+
patchProperties(state, patch, oldNode, props(oldVode), properties);
|
|
358
359
|
hasProps = !!properties;
|
|
359
360
|
}
|
|
360
361
|
} else {
|
|
361
362
|
const properties = props(newVode);
|
|
362
|
-
patchProperties(patch, oldNode, props(oldVode), properties
|
|
363
|
+
patchProperties(state, patch, oldNode, props(oldVode), properties);
|
|
363
364
|
hasProps = !!properties;
|
|
364
365
|
}
|
|
365
366
|
const newKids = children(newVode);
|
|
@@ -368,7 +369,7 @@ function render(state, patch, parent, childIndex, oldVode, newVode, svg) {
|
|
|
368
369
|
for (let i = 0;i < newKids.length; i++) {
|
|
369
370
|
const child2 = newKids[i];
|
|
370
371
|
const oldChild = oldKids && oldKids[i];
|
|
371
|
-
const attached = render(state, patch, oldNode, i, oldChild, child2,
|
|
372
|
+
const attached = render(state, patch, oldNode, i, oldChild, child2, xmlns);
|
|
372
373
|
if (attached) {
|
|
373
374
|
newVode[hasProps ? i + 2 : i + 1] = attached;
|
|
374
375
|
}
|
|
@@ -425,7 +426,7 @@ function unwrap(c, s) {
|
|
|
425
426
|
return c;
|
|
426
427
|
}
|
|
427
428
|
}
|
|
428
|
-
function patchProperties(patch, node, oldProps, newProps
|
|
429
|
+
function patchProperties(s, patch, node, oldProps, newProps) {
|
|
429
430
|
if (!newProps && !oldProps)
|
|
430
431
|
return;
|
|
431
432
|
if (oldProps) {
|
|
@@ -434,9 +435,9 @@ function patchProperties(patch, node, oldProps, newProps, isSvg) {
|
|
|
434
435
|
const newValue = newProps?.[key];
|
|
435
436
|
if (oldValue !== newValue) {
|
|
436
437
|
if (newProps)
|
|
437
|
-
newProps[key] = patchProperty(patch, node, key, oldValue, newValue
|
|
438
|
+
newProps[key] = patchProperty(s, patch, node, key, oldValue, newValue);
|
|
438
439
|
else
|
|
439
|
-
patchProperty(patch, node, key, oldValue, undefined
|
|
440
|
+
patchProperty(s, patch, node, key, oldValue, undefined);
|
|
440
441
|
}
|
|
441
442
|
}
|
|
442
443
|
}
|
|
@@ -444,17 +445,17 @@ function patchProperties(patch, node, oldProps, newProps, isSvg) {
|
|
|
444
445
|
for (const key in newProps) {
|
|
445
446
|
if (!(key in oldProps)) {
|
|
446
447
|
const newValue = newProps[key];
|
|
447
|
-
newProps[key] = patchProperty(patch, node, key, undefined, newValue
|
|
448
|
+
newProps[key] = patchProperty(s, patch, node, key, undefined, newValue);
|
|
448
449
|
}
|
|
449
450
|
}
|
|
450
451
|
} else if (newProps) {
|
|
451
452
|
for (const key in newProps) {
|
|
452
453
|
const newValue = newProps[key];
|
|
453
|
-
newProps[key] = patchProperty(patch, node, key, undefined, newValue
|
|
454
|
+
newProps[key] = patchProperty(s, patch, node, key, undefined, newValue);
|
|
454
455
|
}
|
|
455
456
|
}
|
|
456
457
|
}
|
|
457
|
-
function patchProperty(patch, node, key, oldValue, newValue
|
|
458
|
+
function patchProperty(s, patch, node, key, oldValue, newValue) {
|
|
458
459
|
if (key === "style") {
|
|
459
460
|
if (!newValue) {
|
|
460
461
|
node.style.cssText = "";
|
|
@@ -475,35 +476,17 @@ function patchProperty(patch, node, key, oldValue, newValue, isSvg) {
|
|
|
475
476
|
}
|
|
476
477
|
}
|
|
477
478
|
} else if (key === "class") {
|
|
478
|
-
if (
|
|
479
|
-
|
|
480
|
-
const newClass = classString(newValue);
|
|
481
|
-
node.classList.value = newClass;
|
|
482
|
-
} else {
|
|
483
|
-
node.classList.value = "";
|
|
484
|
-
}
|
|
479
|
+
if (newValue) {
|
|
480
|
+
node.setAttribute("class", classString(newValue));
|
|
485
481
|
} else {
|
|
486
|
-
|
|
487
|
-
const newClass = classString(newValue);
|
|
488
|
-
node.className = newClass;
|
|
489
|
-
} else {
|
|
490
|
-
node.className = "";
|
|
491
|
-
}
|
|
482
|
+
node.removeAttribute("class");
|
|
492
483
|
}
|
|
493
484
|
} else if (key[0] === "o" && key[1] === "n") {
|
|
494
485
|
if (newValue) {
|
|
495
486
|
let eventHandler = null;
|
|
496
487
|
if (typeof newValue === "function") {
|
|
497
488
|
const action = newValue;
|
|
498
|
-
eventHandler = (evt) => patch(
|
|
499
|
-
} else if (Array.isArray(newValue)) {
|
|
500
|
-
const arr = newValue;
|
|
501
|
-
const action = newValue[0];
|
|
502
|
-
if (arr.length > 1) {
|
|
503
|
-
eventHandler = () => patch([action, ...arr.slice(1)]);
|
|
504
|
-
} else {
|
|
505
|
-
eventHandler = (evt) => patch([action, evt]);
|
|
506
|
-
}
|
|
489
|
+
eventHandler = (evt) => patch(action(s, evt));
|
|
507
490
|
} else if (typeof newValue === "object") {
|
|
508
491
|
eventHandler = () => patch(newValue);
|
|
509
492
|
}
|
|
@@ -612,6 +595,7 @@ var RUBY = "ruby";
|
|
|
612
595
|
var S = "s";
|
|
613
596
|
var SAMP = "samp";
|
|
614
597
|
var SCRIPT = "script";
|
|
598
|
+
var SEARCH = "search";
|
|
615
599
|
var SECTION = "section";
|
|
616
600
|
var SELECT = "select";
|
|
617
601
|
var SLOT = "slot";
|
|
@@ -637,6 +621,7 @@ var TR = "tr";
|
|
|
637
621
|
var TRACK = "track";
|
|
638
622
|
var U = "u";
|
|
639
623
|
var UL = "ul";
|
|
624
|
+
var VAR = "var";
|
|
640
625
|
var VIDEO = "video";
|
|
641
626
|
var WBR = "wbr";
|
|
642
627
|
var ANIMATE = "animate";
|
|
@@ -728,6 +713,132 @@ var MTR = "mtr";
|
|
|
728
713
|
var MUNDER = "munder";
|
|
729
714
|
var MUNDEROVER = "munderover";
|
|
730
715
|
var SEMANTICS = "semantics";
|
|
716
|
+
// src/merge-class.ts
|
|
717
|
+
function mergeClass(...classes) {
|
|
718
|
+
if (!classes || classes.length === 0)
|
|
719
|
+
return null;
|
|
720
|
+
if (classes.length === 1)
|
|
721
|
+
return classes[0];
|
|
722
|
+
let finalClass = classes[0];
|
|
723
|
+
for (let index = 1;index < classes.length; index++) {
|
|
724
|
+
const a = finalClass, b = classes[index];
|
|
725
|
+
if (!a) {
|
|
726
|
+
finalClass = b;
|
|
727
|
+
} else if (!b) {
|
|
728
|
+
continue;
|
|
729
|
+
} else if (typeof a === "string" && typeof b === "string") {
|
|
730
|
+
const aSplit = a.split(" ");
|
|
731
|
+
const bSplit = b.split(" ");
|
|
732
|
+
const classSet = new Set([...aSplit, ...bSplit]);
|
|
733
|
+
finalClass = Array.from(classSet).join(" ").trim();
|
|
734
|
+
} else if (typeof a === "string" && Array.isArray(b)) {
|
|
735
|
+
const classSet = new Set([...b, ...a.split(" ")]);
|
|
736
|
+
finalClass = Array.from(classSet).join(" ").trim();
|
|
737
|
+
} else if (Array.isArray(a) && typeof b === "string") {
|
|
738
|
+
const classSet = new Set([...a, ...b.split(" ")]);
|
|
739
|
+
finalClass = Array.from(classSet).join(" ").trim();
|
|
740
|
+
} else if (Array.isArray(a) && Array.isArray(b)) {
|
|
741
|
+
const classSet = new Set([...a, ...b]);
|
|
742
|
+
finalClass = Array.from(classSet).join(" ").trim();
|
|
743
|
+
} else if (typeof a === "string" && typeof b === "object") {
|
|
744
|
+
finalClass = { [a]: true, ...b };
|
|
745
|
+
} else if (typeof a === "object" && typeof b === "string") {
|
|
746
|
+
finalClass = { ...a, [b]: true };
|
|
747
|
+
} else if (typeof a === "object" && typeof b === "object") {
|
|
748
|
+
finalClass = { ...a, ...b };
|
|
749
|
+
} else if (typeof a === "object" && Array.isArray(b)) {
|
|
750
|
+
const aa = { ...a };
|
|
751
|
+
for (const item of b) {
|
|
752
|
+
aa[item] = true;
|
|
753
|
+
}
|
|
754
|
+
finalClass = aa;
|
|
755
|
+
} else if (Array.isArray(a) && typeof b === "object") {
|
|
756
|
+
const aa = {};
|
|
757
|
+
for (const item of a) {
|
|
758
|
+
aa[item] = true;
|
|
759
|
+
}
|
|
760
|
+
for (const bKey of Object.keys(b)) {
|
|
761
|
+
aa[bKey] = b[bKey];
|
|
762
|
+
}
|
|
763
|
+
finalClass = aa;
|
|
764
|
+
} else
|
|
765
|
+
throw new Error(`cannot merge classes of ${a} (${typeof a}) and ${b} (${typeof b})`);
|
|
766
|
+
}
|
|
767
|
+
return finalClass;
|
|
768
|
+
}
|
|
769
|
+
// src/state-context.ts
|
|
770
|
+
class KeyStateContext {
|
|
771
|
+
state;
|
|
772
|
+
path;
|
|
773
|
+
keys;
|
|
774
|
+
constructor(state, path) {
|
|
775
|
+
this.state = state;
|
|
776
|
+
this.path = path;
|
|
777
|
+
this.keys = path.split(".");
|
|
778
|
+
}
|
|
779
|
+
get() {
|
|
780
|
+
const keys = this.keys;
|
|
781
|
+
let raw = this.state ? this.state[keys[0]] : undefined;
|
|
782
|
+
for (let i = 1;i < keys.length && !!raw; i++) {
|
|
783
|
+
raw = raw[keys[i]];
|
|
784
|
+
}
|
|
785
|
+
return raw;
|
|
786
|
+
}
|
|
787
|
+
put(value) {
|
|
788
|
+
this.putDeep(value, this.state);
|
|
789
|
+
}
|
|
790
|
+
patch(value) {
|
|
791
|
+
if (Array.isArray(value)) {
|
|
792
|
+
const animation = [];
|
|
793
|
+
for (const v of value) {
|
|
794
|
+
animation.push(this.createPatch(v));
|
|
795
|
+
}
|
|
796
|
+
this.state.patch(animation);
|
|
797
|
+
}
|
|
798
|
+
this.state.patch(this.createPatch(value));
|
|
799
|
+
}
|
|
800
|
+
createPatch(value) {
|
|
801
|
+
const renderPatch = {};
|
|
802
|
+
this.putDeep(value, renderPatch);
|
|
803
|
+
return renderPatch;
|
|
804
|
+
}
|
|
805
|
+
putDeep(value, target) {
|
|
806
|
+
const keys = this.keys;
|
|
807
|
+
if (keys.length > 1) {
|
|
808
|
+
let i = 0;
|
|
809
|
+
let raw = target[keys[i]];
|
|
810
|
+
if (typeof raw !== "object" || raw === null) {
|
|
811
|
+
target[keys[i]] = raw = {};
|
|
812
|
+
}
|
|
813
|
+
for (i = 1;i < keys.length - 1; i++) {
|
|
814
|
+
const p = raw;
|
|
815
|
+
raw = raw[keys[i]];
|
|
816
|
+
if (typeof raw !== "object" || raw === null) {
|
|
817
|
+
p[keys[i]] = raw = {};
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
raw[keys[i]] = value;
|
|
821
|
+
} else {
|
|
822
|
+
if (typeof target[keys[0]] === "object" && typeof value === "object")
|
|
823
|
+
Object.assign(target[keys[0]], value);
|
|
824
|
+
else
|
|
825
|
+
target[keys[0]] = value;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
class DelegateStateContext {
|
|
831
|
+
state;
|
|
832
|
+
get;
|
|
833
|
+
put;
|
|
834
|
+
patch;
|
|
835
|
+
constructor(state, get, put, patch) {
|
|
836
|
+
this.state = state;
|
|
837
|
+
this.get = get;
|
|
838
|
+
this.put = put;
|
|
839
|
+
this.patch = patch;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
731
842
|
export {
|
|
732
843
|
vode,
|
|
733
844
|
tag,
|
|
@@ -735,6 +846,7 @@ export {
|
|
|
735
846
|
mergeClass,
|
|
736
847
|
memo,
|
|
737
848
|
hydrate,
|
|
849
|
+
globals,
|
|
738
850
|
createState,
|
|
739
851
|
createPatch,
|
|
740
852
|
childrenStart,
|
|
@@ -745,6 +857,7 @@ export {
|
|
|
745
857
|
WBR,
|
|
746
858
|
VIEW,
|
|
747
859
|
VIDEO,
|
|
860
|
+
VAR,
|
|
748
861
|
USE,
|
|
749
862
|
UL,
|
|
750
863
|
U,
|
|
@@ -780,6 +893,7 @@ export {
|
|
|
780
893
|
SEMANTICS,
|
|
781
894
|
SELECT,
|
|
782
895
|
SECTION,
|
|
896
|
+
SEARCH,
|
|
783
897
|
SCRIPT,
|
|
784
898
|
SAMP,
|
|
785
899
|
S,
|
|
@@ -847,6 +961,7 @@ export {
|
|
|
847
961
|
LI,
|
|
848
962
|
LEGEND,
|
|
849
963
|
LABEL,
|
|
964
|
+
KeyStateContext,
|
|
850
965
|
KBD,
|
|
851
966
|
INS,
|
|
852
967
|
INPUT,
|
|
@@ -901,6 +1016,7 @@ export {
|
|
|
901
1016
|
EMBED,
|
|
902
1017
|
EM,
|
|
903
1018
|
ELLIPSE,
|
|
1019
|
+
DelegateStateContext,
|
|
904
1020
|
DT,
|
|
905
1021
|
DL,
|
|
906
1022
|
DIV,
|
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ryupold/vode",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "a minimalist web framework",
|
|
5
5
|
"author": "Michael Scherbakow (ryupold)",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"icon": "icon.webp",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"watch": "tsc -b -w"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"bun": "1.
|
|
37
|
-
"esbuild": "0.25.
|
|
36
|
+
"bun": "1.3.1",
|
|
37
|
+
"esbuild": "0.25.11",
|
|
38
38
|
"typescript": "5.9.3"
|
|
39
39
|
}
|
|
40
40
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ClassProp } from "./vode.js";
|
|
2
|
+
|
|
3
|
+
/** merge `ClassProp`s regardless of structure */
|
|
4
|
+
export function mergeClass(...classes: ClassProp[]): ClassProp {
|
|
5
|
+
if ((!classes || classes.length === 0)) return null;
|
|
6
|
+
if (classes.length === 1) return classes[0];
|
|
7
|
+
|
|
8
|
+
let finalClass: ClassProp = classes[0];
|
|
9
|
+
for (let index = 1; index < classes.length; index++) {
|
|
10
|
+
const a = finalClass, b = classes[index];
|
|
11
|
+
if (!a) {
|
|
12
|
+
finalClass = b;
|
|
13
|
+
}
|
|
14
|
+
else if (!b) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
else if (typeof a === "string" && typeof b === "string") {
|
|
18
|
+
const aSplit = a.split(" ");
|
|
19
|
+
const bSplit = b.split(" ");
|
|
20
|
+
const classSet = new Set([...aSplit, ...bSplit]);
|
|
21
|
+
finalClass = Array.from(classSet).join(" ").trim();
|
|
22
|
+
}
|
|
23
|
+
else if (typeof a === "string" && Array.isArray(b)) {
|
|
24
|
+
const classSet = new Set([...b, ...a.split(" ")]);
|
|
25
|
+
finalClass = Array.from(classSet).join(" ").trim();
|
|
26
|
+
}
|
|
27
|
+
else if (Array.isArray(a) && typeof b === "string") {
|
|
28
|
+
const classSet = new Set([...a, ...b.split(" ")]);
|
|
29
|
+
finalClass = Array.from(classSet).join(" ").trim();
|
|
30
|
+
}
|
|
31
|
+
else if (Array.isArray(a) && Array.isArray(b)) {
|
|
32
|
+
const classSet = new Set([...a, ...b]);
|
|
33
|
+
finalClass = Array.from(classSet).join(" ").trim();
|
|
34
|
+
}
|
|
35
|
+
else if (typeof a === "string" && typeof b === "object") {
|
|
36
|
+
finalClass = { [a]: true, ...b };
|
|
37
|
+
}
|
|
38
|
+
else if (typeof a === "object" && typeof b === "string") {
|
|
39
|
+
finalClass = { ...a, [b]: true };
|
|
40
|
+
}
|
|
41
|
+
else if (typeof a === "object" && typeof b === "object") {
|
|
42
|
+
finalClass = { ...a, ...b };
|
|
43
|
+
} else if (typeof a === "object" && Array.isArray(b)) {
|
|
44
|
+
const aa = { ...a };
|
|
45
|
+
for (const item of b as string[]) {
|
|
46
|
+
(<Record<string, boolean | null | undefined>>aa)[item] = true;
|
|
47
|
+
}
|
|
48
|
+
finalClass = aa;
|
|
49
|
+
} else if (Array.isArray(a) && typeof b === "object") {
|
|
50
|
+
const aa: Record<string, any> = {};
|
|
51
|
+
for (const item of a as string[]) {
|
|
52
|
+
aa[item] = true;
|
|
53
|
+
}
|
|
54
|
+
for (const bKey of Object.keys(b!)) {
|
|
55
|
+
aa[bKey] = (<Record<string, boolean | null | undefined>>b)[bKey];
|
|
56
|
+
}
|
|
57
|
+
finalClass = aa;
|
|
58
|
+
}
|
|
59
|
+
else throw new Error(`cannot merge classes of ${a} (${typeof a}) and ${b} (${typeof b})`);
|
|
60
|
+
}
|
|
61
|
+
return finalClass;
|
|
62
|
+
}
|