deerflow2/frontend/src/components/ui/dropdown-selector.tsx

102 lines
2.3 KiB
TypeScript

import { useState } from "react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
export interface DropdownSelectorOption<T extends string> {
value: T;
label: string;
}
interface DropdownSelectorProps<T extends string> {
value: T;
options: DropdownSelectorOption<T>[];
onChange: (value: T) => void;
triggerClassName?: string;
contentClassName?: string;
}
function ChevronDownIcon() {
return (
<svg
width="10"
height="6"
viewBox="0 0 10 6"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.75 0.75L4.75 4.75L8.75 0.75"
stroke="#666666"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
function ChevronUpIcon() {
return (
<svg
width="10"
height="6"
viewBox="0 0 10 6"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M0.75 4.75L4.75 0.75L8.75 4.75"
stroke="#666666"
strokeWidth="1.5"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
export function DropdownSelector<T extends string>({
value,
options,
onChange,
triggerClassName,
contentClassName,
}: DropdownSelectorProps<T>) {
const selectedOption = options.find((opt) => opt.value === value);
const [isOpen, setIsOpen] = useState(false);
return (
<DropdownMenu open={isOpen} onOpenChange={setIsOpen}>
<DropdownMenuTrigger
className={
triggerClassName ??
"border-none bg-transparent shadow-none select-none focus:outline-none"
}
>
<span className="flex items-center gap-1">
{selectedOption?.label ?? value}
{isOpen ? <ChevronUpIcon /> : <ChevronDownIcon />}
</span>
</DropdownMenuTrigger>
<DropdownMenuContent className={contentClassName}>
<DropdownMenuRadioGroup
value={value}
onValueChange={(v) => onChange(v as T)}
>
{options.map((option) => (
<DropdownMenuRadioItem key={option.value} value={option.value}>
{option.label}
</DropdownMenuRadioItem>
))}
</DropdownMenuRadioGroup>
</DropdownMenuContent>
</DropdownMenu>
);
}