forked from 77media/video-flow
92 lines
2.8 KiB
TypeScript
92 lines
2.8 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;
|
|
placeholder?: string;
|
|
onChange: (value: 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">
|
|
<span>{options.find(opt => opt.value === value)?.label || value}</span>
|
|
{placeholder && <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 === option.value && "text-blue-500"
|
|
)}
|
|
onClick={() => {
|
|
onChange(option.value);
|
|
handleDropdownToggle(dropdownId);
|
|
}}
|
|
whileHover={{ x: 4 }}
|
|
>
|
|
{option.label}
|
|
{value === option.value && <Check className="w-4 h-4" />}
|
|
</motion.button>
|
|
))}
|
|
</motion.div>
|
|
)}
|
|
</AnimatePresence>
|
|
</div>
|
|
)
|
|
}; |