import React, { createContext, useContext, useState, useRef, useEffect, useCallback, } from 'react'; import { cn } from '@/editor/lib/utils'; interface SelectContextType { open: boolean; setOpen: (open: boolean) => void; value: string; setValue: (value: string) => void; } const SelectContext = createContext(undefined); function useSelect() { const context = useContext(SelectContext); if (!context) { throw new Error('Select components must be used within a Select'); } return context; } interface SelectProps { value?: string; onValueChange?: (value: string) => void; children: React.ReactNode; } function Select({ value: controlledValue, onValueChange, children, }: SelectProps) { const [internalValue, setInternalValue] = useState(''); const [open, setOpen] = useState(false); const containerRef = useRef(null); const isControlled = controlledValue !== undefined; const value = isControlled ? controlledValue : internalValue; const handleValueChange = useCallback( (newValue: string) => { if (!isControlled) { setInternalValue(newValue); } onValueChange?.(newValue); setOpen(false); }, [isControlled, onValueChange] ); // Handle clicking outside to close the menu useEffect(() => { function handleClickOutside(event: MouseEvent) { if ( containerRef.current && !containerRef.current.contains(event.target as Node) ) { setOpen(false); } } if (open) { document.addEventListener('mousedown', handleClickOutside); return () => { document.removeEventListener('mousedown', handleClickOutside); }; } }, [open]); return (
{children}
); } interface SelectTriggerProps extends React.ButtonHTMLAttributes { children: React.ReactNode; placeholder?: string; } function SelectTrigger({ className, children, placeholder = 'Select...', ...props }: SelectTriggerProps) { const { open, setOpen, value } = useSelect(); const triggerRef = useRef(null); return ( ); } interface SelectValueProps { children?: React.ReactNode; placeholder?: string; } function SelectValue({ children, placeholder = 'Select...', }: SelectValueProps) { const { value } = useSelect(); return ( {children || value || placeholder} ); } interface SelectContentProps extends React.HTMLAttributes { children: React.ReactNode; } function SelectContent({ className, children, ...props }: SelectContentProps) { const { open } = useSelect(); const contentRef = useRef(null); if (!open) return null; return (
{children}
); } interface SelectItemProps extends React.HTMLAttributes { value: string; children: React.ReactNode; disabled?: boolean; } function SelectItem({ value, children, disabled = false, className, ...props }: SelectItemProps) { const { value: selectedValue, setValue } = useSelect(); const isSelected = selectedValue === value; return (
!disabled && setValue(value)} className={cn( 'focus:bg-accent focus:text-accent-foreground [&_svg:not([class*="text-"])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none transition-colors', !disabled && 'hover:bg-accent hover:text-accent-foreground cursor-pointer', disabled && 'pointer-events-none opacity-50', isSelected && 'bg-accent text-accent-foreground', className )} {...props} > {isSelected && ( )} {children}
); } interface SelectGroupProps extends React.HTMLAttributes { children: React.ReactNode; } function SelectGroup({ className, children, ...props }: SelectGroupProps) { return (
{children}
); } interface SelectLabelProps extends React.HTMLAttributes { children: React.ReactNode; } function SelectLabel({ className, children, ...props }: SelectLabelProps) { return (
{children}
); } interface SelectSeparatorProps extends React.HTMLAttributes {} function SelectSeparator({ className, ...props }: SelectSeparatorProps) { return (
); } export { Select, SelectTrigger, SelectValue, SelectContent, SelectItem, SelectGroup, SelectLabel, SelectSeparator, };