Animations FadeInDown.springify().damping(16) avec stagger 60ms entre sections du HomeScreen : - SearchHeader (delay 0) - SearchSection (delay 60) - RecentScans (delay 120) - FrequentDiseasesHorizontal (delay 200) - PracticalGuides (delay 280) Skeleton loading states : - LargeDiseaseCardCompactSkeleton : nouveau, simule la structure compact (badge + icon + title + desc + footer) — utilisé dans FrequentDiseasesHorizontal en remplacement de CarouselCardSkeleton - ScanListItemSkeleton : nouveau, simule image 64x64 + name + status pill + time + confidence tile — utilisé dans RecentScans - RecentScans / PracticalGuides : nouveau style cardLoading sans shadow/elevation Android (qui ne respecte pas l'opacité de FadeInDown → flash "rectangle blanc + ombre" pendant l'anim). iOS shadow conservé. isLoading propagé depuis useDiseases / useGuides / useHistory vers les sections concernées. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
73 lines
2 KiB
TypeScript
73 lines
2 KiB
TypeScript
import { View, StyleSheet, Platform } from "react-native";
|
|
import { useNavigation } from "@react-navigation/native";
|
|
import type { NativeStackNavigationProp } from "@react-navigation/native-stack";
|
|
|
|
import GuideListItem from "@/components/ui/GuideListItem";
|
|
import { GuideListItemSkeleton } from "@/components/ui/Skeleton";
|
|
import type { Guide } from "@/data/guides";
|
|
import type { RootStackParamList } from "@/types/navigation";
|
|
|
|
type Nav = NativeStackNavigationProp<RootStackParamList>;
|
|
|
|
interface PracticalGuidesProps {
|
|
guides: Guide[];
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
export default function PracticalGuides({ guides, isLoading }: PracticalGuidesProps) {
|
|
const navigation = useNavigation<Nav>();
|
|
const items = guides.slice(0, 3);
|
|
|
|
if (isLoading && items.length === 0) {
|
|
return (
|
|
<View style={styles.cardLoading}>
|
|
<GuideListItemSkeleton />
|
|
<GuideListItemSkeleton />
|
|
<GuideListItemSkeleton />
|
|
</View>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<View style={styles.card}>
|
|
{items.map((guide, index) => (
|
|
<GuideListItem
|
|
key={guide.id}
|
|
guide={guide}
|
|
onPress={() => navigation.navigate("GuideDetail", { guideId: guide.id })}
|
|
showSeparator={index < items.length - 1}
|
|
index={index}
|
|
/>
|
|
))}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
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 },
|
|
}),
|
|
},
|
|
// Loading: pas de shadow / elevation → évite le flash "rectangle blanc + ombre"
|
|
// sur Android avant que les skeletons ne fadent in via FadeInDown du parent.
|
|
cardLoading: {
|
|
backgroundColor: "#FFFFFF",
|
|
borderRadius: 16,
|
|
overflow: "hidden",
|
|
borderWidth: 1,
|
|
borderColor: "#F0F0F0",
|
|
},
|
|
});
|