import { PropsWithChildren, useEffect, useState } from "react"; import { Container } from "~/components/container"; import { ArrowLeft, ArrowRight } from "lucide-react"; import { range } from "~/utils/range"; const IMAGES = [ { id: 1, url: "https://images.pexels.com/photos/1366919/pexels-photo-1366919.jpeg", }, { id: 2, url: "https://images.pexels.com/photos/2662116/pexels-photo-2662116.jpeg", }, { id: 3, url: "https://images.pexels.com/photos/1379636/pexels-photo-1379636.jpeg", }, { id: 4, url: "https://images.pexels.com/photos/1470405/pexels-photo-1470405.jpeg", }, { id: 5, url: "https://images.pexels.com/photos/3225517/pexels-photo-3225517.jpeg", }, { id: 6, url: "https://images.pexels.com/photos/1133957/pexels-photo-1133957.jpeg", }, { id: 7, url: "https://images.pexels.com/photos/1486974/pexels-photo-1486974.jpeg", }, { id: 8, url: "https://images.pexels.com/photos/2662116/pexels-photo-2662116.jpeg", }, { id: 9, url: "https://images.pexels.com/photos/1366630/pexels-photo-1366630.jpeg", }, { id: 10, url: "https://images.pexels.com/photos/2486168/pexels-photo-2486168.jpeg", }, ]; const MAX_ITEMS = 4; export default function Carousel() { const [images, setImages] = useState(IMAGES); const [activeItemWidth, setActiveItemWidth] = useState(60); const [currentIndex, setCurrentIndex] = useState(MAX_ITEMS); const scaleStep = 1 / MAX_ITEMS; const offsetStep = range(-3, MAX_ITEMS * 2 - 1).reduce( (acc: Record, currentValue) => { acc[currentValue] = Math.sign(currentValue) * MAX_ITEMS - Math.abs(currentValue); return acc; }, {} ); function isBetween(value: number, start: number, stop: number) { return start <= value && value >= stop; } function isValidInterval(position: number) { return isBetween(position, -3, -1) || isBetween(position, 1, 3); } function computedOffset(position: number) { if (position !== 0 && position !== -1 && position !== 1) { return ( (Math.abs(position) === MAX_ITEMS - 1 ? Math.abs(position) : 0) + range(1, Math.abs(position) + 1).reduce((acc, currentValue) => { if (offsetStep[currentValue]) { return acc + offsetStep[currentValue]; } return acc; }, 0) ); } return 0; } function computedWidth(position: number) { return MAX_ITEMS - Math.abs(position) + 1; } function computedScale(position: number) { if (position === 0) { return 1; } else if ( isValidInterval(position) || position === -3 || position === -2 ) { return 1 - scaleStep * Math.abs(position) + 0.1; } return 0; } function computedOpacity(position: number) { if ( position === 0 || position === -3 || position === -2 || isBetween(position, -3, -1) ) { return 1; } return 0; } function computedZIndex(position: number) { if (position !== 0) { return images.length - Math.abs(position); } return images.length + 1; } function onNext() { const imagesCopy = [...images]; const firstItem = imagesCopy.shift(); if (firstItem) { imagesCopy.push({ id: Date.now(), url: firstItem.url }); } setImages(imagesCopy); } function onPrev() { const imagesCopy = [...images]; const lastItem = imagesCopy.pop(); if (lastItem) { imagesCopy.unshift({ id: Date.now(), url: lastItem.url }); } setImages(imagesCopy); } return (
{images.map(({ url, id }, index) => { const shift = index - currentIndex; const width = shift === 0 ? activeItemWidth + "rem" : Math.abs(shift) === MAX_ITEMS ? 0 : computedWidth(shift) + "rem"; return (
); })}
); } function Button({ children, position, onClick, }: PropsWithChildren<{ position: "left" | "right"; onClick: () => void }>) { return ( ); }