feat: implement carousel navigation buttons for improved user interaction

This commit is contained in:
fredy.siswanto 2025-03-02 20:08:03 +07:00
parent 82c0894ccd
commit cd3637bf03
3 changed files with 89 additions and 36 deletions

View File

@ -0,0 +1,24 @@
import { CarouselNextIcon } from '~/components/icons/carousel-next'
import { CarouselPreviousIcon } from '~/components/icons/carousel-previous'
type TCarouselButton = {
direction: 'prev' | 'next'
isEnabled: boolean
onClick: () => void
}
export const CarouselButton = ({
direction,
isEnabled,
onClick,
}: TCarouselButton) => {
const Icon = direction === 'prev' ? CarouselPreviousIcon : CarouselNextIcon
return (
<Icon
color={isEnabled ? '#2E2F7C' : '#DCDCDC'}
className={isEnabled ? 'cursor-pointer' : 'cursor-not-allowed'}
width={45}
height={45}
onClick={onClick}
/>
)
}

View File

@ -1,25 +1,41 @@
import useEmblaCarousel from 'embla-carousel-react' import useEmblaCarousel from 'embla-carousel-react'
import { useCallback } from 'react' import { useCallback, useEffect, useState } from 'react'
import { Link } from 'react-router' import { Link } from 'react-router'
import { CarouselNextIcon } from '~/components/icons/carousel-next'
import { CarouselPreviousIcon } from '~/components/icons/carousel-previous'
import { Button } from '~/components/ui/button' import { Button } from '~/components/ui/button'
import { CarouselButton } from '~/components/ui/button-slide'
import { useNewsContext } from '~/contexts/news' import { useNewsContext } from '~/contexts/news'
import type { TNews } from '~/types/news' import type { TNews } from '~/types/news'
export const CarouselHero = (properties: TNews) => { export const CarouselHero = (properties: TNews) => {
const { setIsSuccessOpen } = useNewsContext() const { setIsSuccessOpen } = useNewsContext()
const { title, description, items } = properties const { title, description, items } = properties
const [emblaReference, emblaApi] = useEmblaCarousel({ loop: true }) const [emblaReference, emblaApi] = useEmblaCarousel({ loop: false })
const [canScrollNext, setCanScrollNext] = useState(false)
const [canScrollPrevious, setCanScrollPrevious] = useState(false)
const updateButtons = useCallback(() => {
if (emblaApi) {
setCanScrollPrevious(emblaApi.canScrollPrev())
setCanScrollNext(emblaApi.canScrollNext())
}
}, [emblaApi])
useEffect(() => {
if (emblaApi) {
updateButtons()
emblaApi.on('select', updateButtons)
}
}, [emblaApi, updateButtons])
const previousSlide = useCallback(() => { const previousSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev() if (canScrollPrevious && emblaApi) emblaApi.scrollPrev()
}, [emblaApi]) }, [emblaApi, canScrollPrevious])
const nextSlide = useCallback(() => { const nextSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollNext() if (canScrollNext && emblaApi) emblaApi.scrollNext()
}, [emblaApi]) }, [emblaApi, canScrollNext])
return ( return (
<div className=""> <div className="">
@ -33,18 +49,14 @@ export const CarouselHero = (properties: TNews) => {
</p> </p>
</div> </div>
<div className="flex gap-2.5"> <div className="flex gap-2.5">
<CarouselPreviousIcon <CarouselButton
color="#DCDCDC" direction="prev"
className="cursor-pointer" isEnabled={canScrollPrevious}
width={45}
height={45}
onClick={previousSlide} onClick={previousSlide}
/> />
<CarouselNextIcon <CarouselButton
color="#2E2F7C" direction="next"
className="cursor-pointer" isEnabled={canScrollNext}
width={45}
height={45}
onClick={nextSlide} onClick={nextSlide}
/> />
</div> </div>

View File

@ -1,25 +1,46 @@
import useEmblaCarousel from 'embla-carousel-react' import useEmblaCarousel from 'embla-carousel-react'
import { useCallback } from 'react' import { useCallback, useEffect, useState } from 'react'
import { Link } from 'react-router' import { Link } from 'react-router'
import { CarouselNextIcon } from '~/components/icons/carousel-next'
import { CarouselPreviousIcon } from '~/components/icons/carousel-previous'
import { Button } from '~/components/ui/button' import { Button } from '~/components/ui/button'
import { CarouselButton } from '~/components/ui/button-slide'
import { useNewsContext } from '~/contexts/news' import { useNewsContext } from '~/contexts/news'
import type { TNews } from '~/types/news' import type { TNews } from '~/types/news'
export const CarouselSection = (properties: TNews) => { export const CarouselSection = (properties: TNews) => {
const { setIsSuccessOpen } = useNewsContext() const { setIsSuccessOpen } = useNewsContext()
const { title, description, items } = properties const { title, description, items } = properties
const [emblaReference, emblaApi] = useEmblaCarousel({ loop: false }) const [emblaReference, emblaApi] = useEmblaCarousel({
loop: false,
slidesToScroll: 1,
align: 'start',
})
const [canScrollNext, setCanScrollNext] = useState(false)
const [canScrollPrevious, setCanScrollPrevious] = useState(false)
const updateButtons = useCallback(() => {
if (emblaApi) {
setCanScrollPrevious(emblaApi.canScrollPrev())
setCanScrollNext(emblaApi.canScrollNext())
}
}, [emblaApi])
useEffect(() => {
if (emblaApi) {
updateButtons()
emblaApi.on('select', updateButtons)
}
}, [emblaApi, updateButtons])
const previousSlide = useCallback(() => { const previousSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollPrev() if (canScrollPrevious && emblaApi) emblaApi.scrollPrev()
}, [emblaApi]) }, [emblaApi, canScrollPrevious])
const nextSlide = useCallback(() => { const nextSlide = useCallback(() => {
if (emblaApi) emblaApi.scrollNext() if (canScrollNext && emblaApi) emblaApi.scrollNext()
}, [emblaApi]) }, [emblaApi, canScrollNext])
return ( return (
<div className=""> <div className="">
<div className="mt-3 mb-3 flex items-center justify-between border-b border-black pb-3 sm:mb-[30px] sm:pb-[30px]"> <div className="mt-3 mb-3 flex items-center justify-between border-b border-black pb-3 sm:mb-[30px] sm:pb-[30px]">
@ -33,18 +54,14 @@ export const CarouselSection = (properties: TNews) => {
</div> </div>
<div className="flex gap-2.5"> <div className="flex gap-2.5">
<CarouselPreviousIcon <CarouselButton
color="#DCDCDC" direction="prev"
className="cursor-pointer" isEnabled={canScrollPrevious}
width={45}
height={45}
onClick={previousSlide} onClick={previousSlide}
/> />
<CarouselNextIcon <CarouselButton
color="#2E2F7C" direction="next"
className="cursor-pointer" isEnabled={canScrollNext}
width={45}
height={45}
onClick={nextSlide} onClick={nextSlide}
/> />
</div> </div>