import { useState, useEffect } from "react"; import { View, Text, ScrollView, Image, TouchableOpacity } from "react-native"; import { TextInput, Checkbox } from "react-native-paper"; import { Ionicons } from "@expo/vector-icons"; export interface DropdownItem { id: string; label: string; subtitle?: string; description?: string; imageUrl?: string; initials?: string; } interface SearchableDropdownProps { items: DropdownItem[]; selectedIds?: string | string[]; onSelectionChange: (value: string | string[] | undefined) => void; label?: string; placeholder?: string; error?: string; onSearchChange?: (query: string) => void; mode?: "single" | "multi"; } export const SearchableDropdown = ({ items, selectedIds: selectedIdsProp, onSelectionChange, label = "Select Items", placeholder = "Search...", error, onSearchChange, mode = "single", }: SearchableDropdownProps) => { const [searchQuery, setSearchQuery] = useState(""); const [isOpen, setIsOpen] = useState(false); const [allSelectedItems, setAllSelectedItems] = useState([]); const selectedIds = Array.isArray(selectedIdsProp) ? selectedIdsProp : selectedIdsProp ? [selectedIdsProp] : []; const selectedItems = items.filter((item) => selectedIds.includes(item.id)); // Update allSelectedItems whenever selection changes // This preserves previously selected items even when they're not in the current search results useEffect(() => { // If no items selected, clear all if (selectedIds.length === 0) { setAllSelectedItems([]); return; } // Add newly selected items from current search results if (selectedItems.length > 0) { setAllSelectedItems((prev) => { const itemsMap = new Map(prev.map((item) => [item.id, item])); selectedItems.forEach((item) => { itemsMap.set(item.id, item); }); // Filter to only keep items that are still selected return Array.from(itemsMap.values()).filter((item) => selectedIds.includes(item.id) ); }); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedIds.length, selectedIds.join(",")]); const handleToggleItem = (itemId: string) => { if (mode === "single") { onSelectionChange(itemId); setIsOpen(false); setSearchQuery(""); } else { const isSelected = selectedIds.includes(itemId); const newSelection = isSelected ? selectedIds.filter((id) => id !== itemId) : [...selectedIds, itemId]; onSelectionChange(newSelection); // Keep dropdown open in multi-select mode } }; const handleRemoveItem = (itemId: string) => { if (mode === "single") { onSelectionChange(undefined); } else { onSelectionChange(selectedIds.filter((id) => id !== itemId)); } }; const handleSearchChange = (query: string) => { setSearchQuery(query); if (onSearchChange) { onSearchChange(query); } }; const handleClearSearch = () => { setSearchQuery(""); if (onSearchChange) onSearchChange(""); }; return ( {label} {/* Selected Item Display (Single-select) */} {mode === "single" && selectedItems.length > 0 && !isOpen ? ( setIsOpen(true)} > {selectedItems[0].imageUrl ? ( ) : ( {selectedItems[0].initials || "?"} )} {selectedItems[0].label} {selectedItems[0].subtitle && ( {selectedItems[0].subtitle} )} { handleRemoveItem(selectedItems[0].id); setIsOpen(true); }} className="ml-2 p-1" hitSlop={{ top: 8, right: 8, bottom: 8, left: 8 }} > ) : null} {/* Search Input */} {((mode === "single" && selectedItems.length === 0) || mode === "multi" || isOpen) && ( setIsOpen(true)} onBlur={() => setTimeout(() => setIsOpen(false), 200)} right={ searchQuery ? ( ) : null } /> )} {/* Dropdown List - Only show when search query has 2+ characters */} {isOpen && searchQuery.length >= 2 && ( {items.length === 0 ? ( No items found for "{searchQuery}" ) : ( items.map((item) => ( handleToggleItem(item.id)} > {mode === "multi" && ( handleToggleItem(item.id)} /> )} {item.imageUrl ? ( ) : ( {item.initials || "?"} )} {item.label} {item.subtitle && ( {item.subtitle} )} {item.description && ( {item.description} )} )) )} )} {/* Selected Items Bubbles (Multi-select) - Show below dropdown */} {mode === "multi" && allSelectedItems.length > 0 && ( {allSelectedItems.map((item) => ( {item.imageUrl ? ( ) : ( {item.initials || "?"} )} {item.label} handleRemoveItem(item.id)} className="p-1" > ))} )} {error && {error}} ); };