{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "logo-timeline",
  "title": "logo-timeline",
  "description": "An animated logo timeline component with horizontal scrolling logos.",
  "dependencies": [
    "react",
    "motion"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/eldoraui/logo-timeline.tsx",
      "content": "\"use client\"\n\nimport type React from \"react\"\nimport { useState } from \"react\"\nimport { motion } from \"motion/react\"\n\nimport { cn } from \"@/lib/utils\"\nimport { Icons } from \"@/components/icons\"\n\nexport interface LogoItem {\n  /** The label text displayed next to the icon */\n  label: string\n  /** The icon name from the Icons object (e.g., \"gitHub\", \"react\", \"tailwind\") */\n  icon: keyof typeof Icons\n  /** Animation delay in seconds (use negative values for staggered effect) */\n  animationDelay: number\n  /** Animation duration in seconds */\n  animationDuration: number\n  /** The row number where this logo should appear (1-based) */\n  row: number\n}\n\nexport interface LogoTimelineProps {\n  /** Array of logo items to display */\n  items: LogoItem[]\n  /** Optional title text to display in the center */\n  title?: string\n  /** Height of the timeline container */\n  height?: string\n  /** Additional className for the container */\n  className?: string\n  /** Icon size in pixels (default: 16) */\n  iconSize?: number\n  /** Whether to show separator lines between rows (default: true) */\n  showRowSeparator?: boolean\n  /** Whether to animate logos only on hover (default: false) */\n  animateOnHover?: boolean\n}\n\nexport function LogoTimeline({\n  items,\n  title,\n  height = \"h-[400px] sm:h-[800px]\",\n  className,\n  iconSize = 16,\n  showRowSeparator = true,\n  animateOnHover = false,\n}: LogoTimelineProps) {\n  const [isHovered, setIsHovered] = useState(false)\n\n  // Group items by row\n  const rowsMap = new Map<number, LogoItem[]>()\n  items.forEach((item) => {\n    if (!rowsMap.has(item.row)) {\n      rowsMap.set(item.row, [])\n    }\n    rowsMap.get(item.row)?.push(item)\n  })\n\n  // Convert map to sorted array\n  const rows = Array.from(rowsMap.entries())\n    .sort(([a], [b]) => a - b)\n    .map(([, rowItems]) => rowItems)\n\n  // Determine animation play state\n  const animationPlayState = animateOnHover\n    ? isHovered\n      ? \"running\"\n      : \"paused\"\n    : \"running\"\n\n  const getIconComponent = (iconName: keyof typeof Icons) => {\n    const Icon = Icons[iconName]\n    if (!Icon) {\n      return null\n    }\n    return (\n      <Icon\n        className=\"shrink-0\"\n        style={{ width: iconSize, height: iconSize }}\n        aria-hidden=\"true\"\n      />\n    )\n  }\n\n  return (\n    <section className={cn(\"w-full\", height, className)}>\n      <motion.div\n        aria-hidden=\"true\"\n        className=\"bg-background relative h-full w-full overflow-hidden py-24 ring-inset sm:py-32\"\n        onMouseEnter={() => animateOnHover && setIsHovered(true)}\n        onMouseLeave={() => animateOnHover && setIsHovered(false)}\n      >\n        {title && (\n          <div className=\"absolute top-1/2 left-1/2 mx-auto w-full max-w-[90%] -translate-x-1/2 -translate-y-1/2 text-center\">\n            <div className=\"relative z-10\">\n              <p className=\"text-foreground/10 mx-auto mt-2 max-w-3xl text-4xl font-semibold tracking-tight text-pretty sm:text-5xl md:text-6xl\">\n                {title}\n              </p>\n            </div>\n          </div>\n        )}\n        <div\n          className=\"[container-type:inline-size] absolute inset-0 grid\"\n          style={{ gridTemplateRows: `repeat(${rows.length}, 1fr)` }}\n        >\n          {rows.map((rowItems, index) => (\n            <div className=\"group relative flex items-center\" key={index}>\n              <div className=\"from-foreground/15 dark:from-foreground/15 absolute inset-x-0 top-1/2 h-0.5 bg-gradient-to-r from-[2px] to-[2px] bg-[length:12px_100%]\" />\n              {showRowSeparator && (\n                <div className=\"from-foreground/5 dark:from-foreground/5 absolute inset-x-0 bottom-0 h-0.5 bg-gradient-to-r from-[2px] to-[2px] bg-[length:12px_100%] group-last:hidden\" />\n              )}\n              {rowItems.map((logo) => {\n                const IconComponent = getIconComponent(logo.icon)\n                if (!IconComponent) {\n                  return null\n                }\n                return (\n                  <div\n                    key={`${logo.row}-${logo.label}`}\n                    className={cn(\n                      \"absolute top-1/2 flex -translate-y-1/2 items-center gap-2 px-3 py-1.5 whitespace-nowrap\",\n                      \"from-secondary/50 to-secondary/50 ring-background/10 dark:ring-foreground/10 rounded-full bg-gradient-to-t from-50% ring-1 backdrop-blur-sm ring-inset\",\n                      \"[--move-x-from:-100%] [--move-x-to:calc(100%+100cqw)] [animation-iteration-count:infinite] [animation-name:move-x] [animation-timing-function:linear]\",\n                      \"shadow-[0_0_15px_rgba(0,0,0,0.1)] dark:shadow-none\"\n                    )}\n                    style={{\n                      animationDelay: `${logo.animationDelay}s`,\n                      animationDuration: `${logo.animationDuration}s`,\n                      animationPlayState: animationPlayState,\n                    }}\n                  >\n                    {IconComponent}\n                    <span className=\"text-foreground text-sm/6 font-medium\">\n                      {logo.label}\n                    </span>\n                  </div>\n                )\n              })}\n            </div>\n          ))}\n        </div>\n      </motion.div>\n    </section>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "css": {
    "@keyframes move-x": {
      "from": {
        "transform": "translateX(-100%)"
      },
      "to": {
        "transform": "translateX(100%)"
      }
    }
  },
  "type": "registry:ui"
}