atom.io 0.3.1 → 0.4.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 +11 -3
- package/dist/index.d.ts +84 -30
- package/dist/index.js +427 -230
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +427 -230
- package/dist/index.mjs.map +1 -1
- package/package.json +15 -5
- package/react/dist/index.d.ts +12 -17
- package/react/dist/index.js +25 -34
- package/react/dist/index.js.map +1 -1
- package/react/dist/index.mjs +21 -34
- package/react/dist/index.mjs.map +1 -1
- package/react-devtools/dist/index.css +26 -0
- package/react-devtools/dist/index.css.map +1 -0
- package/react-devtools/dist/index.d.ts +15 -0
- package/react-devtools/dist/index.js +1582 -0
- package/react-devtools/dist/index.js.map +1 -0
- package/react-devtools/dist/index.mjs +1554 -0
- package/react-devtools/dist/index.mjs.map +1 -0
- package/react-devtools/package.json +15 -0
- package/src/index.ts +3 -3
- package/src/internal/atom-internal.ts +10 -5
- package/src/internal/families-internal.ts +4 -4
- package/src/internal/get.ts +8 -8
- package/src/internal/index.ts +2 -0
- package/src/internal/meta/attach-meta.ts +17 -0
- package/src/internal/meta/index.ts +4 -0
- package/src/internal/meta/meta-state.ts +135 -0
- package/src/internal/meta/meta-timelines.ts +1 -0
- package/src/internal/meta/meta-transactions.ts +1 -0
- package/src/internal/operation.ts +0 -1
- package/src/internal/selector-internal.ts +34 -13
- package/src/internal/store.ts +35 -5
- package/src/internal/time-travel-internal.ts +89 -0
- package/src/internal/timeline-internal.ts +23 -103
- package/src/internal/transaction-internal.ts +14 -5
- package/src/react/index.ts +28 -46
- package/src/react-devtools/AtomIODevtools.tsx +107 -0
- package/src/react-devtools/StateEditor.tsx +73 -0
- package/src/react-devtools/TokenList.tsx +57 -0
- package/src/react-devtools/devtools.scss +130 -0
- package/src/react-devtools/index.ts +1 -0
- package/src/react-explorer/AtomIOExplorer.tsx +208 -0
- package/src/react-explorer/explorer-effects.ts +20 -0
- package/src/react-explorer/explorer-states.ts +224 -0
- package/src/react-explorer/index.ts +23 -0
- package/src/react-explorer/space-states.ts +73 -0
- package/src/react-explorer/view-states.ts +43 -0
- package/src/selector.ts +6 -6
- package/src/subscribe.ts +2 -2
- package/src/timeline.ts +1 -5
- package/src/transaction.ts +4 -2
- package/src/web-effects/index.ts +1 -0
- package/src/web-effects/storage.ts +30 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { lastOf } from "~/packages/anvl/src/array"
|
|
2
|
+
import { now } from "~/packages/anvl/src/id"
|
|
3
|
+
import { Join } from "~/packages/anvl/src/join"
|
|
4
|
+
import type { Entries } from "~/packages/anvl/src/object"
|
|
5
|
+
import { cannotExist } from "~/packages/anvl/src/refinement"
|
|
6
|
+
|
|
7
|
+
import { addToIndex, removeFromIndex } from "."
|
|
8
|
+
import {
|
|
9
|
+
makeSpaceLayoutNodeFamily,
|
|
10
|
+
makeSpaceFamily,
|
|
11
|
+
makeSpaceIndex,
|
|
12
|
+
makeSpaceLayoutState,
|
|
13
|
+
} from "./space-states"
|
|
14
|
+
import type { View } from "./view-states"
|
|
15
|
+
import {
|
|
16
|
+
makeViewFocusedFamily,
|
|
17
|
+
makeViewFamily,
|
|
18
|
+
makeViewIndex,
|
|
19
|
+
} from "./view-states"
|
|
20
|
+
import type {
|
|
21
|
+
AtomFamily,
|
|
22
|
+
AtomToken,
|
|
23
|
+
ReadonlySelectorFamily,
|
|
24
|
+
ReadonlySelectorToken,
|
|
25
|
+
SelectorFamily,
|
|
26
|
+
TransactionToken,
|
|
27
|
+
Write,
|
|
28
|
+
} from ".."
|
|
29
|
+
import { selectorFamily, selector, transaction, atom } from ".."
|
|
30
|
+
import { persistAtom } from "../web-effects"
|
|
31
|
+
|
|
32
|
+
export const makeViewsPerSpaceState = (
|
|
33
|
+
key: string
|
|
34
|
+
): AtomToken<Join<null, `viewId`, `spaceId`>> =>
|
|
35
|
+
atom<Join<null, `viewId`, `spaceId`>>({
|
|
36
|
+
key: `${key}:views_per_space`,
|
|
37
|
+
default: new Join({ relationType: `1:n` }),
|
|
38
|
+
effects: [
|
|
39
|
+
persistAtom<Join<null, `viewId`, `spaceId`>>(localStorage)({
|
|
40
|
+
stringify: (index) => JSON.stringify(index.toJSON()),
|
|
41
|
+
parse: (json) =>
|
|
42
|
+
Join.fromJSON(JSON.parse(json), cannotExist, `viewId`, `spaceId`),
|
|
43
|
+
})(`${key}:views_per_space`),
|
|
44
|
+
],
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
export const makeSpaceViewsFamily = (
|
|
48
|
+
key: string,
|
|
49
|
+
viewsPerSpaceState: AtomToken<Join>
|
|
50
|
+
): ReadonlySelectorFamily<string[], string> =>
|
|
51
|
+
selectorFamily<string[], string>({
|
|
52
|
+
key: `${key}:space_views`,
|
|
53
|
+
get:
|
|
54
|
+
(spaceId) =>
|
|
55
|
+
({ get }) => {
|
|
56
|
+
const join = get(viewsPerSpaceState)
|
|
57
|
+
const viewIds = join.getRelatedIds(spaceId)
|
|
58
|
+
return viewIds
|
|
59
|
+
},
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
export const makeSpaceFocusedViewFamily = (
|
|
63
|
+
key: string,
|
|
64
|
+
findSpaceViewsState: ReadonlySelectorFamily<string[], string>,
|
|
65
|
+
findViewFocusedState: AtomFamily<number, string>
|
|
66
|
+
): SelectorFamily<string | null, string> =>
|
|
67
|
+
selectorFamily<string | null, string>({
|
|
68
|
+
key: `${key}:space_focused_view`,
|
|
69
|
+
get:
|
|
70
|
+
(spaceKey) =>
|
|
71
|
+
({ get }) => {
|
|
72
|
+
const views = get(findSpaceViewsState(spaceKey))
|
|
73
|
+
const viewsLastFocused = views.map((viewKey): [string, number] => [
|
|
74
|
+
viewKey,
|
|
75
|
+
get(findViewFocusedState(viewKey)),
|
|
76
|
+
])
|
|
77
|
+
const lastFocused = lastOf(viewsLastFocused.sort((a, b) => b[1] - a[1]))
|
|
78
|
+
return lastFocused ? lastFocused[0] : null
|
|
79
|
+
},
|
|
80
|
+
set:
|
|
81
|
+
(spaceKey) =>
|
|
82
|
+
({ get, set }, viewKey) => {
|
|
83
|
+
if (viewKey === null) {
|
|
84
|
+
return
|
|
85
|
+
}
|
|
86
|
+
const views = get(findSpaceViewsState(spaceKey))
|
|
87
|
+
if (views.includes(viewKey)) {
|
|
88
|
+
set(findViewFocusedState(viewKey), Date.now())
|
|
89
|
+
} else {
|
|
90
|
+
console.warn(`View ${viewKey} not found in space ${spaceKey}`)
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
type AddViewOptions = { spaceId?: string; path?: string }
|
|
96
|
+
type SplitSpaceOptions = { parentId?: string }
|
|
97
|
+
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
99
|
+
export const attachExplorerState = (key: string) => {
|
|
100
|
+
const findSpaceState = makeSpaceFamily(key)
|
|
101
|
+
const findViewState = makeViewFamily(key)
|
|
102
|
+
const findViewFocusedState = makeViewFocusedFamily(key)
|
|
103
|
+
const spaceIndexState = makeSpaceIndex(key)
|
|
104
|
+
const spaceLayoutState = makeSpaceLayoutState(key)
|
|
105
|
+
const viewIndexState = makeViewIndex(key)
|
|
106
|
+
const viewsPerSpaceState = makeViewsPerSpaceState(key)
|
|
107
|
+
|
|
108
|
+
const findSpaceLayoutNode = makeSpaceLayoutNodeFamily(key, spaceLayoutState)
|
|
109
|
+
const findSpaceViewsState = makeSpaceViewsFamily(key, viewsPerSpaceState)
|
|
110
|
+
const findSpaceFocusedViewState = makeSpaceFocusedViewFamily(
|
|
111
|
+
key,
|
|
112
|
+
findSpaceViewsState,
|
|
113
|
+
findViewFocusedState
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
const allViewsState = selector<Entries<string, View>>({
|
|
117
|
+
key: `${key}:all_views`,
|
|
118
|
+
get: ({ get }) => {
|
|
119
|
+
const viewIndex = get(viewIndexState)
|
|
120
|
+
return [...viewIndex].map((id) => [id, get(findViewState(id))])
|
|
121
|
+
},
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const writeOperationAddSpace: Write<
|
|
125
|
+
(options?: SplitSpaceOptions) => string
|
|
126
|
+
> = (transactors, { parentId = `root` } = {}) => {
|
|
127
|
+
const { set } = transactors
|
|
128
|
+
const key = `s-${now()}`
|
|
129
|
+
addToIndex(transactors, { indexAtom: spaceIndexState, id: key })
|
|
130
|
+
set(spaceLayoutState, (current) =>
|
|
131
|
+
current.set({ parent: `parent:${parentId}`, child: key }, { size: 1 })
|
|
132
|
+
)
|
|
133
|
+
set(findSpaceState(key), 1)
|
|
134
|
+
return key
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const writeOperationRemoveSpace: Write<(id: string) => void> = (
|
|
138
|
+
transactors,
|
|
139
|
+
id
|
|
140
|
+
) => {
|
|
141
|
+
removeFromIndex(transactors, { indexAtom: spaceIndexState, id })
|
|
142
|
+
transactors.set(findSpaceState(id), null)
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const writeOperationAddView: Write<(options?: AddViewOptions) => void> = (
|
|
146
|
+
transactors,
|
|
147
|
+
{ spaceId: maybeSpaceId, path } = {}
|
|
148
|
+
) => {
|
|
149
|
+
const { get, set } = transactors
|
|
150
|
+
const id = `v-${now()}`
|
|
151
|
+
|
|
152
|
+
addToIndex(transactors, { indexAtom: viewIndexState, id })
|
|
153
|
+
set(
|
|
154
|
+
findViewState(id),
|
|
155
|
+
(current): View => ({
|
|
156
|
+
...current,
|
|
157
|
+
location: {
|
|
158
|
+
...current.location,
|
|
159
|
+
pathname: path ?? `/`,
|
|
160
|
+
},
|
|
161
|
+
})
|
|
162
|
+
)
|
|
163
|
+
const spaceId =
|
|
164
|
+
maybeSpaceId ??
|
|
165
|
+
lastOf([...get(spaceIndexState)]) ??
|
|
166
|
+
writeOperationAddSpace(transactors)
|
|
167
|
+
set(findViewFocusedState(id), Date.now())
|
|
168
|
+
|
|
169
|
+
set(viewsPerSpaceState, (current) => current.set({ spaceId, viewId: id }))
|
|
170
|
+
set(findViewFocusedState(id), Date.now())
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const writeOperationRemoveView: Write<(viewId: string) => void> = (
|
|
174
|
+
transactors,
|
|
175
|
+
viewId
|
|
176
|
+
) => {
|
|
177
|
+
const { set } = transactors
|
|
178
|
+
removeFromIndex(transactors, { indexAtom: viewIndexState, id: viewId })
|
|
179
|
+
set(viewsPerSpaceState, (current) => current.remove({ viewId }))
|
|
180
|
+
set(findViewState(viewId), null)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const addView = transaction<(options?: AddViewOptions) => void>({
|
|
184
|
+
key: `${key}:add_view`,
|
|
185
|
+
do: writeOperationAddView,
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
const removeView = transaction({
|
|
189
|
+
key: `${key}:remove_view`,
|
|
190
|
+
do: writeOperationRemoveView,
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
const addSpace = transaction({
|
|
194
|
+
key: `${key}:add_space`,
|
|
195
|
+
do: writeOperationAddSpace,
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
const removeSpace = transaction({
|
|
199
|
+
key: `${key}:remove_space`,
|
|
200
|
+
do: writeOperationRemoveSpace,
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
addSpace,
|
|
205
|
+
addView,
|
|
206
|
+
allViewsState,
|
|
207
|
+
findSpaceLayoutNode,
|
|
208
|
+
findSpaceFocusedViewState,
|
|
209
|
+
findSpaceState,
|
|
210
|
+
findSpaceViewsState,
|
|
211
|
+
findViewState,
|
|
212
|
+
findViewFocusedState,
|
|
213
|
+
removeSpace,
|
|
214
|
+
removeView,
|
|
215
|
+
spaceIndexState,
|
|
216
|
+
spaceLayoutState,
|
|
217
|
+
viewIndexState,
|
|
218
|
+
viewsPerSpaceState,
|
|
219
|
+
writeOperationAddSpace,
|
|
220
|
+
writeOperationAddView,
|
|
221
|
+
writeOperationRemoveSpace,
|
|
222
|
+
writeOperationRemoveView,
|
|
223
|
+
}
|
|
224
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { AtomToken, Write } from ".."
|
|
2
|
+
|
|
3
|
+
export * from "./AtomIOExplorer"
|
|
4
|
+
|
|
5
|
+
export type AtomicIndexOptions = {
|
|
6
|
+
indexAtom: AtomToken<Set<string>>
|
|
7
|
+
id: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const addToIndex: Write<(options: AtomicIndexOptions) => void> = (
|
|
11
|
+
{ set },
|
|
12
|
+
{ indexAtom, id }
|
|
13
|
+
): void => set(indexAtom, (currentSet) => new Set(currentSet).add(id))
|
|
14
|
+
|
|
15
|
+
export const removeFromIndex: Write<(options: AtomicIndexOptions) => void> = (
|
|
16
|
+
{ set },
|
|
17
|
+
{ indexAtom, id }
|
|
18
|
+
): void =>
|
|
19
|
+
set(indexAtom, (currentSet) => {
|
|
20
|
+
const newSet = new Set(currentSet)
|
|
21
|
+
newSet.delete(id)
|
|
22
|
+
return newSet
|
|
23
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { isNumber } from "fp-ts/lib/number"
|
|
2
|
+
|
|
3
|
+
import { Join } from "~/packages/anvl/src/join"
|
|
4
|
+
import { parseJson, stringifyJson } from "~/packages/anvl/src/json"
|
|
5
|
+
import { hasExactProperties } from "~/packages/anvl/src/object"
|
|
6
|
+
|
|
7
|
+
import { persistStringSetAtom } from "./explorer-effects"
|
|
8
|
+
import type { AtomToken, ReadonlySelectorFamily, SelectorFamily } from ".."
|
|
9
|
+
import { selectorFamily } from ".."
|
|
10
|
+
import type { AtomFamily } from "../atom"
|
|
11
|
+
import { atom, atomFamily } from "../atom"
|
|
12
|
+
import { lazyLocalStorageEffect, persistAtom } from "../web-effects"
|
|
13
|
+
|
|
14
|
+
export const makeSpaceIndex = (key: string): AtomToken<Set<string>> =>
|
|
15
|
+
atom<Set<string>>({
|
|
16
|
+
key: `${key}:space_index`,
|
|
17
|
+
default: new Set([`root`]),
|
|
18
|
+
effects: [persistStringSetAtom(`${key}:space_index`)],
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
export const makeSpaceLayoutState = (
|
|
22
|
+
key: string
|
|
23
|
+
): AtomToken<Join<{ size: number }, `parent`, `child`>> =>
|
|
24
|
+
atom({
|
|
25
|
+
key: `${key}:space_layout`,
|
|
26
|
+
default: new Join({ relationType: `1:n` }),
|
|
27
|
+
effects: [
|
|
28
|
+
persistAtom<Join<{ size: number }, `parent`, `child`>>(localStorage)({
|
|
29
|
+
stringify: (join) => stringifyJson(join.toJSON()),
|
|
30
|
+
parse: (string) => {
|
|
31
|
+
try {
|
|
32
|
+
const json = parseJson(string)
|
|
33
|
+
const join = Join.fromJSON(
|
|
34
|
+
json,
|
|
35
|
+
hasExactProperties({ size: isNumber }),
|
|
36
|
+
`parent`,
|
|
37
|
+
`child`
|
|
38
|
+
)
|
|
39
|
+
return join
|
|
40
|
+
} catch (thrown) {
|
|
41
|
+
console.error(`Error parsing spaceLayoutState from localStorage`)
|
|
42
|
+
return new Join({ relationType: `1:n` })
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
})(`${key}:space_layout`),
|
|
46
|
+
],
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
export const makeSpaceLayoutNodeFamily = (
|
|
50
|
+
key: string,
|
|
51
|
+
spaceLayoutState: AtomToken<Join<{ size: number }>>
|
|
52
|
+
): ReadonlySelectorFamily<{ childSpaceIds: string[]; size: number }, string> =>
|
|
53
|
+
selectorFamily<{ childSpaceIds: string[]; size: number }, string>({
|
|
54
|
+
key: `${key}:explorer_space`,
|
|
55
|
+
get:
|
|
56
|
+
(me) =>
|
|
57
|
+
({ get }) => {
|
|
58
|
+
const join = get(spaceLayoutState)
|
|
59
|
+
const myFollowers = join.getRelatedIds(`parent:${me}`)
|
|
60
|
+
const myLeader = join.getRelatedId(me)
|
|
61
|
+
const { size } = myLeader
|
|
62
|
+
? join.getContent(myLeader, me) ?? { size: NaN }
|
|
63
|
+
: { size: NaN }
|
|
64
|
+
return { childSpaceIds: myFollowers, size }
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
export const makeSpaceFamily = (key: string): AtomFamily<number, string> =>
|
|
69
|
+
atomFamily<number, string>({
|
|
70
|
+
key: `${key}:space`,
|
|
71
|
+
default: 1,
|
|
72
|
+
effects: (subKey) => [lazyLocalStorageEffect(`${key}:${subKey}`)],
|
|
73
|
+
})
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { Location } from "react-router-dom"
|
|
2
|
+
|
|
3
|
+
import { key } from "~/packages/anvl/src/object"
|
|
4
|
+
|
|
5
|
+
import { persistStringSetAtom } from "./explorer-effects"
|
|
6
|
+
import type { AtomToken } from ".."
|
|
7
|
+
import type { AtomFamily } from "../atom"
|
|
8
|
+
import { atom, atomFamily } from "../atom"
|
|
9
|
+
import { lazyLocalStorageEffect } from "../web-effects"
|
|
10
|
+
|
|
11
|
+
export type View = {
|
|
12
|
+
title: string
|
|
13
|
+
location: Omit<Location, `state`>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const makeViewFamily = (key: string): AtomFamily<View, string> =>
|
|
17
|
+
atomFamily<View, string>({
|
|
18
|
+
key: `${key}:view`,
|
|
19
|
+
default: {
|
|
20
|
+
title: ``,
|
|
21
|
+
location: {
|
|
22
|
+
pathname: ``,
|
|
23
|
+
search: ``,
|
|
24
|
+
hash: ``,
|
|
25
|
+
key: ``,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
effects: (subKey) => [lazyLocalStorageEffect(`${key}:${subKey}`)],
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
export const makeViewIndex = (key: string): AtomToken<Set<string>> =>
|
|
32
|
+
atom<Set<string>>({
|
|
33
|
+
key: `${key}:view_index`,
|
|
34
|
+
default: new Set(),
|
|
35
|
+
effects: [persistStringSetAtom(`${key}:view_index`)],
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
export const makeViewFocusedFamily = (key: string): AtomFamily<number, string> =>
|
|
39
|
+
atomFamily<number, string>({
|
|
40
|
+
key: `${key}:view_focused`,
|
|
41
|
+
default: 0,
|
|
42
|
+
effects: (subKey) => [lazyLocalStorageEffect(`${key}:${subKey}`)],
|
|
43
|
+
})
|
package/src/selector.ts
CHANGED
|
@@ -2,7 +2,7 @@ import type * as Rx from "rxjs"
|
|
|
2
2
|
|
|
3
3
|
import type { Serializable } from "~/packages/anvl/src/json"
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type { ReadonlySelectorToken, SelectorToken } from "."
|
|
6
6
|
import { selectorFamily__INTERNAL, selector__INTERNAL } from "./internal"
|
|
7
7
|
import type { Read, Write } from "./transaction"
|
|
8
8
|
|
|
@@ -13,13 +13,13 @@ export type SelectorOptions<T> = {
|
|
|
13
13
|
}
|
|
14
14
|
export type ReadonlySelectorOptions<T> = Omit<SelectorOptions<T>, `set`>
|
|
15
15
|
|
|
16
|
-
export function selector<T>(options: SelectorOptions<T>): SelectorToken<T>
|
|
17
16
|
export function selector<T>(
|
|
18
17
|
options: ReadonlySelectorOptions<T>
|
|
19
|
-
):
|
|
18
|
+
): ReadonlySelectorToken<T>
|
|
19
|
+
export function selector<T>(options: SelectorOptions<T>): SelectorToken<T>
|
|
20
20
|
export function selector<T>(
|
|
21
21
|
options: ReadonlySelectorOptions<T> | SelectorOptions<T>
|
|
22
|
-
):
|
|
22
|
+
): ReadonlySelectorToken<T> | SelectorToken<T> {
|
|
23
23
|
return selector__INTERNAL(options)
|
|
24
24
|
}
|
|
25
25
|
|
|
@@ -43,10 +43,10 @@ export type SelectorFamily<T, K extends Serializable = Serializable> = ((
|
|
|
43
43
|
|
|
44
44
|
export type ReadonlySelectorFamily<T, K extends Serializable = Serializable> = ((
|
|
45
45
|
key: K
|
|
46
|
-
) =>
|
|
46
|
+
) => ReadonlySelectorToken<T>) & {
|
|
47
47
|
key: string
|
|
48
48
|
type: `readonly_selector_family`
|
|
49
|
-
subject: Rx.Subject<
|
|
49
|
+
subject: Rx.Subject<ReadonlySelectorToken<T>>
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
export function selectorFamily<T, K extends Serializable>(
|
package/src/subscribe.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ReadonlySelectorToken, StateToken, TransactionToken, ƒn } from "."
|
|
2
2
|
import type { Store, TransactionUpdate } from "./internal"
|
|
3
3
|
import { IMPLICIT, subscribeToRootAtoms, withdraw } from "./internal"
|
|
4
4
|
|
|
@@ -6,7 +6,7 @@ export type StateUpdate<T> = { newValue: T; oldValue: T }
|
|
|
6
6
|
export type UpdateHandler<T> = (update: StateUpdate<T>) => void
|
|
7
7
|
|
|
8
8
|
export const subscribe = <T>(
|
|
9
|
-
token:
|
|
9
|
+
token: ReadonlySelectorToken<T> | StateToken<T>,
|
|
10
10
|
handleUpdate: UpdateHandler<T>,
|
|
11
11
|
store: Store = IMPLICIT.STORE
|
|
12
12
|
): (() => void) => {
|
package/src/timeline.ts
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import type { AtomFamily, AtomToken } from "."
|
|
2
2
|
import { IMPLICIT } from "./internal"
|
|
3
|
-
import {
|
|
4
|
-
redo__INTERNAL,
|
|
5
|
-
timeline__INTERNAL,
|
|
6
|
-
undo__INTERNAL,
|
|
7
|
-
} from "./internal/timeline-internal"
|
|
3
|
+
import { redo__INTERNAL, timeline__INTERNAL, undo__INTERNAL } from "./internal/"
|
|
8
4
|
|
|
9
5
|
export type TimelineToken = {
|
|
10
6
|
key: string
|
package/src/transaction.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type * as Rx from "rxjs"
|
|
2
2
|
|
|
3
|
-
import type {
|
|
3
|
+
import type { ReadonlySelectorToken, StateToken, TransactionToken } from "."
|
|
4
4
|
import type { Store, TransactionUpdate } from "./internal"
|
|
5
5
|
import { IMPLICIT, transaction__INTERNAL, withdraw } from "./internal"
|
|
6
6
|
|
|
7
7
|
export type ƒn = (...parameters: any[]) => any
|
|
8
8
|
|
|
9
9
|
export type Transactors = {
|
|
10
|
-
get: <S>(state:
|
|
10
|
+
get: <S>(state: ReadonlySelectorToken<S> | StateToken<S>) => S
|
|
11
11
|
set: <S>(state: StateToken<S>, newValue: S | ((oldValue: S) => S)) => void
|
|
12
12
|
}
|
|
13
13
|
export type ReadonlyTransactors = Pick<Transactors, `get`>
|
|
@@ -33,6 +33,8 @@ export type Transaction<ƒ extends ƒn> = {
|
|
|
33
33
|
run: (...parameters: Parameters<ƒ>) => ReturnType<ƒ>
|
|
34
34
|
subject: Rx.Subject<TransactionUpdate<ƒ>>
|
|
35
35
|
}
|
|
36
|
+
export type TransactionIO<Token extends TransactionToken<any>> =
|
|
37
|
+
Token extends TransactionToken<infer ƒ> ? ƒ : never
|
|
36
38
|
|
|
37
39
|
export function transaction<ƒ extends ƒn>(
|
|
38
40
|
options: TransactionOptions<ƒ>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./storage"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { Json } from "~/packages/anvl/src/json"
|
|
2
|
+
|
|
3
|
+
import type { AtomEffect } from "../index"
|
|
4
|
+
|
|
5
|
+
export type StringInterface<T> = {
|
|
6
|
+
stringify: (t: T) => string
|
|
7
|
+
parse: (s: string) => T
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const persistAtom =
|
|
11
|
+
<T>(storage: Storage) =>
|
|
12
|
+
({ stringify, parse }: StringInterface<T>) =>
|
|
13
|
+
(key: string): AtomEffect<T> =>
|
|
14
|
+
({ setSelf, onSet }) => {
|
|
15
|
+
const savedValue = storage.getItem(key)
|
|
16
|
+
|
|
17
|
+
if (savedValue != null) setSelf(parse(savedValue))
|
|
18
|
+
|
|
19
|
+
onSet(({ newValue }) => {
|
|
20
|
+
if (newValue == null) {
|
|
21
|
+
storage.removeItem(key)
|
|
22
|
+
return
|
|
23
|
+
}
|
|
24
|
+
storage.setItem(key, stringify(newValue))
|
|
25
|
+
})
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const lazyLocalStorageEffect: <J extends Json>(
|
|
29
|
+
key: string
|
|
30
|
+
) => AtomEffect<J> = persistAtom(localStorage)(JSON)
|