video-flow-b/components/ui/select-dropdown.tsx

100 lines
3.3 KiB
TypeScript

import { motion, AnimatePresence } from "framer-motion";
import { cn } from "@/public/lib/utils";
import { Check, ChevronDown } from "lucide-react";
import { useState } from "react";
interface SettingOption {
label: string;
value: string;
}
interface SelectDropdownProps {
dropdownId: string;
label: string;
options: SettingOption[];
value: string | Array<string>;
placeholder?: string;
onChange: (value: string | Array<string>) => void;
}
export const SelectDropdown = (
{
dropdownId,
label,
options,
value,
placeholder,
onChange
}: SelectDropdownProps
) => {
const [openDropdown, setOpenDropdown] = useState<string | null>(null);
const handleDropdownToggle = (dropdownId: string) => {
setOpenDropdown(openDropdown === dropdownId ? null : dropdownId);
};
return (
<div className="relative w-full h-full">
<motion.button
className={cn(
"w-full p-2 rounded-lg border text-left flex items-center justify-between",
openDropdown === dropdownId
? "border-blue-500 bg-blue-500/10"
: "border-white/10 hover:border-white/20"
)}
onClick={() => handleDropdownToggle(dropdownId)}
whileHover={{ scale: 1.01 }}
whileTap={{ scale: 0.99 }}
>
<div className="flex items-center gap-2 overflow-hidden text-ellipsis whitespace-nowrap">
{Array.isArray(value) ? (
<span>{value.map(v => options.find(opt => opt.value === v)?.label).join(',')}</span>
) : (
<span>{options.find(opt => opt.value === value)?.label || value}</span>
)}
{(!value || value.length === 0) && <span className="text-gray-400/60">{placeholder}</span>}
</div>
<motion.div
animate={{ rotate: openDropdown === dropdownId ? 180 : 0 }}
transition={{ duration: 0.2 }}
>
<ChevronDown className="w-4 h-4" />
</motion.div>
</motion.button>
<AnimatePresence>
{openDropdown === dropdownId && (
<motion.div
className="absolute z-10 w-full mt-1 py-1 rounded-lg border border-white/10 bg-black/90 backdrop-blur-xl"
initial={{ opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.2 }}
>
{options.map((option) => (
<motion.button
key={option.value}
className={cn(
"w-full px-4 py-2 text-left flex items-center justify-between hover:bg-white/5",
value.includes(option.value) && "text-blue-500"
)}
onClick={() => {
if (Array.isArray(value) && value.includes(option.value)) {
onChange(value.filter(v => v !== option.value));
} else {
onChange(Array.isArray(value) ? [...value, option.value] : option.value);
}
handleDropdownToggle(dropdownId);
}}
whileHover={{ x: 4 }}
>
{option.label}
{value.includes(option.value) && <Check className="w-4 h-4" />}
</motion.button>
))}
</motion.div>
)}
</AnimatePresence>
</div>
)
};