feat : début de EventCard
This commit is contained in:
@@ -26,6 +26,12 @@ export default function HomeTabScreen() {
|
||||
onPress={() => router.push('/switch-demo')}
|
||||
accessibilityLabel="Ouvrir la page de démonstration du switch"
|
||||
/>
|
||||
<Button
|
||||
label="Voir la démo des cards"
|
||||
variant="secondary"
|
||||
onPress={() => router.push('/card-demo')}
|
||||
accessibilityLabel="Ouvrir la page de présentation des cards"
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -50,6 +50,7 @@ export default function RootLayout() {
|
||||
<Stack.Screen name="(tabs)" />
|
||||
<Stack.Screen name="(org)" />
|
||||
<Stack.Screen name="switch-demo" />
|
||||
<Stack.Screen name="card-demo" />
|
||||
<Stack.Screen name="event/[id]" />
|
||||
<Stack.Screen name="create/event" options={{ presentation: 'modal' }} />
|
||||
<Stack.Screen name="create/post" options={{ presentation: 'modal' }} />
|
||||
|
||||
80
src/app/card-demo.tsx
Normal file
80
src/app/card-demo.tsx
Normal file
@@ -0,0 +1,80 @@
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
import Card from '@/components/Card';
|
||||
import { Screen } from '@/components/ui/Screen';
|
||||
import { mockEvents } from '@/mocks';
|
||||
import { colors } from '@/theme/colors';
|
||||
import { spacing } from '@/theme/spacing';
|
||||
import { typography } from '@/theme/typography';
|
||||
|
||||
const cardParticipants = [
|
||||
{
|
||||
id: 'lea',
|
||||
source:
|
||||
'https://images.unsplash.com/photo-1494790108377-be9c29b29330?auto=format&fit=crop&w=240&q=80',
|
||||
name: 'Lea Martin',
|
||||
},
|
||||
{
|
||||
id: 'yanis',
|
||||
source:
|
||||
'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?auto=format&fit=crop&w=240&q=80',
|
||||
name: 'Yanis Belaid',
|
||||
},
|
||||
{
|
||||
id: 'omar',
|
||||
source:
|
||||
'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?auto=format&fit=crop&w=240&q=80',
|
||||
name: 'Omar Khelifi',
|
||||
},
|
||||
];
|
||||
|
||||
export default function CardDemoScreen() {
|
||||
const demoEvent = mockEvents[0];
|
||||
|
||||
if (!demoEvent) {
|
||||
return (
|
||||
<Screen contentContainerStyle={styles.content}>
|
||||
<Text style={styles.emptyState}>Aucun événement de démo disponible.</Text>
|
||||
</Screen>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Screen contentContainerStyle={styles.content}>
|
||||
<View style={styles.header}>
|
||||
<Text style={styles.kicker}>DESIGN SYSTEM · CARD</Text>
|
||||
<Text style={styles.title}>Page de présentation Card</Text>
|
||||
</View>
|
||||
|
||||
<Card
|
||||
variant="hero"
|
||||
event={demoEvent}
|
||||
avatarStack={cardParticipants}
|
||||
onPress={() => undefined}
|
||||
/>
|
||||
</Screen>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
content: {
|
||||
gap: spacing[16],
|
||||
paddingTop: spacing[24],
|
||||
},
|
||||
header: {
|
||||
gap: spacing[8],
|
||||
marginBottom: spacing[8],
|
||||
},
|
||||
kicker: {
|
||||
...typography.sectionLabel,
|
||||
color: colors.textSecondary,
|
||||
},
|
||||
title: {
|
||||
...typography.body,
|
||||
color: colors.textPrimary,
|
||||
},
|
||||
emptyState: {
|
||||
...typography.body,
|
||||
color: colors.textSecondary,
|
||||
},
|
||||
});
|
||||
88
src/components/Card.tsx
Normal file
88
src/components/Card.tsx
Normal file
@@ -0,0 +1,88 @@
|
||||
import { Event } from '@/models/events';
|
||||
import { StyleSheet, Text, View } from 'react-native';
|
||||
|
||||
import { Badge } from './Badge';
|
||||
import { Button } from './Button';
|
||||
import { AvatarStack, AvatarStackItem } from './ui/AvatarStack';
|
||||
|
||||
import { typography } from '@/theme';
|
||||
import { colors } from '@/theme/colors';
|
||||
import { borderRadius } from '@/theme/spacing';
|
||||
|
||||
import { Heart, MapPin } from 'lucide-react-native';
|
||||
|
||||
export type CardVariant = 'hero' | 'list' | 'compact';
|
||||
|
||||
export type CardProps = {
|
||||
variant: CardVariant;
|
||||
event: Event;
|
||||
avatarStack?: AvatarStackItem[];
|
||||
onPress?: () => void;
|
||||
};
|
||||
|
||||
function Card({
|
||||
variant,
|
||||
event,
|
||||
avatarStack,
|
||||
onPress
|
||||
}: CardProps) {
|
||||
const eventDate = event.start_date;
|
||||
const badgeDate = {
|
||||
day: eventDate.getDate().toString().padStart(2, '0'),
|
||||
month: new Intl.DateTimeFormat('fr-FR', { month: 'short' })
|
||||
.format(eventDate)
|
||||
.replace('.', ''),
|
||||
};
|
||||
|
||||
return (
|
||||
<View
|
||||
style={styles.card}
|
||||
>
|
||||
<View style={styles.header}>
|
||||
<Badge variant='date' date={badgeDate} />
|
||||
<Button variant='ghost' icon={Heart} iconOnly onPress={onPress} />
|
||||
</View>
|
||||
|
||||
<View style={styles.content}>
|
||||
<Badge variant='tonight' label='CE SOIR' />
|
||||
<Text style={styles.title}>
|
||||
{event.name}
|
||||
</Text>
|
||||
<Text style={styles.subtitle}>
|
||||
<MapPin size={16} color={colors.textBody} />{' '}
|
||||
{event.address}, {event.city} - 18 km
|
||||
</Text>
|
||||
{avatarStack && <AvatarStack items={avatarStack} />}
|
||||
</View>
|
||||
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
borderRadius: borderRadius.card,
|
||||
backgroundColor: colors.surface,
|
||||
height: 230,
|
||||
padding: 16,
|
||||
},
|
||||
header: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
content: {
|
||||
flex: 3,
|
||||
justifyContent: 'flex-end',
|
||||
},
|
||||
title: {
|
||||
...typography.cardTitle,
|
||||
color: colors.textPrimary,
|
||||
},
|
||||
subtitle: {
|
||||
...typography.label,
|
||||
color: colors.textBody
|
||||
}
|
||||
});
|
||||
|
||||
export default Card;
|
||||
@@ -9,8 +9,6 @@ export const theme = {
|
||||
borderRadius,
|
||||
} as const;
|
||||
|
||||
export * from './ThemeProvider';
|
||||
export * from './useTheme';
|
||||
export { borderRadius, colors, spacing, typography };
|
||||
|
||||
export default theme;
|
||||
|
||||
Reference in New Issue
Block a user