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