UNPKG

jotai

Version:

👻 Primitive and flexible state management for React

623 lines (609 loc) • 19.9 kB
import { atom } from 'jotai/vanilla'; const RESET = Symbol(); function atomWithReset(initialValue) { const anAtom = atom( initialValue, (get, set, update) => { const nextValue = typeof update === "function" ? update(get(anAtom)) : update; set(anAtom, nextValue === RESET ? initialValue : nextValue); } ); return anAtom; } function atomWithReducer(initialValue, reducer) { const anAtom = atom( initialValue, (get, set, action) => set(anAtom, reducer(get(anAtom), action)) ); return anAtom; } function atomFamily(initializeAtom, areEqual) { let shouldRemove = null; const atoms = /* @__PURE__ */ new Map(); const createAtom = (param) => { let item; if (areEqual === void 0) { item = atoms.get(param); } else { for (const [key, value] of atoms) { if (areEqual(key, param)) { item = value; break; } } } if (item !== void 0) { if (shouldRemove == null ? void 0 : shouldRemove(item[1], param)) { createAtom.remove(param); } else { return item[0]; } } const newAtom = initializeAtom(param); atoms.set(param, [newAtom, Date.now()]); return newAtom; }; createAtom.remove = (param) => { if (areEqual === void 0) { atoms.delete(param); } else { for (const [key] of atoms) { if (areEqual(key, param)) { atoms.delete(key); break; } } } }; createAtom.setShouldRemove = (fn) => { shouldRemove = fn; if (!shouldRemove) return; for (const [key, value] of atoms) { if (shouldRemove(value[1], key)) { atoms.delete(key); } } }; return createAtom; } const getCached$2 = (c, m, k) => (m.has(k) ? m : m.set(k, c())).get(k); const cache1$4 = /* @__PURE__ */ new WeakMap(); const memo3 = (create, dep1, dep2, dep3) => { const cache2 = getCached$2(() => /* @__PURE__ */ new WeakMap(), cache1$4, dep1); const cache3 = getCached$2(() => /* @__PURE__ */ new WeakMap(), cache2, dep2); return getCached$2(create, cache3, dep3); }; function selectAtom(anAtom, selector, equalityFn = Object.is) { return memo3( () => { const EMPTY = Symbol(); const selectValue = ([value, prevSlice]) => { if (prevSlice === EMPTY) { return selector(value); } const slice = selector(value, prevSlice); return equalityFn(prevSlice, slice) ? prevSlice : slice; }; const derivedAtom = atom((get) => { const prev = get(derivedAtom); const value = get(anAtom); if (value instanceof Promise || prev instanceof Promise) { return Promise.all([value, prev]).then(selectValue); } return selectValue([value, prev]); }); derivedAtom.init = EMPTY; return derivedAtom; }, anAtom, selector, equalityFn ); } const cache1$3 = /* @__PURE__ */ new WeakMap(); const memo1$1 = (create, dep1) => (cache1$3.has(dep1) ? cache1$3 : cache1$3.set(dep1, create())).get(dep1); const deepFreeze = (obj) => { if (typeof obj !== "object" || obj === null) return; Object.freeze(obj); const propNames = Object.getOwnPropertyNames(obj); for (const name of propNames) { const value = obj[name]; deepFreeze(value); } return obj; }; function freezeAtom(anAtom) { return memo1$1(() => { const frozenAtom = atom( (get) => deepFreeze(get(anAtom)), (_get, set, arg) => set(anAtom, arg) ); return frozenAtom; }, anAtom); } function freezeAtomCreator(createAtom) { return (...params) => { const anAtom = createAtom(...params); const origRead = anAtom.read; anAtom.read = (get, options) => deepFreeze(origRead(get, options)); return anAtom; }; } const getCached$1 = (c, m, k) => (m.has(k) ? m : m.set(k, c())).get(k); const cache1$2 = /* @__PURE__ */ new WeakMap(); const memo2$1 = (create, dep1, dep2) => { const cache2 = getCached$1(() => /* @__PURE__ */ new WeakMap(), cache1$2, dep1); return getCached$1(create, cache2, dep2); }; const cacheKeyForEmptyKeyExtractor = {}; const isWritable = (atom2) => !!atom2.write; const isFunction = (x) => typeof x === "function"; function splitAtom(arrAtom, keyExtractor) { return memo2$1( () => { const mappingCache = /* @__PURE__ */ new WeakMap(); const getMapping = (arr, prev) => { let mapping = mappingCache.get(arr); if (mapping) { return mapping; } const prevMapping = prev && mappingCache.get(prev); const atomList = []; const keyList = []; arr.forEach((item, index) => { const key = keyExtractor ? keyExtractor(item) : index; keyList[index] = key; const cachedAtom = prevMapping && prevMapping.atomList[prevMapping.keyList.indexOf(key)]; if (cachedAtom) { atomList[index] = cachedAtom; return; } const read = (get) => { const prev2 = get(mappingAtom); const currArr = get(arrAtom); const mapping2 = getMapping(currArr, prev2 == null ? void 0 : prev2.arr); const index2 = mapping2.keyList.indexOf(key); if (index2 < 0 || index2 >= currArr.length) { const prevItem = arr[getMapping(arr).keyList.indexOf(key)]; if (prevItem) { return prevItem; } throw new Error("splitAtom: index out of bounds for read"); } return currArr[index2]; }; const write = (get, set, update) => { const prev2 = get(mappingAtom); const arr2 = get(arrAtom); const mapping2 = getMapping(arr2, prev2 == null ? void 0 : prev2.arr); const index2 = mapping2.keyList.indexOf(key); if (index2 < 0 || index2 >= arr2.length) { throw new Error("splitAtom: index out of bounds for write"); } const nextItem = isFunction(update) ? update(arr2[index2]) : update; set(arrAtom, [ ...arr2.slice(0, index2), nextItem, ...arr2.slice(index2 + 1) ]); }; atomList[index] = isWritable(arrAtom) ? atom(read, write) : atom(read); }); if (prevMapping && prevMapping.keyList.length === keyList.length && prevMapping.keyList.every((x, i) => x === keyList[i])) { mapping = prevMapping; } else { mapping = { arr, atomList, keyList }; } mappingCache.set(arr, mapping); return mapping; }; const mappingAtom = atom((get) => { const prev = get(mappingAtom); const arr = get(arrAtom); const mapping = getMapping(arr, prev == null ? void 0 : prev.arr); return mapping; }); if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { mappingAtom.debugPrivate = true; } mappingAtom.init = void 0; const splittedAtom = isWritable(arrAtom) ? atom( (get) => get(mappingAtom).atomList, (get, set, action) => { switch (action.type) { case "remove": { const index = get(splittedAtom).indexOf(action.atom); if (index >= 0) { const arr = get(arrAtom); set(arrAtom, [ ...arr.slice(0, index), ...arr.slice(index + 1) ]); } break; } case "insert": { const index = action.before ? get(splittedAtom).indexOf(action.before) : get(splittedAtom).length; if (index >= 0) { const arr = get(arrAtom); set(arrAtom, [ ...arr.slice(0, index), action.value, ...arr.slice(index) ]); } break; } case "move": { const index1 = get(splittedAtom).indexOf(action.atom); const index2 = action.before ? get(splittedAtom).indexOf(action.before) : get(splittedAtom).length; if (index1 >= 0 && index2 >= 0) { const arr = get(arrAtom); if (index1 < index2) { set(arrAtom, [ ...arr.slice(0, index1), ...arr.slice(index1 + 1, index2), arr[index1], ...arr.slice(index2) ]); } else { set(arrAtom, [ ...arr.slice(0, index2), arr[index1], ...arr.slice(index2, index1), ...arr.slice(index1 + 1) ]); } } break; } } } ) : atom((get) => get(mappingAtom).atomList); return splittedAtom; }, arrAtom, keyExtractor || cacheKeyForEmptyKeyExtractor ); } function atomWithDefault(getDefault) { const EMPTY = Symbol(); const overwrittenAtom = atom(EMPTY); if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { overwrittenAtom.debugPrivate = true; } const anAtom = atom( (get, options) => { const overwritten = get(overwrittenAtom); if (overwritten !== EMPTY) { return overwritten; } return getDefault(get, options); }, (get, set, update) => { if (update === RESET) { set(overwrittenAtom, EMPTY); } else if (typeof update === "function") { const prevValue = get(anAtom); set(overwrittenAtom, update(prevValue)); } else { set(overwrittenAtom, update); } } ); return anAtom; } const isPromiseLike = (x) => typeof (x == null ? void 0 : x.then) === "function"; function createJSONStorage(getStringStorage) { let lastStr; let lastValue; const storage = { getItem: (key, initialValue) => { var _a, _b; const parse = (str2) => { str2 = str2 || ""; if (lastStr !== str2) { try { lastValue = JSON.parse(str2); } catch { return initialValue; } lastStr = str2; } return lastValue; }; const str = (_b = (_a = getStringStorage()) == null ? void 0 : _a.getItem(key)) != null ? _b : null; if (isPromiseLike(str)) { return str.then(parse); } return parse(str); }, setItem: (key, newValue) => { var _a; return (_a = getStringStorage()) == null ? void 0 : _a.setItem(key, JSON.stringify(newValue)); }, removeItem: (key) => { var _a; return (_a = getStringStorage()) == null ? void 0 : _a.removeItem(key); } }; if (typeof window !== "undefined" && typeof window.addEventListener === "function") { storage.subscribe = (key, callback, initialValue) => { if (!(getStringStorage() instanceof window.Storage)) { return () => { }; } const storageEventCallback = (e) => { if (e.storageArea === getStringStorage() && e.key === key) { let newValue; try { newValue = JSON.parse(e.newValue || ""); } catch { newValue = initialValue; } callback(newValue); } }; window.addEventListener("storage", storageEventCallback); return () => { window.removeEventListener("storage", storageEventCallback); }; }; } return storage; } const defaultStorage = createJSONStorage( () => typeof window !== "undefined" ? window.localStorage : void 0 ); function atomWithStorage(key, initialValue, storage = defaultStorage, unstable_options) { const getOnInit = unstable_options == null ? void 0 : unstable_options.unstable_getOnInit; const baseAtom = atom( getOnInit ? storage.getItem(key, initialValue) : initialValue ); if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { baseAtom.debugPrivate = true; } baseAtom.onMount = (setAtom) => { if (!getOnInit) { setAtom(storage.getItem(key, initialValue)); } let unsub; if (storage.subscribe) { unsub = storage.subscribe(key, setAtom, initialValue); } return unsub; }; const anAtom = atom( (get) => get(baseAtom), (get, set, update) => { const nextValue = typeof update === "function" ? update(get(baseAtom)) : update; if (nextValue === RESET) { set(baseAtom, initialValue); return storage.removeItem(key); } if (nextValue instanceof Promise) { return nextValue.then((resolvedValue) => { set(baseAtom, resolvedValue); return storage.setItem(key, resolvedValue); }); } set(baseAtom, nextValue); return storage.setItem(key, nextValue); } ); return anAtom; } function atomWithObservable(getObservable, options) { const returnResultData = (result) => { if ("e" in result) { throw result.e; } return result.d; }; const observableResultAtom = atom((get) => { var _a; let observable = getObservable(get); const itself = (_a = observable[Symbol.observable]) == null ? void 0 : _a.call(observable); if (itself) { observable = itself; } let resolve; const makePending = () => new Promise((r) => { resolve = r; }); const initialResult = options && "initialValue" in options ? { d: typeof options.initialValue === "function" ? options.initialValue() : options.initialValue } : makePending(); let setResult; let lastResult; const listener = (result) => { lastResult = result; resolve == null ? void 0 : resolve(result); setResult == null ? void 0 : setResult(result); }; let subscription; let timer; const isNotMounted = () => !setResult; const start = () => { if (subscription) { clearTimeout(timer); subscription.unsubscribe(); } subscription = observable.subscribe({ next: (d) => listener({ d }), error: (e) => listener({ e }), complete: () => { } }); if (isNotMounted() && (options == null ? void 0 : options.unstable_timeout)) { timer = setTimeout(() => { if (subscription) { subscription.unsubscribe(); subscription = void 0; } }, options.unstable_timeout); } }; start(); const resultAtom = atom(lastResult || initialResult); if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { resultAtom.debugPrivate = true; } resultAtom.onMount = (update) => { setResult = update; if (lastResult) { update(lastResult); } if (subscription) { clearTimeout(timer); } else { start(); } return () => { setResult = void 0; if (subscription) { subscription.unsubscribe(); subscription = void 0; } }; }; return [resultAtom, observable, makePending, start, isNotMounted]; }); if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { observableResultAtom.debugPrivate = true; } const observableAtom = atom( (get) => { const [resultAtom] = get(observableResultAtom); const result = get(resultAtom); if (result instanceof Promise) { return result.then(returnResultData); } return returnResultData(result); }, (get, set, data) => { const [resultAtom, observable, makePending, start, isNotMounted] = get(observableResultAtom); if ("next" in observable) { if (isNotMounted()) { set(resultAtom, makePending()); start(); } observable.next(data); } else { throw new Error("observable is not subject"); } } ); return observableAtom; } const cache1$1 = /* @__PURE__ */ new WeakMap(); const memo1 = (create, dep1) => (cache1$1.has(dep1) ? cache1$1 : cache1$1.set(dep1, create())).get(dep1); const LOADING = { state: "loading" }; function loadable(anAtom) { return memo1(() => { const loadableCache = /* @__PURE__ */ new WeakMap(); const refreshAtom = atom(0); if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { refreshAtom.debugPrivate = true; } const derivedAtom = atom( (get, { setSelf }) => { get(refreshAtom); const promise = get(anAtom); if (!(promise instanceof Promise)) { return { state: "hasData", data: promise }; } const cached = loadableCache.get(promise); if (cached) { return cached; } loadableCache.set(promise, LOADING); promise.then( (data) => { loadableCache.set(promise, { state: "hasData", data }); }, (error) => { loadableCache.set(promise, { state: "hasError", error }); } ).finally(setSelf); return LOADING; }, (_get, set) => { set(refreshAtom, (c) => c + 1); } ); if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { derivedAtom.debugPrivate = true; } return atom((get) => get(derivedAtom)); }, anAtom); } const getCached = (c, m, k) => (m.has(k) ? m : m.set(k, c())).get(k); const cache1 = /* @__PURE__ */ new WeakMap(); const memo2 = (create, dep1, dep2) => { const cache2 = getCached(() => /* @__PURE__ */ new WeakMap(), cache1, dep1); return getCached(create, cache2, dep2); }; const defaultFallback = () => void 0; function unwrap(anAtom, fallback = defaultFallback) { return memo2( () => { const promiseErrorCache = /* @__PURE__ */ new WeakMap(); const promiseResultCache = /* @__PURE__ */ new WeakMap(); const refreshAtom = atom(0); if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { refreshAtom.debugPrivate = true; } const promiseAndValueAtom = atom( (get, { setSelf }) => { get(refreshAtom); const prev = get(promiseAndValueAtom); const promise = get(anAtom); if (!(promise instanceof Promise)) { return { v: promise }; } if (promise === (prev == null ? void 0 : prev.p)) { if (promiseErrorCache.has(promise)) { throw promiseErrorCache.get(promise); } if (promiseResultCache.has(promise)) { return { p: promise, v: promiseResultCache.get(promise) }; } } if (promise !== (prev == null ? void 0 : prev.p)) { promise.then( (v) => promiseResultCache.set(promise, v), (e) => promiseErrorCache.set(promise, e) ).finally(setSelf); } if (prev && "v" in prev) { return { p: promise, f: fallback(prev.v) }; } return { p: promise, f: fallback() }; }, (_get, set) => { set(refreshAtom, (c) => c + 1); } ); promiseAndValueAtom.init = void 0; if ((import.meta.env ? import.meta.env.MODE : void 0) !== "production") { promiseAndValueAtom.debugPrivate = true; } return atom((get) => { const state = get(promiseAndValueAtom); if ("v" in state) { return state.v; } return state.f; }, anAtom.write); }, anAtom, fallback ); } export { RESET, atomFamily, atomWithDefault, atomWithObservable, atomWithReducer, atomWithReset, atomWithStorage, createJSONStorage, freezeAtom, freezeAtomCreator, loadable, selectAtom, splitAtom, unwrap as unstable_unwrap };