import React, { useRef, useState } from 'react'; import { Select, SelectArrow, SelectItem, SelectItemCheck, SelectLabel, SelectPopover, SelectProvider, } from '@ariakit/react'; import './AnimatePopover.css'; import { cn } from '~/utils'; type MultiSelectItem = T | { label: string; value: T }; function getItemValue(item: MultiSelectItem): T { return typeof item === 'string' ? item : item.value; } function getItemLabel(item: MultiSelectItem): string { return typeof item === 'string' ? item : item.label; } interface MultiSelectProps { items: MultiSelectItem[]; label?: string; placeholder?: string; onSelectedValuesChange?: (values: T[]) => void; renderSelectedValues?: ( values: T[], placeholder?: string, items?: MultiSelectItem[], ) => React.ReactNode; className?: string; itemClassName?: string; labelClassName?: string; selectClassName?: string; selectIcon?: React.ReactNode; popoverClassName?: string; selectItemsClassName?: string; selectedValues: T[]; setSelectedValues: (values: T[]) => void; renderItemContent?: ( value: T, defaultContent: React.ReactNode, isSelected: boolean, ) => React.ReactNode; } function defaultRender( values: T[], placeholder?: string, items?: MultiSelectItem[], ) { if (values.length === 0) { return placeholder || 'Select...'; } if (values.length === 1) { // Find the item to get its label if (items) { const item = items.find((item) => getItemValue(item) === values[0]); if (item) { return getItemLabel(item); } } return values[0]; } return `${values.length} items selected`; } export default function MultiSelect({ items, label, placeholder = 'Select...', onSelectedValuesChange, renderSelectedValues = defaultRender, className, selectIcon, itemClassName, labelClassName, selectClassName, popoverClassName, selectItemsClassName, selectedValues = [], setSelectedValues, renderItemContent, }: MultiSelectProps) { const selectRef = useRef(null); const [isPopoverOpen, setIsPopoverOpen] = useState(false); const handleValueChange = (values: T[]) => { setSelectedValues(values); if (onSelectedValuesChange) { onSelectedValuesChange(values); } }; return (
{label && ( {label} )} {items.map((item) => { const value = getItemValue(item); const label = getItemLabel(item); const defaultContent = ( <> {label} ); const isCurrentItemSelected = selectedValues.includes(value); return ( {renderItemContent ? (renderItemContent( value, defaultContent, isCurrentItemSelected, ) as React.JSX.Element) : (defaultContent as React.JSX.Element)} ); })}
); }