shelving
Version:
Toolkit for using data in JavaScript.
56 lines (55 loc) • 2.27 kB
JavaScript
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];
}