import * as React from 'react'; import {cn} from '@/lib/utils'; import {ChevronDown, Check} from 'lucide-react'; export interface SelectOption { value: string; label: string; } export interface SelectProps { value?: string; onChange?: (value: string) => void; children?: React.ReactNode; className?: string; disabled?: boolean; placeholder?: string; } const SelectContext = React.createContext<{ value?: string; onChange?: (value: string) => void; open: boolean; setOpen: (open: boolean) => void; } | null>(null); const useSelectContext = () => { const context = React.useContext(SelectContext); if (!context) { throw new Error('Select components must be used within a Select'); } return context; }; const Select = React.forwardRef( ({ className, children, value, onChange, disabled, placeholder = 'Select an option...', ...props }, _ref) => { const [open, setOpen] = React.useState(false); const [internalValue, setInternalValue] = React.useState(value); const containerRef = React.useRef(null); const buttonRef = React.useRef(null); const displayValue = React.useMemo(() => { const currentValue = value ?? internalValue; if (!currentValue) return placeholder; // Extract label from children const options = React.Children.toArray(children); const selectedOption = options.find((child) => { if (React.isValidElement(child) && child.type === SelectOption) { return child.props.value === currentValue; } return false; }); if (React.isValidElement(selectedOption)) { return selectedOption.props.children; } return currentValue; }, [value, internalValue, children, placeholder]); React.useEffect(() => { setInternalValue(value); }, [value]); React.useEffect(() => { const 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]); const handleChange = (newValue: string) => { setInternalValue(newValue); onChange?.(newValue); setOpen(false); }; return (
{open && (
{children}
)}
); } ); Select.displayName = 'Select'; export interface SelectOptionProps { value: string; children: React.ReactNode; className?: string; disabled?: boolean; } const SelectOption = React.forwardRef( ({ className, children, value: optionValue, disabled, ...props }, ref) => { const { value, onChange } = useSelectContext(); const isSelected = value === optionValue; return (
{ if (!disabled) { onChange?.(optionValue); } }} {...props} > {children} {isSelected && }
); } ); SelectOption.displayName = 'SelectOption'; export { Select, SelectOption };