UNPKG

reactbits-mcp-server

Version:

MCP Server for React Bits - Access 99+ React components with animations, backgrounds, and UI elements

167 lines (154 loc) 4.2 kB
"use client"; import { motion, useMotionValue, useSpring, useTransform, AnimatePresence, } from "framer-motion"; import { Children, cloneElement, useEffect, useMemo, useRef, useState, } from "react"; function DockItem({ children, className = "", onClick, mouseX, spring, distance, magnification, baseItemSize, }) { const ref = useRef(null); const isHovered = useMotionValue(0); const mouseDistance = useTransform(mouseX, (val) => { const rect = ref.current?.getBoundingClientRect() ?? { x: 0, width: baseItemSize, }; return val - rect.x - baseItemSize / 2; }); const targetSize = useTransform( mouseDistance, [-distance, 0, distance], [baseItemSize, magnification, baseItemSize] ); const size = useSpring(targetSize, spring); return ( <motion.div ref={ref} style={{ width: size, height: size, }} onHoverStart={() => isHovered.set(1)} onHoverEnd={() => isHovered.set(0)} onFocus={() => isHovered.set(1)} onBlur={() => isHovered.set(0)} onClick={onClick} className={`relative inline-flex items-center justify-center rounded-full bg-[#060010] border-neutral-700 border-2 shadow-md ${className}`} tabIndex={0} role="button" aria-haspopup="true" > {Children.map(children, (child) => cloneElement(child, { isHovered }) )} </motion.div> ); } function DockLabel({ children, className = "", ...rest }) { const { isHovered } = rest; const [isVisible, setIsVisible] = useState(false); useEffect(() => { const unsubscribe = isHovered.on("change", (latest) => { setIsVisible(latest === 1); }); return () => unsubscribe(); }, [isHovered]); return ( <AnimatePresence> {isVisible && ( <motion.div initial={{ opacity: 0, y: 0 }} animate={{ opacity: 1, y: -10 }} exit={{ opacity: 0, y: 0 }} transition={{ duration: 0.2 }} className={`${className} absolute -top-6 left-1/2 w-fit whitespace-pre rounded-md border border-neutral-700 bg-[#060010] px-2 py-0.5 text-xs text-white`} role="tooltip" style={{ x: "-50%" }} > {children} </motion.div> )} </AnimatePresence> ); } function DockIcon({ children, className = "" }) { return ( <div className={`flex items-center justify-center ${className}`}> {children} </div> ); } export default function Dock({ items, className = "", spring = { mass: 0.1, stiffness: 150, damping: 12 }, magnification = 70, distance = 200, panelHeight = 64, dockHeight = 256, baseItemSize = 50, }) { const mouseX = useMotionValue(Infinity); const isHovered = useMotionValue(0); const maxHeight = useMemo( () => Math.max(dockHeight, magnification + magnification / 2 + 4), [magnification, dockHeight] ); const heightRow = useTransform(isHovered, [0, 1], [panelHeight, maxHeight]); const height = useSpring(heightRow, spring); return ( <motion.div style={{ height, scrollbarWidth: "none" }} className="mx-2 flex max-w-full items-center" > <motion.div onMouseMove={({ pageX }) => { isHovered.set(1); mouseX.set(pageX); }} onMouseLeave={() => { isHovered.set(0); mouseX.set(Infinity); }} className={`${className} absolute bottom-2 left-1/2 transform -translate-x-1/2 flex items-end w-fit gap-4 rounded-2xl border-neutral-700 border-2 pb-2 px-4`} style={{ height: panelHeight }} role="toolbar" aria-label="Application dock" > {items.map((item, index) => ( <DockItem key={index} onClick={item.onClick} className={item.className} mouseX={mouseX} spring={spring} distance={distance} magnification={magnification} baseItemSize={baseItemSize} > <DockIcon>{item.icon}</DockIcon> <DockLabel>{item.label}</DockLabel> </DockItem> ))} </motion.div> </motion.div> ); }