Skip to content

Commit

Permalink
Merge pull request #27 from TusharSin810/main
Browse files Browse the repository at this point in the history
Cleaned Up carousel and Added Scroll on hover Effect
  • Loading branch information
SOURHEAD authored Sep 26, 2024
2 parents 07fa92c + 79a5b82 commit 715d7bc
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 194 deletions.
35 changes: 0 additions & 35 deletions src/components/Carousel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,71 +18,36 @@ export function Carouselhome() {
);
}

const DummyContent = () => {
return (<>
{[...new Array(3).fill(1)].map((_, index) => {
return (
(<div
key={"dummy-content" + index}
className="bg-[#F5F5F7] dark:bg-neutral-800 p-8 md:p-14 rounded-3xl mb-4">
<p
className="text-neutral-600 dark:text-neutral-400 text-base md:text-2xl font-sans max-w-3xl mx-auto">
<span className="font-bold text-neutral-700 dark:text-neutral-200">
The first rule of Apple club is that you boast about Apple club.
</span>{" "}
Keep a journal, quickly jot down a grocery list, and take amazing
class notes. Want to convert those notes to text? No problem.
Langotiya jeetu ka mara hua yaar is ready to capture every
thought.
</p>
<img
src="https://assets.aceternity.com/macbook.png"
alt="Macbook mockup from Aceternity UI"
height="500"
width="500"
className="md:w-1/2 md:h-1/2 h-full w-full mx-auto object-contain" />
</div>)
);
})}
</>);
};

const data = [
{
category: "Artificial Intelligence",
title: "You can do more with AI.",
src: "https://images.unsplash.com/photo-1593508512255-86ab42a8e620?q=80&w=3556&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
content: <DummyContent />,
},
{
category: "Productivity",
title: "Enhance your productivity.",
src: "https://images.unsplash.com/photo-1531554694128-c4c6665f59c2?q=80&w=3387&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
content: <DummyContent />,
},
{
category: "Product",
title: "Launching the new Apple Vision Pro.",
src: "https://images.unsplash.com/photo-1713869791518-a770879e60dc?q=80&w=2333&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
content: <DummyContent />,
},

{
category: "Product",
title: "Maps for your iPhone 15 Pro Max.",
src: "https://images.unsplash.com/photo-1599202860130-f600f4948364?q=80&w=2515&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
content: <DummyContent />,
},
{
category: "iOS",
title: "Photography just got better.",
src: "https://images.unsplash.com/photo-1602081957921-9137a5d6eaee?q=80&w=2793&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
content: <DummyContent />,
},
{
category: "Hiring",
title: "Hiring for a Staff Software Engineer",
src: "https://images.unsplash.com/photo-1511984804822-e16ba72f5848?q=80&w=2048&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
content: <DummyContent />,
},
];
230 changes: 71 additions & 159 deletions src/components/ui/apple-cards-carousel.jsx
Original file line number Diff line number Diff line change
@@ -1,33 +1,20 @@
"use client";
import React, {
useEffect,
useRef,
useState,
createContext,
useContext,
} from "react";
import {
IconArrowNarrowLeft,
IconArrowNarrowRight,
IconX,
} from "@tabler/icons-react";
import React, { useEffect, useState, createContext } from "react";
import { IconArrowNarrowLeft, IconArrowNarrowRight } from "@tabler/icons-react";
import { cn } from "../../lib/utils";
import { AnimatePresence, motion } from "framer-motion";
import { useOutsideClick } from "../../hooks/use-outside-click";
import { motion } from "framer-motion";

export const CarouselContext = createContext({
onCardClose: () => {},
currentIndex: 0,
});

