Skip to content

Instantly share code, notes, and snippets.

@saltnpixels
Last active March 18, 2024 19:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save saltnpixels/e3c1c82383e28bd34812aa06151b04f4 to your computer and use it in GitHub Desktop.
Save saltnpixels/e3c1c82383e28bd34812aa06151b04f4 to your computer and use it in GitHub Desktop.
Swiper with react customizable
import { useRef, useEffect, Children, cloneElement, ReactElement, useState } from 'react';
import Swiper from 'swiper';
import { SwiperOptions } from 'swiper/types';
import { Pagination, Navigation } from 'swiper/modules';
import { Box, BoxProps, MotionBox } from '@components';
import { Chevronleft, Chevronright } from '@/icons';
import uniqueId from 'lodash.uniqueid';
import { Variants } from 'framer-motion';
// now importing all thee manually with emotion
// import 'swiper/css';
// import 'swiper/css/navigation';
// import 'swiper/css/pagination';
interface CarouselProps {
swiperOptions?: SwiperOptions;
navigation?: boolean | 'outside';
pagination?: boolean | 'outside';
navigationSize?: number;
navigationButtonSize?: number;
children: React.ReactElement<CarouselSlideProps>[] | React.ReactElement<CarouselSlideProps>;
navProps?: BoxProps;
arrowBg?: string;
variant?: 'filters' | 'normal';
hideNavigationOnDrag?: boolean;
}
interface CarouselSlideProps extends BoxProps {}
export const CarouselSlide = ({ className = '', children, ...props }: CarouselSlideProps) => {
return (
<Box className={`swiper-slide ${className}`} {...props}>
{children}
</Box>
);
};
export const Carousel = ({
swiperOptions,
navigation,
navigationSize = 14,
navigationButtonSize = 40,
pagination,
children,
navProps,
arrowBg = 'transparent',
variant = 'normal',
hideNavigationOnDrag = false,
...props
}: CarouselProps) => {
// Ref for the Swiper instance
const swiperRef = useRef<HTMLDivElement>(null);
const id = uniqueId('carousel_');
const [isDragging, setIsDragging] = useState(false);
const [swiperInstance, setSwiperInstance] = useState<Swiper | null>(null);
const defaultOptions: SwiperOptions = {
// Swiper options
modules: [Navigation, Pagination],
slidesPerView: 1,
focusableElements: 'input',
spaceBetween: 0,
pagination: { el: `#${id} .swiper-pagination`, clickable: true }, // Enable and configure pagination
loop: false,
watchOverflow: false,
navigation: {
nextEl: `#${id} .swiper-button-next`,
prevEl: `#${id} .swiper-button-prev`,
},
// Add other Swiper configurations as needed
};
const options = { ...defaultOptions, ...swiperOptions };
let styles = {};
if (variant === 'filters') {
arrowBg = 'white';
styles = {
...styles,
'.swiper-button-prev': {
height: '100%',
left: 0,
borderRadius: 0,
},
'.swiper-button-next': {
height: '100%',
right: 0,
borderRadius: 0,
},
};
}
// Variants for the Framer Motion animation
const navigationVariants: Variants = {
visible: { opacity: 1, transition: { duration: 0.5 }, pointerEvents: 'auto' },
hidden: { opacity: 0, transition: { duration: 0.5 }, pointerEvents: 'auto' },
};
useEffect(() => {
// Initialize Swiper
if (swiperRef.current) {
const swiper = new Swiper(swiperRef.current, options);
setSwiperInstance(swiper);
// Variable to keep track of the timeout
if (hideNavigationOnDrag) {
let timeoutId: NodeJS.Timeout;
// Detect when the user starts dragging
swiper.on('touchStart', function () {
clearTimeout(timeoutId);
setIsDragging(true);
});
// Detect when the user stops dragging
swiper.on('touchEnd', function () {
timeoutId = setTimeout(() => setIsDragging(false), 1000);
});
}
// Cleanup on component unmount
return () => {
swiper.destroy(true, true);
};
}
}, [navigation, pagination, variant, hideNavigationOnDrag, swiperOptions, children]);
useEffect(() => {
if (swiperInstance) {
// This forces Swiper to recalculate slides and pagination
swiperInstance.update();
}
}, [children]);
return (
<Box
className="swiper-container"
sx={styles}
position={'relative'}
userSelect={'none'}
id={id}
px={navigation === 'outside' ? '60px' : ''}
mb={pagination === 'outside' ? '35px' : 0}
{...props}
>
<Box ref={swiperRef} className="swiper">
<Box className="swiper-wrapper">{children}</Box>
</Box>
{pagination && (
<Box
transform={pagination === 'outside' ? 'translateY(35px)' : 'none'}
className="swiper-pagination"
sx={{
'.swiper-pagination-bullet-active': {
backgroundColor: 'primary.main',
},
}}
></Box>
)}
{navigation && (
<Box className="swiper-navigation" color="black" {...navProps}>
<Box
className="swiper-button-prev"
background={arrowBg}
color="inherit"
_after={{ content: 'none' }}
width={`${navigationButtonSize}px`}
aspectRatio="1/1"
borderRadius="100%"
height="auto"
margin="auto"
bottom="50%"
>
<MotionBox
variants={navigationVariants}
initial={false}
animate={isDragging ? 'hidden' : ''}
>
<Chevronleft
style={{
width: `${navigationSize}px`,
position: 'relative',
left: '1px',
height: 'auto',
}}
/>
</MotionBox>
</Box>
<Box
className="swiper-button-next"
background={arrowBg}
color="inherit"
_after={{ content: 'none' }}
width={`${navigationButtonSize}px`}
aspectRatio="1/1"
borderRadius="100%"
height="auto"
margin="auto"
bottom="50%"
padding="10px"
>
<MotionBox
variants={navigationVariants}
initial={false}
animate={isDragging ? 'hidden' : ''}
>
<Chevronright
style={{
width: `${navigationSize}px`,
position: 'relative',
left: '1px',
height: 'auto',
}}
/>
</MotionBox>
</Box>
</Box>
)}
</Box>
);
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment