xlibs-components
Version:
Modern React component library with Aceternity, MagicUI, and ShadCN components for building beautiful web applications
101 lines (100 loc) • 7.58 kB
JavaScript
"use client";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import React, { useEffect, useRef, useState, createContext, useContext, } from "react";
import { IconArrowNarrowLeft, IconArrowNarrowRight, IconX, } from "@tabler/icons-react";
import { cn } from '../../lib/utils.js';
import { AnimatePresence, motion } from "framer-motion";
import { useOutsideClick } from "@/lib";
export const CarouselContext = createContext({
onCardClose: () => { },
currentIndex: 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);
useEffect(() => {
if (carouselRef.current) {
carouselRef.current.scrollLeft = initialScroll;
checkScrollability();
}
}, [initialScroll]);
const checkScrollability = () => {
if (carouselRef.current) {
const { scrollLeft, scrollWidth, clientWidth } = carouselRef.current;
setCanScrollLeft(scrollLeft > 0);
setCanScrollRight(scrollLeft < scrollWidth - clientWidth);
}
};
const scrollLeft = () => {
if (carouselRef.current) {
carouselRef.current.scrollBy({ left: -300, behavior: "smooth" });
}
};
const scrollRight = () => {
if (carouselRef.current) {
carouselRef.current.scrollBy({ left: 300, behavior: "smooth" });
}
};
const handleCardClose = (index) => {
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 isMobile = () => {
return window && window.innerWidth < 768;
};
return (_jsx(CarouselContext.Provider, { value: { onCardClose: handleCardClose, currentIndex }, children: _jsxs("div", { className: "relative w-full", children: [_jsxs("div", { className: "flex w-full overflow-x-scroll overscroll-x-auto scroll-smooth py-10 [scrollbar-width:none] md:py-20", ref: carouselRef, onScroll: checkScrollability, children: [_jsx("div", { className: cn("absolute right-0 z-[1000] h-auto w-[5%] overflow-hidden bg-gradient-to-l") }), _jsx("div", { className: cn("flex flex-row justify-start gap-4 pl-4", "mx-auto max-w-7xl"), children: items.map((item, index) => (_jsx(motion.div, { initial: {
opacity: 0,
y: 20,
}, animate: {
opacity: 1,
y: 0,
transition: {
duration: 0.5,
delay: 0.2 * index,
ease: "easeOut",
},
}, className: "rounded-3xl last:pr-[5%] md:last:pr-[33%]", children: item }, "card" + index))) })] }), _jsxs("div", { className: "mr-10 flex justify-end gap-2", children: [_jsx("button", { className: "relative z-40 flex h-10 w-10 items-center justify-center rounded-full bg-gray-100 disabled:opacity-50", onClick: scrollLeft, disabled: !canScrollLeft, children: _jsx(IconArrowNarrowLeft, { className: "h-6 w-6 text-gray-500" }) }), _jsx("button", { className: "relative z-40 flex h-10 w-10 items-center justify-center rounded-full bg-gray-100 disabled:opacity-50", onClick: scrollRight, disabled: !canScrollRight, children: _jsx(IconArrowNarrowRight, { className: "h-6 w-6 text-gray-500" }) })] })] }) }));
};
export const Card = ({ card, index, layout = false, }) => {
const [open, setOpen] = useState(false);
const containerRef = useRef(null);
const { onCardClose } = 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);
};
return (_jsxs(_Fragment, { children: [_jsx(AnimatePresence, { children: open && (_jsxs("div", { className: "fixed inset-0 z-50 h-screen overflow-auto", children: [_jsx(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, className: "fixed inset-0 h-full w-full bg-black/80 backdrop-blur-lg" }), _jsxs(motion.div, { initial: { opacity: 0 }, animate: { opacity: 1 }, exit: { opacity: 0 }, ref: containerRef, layoutId: layout ? `card-${card.title}` : undefined, className: "relative z-[60] mx-auto my-10 h-fit max-w-5xl rounded-3xl bg-white p-4 font-sans md:p-10 dark:bg-neutral-900", children: [_jsx("button", { className: "sticky top-4 right-0 ml-auto flex h-8 w-8 items-center justify-center rounded-full bg-black dark:bg-white", onClick: handleClose, children: _jsx(IconX, { className: "h-6 w-6 text-neutral-100 dark:text-neutral-900" }) }), _jsx(motion.p, { layoutId: layout ? `category-${card.title}` : undefined, className: "text-base font-medium text-black dark:text-white", children: card.category }), _jsx(motion.p, { layoutId: layout ? `title-${card.title}` : undefined, className: "mt-4 text-2xl font-semibold text-neutral-700 md:text-5xl dark:text-white", children: card.title }), _jsx("div", { className: "py-10", children: card.content })] })] })) }), _jsxs(motion.button, { layoutId: layout ? `card-${card.title}` : undefined, onClick: handleOpen, className: "relative z-10 flex h-80 w-56 flex-col items-start justify-start overflow-hidden rounded-3xl bg-gray-100 md:h-[40rem] md:w-96 dark:bg-neutral-900", children: [_jsx("div", { className: "pointer-events-none absolute inset-x-0 top-0 z-30 h-full bg-gradient-to-b from-black/50 via-transparent to-transparent" }), _jsxs("div", { className: "relative z-40 p-8", children: [_jsx(motion.p, { layoutId: layout ? `category-${card.category}` : undefined, className: "text-left font-sans text-sm font-medium text-white md:text-base", children: card.category }), _jsx(motion.p, { layoutId: layout ? `title-${card.title}` : undefined, className: "mt-2 max-w-xs text-left font-sans text-xl font-semibold [text-wrap:balance] text-white md:text-3xl", children: card.title })] }), _jsx(BlurImage, { src: card.src, alt: card.title, className: "absolute inset-0 z-10 object-cover" })] })] }));
};
export const BlurImage = ({ height, width, src, className, alt, ...rest }) => {
const [isLoading, setLoading] = useState(true);
return (_jsx("img", { className: cn("h-full w-full transition duration-300", isLoading ? "blur-sm" : "blur-0", className), onLoad: () => setLoading(false), src: src, width: width, height: height, loading: "lazy", decoding: "async", alt: alt ? alt : "Background of a beautiful view", ...rest }));
};