preact-sigma 6.1.4 → 6.2.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/README.md +1 -1
- package/dist/index.d.mts +16 -2
- package/dist/index.mjs +40 -2
- package/dist/persist.d.mts +1 -1
- package/dist/persist.mjs +1 -1
- package/dist/{sigma-DEKK3bHd.mjs → sigma-B7kPkW-M.mjs} +5 -4
- package/dist/{sigma-BfaJUPWw.d.mts → sigma-cb2HeDjg.d.mts} +4 -4
- package/docs/context.md +3 -0
- package/examples/external-query-sync.tsx +51 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -48,5 +48,5 @@ console.log(counter.doubled); // 2
|
|
|
48
48
|
- Concepts, lifecycle, invariants, and API selection live in [`docs/context.md`](./docs/context.md).
|
|
49
49
|
- Persistence-specific guidance lives in [`docs/persist.md`](./docs/persist.md).
|
|
50
50
|
- Migration guidance from v5 lives in [`docs/migrations/v5-to-v6.md`](./docs/migrations/v5-to-v6.md).
|
|
51
|
-
- Runnable usage patterns live in [`examples/`](./examples/)
|
|
51
|
+
- Runnable usage patterns live in [`examples/`](./examples/).
|
|
52
52
|
- Exact exported signatures and public API comments live in `dist/index.d.mts` and `dist/persist.d.mts` after `pnpm build`.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as SigmaRef, c as castProtected, d as sigma, f as Draft, h as Cleanup, i as SigmaDefinition, l as query, m as typeSymbol, n as ReadableSigma, o as SigmaState, p as Immutable, r as Sigma, s as SigmaTarget, t as Protected, u as setAutoFreeze } from "./sigma-
|
|
1
|
+
import { a as SigmaRef, c as castProtected, d as sigma, f as Draft, h as Cleanup, i as SigmaDefinition, l as query, m as typeSymbol, n as ReadableSigma, o as SigmaState, p as Immutable, r as Sigma, s as SigmaTarget, t as Protected, u as setAutoFreeze } from "./sigma-cb2HeDjg.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/defaults.d.ts
|
|
4
4
|
/**
|
|
@@ -58,4 +58,18 @@ type UseSigmaArgs<T extends Sigma<any>> = T extends {
|
|
|
58
58
|
*/
|
|
59
59
|
declare function useSigma<T extends Sigma<any>>(...args: UseSigmaArgs<T>): Protected<T>;
|
|
60
60
|
//#endregion
|
|
61
|
-
|
|
61
|
+
//#region src/hooks/useSigmaSync.d.ts
|
|
62
|
+
type PlainObject = Record<string, unknown>;
|
|
63
|
+
/**
|
|
64
|
+
* Synchronizes changed external data into a sigma instance after the initial render.
|
|
65
|
+
*
|
|
66
|
+
* `input` must be a plain object with stable keys. Its values are shallow-compared with
|
|
67
|
+
* `Object.is(...)`, and `sync(...)` runs only after at least one value changes.
|
|
68
|
+
*
|
|
69
|
+
* A changed `instance` resets the baseline input, so newly created component-owned sigma
|
|
70
|
+
* instances can receive their initial external data through construction or setup before
|
|
71
|
+
* later renders synchronize changes through this hook.
|
|
72
|
+
*/
|
|
73
|
+
declare function useSigmaSync<TInstance extends Sigma<any>, TInput extends PlainObject>(instance: TInstance | Protected<TInstance>, input: TInput, sync: (input: Readonly<TInput>) => void): void;
|
|
74
|
+
//#endregion
|
|
75
|
+
export { type Draft, type Immutable, InferEventType, InferListener, Listenable, Protected, ReadableSigma, Sigma, SigmaDefinition, SigmaListenable, SigmaRef, SigmaState, SigmaTarget, UseSigmaArgs, UseSigmaOptions, castProtected, listen, mergeDefaults, query, setAutoFreeze, sigma, useListener, useSigma, useSigmaSync };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as setAutoFreeze, c as listenersSymbol, i as query, n as SigmaTarget, o as sigma, r as castProtected, t as Sigma } from "./sigma-
|
|
1
|
+
import { a as setAutoFreeze, c as listenersSymbol, i as query, n as SigmaTarget, o as sigma, r as castProtected, s as isPlainObject, t as Sigma } from "./sigma-B7kPkW-M.mjs";
|
|
2
2
|
import { useEffect, useRef } from "preact/hooks";
|
|
3
3
|
//#region src/defaults.ts
|
|
4
4
|
/**
|
|
@@ -77,4 +77,42 @@ function useSigma(create, optionsOrDeps) {
|
|
|
77
77
|
return castProtected(instance);
|
|
78
78
|
}
|
|
79
79
|
//#endregion
|
|
80
|
-
|
|
80
|
+
//#region src/hooks/useSigmaSync.ts
|
|
81
|
+
function assertStableKeys(cachedKeys, nextKeys) {
|
|
82
|
+
if (cachedKeys.length !== nextKeys.length || nextKeys.some((key) => !cachedKeys.includes(key))) throw new Error("[preact-sigma] useSigmaSync() input keys must stay stable between renders.");
|
|
83
|
+
}
|
|
84
|
+
function hasChanged(previous, next, keys) {
|
|
85
|
+
return keys.some((key) => !Object.is(previous[key], next[key]));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Synchronizes changed external data into a sigma instance after the initial render.
|
|
89
|
+
*
|
|
90
|
+
* `input` must be a plain object with stable keys. Its values are shallow-compared with
|
|
91
|
+
* `Object.is(...)`, and `sync(...)` runs only after at least one value changes.
|
|
92
|
+
*
|
|
93
|
+
* A changed `instance` resets the baseline input, so newly created component-owned sigma
|
|
94
|
+
* instances can receive their initial external data through construction or setup before
|
|
95
|
+
* later renders synchronize changes through this hook.
|
|
96
|
+
*/
|
|
97
|
+
function useSigmaSync(instance, input, sync) {
|
|
98
|
+
if (!isPlainObject(input)) throw new Error("[preact-sigma] useSigmaSync() input must be a plain object.");
|
|
99
|
+
const previousInput = useRef(void 0);
|
|
100
|
+
const previousKeys = useRef(void 0);
|
|
101
|
+
const previousInstance = useRef(void 0);
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
const nextKeys = Object.keys(input);
|
|
104
|
+
if (previousInstance.current !== instance) {
|
|
105
|
+
previousInstance.current = instance;
|
|
106
|
+
previousInput.current = input;
|
|
107
|
+
previousKeys.current = nextKeys;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
assertStableKeys(previousKeys.current, nextKeys);
|
|
111
|
+
if (hasChanged(previousInput.current, input, nextKeys)) {
|
|
112
|
+
previousInput.current = input;
|
|
113
|
+
sync(input);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
export { Sigma, SigmaTarget, castProtected, listen, mergeDefaults, query, setAutoFreeze, sigma, useListener, useSigma, useSigmaSync };
|
package/dist/persist.d.mts
CHANGED
package/dist/persist.mjs
CHANGED
|
@@ -418,22 +418,23 @@ var SigmaTarget = class SigmaTarget extends Sigma {
|
|
|
418
418
|
/** Helpers for observing, accessing, capturing, and replacing committed sigma state. */
|
|
419
419
|
const sigma = /* @__PURE__ */ Object.freeze({
|
|
420
420
|
subscribe: ((instance, keyOrListener, listenerOrOptions) => {
|
|
421
|
+
const source = instance;
|
|
421
422
|
if (typeof keyOrListener === "string") {
|
|
422
|
-
const signal = getStateSignal(
|
|
423
|
+
const signal = getStateSignal(source, keyOrListener);
|
|
423
424
|
if (!signal) throw new Error(`[preact-sigma] Property named "${keyOrListener}" is not signal-backed.`);
|
|
424
425
|
return signal.subscribe(listenerOrOptions);
|
|
425
426
|
}
|
|
426
427
|
const listener = keyOrListener;
|
|
427
428
|
if (listenerOrOptions?.patches) patchListeners.add(listener);
|
|
428
|
-
let subscriptions = changeListenersMap.get(
|
|
429
|
+
let subscriptions = changeListenersMap.get(source);
|
|
429
430
|
if (!subscriptions) {
|
|
430
431
|
subscriptions = /* @__PURE__ */ new Set();
|
|
431
|
-
changeListenersMap.set(
|
|
432
|
+
changeListenersMap.set(source, subscriptions);
|
|
432
433
|
}
|
|
433
434
|
subscriptions.add(listener);
|
|
434
435
|
return () => {
|
|
435
436
|
subscriptions.delete(listener);
|
|
436
|
-
if (!subscriptions.size) changeListenersMap.delete(
|
|
437
|
+
if (!subscriptions.size) changeListenersMap.delete(source);
|
|
437
438
|
};
|
|
438
439
|
}),
|
|
439
440
|
getSignal(instance, key) {
|
|
@@ -141,14 +141,14 @@ declare class SigmaTarget<TEvents extends object = {}, TState extends object = {
|
|
|
141
141
|
/** Helpers for observing, accessing, capturing, and replacing committed sigma state. */
|
|
142
142
|
declare const sigma: Readonly<{
|
|
143
143
|
/** Subscribes to committed state publishes or to one signal-backed top-level state key. */subscribe: {
|
|
144
|
-
<TState extends object>(instance:
|
|
144
|
+
<TState extends object>(instance: ReadableSigma<TState>, listener: (nextState: Immutable<TState>, baseState: Immutable<TState>, patches: Patch[], inversePatches: Patch[]) => void, options: {
|
|
145
145
|
patches: true;
|
|
146
146
|
}): Cleanup;
|
|
147
|
-
<TState extends object>(instance:
|
|
147
|
+
<TState extends object>(instance: ReadableSigma<TState>, listener: (nextState: Immutable<TState>, baseState: Immutable<TState>, patches: Patch[] | undefined, inversePatches: Patch[] | undefined) => void, options: {
|
|
148
148
|
patches: boolean;
|
|
149
149
|
}): Cleanup;
|
|
150
|
-
<TState extends object>(instance:
|
|
151
|
-
<TState extends object, TKey extends Extract<keyof TState, string>>(instance:
|
|
150
|
+
<TState extends object>(instance: ReadableSigma<TState>, listener: (nextState: Immutable<TState>, baseState: Immutable<TState>) => void): Cleanup;
|
|
151
|
+
<TState extends object, TKey extends Extract<keyof TState, string>>(instance: ReadableSigma<TState>, key: TKey, listener: (value: Immutable<TState[TKey]>) => void): Cleanup;
|
|
152
152
|
}; /** Returns the readonly signal backing one top-level state key. */
|
|
153
153
|
getSignal<TState extends object, TKey extends Extract<keyof TState, string>>(instance: Sigma<TState>, key: TKey): ReadonlySignal<Immutable<TState[TKey]>>; /** Captures the current committed top-level state snapshot. */
|
|
154
154
|
captureState<TState extends object>(instance: ReadableSigma<TState>): Immutable<TState>; /** Publishes a plain-object snapshot, including readonly captured snapshots, as committed state. */
|
package/docs/context.md
CHANGED
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
- Read one top-level state property as a `ReadonlySignal`: `sigma.getSignal(instance, key)`.
|
|
56
56
|
- Own timers, listeners, subscriptions, or nested setup: `onSetup(...)` plus `setup(...)`.
|
|
57
57
|
- Use a sigma instance inside a component: `useSigma(...)`.
|
|
58
|
+
- Synchronize changed component data into a sigma instance after the initial render: `useSigmaSync(instance, input, sync)`.
|
|
58
59
|
- Cast an instance to its readonly consumer view outside a component: `castProtected(instance)`.
|
|
59
60
|
- Subscribe to sigma or DOM events in a component: `useListener(...)`.
|
|
60
61
|
- Subscribe outside components: `listen(instance, ...)`.
|
|
@@ -72,6 +73,7 @@
|
|
|
72
73
|
- Emit directly from standalone `SigmaTarget` instances. In subclasses, emit from actions that have no unpublished draft changes. After mutating state, publish first with `this.commit(); this.emit(...)`.
|
|
73
74
|
- Prefer `listen(...)` for external event subscriptions. It works with sigma targets and DOM targets.
|
|
74
75
|
- Put owned side effects in `onSetup(...)`.
|
|
76
|
+
- Use `useSigmaSync(...)` when a component-owned sigma instance is initialized from external props or hook data and needs to receive later changes through an action. Pass a plain object with stable keys; values are compared with `Object.is(...)`, and a recreated instance treats the current input as its new baseline.
|
|
75
77
|
- Use `sigma.subscribe(this, ...)` inside `onSetup(...)` when a setup-owned side effect should react to future committed publishes. Return that cleanup so the subscription stops with setup.
|
|
76
78
|
```ts
|
|
77
79
|
onSetup() {
|
|
@@ -115,6 +117,7 @@
|
|
|
115
117
|
- Calling `emit(...)` outside an action on a subclass, or before committing the active draft, throws.
|
|
116
118
|
- Calling an action from a computed or query throws.
|
|
117
119
|
- Returning an active draft from an action throws.
|
|
120
|
+
- `useSigmaSync(...)` throws when its input is not a plain object or when the input keys change between renders for the same instance.
|
|
118
121
|
- `sigma.replaceState(...)` throws when the replacement value is not a plain object or when an action still owns unpublished changes.
|
|
119
122
|
- Starting an action on another sigma instance while the current instance has an active action context throws.
|
|
120
123
|
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { FunctionComponent } from "preact";
|
|
2
|
+
|
|
3
|
+
import { Sigma, useSigma, useSigmaSync } from "preact-sigma";
|
|
4
|
+
|
|
5
|
+
type SearchResult = {
|
|
6
|
+
id: string;
|
|
7
|
+
title: string;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type SearchResultsState = {
|
|
11
|
+
query: string;
|
|
12
|
+
results: readonly SearchResult[];
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
class SearchResults extends Sigma<SearchResultsState> {
|
|
16
|
+
constructor(query: string, results: readonly SearchResult[]) {
|
|
17
|
+
super({
|
|
18
|
+
query,
|
|
19
|
+
results,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
syncExternalQuery(query: string, results: readonly SearchResult[]) {
|
|
24
|
+
this.query = query;
|
|
25
|
+
this.results = results;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface SearchResults extends SearchResultsState {}
|
|
30
|
+
|
|
31
|
+
export const ExternalQuerySyncExample: FunctionComponent<{
|
|
32
|
+
query: string;
|
|
33
|
+
results: readonly SearchResult[];
|
|
34
|
+
}> = ({ query, results }) => {
|
|
35
|
+
const search = useSigma(() => new SearchResults(query, results));
|
|
36
|
+
|
|
37
|
+
useSigmaSync(search, { query, results }, ({ query, results }) => {
|
|
38
|
+
search.syncExternalQuery(query, results);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<section>
|
|
43
|
+
<h2>Results for {search.query || "everything"}</h2>
|
|
44
|
+
<ul>
|
|
45
|
+
{search.results.map((result) => (
|
|
46
|
+
<li key={result.id}>{result.title}</li>
|
|
47
|
+
))}
|
|
48
|
+
</ul>
|
|
49
|
+
</section>
|
|
50
|
+
);
|
|
51
|
+
};
|