"use client" import * as React from "react" import { Select as SelectPrimitive } from "@base-ui/react/select" import { cn } from "@/lib/utils" import { HugeiconsIcon } from "@hugeicons/react" import { UnfoldMoreIcon, Tick02Icon, ArrowUp01Icon, ArrowDown01Icon, Search01Icon } from "@hugeicons/core-free-icons" const SEARCH_THRESHOLD = 7 function collectSelectItems(children: React.ReactNode): Array<{ value: unknown; label: string }> { const items: Array<{ value: unknown; label: string }> = [] React.Children.forEach(children, (child) => { if (!React.isValidElement<{ value?: unknown; label?: string; children?: React.ReactNode }>(child)) { return } if ("value" in child.props) { items.push({ value: child.props.value ?? null, label: child.props.label ?? getItemLabel(child.props.children) ?? String(child.props.value ?? ""), }) } if (child.props.children) { items.push(...collectSelectItems(child.props.children)) } }) return items } function Select({ children, items, ...props }: SelectPrimitive.Root.Props) { const derivedItems = React.useMemo( () => (items == null ? collectSelectItems(children) : items), [children, items] ) return ( {children} ) } function getItemLabel(children: React.ReactNode): string | undefined { if (typeof children === "string" || typeof children === "number") { return String(children) } if (Array.isArray(children)) { const parts = children .map(getItemLabel) .filter((part): part is string => Boolean(part)) return parts.length > 0 ? parts.join(" ") : undefined } if (React.isValidElement<{ children?: React.ReactNode }>(children)) { return getItemLabel(children.props.children) } return undefined } function filterSelectChildren(children: React.ReactNode, query: string): React.ReactNode { const normalizedQuery = query.trim().toLowerCase() if (!normalizedQuery) return children const filtered = React.Children.map(children, (child) => { if (!React.isValidElement<{ value?: unknown; label?: string; children?: React.ReactNode }>(child)) { return child } if ("value" in child.props) { const label = child.props.label ?? getItemLabel(child.props.children) ?? "" return label.toLowerCase().includes(normalizedQuery) ? child : null } if (child.props.children) { const filteredChildren = filterSelectChildren(child.props.children, query) if (React.Children.count(filteredChildren) === 0) return null return React.cloneElement(child, undefined, filteredChildren) } return child }) return filtered } function SelectSearchInput({ query, onQueryChange, }: { query: string onQueryChange: (value: string) => void }) { return (
onQueryChange(event.target.value)} onKeyDown={(event) => event.stopPropagation()} placeholder="Search..." className="w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground" />
) } function SelectGroup({ className, ...props }: SelectPrimitive.Group.Props) { return ( ) } function SelectValue({ className, ...props }: SelectPrimitive.Value.Props) { return ( ) } function SelectTrigger({ className, size = "default", children, ...props }: SelectPrimitive.Trigger.Props & { size?: "sm" | "default" }) { return ( {children} } /> ) } function SelectContent({ className, children, side = "bottom", sideOffset = 4, align = "center", alignOffset = 0, alignItemWithTrigger = false, collisionAvoidance = { side: "shift", align: "shift", fallbackAxisSide: "none" }, ...props }: SelectPrimitive.Popup.Props & Pick< SelectPrimitive.Positioner.Props, "align" | "alignOffset" | "side" | "sideOffset" | "alignItemWithTrigger" | "collisionAvoidance" >) { const [query, setQuery] = React.useState("") const itemCount = React.useMemo(() => collectSelectItems(children).length, [children]) const showSearch = itemCount > SEARCH_THRESHOLD const filteredChildren = showSearch ? filterSelectChildren(children, query) : children return ( {showSearch && } {React.Children.count(filteredChildren) === 0 ? (
No results found.
) : ( filteredChildren )}
) } function SelectLabel({ className, ...props }: SelectPrimitive.GroupLabel.Props) { return ( ) } function SelectItem({ className, children, ...props }: SelectPrimitive.Item.Props) { const label = props.label ?? getItemLabel(children) return ( } > {children} ) } function SelectSeparator({ className, ...props }: SelectPrimitive.Separator.Props) { return ( ) } function SelectScrollUpButton({ className, ...props }: React.ComponentProps) { return ( ) } function SelectScrollDownButton({ className, ...props }: React.ComponentProps) { return ( ) } export { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, }