UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

56 lines (55 loc) 2.27 kB
import { jsx as _jsx } from "react/jsx-runtime"; import { createContext, use } from "react"; import { walkElements } from "../../util/element.js"; /** * Create a `[Mapping, Mapper]` pair of components backed by their own private React context. * * - `Mapping` extends or overrides the mapping inside a subtree (useful for swapping in custom renderers for specific element types). * - `Mapper` accepts a pre-walked iterable of elements as `children` and dispatches each to the registered component for its `type`. Any other props passed to `Mapper` are spread onto every dispatched child. * * Each call creates its own context — independent mappers don't interfere with each other. * * @typeParam E The shape of any extra props the mapper threads through to every dispatched child. Defaults to `unknown` (no extras). * * @example * // No extras: * const [TreeCardMapping, TreeCardMapper] = createMapper({ * "tree-directory": DirectoryCard, * "tree-file": FileCard, * }); * <TreeCardMapper>{walkElements(children)}</TreeCardMapper> * * @example * // With extras (`path` threaded into every dispatched child): * const [TreeMenuMapping, TreeMenuMapper] = createMapper<{ path?: AbsolutePath }>({ * "tree-directory": TreeMenuItem, * "tree-file": TreeMenuItem, * }); * <TreeMenuMapper path="/foo">{queryElements(children, query)}</TreeMenuMapper> */ export function createMapper(defaults = {}) { const Context = createContext(defaults); function Mapping({ mapping, children }) { const inherited = use(Context); return _jsx(Context, { value: { ...inherited, ...mapping }, children: children }); } function Mapper({ children, ...extras }) { const mapping = use(Context); const items = []; for (const element of walkElements(children)) { if (typeof element.type !== "string") { items.push(element); continue; } const Component = mapping[element.type]; if (Component) { items.push(_jsx(Component, { ...extras, ...element.props }, element.key)); } else { items.push(element); } } return items; } return [Mapping, Mapper]; }