export const Carousel = ({
items,
initialScroll = 0
}) => {
export const Carousel = ({ items, initialScroll = 0 }) => {
const carouselRef = React.useRef(null);
const [canScrollLeft, setCanScrollLeft] = React.useState(false);
const [canScrollRight, setCanScrollRight] = React.useState(true);
const [currentIndex, setCurrentIndex] = useState(0);
const [canScrollLeft, setCanScrollLeft] = useState(false);
const [canScrollRight, setCanScrollRight] = useState(true);
const [scrollInterval, setScrollInterval] = useState(null);
const [currentIndex] = useState(0);

useEffect(() => {
if (carouselRef.current) {
Expand Down Expand Up @@ -56,58 +43,62 @@ export const Carousel = ({
}
};

const handleCardClose = (index) => {
const startScrollLeft = () => {
if (carouselRef.current) {
const cardWidth = isMobile() ? 230 : 384; // (md:w-96)
const gap = isMobile() ? 4 : 8;
const scrollPosition = (cardWidth + gap) * (index + 1);
carouselRef.current.scrollTo({
left: scrollPosition,
behavior: "smooth",
});
setCurrentIndex(index);
const interval = setInterval(() => {
carouselRef.current.scrollBy({ left: -10, behavior: "smooth" });
}, 16); // ~60fps
setScrollInterval(interval);
}
};

const isMobile = () => {
return window && window.innerWidth < 768;
const startScrollRight = () => {
if (carouselRef.current) {
const interval = setInterval(() => {
carouselRef.current.scrollBy({ left: 10, behavior: "smooth" });
}, 16); // ~60fps
setScrollInterval(interval);
}
};

const stopScrolling = () => {
if (scrollInterval) {
clearInterval(scrollInterval);
setScrollInterval(null);
}
};

return (
<CarouselContext.Provider value={{ onCardClose: handleCardClose, currentIndex }}>
<CarouselContext.Provider value={{ currentIndex }}>
<div className="relative w-full">
<div
className="absolute left-0 top-10 h-4/5 w-[10%] z-50"
onMouseEnter={startScrollLeft}
onMouseLeave={stopScrolling}
/>
<div
className="absolute right-0 top-10 h-4/5 w-[10%] z-50"
onMouseEnter={startScrollRight}
onMouseLeave={stopScrolling}
/>

<div
className="flex w-full overflow-x-scroll overscroll-x-auto py-4 scroll-smooth [scrollbar-width:none]"
ref={carouselRef}
onScroll={checkScrollability}>
<div
className={cn(
"absolute right-0 z-[1000] h-auto w-[5%] overflow-hidden bg-gradient-to-l"
)}></div>
<div className={cn("absolute right-0 z-[1000] h-auto w-[5%] overflow-hidden bg-gradient-to-l")}></div>

<div
className={cn(
"flex flex-row justify-start gap-10 pl-4",
"max-w-7xl mx-auto"
)}>
<div className={cn("flex flex-row justify-start gap-10 pl-4", "max-w-7xl mx-auto")}>
{items.map((item, index) => (
<motion.div
initial={{
opacity: 0,
y: 20,
}}
initial={{ opacity: 0, y: 20 }}
animate={{
opacity: 1,
y: 0,
transition: {
duration: 0.5,
delay: 0.2 * index,
ease: "easeOut",
once: true,
},
transition: { duration: 0.5, delay: 0.2 * index, ease: "easeOut", once: true },
}}
key={"card" + index}
className="last:pr-[5%] md:last:pr-[33%] rounded-3xl">
className="last:pr-[5%] md:last:pr-[33%] rounded-3xl">
{item}
</motion.div>
))}
Expand All @@ -132,115 +123,35 @@ export const Carousel = ({
);
};

export const Card = ({
card,
index,
layout = false
}) => {
const [open, setOpen] = useState(false);
const containerRef = useRef(null);
const { onCardClose, currentIndex } = useContext(CarouselContext);

useEffect(() => {
function onKeyDown(event) {
if (event.key === "Escape") {
handleClose();
}
}

if (open) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "auto";
}

window.addEventListener("keydown", onKeyDown);
return () => window.removeEventListener("keydown", onKeyDown);
}, [open]);

useOutsideClick(containerRef, () => handleClose());

const handleOpen = () => {
setOpen(true);
};

const handleClose = () => {
setOpen(false);
onCardClose(index);
};

export const Card = ({ card, index, layout = false }) => {
return (
<>
<AnimatePresence>
{open && (
<div className="fixed inset-0 h-screen z-50 overflow-auto">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="bg-black/80 backdrop-blur-lg h-full w-full fixed inset-0" />
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
ref={containerRef}
layoutId={layout ? `card-${card.title}` : undefined}
className="max-w-5xl mx-auto bg-white dark:bg-neutral-900 h-fit z-[60] my-10 p-4 md:p-10 rounded-3xl font-sans relative">
<button
className="sticky top-4 h-8 w-8 right-0 ml-auto bg-black dark:bg-white rounded-full flex items-center justify-center"
onClick={handleClose}>
<IconX className="h-6 w-6 text-neutral-100 dark:text-neutral-900" />
</button>
<motion.p
layoutId={layout ? `category-${card.title}` : undefined}
className="text-base font-medium text-black dark:text-white">
{card.category}
</motion.p>
<motion.p
layoutId={layout ? `title-${card.title}` : undefined}
className="text-2xl md:text-5xl font-semibold text-neutral-700 mt-4 dark:text-white">
{card.title}
</motion.p>
<div className="py-10">{card.content}</div>
</motion.div>
</div>
)}
</AnimatePresence>
<motion.button
layoutId={layout ? `card-${card.title}` : undefined}
onClick={handleOpen}
className="rounded-3xl bg-gray-100 dark:bg-neutral-900 h-80 w-56 md:h-[30rem] md:w-96 overflow-hidden flex flex-col items-start justify-start relative z-10">
<div className="absolute h-full top-0 inset-x-0 bg-gradient-to-b from-black/50 via-transparent to-transparent z-30 pointer-events-none" />
<div className="relative z-40 p-8">
<motion.p
layoutId={layout ? `category-${card.category}` : undefined}
className="text-white text-sm md:text-base font-medium font-sans text-left">
{card.category}
</motion.p>
<motion.p
layoutId={layout ? `title-${card.title}` : undefined}
className="text-white text-xl md:text-3xl font-semibold max-w-xs text-left [text-wrap:balance] font-sans mt-2">
{card.title}
</motion.p>
</div>
<BlurImage
src={card.src}
alt={card.title}
fill
className="object-cover absolute z-10 inset-0" />
</motion.button>
</>
<motion.button
layoutId={layout ? `card-${card.title}` : undefined}
className="rounded-3xl bg-gray-100 dark:bg-neutral-900 h-80 w-56 md:h-[35rem] md:w-96 overflow-hidden flex flex-col items-start justify-start relative z-10">
<div className="absolute h-full top-0 inset-x-0 bg-gradient-to-b from-black/50 via-transparent to-transparent z-30 pointer-events-none" />
<div className="relative z-40 p-8">
<motion.p
layoutId={layout ? `category-${card.category}` : undefined}
className="text-white text-sm md:text-base font-medium font-sans text-left">
{card.category}
</motion.p>
<motion.p
layoutId={layout ? `title-${card.title}` : undefined}
className="text-white text-xl md:text-3xl font-semibold max-w-xs text-left [text-wrap:balance] font-sans mt-2">
{card.title}
</motion.p>
</div>
<BlurImage
src={card.src}
alt={card.title}
fill
className="object-cover absolute z-10 inset-0"
/>
</motion.button>
);
};

export const BlurImage = ({
height,
width,
src,
className,
alt,
...rest
}) => {
export const BlurImage = ({ height, width, src, className, alt, ...rest }) => {
const [isLoading, setLoading] = useState(true);
return (
<img
Expand All @@ -253,6 +164,7 @@ export const BlurImage = ({
decoding="async"
blurDataURL={typeof src === "string" ? src : undefined}
alt={alt ? alt : "Background of a beautiful view"}
{...rest} />
{...rest}
/>
);
};

0 comments on commit 715d7bc

Please sign in to comment.