Added Entry and Exit animation for menu
This commit is contained in:
@@ -6,7 +6,20 @@ import { useMenu } from '@/contexts/MenuContext';
|
|||||||
import styles from './Header.module.css';
|
import styles from './Header.module.css';
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
const { isMenuOpen, toggleMenu } = useMenu();
|
const { isMenuOpen, closeMenu, openMenu, startClosing, resetClosing } =
|
||||||
|
useMenu();
|
||||||
|
|
||||||
|
const handleMenuToggle = () => {
|
||||||
|
if (isMenuOpen) {
|
||||||
|
startClosing();
|
||||||
|
setTimeout(() => {
|
||||||
|
closeMenu();
|
||||||
|
resetClosing();
|
||||||
|
}, 800);
|
||||||
|
} else {
|
||||||
|
openMenu();
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
<div className={styles.inner}>
|
<div className={styles.inner}>
|
||||||
@@ -21,7 +34,7 @@ export default function Header() {
|
|||||||
</div>
|
</div>
|
||||||
<div className={styles.menutoggle}>
|
<div className={styles.menutoggle}>
|
||||||
<button
|
<button
|
||||||
onClick={() => toggleMenu()}
|
onClick={() => handleMenuToggle()}
|
||||||
aria-label={isMenuOpen ? 'Close menu' : 'Open menu'}
|
aria-label={isMenuOpen ? 'Close menu' : 'Open menu'}
|
||||||
aria-expanded={isMenuOpen}
|
aria-expanded={isMenuOpen}
|
||||||
aria-controls="main-menu"
|
aria-controls="main-menu"
|
||||||
|
|||||||
@@ -75,11 +75,29 @@
|
|||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
background-color: var(--grid-bg);
|
background-color: var(--grid-bg);
|
||||||
|
clip-path: inset(0 0 100% 0);
|
||||||
|
|
||||||
|
transition: clip-path 0.35s steps(8, end);
|
||||||
|
|
||||||
|
|
||||||
&.isOpen {
|
&.isOpen {
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
|
|
||||||
|
transform: translateY(0);
|
||||||
|
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
clip-path: inset(0 0 0 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.isClosing {
|
||||||
|
clip-path: inset(0 0 100% 0);
|
||||||
|
transition: clip-path 0.25s steps(6, end);
|
||||||
|
|
||||||
|
&.isOpen {
|
||||||
|
clip-path: inset(0 0 100% 0);
|
||||||
|
transition: clip-path 0.25s steps(6, end);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (--bp-desktop) {
|
@media screen and (--bp-desktop) {
|
||||||
|
|||||||
@@ -16,17 +16,16 @@ interface MenuGridProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function MenuGrid({ navigationData }: MenuGridProps) {
|
export default function MenuGrid({ navigationData }: MenuGridProps) {
|
||||||
const { isMenuOpen, closeMenu } = useMenu();
|
const { isMenuOpen, closeMenu, isClosing, startClosing, resetClosing } =
|
||||||
|
useMenu();
|
||||||
const menuRef = React.useRef<HTMLElement>(null);
|
const menuRef = React.useRef<HTMLElement>(null);
|
||||||
const [isClosing, setIsClosing] = React.useState(false);
|
|
||||||
|
|
||||||
const handleClose = React.useCallback(() => {
|
const handleClose = React.useCallback(() => {
|
||||||
setIsClosing(true);
|
startClosing();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
closeMenu();
|
closeMenu();
|
||||||
setIsClosing(false);
|
resetClosing();
|
||||||
}, 800);
|
}, 800);
|
||||||
}, [closeMenu]);
|
}, [closeMenu, startClosing, resetClosing]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const handleEscape = (e: KeyboardEvent) => {
|
const handleEscape = (e: KeyboardEvent) => {
|
||||||
|
|||||||
@@ -4,9 +4,11 @@ import React, { useContext, useEffect } from 'react';
|
|||||||
|
|
||||||
interface MenuContextType {
|
interface MenuContextType {
|
||||||
isMenuOpen: boolean;
|
isMenuOpen: boolean;
|
||||||
toggleMenu: () => void;
|
isClosing: boolean;
|
||||||
closeMenu: () => void;
|
closeMenu: () => void;
|
||||||
openMenu: () => void;
|
openMenu: () => void;
|
||||||
|
startClosing: () => void;
|
||||||
|
resetClosing: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MenuContext = React.createContext<MenuContextType | undefined>(undefined);
|
const MenuContext = React.createContext<MenuContextType | undefined>(undefined);
|
||||||
@@ -16,11 +18,13 @@ interface MenuProviderProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const MenuProvider = ({ children }: MenuProviderProps) => {
|
export const MenuProvider = ({ children }: MenuProviderProps) => {
|
||||||
const [isMenuOpen, setIsMenuOpen] = React.useState(true);
|
const [isMenuOpen, setIsMenuOpen] = React.useState(false);
|
||||||
|
const [isClosing, setIsClosing] = React.useState(false);
|
||||||
|
|
||||||
const toggleMenu = () => setIsMenuOpen(!isMenuOpen);
|
|
||||||
const closeMenu = () => setIsMenuOpen(false);
|
const closeMenu = () => setIsMenuOpen(false);
|
||||||
const openMenu = () => setIsMenuOpen(true);
|
const openMenu = () => setIsMenuOpen(true);
|
||||||
|
const startClosing = () => setIsClosing(true);
|
||||||
|
const resetClosing = () => setIsClosing(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isMenuOpen) {
|
if (isMenuOpen) {
|
||||||
@@ -35,7 +39,14 @@ export const MenuProvider = ({ children }: MenuProviderProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MenuContext.Provider
|
<MenuContext.Provider
|
||||||
value={{ isMenuOpen, toggleMenu, openMenu, closeMenu }}
|
value={{
|
||||||
|
isMenuOpen,
|
||||||
|
openMenu,
|
||||||
|
closeMenu,
|
||||||
|
isClosing,
|
||||||
|
startClosing,
|
||||||
|
resetClosing,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</MenuContext.Provider>
|
</MenuContext.Provider>
|
||||||
|
|||||||
Reference in New Issue
Block a user