elastic-stack
vengenceui/elastic-stackFreeComponent
vengenceui publishes no description for this item.
Live preview
live · rendered by the registry
Rendered by vengenceui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://www.vengenceui.com/r/elastic-stack.jsonSource
1"use client";23import React, { useState } from "react";4import { cn } from "@/lib/utils";56export interface ElasticStackItem {7 id: string | number;8 image?: string;9 name?: string;10}1112export interface ElasticStackProps extends React.HTMLAttributes<HTMLDivElement> {13 items: ElasticStackItem[];14 itemSize?: number;15 overlap?: number;16 pushForce?: number;17}1819export function ElasticStack({20 items,21 itemSize = 70,22 overlap = 30,23 pushForce = 15,24 className,25 ...props26}: ElasticStackProps) {27 const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);2829 const total = items.length;30 // Custom spring-like easing from the original CSS31 const springEasing = "linear(0, 0.79 14.4%, 1.026 22.4%, 1.164 31.2%, 1.207 38.2%, 1.208 46.2%, 1.033 80%, 1)";3233 return (34 <div35 className={cn("flex items-center justify-center cursor-pointer py-8", className)}36 onMouseLeave={() => setHoveredIndex(null)}37 {...props}38 >39 {items.map((item, i) => {40 let translateX = 0;41 let scale = 1;42 let zIndex = i; // Base stacking order43 let isHovered = hoveredIndex === i;4445 if (hoveredIndex !== null) {46 if (i > hoveredIndex) {47 translateX = Math.min(pushForce * (total - i - 1), overlap);48 } else if (i < hoveredIndex) {49 translateX = -Math.min(pushForce * i, overlap);50 } else {51 scale = 1.25;52 zIndex = 100;53 }54 }5556 return (57 <div58 key={item.id}59 onMouseEnter={() => setHoveredIndex(i)}60 className={cn(61 "relative flex items-center justify-center rounded-full isolate transition-all duration-700 bg-neutral-100 dark:bg-neutral-800",62 "border-2 border-white dark:border-neutral-950",63 isHovered ? "shadow-xl" : "shadow-sm"64 )}65 style={{66 width: itemSize,67 height: itemSize,68 marginLeft: i === 0 ? 0 : -overlap,69 transform: `translateX(${translateX}px) scale(${scale})`,70 transitionTimingFunction: springEasing,71 zIndex,72 }}73 >74 {item.image ? (75 // eslint-disable-next-line @next/next/no-img-element76 <img77 src={item.image}78 alt={item.name || `Avatar ${i}`}79 className="w-full h-full object-cover rounded-full pointer-events-none"80 />81// … truncated
Files it writes
More from vengenceui
All 128 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| accordionaccordion | vengenceui | Components | Free | 2 deps | |
| agent-bento-gridagent-bento-grid | vengenceui | Components | Free | 1 dep | |
| alertalert | vengenceui | Components | Free | no deps | |
| animated-buttonanimated-button | vengenceui | Components | Free | 1 dep | |
| animated-footeranimated-footer | vengenceui | Components | Free | 2 deps | |
| animated-numberanimated-number | vengenceui | Components | Free | 1 dep | |
| animated-raysanimated-rays | vengenceui | Components | Free | no deps | |
| animated-tooltipanimated-tooltip | vengenceui | Components | Free | 1 dep |
