{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"panel-glide","type":"registry:ui","title":"Panel Glide","description":"A row of image panels where the hovered or focused one glides open with its caption while the rest compress.","dependencies":[],"registryDependencies":[],"files":[{"path":"registry/ui/panel-glide.tsx","type":"registry:ui","content":"\"use client\";\n/* eslint-disable @next/next/no-img-element */\n\nimport { useRef, useState, type KeyboardEvent } from \"react\";\n\nexport interface PanelGlideItem {\n  src: string;\n  title: string;\n  subtitle?: string;\n}\n\nexport interface PanelGlideProps {\n  items: PanelGlideItem[];\n  /** Height of the row in px. */\n  height?: number;\n  /** How many times wider the open panel is than a closed one. */\n  expandRatio?: number;\n  /** Px between panels. */\n  gap?: number;\n  /** Corner radius in px. */\n  radius?: number;\n  /** Transition length in ms. */\n  duration?: number;\n  /** Index of the panel open at rest. */\n  defaultIndex?: number;\n  className?: string;\n}\n\nexport function PanelGlide({\n  items,\n  height = 380,\n  expandRatio = 5,\n  gap = 10,\n  radius = 20,\n  duration = 700,\n  defaultIndex = 0,\n  className,\n}: PanelGlideProps) {\n  const [active, setActive] = useState(defaultIndex);\n  const refs = useRef<(HTMLDivElement | null)[]>([]);\n  const ease = \"cubic-bezier(.22,.9,.24,1)\";\n\n  const onKey = (e: KeyboardEvent, i: number) => {\n    const step = e.key === \"ArrowRight\" || e.key === \"ArrowDown\" ? 1 : e.key === \"ArrowLeft\" || e.key === \"ArrowUp\" ? -1 : 0;\n    if (!step) return;\n    e.preventDefault();\n    refs.current[(i + step + items.length) % items.length]?.focus();\n  };\n\n  return (\n    <div role=\"list\" className={`flex w-full ${className ?? \"\"}`} style={{ height, gap }}>\n      {items.map((item, i) => {\n        const open = i === active;\n        return (\n          <div\n            key={item.src + i}\n            ref={(el) => {\n              refs.current[i] = el;\n            }}\n            role=\"listitem\"\n            tabIndex={0}\n            aria-label={item.subtitle ? `${item.title}, ${item.subtitle}` : item.title}\n            aria-current={open || undefined}\n            onPointerEnter={() => setActive(i)}\n            onFocus={() => setActive(i)}\n            onKeyDown={(e) => onKey(e, i)}\n            className=\"group relative min-w-0 cursor-pointer overflow-hidden bg-muted outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background motion-reduce:!transition-none\"\n            style={{\n              flex: `${open ? expandRatio : 1} 1 0%`,\n              borderRadius: radius,\n              transition: `flex-grow ${duration}ms ${ease}`,\n            }}\n          >\n            <img\n              src={item.src}\n              alt=\"\"\n              draggable={false}\n              className=\"absolute inset-0 size-full object-cover motion-reduce:!transition-none\"\n              style={{\n                transform: open ? \"scale(1)\" : \"scale(1.15)\",\n                filter: open ? \"none\" : \"saturate(.6) brightness(.7)\",\n                transition: `transform ${duration * 1.4}ms ${ease}, filter ${duration}ms ${ease}`,\n              }}\n            />\n            <div\n              aria-hidden\n              className=\"absolute inset-0 bg-gradient-to-t from-black/80 via-black/10 to-transparent\"\n              style={{ opacity: open ? 1 : 0.6, transition: `opacity ${duration}ms ${ease}` }}\n            />\n            {/* Closed: a vertical label. Open: the full caption. */}\n            <span\n              aria-hidden\n              className=\"absolute bottom-5 left-1/2 whitespace-nowrap text-sm font-medium tracking-wide text-white/85\"\n              style={{\n                writingMode: \"vertical-rl\",\n                transform: \"translateX(-50%) rotate(180deg)\",\n                opacity: open ? 0 : 1,\n                transition: `opacity ${open ? duration * 0.3 : duration * 0.6}ms ${open ? \"0ms\" : `${duration * 0.4}ms`}`,\n              }}\n            >\n              {item.title}\n            </span>\n            <div\n              aria-hidden\n              className=\"absolute inset-x-0 bottom-0 p-6\"\n              style={{\n                opacity: open ? 1 : 0,\n                transform: open ? \"translateY(0)\" : \"translateY(16px)\",\n                transition: open\n                  ? `opacity ${duration * 0.6}ms ${ease} ${duration * 0.35}ms, transform ${duration * 0.8}ms ${ease} ${duration * 0.35}ms`\n                  : `opacity 150ms, transform 150ms`,\n              }}\n            >\n              <p className=\"text-xs font-medium uppercase tracking-[.2em] text-white/60\">{String(i + 1).padStart(2, \"0\")}</p>\n              <p className=\"mt-1 whitespace-nowrap text-2xl font-semibold tracking-tight text-white\">{item.title}</p>\n              {item.subtitle && <p className=\"mt-1 whitespace-nowrap text-sm text-white/70\">{item.subtitle}</p>}\n            </div>\n          </div>\n        );\n      })}\n    </div>\n  );\n}\n"}]}