91 lines
2.5 KiB
TypeScript
91 lines
2.5 KiB
TypeScript
import type { TranslationKey } from '@/i18n/ui'
|
|
import { useTranslations } from '@/i18n/utils'
|
|
import { cn } from '@/libs/cn'
|
|
import React, { useState, useEffect, type ButtonHTMLAttributes } from 'react'
|
|
|
|
const t = useTranslations()
|
|
|
|
type AnimatedBurgerButtonProps = ButtonHTMLAttributes<unknown> & {
|
|
isOpen: boolean
|
|
}
|
|
|
|
const AnimatedBurgerButton: React.FC<AnimatedBurgerButtonProps> = ({ isOpen, ...buttonProps }) => (
|
|
<button
|
|
{...buttonProps}
|
|
className="z-[60] flex h-8 w-8 cursor-pointer flex-col justify-around border-none bg-transparent p-0 focus:outline-none"
|
|
aria-label={isOpen ? 'Close menu' : 'Open menu'}
|
|
aria-expanded={isOpen}
|
|
>
|
|
<div
|
|
className={cn(
|
|
'h-1 w-8 rounded-md bg-white transition-all duration-300 ease-in-out',
|
|
isOpen ? 'translate-y-[11px] rotate-45' : ''
|
|
)}
|
|
/>
|
|
<div
|
|
className={cn(
|
|
'h-1 w-8 rounded-md bg-white transition-all duration-300 ease-in-out',
|
|
isOpen ? 'opacity-0' : ''
|
|
)}
|
|
/>
|
|
<div
|
|
className={cn(
|
|
'h-1 w-8 rounded-md bg-white transition-all duration-300 ease-in-out',
|
|
isOpen ? '-translate-y-[11px] -rotate-45' : ''
|
|
)}
|
|
/>
|
|
</button>
|
|
)
|
|
|
|
type MobileNavigationProps = {
|
|
navItems: readonly {
|
|
key: string
|
|
href: string
|
|
}[]
|
|
}
|
|
|
|
const MobileNavigation: React.FC<MobileNavigationProps> = ({ navItems }) => {
|
|
const [isOpen, setIsOpen] = useState(false)
|
|
|
|
const toggleMenu = () => {
|
|
setIsOpen(!isOpen)
|
|
}
|
|
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
document.body.style.overflow = 'hidden'
|
|
} else {
|
|
document.body.style.overflow = 'unset'
|
|
}
|
|
return () => {
|
|
document.body.style.overflow = 'unset'
|
|
}
|
|
}, [isOpen])
|
|
|
|
return (
|
|
<div className="md:hidden">
|
|
<AnimatedBurgerButton isOpen={isOpen} onClick={toggleMenu} />
|
|
<div
|
|
className={cn(
|
|
'fixed left-0 top-[var(--header-height)] z-50 h-[calc(100dvh-var(--header-height))] w-full transform bg-main p-8 transition-transform duration-300 ease-in-out',
|
|
isOpen ? '-translate-x-0' : 'translate-x-full'
|
|
)}
|
|
>
|
|
<nav className="flex h-full flex-col justify-between">
|
|
{navItems.map((item) => (
|
|
<a key={item.key} href={item.href} className="text-3xl" onClick={toggleMenu}>
|
|
{t(item.key as TranslationKey)}
|
|
</a>
|
|
))}
|
|
<hr />
|
|
<a href="/#services" className="brand-btn w-full" onClick={toggleMenu}>
|
|
{t('hero.cta.getStarted')}
|
|
</a>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default MobileNavigation
|