UNPKG

xlibs-components

Version:

Modern React component library with Aceternity, MagicUI, and ShadCN components for building beautiful web applications

65 lines (64 loc) 4.58 kB
"use client"; import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; /** * Note: Use position fixed according to your needs * Desktop navbar is better positioned at the bottom * Mobile navbar is better positioned at bottom right. **/ import { cn } from '../../lib/utils.js'; import { IconLayoutNavbarCollapse } from "@tabler/icons-react"; import { AnimatePresence, motion, useMotionValue, useSpring, useTransform, } from "framer-motion"; import { useRef, useState } from "react"; export const FloatingDock = ({ items, desktopClassName, mobileClassName, }) => { return (_jsxs(_Fragment, { children: [_jsx(FloatingDockDesktop, { items: items, className: desktopClassName }), _jsx(FloatingDockMobile, { items: items, className: mobileClassName })] })); }; const FloatingDockMobile = ({ items, className, }) => { const [open, setOpen] = useState(false); return (_jsxs("div", { className: cn("relative block md:hidden", className), children: [_jsx(AnimatePresence, { children: open && (_jsx(motion.div, { layoutId: "nav", className: "absolute inset-x-0 bottom-full mb-2 flex flex-col gap-2", children: items.map((item, idx) => (_jsx(motion.div, { initial: { opacity: 0, y: 10 }, animate: { opacity: 1, y: 0, }, exit: { opacity: 0, y: 10, transition: { delay: idx * 0.05, }, }, transition: { delay: (items.length - 1 - idx) * 0.05 }, children: _jsx("a", { href: item.href, className: "flex h-10 w-10 items-center justify-center rounded-full bg-gray-50 dark:bg-neutral-900", children: _jsx("div", { className: "h-4 w-4", children: item.icon }) }, item.title) }, item.title))) })) }), _jsx("button", { onClick: () => setOpen(!open), className: "flex h-10 w-10 items-center justify-center rounded-full bg-gray-50 dark:bg-neutral-800", children: _jsx(IconLayoutNavbarCollapse, { className: "h-5 w-5 text-neutral-500 dark:text-neutral-400" }) })] })); }; const FloatingDockDesktop = ({ items, className, }) => { let mouseX = useMotionValue(Infinity); return (_jsx(motion.div, { onMouseMove: (e) => mouseX.set(e.pageX), onMouseLeave: () => mouseX.set(Infinity), className: cn("mx-auto hidden h-16 items-end gap-4 rounded-2xl bg-gray-50 px-4 pb-3 md:flex dark:bg-neutral-900", className), children: items.map((item) => (_jsx(IconContainer, { mouseX: mouseX, ...item }, item.title))) })); }; function IconContainer({ mouseX, title, icon, href, }) { let ref = useRef(null); let distance = useTransform(mouseX, (val) => { let bounds = ref.current?.getBoundingClientRect() ?? { x: 0, width: 0 }; return val - bounds.x - bounds.width / 2; }); let widthTransform = useTransform(distance, [-150, 0, 150], [40, 80, 40]); let heightTransform = useTransform(distance, [-150, 0, 150], [40, 80, 40]); let widthTransformIcon = useTransform(distance, [-150, 0, 150], [20, 40, 20]); let heightTransformIcon = useTransform(distance, [-150, 0, 150], [20, 40, 20]); let width = useSpring(widthTransform, { mass: 0.1, stiffness: 150, damping: 12, }); let height = useSpring(heightTransform, { mass: 0.1, stiffness: 150, damping: 12, }); let widthIcon = useSpring(widthTransformIcon, { mass: 0.1, stiffness: 150, damping: 12, }); let heightIcon = useSpring(heightTransformIcon, { mass: 0.1, stiffness: 150, damping: 12, }); const [hovered, setHovered] = useState(false); return (_jsx("a", { href: href, children: _jsxs(motion.div, { ref: ref, style: { width, height }, onMouseEnter: () => setHovered(true), onMouseLeave: () => setHovered(false), className: "relative flex aspect-square items-center justify-center rounded-full bg-gray-200 dark:bg-neutral-800", children: [_jsx(AnimatePresence, { children: hovered && (_jsx(motion.div, { initial: { opacity: 0, y: 10, x: "-50%" }, animate: { opacity: 1, y: 0, x: "-50%" }, exit: { opacity: 0, y: 2, x: "-50%" }, className: "absolute -top-8 left-1/2 w-fit rounded-md border border-gray-200 bg-gray-100 px-2 py-0.5 text-xs whitespace-pre text-neutral-700 dark:border-neutral-900 dark:bg-neutral-800 dark:text-white", children: title })) }), _jsx(motion.div, { style: { width: widthIcon, height: heightIcon }, className: "flex items-center justify-center", children: icon })] }) })); }