UNPKG

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