Examples
Context Menu
Tree view with right-click context menu and inline editing
This example shows how to add a context menu with rename functionality. Right-click on any item to see the context menu.
- src
- package.json
- README.md
Code
import * as React from "react";
import { TreeView, TreeViewItem, flattenTree } from "shadcn-treeview";
import { FileIcon, FolderIcon, FolderOpenIcon } from "lucide-react";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuTrigger
} from "@/components/ui/context-menu";
export function ContextMenuTreeView() {
const [data, setData] = React.useState({
id: "root",
name: "Project",
children: [
{
id: "1",
name: "src",
children: [
{
id: "11",
name: "components",
children: [{ id: "111", name: "tree-view.tsx" }]
},
{ id: "12", name: "app.tsx" },
{ id: "13", name: "index.tsx" }
]
},
{ id: "2", name: "package.json" },
{ id: "3", name: "README.md" }
]
});
const handleEditMode = (id: string) => {
setData((prevData) => {
const updateNode = (node: any): any => {
if (node.id === id) {
return { ...node, metadata: { isEditing: true } };
}
if (node.children) {
return { ...node, children: node.children.map(updateNode) };
}
return node;
};
return updateNode(prevData);
});
};
const handleEditSubmit = (id: string, newName: string) => {
setData((prevData) => {
const updateNode = (node: any): any => {
if (node.id === id) {
return { ...node, name: newName, metadata: { isEditing: false } };
}
if (node.children) {
return { ...node, children: node.children.map(updateNode) };
}
return node;
};
return updateNode(prevData);
});
};
return (
<TreeView
data={flattenTree(data)}
aria-label="Context menu tree"
className="w-64 rounded border p-2"
nodeRenderer={({
element,
isBranch,
isExpanded,
isSelected,
getNodeProps,
level
}) => (
<ContextMenu>
<ContextMenuTrigger asChild>
<TreeViewItem
{...getNodeProps()}
name={element.name}
isBranch={isBranch}
isExpanded={isExpanded}
isSelected={isSelected}
level={level}
indentation={16}
isEditing={element.metadata?.isEditing === true}
onEditSubmit={(value) => handleEditSubmit(element.id.toString(), value)}
icon={
isBranch ? (
isExpanded ? (
<FolderOpenIcon className="h-4 w-4 shrink-0 text-muted-foreground" />
) : (
<FolderIcon className="h-4 w-4 shrink-0 text-muted-foreground" />
)
) : (
<FileIcon className="h-4 w-4 shrink-0 text-muted-foreground" />
)
}
/>
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem onSelect={() => handleEditMode(element.id.toString())}>
Rename
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem disabled>Cut</ContextMenuItem>
<ContextMenuItem disabled>Copy</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem disabled className="text-destructive">
Delete
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
)}
/>
);
}Inline Editing Behavior
When isEditing is true:
- The name text is hidden and replaced with an input
- The input is auto-focused
- Press Enter or click outside to submit
- Press Escape to cancel and restore the original name
onEditSubmitis called with the new value