UNPKG

@wordpress/interactivity

Version:

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

143 lines (142 loc) 3.97 kB
// packages/interactivity/src/proxies/signals.ts import { computed, signal, batch } from "@preact/signals"; import { getNamespaceFromProxy } from "./registry"; import { getScope } from "../scopes"; import { setNamespace, resetNamespace } from "../namespaces"; import { withScope } from "../utils"; var NO_SCOPE = {}; var PropSignal = class { /** * Proxy that holds the property this PropSignal is associated with. */ owner; /** * Relation of computeds by scope. These computeds are read-only signals * that depend on whether the property is a value or a getter and, * therefore, can return different values depending on the scope in which * the getter is accessed. */ computedsByScope; /** * Signal with the value assigned to the related property. */ valueSignal; /** * Signal with the getter assigned to the related property. */ getterSignal; /** * Pending getter to be consolidated. */ pendingGetter; /** * Structure that manages reactivity for a property in a state object, using * signals to keep track of property value or getter modifications. * * @param owner Proxy that holds the property this instance is associated * with. */ constructor(owner) { this.owner = owner; this.computedsByScope = /* @__PURE__ */ new WeakMap(); } /** * Changes the internal value. If a getter was set before, it is set to * `undefined`. * * @param value New value. */ setValue(value) { this.update({ value }); } /** * Changes the internal getter. If a value was set before, it is set to * `undefined`. * * @param getter New getter. */ setGetter(getter) { this.update({ get: getter }); } /** * Changes the internal getter asynchronously. * * The update is made in a microtask, which prevents issues with getters * accessing the state, and ensures the update occurs before any render. * * @param getter New getter. */ setPendingGetter(getter) { this.pendingGetter = getter; queueMicrotask(() => this.consolidateGetter()); } /** * Consolidate the pending value of the getter. */ consolidateGetter() { const getter = this.pendingGetter; if (getter) { this.pendingGetter = void 0; this.update({ get: getter }); } } /** * Returns the computed that holds the result of evaluating the prop in the * current scope. * * These computeds are read-only signals that depend on whether the property * is a value or a getter and, therefore, can return different values * depending on the scope in which the getter is accessed. * * @return Computed that depends on the scope. */ getComputed() { const scope = getScope() || NO_SCOPE; if (!this.valueSignal && !this.getterSignal) { this.update({}); } if (this.pendingGetter) { this.consolidateGetter(); } if (!this.computedsByScope.has(scope)) { const callback = () => { const getter = this.getterSignal?.value; return getter ? getter.call(this.owner) : this.valueSignal?.value; }; setNamespace(getNamespaceFromProxy(this.owner)); this.computedsByScope.set( scope, computed(withScope(callback)) ); resetNamespace(); } return this.computedsByScope.get(scope); } /** * Updates the internal signals for the value and the getter of the * corresponding prop. * * @param param0 * @param param0.get New getter. * @param param0.value New value. */ update({ get, value }) { if (!this.valueSignal) { this.valueSignal = signal(value); this.getterSignal = signal(get); } else if (value !== this.valueSignal.peek() || get !== this.getterSignal.peek()) { batch(() => { this.valueSignal.value = value; this.getterSignal.value = get; }); } } }; export { PropSignal }; //# sourceMappingURL=signals.js.map