UNPKG

@zag-js/store

Version:

The reactive store package for zag machines

314 lines (307 loc) • 10.9 kB
'use strict'; var proxyCompare = require('proxy-compare'); // src/global.ts function glob() { if (typeof globalThis !== "undefined") return globalThis; if (typeof self !== "undefined") return self; if (typeof window !== "undefined") return window; if (typeof global !== "undefined") return global; } function globalRef(key, value) { const g = glob(); if (!g) return value(); g[key] || (g[key] = value()); return g[key]; } var refSet = globalRef("__zag__refSet", () => /* @__PURE__ */ new WeakSet()); // src/utils.ts var isReactElement = (x) => typeof x === "object" && x !== null && "$$typeof" in x && "props" in x; var isVueElement = (x) => typeof x === "object" && x !== null && "__v_isVNode" in x; var isDOMElement = (x) => typeof x === "object" && x !== null && "nodeType" in x && typeof x.nodeName === "string"; var isElement = (x) => isReactElement(x) || isVueElement(x) || isDOMElement(x); var isObject = (x) => x !== null && typeof x === "object"; var canProxy = (x) => isObject(x) && !refSet.has(x) && (Array.isArray(x) || !(Symbol.iterator in x)) && !isElement(x) && !(x instanceof WeakMap) && !(x instanceof WeakSet) && !(x instanceof Error) && !(x instanceof Number) && !(x instanceof Date) && !(x instanceof String) && !(x instanceof RegExp) && !(x instanceof ArrayBuffer) && !(x instanceof Promise); var isDev = () => process.env.NODE_ENV !== "production"; // src/clone.ts function set(obj, key, val) { if (typeof val.value === "object" && !canProxy(val.value)) val.value = clone(val.value); if (!val.enumerable || val.get || val.set || !val.configurable || !val.writable || key === "__proto__") { Object.defineProperty(obj, key, val); } else obj[key] = val.value; } function clone(x) { if (typeof x !== "object") return x; var i = 0, k, list, tmp, str = Object.prototype.toString.call(x); if (str === "[object Object]") { tmp = Object.create(Object.getPrototypeOf(x) || null); } else if (str === "[object Array]") { tmp = Array(x.length); } else if (str === "[object Set]") { tmp = /* @__PURE__ */ new Set(); x.forEach(function(val) { tmp.add(clone(val)); }); } else if (str === "[object Map]") { tmp = /* @__PURE__ */ new Map(); x.forEach(function(val, key) { tmp.set(clone(key), clone(val)); }); } else if (str === "[object Date]") { tmp = /* @__PURE__ */ new Date(+x); } else if (str === "[object RegExp]") { tmp = new RegExp(x.source, x.flags); } else if (str === "[object DataView]") { tmp = new x.constructor(clone(x.buffer)); } else if (str === "[object ArrayBuffer]") { tmp = x.slice(0); } else if (str === "[object Blob]") { tmp = x.slice(); } else if (str.slice(-6) === "Array]") { tmp = new x.constructor(x); } if (tmp) { for (list = Object.getOwnPropertySymbols(x); i < list.length; i++) { set(tmp, list[i], Object.getOwnPropertyDescriptor(x, list[i])); } for (i = 0, list = Object.getOwnPropertyNames(x); i < list.length; i++) { if (Object.hasOwnProperty.call(tmp, k = list[i]) && tmp[k] === x[k]) continue; set(tmp, k, Object.getOwnPropertyDescriptor(x, k)); } } return tmp || x; } var proxyStateMap = globalRef("__zag__proxyStateMap", () => /* @__PURE__ */ new WeakMap()); var buildProxyFunction = (objectIs = Object.is, newProxy = (target, handler) => new Proxy(target, handler), snapCache = /* @__PURE__ */ new WeakMap(), createSnapshot = (target, version) => { const cache = snapCache.get(target); if (cache?.[0] === version) { return cache[1]; } const snap = Array.isArray(target) ? [] : Object.create(Object.getPrototypeOf(target)); proxyCompare.markToTrack(snap, true); snapCache.set(target, [version, snap]); Reflect.ownKeys(target).forEach((key) => { const value = Reflect.get(target, key); if (refSet.has(value)) { proxyCompare.markToTrack(value, false); snap[key] = value; } else if (proxyStateMap.has(value)) { snap[key] = snapshot(value); } else { snap[key] = value; } }); return Object.freeze(snap); }, proxyCache = /* @__PURE__ */ new WeakMap(), versionHolder = [1, 1], proxyFunction2 = (initialObject) => { if (!isObject(initialObject)) { throw new Error("object required"); } const found = proxyCache.get(initialObject); if (found) { return found; } let version = versionHolder[0]; const listeners = /* @__PURE__ */ new Set(); const notifyUpdate = (op, nextVersion = ++versionHolder[0]) => { if (version !== nextVersion) { version = nextVersion; listeners.forEach((listener) => listener(op, nextVersion)); } }; let checkVersion = versionHolder[1]; const ensureVersion = (nextCheckVersion = ++versionHolder[1]) => { if (checkVersion !== nextCheckVersion && !listeners.size) { checkVersion = nextCheckVersion; propProxyStates.forEach(([propProxyState]) => { const propVersion = propProxyState[1](nextCheckVersion); if (propVersion > version) { version = propVersion; } }); } return version; }; const createPropListener = (prop) => (op, nextVersion) => { const newOp = [...op]; newOp[1] = [prop, ...newOp[1]]; notifyUpdate(newOp, nextVersion); }; const propProxyStates = /* @__PURE__ */ new Map(); const addPropListener = (prop, propProxyState) => { if (isDev() && propProxyStates.has(prop)) { throw new Error("prop listener already exists"); } if (listeners.size) { const remove = propProxyState[3](createPropListener(prop)); propProxyStates.set(prop, [propProxyState, remove]); } else { propProxyStates.set(prop, [propProxyState]); } }; const removePropListener = (prop) => { const entry = propProxyStates.get(prop); if (entry) { propProxyStates.delete(prop); entry[1]?.(); } }; const addListener = (listener) => { listeners.add(listener); if (listeners.size === 1) { propProxyStates.forEach(([propProxyState, prevRemove], prop) => { if (isDev() && prevRemove) { throw new Error("remove already exists"); } const remove = propProxyState[3](createPropListener(prop)); propProxyStates.set(prop, [propProxyState, remove]); }); } const removeListener = () => { listeners.delete(listener); if (listeners.size === 0) { propProxyStates.forEach(([propProxyState, remove], prop) => { if (remove) { remove(); propProxyStates.set(prop, [propProxyState]); } }); } }; return removeListener; }; const baseObject = Array.isArray(initialObject) ? [] : Object.create(Object.getPrototypeOf(initialObject)); const handler = { deleteProperty(target, prop) { const prevValue = Reflect.get(target, prop); removePropListener(prop); const deleted = Reflect.deleteProperty(target, prop); if (deleted) { notifyUpdate(["delete", [prop], prevValue]); } return deleted; }, set(target, prop, value, receiver) { const hasPrevValue = Reflect.has(target, prop); const prevValue = Reflect.get(target, prop, receiver); if (hasPrevValue && (objectIs(prevValue, value) || proxyCache.has(value) && objectIs(prevValue, proxyCache.get(value)))) { return true; } removePropListener(prop); if (isObject(value)) { value = proxyCompare.getUntracked(value) || value; } let nextValue = value; if (Object.getOwnPropertyDescriptor(target, prop)?.set) ; else { if (!proxyStateMap.has(value) && canProxy(value)) { nextValue = proxy(value); } const childProxyState = !refSet.has(nextValue) && proxyStateMap.get(nextValue); if (childProxyState) { addPropListener(prop, childProxyState); } } Reflect.set(target, prop, nextValue, receiver); notifyUpdate(["set", [prop], value, prevValue]); return true; } }; const proxyObject = newProxy(baseObject, handler); proxyCache.set(initialObject, proxyObject); const proxyState = [baseObject, ensureVersion, createSnapshot, addListener]; proxyStateMap.set(proxyObject, proxyState); Reflect.ownKeys(initialObject).forEach((key) => { const desc = Object.getOwnPropertyDescriptor(initialObject, key); if (desc.get || desc.set) { Object.defineProperty(baseObject, key, desc); } else { proxyObject[key] = initialObject[key]; } }); return proxyObject; }) => [ // public functions proxyFunction2, // shared state proxyStateMap, refSet, // internal things objectIs, newProxy, canProxy, snapCache, createSnapshot, proxyCache, versionHolder ]; var [proxyFunction] = buildProxyFunction(); function proxy(initialObject = {}) { return proxyFunction(initialObject); } function subscribe(proxyObject, callback, notifyInSync) { const proxyState = proxyStateMap.get(proxyObject); if (isDev() && !proxyState) { console.warn("Please use proxy object"); } let promise; const ops = []; const addListener = proxyState[3]; let isListenerActive = false; const listener = (op) => { ops.push(op); if (notifyInSync) { callback(ops.splice(0)); return; } if (!promise) { promise = Promise.resolve().then(() => { promise = void 0; if (isListenerActive) { callback(ops.splice(0)); } }); } }; const removeListener = addListener(listener); isListenerActive = true; return () => { isListenerActive = false; removeListener(); }; } function snapshot(proxyObject) { const proxyState = proxyStateMap.get(proxyObject); if (isDev() && !proxyState) { console.warn("Please use proxy object"); } const [target, ensureVersion, createSnapshot] = proxyState; return createSnapshot(target, ensureVersion()); } function ref(obj) { refSet.add(obj); return obj; } // src/proxy-computed.ts function proxyWithComputed(initialObject, computedFns) { const keys = Object.keys(computedFns); keys.forEach((key) => { if (Object.getOwnPropertyDescriptor(initialObject, key)) { throw new Error("object property already defined"); } const computedFn = computedFns[key]; const { get, set: set2 } = typeof computedFn === "function" ? { get: computedFn } : computedFn; const desc = {}; desc.get = () => get(snapshot(proxyObject)); if (set2) { desc.set = (newValue) => set2(proxyObject, newValue); } Object.defineProperty(initialObject, key, desc); }); const proxyObject = proxy(initialObject); return proxyObject; } exports.clone = clone; exports.globalRef = globalRef; exports.proxy = proxy; exports.proxyWithComputed = proxyWithComputed; exports.ref = ref; exports.snapshot = snapshot; exports.subscribe = subscribe;