From 4d145d9372cd58d828c7104f0822e0af612887c6 Mon Sep 17 00:00:00 2001 From: kudindmitriy Date: Wed, 15 Apr 2026 15:13:32 +0300 Subject: [PATCH] Bob AI: Modify the hero section component to replace the static imag --- src/App.tsx | 6 ++- src/components/sections/hero/HeroSplit.tsx | 10 ++--- src/components/ui/ImageSlider.tsx | 30 +++++++++++++ src/hooks/useCarouselControls.ts | 52 +++++----------------- src/index.css | 1 + 5 files changed, 52 insertions(+), 47 deletions(-) create mode 100644 src/components/ui/ImageSlider.tsx diff --git a/src/App.tsx b/src/App.tsx index a7d22ef..4d0d743 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -35,7 +35,11 @@ export default function App() { text: "Browse Menu", href: "#features"}} secondaryButton={{ text: "Visit Us", href: "#contact"}} - imageSrc="https://images.unsplash.com/photo-1681838675911-625ee52549f5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w4Mzc5ODl8MHwxfHNlYXJjaHwxNHx8YmFrZXJ5JTIwd2luZG93JTIwd2l0aCUyMHdhcm0lMjBsaWdodGluZ3xlbnwxfDB8fHwxNzc2MjUzMTMzfDA&ixlib=rb-4.1.0&q=80&w=1080" + images={[ + "https://images.unsplash.com/photo-1681838675911-625ee52549f5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w4Mzc5ODl8MHwxfHNlYXJjaHwxNHx8YmFrZXJ5JTIwd2luZG93JTIwd2l0aCUyMHdhcm0lMjBsaWdodGluZ3xlbnwxfDB8fHwxNzc2MjUzMTMzfDA&ixlib=rb-4.1.0&q=80&w=1080", + "https://images.unsplash.com/photo-1555507036-ab1f4038808a?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + "https://images.unsplash.com/photo-1568254183919-78a4f43a2877?q=80&w=2070&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + ]} /> diff --git a/src/components/sections/hero/HeroSplit.tsx b/src/components/sections/hero/HeroSplit.tsx index cdc2821..9572d15 100644 --- a/src/components/sections/hero/HeroSplit.tsx +++ b/src/components/sections/hero/HeroSplit.tsx @@ -1,7 +1,7 @@ import { motion } from "motion/react"; import Button from "@/components/ui/Button"; import TextAnimation from "@/components/ui/TextAnimation"; -import ImageOrVideo from "@/components/ui/ImageOrVideo"; +import ImageSlider from "@/components/ui/ImageSlider"; type HeroSplitProps = { tag: string; @@ -9,7 +9,8 @@ type HeroSplitProps = { description: string; primaryButton: { text: string; href: string }; secondaryButton: { text: string; href: string }; -} & ({ imageSrc: string; videoSrc?: never } | { videoSrc: string; imageSrc?: never }); + images: string[]; +}; const HeroSplit = ({ tag, @@ -17,8 +18,7 @@ const HeroSplit = ({ description, primaryButton, secondaryButton, - imageSrc, - videoSrc, + images, }: HeroSplitProps) => { return (
@@ -54,7 +54,7 @@ const HeroSplit = ({ transition={{ duration: 0.6, ease: "easeOut", delay: 0.2 }} className="w-full md:w-1/2 h-100 md:h-[65vh] md:max-h-[75svh] p-5 card rounded overflow-hidden" > - +
diff --git a/src/components/ui/ImageSlider.tsx b/src/components/ui/ImageSlider.tsx new file mode 100644 index 0000000..c8d96f2 --- /dev/null +++ b/src/components/ui/ImageSlider.tsx @@ -0,0 +1,30 @@ +import { useCarouselControls } from "@/hooks/useCarouselControls"; +import { ChevronLeft, ChevronRight } from "lucide-react"; + +type ImageSliderProps = { + images: string[]; +}; + +const ImageSlider = ({ images }: ImageSliderProps) => { + const { currentIndex, prev, next } = useCarouselControls(images.length); + + return ( +
+
+ {images.map((src, index) => ( +
+ {`Slide +
+ ))} +
+ + +
+ ); +}; + +export default ImageSlider; \ No newline at end of file diff --git a/src/hooks/useCarouselControls.ts b/src/hooks/useCarouselControls.ts index 4b3c3ba..bbbb572 100644 --- a/src/hooks/useCarouselControls.ts +++ b/src/hooks/useCarouselControls.ts @@ -1,45 +1,15 @@ -import { useCallback, useEffect, useState } from "react"; -import type { EmblaCarouselType } from "embla-carousel"; +import { useState } from "react"; -export const useCarouselControls = (emblaApi: EmblaCarouselType | undefined) => { - const [prevDisabled, setPrevDisabled] = useState(true); - const [nextDisabled, setNextDisabled] = useState(true); - const [scrollProgress, setScrollProgress] = useState(0); +export const useCarouselControls = (totalSlides: number) => { + const [currentIndex, setCurrentIndex] = useState(0); - const scrollPrev = useCallback(() => { - if (!emblaApi) return; - emblaApi.scrollPrev(); - }, [emblaApi]); + const next = () => { + setCurrentIndex((prevIndex) => (prevIndex + 1) % totalSlides); + }; - const scrollNext = useCallback(() => { - if (!emblaApi) return; - emblaApi.scrollNext(); - }, [emblaApi]); + const prev = () => { + setCurrentIndex((prevIndex) => (prevIndex - 1 + totalSlides) % totalSlides); + }; - const onSelect = useCallback((api: EmblaCarouselType) => { - setPrevDisabled(!api.canScrollPrev()); - setNextDisabled(!api.canScrollNext()); - }, []); - - const onScroll = useCallback((api: EmblaCarouselType) => { - const progress = Math.max(0, Math.min(1, api.scrollProgress())); - setScrollProgress(progress * 100); - }, []); - - useEffect(() => { - if (!emblaApi) return; - - onSelect(emblaApi); - onScroll(emblaApi); - - emblaApi.on("reInit", onSelect).on("select", onSelect); - emblaApi.on("reInit", onScroll).on("scroll", onScroll); - - return () => { - emblaApi.off("reInit", onSelect).off("select", onSelect); - emblaApi.off("reInit", onScroll).off("scroll", onScroll); - }; - }, [emblaApi, onSelect, onScroll]); - - return { prevDisabled, nextDisabled, scrollPrev, scrollNext, scrollProgress }; -}; + return { currentIndex, next, prev }; +}; \ No newline at end of file diff --git a/src/index.css b/src/index.css index e152ca8..97c7036 100644 --- a/src/index.css +++ b/src/index.css @@ -1,6 +1,7 @@ @import "tailwindcss"; @import "./styles/masks.css"; @import "./styles/animations.css"; +@import "./styles/slider.css"; :root { /* @colorThemes/lightTheme/grayBlueAccent */ -- 2.49.1