Initial commit
This commit is contained in:
@@ -0,0 +1,40 @@
|
||||
/* Overlay - active state */
|
||||
[data-navigation-status="active"] .centered-nav__overlay {
|
||||
opacity: 0.15;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* Content expand - grid animation */
|
||||
.centered-nav__content {
|
||||
grid-template-rows: 0fr;
|
||||
}
|
||||
|
||||
[data-navigation-status="active"] .centered-nav__content {
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
/* Inner container - height hack for grid animation */
|
||||
.centered-nav__inner {
|
||||
height: 10000%;
|
||||
}
|
||||
|
||||
/* Link content slide up animation */
|
||||
.centered-nav__link-content {
|
||||
transition: transform 0.6s cubic-bezier(0.65, 0, 0, 1);
|
||||
transform: translateY(150%);
|
||||
transition-delay: inherit;
|
||||
}
|
||||
|
||||
[data-navigation-status="active"] .centered-nav__link-content {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* Separator width animation */
|
||||
.centered-nav__separator {
|
||||
width: 0;
|
||||
transition: width 0.6s cubic-bezier(0.65, 0, 0, 1);
|
||||
}
|
||||
|
||||
[data-navigation-status="active"] .centered-nav__separator {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useCallback, memo, Fragment } from "react";
|
||||
import Logo from "../Logo";
|
||||
import HamburgerButton from "../HamburgerButton";
|
||||
import Button from "@/components/button/Button";
|
||||
import { NavItem } from "@/types/navigation";
|
||||
import { cls } from "@/lib/utils";
|
||||
import { getButtonProps } from "@/lib/buttonUtils";
|
||||
import { useButtonClick } from "@/components/button/useButtonClick";
|
||||
import { useTheme } from "@/providers/themeProvider/ThemeProvider";
|
||||
import type { ButtonConfig } from "@/types/button";
|
||||
import "./NavbarStyleCentered.css";
|
||||
import { ArrowUpRight } from "lucide-react";
|
||||
|
||||
interface NavLinkProps {
|
||||
item: NavItem;
|
||||
index: number;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const NavLink = memo(function NavLink({ item, index, onClose }: NavLinkProps) {
|
||||
const handleClick = useButtonClick(item.id, onClose);
|
||||
|
||||
return (
|
||||
<li
|
||||
className="group m-0 p-0 list-none overflow-clip"
|
||||
style={{ transitionDelay: `${index * 0.05}s` }}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="centered-nav__link relative flex justify-between items-center no-underline w-full text-left bg-transparent border-none cursor-pointer"
|
||||
onClick={handleClick}
|
||||
>
|
||||
<div className="centered-nav__link-content flex justify-between items-center gap-3 w-full">
|
||||
<p className="m-0 text-xl md:text-2xl text-foreground truncate group-hover:ml-3 transition-[margin] duration-300">{item.name}</p>
|
||||
<ArrowUpRight className="h-[var(--text-xl)] md:h-[var(--text-2xl)] w-auto text-foreground group-hover:rotate-45 group-hover:mr-3 transition-all duration-300" strokeWidth={1.5} />
|
||||
</div>
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
});
|
||||
|
||||
interface NavbarStyleCenteredProps {
|
||||
navItems: NavItem[];
|
||||
button: ButtonConfig;
|
||||
// logoSrc?: string;
|
||||
// logoAlt?: string;
|
||||
brandName?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const NavbarStyleCentered = memo(function NavbarStyleCentered({
|
||||
navItems,
|
||||
button,
|
||||
// logoSrc,
|
||||
// logoAlt = "",
|
||||
brandName = "Webild",
|
||||
className = "",
|
||||
}: NavbarStyleCenteredProps) {
|
||||
const [isActive, setIsActive] = useState(false);
|
||||
const theme = useTheme();
|
||||
|
||||
const getButtonConfigProps = () => {
|
||||
if (theme.defaultButtonVariant === "hover-bubble") {
|
||||
return { bgClassName: "w-full" };
|
||||
}
|
||||
if (theme.defaultButtonVariant === "icon-arrow") {
|
||||
return { className: "justify-between" };
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
const handleToggle = useCallback(() => {
|
||||
setIsActive((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
const handleClose = useCallback(() => {
|
||||
setIsActive(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === "Escape" && isActive) {
|
||||
setIsActive(false);
|
||||
}
|
||||
};
|
||||
|
||||
document.addEventListener("keydown", handleKeyDown);
|
||||
return () => document.removeEventListener("keydown", handleKeyDown);
|
||||
}, [isActive]);
|
||||
|
||||
return (
|
||||
<nav
|
||||
role="navigation"
|
||||
aria-label="Main navigation"
|
||||
data-navigation-status={isActive ? "active" : "not-active"}
|
||||
className={cls("fixed inset-0 z-[1000] pointer-events-none", className)}
|
||||
>
|
||||
<div
|
||||
className="centered-nav__overlay absolute inset-0 bg-foreground pointer-events-auto opacity-0 invisible transition-all duration-700 ease-[cubic-bezier(0.5,0.5,0,1)]"
|
||||
onClick={handleClose}
|
||||
/>
|
||||
<div className="pointer-events-auto absolute top-6 left-1/2 -translate-x-1/2 w-content-width md:top-8 md:w-35 flex flex-col justify-start items-stretch rounded-theme-capped">
|
||||
<div className="absolute! inset-0 card backdrop-blur-xs rounded-theme-capped" />
|
||||
<div className="relative z-10 flex justify-between items-center py-3 px-6">
|
||||
<Logo
|
||||
brandName={brandName}
|
||||
/>
|
||||
<HamburgerButton
|
||||
isActive={isActive}
|
||||
onClick={handleToggle}
|
||||
activeBarClassName="bg-foreground"
|
||||
inactiveBarClassName="bg-foreground"
|
||||
/>
|
||||
</div>
|
||||
<div className="centered-nav__content relative overflow-hidden rounded-b-theme-capped grid transition-[grid-template-rows] duration-600 ease-[cubic-bezier(0.625,0.05,0,1)]">
|
||||
<div className="centered-nav__inner flex flex-col justify-start items-center w-full relative overflow-hidden gap-6">
|
||||
<div className="w-full px-6" >
|
||||
<ul className="relative w-full card p-6 rounded-theme-capped flex flex-col gap-3 justify-start items-stretch m-0 list-none">
|
||||
{navItems.map((item, index) => (
|
||||
<Fragment key={item.id}>
|
||||
<NavLink item={item} index={index} onClose={handleClose} />
|
||||
{index < navItems.length - 1 && <div className="centered-nav__separator h-px bg-accent/50" />}
|
||||
</Fragment>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="w-full px-6 pb-6">
|
||||
<Button
|
||||
{...getButtonProps(
|
||||
{
|
||||
...button,
|
||||
onClick: () => {
|
||||
button.onClick?.();
|
||||
handleClose();
|
||||
},
|
||||
props: { ...button.props, ...getButtonConfigProps() }
|
||||
},
|
||||
0,
|
||||
theme.defaultButtonVariant,
|
||||
"w-full"
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
});
|
||||
|
||||
NavbarStyleCentered.displayName = "NavbarStyleCentered";
|
||||
|
||||
export default NavbarStyleCentered;
|
||||
Reference in New Issue
Block a user