Files
documenso/apps/marketing/src/components/(marketing)/carousel.tsx
2024-05-23 10:21:29 +00:00

181 lines
4.4 KiB
TypeScript

'use client';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import Autoplay from 'embla-carousel-autoplay';
import useEmblaCarousel from 'embla-carousel-react';
import { Card } from '@documenso/ui/primitives/card';
import { Thumb } from './thumb';
type SlideType = {
label: string;
imageSrc: string;
type: 'image' | 'video';
};
type CarouselPropType = {
slides: SlideType[];
};
const SLIDES = [
{
label: 'Signing Process',
type: 'video',
src: '/signing.mp4',
},
{
label: 'Templates/Fields',
type: 'video',
src: '/signing.mp4',
},
{
label: 'Zapier',
type: 'video',
src: '/signing.mp4',
},
{
label: 'Webhooks',
type: 'video',
src: '/signing.mp4',
},
{
label: 'API',
type: 'video',
src: '/signing.mp4',
},
{
label: 'Teams',
type: 'video',
src: '/signing.mp4',
},
{
label: 'Profile',
type: 'video',
src: '/signing.mp4',
},
];
export const Carousel = () => {
const slides = SLIDES;
const [_isPlaying, setIsPlaying] = useState(false);
const [selectedIndex, setSelectedIndex] = useState(0);
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true }, [
Autoplay({ playOnInit: true, delay: 5000 }),
]);
const [emblaThumbsRef, emblaThumbsApi] = useEmblaCarousel(
{
loop: true,
containScroll: 'keepSnaps',
dragFree: true,
},
[Autoplay({ playOnInit: true, delay: 5000 })],
);
const onThumbClick = useCallback(
(index: number) => {
if (!emblaApi || !emblaThumbsApi) return;
emblaApi.scrollTo(index);
},
[emblaApi, emblaThumbsApi],
);
const onSelect = useCallback(() => {
if (!emblaApi || !emblaThumbsApi) return;
setSelectedIndex(emblaApi.selectedScrollSnap());
emblaThumbsApi.scrollTo(emblaApi.selectedScrollSnap());
}, [emblaApi, emblaThumbsApi, setSelectedIndex]);
const videoRefs = useRef<(HTMLVideoElement | null)[]>([]);
useEffect(() => {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const video = entry.target as HTMLVideoElement;
video
.play()
.catch((error) => console.log('Error attempting to play the video:', error));
} else {
const video = entry.target as HTMLVideoElement;
video.pause();
}
});
},
{
threshold: 0.5,
},
);
videoRefs.current.forEach((video) => {
if (video) {
observer.observe(video);
}
});
return () => {
observer.disconnect();
};
}, [slides]);
useEffect(() => {
if (!emblaApi) return;
onSelect();
emblaApi.on('select', onSelect).on('reInit', onSelect);
}, [emblaApi, onSelect]);
useEffect(() => {
const autoplay = emblaApi?.plugins()?.autoplay;
if (!autoplay) return;
setIsPlaying(autoplay.isPlaying());
emblaApi
.on('autoplay:play', () => setIsPlaying(true))
.on('autoplay:stop', () => setIsPlaying(false))
.on('reInit', () => setIsPlaying(autoplay.isPlaying()));
}, [emblaApi]);
return (
<>
<Card className="mx-auto mt-12 w-full max-w-4xl rounded-2xl p-1 before:rounded-2xl" gradient>
<div className="overflow-hidden rounded-xl" ref={emblaRef}>
<div className="flex touch-pan-y rounded-xl">
{slides.map((slide, index) => (
<div className="min-w-[10rem] flex-none basis-full rounded-xl" key={index}>
{slide.type === 'video' && (
<video
ref={(el) => (videoRefs.current[index] = el)}
muted
loop
className="h-auto w-full rounded-xl"
>
<source src={slide.src} type="video/mp4" />
Your browser does not support the video tag.
</video>
)}
</div>
))}
</div>
</div>
</Card>
<div className="mx-auto mt-12 max-w-4xl px-2">
<div className="flex justify-between" ref={emblaThumbsRef}>
{slides.map((slide, index) => (
<Thumb
key={index}
onClick={() => onThumbClick(index)}
selected={index === selectedIndex}
index={index}
label={slide.label}
/>
))}
</div>
</div>
</>
);
};