Feat: Create a folder #3221 (#7228)

### What problem does this PR solve?

Feat: Create a folder #3221

### Type of change


- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
balibabu
2025-04-23 15:21:09 +08:00
committed by GitHub
parent 94181a990b
commit 51d9bde5a3
20 changed files with 626 additions and 133 deletions

View File

@ -3,18 +3,19 @@ import React, {
ChangeEventHandler,
FunctionComponent,
PropsWithChildren,
ReactNode,
} from 'react';
import { Button, ButtonProps } from './ui/button';
import { SearchInput } from './ui/input';
interface IProps {
title: string;
showDialog?: () => void;
title?: string;
FilterPopover?: FunctionComponent<any>;
searchString?: string;
onSearchChange?: ChangeEventHandler<HTMLInputElement>;
count?: number;
showFilter?: boolean;
leftPanel?: ReactNode;
}
const FilterButton = React.forwardRef<
@ -31,16 +32,16 @@ const FilterButton = React.forwardRef<
export default function ListFilterBar({
title,
children,
showDialog,
FilterPopover,
searchString,
onSearchChange,
count,
showFilter = true,
leftPanel,
}: PropsWithChildren<IProps>) {
return (
<div className="flex justify-between mb-6">
<span className="text-3xl font-bold ">{title}</span>
<div className="flex justify-between mb-6 items-center">
<span className="text-3xl font-bold ">{leftPanel || title}</span>
<div className="flex gap-4 items-center">
{showFilter &&
(FilterPopover ? (
@ -55,9 +56,7 @@ export default function ListFilterBar({
value={searchString}
onChange={onSearchChange}
></SearchInput>
<Button variant={'tertiary'} size={'sm'} onClick={showDialog}>
{children}
</Button>
{children}
</div>
</div>
);

View File

@ -0,0 +1,95 @@
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { ChevronDown, X } from 'lucide-react';
import React, { ReactNode, useState } from 'react';
export type TreeNode = {
id: number;
label: ReactNode;
children?: TreeNode[];
};
type SingleSelectTreeDropdownProps = {
allowDelete?: boolean;
treeData: TreeNode[];
};
const SingleTreeSelect: React.FC<SingleSelectTreeDropdownProps> = ({
allowDelete = false,
treeData,
}) => {
const [selectedOption, setSelectedOption] = useState<TreeNode | null>(null);
const handleSelect = (option: TreeNode) => {
setSelectedOption(option);
};
const handleDelete = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
console.log(
'Delete button clicked. Current selected option:',
selectedOption,
);
setSelectedOption(null);
console.log('After deletion, selected option:', selectedOption);
};
const renderTree = (nodes: TreeNode[]) => {
return nodes.map((node) => (
<div key={node.id} className="pl-4">
<DropdownMenuItem
onClick={() => handleSelect(node)}
className={`flex items-center ${
selectedOption?.id === node.id ? 'bg-gray-500' : ''
}`}
>
<span>{node.label}</span>
{node.children && (
<ChevronDown className="ml-2 h-4 w-4 text-gray-400" />
)}
</DropdownMenuItem>
{node.children && renderTree(node.children)}
</div>
));
};
return (
<div className="relative">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button
type="button"
className="flex items-center justify-between space-x-1 p-2 border rounded-md focus:outline-none w-full"
>
{selectedOption ? (
<>
<span>{selectedOption.label}</span>
{allowDelete && (
<button
type="button"
className="ml-2 text-gray-500 hover:text-red-500 focus:outline-none"
onClick={handleDelete}
>
<X className="h-4 w-4" />
</button>
)}
</>
) : (
'Select an option'
)}
<ChevronDown className="h-4 w-4" />
</button>
</DropdownMenuTrigger>
<DropdownMenuContent className=" mt-2 w-56 origin-top-right rounded-md shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none">
{renderTree(treeData)}
</DropdownMenuContent>
</DropdownMenu>
</div>
);
};
export default SingleTreeSelect;