@wordpress/interactivity
Version:
Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.
134 lines (127 loc) • 4.68 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.setScope = exports.resetScope = exports.getServerContext = exports.getScope = exports.getElement = exports.getContext = void 0;
var _namespaces = require("./namespaces");
/**
* External dependencies
*/
/**
* Internal dependencies
*/
// Store stacks for the current scope and the default namespaces and export APIs
// to interact with them.
const scopeStack = [];
const getScope = () => scopeStack.slice(-1)[0];
exports.getScope = getScope;
const setScope = scope => {
scopeStack.push(scope);
};
exports.setScope = setScope;
const resetScope = () => {
scopeStack.pop();
};
// Wrap the element props to prevent modifications.
exports.resetScope = resetScope;
const immutableMap = new WeakMap();
const immutableError = () => {
throw new Error('Please use `data-wp-bind` to modify the attributes of an element.');
};
const immutableHandlers = {
get(target, key, receiver) {
const value = Reflect.get(target, key, receiver);
return !!value && typeof value === 'object' ? deepImmutable(value) : value;
},
set: immutableError,
deleteProperty: immutableError
};
const deepImmutable = target => {
if (!immutableMap.has(target)) {
immutableMap.set(target, new Proxy(target, immutableHandlers));
}
return immutableMap.get(target);
};
/**
* Retrieves the context inherited by the element evaluating a function from the
* store. The returned value depends on the element and the namespace where the
* function calling `getContext()` exists.
*
* @param namespace Store namespace. By default, the namespace where the calling
* function exists is used.
* @return The context content.
*/
const getContext = namespace => {
const scope = getScope();
if (globalThis.SCRIPT_DEBUG) {
if (!scope) {
throw Error('Cannot call `getContext()` when there is no scope. If you are using an async function, please consider using a generator instead. If you are using some sort of async callbacks, like `setTimeout`, please wrap the callback with `withScope(callback)`.');
}
}
return scope.context[namespace || (0, _namespaces.getNamespace)()];
};
/**
* Retrieves a representation of the element where a function from the store
* is being evaluated. Such representation is read-only, and contains a
* reference to the DOM element, its props and a local reactive state.
*
* @return Element representation.
*/
exports.getContext = getContext;
const getElement = () => {
const scope = getScope();
if (globalThis.SCRIPT_DEBUG) {
if (!scope) {
throw Error('Cannot call `getElement()` when there is no scope. If you are using an async function, please consider using a generator instead. If you are using some sort of async callbacks, like `setTimeout`, please wrap the callback with `withScope(callback)`.');
}
}
const {
ref,
attributes
} = scope;
return Object.freeze({
ref: ref.current,
attributes: deepImmutable(attributes)
});
};
/**
* Retrieves the part of the inherited context defined and updated from the
* server.
*
* The object returned is read-only, and includes the context defined in PHP
* with `wp_interactivity_data_wp_context()`, including the corresponding
* inherited properties. When `actions.navigate()` is called, this object is
* updated to reflect the changes in the new visited page, without affecting the
* context returned by `getContext()`. Directives can subscribe to those changes
* to update the context if needed.
*
* @example
* ```js
* store('...', {
* callbacks: {
* updateServerContext() {
* const context = getContext();
* const serverContext = getServerContext();
* // Override some property with the new value that came from the server.
* context.overridableProp = serverContext.overridableProp;
* },
* },
* });
* ```
*
* @param namespace Store namespace. By default, the namespace where the calling
* function exists is used.
* @return The server context content.
*/
exports.getElement = getElement;
const getServerContext = namespace => {
const scope = getScope();
if (globalThis.SCRIPT_DEBUG) {
if (!scope) {
throw Error('Cannot call `getServerContext()` when there is no scope. If you are using an async function, please consider using a generator instead. If you are using some sort of async callbacks, like `setTimeout`, please wrap the callback with `withScope(callback)`.');
}
}
return scope.serverContext[namespace || (0, _namespaces.getNamespace)()];
};
exports.getServerContext = getServerContext;
//# sourceMappingURL=scopes.js.map