import { useRef, useState, useEffect, useCallback } from 'react' import { createPortal } from 'react-dom' import { ChevronDown } from 'lucide-react' import { cn } from '../../lib/cn' export interface SelectOption { value: string | number label: string } interface NofxSelectProps { value: string | number onChange: (value: string) => void options: SelectOption[] disabled?: boolean className?: string style?: React.CSSProperties } export function NofxSelect({ value, onChange, options, disabled, className, style }: NofxSelectProps) { const [open, setOpen] = useState(false) const triggerRef = useRef(null) const dropdownRef = useRef(null) const [pos, setPos] = useState({ top: 0, left: 0, width: 0 }) const selected = options.find(o => String(o.value) === String(value)) const updatePos = useCallback(() => { if (!triggerRef.current) return const rect = triggerRef.current.getBoundingClientRect() setPos({ top: rect.bottom + 4, left: rect.left, width: rect.width }) }, []) useEffect(() => { if (!open) return updatePos() const handleClose = (e: MouseEvent) => { const target = e.target as Node if (triggerRef.current?.contains(target)) return if (dropdownRef.current?.contains(target)) return setOpen(false) } const handleScroll = () => setOpen(false) document.addEventListener('mousedown', handleClose) window.addEventListener('scroll', handleScroll, true) return () => { document.removeEventListener('mousedown', handleClose) window.removeEventListener('scroll', handleScroll, true) } }, [open, updatePos]) return (
{ e.stopPropagation() if (!disabled) setOpen(!open) }} > {selected?.label ?? String(value)}
{open && createPortal(
{options.map((opt) => (
{ e.stopPropagation() onChange(String(opt.value)) setOpen(false) }} > {opt.label}
))}
, document.body, )}
) }