lightswind
Version:
A professionally designed animate react component library & templates market that brings together functionality, accessibility, and beautiful aesthetics for modern applications.
209 lines (185 loc) • 8.74 kB
JavaScript
"use client";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useRef, useEffect, useState } from "react";
import { cn } from "../lib/utils";
import { Pause, Play } from "lucide-react";
export function SlidingLogoMarquee({ items, speed = 60, pauseOnHover = true, enableBlur = true, blurIntensity = 1, height = "100px", width = "100%", gap = "0.5rem", scale = 1, direction = "horizontal", autoPlay = true, backgroundColor, showGridBackground = false, className, onItemClick, enableSpillEffect = false, animationSteps = 8, showControls = true, }) {
const containerRef = useRef(null);
const [isPlaying, setIsPlaying] = useState(autoPlay);
const [dimensions, setDimensions] = useState({ width: 0, height: 0 });
useEffect(() => {
const updateDimensions = () => {
if (containerRef.current) {
const rect = containerRef.current.getBoundingClientRect();
setDimensions({ width: rect.width, height: rect.height });
}
};
updateDimensions();
window.addEventListener("resize", updateDimensions);
return () => window.removeEventListener("resize", updateDimensions);
}, []);
const handleItemClick = (item) => {
if (item.href) {
window.open(item.href, "_blank", "noopener,noreferrer");
}
onItemClick?.(item);
};
const togglePlayState = () => {
setIsPlaying(!isPlaying);
};
const blurDivs = Array.from({ length: animationSteps }, (_, index) => (_jsx("div", { style: { "--index": index } }, index)));
return (_jsxs(_Fragment, { children: [_jsx("style", { children: `
.sliding-marquee-container {
--speed: ${speed};
--count: ${items.length};
--scale: ${scale};
--blur: ${blurIntensity};
--blurs: ${animationSteps};
}
.sliding-marquee-resizable {
overflow: clip;
container-type: size;
scale: var(--scale);
width: 100%;
height: ${height};
min-height: 100px;
min-width: 300px;
}
@media (min-width: 600px) {
.sliding-marquee-resizable {
min-width: 500px;
}
}
@media (min-width: 1024px) {
.sliding-marquee-resizable {
min-width: 800px;
}
}
.sliding-marquee-resizable[data-spill="true"] .sliding-marquee-inner::after {
content: "";
position: fixed;
top: 50%;
left: 50%;
width: calc(var(--scale) * 10000vw);
height: calc(var(--scale) * 10000vh);
pointer-events: none;
translate: -50% -50%;
mask: linear-gradient(white, white) 50% 50% / 100% 100% no-repeat,
linear-gradient(white, white) 50% 50% / 100cqi 100cqh no-repeat;
mask-composite: exclude;
}
.sliding-marquee-inner {
height: 100%;
width: 100%;
position: relative;
mask: linear-gradient(90deg, transparent, black 15% 85%, transparent);
display: grid;
min-height: 100px;
min-width: 300px;
pointer-events: none;
}
.sliding-marquee-blur {
position: absolute;
top: 0;
bottom: 0;
width: 25%;
z-index: 2;
pointer-events: none;
}
.sliding-marquee-blur--right {
right: 0;
}
.sliding-marquee-blur--left {
left: 0;
rotate: 180deg;
}
.sliding-marquee-blur div {
position: absolute;
inset: 0;
z-index: var(--index);
mask: linear-gradient(90deg,
transparent calc(var(--index) * calc((100 / var(--blurs)) * 1%)),
black calc((var(--index) + 1) * calc((100 / var(--blurs)) * 1%)),
black calc((var(--index) + 2) * calc((100 / var(--blurs)) * 1%)),
transparent calc((var(--index) + 3) * calc((100 / var(--blurs)) * 1%)));
backdrop-filter: blur(calc((var(--index, 0) * var(--blur, 0)) * 1px));
}
.sliding-marquee-list {
display: flex;
gap: ${gap};
padding: 0;
margin: 0;
list-style-type: none;
height: 100%;
width: fit-content;
align-items: center;
pointer-events: auto;
}
.sliding-marquee-item {
height: 80%;
aspect-ratio: 16 / 9;
font-size: clamp(1rem, 3vw + 0.5rem, 4rem);
display: grid;
place-items: center;
cursor: pointer;
transition: transform 0.2s ease;
pointer-events: auto;
}
.sliding-marquee-item:hover {
transform: scale(1.05);
}
.sliding-marquee-item svg {
height: 65%;
}
@media (max-width: 767px) {
.sliding-marquee-list {
gap: 0.25rem !important;
}
.sliding-marquee-item {
height: 60% !important;
font-size: 0.875rem !important;
}
.sliding-marquee-item svg {
height: 45% !important;
}
}
[data-play-state="running"] .sliding-marquee-list,
[data-play-state="running"] .sliding-marquee-item {
animation-play-state: running !important;
}
[data-play-state="paused"] .sliding-marquee-list,
[data-play-state="paused"] .sliding-marquee-item {
animation-play-state: paused !important;
}
@media (prefers-reduced-motion: no-preference) {
[data-translate="items"] .sliding-marquee-list {
gap: 0;
}
[data-translate="items"][data-direction="horizontal"] .sliding-marquee-inner {
padding-inline: 0;
}
[data-translate="items"] .sliding-marquee-item {
--duration: calc(var(--speed) * 1s);
--delay: calc((var(--duration) / var(--count)) * (var(--index, 0) * -1));
animation: slide var(--duration) var(--delay) infinite linear paused;
translate: var(--origin-x) var(--origin-y);
}
[data-translate="items"][data-direction="horizontal"] .sliding-marquee-item {
--origin-x: calc(((var(--count) - var(--index)) + var(--inset, 0)) * 100%);
--origin-y: 0;
--destination-x: calc(calc((var(--index) + 1 + var(--outset, 0)) * -100%));
--destination-y: 0;
}
@keyframes slide {
100% {
translate: var(--destination-x) var(--destination-y);
}
}
}
` }), _jsxs("div", { ref: containerRef, className: cn("sliding-marquee-container relative", className), style: { width, background: backgroundColor }, onMouseEnter: () => pauseOnHover && setIsPlaying(false), onMouseLeave: () => pauseOnHover && setIsPlaying(true), children: [showGridBackground && _jsx("div", { className: "" }), _jsx("div", { className: "sliding-marquee-resizable", "data-translate": "items", "data-direction": direction, "data-blurring": enableBlur, "data-play-state": isPlaying ? "running" : "paused", "data-spill": enableSpillEffect, children: _jsxs("div", { className: "sliding-marquee-inner", children: [enableBlur && (_jsx("div", { className: "sliding-marquee-blur sliding-marquee-blur--left", children: blurDivs })), _jsx("ul", { className: "sliding-marquee-list text-foreground", children: items.map((item, index) => (_jsx("li", { className: "sliding-marquee-item text-foreground", style: { "--index": index }, onClick: () => handleItemClick(item), role: "button", tabIndex: 0, onKeyDown: (e) => {
if (e.key === "Enter" || e.key === " ") {
handleItemClick(item);
}
}, children: item.content }, item.id))) }), enableBlur && (_jsx("div", { className: "sliding-marquee-blur sliding-marquee-blur--right", children: blurDivs }))] }) }), showControls && (_jsx("button", { onClick: togglePlayState, className: "absolute top-0 right-0 z-10 px-2 py-1 text-xs bg-white/10 text-foreground\r\n rounded hover:bg-background/20 transition-colors", "aria-label": isPlaying ? "Pause animation" : "Play animation", children: isPlaying ? _jsx(Pause, {}) : _jsx(Play, {}) }))] })] }));
}
export default SlidingLogoMarquee;