import React, { useRef, useEffect } from 'react'; import { View, TextInput, TouchableOpacity, Text, StyleSheet, Animated } from 'react-native'; import { MaterialIcons } from '@expo/vector-icons'; import type { ComponentProps } from 'react'; type IconName = ComponentProps['name']; export interface SearchFilters { name: boolean; ocrText: boolean; } export type MediaFilter = 'all' | 'documents' | 'photos-videos'; interface SearchBarProps { query: string; onQueryChange: (q: string) => void; onClear: () => void; filters: SearchFilters; onFiltersChange: (f: SearchFilters) => void; mediaFilter: MediaFilter; onMediaFilterChange: (f: MediaFilter) => void; bottomPadding?: number; } const FILTER_OPTIONS: { key: keyof SearchFilters; label: string; icon: IconName }[] = [ { key: 'name', label: 'Nom', icon: 'drive-file-rename-outline' }, { key: 'ocrText', label: 'Texte OCR', icon: 'document-scanner' }, ]; const MEDIA_OPTIONS: { key: MediaFilter; label: string; icon: IconName }[] = [ { key: 'all', label: 'Tous', icon: 'filter-none' }, { key: 'documents', label: 'Documents', icon: 'description' }, { key: 'photos-videos', label: 'Photos', icon: 'photo-library' }, ]; const MEDIA_ICONS: Record = { all: 'filter-none', documents: 'description', 'photos-videos': 'photo-library', }; const MEDIA_LABELS: Record = { all: 'Tous', documents: 'Documents', 'photos-videos': 'Photos', }; function cycleMediaFilter(current: MediaFilter): MediaFilter { if (current === 'all') return 'documents'; if (current === 'documents') return 'photos-videos'; return 'all'; } export function SearchBar({ query, onQueryChange, onClear, filters, onFiltersChange, mediaFilter, onMediaFilterChange, bottomPadding = 0 }: SearchBarProps) { const animatedHeight = useRef(new Animated.Value(0)).current; const [panelOpen, setPanelOpen] = React.useState(false); useEffect(() => { Animated.spring(animatedHeight, { toValue: panelOpen ? 1 : 0, useNativeDriver: false, tension: 60, friction: 8, }).start(); }, [panelOpen]); const panelMaxHeight = animatedHeight.interpolate({ inputRange: [0, 1], outputRange: [0, 120], }); const toggleFilter = (key: keyof SearchFilters) => { onFiltersChange({ ...filters, [key]: !filters[key] }); }; const hasActiveFilter = filters.name || filters.ocrText; return ( {query.length > 0 && ( )} setPanelOpen(!panelOpen)} > onMediaFilterChange(cycleMediaFilter(mediaFilter))} > {FILTER_OPTIONS.map((opt) => ( toggleFilter(opt.key)} > {opt.label} ))} {MEDIA_OPTIONS.map((opt) => ( onMediaFilterChange(opt.key)} > {opt.label} ))} ); } const styles = StyleSheet.create({ wrapper: { backgroundColor: '#fff', borderTopWidth: 1, borderTopColor: '#e0e0e0', }, inputRow: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 12, paddingVertical: 10, gap: 8, }, inputContainer: { flex: 1, flexDirection: 'row', alignItems: 'center', backgroundColor: '#f5f5f5', borderRadius: 10, paddingHorizontal: 10, }, searchIcon: { marginRight: 6, }, input: { flex: 1, paddingVertical: 8, fontSize: 14, color: '#333', }, clearBtn: { padding: 4, }, iconBtn: { width: 40, height: 40, borderRadius: 10, borderWidth: 1, borderColor: '#1976D2', justifyContent: 'center', alignItems: 'center', backgroundColor: '#fff', }, iconBtnActive: { backgroundColor: '#1976D2', }, filterPanel: { flexDirection: 'row', flexWrap: 'wrap', paddingHorizontal: 12, paddingBottom: 10, gap: 8, overflow: 'hidden', }, mediaDivider: { width: '100%', height: 1, backgroundColor: '#e0e0e0', marginVertical: 4, }, filterChip: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 12, paddingVertical: 6, borderRadius: 16, borderWidth: 1, borderColor: '#1976D2', gap: 4, }, filterChipActive: { backgroundColor: '#1976D2', }, filterChipText: { fontSize: 12, color: '#1976D2', }, filterChipTextActive: { color: '#fff', }, });