@molecule/app-mind-map-canvas-react
v1.0.1
Published
React mind-map canvas — tree-structured nodes with parent/child links, automatic radial/horizontal/vertical tree layout, fold/unfold subtrees, inline edit-on-double-click; thin domain wrapper over @molecule/app-feature-canvas-react.
Downloads
139
Maintainers
Readme
@molecule/app-mind-map-canvas-react
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
React mind-map canvas — thin domain wrapper over the shared canvas base. Tree-structured nodes (parent/child links), automatic radial / horizontal / vertical tree layout, fold/unfold subtrees, inline edit-on-double-click, +/- toggles to add and collapse children.
Pan / zoom is delegated to <CanvasSurface> from
@molecule/app-feature-canvas-react. This package owns only the
mind-map domain semantics — adding mechanics that live above the
pan/zoom-and-paint base.
Exports:
<MindMapCanvas>— top-level widget; controlled or uncontrolled.MindMapNode,MindMapLayout,MindMapLayoutResulttypes.- Pure layout helpers:
computeRadialPositions,computeHorizontalTreePositions, layout knobs + defaults. - Pure tree mutators:
findNode,updateNode,setNodeText,toggleCollapsed,addChild,removeNode.
Quick Start
import { useState } from 'react'
import { MindMapCanvas, type MindMapNode } from '@molecule/app-mind-map-canvas-react'
function Demo() {
const [root, setRoot] = useState<MindMapNode>({
id: 'r',
text: 'Project',
children: [
{ id: 'a', text: 'Plan', children: [] },
{ id: 'b', text: 'Build', children: [] },
],
})
return <MindMapCanvas root={root} onChange={setRoot} layout="radial" />
}Type
feature
Installation
npm install @molecule/app-mind-map-canvas-react @molecule/app-feature-canvas-react @molecule/app-react @molecule/app-ui react
npm install -D @types/reactAPI
Interfaces
LayoutOptions
Per-layout knobs (all optional).
interface LayoutOptions {
/** Node width in canvas units. */
nodeWidth?: number
/** Node height in canvas units. */
nodeHeight?: number
/** Distance between concentric rings (radial layout). */
ringSpacing?: number
/** Horizontal step between generations (horizontal/vertical layout). */
stepX?: number
/** Vertical step between sibling subtrees (horizontal layout). */
stepY?: number
/** Origin in canvas-space for the root node's center. Defaults to `(0, 0)`. */
origin?: Point
}MindMapCanvasProps
<MindMapCanvas> props.
interface MindMapCanvasProps {
/**
* Root of the mind-map tree. The canvas is fully-controlled when
* `onChange` is supplied; otherwise the component manages its own
* mirror via `useState`.
*/
root: MindMapNode
/**
* Optional change callback. When provided, the canvas runs in
* controlled mode — every fold/edit/add-child mutation calls
* `onChange(nextRoot)` and the parent decides whether to commit.
*/
onChange?: (next: MindMapNode) => void
/** Layout strategy. Defaults to `'radial'`. */
layout?: MindMapLayout
/** Width of the surface in CSS pixels. Defaults to `800`. */
width?: number
/** Height of the surface in CSS pixels. Defaults to `600`. */
height?: number
/** Node width in canvas units. Defaults to {@link DEFAULT_NODE_WIDTH}. */
nodeWidth?: number
/** Node height in canvas units. Defaults to {@link DEFAULT_NODE_HEIGHT}. */
nodeHeight?: number
/** Fired when a node is clicked (single click, no drag). */
onNodeClick?: (node: MindMapNode) => void
/**
* Fired when a node's text is committed via inline edit. Receives the
* node id and the new text. Useful for analytics / autosave.
*/
onNodeEdit?: (id: string, text: string) => void
/** Extra classes merged onto the outer wrapper. */
className?: string
}MindMapLayoutResult
Result of a layout computation: a flat map of nodeId → canvas-space
top-left position, plus the parent/child pairs that should be drawn
as edges (in tree order, parents first). Collapsed subtrees are
omitted.
interface MindMapLayoutResult {
/** Map of `nodeId` → canvas-space top-left position. */
positions: Map<string, Point>
/** Parent/child id pairs to draw as edges. */
edges: Array<{ parentId: string; childId: string }>
/** Flat list of nodes that should be rendered (i.e. not hidden by a collapsed ancestor). */
visibleNodes: MindMapNode[]
}MindMapNode
One mind-map node. Every node is the root of its own subtree (the
top-level node passed as root is just the outermost example).
interface MindMapNode {
/** Stable identifier — must be unique across the entire tree. */
id: string
/** Display text shown in the node's body. */
text: string
/** Direct child subtrees, rendered in array order. */
children: MindMapNode[]
/**
* If `true`, the node renders without its descendants — the subtree
* is effectively hidden until expanded. Defaults to `false`.
*/
collapsed?: boolean
/**
* Optional accent color (CSS color string). Used as the inline
* border-left accent on the node body; ClassMap drives the rest of
* the chrome.
*/
color?: string
}Types
MindMapLayout
Layout strategies <MindMapCanvas> knows how to compute.
type MindMapLayout = 'radial' | 'horizontal' | 'vertical'Functions
addChild(root, parentId, child)
Append a child to the node with the given id. The new child is pushed at the end of the children array. If the parent is currently collapsed, it is automatically expanded so the new child is visible.
function addChild(root: MindMapNode, parentId: string, child: MindMapNode): MindMapNoderoot— Tree root.parentId— Id of the parent that will receive the new child.child— The child node to append.
Returns: A new tree with the child appended (and the parent expanded).
computeHorizontalTreePositions(root, options, axis)
Horizontal tidy-tree layout: root on the left, children fan out to
the right, each subtree's children stacked vertically with enough
room for every leaf descendant. The axis option swaps the role
of the X and Y axes for a vertical (top-down) tree.
function computeHorizontalTreePositions(
root: MindMapNode,
options?: LayoutOptions,
axis?: 'horizontal' | 'vertical',
): MindMapLayoutResultroot— Tree root.options— Optional layout knobs.axis—'horizontal'(default) places generations along +X;'vertical'places generations along +Y.
Returns: Layout result with positions, edges, and the visible-node list.
computeRadialPositions(root, options)
Radial layout: root at origin, children fan out around it at
uniform angles, each generation living on its own concentric ring.
For deeper generations the angular slice of each child is its
parent's slice divided by the parent's child count.
function computeRadialPositions(root: MindMapNode, options?: LayoutOptions): MindMapLayoutResultroot— Tree root.options— Optional layout knobs.
Returns: Layout result with positions, edges, and the visible-node list.
findNode(root, id)
Walk the tree and find the node with the given id, returning a pointer-style reference (not a clone). Used internally by the other helpers — exported because it's also handy at call sites.
function findNode(root: MindMapNode, id: string): MindMapNode | nullroot— Tree root.id— Id of the node to locate.
Returns: The node, or null if no node has that id.
MindMapCanvas(props)
Mind-map canvas — a thin domain wrapper over
@molecule/app-feature-canvas-react. Composes <CanvasSurface> for
pan/zoom, <CanvasNode> for each tree node, and <CanvasEdge
kind="bezier"> for parent → child links. Domain-specific behavior
lives here, not in the base:
- Auto-layout — radial / horizontal / vertical positions are
computed from the tree shape via the pure helpers in
./layout.js. - Fold / unfold — the +/- toggle on a non-leaf node flips
collapsed, hiding (or revealing) the entire subtree. - Inline edit — double-click any node body to enter an inline
<input>; commit on Enter / blur, cancel on Escape. - Add child — the
+button on every node appends a new child ("New idea") and auto-expands the parent.
Pan / zoom is owned by <CanvasSurface>; this wrapper never
re-implements those mechanics.
Style is driven by getClassMap(). Inline styles are reserved for
canvas-space geometry and the optional accent border.
function MindMapCanvas(props: MindMapCanvasProps): JSX.Elementprops— Component props.
Returns: The mind-map canvas element.
removeNode(root, id)
Remove the node with the given id (and its entire subtree) from the tree. The root itself cannot be removed — passing the root id is a no-op that returns the original tree.
function removeNode(root: MindMapNode, id: string): MindMapNoderoot— Tree root.id— Id of the node to remove.
Returns: A new tree with the node removed, or the original tree if id matches the root.
setNodeText(root, id, text)
Set a node's text.
function setNodeText(root: MindMapNode, id: string, text: string): MindMapNoderoot— Tree root.id— Id of the node to edit.text— New text content.
Returns: A new tree with the node's text updated.
toggleCollapsed(root, id)
Toggle a node's collapsed flag.
function toggleCollapsed(root: MindMapNode, id: string): MindMapNoderoot— Tree root.id— Id of the node to toggle.
Returns: A new tree with the node's collapsed flag flipped.
updateNode(root, id, updater)
Apply a transformation to the node with the given id, returning a new tree where every ancestor of the touched node has been re-cloned so React-style reference equality remains correct on unchanged subtrees.
function updateNode(
root: MindMapNode,
id: string,
updater: (node: MindMapNode) => MindMapNode,
): MindMapNoderoot— Tree root.id— Id of the node to update.updater— Function that returns the replacement node.
Returns: A new tree, or the original if id was not found.
walkVisible(root, visit)
Walk the tree and call visit(node, parent | null, depth) for every
node not hidden by a collapsed ancestor. Visits in pre-order.
function walkVisible(
root: MindMapNode,
visit: (node: MindMapNode, parent: MindMapNode | null, depth: number) => void,
): voidroot— Tree root.visit— Callback invoked once per visible node.
Constants
DEFAULT_HORIZONTAL_STEP_X
Default horizontal tree per-generation step in canvas units.
const DEFAULT_HORIZONTAL_STEP_X: 220DEFAULT_HORIZONTAL_STEP_Y
Default horizontal tree per-leaf step in canvas units.
const DEFAULT_HORIZONTAL_STEP_Y: 72DEFAULT_NODE_HEIGHT
Default canvas-space height of one node.
const DEFAULT_NODE_HEIGHT: 48DEFAULT_NODE_WIDTH
Default canvas-space size of one node, used to space siblings.
const DEFAULT_NODE_WIDTH: 160DEFAULT_RADIAL_RING
Default radial ring spacing in canvas units.
const DEFAULT_RADIAL_RING: 180Injection Notes
Requirements
Peer dependencies:
@molecule/app-feature-canvas-react^1.0.1@molecule/app-react^1.0.1@molecule/app-ui^1.0.1react^18.0.0 || ^19.0.0
Runtime Dependencies
@molecule/app-feature-canvas-react@molecule/app-react@molecule/app-uireact
Requires a wired ClassMap bond (setClassMap(...) at startup) and a
React I18nProvider ancestor — getClassMap() and useTranslation()
both throw before wiring. Pair with the companion locale bond
@molecule/app-locales-mind-map-canvas for translated aria labels and
the "New idea" default child text (English fallbacks otherwise).
The surface is FIXED-SIZE: width / height are pixel props
(default 800x600) — the canvas does not track its parent's size. To
fill a panel, measure the parent (e.g. a resize observer) and pass
the measured pixels down; CSS alone will clip, not resize.
Supplying onChange makes the tree fully controlled (every
fold / edit / add-child calls it with the next root); omitting it lets
the canvas manage an internal copy seeded from root.
Translations
Translation strings are provided by @molecule/app-locales-mind-map-canvas.
