import * as React from "react" import { createPortal } from "react-dom" import { cn } from "@/lib/utils" import { ChevronDown } from "lucide-react" const SelectContext = React.createContext(null) const Select = ({ value, onValueChange, children, ...props }) => { const [isOpen, setIsOpen] = React.useState(false) const [internalValue, setInternalValue] = React.useState("") const [menuPos, setMenuPos] = React.useState({ top: 0, left: 0, width: 0 }) const triggerRef = React.useRef(null) const menuRef = React.useRef(null) const currentValue = value !== undefined ? value : internalValue const handleValueChange = (newValue) => { if (onValueChange) { onValueChange(newValue) } else { setInternalValue(newValue) } setIsOpen(false) } React.useLayoutEffect(() => { if (!isOpen || !triggerRef.current) return const r = triggerRef.current.getBoundingClientRect() setMenuPos({ top: r.bottom + 4, left: r.left, width: r.width }) }, [isOpen]) React.useEffect(() => { if (!isOpen) return const reposition = () => { if (!triggerRef.current) return const r = triggerRef.current.getBoundingClientRect() setMenuPos({ top: r.bottom + 4, left: r.left, width: r.width }) } window.addEventListener("scroll", reposition, true) window.addEventListener("resize", reposition) return () => { window.removeEventListener("scroll", reposition, true) window.removeEventListener("resize", reposition) } }, [isOpen]) React.useEffect(() => { if (!isOpen) return const onPointerDown = (e) => { const t = triggerRef.current const m = menuRef.current if (t?.contains(e.target) || m?.contains(e.target)) return setIsOpen(false) } document.addEventListener("pointerdown", onPointerDown) return () => document.removeEventListener("pointerdown", onPointerDown) }, [isOpen]) return (
{children}
) } const SelectTrigger = React.forwardRef(({ className, children, ...props }, ref) => { const context = React.useContext(SelectContext) const setRefs = (node) => { if (context?.triggerRef) context.triggerRef.current = node if (typeof ref === "function") ref(node) else if (ref) ref.current = node } return ( ) }) SelectTrigger.displayName = "SelectTrigger" const SelectValue = ({ placeholder, ...props }) => { const context = React.useContext(SelectContext) const displayValue = context?.value || placeholder return {displayValue} } const SelectContent = React.forwardRef(({ className, children, ...props }, ref) => { const context = React.useContext(SelectContext) const [mounted, setMounted] = React.useState(false) React.useEffect(() => { setMounted(true) }, []) if (!context?.isOpen || !mounted) return null const { top, left, width } = context.menuPos const style = { position: "fixed", top, left, minWidth: width > 0 ? width : undefined, zIndex: 10050, } const setRefs = (node) => { if (context?.menuRef) context.menuRef.current = node if (typeof ref === "function") ref(node) else if (ref) ref.current = node } return createPortal(
{children}
, document.body ) }) SelectContent.displayName = "SelectContent" const SelectItem = React.forwardRef(({ className, value, children, ...props }, ref) => { const context = React.useContext(SelectContext) const isSelected = context?.value === value return (
context?.onValueChange(value)} className={cn( "relative flex w-full cursor-pointer select-none items-center rounded-sm py-1.5 px-2 text-sm outline-none hover:bg-slate-100 focus:bg-slate-100", isSelected && "bg-slate-100", className )} {...props} > {children}
) }) SelectItem.displayName = "SelectItem" export { Select, SelectTrigger, SelectValue, SelectContent, SelectItem }