@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
JavaScript
// 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