shelving
Version:
Toolkit for using data in JavaScript.
52 lines (51 loc) • 2.7 kB
TypeScript
import { type ComponentType, type FunctionComponent, type JSX } from "react";
import type { Elements } from "../../util/element.js";
import type { ChildProps } from "../util/props.js";
/**
* Dispatch table from a `JSX.IntrinsicElements` key to a renderer component.
* - Each entry is optional — unmapped elements fall through and render as themselves (e.g. an unmapped `<tree-foo>` appears as a raw `<tree-foo>` HTML element).
* - Per-entry component receives `JSX.IntrinsicElements[K] & E` — the declared props for that element type, plus any extra props `E` the mapper is configured to thread through.
*/
export type Mapping<E = unknown> = {
[K in keyof JSX.IntrinsicElements]?: ComponentType<JSX.IntrinsicElements[K] & E>;
};
/** Props for the `Mapping` component returned by `createMapper()`. */
export interface MappingProps<E = unknown> extends ChildProps {
/** Mapping entries that extend or override the inherited mapping inside this subtree. */
readonly mapping: Mapping<E>;
}
/**
* Props for the `Mapper` component returned by `createMapper()`.
* - `children` holds the pre-walked elements to dispatch.
* - All other props are spread onto every mapped child as additional props (`E`).
*/
export type MapperProps<E = unknown> = E & {
readonly children?: Elements;
};
/**
* 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 declare function createMapper<E = unknown>(defaults?: Mapping<E>): [Mapping: FunctionComponent<MappingProps<E>>, Mapper: FunctionComponent<MapperProps<E>>];