UNPKG

22.1 kBJavaScriptView Raw
1import { atom, SECRET_INTERNAL_getScopeContext, useAtom, useSetAtom } from 'jotai';
2export { useAtomValue, useSetAtom as useUpdateAtom } from 'jotai';
3import { useContext, useCallback, useMemo } from 'react';
4
5const RESET = Symbol();
6
7function atomWithReset(initialValue) {
8 const anAtom = atom(initialValue, (get, set, update) => {
9 if (update === RESET) {
10 set(anAtom, initialValue);
11 } else {
12 set(anAtom, typeof update === "function" ? update(get(anAtom)) : update);
13 }
14 });
15 return anAtom;
16}
17
18const WRITE_ATOM = "w";
19const RESTORE_ATOMS = "h";
20
21function useResetAtom(anAtom, scope) {
22 const ScopeContext = SECRET_INTERNAL_getScopeContext(scope);
23 const store = useContext(ScopeContext).s;
24 const setAtom = useCallback(() => store[WRITE_ATOM](anAtom, RESET), [store, anAtom]);
25 return setAtom;
26}
27
28function useReducerAtom(anAtom, reducer, scope) {
29 const [state, setState] = useAtom(anAtom, scope);
30 const dispatch = useCallback((action) => {
31 setState((prev) => reducer(prev, action));
32 }, [setState, reducer]);
33 return [state, dispatch];
34}
35
36function atomWithReducer(initialValue, reducer) {
37 const anAtom = atom(initialValue, (get, set, action) => set(anAtom, reducer(get(anAtom), action)));
38 return anAtom;
39}
40
41function atomFamily(initializeAtom, areEqual) {
42 let shouldRemove = null;
43 const atoms = /* @__PURE__ */ new Map();
44 const createAtom = (param) => {
45 let item;
46 if (areEqual === void 0) {
47 item = atoms.get(param);
48 } else {
49 for (const [key, value] of atoms) {
50 if (areEqual(key, param)) {
51 item = value;
52 break;
53 }
54 }
55 }
56 if (item !== void 0) {
57 if (shouldRemove == null ? void 0 : shouldRemove(item[1], param)) {
58 atoms.delete(param);
59 } else {
60 return item[0];
61 }
62 }
63 const newAtom = initializeAtom(param);
64 atoms.set(param, [newAtom, Date.now()]);
65 return newAtom;
66 };
67 createAtom.remove = (param) => {
68 if (areEqual === void 0) {
69 atoms.delete(param);
70 } else {
71 for (const [key] of atoms) {
72 if (areEqual(key, param)) {
73 atoms.delete(key);
74 break;
75 }
76 }
77 }
78 };
79 createAtom.setShouldRemove = (fn) => {
80 shouldRemove = fn;
81 if (!shouldRemove)
82 return;
83 for (const [key, value] of atoms) {
84 if (shouldRemove(value[1], key)) {
85 atoms.delete(key);
86 }
87 }
88 };
89 return createAtom;
90}
91
92const getWeakCacheItem = (cache, deps) => {
93 do {
94 const [dep, ...rest] = deps;
95 const entry = cache.get(dep);
96 if (!entry) {
97 return;
98 }
99 if (!rest.length) {
100 return entry[1];
101 }
102 cache = entry[0];
103 deps = rest;
104 } while (deps.length);
105};
106const setWeakCacheItem = (cache, deps, item) => {
107 do {
108 const [dep, ...rest] = deps;
109 let entry = cache.get(dep);
110 if (!entry) {
111 entry = [ new WeakMap()];
112 cache.set(dep, entry);
113 }
114 if (!rest.length) {
115 entry[1] = item;
116 return;
117 }
118 cache = entry[0];
119 deps = rest;
120 } while (deps.length);
121};
122const createMemoizeAtom = () => {
123 const cache = /* @__PURE__ */ new WeakMap();
124 const memoizeAtom = (createAtom, deps) => {
125 const cachedAtom = getWeakCacheItem(cache, deps);
126 if (cachedAtom) {
127 return cachedAtom;
128 }
129 const createdAtom = createAtom();
130 setWeakCacheItem(cache, deps, createdAtom);
131 return createdAtom;
132 };
133 return memoizeAtom;
134};
135
136const memoizeAtom$4 = createMemoizeAtom();
137function selectAtom(anAtom, selector, equalityFn = Object.is) {
138 return memoizeAtom$4(() => {
139 const refAtom = atom(() => ({}));
140 const derivedAtom = atom((get) => {
141 const slice = selector(get(anAtom));
142 const ref = get(refAtom);
143 if ("prev" in ref && equalityFn(ref.prev, slice)) {
144 return ref.prev;
145 }
146 ref.prev = slice;
147 return slice;
148 });
149 return derivedAtom;
150 }, [anAtom, selector, equalityFn]);
151}
152
153function useAtomCallback(callback, scope) {
154 const anAtom = useMemo(() => atom(null, (get, set, [arg, resolve, reject]) => {
155 try {
156 resolve(callback(get, set, arg));
157 } catch (e) {
158 reject(e);
159 }
160 }), [callback]);
161 const invoke = useSetAtom(anAtom, scope);
162 return useCallback((arg) => {
163 let isSync = true;
164 let settled = {};
165 const promise = new Promise((resolve, reject) => {
166 invoke([
167 arg,
168 (v) => {
169 if (isSync) {
170 settled = { v };
171 } else {
172 resolve(v);
173 }
174 },
175 (e) => {
176 if (isSync) {
177 settled = { e };
178 } else {
179 reject(e);
180 }
181 }
182 ]);
183 });
184 isSync = false;
185 if ("e" in settled) {
186 throw settled.e;
187 }
188 if ("v" in settled) {
189 return settled.v;
190 }
191 return promise;
192 }, [invoke]);
193}
194
195const memoizeAtom$3 = createMemoizeAtom();
196const deepFreeze = (obj) => {
197 if (typeof obj !== "object" || obj === null)
198 return;
199 Object.freeze(obj);
200 const propNames = Object.getOwnPropertyNames(obj);
201 for (const name of propNames) {
202 const value = obj[name];
203 deepFreeze(value);
204 }
205 return obj;
206};
207function freezeAtom(anAtom) {
208 return memoizeAtom$3(() => {
209 const frozenAtom = atom((get) => deepFreeze(get(anAtom)), (_get, set, arg) => set(anAtom, arg));
210 return frozenAtom;
211 }, [anAtom]);
212}
213function freezeAtomCreator(createAtom) {
214 return (...params) => {
215 const anAtom = createAtom(...params);
216 const origRead = anAtom.read;
217 anAtom.read = (get) => deepFreeze(origRead(get));
218 return anAtom;
219 };
220}
221
222const memoizeAtom$2 = createMemoizeAtom();
223const isWritable = (atom2) => !!atom2.write;
224const isFunction = (x) => typeof x === "function";
225function splitAtom(arrAtom, keyExtractor) {
226 return memoizeAtom$2(() => {
227 const mappingCache = /* @__PURE__ */ new WeakMap();
228 const getMapping = (arr, prev) => {
229 let mapping = mappingCache.get(arr);
230 if (mapping) {
231 return mapping;
232 }
233 const prevMapping = prev && mappingCache.get(prev);
234 const atomList = [];
235 const keyList = [];
236 arr.forEach((item, index) => {
237 const key = keyExtractor ? keyExtractor(item) : index;
238 keyList[index] = key;
239 const cachedAtom = prevMapping && prevMapping.atomList[prevMapping.keyList.indexOf(key)];
240 if (cachedAtom) {
241 atomList[index] = cachedAtom;
242 return;
243 }
244 const read2 = (get) => {
245 const ref = get(refAtom);
246 const currArr = get(arrAtom);
247 const mapping2 = getMapping(currArr, ref.prev);
248 const index2 = mapping2.keyList.indexOf(key);
249 if (index2 < 0 || index2 >= currArr.length) {
250 const prevItem = arr[getMapping(arr).keyList.indexOf(key)];
251 if (prevItem) {
252 return prevItem;
253 }
254 throw new Error("splitAtom: index out of bounds for read");
255 }
256 return currArr[index2];
257 };
258 const write2 = (get, set, update) => {
259 const ref = get(refAtom);
260 const arr2 = get(arrAtom);
261 const mapping2 = getMapping(arr2, ref.prev);
262 const index2 = mapping2.keyList.indexOf(key);
263 if (index2 < 0 || index2 >= arr2.length) {
264 throw new Error("splitAtom: index out of bounds for write");
265 }
266 const nextItem = isFunction(update) ? update(arr2[index2]) : update;
267 set(arrAtom, [
268 ...arr2.slice(0, index2),
269 nextItem,
270 ...arr2.slice(index2 + 1)
271 ]);
272 };
273 atomList[index] = isWritable(arrAtom) ? atom(read2, write2) : atom(read2);
274 });
275 if (prevMapping && prevMapping.keyList.length === keyList.length && prevMapping.keyList.every((x, i) => x === keyList[i])) {
276 mapping = prevMapping;
277 } else {
278 mapping = { atomList, keyList };
279 }
280 mappingCache.set(arr, mapping);
281 return mapping;
282 };
283 const refAtom = atom(() => ({}));
284 const read = (get) => {
285 const ref = get(refAtom);
286 const arr = get(arrAtom);
287 const mapping = getMapping(arr, ref.prev);
288 ref.prev = arr;
289 return mapping.atomList;
290 };
291 const write = (get, set, action) => {
292 if ("read" in action) {
293 console.warn("atomToRemove is deprecated. use action with type");
294 action = { type: "remove", atom: action };
295 }
296 switch (action.type) {
297 case "remove": {
298 const index = get(splittedAtom).indexOf(action.atom);
299 if (index >= 0) {
300 const arr = get(arrAtom);
301 set(arrAtom, [
302 ...arr.slice(0, index),
303 ...arr.slice(index + 1)
304 ]);
305 }
306 break;
307 }
308 case "insert": {
309 const index = action.before ? get(splittedAtom).indexOf(action.before) : get(splittedAtom).length;
310 if (index >= 0) {
311 const arr = get(arrAtom);
312 set(arrAtom, [
313 ...arr.slice(0, index),
314 action.value,
315 ...arr.slice(index)
316 ]);
317 }
318 break;
319 }
320 case "move": {
321 const index1 = get(splittedAtom).indexOf(action.atom);
322 const index2 = action.before ? get(splittedAtom).indexOf(action.before) : get(splittedAtom).length;
323 if (index1 >= 0 && index2 >= 0) {
324 const arr = get(arrAtom);
325 if (index1 < index2) {
326 set(arrAtom, [
327 ...arr.slice(0, index1),
328 ...arr.slice(index1 + 1, index2),
329 arr[index1],
330 ...arr.slice(index2)
331 ]);
332 } else {
333 set(arrAtom, [
334 ...arr.slice(0, index2),
335 arr[index1],
336 ...arr.slice(index2, index1),
337 ...arr.slice(index1 + 1)
338 ]);
339 }
340 }
341 break;
342 }
343 }
344 };
345 const splittedAtom = isWritable(arrAtom) ? atom(read, write) : atom(read);
346 return splittedAtom;
347 }, keyExtractor ? [arrAtom, keyExtractor] : [arrAtom]);
348}
349
350function atomWithDefault(getDefault) {
351 const EMPTY = Symbol();
352 const overwrittenAtom = atom(EMPTY);
353 const anAtom = atom((get) => {
354 const overwritten = get(overwrittenAtom);
355 if (overwritten !== EMPTY) {
356 return overwritten;
357 }
358 return getDefault(get);
359 }, (get, set, update) => {
360 if (update === RESET) {
361 return set(overwrittenAtom, EMPTY);
362 }
363 return set(overwrittenAtom, typeof update === "function" ? update(get(anAtom)) : update);
364 });
365 return anAtom;
366}
367
368var __defProp$1 = Object.defineProperty;
369var __defProps$1 = Object.defineProperties;
370var __getOwnPropDescs$1 = Object.getOwnPropertyDescriptors;
371var __getOwnPropSymbols$1 = Object.getOwnPropertySymbols;
372var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
373var __propIsEnum$1 = Object.prototype.propertyIsEnumerable;
374var __defNormalProp$1 = (obj, key, value) => key in obj ? __defProp$1(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
375var __spreadValues$1 = (a, b) => {
376 for (var prop in b || (b = {}))
377 if (__hasOwnProp$1.call(b, prop))
378 __defNormalProp$1(a, prop, b[prop]);
379 if (__getOwnPropSymbols$1)
380 for (var prop of __getOwnPropSymbols$1(b)) {
381 if (__propIsEnum$1.call(b, prop))
382 __defNormalProp$1(a, prop, b[prop]);
383 }
384 return a;
385};
386var __spreadProps$1 = (a, b) => __defProps$1(a, __getOwnPropDescs$1(b));
387const memoizeAtom$1 = createMemoizeAtom();
388function waitForAll(atoms) {
389 const createAtom = () => {
390 const unwrappedAtoms = unwrapAtoms(atoms);
391 const derivedAtom = atom((get) => {
392 const promises = [];
393 const values = unwrappedAtoms.map((anAtom, index) => {
394 try {
395 return get(anAtom);
396 } catch (e) {
397 if (e instanceof Promise) {
398 promises[index] = e;
399 } else {
400 throw e;
401 }
402 }
403 });
404 if (promises.length) {
405 throw Promise.all(promises);
406 }
407 return wrapResults(atoms, values);
408 });
409 return derivedAtom;
410 };
411 if (Array.isArray(atoms)) {
412 return memoizeAtom$1(createAtom, atoms);
413 }
414 return createAtom();
415}
416const unwrapAtoms = (atoms) => Array.isArray(atoms) ? atoms : Object.getOwnPropertyNames(atoms).map((key) => atoms[key]);
417const wrapResults = (atoms, results) => Array.isArray(atoms) ? results : Object.getOwnPropertyNames(atoms).reduce((out, key, idx) => __spreadProps$1(__spreadValues$1({}, out), { [key]: results[idx] }), {});
418
419var __defProp = Object.defineProperty;
420var __defProps = Object.defineProperties;
421var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
422var __getOwnPropSymbols = Object.getOwnPropertySymbols;
423var __hasOwnProp = Object.prototype.hasOwnProperty;
424var __propIsEnum = Object.prototype.propertyIsEnumerable;
425var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
426var __spreadValues = (a, b) => {
427 for (var prop in b || (b = {}))
428 if (__hasOwnProp.call(b, prop))
429 __defNormalProp(a, prop, b[prop]);
430 if (__getOwnPropSymbols)
431 for (var prop of __getOwnPropSymbols(b)) {
432 if (__propIsEnum.call(b, prop))
433 __defNormalProp(a, prop, b[prop]);
434 }
435 return a;
436};
437var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
438function createJSONStorage(getStringStorage) {
439 let lastStr;
440 let lastValue;
441 return {
442 getItem: (key) => {
443 const parse = (str2) => {
444 str2 = str2 || "";
445 if (lastStr !== str2) {
446 lastValue = JSON.parse(str2);
447 lastStr = str2;
448 }
449 return lastValue;
450 };
451 const str = getStringStorage().getItem(key);
452 if (str instanceof Promise) {
453 return str.then(parse);
454 }
455 return parse(str);
456 },
457 setItem: (key, newValue) => getStringStorage().setItem(key, JSON.stringify(newValue)),
458 removeItem: (key) => getStringStorage().removeItem(key)
459 };
460}
461const defaultStorage = createJSONStorage(() => localStorage);
462defaultStorage.subscribe = (key, callback) => {
463 const storageEventCallback = (e) => {
464 if (e.key === key && e.newValue) {
465 callback(JSON.parse(e.newValue));
466 }
467 };
468 window.addEventListener("storage", storageEventCallback);
469 return () => {
470 window.removeEventListener("storage", storageEventCallback);
471 };
472};
473function atomWithStorage(key, initialValue, storage = defaultStorage) {
474 const getInitialValue = () => {
475 try {
476 const value = storage.getItem(key);
477 if (value instanceof Promise) {
478 return value.catch(() => initialValue);
479 }
480 return value;
481 } catch {
482 return initialValue;
483 }
484 };
485 const baseAtom = atom(storage.delayInit ? initialValue : getInitialValue());
486 baseAtom.onMount = (setAtom) => {
487 let unsub;
488 if (storage.subscribe) {
489 unsub = storage.subscribe(key, setAtom);
490 setAtom(getInitialValue());
491 }
492 if (storage.delayInit) {
493 const value = getInitialValue();
494 if (value instanceof Promise) {
495 value.then(setAtom);
496 } else {
497 setAtom(value);
498 }
499 }
500 return unsub;
501 };
502 const anAtom = atom((get) => get(baseAtom), (get, set, update) => {
503 if (update === RESET) {
504 set(baseAtom, initialValue);
505 return storage.removeItem(key);
506 }
507 const newValue = typeof update === "function" ? update(get(baseAtom)) : update;
508 set(baseAtom, newValue);
509 return storage.setItem(key, newValue);
510 });
511 return anAtom;
512}
513function atomWithHash(key, initialValue, options) {
514 const serialize = (options == null ? void 0 : options.serialize) || JSON.stringify;
515 const deserialize = (options == null ? void 0 : options.deserialize) || JSON.parse;
516 const subscribe = (options == null ? void 0 : options.subscribe) || ((callback) => {
517 window.addEventListener("hashchange", callback);
518 return () => {
519 window.removeEventListener("hashchange", callback);
520 };
521 });
522 const hashStorage = __spreadProps(__spreadValues({
523 getItem: (key2) => {
524 const searchParams = new URLSearchParams(location.hash.slice(1));
525 const storedValue = searchParams.get(key2);
526 if (storedValue === null) {
527 throw new Error("no value stored");
528 }
529 return deserialize(storedValue);
530 },
531 setItem: (key2, newValue) => {
532 const searchParams = new URLSearchParams(location.hash.slice(1));
533 searchParams.set(key2, serialize(newValue));
534 if (options == null ? void 0 : options.replaceState) {
535 history.replaceState(null, "", "#" + searchParams.toString());
536 } else {
537 location.hash = searchParams.toString();
538 }
539 },
540 removeItem: (key2) => {
541 const searchParams = new URLSearchParams(location.hash.slice(1));
542 searchParams.delete(key2);
543 if (options == null ? void 0 : options.replaceState) {
544 history.replaceState(null, "", "#" + searchParams.toString());
545 } else {
546 location.hash = searchParams.toString();
547 }
548 }
549 }, (options == null ? void 0 : options.delayInit) && { delayInit: true }), {
550 subscribe: (key2, setValue) => {
551 const callback = () => {
552 const searchParams = new URLSearchParams(location.hash.slice(1));
553 const str = searchParams.get(key2);
554 if (str !== null) {
555 setValue(deserialize(str));
556 } else {
557 setValue(initialValue);
558 }
559 };
560 return subscribe(callback);
561 }
562 });
563 return atomWithStorage(key, initialValue, hashStorage);
564}
565
566function atomWithObservable(createObservable, options) {
567 const observableResultAtom = atom((get) => {
568 var _a;
569 let observable = createObservable(get);
570 const itself = (_a = observable[Symbol.observable]) == null ? void 0 : _a.call(observable);
571 if (itself) {
572 observable = itself;
573 }
574 const dataAtom = atom((options == null ? void 0 : options.initialValue) ? getInitialValue(options) : firstValueFrom(observable));
575 let setData = () => {
576 throw new Error("setting data without mount");
577 };
578 const dataListener = (data) => {
579 setData(data);
580 };
581 const errorListener = (error) => {
582 setData(Promise.reject(error));
583 };
584 let subscription = null;
585 dataAtom.onMount = (update) => {
586 setData = update;
587 if (!subscription) {
588 subscription = observable.subscribe(dataListener, errorListener);
589 }
590 return () => {
591 subscription == null ? void 0 : subscription.unsubscribe();
592 subscription = null;
593 };
594 };
595 return { dataAtom, observable };
596 });
597 const observableAtom = atom((get) => {
598 const { dataAtom } = get(observableResultAtom);
599 return get(dataAtom);
600 }, (get, _set, data) => {
601 const { observable } = get(observableResultAtom);
602 if ("next" in observable) {
603 observable.next(data);
604 } else {
605 throw new Error("observable is not subject");
606 }
607 });
608 return observableAtom;
609}
610function getInitialValue(options) {
611 const initialValue = options.initialValue;
612 return initialValue instanceof Function ? initialValue() : initialValue;
613}
614function firstValueFrom(source) {
615 return new Promise((resolve, reject) => {
616 let resolved = false;
617 const subscription = source.subscribe({
618 next: (value) => {
619 resolve(value);
620 resolved = true;
621 if (subscription) {
622 subscription.unsubscribe();
623 }
624 },
625 error: reject,
626 complete: () => {
627 reject();
628 }
629 });
630 if (resolved) {
631 subscription.unsubscribe();
632 }
633 });
634}
635
636const hydratedMap = /* @__PURE__ */ new WeakMap();
637function useHydrateAtoms(values, scope) {
638 const ScopeContext = SECRET_INTERNAL_getScopeContext(scope);
639 const scopeContainer = useContext(ScopeContext);
640 const store = scopeContainer.s;
641 const hydratedSet = getHydratedSet(scopeContainer);
642 const tuplesToRestore = [];
643 for (const tuple of values) {
644 const atom = tuple[0];
645 if (!hydratedSet.has(atom)) {
646 hydratedSet.add(atom);
647 tuplesToRestore.push(tuple);
648 }
649 }
650 if (tuplesToRestore.length) {
651 store[RESTORE_ATOMS](tuplesToRestore);
652 }
653}
654function getHydratedSet(scopeContainer) {
655 let hydratedSet = hydratedMap.get(scopeContainer);
656 if (!hydratedSet) {
657 hydratedSet = /* @__PURE__ */ new WeakSet();
658 hydratedMap.set(scopeContainer, hydratedSet);
659 }
660 return hydratedSet;
661}
662
663const memoizeAtom = createMemoizeAtom();
664const LOADING = { state: "loading" };
665function loadable(anAtom) {
666 return memoizeAtom(() => {
667 const loadableAtomCache = /* @__PURE__ */ new WeakMap();
668 const catchAtom = atom((get) => {
669 let promise;
670 try {
671 const data = get(anAtom);
672 const loadableAtom2 = atom({ state: "hasData", data });
673 return loadableAtom2;
674 } catch (error) {
675 if (error instanceof Promise) {
676 promise = error;
677 } else {
678 const loadableAtom2 = atom({
679 state: "hasError",
680 error
681 });
682 return loadableAtom2;
683 }
684 }
685 const cached = loadableAtomCache.get(promise);
686 if (cached) {
687 return cached;
688 }
689 const loadableAtom = atom(LOADING, async (get2, set) => {
690 try {
691 const data = await get2(anAtom, { unstable_promise: true });
692 set(loadableAtom, { state: "hasData", data });
693 } catch (error) {
694 set(loadableAtom, { state: "hasError", error });
695 }
696 });
697 loadableAtom.onMount = (init) => {
698 init();
699 };
700 loadableAtomCache.set(promise, loadableAtom);
701 return loadableAtom;
702 });
703 const derivedAtom = atom((get) => {
704 const loadableAtom = get(catchAtom);
705 return get(loadableAtom);
706 });
707 return derivedAtom;
708 }, [anAtom]);
709}
710
711export { RESET, atomFamily, atomWithDefault, atomWithHash, atomWithObservable, atomWithReducer, atomWithReset, atomWithStorage, createJSONStorage, freezeAtom, freezeAtomCreator, loadable, selectAtom, splitAtom, useAtomCallback, useHydrateAtoms, useReducerAtom, useResetAtom, waitForAll };