preact-sigma 5.0.0 → 6.0.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 +24 -18
- package/dist/index.d.mts +50 -9
- package/dist/index.mjs +58 -140
- package/dist/persist.d.mts +69 -44
- package/dist/persist.mjs +40 -48
- package/dist/sigma-CJibGQ6g.mjs +383 -0
- package/dist/sigma-DD7HfTvw.d.mts +162 -0
- package/docs/context.md +65 -58
- package/docs/migrations/v5-to-v6.md +273 -0
- package/docs/persist.md +19 -18
- package/examples/async-commit.ts +38 -31
- package/examples/basic-counter.ts +21 -16
- package/examples/command-palette.tsx +114 -104
- package/examples/observe-and-restore.ts +21 -15
- package/examples/persist-search-draft.ts +33 -30
- package/examples/setup-act.ts +17 -9
- package/examples/sigma-target.ts +16 -7
- package/package.json +9 -10
- package/dist/framework-GgPzRfff.d.mts +0 -331
- package/dist/runtime-nX4Aygb8.mjs +0 -595
- package/examples/signal-access.ts +0 -31
package/docs/persist.md
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
`preact-sigma/persist` persists and restores committed top-level sigma state without moving storage, scheduling, or migration policy into `
|
|
5
|
+
`preact-sigma/persist` persists and restores committed top-level sigma state without moving storage, scheduling, or migration policy into `Sigma` classes.
|
|
6
6
|
|
|
7
7
|
The module builds on the core committed-state helpers:
|
|
8
8
|
|
|
9
|
-
- `sigma.
|
|
9
|
+
- `sigma.captureState(instance)` reads the current committed snapshot.
|
|
10
10
|
- `sigma.replaceState(instance, nextState)` restores a committed snapshot.
|
|
11
11
|
- `sigma.subscribe(instance, handler)` observes future committed publishes.
|
|
12
12
|
|
|
@@ -15,52 +15,53 @@ Use the persist module when those primitives are the right boundary, but you do
|
|
|
15
15
|
## When to Use
|
|
16
16
|
|
|
17
17
|
- State should survive reloads, navigation, or app restarts.
|
|
18
|
-
- Persistence needs to stay instance-specific instead of becoming part of the model
|
|
18
|
+
- Persistence needs to stay instance-specific instead of becoming part of the model class.
|
|
19
19
|
- Storage may be synchronous or asynchronous.
|
|
20
20
|
- Stored payloads need versioning, migration, or partial persistence.
|
|
21
21
|
- Restore and future persistence should share one small lifecycle helper.
|
|
22
22
|
|
|
23
23
|
## When Not to Use
|
|
24
24
|
|
|
25
|
-
- A one-off snapshot or replay flow is enough. Use `sigma.
|
|
25
|
+
- A one-off snapshot or replay flow is enough. Use `sigma.captureState(...)` and `sigma.replaceState(...)` directly.
|
|
26
26
|
- The data is really a remote cache, normalization layer, or conflict-resolution problem.
|
|
27
27
|
- You need unpublished drafts, computeds, queries, setup resources, or emitted events persisted.
|
|
28
28
|
- The model should start side effects before async restore completes. Sequence that explicitly outside `useSigma(...)`.
|
|
29
29
|
|
|
30
30
|
## Core Pieces
|
|
31
31
|
|
|
32
|
-
- Store: owns `
|
|
32
|
+
- Store: owns `get`, `set`, and `delete` for persisted records. These names match [Keyv](https://github.com/jaredwray/keyv) and `Map`.
|
|
33
33
|
- Codec: owns payload shape, versioning, and migration logic between stored data and a full committed snapshot.
|
|
34
|
-
-
|
|
34
|
+
- Pick options: persist selected top-level keys without writing a custom codec.
|
|
35
|
+
- Helper: owns restore sequencing, subscription lifecycle, and write scheduling for one sigma instance.
|
|
35
36
|
|
|
36
37
|
## Common Tasks -> Recommended APIs
|
|
37
38
|
|
|
38
|
-
- Restore once through an async store: `
|
|
39
|
-
- Restore once through a sync store: `
|
|
40
|
-
- Persist future committed changes only: `
|
|
41
|
-
- Restore first, then persist future changes: `
|
|
42
|
-
- Persist only selected top-level keys while restoring the full state shape: `
|
|
39
|
+
- Restore once through an async store: `restore(instance, options)`
|
|
40
|
+
- Restore once through a sync store: `restoreSync(instance, options)`
|
|
41
|
+
- Persist future committed changes only: `persist(instance, options)`
|
|
42
|
+
- Restore first, then persist future changes: `hydrate(instance, options)` or `hydrateSync(instance, options)`
|
|
43
|
+
- Persist only selected top-level keys while restoring the full state shape: pass `pick: ["key"]`
|
|
43
44
|
|
|
44
45
|
## Scheduling and Lifecycle
|
|
45
46
|
|
|
46
47
|
- Persistence helpers only read and write committed snapshots. Unpublished drafts never reach storage.
|
|
47
|
-
- `
|
|
48
|
+
- `persist(...)` defaults to `"microtask"` scheduling so multiple same-turn publishes can coalesce into one write.
|
|
48
49
|
- `writeInitial` defaults to `false`, which prevents a new binding from overwriting an older record before restore runs.
|
|
49
50
|
- `flush()` waits for scheduled or active writes to finish.
|
|
50
51
|
- `clear()` removes the stored record and keeps the binding usable for later writes.
|
|
51
|
-
- `stop()` unsubscribes the binding and waits for any
|
|
52
|
-
- `
|
|
52
|
+
- `stop()` unsubscribes the binding, cancels unwritten scheduled state, and waits for any active write to settle.
|
|
53
|
+
- `hydrate(...)` starts future persistence only after restore resolves successfully.
|
|
53
54
|
|
|
54
55
|
## Constraints
|
|
55
56
|
|
|
56
|
-
- `sigma.replaceState(...)`
|
|
57
|
-
-
|
|
57
|
+
- `sigma.replaceState(...)` requires a plain object replacement snapshot. In supported TypeScript usage, pass the class's full `TState` shape.
|
|
58
|
+
- Custom partial persistence codecs should reconstruct a full replacement snapshot before restore finishes.
|
|
58
59
|
- Nested sigma-state values are stored only if the chosen codec and payload format support them explicitly.
|
|
59
|
-
- Async restore failures reject through `
|
|
60
|
+
- Async restore failures reject through `restore(...)` or the `restored` promise from `hydrate(...)`.
|
|
60
61
|
- Background write failures route through `onWriteError(...)` without automatically stopping persistence.
|
|
61
62
|
|
|
62
63
|
## Example Routes
|
|
63
64
|
|
|
64
|
-
- [`examples/persist-search-draft.ts`](../examples/persist-search-draft.ts): sync restore-first persistence with `
|
|
65
|
+
- [`examples/persist-search-draft.ts`](../examples/persist-search-draft.ts): sync restore-first persistence with `hydrateSync(...)` and `pick`
|
|
65
66
|
- [`examples/observe-and-restore.ts`](../examples/observe-and-restore.ts): direct snapshot and restore without the persist subpath
|
|
66
67
|
- [`dist/persist.d.mts`](../dist/persist.d.mts): exact exported signatures for the persist module
|
package/examples/async-commit.ts
CHANGED
|
@@ -1,38 +1,43 @@
|
|
|
1
|
-
import { listen,
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import { listen, SigmaTarget } from "preact-sigma";
|
|
2
|
+
|
|
3
|
+
type SaveIndicatorState = {
|
|
4
|
+
savedCount: number;
|
|
5
|
+
saving: boolean;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
type SaveIndicatorEvents = {
|
|
9
|
+
saved: {
|
|
10
|
+
count: number;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
class SaveIndicator extends SigmaTarget<SaveIndicatorEvents, SaveIndicatorState> {
|
|
15
|
+
constructor() {
|
|
16
|
+
super({
|
|
17
|
+
savedCount: 0,
|
|
18
|
+
saving: false,
|
|
19
|
+
});
|
|
12
20
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.emit("saved", { count: this.savedCount });
|
|
30
|
-
},
|
|
31
|
-
});
|
|
21
|
+
|
|
22
|
+
async save() {
|
|
23
|
+
this.saving = true;
|
|
24
|
+
this.commit(); // Publish before the async boundary.
|
|
25
|
+
|
|
26
|
+
await Promise.resolve();
|
|
27
|
+
|
|
28
|
+
this.savedCount += 1;
|
|
29
|
+
this.saving = false;
|
|
30
|
+
this.commit(); // Publish before emitting the event boundary.
|
|
31
|
+
|
|
32
|
+
this.emit("saved", { count: this.savedCount });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface SaveIndicator extends SaveIndicatorState {}
|
|
32
37
|
|
|
33
38
|
const indicator = new SaveIndicator();
|
|
34
39
|
|
|
35
|
-
listen(indicator, "saved", ({ count }) => {
|
|
40
|
+
const stop = listen(indicator, "saved", ({ count }) => {
|
|
36
41
|
console.log(`Saved ${count} times`);
|
|
37
42
|
});
|
|
38
43
|
|
|
@@ -40,3 +45,5 @@ await indicator.save();
|
|
|
40
45
|
|
|
41
46
|
console.log(indicator.saving); // false
|
|
42
47
|
console.log(indicator.savedCount); // 1
|
|
48
|
+
|
|
49
|
+
stop();
|
|
@@ -1,19 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
1
|
+
import { Sigma } from "preact-sigma";
|
|
2
|
+
|
|
3
|
+
type CounterState = { count: number };
|
|
4
|
+
|
|
5
|
+
class Counter extends Sigma<CounterState> {
|
|
6
|
+
constructor() {
|
|
7
|
+
super({
|
|
8
|
+
count: 0,
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
get doubled() {
|
|
13
|
+
return this.count * 2;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
increment() {
|
|
17
|
+
this.count += 1;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface Counter extends CounterState {}
|
|
17
22
|
|
|
18
23
|
const counter = new Counter();
|
|
19
24
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from "preact/hooks";
|
|
2
2
|
|
|
3
|
-
import { listen, query,
|
|
3
|
+
import { listen, query, Sigma, SigmaTarget, useListener, useSigma } from "preact-sigma";
|
|
4
4
|
|
|
5
5
|
type Command = {
|
|
6
6
|
id: string;
|
|
@@ -9,18 +9,18 @@ type Command = {
|
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
class UsageLedger {
|
|
12
|
-
counts = new Map<string, number>();
|
|
12
|
+
#counts = new Map<string, number>();
|
|
13
13
|
|
|
14
14
|
get(id: string) {
|
|
15
|
-
return this
|
|
15
|
+
return this.#counts.get(id) ?? 0;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
increment(id: string) {
|
|
19
|
-
this
|
|
19
|
+
this.#counts.set(id, this.get(id) + 1);
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
23
|
+
function matchesText(command: Command, draft: string) {
|
|
24
24
|
const needle = draft.trim().toLowerCase();
|
|
25
25
|
if (!needle) {
|
|
26
26
|
return true;
|
|
@@ -30,101 +30,115 @@ const matchesText = query((command: Command, draft: string) => {
|
|
|
30
30
|
command.title.toLowerCase().includes(needle) ||
|
|
31
31
|
command.keywords.some((keyword) => keyword.toLowerCase().includes(needle))
|
|
32
32
|
);
|
|
33
|
-
}
|
|
33
|
+
}
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
type SearchHistoryState = {
|
|
36
36
|
items: string[];
|
|
37
|
-
}
|
|
38
|
-
.defaultState({
|
|
39
|
-
items: [],
|
|
40
|
-
})
|
|
41
|
-
.actions({
|
|
42
|
-
remember(query: string) {
|
|
43
|
-
const value = query.trim();
|
|
44
|
-
if (!value) {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
this.items = [value, ...this.items.filter((item) => item !== value)].slice(0, 5);
|
|
49
|
-
},
|
|
50
|
-
});
|
|
37
|
+
};
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
cursor: number;
|
|
58
|
-
draft: string;
|
|
59
|
-
history: SearchHistory;
|
|
60
|
-
usage: UsageLedger;
|
|
61
|
-
},
|
|
62
|
-
{
|
|
63
|
-
ran: Command;
|
|
39
|
+
class SearchHistory extends Sigma<SearchHistoryState> {
|
|
40
|
+
constructor() {
|
|
41
|
+
super({
|
|
42
|
+
items: [],
|
|
43
|
+
});
|
|
64
44
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
],
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
45
|
+
|
|
46
|
+
remember(query: string) {
|
|
47
|
+
const value = query.trim();
|
|
48
|
+
if (!value) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
this.items = [value, ...this.items.filter((item) => item !== value)].slice(0, 5);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface SearchHistory extends SearchHistoryState {}
|
|
57
|
+
|
|
58
|
+
type CommandPaletteState = {
|
|
59
|
+
commands: Command[];
|
|
60
|
+
cursor: number;
|
|
61
|
+
draft: string;
|
|
62
|
+
history: SearchHistory;
|
|
63
|
+
usage: UsageLedger;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
type CommandPaletteEvents = {
|
|
67
|
+
ran: Command;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
class CommandPalette extends SigmaTarget<CommandPaletteEvents, CommandPaletteState> {
|
|
71
|
+
constructor() {
|
|
72
|
+
super({
|
|
73
|
+
commands: [
|
|
74
|
+
{ id: "inbox", title: "Open inbox", keywords: ["mail", "messages", "triage"] },
|
|
75
|
+
{ id: "capture", title: "Capture note", keywords: ["write", "quick", "idea"] },
|
|
76
|
+
{ id: "focus", title: "Start focus timer", keywords: ["pomodoro", "deep work"] },
|
|
77
|
+
{ id: "theme", title: "Toggle theme", keywords: ["appearance", "dark", "light"] },
|
|
78
|
+
],
|
|
79
|
+
cursor: 0,
|
|
80
|
+
draft: "",
|
|
81
|
+
history: new SearchHistory(),
|
|
82
|
+
usage: new UsageLedger(),
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
get visibleCommands() {
|
|
87
|
+
return this.commands.filter((command) => matchesText(command, this.draft));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get activeCommand() {
|
|
91
|
+
return this.visibleCommands[this.cursor] ?? null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get canRun() {
|
|
95
|
+
return this.activeCommand !== null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
@query
|
|
99
|
+
usageCount(id: string) {
|
|
100
|
+
return this.usage.get(id);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
setDraft(draft: string) {
|
|
104
|
+
this.draft = draft;
|
|
105
|
+
this.cursor = 0;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
move(step: number) {
|
|
109
|
+
if (this.visibleCommands.length === 0) {
|
|
124
110
|
this.cursor = 0;
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const lastIndex = this.visibleCommands.length - 1;
|
|
115
|
+
this.cursor = Math.max(0, Math.min(lastIndex, this.cursor + step));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
seedDraftFromHistory() {
|
|
119
|
+
const latest = this.history.items[0];
|
|
120
|
+
if (latest) {
|
|
121
|
+
this.setDraft(latest);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
runActive() {
|
|
126
|
+
const command = this.activeCommand;
|
|
127
|
+
if (!command) {
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const search = this.draft || command.title;
|
|
132
|
+
this.usage.increment(command.id);
|
|
133
|
+
this.draft = "";
|
|
134
|
+
this.cursor = 0;
|
|
135
|
+
this.commit();
|
|
136
|
+
|
|
137
|
+
this.history.remember(search);
|
|
138
|
+
this.emit("ran", command);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
onSetup() {
|
|
128
142
|
return [
|
|
129
143
|
listen(window, "keydown", (event) => {
|
|
130
144
|
if ((event.metaKey || event.ctrlKey) && event.key === "k") {
|
|
@@ -144,9 +158,10 @@ const CommandPalette = new SigmaType<
|
|
|
144
158
|
}
|
|
145
159
|
}),
|
|
146
160
|
];
|
|
147
|
-
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
148
163
|
|
|
149
|
-
interface CommandPalette extends
|
|
164
|
+
interface CommandPalette extends CommandPaletteState {}
|
|
150
165
|
|
|
151
166
|
export function CommandPaletteExample() {
|
|
152
167
|
const palette = useSigma(() => new CommandPalette());
|
|
@@ -158,16 +173,11 @@ export function CommandPaletteExample() {
|
|
|
158
173
|
|
|
159
174
|
return (
|
|
160
175
|
<section>
|
|
161
|
-
<p>
|
|
162
|
-
<strong>Command palette</strong>: setup-owned keyboard shortcuts, computed getters, tracked
|
|
163
|
-
queries with args, typed events, nested sigma state, and a mutable custom class instance.
|
|
164
|
-
</p>
|
|
165
|
-
|
|
166
176
|
<label>
|
|
167
177
|
Search
|
|
168
178
|
<input
|
|
169
179
|
value={palette.draft}
|
|
170
|
-
onInput={(event) => palette.setDraft(
|
|
180
|
+
onInput={(event) => palette.setDraft(event.currentTarget.value)}
|
|
171
181
|
placeholder="Try: note, timer, inbox"
|
|
172
182
|
/>
|
|
173
183
|
</label>
|
|
@@ -179,7 +189,7 @@ export function CommandPaletteExample() {
|
|
|
179
189
|
<button type="button" onClick={() => palette.move(1)}>
|
|
180
190
|
Down
|
|
181
191
|
</button>
|
|
182
|
-
<button type="button" onClick={() => palette.runActive()} disabled={!palette.canRun
|
|
192
|
+
<button type="button" onClick={() => palette.runActive()} disabled={!palette.canRun}>
|
|
183
193
|
Run
|
|
184
194
|
</button>
|
|
185
195
|
</div>
|
|
@@ -1,29 +1,35 @@
|
|
|
1
|
-
import { sigma,
|
|
1
|
+
import { sigma, Sigma } from "preact-sigma";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
type TodoListState = {
|
|
4
4
|
todos: string[];
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
class TodoList extends Sigma<TodoListState> {
|
|
8
|
+
constructor() {
|
|
9
|
+
super({
|
|
10
|
+
todos: [],
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
add(title: string) {
|
|
15
|
+
this.todos.push(title);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface TodoList extends TodoListState {}
|
|
14
20
|
|
|
15
21
|
const todoList = new TodoList();
|
|
16
|
-
const stop = sigma.subscribe(todoList, (
|
|
17
|
-
console.log(`${
|
|
22
|
+
const stop = sigma.subscribe(todoList, (nextState, baseState) => {
|
|
23
|
+
console.log(`${baseState.todos.length} -> ${nextState.todos.length}`);
|
|
18
24
|
});
|
|
19
25
|
|
|
20
26
|
todoList.add("Write docs");
|
|
21
27
|
|
|
22
|
-
const saved = sigma.
|
|
28
|
+
const saved = sigma.captureState(todoList);
|
|
23
29
|
|
|
24
30
|
todoList.add("Ship release");
|
|
25
31
|
sigma.replaceState(todoList, saved);
|
|
26
32
|
|
|
27
|
-
console.log(sigma.
|
|
33
|
+
console.log(sigma.captureState(todoList).todos); // ["Write docs"]
|
|
28
34
|
|
|
29
35
|
stop();
|
|
@@ -1,29 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
bindPersistenceSync,
|
|
4
|
-
pickStateCodec,
|
|
5
|
-
type PersistRecord,
|
|
6
|
-
type SyncPersistStore,
|
|
7
|
-
} from "preact-sigma/persist";
|
|
1
|
+
import { Sigma } from "preact-sigma";
|
|
2
|
+
import { hydrateSync, type PersistRecord, type SyncPersistStore } from "preact-sigma/persist";
|
|
8
3
|
|
|
9
|
-
|
|
4
|
+
type SearchState = {
|
|
10
5
|
draft: string;
|
|
11
6
|
page: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
class Search extends Sigma<SearchState> {
|
|
10
|
+
constructor(initialState: Partial<SearchState> = {}) {
|
|
11
|
+
super({
|
|
12
|
+
draft: "",
|
|
13
|
+
page: 1,
|
|
14
|
+
...initialState,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
nextPage() {
|
|
19
|
+
this.page += 1;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
setDraft(draft: string) {
|
|
23
|
+
this.draft = draft;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface Search extends SearchState {}
|
|
25
28
|
|
|
26
|
-
const records = new Map<string, PersistRecord<
|
|
29
|
+
const records = new Map<string, PersistRecord<Pick<SearchState, "draft">>>([
|
|
27
30
|
[
|
|
28
31
|
"search",
|
|
29
32
|
{
|
|
@@ -34,22 +37,22 @@ const records = new Map<string, PersistRecord<{ draft: string }>>([
|
|
|
34
37
|
],
|
|
35
38
|
]);
|
|
36
39
|
|
|
37
|
-
const store: SyncPersistStore<
|
|
38
|
-
|
|
40
|
+
const store: SyncPersistStore<Pick<SearchState, "draft">> = {
|
|
41
|
+
get(key) {
|
|
39
42
|
return records.get(key);
|
|
40
43
|
},
|
|
41
|
-
|
|
42
|
-
records.set(key, record);
|
|
44
|
+
set(key, record) {
|
|
45
|
+
return records.set(key, record);
|
|
43
46
|
},
|
|
44
|
-
|
|
45
|
-
records.delete(key);
|
|
47
|
+
delete(key) {
|
|
48
|
+
return records.delete(key);
|
|
46
49
|
},
|
|
47
50
|
};
|
|
48
51
|
|
|
49
52
|
const search = new Search({ page: 3 });
|
|
50
|
-
const persistence =
|
|
51
|
-
codec: pickStateCodec(["draft"]),
|
|
53
|
+
const persistence = hydrateSync(search, {
|
|
52
54
|
key: "search",
|
|
55
|
+
pick: ["draft"],
|
|
53
56
|
store,
|
|
54
57
|
});
|
|
55
58
|
|
package/examples/setup-act.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import { listen,
|
|
1
|
+
import { listen, Sigma } from "preact-sigma";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
type ClickTrackerState = {
|
|
4
4
|
clicks: number;
|
|
5
5
|
status: "idle" | "ready";
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
class ClickTracker extends Sigma<ClickTrackerState> {
|
|
9
|
+
constructor() {
|
|
10
|
+
super({
|
|
11
|
+
clicks: 0,
|
|
12
|
+
status: "idle",
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
onSetup(target: EventTarget) {
|
|
12
17
|
this.act(function () {
|
|
13
18
|
this.status = "ready";
|
|
14
19
|
});
|
|
@@ -20,7 +25,10 @@ const ClickTracker = new SigmaType<{
|
|
|
20
25
|
});
|
|
21
26
|
}),
|
|
22
27
|
];
|
|
23
|
-
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
interface ClickTracker extends ClickTrackerState {}
|
|
24
32
|
|
|
25
33
|
const target = new EventTarget();
|
|
26
34
|
const tracker = new ClickTracker();
|