"use client" import { useState, useRef, useEffect } from "react" import { createPortal } from "react-dom" import { cn } from "@/lib/utils" interface Option { value: string label: string sublabel?: string } interface SingleSelectProps { label: string options: Option[] selected: string onChange: (selected: string) => void placeholder?: string wide?: boolean dropUp?: boolean } export function SingleSelect({ label, options, selected, onChange, placeholder, wide, dropUp, }: SingleSelectProps) { const [open, setOpen] = useState(false) const [position, setPosition] = useState({ top: 0, left: 0, width: 0, bottom: 0 }) const triggerRef = useRef(null) const dropdownRef = useRef(null) useEffect(() => { if (open && triggerRef.current) { const rect = triggerRef.current.getBoundingClientRect() const minWidth = wide ? 400 : 200 setPosition({ top: rect.bottom + 4, left: rect.left, width: Math.max(rect.width, minWidth), bottom: window.innerHeight - rect.top + 4, }) } }, [open, wide]) // Click outside handler useEffect(() => { function handleClickOutside(event: MouseEvent) { const target = event.target as Node if ( triggerRef.current && !triggerRef.current.contains(target) && dropdownRef.current && !dropdownRef.current.contains(target) ) { setOpen(false) } } if (open) { document.addEventListener("mousedown", handleClickOutside) } return () => document.removeEventListener("mousedown", handleClickOutside) }, [open]) const selectOption = (value: string) => { onChange(value) setOpen(false) } const displayText = selected ? options.find((o) => o.value === selected)?.label || selected : placeholder || label return ( <> {/* Trigger button */} {/* Dropdown - rendered via portal */} {open && typeof document !== "undefined" && createPortal(
{/* Options list */}
{options.length === 0 ? (
No options
) : ( options.map((option) => { const isSelected = selected === option.value return ( ) }) )}
, document.body )} ) }