import { useCallback, useMemo, useState, } from 'react' import { useTranslation } from 'react-i18next' import { RiArrowDownSLine, RiCheckLine, } from '@remixicon/react' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger, } from '@/app/components/base/portal-to-follow-elem' import type { PortalToFollowElemOptions, } from '@/app/components/base/portal-to-follow-elem' import cn from '@/utils/classnames' export type Option = { label: string value: string } type SharedPureSelectProps = { options: Option[] containerProps?: PortalToFollowElemOptions & { open?: boolean onOpenChange?: (open: boolean) => void } triggerProps?: { className?: string }, popupProps?: { wrapperClassName?: string className?: string itemClassName?: string title?: string titleClassName?: string }, placeholder?: string disabled?: boolean triggerPopupSameWidth?: boolean } type SingleSelectProps = { multiple?: false value?: string onChange?: (value: string) => void } type MultiSelectProps = { multiple: true value?: string[] onChange?: (value: string[]) => void } export type PureSelectProps = SharedPureSelectProps & (SingleSelectProps | MultiSelectProps) const PureSelect = (props: PureSelectProps) => { const { options, containerProps, triggerProps, popupProps, placeholder, disabled, triggerPopupSameWidth, multiple, value, onChange, } = props const { t } = useTranslation() const { open, onOpenChange, placement, offset, } = containerProps || {} const { className: triggerClassName, } = triggerProps || {} const { wrapperClassName: popupWrapperClassName, className: popupClassName, itemClassName: popupItemClassName, title: popupTitle, titleClassName: popupTitleClassName, } = popupProps || {} const [localOpen, setLocalOpen] = useState(false) const mergedOpen = open ?? localOpen const handleOpenChange = useCallback((openValue: boolean) => { onOpenChange?.(openValue) setLocalOpen(openValue) }, [onOpenChange]) const triggerText = useMemo(() => { const placeholderText = placeholder || t('common.placeholder.select') if (multiple) return value?.length ? t('common.dynamicSelect.selected', { count: value.length }) : placeholderText return options.find(option => option.value === value)?.label || placeholderText }, [multiple, value, options, placeholder]) return ( !disabled && handleOpenChange(!mergedOpen)} asChild >
{triggerText}
{ popupTitle && (
{popupTitle}
) } { options.map(option => (
{ if (disabled) return if (multiple) { const currentValues = value ?? [] const nextValues = currentValues.includes(option.value) ? currentValues.filter(valueItem => valueItem !== option.value) : [...currentValues, option.value] onChange?.(nextValues) return } onChange?.(option.value) handleOpenChange(false) }} >
{option.label}
{ ( multiple ? (value ?? []).includes(option.value) : value === option.value ) && }
)) }
) } export default PureSelect