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
JavaScript
"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 })] }) }));
}