@ryupold/vode 0.11.0 → 0.12.1
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/package.json +1 -1
- package/src/vode.ts +21 -29
- package/vode.mjs +20 -21
package/package.json
CHANGED
package/src/vode.ts
CHANGED
|
@@ -11,10 +11,9 @@ export type Component<S> = (s: S) => ChildVode<S>;
|
|
|
11
11
|
|
|
12
12
|
export type Patch<S> =
|
|
13
13
|
| NoRenderPatch // ignored
|
|
14
|
-
|
|
|
14
|
+
| {} | DeepPartial<S> // render patches
|
|
15
15
|
| Promise<Patch<S>> | Effect<S>; // effects resulting in patches
|
|
16
16
|
|
|
17
|
-
export const EmptyPatch = {} as const; // smallest patch to cause a render without any changes
|
|
18
17
|
export type NoRenderPatch = undefined | null | number | boolean | bigint | string | symbol | void;
|
|
19
18
|
|
|
20
19
|
export type DeepPartial<S> = { [P in keyof S]?: S[P] extends Array<infer I> ? Array<Patch<I>> : Patch<S[P]> };
|
|
@@ -78,7 +77,7 @@ export type ContainerNode<S> = HTMLElement & {
|
|
|
78
77
|
vode: AttachedVode<S>, //don't touch this
|
|
79
78
|
patch: Dispatch<S>, // can't touch this
|
|
80
79
|
render: () => void, // can't touch this
|
|
81
|
-
q:
|
|
80
|
+
q: object | null, // next patch aggregate to be applied
|
|
82
81
|
isRendering: boolean, // under no circumstances touch this
|
|
83
82
|
/** stats about the overall patches & last render time */
|
|
84
83
|
stats: {
|
|
@@ -86,9 +85,7 @@ export type ContainerNode<S> = HTMLElement & {
|
|
|
86
85
|
liveEffectCount: number,
|
|
87
86
|
renderPatchCount: number,
|
|
88
87
|
renderCount: number,
|
|
89
|
-
|
|
90
|
-
queueLengthBeforeRender: number,
|
|
91
|
-
queueLengthAfterRender: number,
|
|
88
|
+
lastRenderTime: number,
|
|
92
89
|
},
|
|
93
90
|
}
|
|
94
91
|
};
|
|
@@ -97,7 +94,7 @@ export type ContainerNode<S> = HTMLElement & {
|
|
|
97
94
|
export function createState<S extends object | unknown>(state: S): PatchableState<S> { return state as PatchableState<S>; }
|
|
98
95
|
|
|
99
96
|
/** type safe way to create a patch. useful for type inference and autocompletion. */
|
|
100
|
-
export function createPatch<S extends object | unknown>(p: DeepPartial<S> | Effect<S> | NoRenderPatch):
|
|
97
|
+
export function createPatch<S extends object | unknown>(p: DeepPartial<S> | Effect<S> | NoRenderPatch): typeof p { return p; }
|
|
101
98
|
|
|
102
99
|
/** type-safe way to create a vode. useful for type inference and autocompletion.
|
|
103
100
|
*
|
|
@@ -107,13 +104,10 @@ export function createPatch<S extends object | unknown>(p: DeepPartial<S> | Effe
|
|
|
107
104
|
* - identity: `vode(["div", ["span", "bar"]])` => `["div", ["span", "bar"]]` --*rendered*-> `<div><span>bar</span></div>`
|
|
108
105
|
*/
|
|
109
106
|
export function vode<S extends object | unknown>(tag: Tag | Vode<S>, props?: Props<S> | ChildVode<S>, ...children: ChildVode<S>[]): Vode<S> {
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
return [tag, props as Props<S>, ...children];
|
|
115
|
-
}
|
|
116
|
-
return [tag, ...children];
|
|
107
|
+
if(!tag) throw new Error("tag must be a string or vode");
|
|
108
|
+
if (Array.isArray(tag)) return tag;
|
|
109
|
+
else if (props) return [tag, props as Props<S>, ...children];
|
|
110
|
+
else return [tag, ...children];
|
|
117
111
|
}
|
|
118
112
|
|
|
119
113
|
/** create a vode app inside a container element
|
|
@@ -124,8 +118,12 @@ export function vode<S extends object | unknown>(tag: Tag | Vode<S>, props?: Pro
|
|
|
124
118
|
* @returns a patch function that can be used to update the state
|
|
125
119
|
*/
|
|
126
120
|
export function app<S extends object | unknown>(container: HTMLElement, initialState: Omit<S, "patch">, dom: Component<S>, ...initialPatches: Patch<S>[]) {
|
|
121
|
+
if (!container) throw new Error("container must be a valid HTMLElement");
|
|
122
|
+
if (!initialState || typeof initialState !== "object") throw new Error("initialState must be an object");
|
|
123
|
+
if (typeof dom !== "function") throw new Error("dom must be a function that returns a vode");
|
|
124
|
+
|
|
127
125
|
const _vode = {} as ContainerNode<S>["_vode"];
|
|
128
|
-
_vode.stats = {
|
|
126
|
+
_vode.stats = { lastRenderTime: 0, renderCount: 0, liveEffectCount: 0, patchCount: 0, renderPatchCount: 0 };
|
|
129
127
|
|
|
130
128
|
Object.defineProperty(initialState, "patch", {
|
|
131
129
|
enumerable: false, configurable: true,
|
|
@@ -171,7 +169,7 @@ export function app<S extends object | unknown>(container: HTMLElement, initialS
|
|
|
171
169
|
_vode.patch!((<EffectFunction<S>>action)(_vode.state));
|
|
172
170
|
} else {
|
|
173
171
|
_vode.stats.renderPatchCount++;
|
|
174
|
-
_vode.q
|
|
172
|
+
_vode.q = mergeState(_vode.q || {}, action);
|
|
175
173
|
if (!_vode.isRendering) _vode.render!();
|
|
176
174
|
}
|
|
177
175
|
}
|
|
@@ -180,25 +178,19 @@ export function app<S extends object | unknown>(container: HTMLElement, initialS
|
|
|
180
178
|
Object.defineProperty(_vode, "render", {
|
|
181
179
|
enumerable: false, configurable: true,
|
|
182
180
|
writable: false, value: () => requestAnimationFrame(() => {
|
|
183
|
-
if (_vode.isRendering || _vode.q
|
|
181
|
+
if (_vode.isRendering || !_vode.q) return;
|
|
184
182
|
_vode.isRendering = true;
|
|
185
183
|
const sw = Date.now();
|
|
186
184
|
try {
|
|
187
|
-
_vode.
|
|
188
|
-
|
|
189
|
-
while (_vode.q!.length > 0) {
|
|
190
|
-
const patch = _vode.q!.shift();
|
|
191
|
-
if (patch === EmptyPatch) continue;
|
|
192
|
-
mergeState(_vode.state, patch);
|
|
193
|
-
}
|
|
185
|
+
_vode.state = mergeState(_vode.state, _vode.q);
|
|
186
|
+
_vode.q = null;
|
|
194
187
|
_vode.vode = render(_vode.state, _vode.patch, container, 0, _vode.vode, dom(_vode.state))!;
|
|
195
188
|
} finally {
|
|
196
189
|
_vode.isRendering = false;
|
|
197
190
|
_vode.stats.renderCount++;
|
|
198
|
-
_vode.stats.
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
_vode.render!();
|
|
191
|
+
_vode.stats.lastRenderTime = Date.now() - sw;
|
|
192
|
+
if (_vode.q) {
|
|
193
|
+
_vode.render();
|
|
202
194
|
}
|
|
203
195
|
}
|
|
204
196
|
})
|
|
@@ -206,7 +198,7 @@ export function app<S extends object | unknown>(container: HTMLElement, initialS
|
|
|
206
198
|
|
|
207
199
|
_vode.patch = (<PatchableState<S>>initialState).patch;
|
|
208
200
|
_vode.state = <PatchableState<S>>initialState;
|
|
209
|
-
_vode.q =
|
|
201
|
+
_vode.q = null;
|
|
210
202
|
|
|
211
203
|
const root = container as ContainerNode<S>;
|
|
212
204
|
root._vode = _vode;
|
package/vode.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// src/vode.ts
|
|
2
|
-
var EmptyPatch = {};
|
|
3
2
|
function createState(state) {
|
|
4
3
|
return state;
|
|
5
4
|
}
|
|
@@ -7,17 +6,24 @@ function createPatch(p) {
|
|
|
7
6
|
return p;
|
|
8
7
|
}
|
|
9
8
|
function vode(tag, props, ...children) {
|
|
10
|
-
if (
|
|
9
|
+
if (!tag)
|
|
10
|
+
throw new Error("tag must be a string or vode");
|
|
11
|
+
if (Array.isArray(tag))
|
|
11
12
|
return tag;
|
|
12
|
-
|
|
13
|
-
if (props) {
|
|
13
|
+
else if (props)
|
|
14
14
|
return [tag, props, ...children];
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
else
|
|
16
|
+
return [tag, ...children];
|
|
17
17
|
}
|
|
18
18
|
function app(container, initialState, dom, ...initialPatches) {
|
|
19
|
+
if (!container)
|
|
20
|
+
throw new Error("container must be a valid HTMLElement");
|
|
21
|
+
if (!initialState || typeof initialState !== "object")
|
|
22
|
+
throw new Error("initialState must be an object");
|
|
23
|
+
if (typeof dom !== "function")
|
|
24
|
+
throw new Error("dom must be a function that returns a vode");
|
|
19
25
|
const _vode = {};
|
|
20
|
-
_vode.stats = {
|
|
26
|
+
_vode.stats = { lastRenderTime: 0, renderCount: 0, liveEffectCount: 0, patchCount: 0, renderPatchCount: 0 };
|
|
21
27
|
Object.defineProperty(initialState, "patch", {
|
|
22
28
|
enumerable: false,
|
|
23
29
|
configurable: true,
|
|
@@ -65,7 +71,7 @@ function app(container, initialState, dom, ...initialPatches) {
|
|
|
65
71
|
_vode.patch(action(_vode.state));
|
|
66
72
|
} else {
|
|
67
73
|
_vode.stats.renderPatchCount++;
|
|
68
|
-
_vode.q.
|
|
74
|
+
_vode.q = mergeState(_vode.q || {}, action);
|
|
69
75
|
if (!_vode.isRendering)
|
|
70
76
|
_vode.render();
|
|
71
77
|
}
|
|
@@ -76,25 +82,19 @@ function app(container, initialState, dom, ...initialPatches) {
|
|
|
76
82
|
configurable: true,
|
|
77
83
|
writable: false,
|
|
78
84
|
value: () => requestAnimationFrame(() => {
|
|
79
|
-
if (_vode.isRendering || _vode.q
|
|
85
|
+
if (_vode.isRendering || !_vode.q)
|
|
80
86
|
return;
|
|
81
87
|
_vode.isRendering = true;
|
|
82
88
|
const sw = Date.now();
|
|
83
89
|
try {
|
|
84
|
-
_vode.
|
|
85
|
-
|
|
86
|
-
const patch = _vode.q.shift();
|
|
87
|
-
if (patch === EmptyPatch)
|
|
88
|
-
continue;
|
|
89
|
-
mergeState(_vode.state, patch);
|
|
90
|
-
}
|
|
90
|
+
_vode.state = mergeState(_vode.state, _vode.q);
|
|
91
|
+
_vode.q = null;
|
|
91
92
|
_vode.vode = render(_vode.state, _vode.patch, container, 0, _vode.vode, dom(_vode.state));
|
|
92
93
|
} finally {
|
|
93
94
|
_vode.isRendering = false;
|
|
94
95
|
_vode.stats.renderCount++;
|
|
95
|
-
_vode.stats.
|
|
96
|
-
|
|
97
|
-
if (_vode.q.length > 0) {
|
|
96
|
+
_vode.stats.lastRenderTime = Date.now() - sw;
|
|
97
|
+
if (_vode.q) {
|
|
98
98
|
_vode.render();
|
|
99
99
|
}
|
|
100
100
|
}
|
|
@@ -102,7 +102,7 @@ function app(container, initialState, dom, ...initialPatches) {
|
|
|
102
102
|
});
|
|
103
103
|
_vode.patch = initialState.patch;
|
|
104
104
|
_vode.state = initialState;
|
|
105
|
-
_vode.q =
|
|
105
|
+
_vode.q = null;
|
|
106
106
|
const root = container;
|
|
107
107
|
root._vode = _vode;
|
|
108
108
|
const initialVode = dom(initialState);
|
|
@@ -894,7 +894,6 @@ export {
|
|
|
894
894
|
FECOMPONENTTRANSFER,
|
|
895
895
|
FECOLORMATRIX,
|
|
896
896
|
FEBLEND,
|
|
897
|
-
EmptyPatch,
|
|
898
897
|
EMBED,
|
|
899
898
|
EM,
|
|
900
899
|
ELLIPSE,
|