qualifier / frontend /src /components /ui /select.jsx
Sethu0330's picture
Add FTL RFQ Qualifier agent + full SALES CRM app for HF Spaces testing
005df22 unverified
Raw History Blame Contribute Delete
5.14 kB
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 (
<SelectContext.Provider
value={{
value: currentValue,
onValueChange: handleValueChange,
isOpen,
setIsOpen,
triggerRef,
menuRef,
menuPos,
}}
>
<div className="relative" {...props}>
{children}
</div>
</SelectContext.Provider>
)
}
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 (
<button
ref={setRefs}
type="button"
onClick={() => context?.setIsOpen(!context.isOpen)}
className={cn(
"flex h-10 w-full items-center justify-between rounded-md border border-slate-200 bg-white px-3 py-2 text-sm ring-offset-white placeholder:text-slate-500 focus:outline-none focus:ring-2 focus:ring-slate-950 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
{children}
<ChevronDown className="h-4 w-4 opacity-50" />
</button>
)
})
SelectTrigger.displayName = "SelectTrigger"
const SelectValue = ({ placeholder, ...props }) => {
const context = React.useContext(SelectContext)
const displayValue = context?.value || placeholder
return <span {...props}>{displayValue}</span>
}
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(
<div
ref={setRefs}
style={style}
className={cn(
"rounded-md border border-slate-200 bg-white text-slate-950 shadow-lg",
"max-h-[min(70vh,22rem)] overflow-y-auto overflow-x-hidden",
className
)}
{...props}
>
<div className="p-1">{children}</div>
</div>,
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 (
<div
ref={ref}
onClick={() => 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}
</div>
)
})
SelectItem.displayName = "SelectItem"
export { Select, SelectTrigger, SelectValue, SelectContent, SelectItem }