@ariakit/store 0.1.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/CHANGELOG.md +23 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +216 -0
- package/dist/index.js.map +1 -0
- package/license +21 -0
- package/package.json +40 -0
- package/readme.md +17 -0
- package/src/index.ts +432 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# @ariakit/store
|
|
2
|
+
|
|
3
|
+
## 0.1.0
|
|
4
|
+
|
|
5
|
+
### Added standalone utility and store packages
|
|
6
|
+
|
|
7
|
+
The shared utility and store helpers are now available as pure ESM packages with a single public entrypoint:
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { invariant } from "@ariakit/utils";
|
|
11
|
+
import { createStore } from "@ariakit/store";
|
|
12
|
+
import { useStoreState } from "@ariakit/react-store";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
React consumers importing from `@ariakit/react` can continue to use `useStoreState` there. The standalone store packages are available for direct utility imports, and `@ariakit/react-components/store` exposes the React store helpers for component internals.
|
|
16
|
+
|
|
17
|
+
### Other updates
|
|
18
|
+
|
|
19
|
+
- Updated dependencies: `@ariakit/utils@0.1.0`
|
|
20
|
+
|
|
21
|
+
## 0.0.0
|
|
22
|
+
|
|
23
|
+
Initial release.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { AnyObject, SetStateAction } from "@ariakit/utils";
|
|
2
|
+
|
|
3
|
+
//#region src/index.d.ts
|
|
4
|
+
type Listener<S> = (state: S, prevState: S) => void | (() => void);
|
|
5
|
+
type Sync<S, K extends keyof S> = (keys: K[] | null, listener: Listener<Pick<S, K>>) => () => void;
|
|
6
|
+
type StoreSetup = (callback: () => void | (() => void)) => () => void;
|
|
7
|
+
type StoreInit = () => () => void;
|
|
8
|
+
type StoreSubscribe<S = State, K extends keyof S = keyof S> = Sync<S, K>;
|
|
9
|
+
type StoreSync<S = State, K extends keyof S = keyof S> = Sync<S, K>;
|
|
10
|
+
type StoreBatch<S = State, K extends keyof S = keyof S> = Sync<S, K>;
|
|
11
|
+
type StorePick<S = State, K extends ReadonlyArray<keyof S> = ReadonlyArray<keyof S>> = (keys: K) => Store<Pick<S, K[number]>>;
|
|
12
|
+
type StoreOmit<S = State, K extends ReadonlyArray<keyof S> = ReadonlyArray<keyof S>> = (keys: K) => Store<Omit<S, K[number]>>;
|
|
13
|
+
/**
|
|
14
|
+
* Creates a store.
|
|
15
|
+
* @param initialState Initial state.
|
|
16
|
+
* @param stores Stores to extend.
|
|
17
|
+
*/
|
|
18
|
+
declare function createStore<S extends State>(initialState: S, ...stores: Array<Store<Partial<S>> | undefined>): Store<S>;
|
|
19
|
+
declare function setup<T extends Store>(store?: T | null, ...args: Parameters<StoreSetup>): T extends Store ? ReturnType<StoreSetup> : void;
|
|
20
|
+
declare function init<T extends Store>(store?: T | null, ...args: Parameters<StoreInit>): T extends Store ? ReturnType<StoreInit> : void;
|
|
21
|
+
declare function subscribe<T extends Store, K extends keyof StoreState<T>>(store?: T | null, ...args: Parameters<StoreSubscribe<StoreState<T>, K>>): T extends Store ? ReturnType<StoreSubscribe<StoreState<T>, K>> : void;
|
|
22
|
+
declare function sync<T extends Store, K extends keyof StoreState<T>>(store?: T | null, ...args: Parameters<StoreSync<StoreState<T>, K>>): T extends Store ? ReturnType<StoreSync<StoreState<T>, K>> : void;
|
|
23
|
+
declare function batch<T extends Store, K extends keyof StoreState<T>>(store?: T | null, ...args: Parameters<StoreBatch<StoreState<T>, K>>): T extends Store ? ReturnType<StoreBatch<StoreState<T>, K>> : void;
|
|
24
|
+
declare function omit<T extends Store, K extends ReadonlyArray<keyof StoreState<T>>>(store?: T | null, ...args: Parameters<StoreOmit<StoreState<T>, K>>): T extends Store ? ReturnType<StoreOmit<StoreState<T>, K>> : void;
|
|
25
|
+
declare function pick<T extends Store, K extends ReadonlyArray<keyof StoreState<T>>>(store?: T | null, ...args: Parameters<StorePick<StoreState<T>, K>>): T extends Store ? ReturnType<StorePick<StoreState<T>, K>> : void;
|
|
26
|
+
/**
|
|
27
|
+
* Merges multiple stores into a single store.
|
|
28
|
+
*/
|
|
29
|
+
declare function mergeStore<S extends State>(...stores: Array<Store<S> | undefined>): Store<S>;
|
|
30
|
+
/**
|
|
31
|
+
* Throws when a store prop is passed in conjunction with a default state.
|
|
32
|
+
*/
|
|
33
|
+
declare function throwOnConflictingProps(props: AnyObject, store?: Store): void;
|
|
34
|
+
/**
|
|
35
|
+
* Store state type.
|
|
36
|
+
*/
|
|
37
|
+
type State = AnyObject;
|
|
38
|
+
/**
|
|
39
|
+
* Initial state that can be passed to a store creator function.
|
|
40
|
+
* @template S State type.
|
|
41
|
+
* @template K Key type.
|
|
42
|
+
*/
|
|
43
|
+
type StoreOptions<S extends State, K extends keyof S> = Partial<Pick<S, K>>;
|
|
44
|
+
/**
|
|
45
|
+
* Props that can be passed to a store creator function.
|
|
46
|
+
* @template S State type.
|
|
47
|
+
*/
|
|
48
|
+
interface StoreProps<S extends State = State> {
|
|
49
|
+
/**
|
|
50
|
+
* Another store object that will be kept in sync with the original store.
|
|
51
|
+
*
|
|
52
|
+
* Live examples:
|
|
53
|
+
* - [Navigation Menubar](https://ariakit.com/examples/menubar-navigation)
|
|
54
|
+
*/
|
|
55
|
+
store?: Store<Partial<S>>;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Extracts the state type from a store type.
|
|
59
|
+
* @template T Store type.
|
|
60
|
+
*/
|
|
61
|
+
type StoreState<T> = T extends Store<infer S> ? S : never;
|
|
62
|
+
/**
|
|
63
|
+
* Store.
|
|
64
|
+
* @template S State type.
|
|
65
|
+
*/
|
|
66
|
+
interface Store<S = State> {
|
|
67
|
+
/**
|
|
68
|
+
* Returns the current store state.
|
|
69
|
+
*/
|
|
70
|
+
getState(): S;
|
|
71
|
+
/**
|
|
72
|
+
* Sets a state value.
|
|
73
|
+
*/
|
|
74
|
+
setState<K extends keyof S>(key: K, value: SetStateAction<S[K]>): void;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { State, Store, StoreOptions, StoreProps, StoreState, batch, createStore, init, mergeStore, omit, pick, setup, subscribe, sync, throwOnConflictingProps };
|
|
78
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":["AnyObject","SetStateAction","Listener","S","state","prevState","Sync","K","Pick","keys","listener","StoreSetup","callback","StoreInit","StoreSubscribe","State","StoreSync","StoreBatch","StorePick","ReadonlyArray","Store","StoreOmit","Omit","createStore","Partial","Array","initialState","stores","setup","T","Parameters","ReturnType","store","args","init","subscribe","StoreState","sync","batch","omit","pick","mergeStore","throwOnConflictingProps","props","StoreOptions","StoreProps","getState","setState","key","value"],"sources":["../src/index.d.ts"],"mappings":"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"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { applyState, chain, getKeys, hasOwnProperty, invariant, noop, omit as omit$1, pick as pick$1 } from "@ariakit/utils";
|
|
2
|
+
//#region src/index.ts
|
|
3
|
+
function getInternal(store, key) {
|
|
4
|
+
const internals = store.__unstableInternals;
|
|
5
|
+
invariant(internals, "Invalid store");
|
|
6
|
+
return internals[key];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Creates a store.
|
|
10
|
+
* @param initialState Initial state.
|
|
11
|
+
* @param stores Stores to extend.
|
|
12
|
+
*/
|
|
13
|
+
function createStore(initialState, ...stores) {
|
|
14
|
+
let state = initialState;
|
|
15
|
+
let prevStateBatch = state;
|
|
16
|
+
let lastUpdate = Symbol();
|
|
17
|
+
let destroy = noop;
|
|
18
|
+
const instances = /* @__PURE__ */ new Set();
|
|
19
|
+
const updatedKeys = /* @__PURE__ */ new Set();
|
|
20
|
+
const setups = /* @__PURE__ */ new Set();
|
|
21
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
22
|
+
const batchListeners = /* @__PURE__ */ new Set();
|
|
23
|
+
const disposables = /* @__PURE__ */ new WeakMap();
|
|
24
|
+
const listenerKeys = /* @__PURE__ */ new WeakMap();
|
|
25
|
+
const storeSetup = (callback) => {
|
|
26
|
+
setups.add(callback);
|
|
27
|
+
return () => setups.delete(callback);
|
|
28
|
+
};
|
|
29
|
+
const storeInit = () => {
|
|
30
|
+
const initialized = instances.size;
|
|
31
|
+
const instance = Symbol();
|
|
32
|
+
instances.add(instance);
|
|
33
|
+
const maybeDestroy = () => {
|
|
34
|
+
instances.delete(instance);
|
|
35
|
+
if (instances.size) return;
|
|
36
|
+
destroy();
|
|
37
|
+
};
|
|
38
|
+
if (initialized) return maybeDestroy;
|
|
39
|
+
const desyncs = getKeys(state).map((key) => chain(...stores.map((store) => {
|
|
40
|
+
const storeState = store?.getState?.();
|
|
41
|
+
if (!storeState) return;
|
|
42
|
+
if (!hasOwnProperty(storeState, key)) return;
|
|
43
|
+
return sync(store, [key], (state) => {
|
|
44
|
+
setState(key, state[key], true);
|
|
45
|
+
});
|
|
46
|
+
})));
|
|
47
|
+
const teardowns = [];
|
|
48
|
+
for (const setup of setups) teardowns.push(setup());
|
|
49
|
+
const cleanups = stores.map(init);
|
|
50
|
+
destroy = chain(...desyncs, ...teardowns, ...cleanups);
|
|
51
|
+
return maybeDestroy;
|
|
52
|
+
};
|
|
53
|
+
const sub = (keys, listener, set = listeners) => {
|
|
54
|
+
set.add(listener);
|
|
55
|
+
listenerKeys.set(listener, keys);
|
|
56
|
+
return () => {
|
|
57
|
+
disposables.get(listener)?.();
|
|
58
|
+
disposables.delete(listener);
|
|
59
|
+
listenerKeys.delete(listener);
|
|
60
|
+
set.delete(listener);
|
|
61
|
+
};
|
|
62
|
+
};
|
|
63
|
+
const storeSubscribe = (keys, listener) => sub(keys, listener);
|
|
64
|
+
const storeSync = (keys, listener) => {
|
|
65
|
+
disposables.set(listener, listener(state, state));
|
|
66
|
+
return sub(keys, listener);
|
|
67
|
+
};
|
|
68
|
+
const storeBatch = (keys, listener) => {
|
|
69
|
+
disposables.set(listener, listener(state, prevStateBatch));
|
|
70
|
+
return sub(keys, listener, batchListeners);
|
|
71
|
+
};
|
|
72
|
+
const storePick = (keys) => createStore(pick$1(state, keys), finalStore);
|
|
73
|
+
const storeOmit = (keys) => createStore(omit$1(state, keys), finalStore);
|
|
74
|
+
const getState = () => state;
|
|
75
|
+
const setState = (key, value, fromStores = false) => {
|
|
76
|
+
if (!hasOwnProperty(state, key)) return;
|
|
77
|
+
const nextValue = applyState(value, state[key]);
|
|
78
|
+
if (nextValue === state[key]) return;
|
|
79
|
+
if (!fromStores) for (const store of stores) store?.setState?.(key, nextValue);
|
|
80
|
+
const prevState = state;
|
|
81
|
+
state = {
|
|
82
|
+
...state,
|
|
83
|
+
[key]: nextValue
|
|
84
|
+
};
|
|
85
|
+
const thisUpdate = Symbol();
|
|
86
|
+
lastUpdate = thisUpdate;
|
|
87
|
+
updatedKeys.add(key);
|
|
88
|
+
const run = (listener, prev, uKeys) => {
|
|
89
|
+
const keys = listenerKeys.get(listener);
|
|
90
|
+
const updated = (k) => uKeys ? uKeys.has(k) : k === key;
|
|
91
|
+
if (!keys || keys.some(updated)) {
|
|
92
|
+
disposables.get(listener)?.();
|
|
93
|
+
disposables.set(listener, listener(state, prev));
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
for (const listener of listeners) run(listener, prevState);
|
|
97
|
+
queueMicrotask(() => {
|
|
98
|
+
if (lastUpdate !== thisUpdate) return;
|
|
99
|
+
const snapshot = state;
|
|
100
|
+
for (const listener of batchListeners) run(listener, prevStateBatch, updatedKeys);
|
|
101
|
+
prevStateBatch = snapshot;
|
|
102
|
+
updatedKeys.clear();
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
const finalStore = {
|
|
106
|
+
getState,
|
|
107
|
+
setState,
|
|
108
|
+
__unstableInternals: {
|
|
109
|
+
setup: storeSetup,
|
|
110
|
+
init: storeInit,
|
|
111
|
+
subscribe: storeSubscribe,
|
|
112
|
+
sync: storeSync,
|
|
113
|
+
batch: storeBatch,
|
|
114
|
+
pick: storePick,
|
|
115
|
+
omit: storeOmit
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
return finalStore;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Register a callback function that's called when the store is initialized.
|
|
122
|
+
*/
|
|
123
|
+
function setup(store, ...args) {
|
|
124
|
+
if (!store) return;
|
|
125
|
+
return getInternal(store, "setup")(...args);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Function that should be called when the store is initialized.
|
|
129
|
+
*/
|
|
130
|
+
function init(store, ...args) {
|
|
131
|
+
if (!store) return;
|
|
132
|
+
return getInternal(store, "init")(...args);
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Registers a listener function that's called after state changes in the store.
|
|
136
|
+
*/
|
|
137
|
+
function subscribe(store, ...args) {
|
|
138
|
+
if (!store) return;
|
|
139
|
+
return getInternal(store, "subscribe")(...args);
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Registers a listener function that's called immediately and synchronously
|
|
143
|
+
* whenever the store state changes.
|
|
144
|
+
*/
|
|
145
|
+
function sync(store, ...args) {
|
|
146
|
+
if (!store) return;
|
|
147
|
+
return getInternal(store, "sync")(...args);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Registers a listener function that's called immediately and after a batch
|
|
151
|
+
* of state changes in the store.
|
|
152
|
+
*/
|
|
153
|
+
function batch(store, ...args) {
|
|
154
|
+
if (!store) return;
|
|
155
|
+
return getInternal(store, "batch")(...args);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Creates a new store with a subset of the current store state and keeps them
|
|
159
|
+
* in sync.
|
|
160
|
+
*/
|
|
161
|
+
function omit(store, ...args) {
|
|
162
|
+
if (!store) return;
|
|
163
|
+
return getInternal(store, "omit")(...args);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Creates a new store with a subset of the current store state and keeps them
|
|
167
|
+
* in sync.
|
|
168
|
+
*/
|
|
169
|
+
function pick(store, ...args) {
|
|
170
|
+
if (!store) return;
|
|
171
|
+
return getInternal(store, "pick")(...args);
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Merges multiple stores into a single store.
|
|
175
|
+
*/
|
|
176
|
+
function mergeStore(...stores) {
|
|
177
|
+
const initialState = {};
|
|
178
|
+
for (const store of stores) {
|
|
179
|
+
const nextState = store?.getState?.();
|
|
180
|
+
if (nextState) Object.assign(initialState, nextState);
|
|
181
|
+
}
|
|
182
|
+
const store = createStore(initialState, ...stores);
|
|
183
|
+
return Object.assign({}, ...stores, store);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Throws when a store prop is passed in conjunction with a default state.
|
|
187
|
+
*/
|
|
188
|
+
function throwOnConflictingProps(props, store) {
|
|
189
|
+
if (!store) return;
|
|
190
|
+
const defaultKeys = Object.entries(props).filter(([key, value]) => key.startsWith("default") && value !== void 0).map(([key]) => {
|
|
191
|
+
const stateKey = key.replace("default", "");
|
|
192
|
+
return `${stateKey[0]?.toLowerCase() || ""}${stateKey.slice(1)}`;
|
|
193
|
+
});
|
|
194
|
+
if (!defaultKeys.length) return;
|
|
195
|
+
const storeState = store.getState();
|
|
196
|
+
if (!defaultKeys.filter((key) => hasOwnProperty(storeState, key)).length) return;
|
|
197
|
+
throw new Error(`Passing a store prop in conjunction with a default state is not supported.
|
|
198
|
+
|
|
199
|
+
const store = useSelectStore();
|
|
200
|
+
<SelectProvider store={store} defaultValue="Apple" />
|
|
201
|
+
^ ^
|
|
202
|
+
|
|
203
|
+
Instead, pass the default state to the topmost store:
|
|
204
|
+
|
|
205
|
+
const store = useSelectStore({ defaultValue: "Apple" });
|
|
206
|
+
<SelectProvider store={store} />
|
|
207
|
+
|
|
208
|
+
See https://github.com/ariakit/ariakit/pull/2745 for more details.
|
|
209
|
+
|
|
210
|
+
If there's a particular need for this, please submit a feature request at https://github.com/ariakit/ariakit
|
|
211
|
+
`);
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
export { batch, createStore, init, mergeStore, omit, pick, setup, subscribe, sync, throwOnConflictingProps };
|
|
215
|
+
|
|
216
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":["_pick","_omit"],"sources":["../src/index.ts"],"sourcesContent":["import {\n omit as _omit,\n pick as _pick,\n applyState,\n chain,\n getKeys,\n hasOwnProperty,\n invariant,\n noop,\n} from \"@ariakit/utils\";\nimport type { AnyObject, SetStateAction } from \"@ariakit/utils\";\n\ntype Listener<S> = (state: S, prevState: S) => void | (() => void);\n\ntype Sync<S, K extends keyof S> = (\n keys: K[] | null,\n listener: Listener<Pick<S, K>>,\n) => () => void;\n\ntype StoreSetup = (callback: () => void | (() => void)) => () => void;\ntype StoreInit = () => () => void;\ntype StoreSubscribe<S = State, K extends keyof S = keyof S> = Sync<S, K>;\ntype StoreSync<S = State, K extends keyof S = keyof S> = Sync<S, K>;\ntype StoreBatch<S = State, K extends keyof S = keyof S> = Sync<S, K>;\ntype StorePick<\n S = State,\n K extends ReadonlyArray<keyof S> = ReadonlyArray<keyof S>,\n> = (keys: K) => Store<Pick<S, K[number]>>;\ntype StoreOmit<\n S = State,\n K extends ReadonlyArray<keyof S> = ReadonlyArray<keyof S>,\n> = (keys: K) => Store<Omit<S, K[number]>>;\n\ninterface StoreInternals<S = State> {\n setup: StoreSetup;\n init: StoreInit;\n subscribe: StoreSubscribe<S>;\n sync: StoreSync<S>;\n batch: StoreBatch<S>;\n pick: StorePick<S>;\n omit: StoreOmit<S>;\n}\n\nfunction getInternal<K extends keyof StoreInternals>(\n store: Store & { __unstableInternals?: StoreInternals },\n key: K,\n): StoreInternals[K] {\n const internals = store.__unstableInternals;\n invariant(internals, \"Invalid store\");\n return internals[key];\n}\n\n/**\n * Creates a store.\n * @param initialState Initial state.\n * @param stores Stores to extend.\n */\nexport function createStore<S extends State>(\n initialState: S,\n ...stores: Array<Store<Partial<S>> | undefined>\n): Store<S> {\n let state = initialState;\n let prevStateBatch = state;\n let lastUpdate = Symbol();\n let destroy = noop;\n const instances = new Set<symbol>();\n const updatedKeys = new Set<keyof S>();\n\n const setups = new Set<() => void | (() => void)>();\n const listeners = new Set<Listener<S>>();\n const batchListeners = new Set<Listener<S>>();\n const disposables = new WeakMap<Listener<S>, void | (() => void)>();\n const listenerKeys = new WeakMap<Listener<S>, Array<keyof S> | null>();\n\n const storeSetup: StoreSetup = (callback) => {\n setups.add(callback);\n return () => setups.delete(callback);\n };\n\n const storeInit: StoreInit = () => {\n // Make sure we only initialize the store once, even when it's passed to\n // other stores. However, the store can't be destroyed until all instances\n // are unmounted. See https://github.com/ariakit/ariakit/issues/3147. See\n // select-default-open-controlled tests.\n const initialized = instances.size;\n const instance = Symbol();\n instances.add(instance);\n\n const maybeDestroy = () => {\n instances.delete(instance);\n if (instances.size) return;\n destroy();\n };\n\n if (initialized) return maybeDestroy;\n\n const desyncs = getKeys(state).map((key) =>\n chain(\n ...stores.map((store) => {\n const storeState = store?.getState?.();\n if (!storeState) return;\n if (!hasOwnProperty(storeState, key)) return;\n return sync(store, [key], (state) => {\n setState(\n key,\n state[key],\n // @ts-expect-error - Not public API. This is just to prevent\n // infinite loops.\n true,\n );\n });\n }),\n ),\n );\n\n const teardowns: Array<void | (() => void)> = [];\n for (const setup of setups) {\n teardowns.push(setup());\n }\n\n const cleanups = stores.map(init);\n\n destroy = chain(...desyncs, ...teardowns, ...cleanups);\n\n return maybeDestroy;\n };\n\n const sub = (\n keys: Array<keyof S> | null,\n listener: Listener<S>,\n set = listeners,\n ) => {\n set.add(listener);\n listenerKeys.set(listener, keys);\n return () => {\n disposables.get(listener)?.();\n disposables.delete(listener);\n listenerKeys.delete(listener);\n set.delete(listener);\n };\n };\n\n const storeSubscribe: StoreSubscribe<S> = (keys, listener) =>\n sub(keys, listener);\n\n const storeSync: StoreSync<S> = (keys, listener) => {\n disposables.set(listener, listener(state, state));\n return sub(keys, listener);\n };\n\n const storeBatch: StoreBatch<S> = (keys, listener) => {\n disposables.set(listener, listener(state, prevStateBatch));\n return sub(keys, listener, batchListeners);\n };\n\n const storePick: StorePick<S, ReadonlyArray<keyof S>> = (keys) =>\n createStore(_pick(state, keys), finalStore);\n\n const storeOmit: StoreOmit<S, ReadonlyArray<keyof S>> = (keys) =>\n createStore(_omit(state, keys), finalStore);\n\n const getState: Store<S>[\"getState\"] = () => state;\n\n const setState: Store<S>[\"setState\"] = (key, value, fromStores = false) => {\n if (!hasOwnProperty(state, key)) return;\n\n const nextValue = applyState(value, state[key]);\n\n if (nextValue === state[key]) return;\n\n if (!fromStores) {\n for (const store of stores) {\n store?.setState?.(key, nextValue);\n }\n }\n\n const prevState = state;\n state = { ...state, [key]: nextValue };\n\n const thisUpdate = Symbol();\n lastUpdate = thisUpdate;\n updatedKeys.add(key);\n\n const run = (listener: Listener<S>, prev: S, uKeys?: Set<keyof S>) => {\n const keys = listenerKeys.get(listener);\n const updated = (k: keyof S) => (uKeys ? uKeys.has(k) : k === key);\n if (!keys || keys.some(updated)) {\n disposables.get(listener)?.();\n disposables.set(listener, listener(state, prev));\n }\n };\n\n for (const listener of listeners) {\n run(listener, prevState);\n }\n\n queueMicrotask(() => {\n // If setState is called again before this microtask runs, skip this\n // update. This is to prevent unnecessary updates when multiple keys are\n // updated in a single microtask.\n if (lastUpdate !== thisUpdate) return;\n // Take a snapshot of the state before running batch listeners. This is\n // necessary because batch listeners can setState.\n const snapshot = state;\n for (const listener of batchListeners) {\n run(listener, prevStateBatch, updatedKeys);\n }\n prevStateBatch = snapshot;\n updatedKeys.clear();\n });\n };\n\n const finalStore = {\n getState,\n setState,\n __unstableInternals: {\n setup: storeSetup,\n init: storeInit,\n subscribe: storeSubscribe,\n sync: storeSync,\n batch: storeBatch,\n pick: storePick,\n omit: storeOmit,\n },\n };\n\n return finalStore;\n}\n\nexport function setup<T extends Store>(\n store?: T | null,\n ...args: Parameters<StoreSetup>\n): T extends Store ? ReturnType<StoreSetup> : void;\n\n/**\n * Register a callback function that's called when the store is initialized.\n */\nexport function setup(store?: Store, ...args: Parameters<StoreSetup>) {\n if (!store) return;\n return getInternal(store, \"setup\")(...args);\n}\n\nexport function init<T extends Store>(\n store?: T | null,\n ...args: Parameters<StoreInit>\n): T extends Store ? ReturnType<StoreInit> : void;\n\n/**\n * Function that should be called when the store is initialized.\n */\nexport function init(store?: Store, ...args: Parameters<StoreInit>) {\n if (!store) return;\n return getInternal(store, \"init\")(...args);\n}\n\nexport function subscribe<T extends Store, K extends keyof StoreState<T>>(\n store?: T | null,\n ...args: Parameters<StoreSubscribe<StoreState<T>, K>>\n): T extends Store ? ReturnType<StoreSubscribe<StoreState<T>, K>> : void;\n\n/**\n * Registers a listener function that's called after state changes in the store.\n */\nexport function subscribe(store?: Store, ...args: Parameters<StoreSubscribe>) {\n if (!store) return;\n return getInternal(store, \"subscribe\")(...args);\n}\n\nexport function sync<T extends Store, K extends keyof StoreState<T>>(\n store?: T | null,\n ...args: Parameters<StoreSync<StoreState<T>, K>>\n): T extends Store ? ReturnType<StoreSync<StoreState<T>, K>> : void;\n\n/**\n * Registers a listener function that's called immediately and synchronously\n * whenever the store state changes.\n */\nexport function sync(store?: Store, ...args: Parameters<StoreSync>) {\n if (!store) return;\n return getInternal(store, \"sync\")(...args);\n}\n\nexport function batch<T extends Store, K extends keyof StoreState<T>>(\n store?: T | null,\n ...args: Parameters<StoreBatch<StoreState<T>, K>>\n): T extends Store ? ReturnType<StoreBatch<StoreState<T>, K>> : void;\n\n/**\n * Registers a listener function that's called immediately and after a batch\n * of state changes in the store.\n */\nexport function batch(store?: Store, ...args: Parameters<StoreBatch>) {\n if (!store) return;\n return getInternal(store, \"batch\")(...args);\n}\n\nexport function omit<\n T extends Store,\n K extends ReadonlyArray<keyof StoreState<T>>,\n>(\n store?: T | null,\n ...args: Parameters<StoreOmit<StoreState<T>, K>>\n): T extends Store ? ReturnType<StoreOmit<StoreState<T>, K>> : void;\n\n/**\n * Creates a new store with a subset of the current store state and keeps them\n * in sync.\n */\nexport function omit(store?: Store, ...args: Parameters<StoreOmit>) {\n if (!store) return;\n return getInternal(store, \"omit\")(...args);\n}\n\nexport function pick<\n T extends Store,\n K extends ReadonlyArray<keyof StoreState<T>>,\n>(\n store?: T | null,\n ...args: Parameters<StorePick<StoreState<T>, K>>\n): T extends Store ? ReturnType<StorePick<StoreState<T>, K>> : void;\n\n/**\n * Creates a new store with a subset of the current store state and keeps them\n * in sync.\n */\nexport function pick(store: Store, ...args: Parameters<StorePick>) {\n if (!store) return;\n return getInternal(store, \"pick\")(...args);\n}\n\n/**\n * Merges multiple stores into a single store.\n */\nexport function mergeStore<S extends State>(\n ...stores: Array<Store<S> | undefined>\n): Store<S> {\n const initialState = {} as S;\n for (const store of stores) {\n const nextState = store?.getState?.();\n if (nextState) {\n Object.assign(initialState, nextState);\n }\n }\n const store = createStore(initialState, ...stores);\n return Object.assign({}, ...stores, store);\n}\n\n/**\n * Throws when a store prop is passed in conjunction with a default state.\n */\nexport function throwOnConflictingProps(props: AnyObject, store?: Store) {\n if (process.env.NODE_ENV === \"production\") return;\n if (!store) return;\n const defaultKeys = Object.entries(props)\n .filter(([key, value]) => key.startsWith(\"default\") && value !== undefined)\n .map(([key]) => {\n const stateKey = key.replace(\"default\", \"\");\n return `${stateKey[0]?.toLowerCase() || \"\"}${stateKey.slice(1)}`;\n });\n if (!defaultKeys.length) return;\n const storeState = store.getState();\n const conflictingProps = defaultKeys.filter((key) =>\n hasOwnProperty(storeState, key),\n );\n if (!conflictingProps.length) return;\n throw new Error(\n `Passing a store prop in conjunction with a default state is not supported.\n\nconst store = useSelectStore();\n<SelectProvider store={store} defaultValue=\"Apple\" />\n ^ ^\n\nInstead, pass the default state to the topmost store:\n\nconst store = useSelectStore({ defaultValue: \"Apple\" });\n<SelectProvider store={store} />\n\nSee https://github.com/ariakit/ariakit/pull/2745 for more details.\n\nIf there's a particular need for this, please submit a feature request at https://github.com/ariakit/ariakit\n`,\n );\n}\n\n/**\n * Store state type.\n */\nexport type State = AnyObject;\n\n/**\n * Initial state that can be passed to a store creator function.\n * @template S State type.\n * @template K Key type.\n */\nexport type StoreOptions<S extends State, K extends keyof S> = Partial<\n Pick<S, K>\n>;\n\n/**\n * Props that can be passed to a store creator function.\n * @template S State type.\n */\nexport interface StoreProps<S extends State = State> {\n /**\n * Another store object that will be kept in sync with the original store.\n *\n * Live examples:\n * - [Navigation Menubar](https://ariakit.com/examples/menubar-navigation)\n */\n store?: Store<Partial<S>>;\n}\n\n/**\n * Extracts the state type from a store type.\n * @template T Store type.\n */\nexport type StoreState<T> = T extends Store<infer S> ? S : never;\n\n/**\n * Store.\n * @template S State type.\n */\nexport interface Store<S = State> {\n /**\n * Returns the current store state.\n */\n getState(): S;\n /**\n * Sets a state value.\n */\n setState<K extends keyof S>(key: K, value: SetStateAction<S[K]>): void;\n}\n"],"mappings":"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"}
|
package/license
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025-present Ariakit FZ-LLC
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ariakit/store",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Ariakit store utilities",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"ariakit",
|
|
7
|
+
"store"
|
|
8
|
+
],
|
|
9
|
+
"homepage": "https://ariakit.com",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"author": {
|
|
12
|
+
"name": "Diego Haz",
|
|
13
|
+
"email": "hazdiego@gmail.com",
|
|
14
|
+
"url": "https://github.com/diegohaz"
|
|
15
|
+
},
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://github.com/ariakit/ariakit.git",
|
|
19
|
+
"directory": "packages/ariakit-store"
|
|
20
|
+
},
|
|
21
|
+
"type": "module",
|
|
22
|
+
"sideEffects": false,
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@ariakit/utils": "0.1.0"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"@ariakit/scripts": "0.0.0"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"import": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "ariakit build --index-only",
|
|
38
|
+
"clean": "ariakit clean"
|
|
39
|
+
}
|
|
40
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# @ariakit/store
|
|
2
|
+
|
|
3
|
+
**Important:** This package is experimental and does not follow semantic versioning, meaning breaking changes may occur in patch and minor versions.
|
|
4
|
+
|
|
5
|
+
Framework-agnostic store primitives used by Ariakit packages.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npm i @ariakit/store
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Import store helpers from the package root:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createStore } from "@ariakit/store";
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This package is ESM-only and exposes a single public entrypoint.
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import {
|
|
2
|
+
omit as _omit,
|
|
3
|
+
pick as _pick,
|
|
4
|
+
applyState,
|
|
5
|
+
chain,
|
|
6
|
+
getKeys,
|
|
7
|
+
hasOwnProperty,
|
|
8
|
+
invariant,
|
|
9
|
+
noop,
|
|
10
|
+
} from "@ariakit/utils";
|
|
11
|
+
import type { AnyObject, SetStateAction } from "@ariakit/utils";
|
|
12
|
+
|
|
13
|
+
type Listener<S> = (state: S, prevState: S) => void | (() => void);
|
|
14
|
+
|
|
15
|
+
type Sync<S, K extends keyof S> = (
|
|
16
|
+
keys: K[] | null,
|
|
17
|
+
listener: Listener<Pick<S, K>>,
|
|
18
|
+
) => () => void;
|
|
19
|
+
|
|
20
|
+
type StoreSetup = (callback: () => void | (() => void)) => () => void;
|
|
21
|
+
type StoreInit = () => () => void;
|
|
22
|
+
type StoreSubscribe<S = State, K extends keyof S = keyof S> = Sync<S, K>;
|
|
23
|
+
type StoreSync<S = State, K extends keyof S = keyof S> = Sync<S, K>;
|
|
24
|
+
type StoreBatch<S = State, K extends keyof S = keyof S> = Sync<S, K>;
|
|
25
|
+
type StorePick<
|
|
26
|
+
S = State,
|
|
27
|
+
K extends ReadonlyArray<keyof S> = ReadonlyArray<keyof S>,
|
|
28
|
+
> = (keys: K) => Store<Pick<S, K[number]>>;
|
|
29
|
+
type StoreOmit<
|
|
30
|
+
S = State,
|
|
31
|
+
K extends ReadonlyArray<keyof S> = ReadonlyArray<keyof S>,
|
|
32
|
+
> = (keys: K) => Store<Omit<S, K[number]>>;
|
|
33
|
+
|
|
34
|
+
interface StoreInternals<S = State> {
|
|
35
|
+
setup: StoreSetup;
|
|
36
|
+
init: StoreInit;
|
|
37
|
+
subscribe: StoreSubscribe<S>;
|
|
38
|
+
sync: StoreSync<S>;
|
|
39
|
+
batch: StoreBatch<S>;
|
|
40
|
+
pick: StorePick<S>;
|
|
41
|
+
omit: StoreOmit<S>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getInternal<K extends keyof StoreInternals>(
|
|
45
|
+
store: Store & { __unstableInternals?: StoreInternals },
|
|
46
|
+
key: K,
|
|
47
|
+
): StoreInternals[K] {
|
|
48
|
+
const internals = store.__unstableInternals;
|
|
49
|
+
invariant(internals, "Invalid store");
|
|
50
|
+
return internals[key];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Creates a store.
|
|
55
|
+
* @param initialState Initial state.
|
|
56
|
+
* @param stores Stores to extend.
|
|
57
|
+
*/
|
|
58
|
+
export function createStore<S extends State>(
|
|
59
|
+
initialState: S,
|
|
60
|
+
...stores: Array<Store<Partial<S>> | undefined>
|
|
61
|
+
): Store<S> {
|
|
62
|
+
let state = initialState;
|
|
63
|
+
let prevStateBatch = state;
|
|
64
|
+
let lastUpdate = Symbol();
|
|
65
|
+
let destroy = noop;
|
|
66
|
+
const instances = new Set<symbol>();
|
|
67
|
+
const updatedKeys = new Set<keyof S>();
|
|
68
|
+
|
|
69
|
+
const setups = new Set<() => void | (() => void)>();
|
|
70
|
+
const listeners = new Set<Listener<S>>();
|
|
71
|
+
const batchListeners = new Set<Listener<S>>();
|
|
72
|
+
const disposables = new WeakMap<Listener<S>, void | (() => void)>();
|
|
73
|
+
const listenerKeys = new WeakMap<Listener<S>, Array<keyof S> | null>();
|
|
74
|
+
|
|
75
|
+
const storeSetup: StoreSetup = (callback) => {
|
|
76
|
+
setups.add(callback);
|
|
77
|
+
return () => setups.delete(callback);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
const storeInit: StoreInit = () => {
|
|
81
|
+
// Make sure we only initialize the store once, even when it's passed to
|
|
82
|
+
// other stores. However, the store can't be destroyed until all instances
|
|
83
|
+
// are unmounted. See https://github.com/ariakit/ariakit/issues/3147. See
|
|
84
|
+
// select-default-open-controlled tests.
|
|
85
|
+
const initialized = instances.size;
|
|
86
|
+
const instance = Symbol();
|
|
87
|
+
instances.add(instance);
|
|
88
|
+
|
|
89
|
+
const maybeDestroy = () => {
|
|
90
|
+
instances.delete(instance);
|
|
91
|
+
if (instances.size) return;
|
|
92
|
+
destroy();
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
if (initialized) return maybeDestroy;
|
|
96
|
+
|
|
97
|
+
const desyncs = getKeys(state).map((key) =>
|
|
98
|
+
chain(
|
|
99
|
+
...stores.map((store) => {
|
|
100
|
+
const storeState = store?.getState?.();
|
|
101
|
+
if (!storeState) return;
|
|
102
|
+
if (!hasOwnProperty(storeState, key)) return;
|
|
103
|
+
return sync(store, [key], (state) => {
|
|
104
|
+
setState(
|
|
105
|
+
key,
|
|
106
|
+
state[key],
|
|
107
|
+
// @ts-expect-error - Not public API. This is just to prevent
|
|
108
|
+
// infinite loops.
|
|
109
|
+
true,
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
}),
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const teardowns: Array<void | (() => void)> = [];
|
|
117
|
+
for (const setup of setups) {
|
|
118
|
+
teardowns.push(setup());
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const cleanups = stores.map(init);
|
|
122
|
+
|
|
123
|
+
destroy = chain(...desyncs, ...teardowns, ...cleanups);
|
|
124
|
+
|
|
125
|
+
return maybeDestroy;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const sub = (
|
|
129
|
+
keys: Array<keyof S> | null,
|
|
130
|
+
listener: Listener<S>,
|
|
131
|
+
set = listeners,
|
|
132
|
+
) => {
|
|
133
|
+
set.add(listener);
|
|
134
|
+
listenerKeys.set(listener, keys);
|
|
135
|
+
return () => {
|
|
136
|
+
disposables.get(listener)?.();
|
|
137
|
+
disposables.delete(listener);
|
|
138
|
+
listenerKeys.delete(listener);
|
|
139
|
+
set.delete(listener);
|
|
140
|
+
};
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const storeSubscribe: StoreSubscribe<S> = (keys, listener) =>
|
|
144
|
+
sub(keys, listener);
|
|
145
|
+
|
|
146
|
+
const storeSync: StoreSync<S> = (keys, listener) => {
|
|
147
|
+
disposables.set(listener, listener(state, state));
|
|
148
|
+
return sub(keys, listener);
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
const storeBatch: StoreBatch<S> = (keys, listener) => {
|
|
152
|
+
disposables.set(listener, listener(state, prevStateBatch));
|
|
153
|
+
return sub(keys, listener, batchListeners);
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const storePick: StorePick<S, ReadonlyArray<keyof S>> = (keys) =>
|
|
157
|
+
createStore(_pick(state, keys), finalStore);
|
|
158
|
+
|
|
159
|
+
const storeOmit: StoreOmit<S, ReadonlyArray<keyof S>> = (keys) =>
|
|
160
|
+
createStore(_omit(state, keys), finalStore);
|
|
161
|
+
|
|
162
|
+
const getState: Store<S>["getState"] = () => state;
|
|
163
|
+
|
|
164
|
+
const setState: Store<S>["setState"] = (key, value, fromStores = false) => {
|
|
165
|
+
if (!hasOwnProperty(state, key)) return;
|
|
166
|
+
|
|
167
|
+
const nextValue = applyState(value, state[key]);
|
|
168
|
+
|
|
169
|
+
if (nextValue === state[key]) return;
|
|
170
|
+
|
|
171
|
+
if (!fromStores) {
|
|
172
|
+
for (const store of stores) {
|
|
173
|
+
store?.setState?.(key, nextValue);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const prevState = state;
|
|
178
|
+
state = { ...state, [key]: nextValue };
|
|
179
|
+
|
|
180
|
+
const thisUpdate = Symbol();
|
|
181
|
+
lastUpdate = thisUpdate;
|
|
182
|
+
updatedKeys.add(key);
|
|
183
|
+
|
|
184
|
+
const run = (listener: Listener<S>, prev: S, uKeys?: Set<keyof S>) => {
|
|
185
|
+
const keys = listenerKeys.get(listener);
|
|
186
|
+
const updated = (k: keyof S) => (uKeys ? uKeys.has(k) : k === key);
|
|
187
|
+
if (!keys || keys.some(updated)) {
|
|
188
|
+
disposables.get(listener)?.();
|
|
189
|
+
disposables.set(listener, listener(state, prev));
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
for (const listener of listeners) {
|
|
194
|
+
run(listener, prevState);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
queueMicrotask(() => {
|
|
198
|
+
// If setState is called again before this microtask runs, skip this
|
|
199
|
+
// update. This is to prevent unnecessary updates when multiple keys are
|
|
200
|
+
// updated in a single microtask.
|
|
201
|
+
if (lastUpdate !== thisUpdate) return;
|
|
202
|
+
// Take a snapshot of the state before running batch listeners. This is
|
|
203
|
+
// necessary because batch listeners can setState.
|
|
204
|
+
const snapshot = state;
|
|
205
|
+
for (const listener of batchListeners) {
|
|
206
|
+
run(listener, prevStateBatch, updatedKeys);
|
|
207
|
+
}
|
|
208
|
+
prevStateBatch = snapshot;
|
|
209
|
+
updatedKeys.clear();
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const finalStore = {
|
|
214
|
+
getState,
|
|
215
|
+
setState,
|
|
216
|
+
__unstableInternals: {
|
|
217
|
+
setup: storeSetup,
|
|
218
|
+
init: storeInit,
|
|
219
|
+
subscribe: storeSubscribe,
|
|
220
|
+
sync: storeSync,
|
|
221
|
+
batch: storeBatch,
|
|
222
|
+
pick: storePick,
|
|
223
|
+
omit: storeOmit,
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
return finalStore;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export function setup<T extends Store>(
|
|
231
|
+
store?: T | null,
|
|
232
|
+
...args: Parameters<StoreSetup>
|
|
233
|
+
): T extends Store ? ReturnType<StoreSetup> : void;
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Register a callback function that's called when the store is initialized.
|
|
237
|
+
*/
|
|
238
|
+
export function setup(store?: Store, ...args: Parameters<StoreSetup>) {
|
|
239
|
+
if (!store) return;
|
|
240
|
+
return getInternal(store, "setup")(...args);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function init<T extends Store>(
|
|
244
|
+
store?: T | null,
|
|
245
|
+
...args: Parameters<StoreInit>
|
|
246
|
+
): T extends Store ? ReturnType<StoreInit> : void;
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Function that should be called when the store is initialized.
|
|
250
|
+
*/
|
|
251
|
+
export function init(store?: Store, ...args: Parameters<StoreInit>) {
|
|
252
|
+
if (!store) return;
|
|
253
|
+
return getInternal(store, "init")(...args);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function subscribe<T extends Store, K extends keyof StoreState<T>>(
|
|
257
|
+
store?: T | null,
|
|
258
|
+
...args: Parameters<StoreSubscribe<StoreState<T>, K>>
|
|
259
|
+
): T extends Store ? ReturnType<StoreSubscribe<StoreState<T>, K>> : void;
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Registers a listener function that's called after state changes in the store.
|
|
263
|
+
*/
|
|
264
|
+
export function subscribe(store?: Store, ...args: Parameters<StoreSubscribe>) {
|
|
265
|
+
if (!store) return;
|
|
266
|
+
return getInternal(store, "subscribe")(...args);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function sync<T extends Store, K extends keyof StoreState<T>>(
|
|
270
|
+
store?: T | null,
|
|
271
|
+
...args: Parameters<StoreSync<StoreState<T>, K>>
|
|
272
|
+
): T extends Store ? ReturnType<StoreSync<StoreState<T>, K>> : void;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Registers a listener function that's called immediately and synchronously
|
|
276
|
+
* whenever the store state changes.
|
|
277
|
+
*/
|
|
278
|
+
export function sync(store?: Store, ...args: Parameters<StoreSync>) {
|
|
279
|
+
if (!store) return;
|
|
280
|
+
return getInternal(store, "sync")(...args);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function batch<T extends Store, K extends keyof StoreState<T>>(
|
|
284
|
+
store?: T | null,
|
|
285
|
+
...args: Parameters<StoreBatch<StoreState<T>, K>>
|
|
286
|
+
): T extends Store ? ReturnType<StoreBatch<StoreState<T>, K>> : void;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* Registers a listener function that's called immediately and after a batch
|
|
290
|
+
* of state changes in the store.
|
|
291
|
+
*/
|
|
292
|
+
export function batch(store?: Store, ...args: Parameters<StoreBatch>) {
|
|
293
|
+
if (!store) return;
|
|
294
|
+
return getInternal(store, "batch")(...args);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function omit<
|
|
298
|
+
T extends Store,
|
|
299
|
+
K extends ReadonlyArray<keyof StoreState<T>>,
|
|
300
|
+
>(
|
|
301
|
+
store?: T | null,
|
|
302
|
+
...args: Parameters<StoreOmit<StoreState<T>, K>>
|
|
303
|
+
): T extends Store ? ReturnType<StoreOmit<StoreState<T>, K>> : void;
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Creates a new store with a subset of the current store state and keeps them
|
|
307
|
+
* in sync.
|
|
308
|
+
*/
|
|
309
|
+
export function omit(store?: Store, ...args: Parameters<StoreOmit>) {
|
|
310
|
+
if (!store) return;
|
|
311
|
+
return getInternal(store, "omit")(...args);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
export function pick<
|
|
315
|
+
T extends Store,
|
|
316
|
+
K extends ReadonlyArray<keyof StoreState<T>>,
|
|
317
|
+
>(
|
|
318
|
+
store?: T | null,
|
|
319
|
+
...args: Parameters<StorePick<StoreState<T>, K>>
|
|
320
|
+
): T extends Store ? ReturnType<StorePick<StoreState<T>, K>> : void;
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Creates a new store with a subset of the current store state and keeps them
|
|
324
|
+
* in sync.
|
|
325
|
+
*/
|
|
326
|
+
export function pick(store: Store, ...args: Parameters<StorePick>) {
|
|
327
|
+
if (!store) return;
|
|
328
|
+
return getInternal(store, "pick")(...args);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Merges multiple stores into a single store.
|
|
333
|
+
*/
|
|
334
|
+
export function mergeStore<S extends State>(
|
|
335
|
+
...stores: Array<Store<S> | undefined>
|
|
336
|
+
): Store<S> {
|
|
337
|
+
const initialState = {} as S;
|
|
338
|
+
for (const store of stores) {
|
|
339
|
+
const nextState = store?.getState?.();
|
|
340
|
+
if (nextState) {
|
|
341
|
+
Object.assign(initialState, nextState);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const store = createStore(initialState, ...stores);
|
|
345
|
+
return Object.assign({}, ...stores, store);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Throws when a store prop is passed in conjunction with a default state.
|
|
350
|
+
*/
|
|
351
|
+
export function throwOnConflictingProps(props: AnyObject, store?: Store) {
|
|
352
|
+
if (process.env.NODE_ENV === "production") return;
|
|
353
|
+
if (!store) return;
|
|
354
|
+
const defaultKeys = Object.entries(props)
|
|
355
|
+
.filter(([key, value]) => key.startsWith("default") && value !== undefined)
|
|
356
|
+
.map(([key]) => {
|
|
357
|
+
const stateKey = key.replace("default", "");
|
|
358
|
+
return `${stateKey[0]?.toLowerCase() || ""}${stateKey.slice(1)}`;
|
|
359
|
+
});
|
|
360
|
+
if (!defaultKeys.length) return;
|
|
361
|
+
const storeState = store.getState();
|
|
362
|
+
const conflictingProps = defaultKeys.filter((key) =>
|
|
363
|
+
hasOwnProperty(storeState, key),
|
|
364
|
+
);
|
|
365
|
+
if (!conflictingProps.length) return;
|
|
366
|
+
throw new Error(
|
|
367
|
+
`Passing a store prop in conjunction with a default state is not supported.
|
|
368
|
+
|
|
369
|
+
const store = useSelectStore();
|
|
370
|
+
<SelectProvider store={store} defaultValue="Apple" />
|
|
371
|
+
^ ^
|
|
372
|
+
|
|
373
|
+
Instead, pass the default state to the topmost store:
|
|
374
|
+
|
|
375
|
+
const store = useSelectStore({ defaultValue: "Apple" });
|
|
376
|
+
<SelectProvider store={store} />
|
|
377
|
+
|
|
378
|
+
See https://github.com/ariakit/ariakit/pull/2745 for more details.
|
|
379
|
+
|
|
380
|
+
If there's a particular need for this, please submit a feature request at https://github.com/ariakit/ariakit
|
|
381
|
+
`,
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Store state type.
|
|
387
|
+
*/
|
|
388
|
+
export type State = AnyObject;
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Initial state that can be passed to a store creator function.
|
|
392
|
+
* @template S State type.
|
|
393
|
+
* @template K Key type.
|
|
394
|
+
*/
|
|
395
|
+
export type StoreOptions<S extends State, K extends keyof S> = Partial<
|
|
396
|
+
Pick<S, K>
|
|
397
|
+
>;
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Props that can be passed to a store creator function.
|
|
401
|
+
* @template S State type.
|
|
402
|
+
*/
|
|
403
|
+
export interface StoreProps<S extends State = State> {
|
|
404
|
+
/**
|
|
405
|
+
* Another store object that will be kept in sync with the original store.
|
|
406
|
+
*
|
|
407
|
+
* Live examples:
|
|
408
|
+
* - [Navigation Menubar](https://ariakit.com/examples/menubar-navigation)
|
|
409
|
+
*/
|
|
410
|
+
store?: Store<Partial<S>>;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Extracts the state type from a store type.
|
|
415
|
+
* @template T Store type.
|
|
416
|
+
*/
|
|
417
|
+
export type StoreState<T> = T extends Store<infer S> ? S : never;
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Store.
|
|
421
|
+
* @template S State type.
|
|
422
|
+
*/
|
|
423
|
+
export interface Store<S = State> {
|
|
424
|
+
/**
|
|
425
|
+
* Returns the current store state.
|
|
426
|
+
*/
|
|
427
|
+
getState(): S;
|
|
428
|
+
/**
|
|
429
|
+
* Sets a state value.
|
|
430
|
+
*/
|
|
431
|
+
setState<K extends keyof S>(key: K, value: SetStateAction<S[K]>): void;
|
|
432
|
+
}
|