Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 'use client'; import { Carousel } from '@mantine/carousel'; import { Box, Skeleton, Text } from '@mantine/core'; import { useTranslations } from 'next-intl'; import { RecipeCard } from '../RecipeCard'; import classes from './RecipeCarousel.module.css'; import type { RecipeCarouselProps } from './types'; const CAROUSEL_PROPS = { slideSize: { base: '90%', sm: '45%', md: '30%', lg: '23%' }, slideGap: 'md' as const, withControls: true, emblaOptions: { containScroll: 'trimSnaps' as const }, }; const RecipeCarousel = ({ recipes, loading = false, withFavorite = true, emptyMessage, skeletonCount = 4, }: RecipeCarouselProps) => { const t = useTranslations('recipe'); const empty = emptyMessage ?? t('empty'); if (loading) { const skeletonItems = Array.from({ length: skeletonCount }, (_, i) => i); return ( <Carousel {...CAROUSEL_PROPS}> {skeletonItems.map((item) => ( <Carousel.Slide key={`carousel-skeleton-${item}`} className={classes.carouselSlide} > <Skeleton height={320} radius="md" /> </Carousel.Slide> ))} </Carousel> ); } if (recipes.length === 0) { return ( <Box className={classes.emptyCarousel}> <Text c="dimmed" size="lg"> {empty} </Text> </Box> ); } const shouldLoop = recipes.length > 4; return ( <Carousel {...CAROUSEL_PROPS} emblaOptions={{ ...CAROUSEL_PROPS.emblaOptions, loop: shouldLoop }} > {recipes.map((recipe) => ( <Carousel.Slide key={recipe.id} className={classes.carouselSlide}> <RecipeCard recipe={recipe} withFavorite={withFavorite} /> </Carousel.Slide> ))} </Carousel> ); }; export default RecipeCarousel; |