"use client"; import { useId, useState, type MouseEvent, type ReactNode } from "react"; import { motion, useReducedMotion } from "motion/react"; export type BubbleDockItem = { label: string; href: string; /** Icon (an svg works best). */ icon: ReactNode; }; type BubbleDockProps = { items: BubbleDockItem[]; /** href of the active link at start. Defaults to the first item. */ active?: string; /** Called on click. Call e.preventDefault() to stop the browser following the link. */ onSelect?: (item: BubbleDockItem, e: MouseEvent) => void; /** Bar height in pixels. The bubble is the same size. */ size?: number; /** Bar and bubble colour. */ color?: string; /** Colour of the icon inside the bubble. */ activeColor?: string; /** Name read out by screen readers. */ label?: string; /** Icon / label colour on the bar. Width comes from the parent (full width). */ className?: string; }; /* BubbleDock: a tab bar where the active icon rises in a bubble that melts out of the bar, and the bubble rolls to whichever tab you pick. */ export function BubbleDock({ items, active, onSelect, size = 60, color = "#241e44", activeColor = "#a78bfa", label = "Tabs", className = "text-white/60", }: BubbleDockProps) { const reduce = useReducedMotion(); const filterId = `bubble-${useId().replace(/:/g, "")}`; const [current, setCurrent] = useState(active ?? items[0]?.href); const index = Math.max(0, items.findIndex((i) => i.href === current)); const n = items.length || 1; const rise = size * 0.5; const roll = reduce ? { duration: 0 } : { type: "spring" as const, stiffness: 260, damping: 24 }; return ( ); }