@wordpress/interactivity
Version:
Package that provides a standard and simple way to handle the frontend interactivity of Gutenberg blocks.
176 lines (175 loc) • 5.12 kB
JavaScript
// packages/interactivity/src/hooks.tsx
import {
h as createElement,
options,
createContext,
cloneElement
} from "preact";
import { useRef, useCallback, useContext } from "preact/hooks";
import { store, stores, universalUnlock } from "./store";
import { warn } from "./utils";
import { getScope, setScope, resetScope } from "./scopes";
import { PENDING_GETTER } from "./proxies/state";
function isNonDefaultDirectiveSuffix(entry) {
return entry.suffix !== null;
}
function isDefaultDirectiveSuffix(entry) {
return entry.suffix === null;
}
var context = createContext({ client: {}, server: {} });
var directiveCallbacks = {};
var directivePriorities = {};
var directive = (name, callback, { priority = 10 } = {}) => {
directiveCallbacks[name] = callback;
directivePriorities[name] = priority;
};
var resolve = (path, namespace) => {
if (!namespace) {
warn(
`Namespace missing for "${path}". The value for that path won't be resolved.`
);
return;
}
let resolvedStore = stores.get(namespace);
if (typeof resolvedStore === "undefined") {
resolvedStore = store(
namespace,
{},
{
lock: universalUnlock
}
);
}
const current = {
...resolvedStore,
context: getScope().context[namespace]
};
try {
const pathParts = path.split(".");
return pathParts.reduce((acc, key) => acc[key], current);
} catch (e) {
if (e === PENDING_GETTER) {
return PENDING_GETTER;
}
}
};
var getEvaluate = ({ scope }) => (
// TODO: When removing the temporarily remaining `value( ...args )` call below, remove the `...args` parameter too.
(entry, ...args) => {
let { value: path, namespace } = entry;
if (typeof path !== "string") {
throw new Error("The `value` prop should be a string path");
}
const hasNegationOperator = path[0] === "!" && !!(path = path.slice(1));
setScope(scope);
const value = resolve(path, namespace);
if (typeof value === "function") {
if (hasNegationOperator) {
warn(
"Using a function with a negation operator is deprecated and will stop working in WordPress 6.9. Please use derived state instead."
);
const functionResult = !value(...args);
resetScope();
return functionResult;
}
resetScope();
const wrappedFunction = (...functionArgs) => {
setScope(scope);
const functionResult = value(...functionArgs);
resetScope();
return functionResult;
};
if (value.sync) {
const syncAwareFunction = wrappedFunction;
syncAwareFunction.sync = true;
}
return wrappedFunction;
}
const result = value;
resetScope();
return hasNegationOperator && value !== PENDING_GETTER ? !result : result;
}
);
var getPriorityLevels = (directives) => {
const byPriority = Object.keys(directives).reduce((obj, name) => {
if (directiveCallbacks[name]) {
const priority = directivePriorities[name];
(obj[priority] = obj[priority] || []).push(name);
}
return obj;
}, {});
return Object.entries(byPriority).sort(([p1], [p2]) => parseInt(p1) - parseInt(p2)).map(([, arr]) => arr);
};
var Directives = ({
directives,
priorityLevels: [currentPriorityLevel, ...nextPriorityLevels],
element,
originalProps,
previousScope
}) => {
const scope = useRef({}).current;
scope.evaluate = useCallback(getEvaluate({ scope }), []);
const { client, server } = useContext(context);
scope.context = client;
scope.serverContext = server;
scope.ref = previousScope?.ref || useRef(null);
element = cloneElement(element, { ref: scope.ref });
scope.attributes = element.props;
const children = nextPriorityLevels.length > 0 ? createElement(Directives, {
directives,
priorityLevels: nextPriorityLevels,
element,
originalProps,
previousScope: scope
}) : element;
const props = { ...originalProps, children };
const directiveArgs = {
directives,
props,
element,
context,
evaluate: scope.evaluate
};
setScope(scope);
for (const directiveName of currentPriorityLevel) {
const wrapper = directiveCallbacks[directiveName]?.(directiveArgs);
if (wrapper !== void 0) {
props.children = wrapper;
}
}
resetScope();
return props.children;
};
var old = options.vnode;
options.vnode = (vnode) => {
if (vnode.props.__directives) {
const props = vnode.props;
const directives = props.__directives;
if (directives.key) {
vnode.key = directives.key.find(isDefaultDirectiveSuffix).value;
}
delete props.__directives;
const priorityLevels = getPriorityLevels(directives);
if (priorityLevels.length > 0) {
vnode.props = {
directives,
priorityLevels,
originalProps: props,
type: vnode.type,
element: createElement(vnode.type, props),
top: true
};
vnode.type = Directives;
}
}
if (old) {
old(vnode);
}
};
export {
directive,
getEvaluate,
isDefaultDirectiveSuffix,
isNonDefaultDirectiveSuffix
};
//# sourceMappingURL=hooks.js.map