mirror of
https://git.mirrors.martin98.com/https://github.com/infiniflow/ragflow.git
synced 2025-04-19 12:39:59 +08:00
### What problem does this PR solve? Feat: Add TreeView component #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
parent
09291db805
commit
57cbefa589
120
web/package-lock.json
generated
120
web/package-lock.json
generated
@ -15,6 +15,7 @@
|
||||
"@js-preview/excel": "^1.7.8",
|
||||
"@lexical/react": "^0.23.1",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.4",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.0",
|
||||
"@radix-ui/react-avatar": "^1.1.1",
|
||||
@ -4563,6 +4564,125 @@
|
||||
"resolved": "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-1.1.0.tgz",
|
||||
"integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA=="
|
||||
},
|
||||
"node_modules/@radix-ui/react-accordion": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmmirror.com/@radix-ui/react-accordion/-/react-accordion-1.2.3.tgz",
|
||||
"integrity": "sha512-RIQ15mrcvqIkDARJeERSuXSry2N8uYnxkdDetpfmalT/+0ntOXLkFOsh9iwlAsCv+qcmhZjbdJogIm6WBa6c4A==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.1",
|
||||
"@radix-ui/react-collapsible": "1.1.3",
|
||||
"@radix-ui/react-collection": "1.1.2",
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-direction": "1.1.0",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.2",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/primitive": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/@radix-ui/primitive/-/primitive-1.1.1.tgz",
|
||||
"integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-collection": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/@radix-ui/react-collection/-/react-collection-1.1.2.tgz",
|
||||
"integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-primitive": "2.0.2",
|
||||
"@radix-ui/react-slot": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-compose-refs": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmmirror.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz",
|
||||
"integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmmirror.com/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-accordion/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmmirror.com/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-alert-dialog": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmmirror.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.4.tgz",
|
||||
|
@ -26,6 +26,7 @@
|
||||
"@js-preview/excel": "^1.7.8",
|
||||
"@lexical/react": "^0.23.1",
|
||||
"@monaco-editor/react": "^4.6.0",
|
||||
"@radix-ui/react-accordion": "^1.2.3",
|
||||
"@radix-ui/react-alert-dialog": "^1.1.4",
|
||||
"@radix-ui/react-aspect-ratio": "^1.1.0",
|
||||
"@radix-ui/react-avatar": "^1.1.1",
|
||||
|
58
web/src/components/ui/accordion.tsx
Normal file
58
web/src/components/ui/accordion.tsx
Normal file
@ -0,0 +1,58 @@
|
||||
'use client';
|
||||
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
import { ChevronDown } from 'lucide-react';
|
||||
import * as React from 'react';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
const Accordion = AccordionPrimitive.Root;
|
||||
|
||||
const AccordionItem = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<AccordionPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn('border-b', className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
AccordionItem.displayName = 'AccordionItem';
|
||||
|
||||
const AccordionTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Header className="flex">
|
||||
<AccordionPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
));
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||
|
||||
const AccordionContent = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Content
|
||||
ref={ref}
|
||||
className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||
{...props}
|
||||
>
|
||||
<div className={cn('pb-4 pt-0', className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
));
|
||||
|
||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||
|
||||
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
358
web/src/components/ui/tree-view.tsx
Normal file
358
web/src/components/ui/tree-view.tsx
Normal file
@ -0,0 +1,358 @@
|
||||
'use client';
|
||||
|
||||
import { cn } from '@/lib/utils';
|
||||
import * as AccordionPrimitive from '@radix-ui/react-accordion';
|
||||
import { cva } from 'class-variance-authority';
|
||||
import { ChevronRight } from 'lucide-react';
|
||||
import React from 'react';
|
||||
|
||||
const treeVariants = cva(
|
||||
'group hover:before:opacity-100 before:absolute before:rounded-lg before:left-0 px-2 before:w-full before:opacity-0 before:bg-accent/70 before:h-[2rem] before:-z-10',
|
||||
);
|
||||
|
||||
const selectedTreeVariants = cva(
|
||||
'before:opacity-100 before:bg-accent/70 text-accent-foreground',
|
||||
);
|
||||
|
||||
interface TreeDataItem {
|
||||
id: string;
|
||||
name: string;
|
||||
icon?: any;
|
||||
selectedIcon?: any;
|
||||
openIcon?: any;
|
||||
children?: TreeDataItem[];
|
||||
actions?: React.ReactNode;
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
const AccordionTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Header>
|
||||
<AccordionPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'flex flex-1 w-full items-center py-2 transition-all first:[&[data-state=open]>svg]:rotate-90',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronRight className="h-4 w-4 shrink-0 transition-transform duration-200 text-accent-foreground/50 mr-1" />
|
||||
{children}
|
||||
</AccordionPrimitive.Trigger>
|
||||
</AccordionPrimitive.Header>
|
||||
));
|
||||
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||
|
||||
const AccordionContent = React.forwardRef<
|
||||
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<AccordionPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div className="pb-1 pt-0">{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
));
|
||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||
|
||||
const TreeIcon = ({
|
||||
item,
|
||||
isOpen,
|
||||
isSelected,
|
||||
default: defaultIcon,
|
||||
}: {
|
||||
item: TreeDataItem;
|
||||
isOpen?: boolean;
|
||||
isSelected?: boolean;
|
||||
default?: any;
|
||||
}) => {
|
||||
let Icon = defaultIcon;
|
||||
if (isSelected && item.selectedIcon) {
|
||||
Icon = item.selectedIcon;
|
||||
} else if (isOpen && item.openIcon) {
|
||||
Icon = item.openIcon;
|
||||
} else if (item.icon) {
|
||||
Icon = item.icon;
|
||||
}
|
||||
return Icon ? <Icon className="h-4 w-4 shrink-0 mr-2" /> : <></>;
|
||||
};
|
||||
|
||||
const TreeActions = ({
|
||||
children,
|
||||
isSelected,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
isSelected: boolean;
|
||||
}) => {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
isSelected ? 'block' : 'hidden',
|
||||
'absolute right-3 group-hover:block',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const TreeNode = ({
|
||||
item,
|
||||
handleSelectChange,
|
||||
expandedItemIds,
|
||||
selectedItemId,
|
||||
defaultNodeIcon,
|
||||
defaultLeafIcon,
|
||||
}: {
|
||||
item: TreeDataItem;
|
||||
handleSelectChange: (item: TreeDataItem | undefined) => void;
|
||||
expandedItemIds: string[];
|
||||
selectedItemId?: string;
|
||||
defaultNodeIcon?: any;
|
||||
defaultLeafIcon?: any;
|
||||
}) => {
|
||||
const [value, setValue] = React.useState(
|
||||
expandedItemIds.includes(item.id) ? [item.id] : [],
|
||||
);
|
||||
return (
|
||||
<AccordionPrimitive.Root
|
||||
type="multiple"
|
||||
value={value}
|
||||
onValueChange={(s) => setValue(s)}
|
||||
>
|
||||
<AccordionPrimitive.Item value={item.id}>
|
||||
<AccordionTrigger
|
||||
className={cn(
|
||||
treeVariants(),
|
||||
selectedItemId === item.id && selectedTreeVariants(),
|
||||
)}
|
||||
onClick={() => {
|
||||
handleSelectChange(item);
|
||||
item.onClick?.();
|
||||
}}
|
||||
>
|
||||
<TreeIcon
|
||||
item={item}
|
||||
isSelected={selectedItemId === item.id}
|
||||
isOpen={value.includes(item.id)}
|
||||
default={defaultNodeIcon}
|
||||
/>
|
||||
<span className="text-sm truncate">{item.name}</span>
|
||||
<TreeActions isSelected={selectedItemId === item.id}>
|
||||
{item.actions}
|
||||
</TreeActions>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent className="ml-4 pl-1 border-l">
|
||||
<TreeItem
|
||||
data={item.children ? item.children : item}
|
||||
selectedItemId={selectedItemId}
|
||||
handleSelectChange={handleSelectChange}
|
||||
expandedItemIds={expandedItemIds}
|
||||
defaultLeafIcon={defaultLeafIcon}
|
||||
defaultNodeIcon={defaultNodeIcon}
|
||||
/>
|
||||
</AccordionContent>
|
||||
</AccordionPrimitive.Item>
|
||||
</AccordionPrimitive.Root>
|
||||
);
|
||||
};
|
||||
|
||||
type TreeItemProps = TreeProps & {
|
||||
selectedItemId?: string;
|
||||
handleSelectChange: (item: TreeDataItem | undefined) => void;
|
||||
expandedItemIds: string[];
|
||||
defaultNodeIcon?: any;
|
||||
defaultLeafIcon?: any;
|
||||
};
|
||||
|
||||
const TreeItem = React.forwardRef<HTMLDivElement, TreeItemProps>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
data,
|
||||
selectedItemId,
|
||||
handleSelectChange,
|
||||
expandedItemIds,
|
||||
defaultNodeIcon,
|
||||
defaultLeafIcon,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
if (!(data instanceof Array)) {
|
||||
data = [data];
|
||||
}
|
||||
return (
|
||||
<div ref={ref} role="tree" className={className} {...props}>
|
||||
<ul>
|
||||
{data.map((item) => (
|
||||
<li key={item.id}>
|
||||
{item.children ? (
|
||||
<TreeNode
|
||||
item={item}
|
||||
selectedItemId={selectedItemId}
|
||||
expandedItemIds={expandedItemIds}
|
||||
handleSelectChange={handleSelectChange}
|
||||
defaultNodeIcon={defaultNodeIcon}
|
||||
defaultLeafIcon={defaultLeafIcon}
|
||||
/>
|
||||
) : (
|
||||
<TreeLeaf
|
||||
item={item}
|
||||
selectedItemId={selectedItemId}
|
||||
handleSelectChange={handleSelectChange}
|
||||
defaultLeafIcon={defaultLeafIcon}
|
||||
/>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
TreeItem.displayName = 'TreeItem';
|
||||
|
||||
const TreeLeaf = React.forwardRef<
|
||||
HTMLDivElement,
|
||||
React.HTMLAttributes<HTMLDivElement> & {
|
||||
item: TreeDataItem;
|
||||
selectedItemId?: string;
|
||||
handleSelectChange: (item: TreeDataItem | undefined) => void;
|
||||
defaultLeafIcon?: any;
|
||||
}
|
||||
>(
|
||||
(
|
||||
{
|
||||
className,
|
||||
item,
|
||||
selectedItemId,
|
||||
handleSelectChange,
|
||||
defaultLeafIcon,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={cn(
|
||||
'ml-5 flex text-left items-center py-2 cursor-pointer before:right-1',
|
||||
treeVariants(),
|
||||
className,
|
||||
selectedItemId === item.id && selectedTreeVariants(),
|
||||
)}
|
||||
onClick={() => {
|
||||
handleSelectChange(item);
|
||||
item.onClick?.();
|
||||
}}
|
||||
{...props}
|
||||
>
|
||||
<TreeIcon
|
||||
item={item}
|
||||
isSelected={selectedItemId === item.id}
|
||||
default={defaultLeafIcon}
|
||||
/>
|
||||
<span className="flex-grow text-sm truncate">{item.name}</span>
|
||||
<TreeActions isSelected={selectedItemId === item.id}>
|
||||
{item.actions}
|
||||
</TreeActions>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
TreeLeaf.displayName = 'TreeLeaf';
|
||||
|
||||
type TreeProps = React.HTMLAttributes<HTMLDivElement> & {
|
||||
data: TreeDataItem[] | TreeDataItem;
|
||||
initialSelectedItemId?: string;
|
||||
onSelectChange?: (item: TreeDataItem | undefined) => void;
|
||||
expandAll?: boolean;
|
||||
defaultNodeIcon?: any;
|
||||
defaultLeafIcon?: any;
|
||||
};
|
||||
|
||||
const TreeView = React.forwardRef<HTMLDivElement, TreeProps>(
|
||||
(
|
||||
{
|
||||
data,
|
||||
initialSelectedItemId,
|
||||
onSelectChange,
|
||||
expandAll,
|
||||
defaultLeafIcon,
|
||||
defaultNodeIcon,
|
||||
className,
|
||||
...props
|
||||
},
|
||||
ref,
|
||||
) => {
|
||||
const [selectedItemId, setSelectedItemId] = React.useState<
|
||||
string | undefined
|
||||
>(initialSelectedItemId);
|
||||
|
||||
const handleSelectChange = React.useCallback(
|
||||
(item: TreeDataItem | undefined) => {
|
||||
setSelectedItemId(item?.id);
|
||||
if (onSelectChange) {
|
||||
onSelectChange(item);
|
||||
}
|
||||
},
|
||||
[onSelectChange],
|
||||
);
|
||||
|
||||
const expandedItemIds = React.useMemo(() => {
|
||||
if (!initialSelectedItemId) {
|
||||
return [] as string[];
|
||||
}
|
||||
|
||||
const ids: string[] = [];
|
||||
|
||||
function walkTreeItems(
|
||||
items: TreeDataItem[] | TreeDataItem,
|
||||
targetId: string,
|
||||
) {
|
||||
if (items instanceof Array) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
ids.push(items[i]!.id);
|
||||
if (walkTreeItems(items[i]!, targetId) && !expandAll) {
|
||||
return true;
|
||||
}
|
||||
if (!expandAll) ids.pop();
|
||||
}
|
||||
} else if (!expandAll && items.id === targetId) {
|
||||
return true;
|
||||
} else if (items.children) {
|
||||
return walkTreeItems(items.children, targetId);
|
||||
}
|
||||
}
|
||||
|
||||
walkTreeItems(data, initialSelectedItemId);
|
||||
return ids;
|
||||
}, [data, expandAll, initialSelectedItemId]);
|
||||
|
||||
return (
|
||||
<div className={cn('overflow-hidden relative p-2', className)}>
|
||||
<TreeItem
|
||||
data={data}
|
||||
ref={ref}
|
||||
selectedItemId={selectedItemId}
|
||||
handleSelectChange={handleSelectChange}
|
||||
expandedItemIds={expandedItemIds}
|
||||
defaultLeafIcon={defaultLeafIcon}
|
||||
defaultNodeIcon={defaultNodeIcon}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
);
|
||||
TreeView.displayName = 'TreeView';
|
||||
|
||||
export { TreeView, type TreeDataItem };
|
Loading…
x
Reference in New Issue
Block a user