UNPKG

lightswind

Version:

A professionally designed animate react component library & templates market that brings together functionality, accessibility, and beautiful aesthetics for modern applications.

135 lines (134 loc) 8.4 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { useEffect, useRef, useState, useCallback } from 'react'; import { cn } from '../lib/utils'; export const MorphingNavigation = ({ links, scrollThreshold = 100, enablePageBlur = true, theme = 'glass', backgroundColor, textColor, borderColor, initialTop = 70, compactTop = 20, animationDuration = 1, className, onLinkClick, onMenuToggle, enableSmoothTransitions = true, customHamburgerIcon, disableAutoMorph = false, }) => { const [isSticky, setIsSticky] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false); const headerRef = useRef(null); const navRef = useRef(null); // Theme styles const getThemeStyles = useCallback(() => { switch (theme) { case 'dark': return { nav: 'bg-black/80 border-gray-800', text: 'text-white', button: 'bg-black/50 border-gray-700', }; case 'light': return { nav: 'bg-white/80 border-gray-200', text: 'text-gray-900', button: 'bg-white/50 border-gray-300', }; case 'custom': return { nav: backgroundColor ? '' : 'bg-white/5 border-white/10', text: textColor ? '' : 'text-white', button: 'bg-black/30 border-white/10', }; case 'glass': default: return { nav: 'bg-white/5 border-white/10', text: 'text-white', button: 'bg-black/30 border-white/10', }; } }, [theme, backgroundColor, textColor]); const themeStyles = getThemeStyles(); // Handle scroll events useEffect(() => { if (disableAutoMorph) return; const handleScroll = () => { setIsMenuOpen(false); const shouldBeSticky = window.scrollY >= scrollThreshold; setIsSticky(shouldBeSticky); }; window.addEventListener('scroll', handleScroll); return () => window.removeEventListener('scroll', handleScroll); }, [scrollThreshold, disableAutoMorph]); // Handle menu toggle const handleMenuToggle = () => { const newIsOpen = !isMenuOpen; setIsMenuOpen(newIsOpen); setIsSticky(false); onMenuToggle?.(newIsOpen); }; // Handle link click const handleLinkClick = (link, event) => { event.preventDefault(); setIsMenuOpen(false); onLinkClick?.(link); if (enableSmoothTransitions) { const targetElement = document.querySelector(link.href); if (targetElement) { targetElement.scrollIntoView({ behavior: 'smooth', block: 'start', }); } } }; // Handle outside click to close menu useEffect(() => { const handleOutsideClick = (event) => { if (navRef.current && !navRef.current.contains(event.target) && isMenuOpen) { setIsMenuOpen(false); } }; document.addEventListener('click', handleOutsideClick); return () => document.removeEventListener('click', handleOutsideClick); }, [isMenuOpen]); // Custom styles const customStyles = { backgroundColor: theme === 'custom' && backgroundColor ? backgroundColor : undefined, color: theme === 'custom' && textColor ? textColor : undefined, borderColor: theme === 'custom' && borderColor ? borderColor : undefined, }; return (_jsxs(_Fragment, { children: [enablePageBlur && isMenuOpen && (_jsx("div", { className: "fixed inset-0 bg-black/20 backdrop-blur-sm transition-all duration-1000 ease-in-out z-40", style: { opacity: isMenuOpen ? 1 : 0, visibility: isMenuOpen ? 'visible' : 'hidden', } })), _jsx("header", { ref: headerRef, className: cn('fixed z-50 transition-all duration-1000 ease-in-out', className), style: { animationDuration: `${animationDuration}s`, }, children: _jsxs("nav", { ref: navRef, className: cn('flex justify-center items-center fixed left-0 right-0 mx-auto backdrop-blur-md border transition-all duration-1000 ease-in-out', themeStyles.nav, themeStyles.text, { // Expanded state 'h-[100px] w-[500px] px-5 rounded-full': !isSticky, // Compact state 'h-[90px] w-[90px] px-0 rounded-full': isSticky, }), style: { top: isSticky ? `${compactTop}px` : `${initialTop}px`, transitionDelay: isSticky ? '0.5s' : '0.2s', ...customStyles, }, children: [links.map((link) => (_jsxs("a", { href: link.href, onClick: (e) => handleLinkClick(link, e), className: cn('font-bold block leading-none text-sm tracking-[2px] lowercase transition-all duration-300 ease-in-out', themeStyles.text, { 'px-5 py-2.5 opacity-100 scale-100': !isSticky, 'px-0 py-0 opacity-0 scale-[0.3] pointer-events-none': isSticky, }), style: { transitionDelay: isSticky ? '0.2s' : '0.6s', letterSpacing: isSticky ? '0px' : '2px', }, children: [link.icon && (_jsx("span", { className: "inline-block mr-2", children: link.icon })), link.label] }, link.id))), _jsx("button", { onClick: handleMenuToggle, className: cn('absolute left-0 right-0 top-0 bottom-0 m-auto w-[60px] h-[60px] rounded-full outline-none border cursor-pointer backdrop-blur-md transition-all duration-300 ease-in-out', themeStyles.button, { 'scale-0': !isSticky, 'scale-100': isSticky, 'border-white/50': isSticky && theme === 'glass', }), style: { transitionDelay: isSticky ? '0.6s' : '0.2s', }, "aria-label": "Toggle menu", children: customHamburgerIcon || (_jsxs("div", { className: "flex flex-col items-center justify-center h-full", children: [_jsx("span", { className: cn('block h-0.5 bg-current transition-all duration-600 ease-in-out', { 'w-[40%] scale-x-0': !isSticky, 'w-[40%] scale-x-100': isSticky, 'my-1': !isMenuOpen, 'my-2.5': isMenuOpen, }), style: { transitionDelay: isSticky ? '0.8s' : '0s', } }), _jsx("span", { className: cn('block h-0.5 bg-current transition-all duration-600 ease-in-out', { 'w-[40%] scale-x-0': !isSticky, 'w-[40%] scale-x-100': isSticky, 'my-1': !isMenuOpen, 'my-2.5': isMenuOpen, }), style: { transitionDelay: isSticky ? '0.8s' : '0s', } })] })) })] }) }), isMenuOpen && (_jsx("div", { className: "fixed inset-0 z-40 flex items-center justify-center", children: _jsx("div", { className: cn('p-8 rounded-2xl backdrop-blur-md border transition-all duration-500', themeStyles.nav, themeStyles.text), style: customStyles, children: _jsx("div", { className: "flex flex-col space-y-4", children: links.map((link) => (_jsxs("a", { href: link.href, onClick: (e) => handleLinkClick(link, e), className: cn('font-bold text-lg tracking-wide lowercase hover:scale-105 transition-transform duration-200', themeStyles.text), children: [link.icon && (_jsx("span", { className: "inline-block mr-3", children: link.icon })), link.label] }, link.id))) }) }) }))] })); }; export default MorphingNavigation;