{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "breadcrumb-icon", "type": "registry:ui", "title": "Breadcrumb Icon", "description": "Breadcrumb navigation with icons for each item.", "dependencies": [ "lucide-react" ], "files": [ { "path": "registry/ruixenui/breadcrumb-icon.tsx", "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { Home, type LucideIcon } from \"lucide-react\";\n\n/* ═══════════════════════════════════════════════════════════\n Breadcrumb Icon — Magnetic pill-tracking breadcrumb.\n\n Flips the cascade metaphor on its head. Instead of dimming\n everything else when you hover, a soft pill highlight\n slides TO the item you're pointing at — the highlight\n comes to you. The hovered icon does a spring micro-bounce\n for tactile feedback without disturbing its neighbors.\n\n Two fundamentally different interaction approaches:\n • breadcrumb-separator: hover affects OTHER items (cascade)\n • breadcrumb-icon: hover affects ONLY the target (pill tracks)\n\n Curves:\n • Pill slide: cubic-bezier(0.33, 1.52, 0.58, 1) — spring overshoot\n • Icon pop: cubic-bezier(0.34, 1.56, 0.64, 1) — tactile bounce\n ═══════════════════════════════════════════════════════════ */\n\nexport interface BreadcrumbIconItem {\n label: string;\n href?: string;\n icon?: LucideIcon;\n}\n\nexport interface BreadcrumbIconProps {\n items: BreadcrumbIconItem[];\n showHomeIcon?: boolean;\n iconOnly?: boolean;\n className?: string;\n}\n\nexport function BreadcrumbIcon({\n items,\n showHomeIcon = true,\n iconOnly = false,\n className,\n}: BreadcrumbIconProps) {\n const [hoveredIndex, setHoveredIndex] = React.useState(null);\n const [pill, setPill] = React.useState({ left: 0, width: 0 });\n const containerRef = React.useRef(null);\n const itemRefs = React.useRef<(HTMLElement | null)[]>([]);\n const hasPositioned = React.useRef(false);\n\n const itemsWithIcons = items.map((item, index) => ({\n ...item,\n icon: item.icon || (index === 0 && showHomeIcon ? Home : undefined),\n }));\n\n const handleHover = React.useCallback((index: number) => {\n setHoveredIndex(index);\n const el = itemRefs.current[index];\n const container = containerRef.current;\n if (el && container) {\n const containerRect = container.getBoundingClientRect();\n const elRect = el.getBoundingClientRect();\n setPill({\n left: elRect.left - containerRect.left,\n width: elRect.width,\n });\n }\n }, []);\n\n React.useEffect(() => {\n if (hoveredIndex !== null) {\n hasPositioned.current = true;\n }\n }, [hoveredIndex]);\n\n if (items.length === 0) return null;\n\n return (\n \n );\n}\n", "type": "registry:ui", "target": "components/ruixen/breadcrumb-icon.tsx" } ] }