mirror of
https://github.com/documenso/documenso.git
synced 2025-11-12 15:53:02 +10:00
181 lines
4.4 KiB
TypeScript
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>
|
|
</>
|
|
);
|
|
};
|