diff --git a/client/package.json b/client/package.json index e08860e..a7344b5 100644 --- a/client/package.json +++ b/client/package.json @@ -13,6 +13,8 @@ "@apollo/client": "^4.3.1", "@radix-ui/react-dialog": "^1.1.23", "@tailwindcss/vite": "^4.3.3", + "embla-carousel-autoplay": "^8.6.0", + "embla-carousel-react": "^8.6.0", "graphql": "^16.11.0", "react": "^19.2.8", "react-dom": "^19.2.8", diff --git a/client/src/components/slideshow/RecipeSlideshow.tsx b/client/src/components/slideshow/RecipeSlideshow.tsx new file mode 100644 index 0000000..1402791 --- /dev/null +++ b/client/src/components/slideshow/RecipeSlideshow.tsx @@ -0,0 +1,105 @@ +import type { Recipe } from '../../types/recipe'; +import useEmblaCarousel from 'embla-carousel-react'; +import { Link } from 'react-router'; +import { useEffect, useState } from 'react'; + +// Fargetemaer for slidesene. panel er tekstboksen, button er knappen for å se en oppskrift +type SlideshowTheme = 'light' | 'dark' | 'rose'; + +const themes = { + light: { panel: 'bg-secondary/50 text-olive-dark', button: 'bg-olive-dark text-background', dot: 'border-olive-dark', dotActive: 'bg-olive-dark' }, + dark: { panel: 'bg-olive-dark text-background', button: 'bg-pink-light text-olive-dark', dot: 'border-background', dotActive: 'bg-background' }, + rose: { panel: 'bg-pink text-pink-light', button: 'bg-primary-hover text-pink-light', dot: 'border-background', dotActive: 'bg-background' }, +} + +type RecipeSlideshowProps = { + recipes: Recipe[]; + reversed?: boolean; // Denne er valgfri, og gjør at karusellen i midten er speilvendt + theme?: SlideshowTheme; +}; + +// Slideshow på forsiden som viser én oppskrift av gangen. +// Det er mulig å bla med piler, prikkene, sveiping eller med tastaturet. +export function RecipeSlideshow({ recipes, reversed = false, theme = 'light' }: RecipeSlideshowProps) { + // emblaRef kobler Embla opp til vinduet, også brukes emblaAPI til å styre slideshowet. + const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true }); + // Husker hvilken av sidene i slideshowet som vises, sånn at riktig prikk blir farget inn. + const [selectedIndex, setSelectedIndex] = useState(0); + + const colors = themes[theme]; + + // Oppdaterer selectedIndex hver gang det byttes hvilken side som vises. + useEffect(() => { + if(!emblaApi) return; + const onSelect = () => setSelectedIndex(emblaApi.selectedScrollSnap()); + emblaApi.on('select', onSelect); + + // Slutter å lytte når slideshowet fjernes fra siden, sånn at det ikke blir minnelekkasjer. + return () => { + emblaApi.off('select', onSelect); + }; + }, [emblaApi]); + + return ( +
+ {/* Vinduet: det skjuler slidene som ikke vises */} +
+ {/* Raden med alle slidene som Embla flytter sidelengs */} +
+ {recipes.map((recipe) => ( + // Én slide som bruker hele bredden i vinduet +
+
+ {/* Tomt alternativ for bildet, ettersom tittelen allerede står rett ved siden av bildet. */} + +
+

{recipe.title}

+

{recipe.description}

+
+

{recipe.prepTime} min

+

{recipe.portions} porsjoner

+
+ Se oppskrift +
+
+
+ ))} +
+
+ + {/* Pilknapper for å kunne bla. aria-label gir skjermlesere en tekst, ettersom pilene bare er symboler */} + + + + {/* Prikker: én prikk per oppskrift. Den siden som vises er fylt inn. Dersom du trykker på en annen prikk hopper du til den siden. */} +
+ {recipes.map((recipe, index) => ( +
+
+ ); +} \ No newline at end of file diff --git a/client/src/index.css b/client/src/index.css index f97733f..6211b1c 100644 --- a/client/src/index.css +++ b/client/src/index.css @@ -13,6 +13,7 @@ --color-secondary: #c2c395; --color-olive: #999d4f; + --color-olive-dark: #343301; --color-pink-light: #f2c6cf; --color-pink: #b46a72; diff --git a/client/src/routes/HomePage.tsx b/client/src/routes/HomePage.tsx index 2b58be0..f1f7d3c 100644 --- a/client/src/routes/HomePage.tsx +++ b/client/src/routes/HomePage.tsx @@ -1,5 +1,7 @@ import { useState } from 'react' import { Tag } from '../components/ui/Tag' +import { RecipeSlideshow } from '../components/slideshow/RecipeSlideshow' +import { recipes } from '../mocks/recipes' @@ -12,19 +14,12 @@ export default function HomePage() {
- {/* Slideshow-placeholder */} -
- Slideshow kommer her -
+
+ + + +
+