import * as React from "react"; import { ModalContent, Modal, ModalBody, ModalOverlay, IconButton } from "@chakra-ui/react"; import { ChevronRightIcon, ChevronLeftIcon } from "@chakra-ui/icons"; import coverImages from "data/cover_images"; import { motion, AnimatePresence } from "framer-motion"; import "assets/stylesheets/carousel.css"; const variants = { enter: (direction: number) => { return { x: direction > 0 ? 1000 : -1000, opacity: 0 }; }, center: { zIndex: 1, x: 0, opacity: 1 }, exit: (direction: number) => { return { zIndex: 0, x: direction < 0 ? 1000 : -1000, opacity: 0 }; } }; const swipeConfidenceThreshold = 10000; const swipePower = (offset: number, velocity: number) => { return Math.abs(offset) * velocity; }; export interface CarouselProps { onOpen: () => void; onClose: () => void; isOpen: boolean; repoId: number; } const Carousel: React.SFC<CarouselProps> = ({ onOpen, onClose, isOpen, repoId }) => { const [[page, direction], setPage] = React.useState([0, 0]); const [imageIndex, setImageIndex] = React.useState<number>(0); const paginate = (newDirection: number) => { setPage([page + newDirection, newDirection]); }; React.useEffect(() => { setImageIndex(repoId); }, [repoId]); const nextImage = (newDirection: number) => { paginate(newDirection); setImageIndex(imageIndex + 1 < coverImages.length ? imageIndex + 1 : 0); }; const prevImage = (newDirection: number) => { paginate(newDirection); setImageIndex( 0 === imageIndex ? coverImages.length - 1 : imageIndex - 1 ); }; return ( <Modal isCentered onClose={onClose} size={"6xl"} isOpen={isOpen}> <ModalOverlay /> <ModalContent> <ModalBody padding="0"> <div className="carousel-container"> <AnimatePresence initial={false} custom={direction}> <motion.img key={page} src={coverImages[imageIndex]} custom={direction} variants={variants} initial="enter" animate="center" exit="exit" transition={{ x: { type: "spring", stiffness: 300, damping: 30 }, opacity: { duration: 0.2 } }} drag="x" dragConstraints={{ left: 0, right: 0 }} dragElastic={1} onDragEnd={(e, { offset, velocity }) => { const swipe = swipePower(offset.x, velocity.x); if (swipe < -swipeConfidenceThreshold) { paginate(1); } else if (swipe > swipeConfidenceThreshold) { paginate(-1); } }} /> </AnimatePresence> <div className="next" onClick={() => nextImage(1)}> <IconButton aria-label="left image" icon={<ChevronLeftIcon />} cursor="pointer" as={ChevronRightIcon} size="md" colorScheme="teal" borderRadius="full" /> </div> <div className="prev" onClick={() => prevImage(-1)}> <IconButton aria-label="right image" icon={<ChevronRightIcon />} cursor="pointer" as={ChevronLeftIcon} size="md" colorScheme="teal" borderRadius="full" /> </div> </div> </ModalBody> </ModalContent> </Modal> ); }; export default Carousel;