feat(my-plants): grouped card style for date groups

Aligne le rendu des date groups sur le pattern PracticalGuides : items
encapsulés dans une card blanche rounded-16, séparés par une ligne grise
indentée.

- ScanListItem : nouvelles props grouped + showSeparator → désactive
  borderRadius/margins/border individuels et active la ligne séparatrice
  alignée sous le texte
- DateGroupAccordion : wrappe les ScanListItem dans une View card avec
  shadow iOS / elevation Android

Le même pattern est réutilisé par RecentScans (Home) — voir commit suivant.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Yanis 2026-05-01 00:02:40 +02:00
parent 3781b1c0f4
commit 4ebbc692ff
2 changed files with 57 additions and 13 deletions

View file

@ -1,5 +1,5 @@
import { useEffect } from 'react';
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';
import { View, TouchableOpacity, Text, StyleSheet, Platform } from 'react-native';
import Animated, {
useAnimatedStyle,
useSharedValue,
@ -63,15 +63,19 @@ export function DateGroupAccordion({
{/* Content */}
{isOpen && (
<View style={styles.content}>
{scans.map((scan) => (
<ScanListItem
key={scan.id}
scan={scan}
onPress={() => onPressScan(scan)}
onToggleFavorite={() => onToggleFavorite(scan.id)}
onDelete={() => onDeleteScan(scan.id)}
/>
))}
<View style={styles.card}>
{scans.map((scan, index) => (
<ScanListItem
key={scan.id}
scan={scan}
onPress={() => onPressScan(scan)}
onToggleFavorite={() => onToggleFavorite(scan.id)}
onDelete={() => onDeleteScan(scan.id)}
grouped
showSeparator={index < scans.length - 1}
/>
))}
</View>
</View>
)}
</View>
@ -107,7 +111,24 @@ const styles = StyleSheet.create({
color: '#8E8E93',
},
content: {
paddingTop: 4,
paddingTop: 8,
paddingBottom: 8,
paddingHorizontal: 20,
},
card: {
backgroundColor: '#FFFFFF',
borderRadius: 16,
overflow: 'hidden',
borderWidth: 1,
borderColor: '#F0F0F0',
...Platform.select({
ios: {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.04,
shadowRadius: 8,
},
android: { elevation: 2 },
}),
},
});

View file

@ -19,6 +19,8 @@ interface ScanListItemProps {
onPress: () => void;
onToggleFavorite: () => void;
onDelete: () => void;
grouped?: boolean;
showSeparator?: boolean;
}
const STATUS_FILL: Record<ScanStatus, string> = {
@ -46,7 +48,14 @@ function getPlantName(scan: ScanRecord, t: (key: string) => string): string {
return t('result.notVine');
}
export function ScanListItem({ scan, onPress, onToggleFavorite, onDelete }: ScanListItemProps) {
export function ScanListItem({
scan,
onPress,
onToggleFavorite,
onDelete,
grouped = false,
showSeparator = false,
}: ScanListItemProps) {
const { t } = useTranslation();
const swipeableRef = useRef<Swipeable>(null);
const isFav = scan.isFavorite === true;
@ -117,7 +126,7 @@ export function ScanListItem({ scan, onPress, onToggleFavorite, onDelete }: Scan
friction={2}
>
<TouchableOpacity
style={styles.container}
style={[styles.container, grouped && styles.containerGrouped]}
onPress={() => {
hapticLight();
onPress();
@ -160,6 +169,7 @@ export function ScanListItem({ scan, onPress, onToggleFavorite, onDelete }: Scan
)}
</View>
</TouchableOpacity>
{grouped && showSeparator && <View style={styles.separator} />}
</Swipeable>
);
}
@ -176,6 +186,19 @@ const styles = StyleSheet.create({
borderWidth: 1,
borderColor: '#F0F0F0',
},
containerGrouped: {
borderRadius: 0,
marginHorizontal: 0,
marginVertical: 0,
borderWidth: 0,
paddingVertical: 14,
paddingHorizontal: 16,
},
separator: {
height: 1,
backgroundColor: '#F0F0F0',
marginLeft: 92,
},
imageWrapper: {
width: 64,
height: 64,