UNPKG

@wordpress/interactivity

Version:

Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.

197 lines (196 loc) 6.01 kB
// packages/interactivity/src/proxies/state.ts import { batch, signal } from "@preact/signals"; import { createProxy, getProxyFromObject, getNamespaceFromProxy, shouldProxy, getObjectFromProxy } from "./registry"; import { PropSignal } from "./signals"; import { setNamespace, resetNamespace } from "../namespaces"; import { isPlainObject } from "../utils"; var wellKnownSymbols = new Set( Object.getOwnPropertyNames(Symbol).map((key) => Symbol[key]).filter((value) => typeof value === "symbol") ); var proxyToProps = /* @__PURE__ */ new WeakMap(); var hasPropSignal = (proxy, key) => proxyToProps.has(proxy) && proxyToProps.get(proxy).has(key); var getPropSignal = (proxy, key, initial) => { if (!proxyToProps.has(proxy)) { proxyToProps.set(proxy, /* @__PURE__ */ new Map()); } key = typeof key === "number" ? `${key}` : key; const props = proxyToProps.get(proxy); if (!props.has(key)) { const ns = getNamespaceFromProxy(proxy); const prop = new PropSignal(proxy); props.set(key, prop); if (initial) { const { get, value } = initial; if (get) { prop.setGetter(get); } else { prop.setValue( shouldProxy(value) ? proxifyState(ns, value) : value ); } } } return props.get(key); }; var objToIterable = /* @__PURE__ */ new WeakMap(); var peeking = false; var PENDING_GETTER = Symbol("PENDING_GETTER"); var stateHandlers = { get(target, key, receiver) { if (peeking || !target.hasOwnProperty(key) && key in target || typeof key === "symbol" && wellKnownSymbols.has(key)) { return Reflect.get(target, key, receiver); } const desc = Object.getOwnPropertyDescriptor(target, key); const prop = getPropSignal(receiver, key, desc); const result = prop.getComputed().value; if (result === PENDING_GETTER) { throw PENDING_GETTER; } if (typeof result === "function") { const ns = getNamespaceFromProxy(receiver); return (...args) => { setNamespace(ns); try { return result.call(receiver, ...args); } finally { resetNamespace(); } }; } return result; }, set(target, key, value, receiver) { setNamespace(getNamespaceFromProxy(receiver)); try { return Reflect.set(target, key, value, receiver); } finally { resetNamespace(); } }, defineProperty(target, key, desc) { const isNew = !(key in target); const result = Reflect.defineProperty(target, key, desc); if (result) { const receiver = getProxyFromObject(target); const prop = getPropSignal(receiver, key); const { get, value } = desc; if (get) { prop.setGetter(get); } else { const ns = getNamespaceFromProxy(receiver); prop.setValue( shouldProxy(value) ? proxifyState(ns, value) : value ); } if (isNew && objToIterable.has(target)) { objToIterable.get(target).value++; } if (Array.isArray(target) && proxyToProps.get(receiver)?.has("length")) { const length = getPropSignal(receiver, "length"); length.setValue(target.length); } } return result; }, deleteProperty(target, key) { const result = Reflect.deleteProperty(target, key); if (result) { const prop = getPropSignal(getProxyFromObject(target), key); prop.setValue(void 0); if (objToIterable.has(target)) { objToIterable.get(target).value++; } } return result; }, ownKeys(target) { if (!objToIterable.has(target)) { objToIterable.set(target, signal(0)); } objToIterable._ = objToIterable.get(target).value; return Reflect.ownKeys(target); } }; var proxifyState = (namespace, obj) => { return createProxy(namespace, obj, stateHandlers); }; var peek = (obj, key) => { peeking = true; try { return obj[key]; } finally { peeking = false; } }; var deepMergeRecursive = (target, source, override = true) => { if (!(isPlainObject(target) && isPlainObject(source))) { return; } let hasNewKeys = false; for (const key in source) { const isNew = !(key in target); hasNewKeys = hasNewKeys || isNew; const desc = Object.getOwnPropertyDescriptor(source, key); const proxy = getProxyFromObject(target); const propSignal = !!proxy && hasPropSignal(proxy, key) && getPropSignal(proxy, key); if (typeof desc.get === "function" || typeof desc.set === "function") { if (override || isNew) { Object.defineProperty(target, key, { ...desc, configurable: true, enumerable: true }); if (desc.get && propSignal) { propSignal.setPendingGetter(desc.get); } } } else if (isPlainObject(source[key])) { const targetValue = Object.getOwnPropertyDescriptor(target, key)?.value; if (isNew || override && !isPlainObject(targetValue)) { target[key] = {}; if (propSignal) { const ns = getNamespaceFromProxy(proxy); propSignal.setValue( proxifyState(ns, target[key]) ); } deepMergeRecursive(target[key], source[key], override); } else if (isPlainObject(targetValue)) { deepMergeRecursive(target[key], source[key], override); } } else if (override || isNew) { Object.defineProperty(target, key, desc); if (propSignal) { const { value } = desc; const ns = getNamespaceFromProxy(proxy); propSignal.setValue( shouldProxy(value) ? proxifyState(ns, value) : value ); } } } if (hasNewKeys && objToIterable.has(target)) { objToIterable.get(target).value++; } }; var deepMerge = (target, source, override = true) => batch( () => deepMergeRecursive( getObjectFromProxy(target) || target, source, override ) ); export { PENDING_GETTER, deepMerge, hasPropSignal, peek, proxifyState }; //# sourceMappingURL=state.js.map