@edux-design/tree-select
v0.2.1
Published
TreeSelect renders hierarchical data with expand/collapse controls, optional inline editing, optional add-item actions, and drag-and-drop reordering. The component is controlled: you own the tree data and receive updates via callbacks.
Readme
@edux-design/tree-select
TreeSelect renders hierarchical data with expand/collapse controls, optional inline editing, optional add-item actions, and drag-and-drop reordering. The component is controlled: you own the tree data and receive updates via callbacks.
Installation
pnpm add @edux-design/tree-select @edux-design/forms @edux-design/buttons @edux-design/icons @edux-design/tooltips @dnd-kit/core @dnd-kit/sortable
# or
npm install @edux-design/tree-select @edux-design/forms @edux-design/buttons @edux-design/icons @edux-design/tooltips @dnd-kit/core @dnd-kit/sortablePeer deps include react@^19.1.0 and react-dom@^19.1.0.
Data shape
Each node must have a stable id. Children are optional.
const data = [
{
id: "docs",
label: "Documentation",
children: [
{ id: "intro", label: "Introduction" },
{ id: "install", label: "Installation" },
],
},
];idis required and must be unique within the tree.labelis displayed; when missing theidis used.childrenis an array of child nodes.
Usage
Basic
import { TreeSelect } from "@edux-design/tree-select";
export function Example() {
return <TreeSelect data={data} />;
}Drag and drop
export function DragExample() {
const [tree, setTree] = useState(data);
return (
<TreeSelect
data={tree}
allowDragAndDrop
onDataChange={setTree}
/>
);
}Multi-select drag
export function MultiDragExample() {
const [tree, setTree] = useState(data);
return (
<TreeSelect
data={tree}
allowDragAndDrop
allowMultiDrag
onDataChange={setTree}
/>
);
}- Hold CMD (Mac) or CTRL (Windows) to select multiple adjacent siblings.
- Hold SHIFT to select a range of adjacent siblings.
- Multi-select only works within the same sibling group and must stay contiguous.
Editable labels and add actions
export function EditableExample() {
const [tree, setTree] = useState(data);
return (
<TreeSelect
data={tree}
isEditable
maxDepth={3}
addChildTooltip="Add a child item"
addSiblingTooltip="Add a sibling item"
onDataChange={setTree}
/>
);
}Chevron toggle
export function ChevronExample() {
return <TreeSelect data={data} useChevron />;
}Props
data (array, required)
Tree data to render. This component is controlled; updates are emitted through onDataChange.
onDataChange (function)
Called with the updated tree when edits, add actions, or drag-and-drop reordering occur.
onLabelChange (function)
Called with (id, nextLabel) when a label edit is committed. When provided, label edits do not emit onDataChange.
allowDragAndDrop (boolean, default false)
Enables drag-and-drop. When false, drag handles are hidden and items cannot be reordered.
allowMultiDrag (boolean, default false)
Allows multi-select drag using CMD/CTRL or SHIFT. Selection is restricted to adjacent siblings.
isEditable (boolean, default false)
Enables inline label editing and add-item controls.
allowDelete (boolean, default false)
Shows a delete action for each item and removes the node (plus descendants).
actionButtonsPosition ("start" | "end", default "start")
Positions the action buttons within a row. "end" pushes the actions to the end of the row.
maxLabelLength (number)
Limits label width by character count and truncates with an ellipsis when exceeded.
useChevron (boolean, default false)
Uses a chevron control for expand/collapse instead of the checkbox toggle.
defaultExpanded ("all" | number | string[] | Set<string | number>)
Defines the initial expansion state. Use "all" to expand every node with children, a number to expand up to that level (1 expands top-level nodes), or an array/set of ids to expand specific items.
expanded ("all" | number | string[] | Set<string | number>)
Controlled expansion state. When provided, the tree expansion is driven by this value.
onExpandedChange (function)
Called with a Set of expanded ids when the user toggles expansion in controlled mode.
maxDepth (number)
Limits how deep new items can be added. For example, maxDepth={3} allows at most 3 levels.
addChildTooltip (string)
Tooltip copy for the add-child button.
addSiblingTooltip (string)
Tooltip copy for the add-sibling button.
deleteTooltip (string)
Tooltip copy for the delete button.
levelConfig (object)
Per-level configuration for editing, actions, and tooltips. Use default for global settings and levels to override by depth.
Values in levelConfig override isEditable, allowDelete, and tooltip props for the matching depth.
levelConfig={{
default: {
editable: true,
actions: { addChild: true, addSibling: true, delete: false },
tooltips: {
addChild: "Add child",
addSibling: "Add sibling",
delete: "Delete item",
},
},
levels: {
0: {
editable: false,
tooltips: { addChild: "Add chapter" },
},
},
}}Behaviour details
- Nodes with children render an expand/collapse control.
- Leaf nodes do not render a checkbox/chevron toggle.
- Expansion only controls visibility of children.
- When
useChevronisfalse, the toggle uses the Checkbox plus/indeterminate states. defaultExpandedonly seeds the initial state; useexpandedfor controlled expansion.- When
expandedis provided,onExpandedChangeis called with the next set of expanded ids. - Deleting a node removes the entire subtree and clears any selected/expanded state for those ids.
levelConfigcan override editability, actions, and tooltips per depth.- Drag-and-drop supports reordering within a level and moving items between levels.
- Drag handle alignment is set so child handles align under the parent toggle.
Development
pnpm --filter @edux-design/tree-select lint
pnpm --filter @edux-design/tree-select check-types
pnpm --filter @edux-design/tree-select buildStorybook examples live in src/demos/TreeSelect.stories.jsx.
