jotai
Version:
👻 Primitive and flexible state management for React
623 lines (609 loc) • 19.9 kB
JavaScript
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 };