import { useEffect, useMemo, useRef, useState } from 'react' import { useInfiniteQuery } from '@tanstack/react-query' import { Ic } from '@/icons' import { cn } from '@/lib/utils' import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' type SingleSelectProps = { label?: string items?: string[] value: string onChange: (value: string) => void placeholder?: string searchPlaceholder?: string emptyLabel?: string className?: string contentClassName?: string searchable?: boolean queryKey?: readonly unknown[] loadOptions?: (params: { query: string; page: number }) => Promise<{ items: string[]; nextPage?: number }> } export default function SingleSelect({ label, items = [], value, onChange, placeholder = 'انتخاب کنید…', searchPlaceholder = 'جستجو…', emptyLabel, className, contentClassName, searchable = true, queryKey, loadOptions, }: SingleSelectProps) { const [open, setOpen] = useState(false) const [query, setQuery] = useState('') const searchRef = useRef(null) const listRef = useRef(null) const infinite = useInfiniteQuery({ queryKey: [...(queryKey ?? ['single-select-options']), query], queryFn: ({ pageParam }) => loadOptions!({ query, page: pageParam }), initialPageParam: 1, getNextPageParam: (lastPage) => lastPage.nextPage, enabled: open && !!loadOptions, }) useEffect(() => { if (open && searchable) setTimeout(() => searchRef.current?.focus(), 50) }, [open, searchable]) const setPopoverOpen = (nextOpen: boolean) => { if (!nextOpen) setQuery('') setOpen(nextOpen) } const options = useMemo(() => { if (loadOptions) return infinite.data?.pages.flatMap((page) => page.items) ?? [] return query ? items.filter((item) => item.includes(query)) : items }, [items, query, loadOptions, infinite.data]) const renderedOptions = emptyLabel ? [emptyLabel, ...options] : options const selectedLabel = value || '' const select = (item: string) => { onChange(item === emptyLabel ? '' : item) setOpen(false) } const onListScroll = () => { const list = listRef.current if (!list || !loadOptions || !infinite.hasNextPage || infinite.isFetchingNextPage) return if (list.scrollTop + list.clientHeight >= list.scrollHeight - 24) infinite.fetchNextPage() } return (
{label &&

{label}

} {searchable && (
setQuery(e.target.value)} placeholder={searchPlaceholder} className="w-full rounded-md border border-border-soft bg-white/5 px-3 py-1.5 text-[12.5px] text-grey-2 outline-none transition-colors placeholder:text-grey-4 focus:border-red-mid/50" />
)}
{infinite.isLoading ? (

در حال بارگذاری…

) : renderedOptions.length === 0 ? (

نتیجه‌ای یافت نشد

) : ( renderedOptions.map((item) => { const active = item === selectedLabel || (item === emptyLabel && !selectedLabel) return ( ) }) )} {infinite.isFetchingNextPage && (
بارگذاری موارد بیشتر…
)}
) }