import type { Meta, StoryObj } from '@storybook/react'; import { TreeView } from './tree-view'; import { useTreeView, type TreeNode } from './use-tree-view'; import { Folder, File, Code, Database, Globe, ChevronRight, ChevronDown } from 'lucide-react'; import React, { useState } from 'react'; import { cn } from '../../shared/utils'; const meta: Meta = { title: 'UI/TreeView', component: TreeView, render: args => , }; export default meta; type Story = StoryObj; const fileTree: TreeNode[] = [ { id: 'root', label: 'Project', icon: , children: [ { id: 'src', label: 'src', icon: , children: [ { id: 'components', label: 'components', icon: , children: [ { id: 'btn', label: 'Button.tsx', icon: }, { id: 'inp', label: 'Input.tsx', icon: }, ], }, { id: 'app', label: 'App.tsx', icon: }, ], }, { id: 'public', label: 'public', icon: , children: [{ id: 'favicon', label: 'favicon.ico', icon: }], }, { id: 'json', label: 'package.json', icon: }, ], }, ]; export const Default: Story = { args: { data: fileTree, }, render: args => (
console.log('Selected:', node)} />
), }; /** * ## useTreeView — Headless Hook * * Use `useTreeView` when you need the full tree logic (expand/collapse, * keyboard navigation, selection) but want to render a completely custom UI. * The example below renders a flat indented list with custom styling instead * of the default `` component. * * ```tsx * import { useTreeView } from '@xertica/ui/tree-view'; * * const { expanded, effectiveSelectedId, toggleExpand, handleSelect, handleKeyDown, getNodeRef } = * useTreeView({ data, onNodeSelect }); * ``` */ export const HeadlessHook: Story = { name: 'useTreeView (headless)', render: () => { const [selectedId, setSelectedId] = useState(undefined); const { expanded, effectiveSelectedId, getNodeRef, toggleExpand, handleSelect, handleKeyDown } = useTreeView({ data: fileTree, defaultExpanded: ['root', 'src'], selectedNodeId: selectedId, onNodeSelect: node => { setSelectedId(node.id); console.log('Selected:', node.label); }, }); const renderNode = (node: TreeNode, level: number): React.ReactNode => { const hasChildren = !!node.children?.length; const isExpanded = expanded.has(node.id); const isSelected = node.id === effectiveSelectedId; return (
{hasChildren && isExpanded && (
{node.children!.map(child => renderNode(child, level + 1))}
)}
); }; return (

Explorer

{fileTree.map(node => renderNode(node, 0))}
{selectedId && (

Selected: {selectedId}

)}
); }, };