import React, { useState, useRef, useEffect } from 'react' import ReactDOM from 'react-dom' import { ChevronDown, Check } from 'lucide-react' export default function CustomSelect({ value, onChange, options = [], // [{ value, label, icon? }] placeholder = '', searchable = false, style = {}, size = 'md', // 'sm' | 'md' }) { const [open, setOpen] = useState(false) const [search, setSearch] = useState('') const ref = useRef(null) const dropRef = useRef(null) const searchRef = useRef(null) useEffect(() => { if (open && searchable && searchRef.current) searchRef.current.focus() }, [open, searchable]) useEffect(() => { const handleClick = (e) => { if (ref.current?.contains(e.target)) return if (dropRef.current?.contains(e.target)) return setOpen(false) } if (open) document.addEventListener('mousedown', handleClick) return () => document.removeEventListener('mousedown', handleClick) }, [open]) const selected = options.find(o => o.value === value) const filtered = searchable && search ? options.filter(o => o.label.toLowerCase().includes(search.toLowerCase())) : options const sm = size === 'sm' return (
{/* Trigger */} {/* Dropdown */} {open && ReactDOM.createPortal(
{ const r = ref.current?.getBoundingClientRect(); return r ? r.bottom + 4 : 0 })(), left: (() => { const r = ref.current?.getBoundingClientRect(); return r ? r.left : 0 })(), width: (() => { const r = ref.current?.getBoundingClientRect(); return r ? r.width : 200 })(), zIndex: 99999, background: 'var(--bg-card)', backdropFilter: 'blur(24px) saturate(180%)', WebkitBackdropFilter: 'blur(24px) saturate(180%)', border: '1px solid var(--border-primary)', borderRadius: 10, boxShadow: '0 8px 32px rgba(0,0,0,0.12)', overflow: 'hidden', animation: 'selectIn 0.15s ease-out', }}> {/* Search */} {searchable && (
setSearch(e.target.value)} placeholder="..." style={{ width: '100%', border: '1px solid var(--border-secondary)', borderRadius: 6, padding: '5px 8px', fontSize: 12, outline: 'none', fontFamily: 'inherit', background: 'var(--bg-secondary)', color: 'var(--text-primary)', boxSizing: 'border-box', }} />
)} {/* Options */}
{filtered.length === 0 ? (
) : ( filtered.map(option => { const isSelected = option.value === value return ( ) }) )}
, document.body )}
) }