From 3781b1c0f4b9577dcf7905edd9545486891e7e3b Mon Sep 17 00:00:00 2001 From: Yanis Date: Fri, 1 May 2026 00:02:28 +0200 Subject: [PATCH] feat(ui): shared SearchBar reused on Home, Map and MyPlants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extrait la barre de recherche en composant partagé (components/shared/SearchBar.tsx) avec props placeholder/value/onChangeText/ showFilter. - Home (SearchSection) : utilise le composant partagé - Map (FloatingSearch) : remplace l'input custom + ajuste les chips (border primary, font-size 15→12, MapPin couleur primary) - MyPlants : remplace l'input custom + son bouton clear Bonus : SearchBar gère proprement le clavier Android via numberOfLines={1}, multiline={false}, scrollEnabled={false}, lineHeight 20 + textAlignVertical center + includeFontPadding false → le placeholder ne wrappe plus sur 2 lignes. Co-Authored-By: Claude Opus 4.7 (1M context) --- VinEye/src/components/home/SearchSection.tsx | 66 +-------- VinEye/src/components/map/FloatingSearch.tsx | 135 +++++-------------- VinEye/src/components/shared/SearchBar.tsx | 97 +++++++++++++ VinEye/src/screens/MyPlantsScreen.tsx | 53 +------- 4 files changed, 142 insertions(+), 209 deletions(-) create mode 100644 VinEye/src/components/shared/SearchBar.tsx diff --git a/VinEye/src/components/home/SearchSection.tsx b/VinEye/src/components/home/SearchSection.tsx index 0523de2..1980403 100644 --- a/VinEye/src/components/home/SearchSection.tsx +++ b/VinEye/src/components/home/SearchSection.tsx @@ -1,38 +1,14 @@ -import { View, TextInput, StyleSheet, TouchableOpacity } from "react-native"; +import { View, StyleSheet } from "react-native"; import { useTranslation } from "react-i18next"; -import { Ionicons } from "@expo/vector-icons"; -import { colors } from "@/theme/colors"; +import SearchBar from "@/components/shared/SearchBar"; export default function SearchSection() { const { t } = useTranslation(); return ( - - {/* Icône de recherche */} - - - {/* Champ de saisie */} - - - {/* Optionnel: Petit séparateur + Icône Filtre pour le look Premium */} - - - - - + ); } @@ -43,38 +19,4 @@ const styles = StyleSheet.create({ paddingBottom: 16, paddingTop: 4, }, - searchWrapper: { - flexDirection: "row", - alignItems: "center", - backgroundColor: "#F5F7F9", // Un gris bleuté plus frais que neutral-200 - borderRadius: 100, // On garde ton style "full" - paddingHorizontal: 16, - height: 52, // Hauteur standardisée pour le tactile - borderWidth: 1, - borderColor: "#EAECEF", - }, - searchIcon: { - marginRight: 10, - }, - input: { - flex: 1, - fontSize: 15, - - fontWeight: "500", - color: colors.neutral[900], - // Évite le décalage de texte sur Android - paddingVertical: 0, - height: "100%", - }, - filterButton: { - flexDirection: "row", - alignItems: "center", - paddingLeft: 12, - }, - divider: { - width: 1, - height: 20, - backgroundColor: "#E2E4E7", - marginRight: 12, - }, -}); \ No newline at end of file +}); diff --git a/VinEye/src/components/map/FloatingSearch.tsx b/VinEye/src/components/map/FloatingSearch.tsx index feae234..8df381e 100644 --- a/VinEye/src/components/map/FloatingSearch.tsx +++ b/VinEye/src/components/map/FloatingSearch.tsx @@ -1,16 +1,10 @@ import { useState } from "react"; -import { - View, - TextInput, - ScrollView, - Pressable, - StyleSheet, - Image, -} from "react-native"; +import { View, ScrollView, Pressable, StyleSheet } from "react-native"; import { useTranslation } from "react-i18next"; -import { Search, MapPin } from "lucide-react-native"; +import { MapPin } from "lucide-react-native"; import { Text } from "@/components/ui/text"; +import SearchBar from "@/components/shared/SearchBar"; import { HeaderActionButtons } from "@/components/shared/HeaderActionButtons"; import { colors } from "@/theme/colors"; import { WINE_REGIONS } from "@/data/wineRegions"; @@ -43,24 +37,14 @@ export function FloatingSearch({ ]; return ( - - - - - + + + - {/* - - */} @@ -68,7 +52,7 @@ export function FloatingSearch({ {filters.map((filter) => { const isActive = activeFilter === filter.id; @@ -76,17 +60,36 @@ export function FloatingSearch({ onFilterPress?.(filter.id)} - style={[styles.chip, isActive && styles.chipActive]} + className="flex-row items-center gap-1.5 px-3 py-1.5 rounded-full border" + style={[ + styles.chipShadow, + isActive + ? { + backgroundColor: colors.primary[800], + borderColor: colors.primary[800], + shadowOpacity: 0.12, + // elevation: 24, + } + : { + backgroundColor: "#FFFFFF", + borderColor: colors.primary[800], + }, + ]} > {filter.icon === "location" && ( )} {t(filter.labelKey)} @@ -99,83 +102,13 @@ export function FloatingSearch({ } const styles = StyleSheet.create({ - root: { + rootElevation: { elevation: 24, }, - searchRow: { - flexDirection: "row", - alignItems: "center", - gap: 10, - }, - searchBar: { - flex: 1, - flexDirection: "row", - alignItems: "center", - backgroundColor: "#FFFFFF", - borderRadius: 75, - paddingHorizontal: 16, - paddingVertical: 12, - gap: 12, - shadowColor: "#000", - shadowOffset: { width: 0, height: 4 }, - shadowOpacity: 0.12, - shadowRadius: 12, - elevation: 24, - borderWidth: 1, - borderColor: colors.neutral[200], - }, - input: { - flex: 1, - fontSize: 14, - color: colors.neutral[900], - padding: 0, - }, - logoWrap: { - width: 32, - height: 32, - borderRadius: 16, - overflow: "hidden", - borderWidth: 2, - borderColor: colors.primary[200], - }, - logo: { - width: "100%", - height: "100%", - }, - chipsRow: { - gap: 8, - paddingTop: 12, - paddingHorizontal: 2, - }, - chip: { - flexDirection: "row", - alignItems: "center", - gap: 6, - backgroundColor: "#FFFFFF", - paddingHorizontal: 20, - paddingVertical: 10, - borderRadius: 999, - borderWidth: 1, - borderColor: "#F0F0F0", + chipShadow: { shadowColor: "#000", shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.04, shadowRadius: 6, - elevation: 24, - }, - chipActive: { - backgroundColor: colors.primary[800], - borderColor: colors.primary[800], - shadowOpacity: 0.12, - elevation: 24, - }, - chipText: { - fontSize: 13, - fontWeight: "500", - color: "#2D2D2D", - }, - chipTextActive: { - color: "#FFFFFF", - fontWeight: "600", }, }); diff --git a/VinEye/src/components/shared/SearchBar.tsx b/VinEye/src/components/shared/SearchBar.tsx new file mode 100644 index 0000000..b5a8be6 --- /dev/null +++ b/VinEye/src/components/shared/SearchBar.tsx @@ -0,0 +1,97 @@ +import { View, TextInput, TouchableOpacity, StyleSheet } from "react-native"; +import { Ionicons } from "@expo/vector-icons"; + +import { colors } from "@/theme/colors"; + +interface SearchBarProps { + placeholder?: string; + value?: string; + onChangeText?: (text: string) => void; + onFilterPress?: () => void; + showFilter?: boolean; +} + +export default function SearchBar({ + placeholder, + value, + onChangeText, + onFilterPress, + showFilter = true, +}: SearchBarProps) { + return ( + + + + + + {showFilter && ( + + + + + )} + + ); +} + +const styles = StyleSheet.create({ + searchWrapper: { + flexDirection: "row", + alignItems: "center", + backgroundColor: "#fff", + borderRadius: 100, + paddingHorizontal: 16, + height: 52, + borderWidth: 1, + borderColor: "#EAECEF", + }, + searchIcon: { + marginRight: 10, + }, + input: { + flex: 1, + fontSize: 15, + lineHeight: 20, + fontWeight: "500", + color: colors.neutral[900], + paddingVertical: 0, + paddingHorizontal: 0, + textAlignVertical: "center", + includeFontPadding: false, + }, + filterButton: { + flexDirection: "row", + alignItems: "center", + paddingLeft: 12, + }, + divider: { + width: 1, + height: 20, + backgroundColor: "#E2E4E7", + marginRight: 12, + }, +}); diff --git a/VinEye/src/screens/MyPlantsScreen.tsx b/VinEye/src/screens/MyPlantsScreen.tsx index ae9ff6e..3627f82 100644 --- a/VinEye/src/screens/MyPlantsScreen.tsx +++ b/VinEye/src/screens/MyPlantsScreen.tsx @@ -2,7 +2,6 @@ import { useState, useMemo, useCallback } from 'react'; import { View, FlatList, - TextInput, TouchableOpacity, Alert, StyleSheet, @@ -12,12 +11,12 @@ import { useNavigation, useFocusEffect } from '@react-navigation/native'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import { useTranslation } from 'react-i18next'; import { Image } from 'expo-image'; -import { Ionicons } from '@expo/vector-icons'; -import { Search, ScanLine } from 'lucide-react-native'; +import { ScanLine } from 'lucide-react-native'; import { Text } from '@/components/ui/text'; import { DateGroupAccordion } from '@/components/my-plants/DateGroupAccordion'; import { HeaderActionButtons } from '@/components/shared/HeaderActionButtons'; +import SearchBar from '@/components/shared/SearchBar'; import { useHistory } from '@/hooks/useHistory'; import { getCepageById } from '@/utils/cepages'; import { groupScansByDate } from '@/utils/dateGrouping'; @@ -142,27 +141,11 @@ export default function MyPlantsScreen() { {/* Search bar */} - - - - {searchQuery.length > 0 && ( - setSearchQuery('')} - style={styles.clearBtn} - activeOpacity={0.7} - > - - - )} - + {/* Content */} @@ -225,28 +208,6 @@ const styles = StyleSheet.create({ paddingHorizontal: 20, paddingVertical: 12, }, - searchWrapper: { - flexDirection: 'row', - alignItems: 'center', - backgroundColor: '#F5F7F9', - borderRadius: 100, - paddingHorizontal: 16, - height: 48, - borderWidth: 1, - borderColor: '#EAECEF', - gap: 10, - }, - searchInput: { - flex: 1, - fontSize: 15, - fontWeight: '500', - color: colors.neutral[900], - paddingVertical: 0, - height: '100%', - }, - clearBtn: { - padding: 4, - }, // List listContent: { paddingBottom: 100,