wenay-react2 1.0.34 → 1.0.35
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.
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ObserveAll2 } from "wenay-common2";
|
|
2
2
|
type StoreChange = ObserveAll2.StoreChange;
|
|
3
|
+
type StoreEachCtx = ObserveAll2.StoreEachCtx;
|
|
3
4
|
type StoreSubOpts = ObserveAll2.StoreSubOpts;
|
|
4
5
|
type StoreSyncOpts = ObserveAll2.StoreSyncOpts;
|
|
5
6
|
type StoreDrain = ObserveAll2.StoreDrain;
|
|
@@ -55,6 +56,20 @@ export type StoreKeysController<T extends object = any> = {
|
|
|
55
56
|
};
|
|
56
57
|
export declare function useStoreKeys<T extends object>(node: StoreNode<T>, options?: Omit<UseStoreNodeOptions<T>, "mode">): StoreKeysController<T>;
|
|
57
58
|
export declare function useStoreSelect<T, M extends StoreMask<T>>(selection: StoreSelection<T, M>, options?: UseStoreSelectOptions<StorePick<T, M>>): StoreSelectionController<T, M>;
|
|
59
|
+
export type UseStoreEachOptions = {
|
|
60
|
+
/** false = do not subscribe (and drop the current subscription). Default true. */
|
|
61
|
+
enabled?: boolean;
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Per-changed-top-level-key feed of a store (`store.each()` of wenay-common2): cb fires once per
|
|
65
|
+
* CHANGED key per drain window with the current `store.state[key]` (`undefined` = key deleted);
|
|
66
|
+
* a root replace (store.replace / mirror keyframe) EXPANDS into one call per key, so cold start
|
|
67
|
+
* is not a special case for per-key consumers (grid rows, canvas layers, ...). The fold target
|
|
68
|
+
* should live outside React state (ref/store/grid api) — this hook renders nothing by itself.
|
|
69
|
+
* cb goes through a ref — a new identity does not resubscribe.
|
|
70
|
+
* The remote (wire) counterpart is useStoreReplayEach in useReplay.ts.
|
|
71
|
+
*/
|
|
72
|
+
export declare function useStoreEach<T extends object>(store: ObserveAll2.Store<T> | null | undefined, cb: (key: string, value: T[keyof T] | undefined, ctx: StoreEachCtx) => void, options?: UseStoreEachOptions): void;
|
|
58
73
|
export type RemoteStoreLike<T extends object> = {
|
|
59
74
|
get(mask?: any): T | Promise<T>;
|
|
60
75
|
changed: ListenLike<readonly []>;
|
|
@@ -102,4 +117,4 @@ export declare function useListenValue<T, TArgs extends readonly unknown[] = rea
|
|
|
102
117
|
current?: boolean | (() => TArgs | undefined);
|
|
103
118
|
map?: (...args: TArgs) => T;
|
|
104
119
|
}): T | undefined;
|
|
105
|
-
export type { StoreChange, StoreDrain, StoreMask, StoreNode, StorePick, StoreSelection, StoreSubOpts, StoreSyncOpts };
|
|
120
|
+
export type { StoreChange, StoreDrain, StoreEachCtx, StoreMask, StoreNode, StorePick, StoreSelection, StoreSubOpts, StoreSyncOpts };
|
|
@@ -96,6 +96,25 @@ export function useStoreSelect(selection, options = {}) {
|
|
|
96
96
|
get: () => selection.get(),
|
|
97
97
|
}), [selection, value, refresh]);
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Per-changed-top-level-key feed of a store (`store.each()` of wenay-common2): cb fires once per
|
|
101
|
+
* CHANGED key per drain window with the current `store.state[key]` (`undefined` = key deleted);
|
|
102
|
+
* a root replace (store.replace / mirror keyframe) EXPANDS into one call per key, so cold start
|
|
103
|
+
* is not a special case for per-key consumers (grid rows, canvas layers, ...). The fold target
|
|
104
|
+
* should live outside React state (ref/store/grid api) — this hook renders nothing by itself.
|
|
105
|
+
* cb goes through a ref — a new identity does not resubscribe.
|
|
106
|
+
* The remote (wire) counterpart is useStoreReplayEach in useReplay.ts.
|
|
107
|
+
*/
|
|
108
|
+
export function useStoreEach(store, cb, options = {}) {
|
|
109
|
+
const { enabled = true } = options;
|
|
110
|
+
const cbRef = useLatestRef(cb);
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (!store || !enabled)
|
|
113
|
+
return;
|
|
114
|
+
// each() is created lazily per subscription: it runs while it has subscribers and closes on off()
|
|
115
|
+
return store.each().on((key, value, ctx) => cbRef.current(key, value, ctx));
|
|
116
|
+
}, [store, enabled, cbRef]);
|
|
117
|
+
}
|
|
99
118
|
export function useStoreMirror(remote, initial, options) {
|
|
100
119
|
const { mask, auto = true, current = true, drain, key, partial, onError } = options;
|
|
101
120
|
const stableMask = useStableStoreMask(mask);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { ObserveAll2, Replay } from "wenay-common2";
|
|
2
|
+
type StoreDrain = ObserveAll2.StoreDrain;
|
|
3
|
+
type StoreEachCtx = ObserveAll2.StoreEachCtx;
|
|
2
4
|
type StorePatch = ObserveAll2.StorePatch;
|
|
3
5
|
type ReplayEvent<Z extends any[]> = Replay.ReplayEvent<Z>;
|
|
4
6
|
type ReplayRemote<Z extends any[]> = Replay.ReplayRemote<Z>;
|
|
@@ -78,6 +80,31 @@ export type StoreReplayMirrorController<T extends object> = StoreReplaySyncContr
|
|
|
78
80
|
* so reconnect is a journal tail, not a keyframe. A new `remote` recreates the store.
|
|
79
81
|
*/
|
|
80
82
|
export declare function useStoreReplayMirror<T extends object>(remote: ReplayRemote<[StorePatch]> | null | undefined, initial: T, options?: UseStoreReplaySyncOptions): StoreReplayMirrorController<T>;
|
|
83
|
+
export type UseStoreReplayEachOptions<T extends object> = UseStoreReplaySyncOptions & {
|
|
84
|
+
/**
|
|
85
|
+
* Seed of the internal mirror store. Creation-time only (a later identity change does nothing).
|
|
86
|
+
* Reconnect after a FULL unmount: pass the saved snapshot together with `since`
|
|
87
|
+
* ({since: prev.seq(), initial: prev.store.snapshot()}) — the tail lands ON TOP of the previous
|
|
88
|
+
* state; a fresh empty mirror would not converge.
|
|
89
|
+
*/
|
|
90
|
+
initial?: T;
|
|
91
|
+
/** Drain of the internal mirror store — the coalescing window of the per-key feed. Creation-time only. */
|
|
92
|
+
drain?: StoreDrain;
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* Per-key fold over a store replay line — React counterpart of `ObserveAll2.syncStoreReplayEach`
|
|
96
|
+
* (internal mirror store + syncStoreReplay + store.each()). cb fires once per CHANGED top-level
|
|
97
|
+
* key per drain window with the current value; the first delivery is the keyframe EXPANDED per
|
|
98
|
+
* key; (key, undefined) = key deleted — cold start / reconnect are not special cases for per-key
|
|
99
|
+
* consumers (grid rows, canvas layers, ...). The fold target should live outside React state.
|
|
100
|
+
*
|
|
101
|
+
* Unlike the library one-call (a fresh store per call), the mirror here lives in a ref: within
|
|
102
|
+
* one mounted component every resubscribe (StrictMode double-effect, restart(), enabled toggling,
|
|
103
|
+
* staleMs/policy change) reconnects by tail ON TOP of the kept state — no snapshot/initial dance.
|
|
104
|
+
* A new `remote` identity recreates the store (fresh keyframe). Direct reads / extra
|
|
105
|
+
* subscriptions: controller.store (useStoreNode/useStoreKeys work on it as usual).
|
|
106
|
+
*/
|
|
107
|
+
export declare function useStoreReplayEach<T extends object>(remote: ReplayRemote<[StorePatch]> | null | undefined, cb: (key: string, value: T[keyof T] | undefined, ctx: StoreEachCtx) => void, options?: UseStoreReplayEachOptions<T>): StoreReplayMirrorController<T>;
|
|
81
108
|
export type UseReplayFrameOptions = {
|
|
82
109
|
/** Pull period, ms. Default 300. */
|
|
83
110
|
intervalMs?: number;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { ObserveAll2, Replay } from "wenay-common2";
|
|
3
|
+
import { useStoreEach } from "./useObserveStore";
|
|
3
4
|
/**
|
|
4
5
|
* React bridge over the Replay stack (snapshot + sequenced delta line) of wenay-common2.
|
|
5
6
|
*
|
|
@@ -201,6 +202,32 @@ export function useStoreReplayMirror(remote, initial, options = {}) {
|
|
|
201
202
|
const sync = useStoreReplaySync(store, remote, options);
|
|
202
203
|
return useMemo(() => ({ ...sync, store }), [sync, store]);
|
|
203
204
|
}
|
|
205
|
+
/**
|
|
206
|
+
* Per-key fold over a store replay line — React counterpart of `ObserveAll2.syncStoreReplayEach`
|
|
207
|
+
* (internal mirror store + syncStoreReplay + store.each()). cb fires once per CHANGED top-level
|
|
208
|
+
* key per drain window with the current value; the first delivery is the keyframe EXPANDED per
|
|
209
|
+
* key; (key, undefined) = key deleted — cold start / reconnect are not special cases for per-key
|
|
210
|
+
* consumers (grid rows, canvas layers, ...). The fold target should live outside React state.
|
|
211
|
+
*
|
|
212
|
+
* Unlike the library one-call (a fresh store per call), the mirror here lives in a ref: within
|
|
213
|
+
* one mounted component every resubscribe (StrictMode double-effect, restart(), enabled toggling,
|
|
214
|
+
* staleMs/policy change) reconnects by tail ON TOP of the kept state — no snapshot/initial dance.
|
|
215
|
+
* A new `remote` identity recreates the store (fresh keyframe). Direct reads / extra
|
|
216
|
+
* subscriptions: controller.store (useStoreNode/useStoreKeys work on it as usual).
|
|
217
|
+
*/
|
|
218
|
+
export function useStoreReplayEach(remote, cb, options = {}) {
|
|
219
|
+
const { initial, drain, ...syncOptions } = options;
|
|
220
|
+
const storeRef = useRef(null);
|
|
221
|
+
if (!storeRef.current || storeRef.current.remote !== remote) {
|
|
222
|
+
storeRef.current = { remote, store: ObserveAll2.createStore((initial ?? {}), drain !== undefined ? { drain } : undefined) };
|
|
223
|
+
}
|
|
224
|
+
const store = storeRef.current.store;
|
|
225
|
+
// each BEFORE sync: effects run in hook-call order, so the per-key subscriber already exists
|
|
226
|
+
// when the keyframe applies to the store (the expansion is not missed)
|
|
227
|
+
useStoreEach(store, cb, { enabled: syncOptions.enabled });
|
|
228
|
+
const sync = useStoreReplaySync(store, remote, syncOptions);
|
|
229
|
+
return useMemo(() => ({ ...sync, store }), [sync, store]);
|
|
230
|
+
}
|
|
204
231
|
/**
|
|
205
232
|
* Pull a replay line at YOUR pace (the frame model of common2): a timer around
|
|
206
233
|
* `remote.frame(seq, hint)` — the server condenses the tail via the line's `frame` lambda
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wenay-react2",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.35",
|
|
4
4
|
"description": "Common react",
|
|
5
5
|
"strict": true,
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"react": "^19.2.0",
|
|
24
24
|
"react-dom": "^19.2.0",
|
|
25
25
|
"react-rnd": "^10.5.3",
|
|
26
|
-
"wenay-common2": "^1.0.
|
|
26
|
+
"wenay-common2": "^1.0.61"
|
|
27
27
|
},
|
|
28
28
|
"peerDependencies": {
|
|
29
29
|
"react": "^19.2.0",
|