@ryupold/vode 0.11.0 → 0.12.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/package.json +1 -1
- package/src/vode.ts +18 -24
- package/vode.mjs +18 -19
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: {
|
|
@@ -97,7 +96,7 @@ export type ContainerNode<S> = HTMLElement & {
|
|
|
97
96
|
export function createState<S extends object | unknown>(state: S): PatchableState<S> { return state as PatchableState<S>; }
|
|
98
97
|
|
|
99
98
|
/** 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):
|
|
99
|
+
export function createPatch<S extends object | unknown>(p: DeepPartial<S> | Effect<S> | NoRenderPatch): typeof p { return p; }
|
|
101
100
|
|
|
102
101
|
/** type-safe way to create a vode. useful for type inference and autocompletion.
|
|
103
102
|
*
|
|
@@ -107,13 +106,10 @@ export function createPatch<S extends object | unknown>(p: DeepPartial<S> | Effe
|
|
|
107
106
|
* - identity: `vode(["div", ["span", "bar"]])` => `["div", ["span", "bar"]]` --*rendered*-> `<div><span>bar</span></div>`
|
|
108
107
|
*/
|
|
109
108
|
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];
|
|
109
|
+
if(!tag) throw new Error("tag must be a string or vode");
|
|
110
|
+
if (Array.isArray(tag)) return tag;
|
|
111
|
+
else if (props) return [tag, props as Props<S>, ...children];
|
|
112
|
+
else return [tag, ...children];
|
|
117
113
|
}
|
|
118
114
|
|
|
119
115
|
/** create a vode app inside a container element
|
|
@@ -124,6 +120,10 @@ export function vode<S extends object | unknown>(tag: Tag | Vode<S>, props?: Pro
|
|
|
124
120
|
* @returns a patch function that can be used to update the state
|
|
125
121
|
*/
|
|
126
122
|
export function app<S extends object | unknown>(container: HTMLElement, initialState: Omit<S, "patch">, dom: Component<S>, ...initialPatches: Patch<S>[]) {
|
|
123
|
+
if (!container) throw new Error("container must be a valid HTMLElement");
|
|
124
|
+
if (!initialState || typeof initialState !== "object") throw new Error("initialState must be an object");
|
|
125
|
+
if (typeof dom !== "function") throw new Error("dom must be a function that returns a vode");
|
|
126
|
+
|
|
127
127
|
const _vode = {} as ContainerNode<S>["_vode"];
|
|
128
128
|
_vode.stats = { renderTime: 0, renderCount: 0, queueLengthBeforeRender: 0, queueLengthAfterRender: 0, liveEffectCount: 0, patchCount: 0, renderPatchCount: 0 };
|
|
129
129
|
|
|
@@ -171,7 +171,7 @@ export function app<S extends object | unknown>(container: HTMLElement, initialS
|
|
|
171
171
|
_vode.patch!((<EffectFunction<S>>action)(_vode.state));
|
|
172
172
|
} else {
|
|
173
173
|
_vode.stats.renderPatchCount++;
|
|
174
|
-
_vode.q
|
|
174
|
+
_vode.q = mergeState(_vode.q || {}, action);
|
|
175
175
|
if (!_vode.isRendering) _vode.render!();
|
|
176
176
|
}
|
|
177
177
|
}
|
|
@@ -180,25 +180,19 @@ export function app<S extends object | unknown>(container: HTMLElement, initialS
|
|
|
180
180
|
Object.defineProperty(_vode, "render", {
|
|
181
181
|
enumerable: false, configurable: true,
|
|
182
182
|
writable: false, value: () => requestAnimationFrame(() => {
|
|
183
|
-
if (_vode.isRendering || _vode.q
|
|
183
|
+
if (_vode.isRendering || !_vode.q) return;
|
|
184
184
|
_vode.isRendering = true;
|
|
185
185
|
const sw = Date.now();
|
|
186
186
|
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
|
-
}
|
|
187
|
+
_vode.state = mergeState(_vode.state, _vode.q);
|
|
188
|
+
_vode.q = null;
|
|
194
189
|
_vode.vode = render(_vode.state, _vode.patch, container, 0, _vode.vode, dom(_vode.state))!;
|
|
195
190
|
} finally {
|
|
196
191
|
_vode.isRendering = false;
|
|
197
192
|
_vode.stats.renderCount++;
|
|
198
193
|
_vode.stats.renderTime = Date.now() - sw;
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
_vode.render!();
|
|
194
|
+
if (_vode.q) {
|
|
195
|
+
_vode.render();
|
|
202
196
|
}
|
|
203
197
|
}
|
|
204
198
|
})
|
|
@@ -206,7 +200,7 @@ export function app<S extends object | unknown>(container: HTMLElement, initialS
|
|
|
206
200
|
|
|
207
201
|
_vode.patch = (<PatchableState<S>>initialState).patch;
|
|
208
202
|
_vode.state = <PatchableState<S>>initialState;
|
|
209
|
-
_vode.q =
|
|
203
|
+
_vode.q = null;
|
|
210
204
|
|
|
211
205
|
const root = container as ContainerNode<S>;
|
|
212
206
|
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,15 +6,22 @@ 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
26
|
_vode.stats = { renderTime: 0, renderCount: 0, queueLengthBeforeRender: 0, queueLengthAfterRender: 0, liveEffectCount: 0, patchCount: 0, renderPatchCount: 0 };
|
|
21
27
|
Object.defineProperty(initialState, "patch", {
|
|
@@ -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
96
|
_vode.stats.renderTime = Date.now() - sw;
|
|
96
|
-
|
|
97
|
-
if (_vode.q.length > 0) {
|
|
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,
|