Files
ipcostruzioni_app/components/PlaceFilter.tsx
2026-07-31 16:53:16 +02:00

155 lines
7.8 KiB
TypeScript

import { Place } from '@/types/types';
import { ChevronDown, Search, X } from 'lucide-react-native';
import React, { useState, useEffect, useMemo } from 'react';
import { Modal, FlatList, Text, TextInput, TouchableOpacity, View, Keyboard, KeyboardEvent, Platform } from 'react-native';
interface PlaceFilterProps {
places: Place[];
selectedPlaceId: any;
onPlaceSelect: (placeId: any) => void;
textColor?: string; // Optional prop for text color
}
export default function PlaceFilter({ places, selectedPlaceId, onPlaceSelect, textColor }: PlaceFilterProps) {
const [showPicker, setShowPicker] = useState(false);
const [searchQuery, setSearchQuery] = useState('');
const [keyboardHeight, setKeyboardHeight] = useState(0);
// Keyboard height listener
useEffect(() => {
const showSubscription = Keyboard.addListener(
Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow',
(e: KeyboardEvent) => setKeyboardHeight(e.endCoordinates.height)
);
const hideSubscription = Keyboard.addListener(
Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide',
() => setKeyboardHeight(0)
);
return () => {
showSubscription.remove();
hideSubscription.remove();
};
}, []);
// Reset search query every time modal is opened
useEffect(() => {
if (showPicker) {
setSearchQuery('');
}
}, [showPicker]);
const filteredPlaces = useMemo(() => {
if (!showPicker) return [];
if (!searchQuery.trim()) return places;
const lowerQuery = searchQuery.toLowerCase();
return places.filter(p => p.label.toLowerCase().includes(lowerQuery));
}, [places, searchQuery, showPicker]);
const selectedPlaceLabel = selectedPlaceId
? places.find(p => p.id === selectedPlaceId)?.label || 'Cantiere Selezionato'
: 'Tutti i cantieri';
return (
<View className="mb-8">
<Text className={`text-lg font-bold ${textColor || 'text-gray-700'} mb-3`}>Cantiere</Text>
<View className="flex-row items-center gap-3">
<TouchableOpacity
onPress={() => setShowPicker(true)}
className="flex-1 flex-row items-center justify-between bg-white px-5 py-4 rounded-2xl border border-gray-200 shadow-sm"
>
<Text className={`font-medium text-base flex-1 mr-2 ${selectedPlaceId ? 'text-gray-800' : 'text-gray-500'}`} numberOfLines={1}>
{selectedPlaceLabel}
</Text>
<ChevronDown size={20} color="#6b7280" pointerEvents="none" />
</TouchableOpacity>
{selectedPlaceId !== null && (
<TouchableOpacity
onPress={() => onPlaceSelect(null)}
className="bg-gray-50 p-4 rounded-2xl border border-gray-200 shadow-sm justify-center items-center"
>
<X size={22} color="#4b5563" pointerEvents="none" />
</TouchableOpacity>
)}
</View>
{/* Nested Place Picker Modal */}
<Modal visible={showPicker} transparent={true} animationType="fade" onRequestClose={() => setShowPicker(false)}>
<TouchableOpacity
activeOpacity={1}
onPress={() => setShowPicker(false)}
className="flex-1 bg-black/50 justify-end"
style={{ paddingBottom: keyboardHeight }}
>
<View className="bg-white rounded-t-3xl p-5 max-h-[70%]" onStartShouldSetResponder={() => true}>
<View className="flex-row justify-between items-center mb-4 border-b border-gray-100 pb-4">
<Text className="text-xl font-bold text-gray-800">Seleziona Cantiere</Text>
<TouchableOpacity onPress={() => setShowPicker(false)} className="p-2 bg-gray-100 rounded-full active:bg-gray-200">
<X size={20} color="#4b5563" pointerEvents="none" />
</TouchableOpacity>
</View>
{/* Search Bar */}
<View className="bg-gray-100 flex-row items-center px-4 py-3 rounded-2xl mb-4 border border-gray-200 focus:border-[#1071C2]">
<Search size={20} color="#9ca3af" />
<TextInput
className="flex-1 ml-3 text-base text-gray-800"
placeholder="Cerca cantiere..."
value={searchQuery}
onChangeText={setSearchQuery}
placeholderTextColor="#9ca3af"
autoCorrect={false}
/>
{searchQuery.length > 0 && (
<TouchableOpacity onPress={() => setSearchQuery('')} className="p-1">
<X size={18} color="#6b7280" />
</TouchableOpacity>
)}
</View>
<FlatList
showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingBottom: 30 }}
keyboardShouldPersistTaps="handled"
data={filteredPlaces}
keyExtractor={(item) => item.id.toString()}
ListHeaderComponent={() => (
searchQuery.trim() === '' ? (
<TouchableOpacity
className={`py-4 px-4 rounded-xl flex-row justify-between items-center mb-2 ${selectedPlaceId === null ? 'bg-blue-50' : ''}`}
onPress={() => { onPlaceSelect(null); setShowPicker(false); }}
>
<Text className={`text-lg ${selectedPlaceId === null ? 'font-bold text-[#1071C2]' : 'text-gray-700'}`}>
Tutti i cantieri
</Text>
</TouchableOpacity>
) : null
)}
ListEmptyComponent={() => (
<View className="py-8 items-center">
<Text className="text-gray-500 font-medium text-center">Nessun cantiere trovato per "{searchQuery}"</Text>
</View>
)}
initialNumToRender={15}
maxToRenderPerBatch={20}
windowSize={5}
removeClippedSubviews={true}
renderItem={({ item }) => (
<TouchableOpacity
className={`py-4 px-4 rounded-xl flex-row justify-between items-center mb-2 ${selectedPlaceId === item.id ? 'bg-blue-50' : ''}`}
onPress={() => { onPlaceSelect(item.id); setShowPicker(false); }}
>
<Text className={`text-lg ${selectedPlaceId === item.id ? 'font-bold text-[#1071C2]' : 'text-gray-700'}`}>
{item.label}
</Text>
</TouchableOpacity>
)}
/>
</View>
</TouchableOpacity>
</Modal>
</View>
);
}