jotai
Version:
👻 Primitive and flexible state management for React
784 lines (767 loc) • 27.4 kB
JavaScript
System.register(['jotai/vanilla'], (function (exports) {
'use strict';
var atom;
return {
setters: [function (module) {
atom = module.atom;
}],
execute: (function () {
exports({
atomFamily: atomFamily,
atomWithDefault: atomWithDefault,
atomWithLazy: atomWithLazy,
atomWithObservable: atomWithObservable,
atomWithReducer: atomWithReducer,
atomWithRefresh: atomWithRefresh,
atomWithReset: atomWithReset,
atomWithStorage: atomWithStorage,
createJSONStorage: createJSONStorage,
freezeAtom: freezeAtom,
freezeAtomCreator: freezeAtomCreator,
loadable: loadable,
selectAtom: selectAtom,
splitAtom: splitAtom,
unstable_withStorageValidator: withStorageValidator,
unwrap: unwrap
});
const RESET = exports("RESET", Symbol(
"RESET"
));
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) {
return atom(initialValue, function(get, set, action) {
set(this, reducer(get(this), action));
});
}
function atomFamily(initializeAtom, areEqual) {
let shouldRemove = null;
const atoms = /* @__PURE__ */ new Map();
const listeners = /* @__PURE__ */ new Set();
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()]);
notifyListeners("CREATE", param, newAtom);
return newAtom;
};
const notifyListeners = (type, param, atom) => {
for (const listener of listeners) {
listener({ type, param, atom });
}
};
createAtom.unstable_listen = (callback) => {
listeners.add(callback);
return () => {
listeners.delete(callback);
};
};
createAtom.getParams = () => atoms.keys();
createAtom.remove = (param) => {
if (areEqual === void 0) {
if (!atoms.has(param)) return;
const [atom] = atoms.get(param);
atoms.delete(param);
notifyListeners("REMOVE", param, atom);
} else {
for (const [key, [atom]] of atoms) {
if (areEqual(key, param)) {
atoms.delete(key);
notifyListeners("REMOVE", key, atom);
break;
}
}
}
};
createAtom.setShouldRemove = (fn) => {
shouldRemove = fn;
if (!shouldRemove) return;
for (const [key, [atom, createdAt]] of atoms) {
if (shouldRemove(createdAt, key)) {
atoms.delete(key);
notifyListeners("REMOVE", key, atom);
}
}
};
return createAtom;
}
const getCached$2 = (c, m, k) => (m.has(k) ? m : m.set(k, c())).get(k);
const cache1$3 = /* @__PURE__ */ new WeakMap();
const memo3 = (create, dep1, dep2, dep3) => {
const cache2 = getCached$2(() => /* @__PURE__ */ new WeakMap(), cache1$3, 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);
return selectValue([value, prev]);
});
derivedAtom.init = EMPTY;
return derivedAtom;
},
anAtom,
selector,
equalityFn
);
}
const frozenAtoms = /* @__PURE__ */ new WeakSet();
const deepFreeze = (value) => {
if (typeof value !== "object" || value === null) {
return value;
}
Object.freeze(value);
const propNames = Object.getOwnPropertyNames(value);
for (const name of propNames) {
deepFreeze(value[name]);
}
return value;
};
function freezeAtom(anAtom) {
if (frozenAtoms.has(anAtom)) {
return anAtom;
}
frozenAtoms.add(anAtom);
const origRead = anAtom.read;
anAtom.read = function(get, options) {
return deepFreeze(origRead.call(this, get, options));
};
if ("write" in anAtom) {
const origWrite = anAtom.write;
anAtom.write = function(get, set, ...args) {
return origWrite.call(
this,
get,
(...setArgs) => {
if (setArgs[0] === anAtom) {
setArgs[1] = deepFreeze(setArgs[1]);
}
return set(...setArgs);
},
...args
);
};
}
return anAtom;
}
function freezeAtomCreator(createAtom) {
{
console.warn(
"[DEPRECATED] freezeAtomCreator is deprecated, define it on users end"
);
}
return (...args) => freezeAtom(createAtom(...args));
}
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;
if (!Object.is(arr2[index2], nextItem)) {
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;
});
{
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);
{
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 withStorageValidator(validator) {
return (unknownStorage) => {
const storage = {
...unknownStorage,
getItem: (key, initialValue) => {
const validate = (value2) => {
if (!validator(value2)) {
return initialValue;
}
return value2;
};
const value = unknownStorage.getItem(key, initialValue);
if (isPromiseLike(value)) {
return value.then(validate);
}
return validate(value);
}
};
return storage;
};
}
function createJSONStorage(getStringStorage = () => {
try {
return window.localStorage;
} catch (e) {
{
if (typeof window !== "undefined") {
console.warn(e);
}
}
return void 0;
}
}, options) {
var _a;
let lastStr;
let lastValue;
const storage = {
getItem: (key, initialValue) => {
var _a2, _b;
const parse = (str2) => {
str2 = str2 || "";
if (lastStr !== str2) {
try {
lastValue = JSON.parse(str2, options == null ? void 0 : options.reviver);
} catch (e) {
return initialValue;
}
lastStr = str2;
}
return lastValue;
};
const str = (_b = (_a2 = getStringStorage()) == null ? void 0 : _a2.getItem(key)) != null ? _b : null;
if (isPromiseLike(str)) {
return str.then(parse);
}
return parse(str);
},
setItem: (key, newValue) => {
var _a2;
return (_a2 = getStringStorage()) == null ? void 0 : _a2.setItem(
key,
JSON.stringify(newValue, options == null ? void 0 : options.replacer)
);
},
removeItem: (key) => {
var _a2;
return (_a2 = getStringStorage()) == null ? void 0 : _a2.removeItem(key);
}
};
const createHandleSubscribe = (subscriber2) => (key, callback, initialValue) => subscriber2(key, (v) => {
let newValue;
try {
newValue = JSON.parse(v || "");
} catch (e) {
newValue = initialValue;
}
callback(newValue);
});
let subscriber;
try {
subscriber = (_a = getStringStorage()) == null ? void 0 : _a.subscribe;
} catch (e) {
}
if (!subscriber && typeof window !== "undefined" && typeof window.addEventListener === "function" && window.Storage) {
subscriber = (key, callback) => {
if (!(getStringStorage() instanceof window.Storage)) {
return () => {
};
}
const storageEventCallback = (e) => {
if (e.storageArea === getStringStorage() && e.key === key) {
callback(e.newValue);
}
};
window.addEventListener("storage", storageEventCallback);
return () => {
window.removeEventListener("storage", storageEventCallback);
};
};
}
if (subscriber) {
storage.subscribe = createHandleSubscribe(subscriber);
}
return storage;
}
const defaultStorage = createJSONStorage();
function atomWithStorage(key, initialValue, storage = defaultStorage, options) {
const getOnInit = options == null ? void 0 : options.getOnInit;
const baseAtom = atom(
getOnInit ? storage.getItem(key, initialValue) : initialValue
);
{
baseAtom.debugPrivate = true;
}
baseAtom.onMount = (setAtom) => {
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 unsubscribe = () => {
if (subscription) {
subscription.unsubscribe();
subscription = void 0;
}
};
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(unsubscribe, options.unstable_timeout);
}
};
start();
const resultAtom = atom(lastResult || initialResult);
{
resultAtom.debugPrivate = true;
}
resultAtom.onMount = (update) => {
setResult = update;
if (lastResult) {
update(lastResult);
}
if (subscription) {
clearTimeout(timer);
} else {
start();
}
return () => {
setResult = void 0;
if (options == null ? void 0 : options.unstable_timeout) {
timer = setTimeout(unsubscribe, options.unstable_timeout);
} else {
unsubscribe();
}
};
};
return [resultAtom, observable, makePending, start, isNotMounted];
});
{
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 isPromise$1 = (x) => x instanceof Promise;
const LOADING = { state: "loading" };
function loadable(anAtom) {
return memo1(() => {
const loadableCache = /* @__PURE__ */ new WeakMap();
const refreshAtom = atom(0);
{
refreshAtom.debugPrivate = true;
}
const derivedAtom = atom(
(get, { setSelf }) => {
get(refreshAtom);
let value;
try {
value = get(anAtom);
} catch (error) {
return { state: "hasError", error };
}
if (!isPromise$1(value)) {
return { state: "hasData", data: value };
}
const promise = value;
const cached1 = loadableCache.get(promise);
if (cached1) {
return cached1;
}
promise.then(
(data) => {
loadableCache.set(promise, { state: "hasData", data });
setSelf();
},
(error) => {
loadableCache.set(promise, { state: "hasError", error });
setSelf();
}
);
const cached2 = loadableCache.get(promise);
if (cached2) {
return cached2;
}
loadableCache.set(promise, LOADING);
return LOADING;
},
(_get, set) => {
set(refreshAtom, (c) => c + 1);
}
);
{
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 isPromise = (x) => x instanceof Promise;
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);
{
refreshAtom.debugPrivate = true;
}
const promiseAndValueAtom = atom(
(get, { setSelf }) => {
get(refreshAtom);
const prev = get(promiseAndValueAtom);
const promise = get(anAtom);
if (!isPromise(promise)) {
return { v: promise };
}
if (promise !== (prev == null ? void 0 : prev.p)) {
promise.then(
(v) => {
promiseResultCache.set(promise, v);
setSelf();
},
(e) => {
promiseErrorCache.set(promise, e);
setSelf();
}
);
}
if (promiseErrorCache.has(promise)) {
throw promiseErrorCache.get(promise);
}
if (promiseResultCache.has(promise)) {
return {
p: promise,
v: promiseResultCache.get(promise)
};
}
if (prev && "v" in prev) {
return { p: promise, f: fallback(prev.v), v: prev.v };
}
return { p: promise, f: fallback() };
},
(_get, set) => {
set(refreshAtom, (c) => c + 1);
}
);
promiseAndValueAtom.init = void 0;
{
promiseAndValueAtom.debugPrivate = true;
}
return atom(
(get) => {
const state = get(promiseAndValueAtom);
if ("f" in state) {
return state.f;
}
return state.v;
},
(_get, set, ...args) => set(anAtom, ...args)
);
},
anAtom,
fallback
);
}
function atomWithRefresh(read, write) {
const refreshAtom = atom(0);
{
refreshAtom.debugPrivate = true;
}
return atom(
(get, options) => {
get(refreshAtom);
return read(get, options);
},
(get, set, ...args) => {
if (args.length === 0) {
set(refreshAtom, (c) => c + 1);
} else if (write) {
return write(get, set, ...args);
} else {
throw new Error("refresh must be called without arguments");
}
}
);
}
function atomWithLazy(makeInitial) {
const a = atom(void 0);
delete a.init;
Object.defineProperty(a, "init", {
get() {
return makeInitial();
}
});
return a;
}
})
};
}));