import React, { ReactNode, useState, useMemo } from 'react'; import { Stack, Text, Popover, Box, Checkbox, Group, TextInput } from '@mantine/core'; import UnfoldMoreIcon from '@mui/icons-material/UnfoldMore'; import SearchIcon from '@mui/icons-material/Search'; export interface DropdownItem { value: string; name: string; leftIcon?: ReactNode; disabled?: boolean; } export interface DropdownListWithFooterProps { // Value and onChange - support both single and multi-select value: string | string[]; onChange: (value: string | string[]) => void; // Items and display items: DropdownItem[]; placeholder?: string; disabled?: boolean; // Labels and headers label?: string; header?: ReactNode; footer?: ReactNode; // Behavior multiSelect?: boolean; searchable?: boolean; maxHeight?: number; // Styling className?: string; dropdownClassName?: string; // Popover props position?: 'top' | 'bottom' | 'left' | 'right'; withArrow?: boolean; width?: 'target' | number; } const DropdownListWithFooter: React.FC = ({ value, onChange, items, placeholder = 'Select option', disabled = false, label, header, footer, multiSelect = false, searchable = false, maxHeight = 300, className = '', dropdownClassName = '', position = 'bottom', withArrow = false, width = 'target' }) => { const [searchTerm, setSearchTerm] = useState(''); const isMultiValue = Array.isArray(value); const selectedValues = isMultiValue ? value : (value ? [value] : []); // Filter items based on search term const filteredItems = useMemo(() => { if (!searchable || !searchTerm.trim()) { return items; } return items.filter(item => item.name.toLowerCase().includes(searchTerm.toLowerCase()) ); }, [items, searchTerm, searchable]); const handleItemClick = (itemValue: string) => { if (multiSelect) { const newSelection = selectedValues.includes(itemValue) ? selectedValues.filter(v => v !== itemValue) : [...selectedValues, itemValue]; onChange(newSelection); } else { onChange(itemValue); } }; const getDisplayText = () => { if (selectedValues.length === 0) { return placeholder; } else if (selectedValues.length === 1) { const selectedItem = items.find(item => item.value === selectedValues[0]); return selectedItem?.name || selectedValues[0]; } else { return `${selectedValues.length} selected`; } }; const handleSearchChange = (event: React.ChangeEvent) => { setSearchTerm(event.currentTarget.value); }; return ( {label && ( {label} )} searchable && setSearchTerm('')} > {getDisplayText()} {header && ( {header} )} {searchable && ( } size="sm" style={{ width: '100%' }} /> )} {filteredItems.length === 0 ? ( {searchable && searchTerm ? 'No results found' : 'No items available'} ) : ( filteredItems.map((item) => ( !item.disabled && handleItemClick(item.value)} style={{ padding: '8px 12px', cursor: item.disabled ? 'not-allowed' : 'pointer', borderRadius: 'var(--mantine-radius-sm)', opacity: item.disabled ? 0.5 : 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }} onMouseEnter={(e) => { if (!item.disabled) { e.currentTarget.style.backgroundColor = 'light-dark(var(--mantine-color-gray-0), var(--mantine-color-dark-5))'; } }} onMouseLeave={(e) => { e.currentTarget.style.backgroundColor = 'transparent'; }} > {item.leftIcon && ( {item.leftIcon} )} {item.name} {multiSelect && ( {}} // Handled by parent onClick size="sm" disabled={item.disabled} /> )} )) )} {footer && ( {footer} )} ); }; export default DropdownListWithFooter;