@ariakit/core
Version:
Ariakit core
234 lines (226 loc) • 7.04 kB
JavaScript
"use client";
import {
applyState,
chain,
getKeys,
hasOwnProperty,
invariant,
noop,
omit,
pick
} from "./PBFD2E7P.js";
import {
__spreadProps,
__spreadValues
} from "./3YLGPPWQ.js";
// src/utils/store.ts
function getInternal(store, key) {
const internals = store.__unstableInternals;
invariant(internals, "Invalid store");
return internals[key];
}
function createStore(initialState, ...stores) {
let state = initialState;
let prevStateBatch = state;
let lastUpdate = Symbol();
let destroy = noop;
const instances = /* @__PURE__ */ new Set();
const updatedKeys = /* @__PURE__ */ new Set();
const setups = /* @__PURE__ */ new Set();
const listeners = /* @__PURE__ */ new Set();
const batchListeners = /* @__PURE__ */ new Set();
const disposables = /* @__PURE__ */ new WeakMap();
const listenerKeys = /* @__PURE__ */ new WeakMap();
const storeSetup = (callback) => {
setups.add(callback);
return () => setups.delete(callback);
};
const storeInit = () => {
const initialized = instances.size;
const instance = Symbol();
instances.add(instance);
const maybeDestroy = () => {
instances.delete(instance);
if (instances.size) return;
destroy();
};
if (initialized) return maybeDestroy;
const desyncs = getKeys(state).map(
(key) => chain(
...stores.map((store) => {
var _a;
const storeState = (_a = store == null ? void 0 : store.getState) == null ? void 0 : _a.call(store);
if (!storeState) return;
if (!hasOwnProperty(storeState, key)) return;
return sync(store, [key], (state2) => {
setState(
key,
state2[key],
// @ts-expect-error - Not public API. This is just to prevent
// infinite loops.
true
);
});
})
)
);
const teardowns = [];
for (const setup2 of setups) {
teardowns.push(setup2());
}
const cleanups = stores.map(init);
destroy = chain(...desyncs, ...teardowns, ...cleanups);
return maybeDestroy;
};
const sub = (keys, listener, set = listeners) => {
set.add(listener);
listenerKeys.set(listener, keys);
return () => {
var _a;
(_a = disposables.get(listener)) == null ? void 0 : _a();
disposables.delete(listener);
listenerKeys.delete(listener);
set.delete(listener);
};
};
const storeSubscribe = (keys, listener) => sub(keys, listener);
const storeSync = (keys, listener) => {
disposables.set(listener, listener(state, state));
return sub(keys, listener);
};
const storeBatch = (keys, listener) => {
disposables.set(listener, listener(state, prevStateBatch));
return sub(keys, listener, batchListeners);
};
const storePick = (keys) => createStore(pick(state, keys), finalStore);
const storeOmit = (keys) => createStore(omit(state, keys), finalStore);
const getState = () => state;
const setState = (key, value, fromStores = false) => {
var _a;
if (!hasOwnProperty(state, key)) return;
const nextValue = applyState(value, state[key]);
if (nextValue === state[key]) return;
if (!fromStores) {
for (const store of stores) {
(_a = store == null ? void 0 : store.setState) == null ? void 0 : _a.call(store, key, nextValue);
}
}
const prevState = state;
state = __spreadProps(__spreadValues({}, state), { [key]: nextValue });
const thisUpdate = Symbol();
lastUpdate = thisUpdate;
updatedKeys.add(key);
const run = (listener, prev, uKeys) => {
var _a2;
const keys = listenerKeys.get(listener);
const updated = (k) => uKeys ? uKeys.has(k) : k === key;
if (!keys || keys.some(updated)) {
(_a2 = disposables.get(listener)) == null ? void 0 : _a2();
disposables.set(listener, listener(state, prev));
}
};
for (const listener of listeners) {
run(listener, prevState);
}
queueMicrotask(() => {
if (lastUpdate !== thisUpdate) return;
const snapshot = state;
for (const listener of batchListeners) {
run(listener, prevStateBatch, updatedKeys);
}
prevStateBatch = snapshot;
updatedKeys.clear();
});
};
const finalStore = {
getState,
setState,
__unstableInternals: {
setup: storeSetup,
init: storeInit,
subscribe: storeSubscribe,
sync: storeSync,
batch: storeBatch,
pick: storePick,
omit: storeOmit
}
};
return finalStore;
}
function setup(store, ...args) {
if (!store) return;
return getInternal(store, "setup")(...args);
}
function init(store, ...args) {
if (!store) return;
return getInternal(store, "init")(...args);
}
function subscribe(store, ...args) {
if (!store) return;
return getInternal(store, "subscribe")(...args);
}
function sync(store, ...args) {
if (!store) return;
return getInternal(store, "sync")(...args);
}
function batch(store, ...args) {
if (!store) return;
return getInternal(store, "batch")(...args);
}
function omit2(store, ...args) {
if (!store) return;
return getInternal(store, "omit")(...args);
}
function pick2(store, ...args) {
if (!store) return;
return getInternal(store, "pick")(...args);
}
function mergeStore(...stores) {
const initialState = stores.reduce((state, store2) => {
var _a;
const nextState = (_a = store2 == null ? void 0 : store2.getState) == null ? void 0 : _a.call(store2);
if (!nextState) return state;
return Object.assign(state, nextState);
}, {});
const store = createStore(initialState, ...stores);
return Object.assign({}, ...stores, store);
}
function throwOnConflictingProps(props, store) {
if (process.env.NODE_ENV === "production") return;
if (!store) return;
const defaultKeys = Object.entries(props).filter(([key, value]) => key.startsWith("default") && value !== void 0).map(([key]) => {
var _a;
const stateKey = key.replace("default", "");
return `${((_a = stateKey[0]) == null ? void 0 : _a.toLowerCase()) || ""}${stateKey.slice(1)}`;
});
if (!defaultKeys.length) return;
const storeState = store.getState();
const conflictingProps = defaultKeys.filter(
(key) => hasOwnProperty(storeState, key)
);
if (!conflictingProps.length) return;
throw new Error(
`Passing a store prop in conjunction with a default state is not supported.
const store = useSelectStore();
<SelectProvider store={store} defaultValue="Apple" />
^ ^
Instead, pass the default state to the topmost store:
const store = useSelectStore({ defaultValue: "Apple" });
<SelectProvider store={store} />
See https://github.com/ariakit/ariakit/pull/2745 for more details.
If there's a particular need for this, please submit a feature request at https://github.com/ariakit/ariakit
`
);
}
export {
createStore,
setup,
init,
subscribe,
sync,
batch,
omit2 as omit,
pick2 as pick,
mergeStore,
throwOnConflictingProps
};