Skip to content

Commit

Permalink
add menu drawer for mobile nav and add animation for burger menu
Browse files Browse the repository at this point in the history
  • Loading branch information
Alex committed Aug 13, 2023
1 parent 256fccc commit 3046e9a
Show file tree
Hide file tree
Showing 12 changed files with 450 additions and 62 deletions.
2 changes: 1 addition & 1 deletion app/[lng]/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import '@/styles/globals.css'
import '@/styles/globals.scss'
import { Metadata } from 'next'
import { languages } from '@/i18n/settings'
import { dir } from 'i18next'
Expand Down
2 changes: 1 addition & 1 deletion components.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"style": "default",
"tailwind": {
"config": "tailwind.config.js",
"css": "app/globals.css",
"css": "app/globals.scss",
"baseColor": "slate",
"cssVariables": true
},
Expand Down
14 changes: 14 additions & 0 deletions components/layout/burger.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import '@/styles/burger.scss'
import { useState, useEffect } from 'react';

function Burger({mobileNavOpen}: {mobileNavOpen: boolean}) {
console.log('mobileNavOpen:', mobileNavOpen)

return (
<div className={`icon-wrapper ${mobileNavOpen ? 'active' : ''}`}>
<div className="menu-icon"/>
</div>
);
}

export default Burger;
2 changes: 1 addition & 1 deletion components/layout/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import LanguageSwitcher from './language-switcher'

export function Header({ lng }: { lng: Lang }) {
return (
<header className="sticky top-0 z-40 w-full border-b bg-background/60 drop-shadow-sm" style={{backdropFilter: 'blur(10px)'}}>
<header className="z-[100] sticky top-0 w-full border-b bg-background/80 backdrop-blur-sm drop-shadow-sm" style={{backdropFilter: 'blur(10px)'}}>
<section className="md:max-w-4xl md:mx-auto flex flex-1 items-center space-x-4 h-14 md:px-2">
<MainNav items={siteConfig.mainNav} lng={lng} />
<section className="hidden md:flex items-center space-x-1">
Expand Down
136 changes: 78 additions & 58 deletions components/layout/main-nav.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
'use client'

import Link from 'next/link'
import { useEffect, useState } from 'react'
import Link from 'next/link'
import { useSelectedLayoutSegment } from 'next/navigation'
import { Lang } from '@/types'
import { Menu, X } from 'lucide-react'
import { Lang, NavItem } from '@/types'

import { NavItem } from '@/types'
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'
import { Icons } from '@/assets/icons'
import { cn } from '@/assets/utils'

import Burger from './burger'
import LanguageSwitcher from './language-switcher'
import { ThemeToggle } from './theme-toggle'
import { Button } from '@/components/ui/button'

interface MainNavProps {
items: NavItem[]
Expand All @@ -27,72 +26,93 @@ export function MainNav({ items, lng }: MainNavProps) {

useEffect(() => {
const lang = window.location.pathname.split('/')[1]
if(lang === 'en' || lang === 'bg' ) {
if (lang === 'en' || lang === 'bg') {
setActiveLang(lang as Lang)
}
}, [])

return (
<div className="mr-2 flex flex-1 justify-between h-full">
<Link href={`/${activeLang}`} className="flex items-center space-x-2 ml-4 md:ml-0">
<Link
href={`/${activeLang}`}
className="flex items-center space-x-2 ml-4 md:ml-0"
>
<Icons.logo className="h-9" />
</Link>
<Button
className="px-3 md:hidden mr-4 md:mr-0"
onClick={() => setMobileNavOpen((state) => !state)}
{/* Mobile nav */}
<Sheet
open={mobileNavOpen}
onOpenChange={() => setMobileNavOpen((state) => !state)}
>
{mobileNavOpen ? <X className="h-7" /> : <Menu className="h-7" />}
</Button>
<SheetTrigger aria-controls="sheet-toggle">
<Burger mobileNavOpen={mobileNavOpen} />
</SheetTrigger>
<SheetContent id="sheet-toggle" side="top" className="opacity-90 pb-4 border-b border-b-slate-200">
<section className="w-full pt-14">
<nav className="flex flex-col gap-6 w-auto pt-2">
<NavItems
items={items}
lng={lng}
activeSegment={activeSegment}
mobile
onClick={() => setMobileNavOpen(false)}
/>
</nav>
<section className="flex flex-col justify-center items-center pt-1 mt-4">
<span className="border-t my-4 w-10" />
<span className="flex justify-center items-center">
<ThemeToggle />
<LanguageSwitcher extraAction={() => setMobileNavOpen(false)} />
</span>
</section>
</section>
</SheetContent>
</Sheet>

{/* Main nav */}
{items?.length ? (
<nav className="hidden md:flex gap-6 w-auto">
{items?.map(
(item, index) =>
item.href && (
<Link
key={index}
href={`/${activeLang}${item.href}`}
className={cn(
'uppercase flex items-center text-sm font-medium text-muted-foreground hover:text-primary',
activeSegment === item.activeSegment && 'text-primary font-semibold',
item.disabled && 'cursor-not-allowed opacity-80'
)}
>
{item.title[lng]}
</Link>
)
)}
<NavItems items={items} lng={lng} activeSegment={activeSegment} />
</nav>
) : null}
{mobileNavOpen && (
<section className="opacity-90 md:hidden absolute bg-background w-full pt-5 px-3 mt-12 drop-shadow-xl border-y border-b-primary">
<nav className="flex flex-col gap-6 w-auto">
{items?.map(
(item, index) =>
item.href && (
<Link
key={index}
href={`/${activeLang}${item.href}`}
onClick={() => setMobileNavOpen(false)}
className={cn(
'uppercase flex justify-center items-center text-sm font-medium text-muted-foreground hover:text-primary',
activeSegment === item.activeSegment && 'text-primary font-semibold',
item.disabled && 'cursor-not-allowed opacity-80'
)}
>
{item.title[lng]}
</Link>
)
)}
</nav>
<section className="flex flex-col justify-center items-center py-2 mt-4">
<span className="border-t my-4 w-10" />
<span className="flex justify-center items-center">
<ThemeToggle />
<LanguageSwitcher extraAction={() => setMobileNavOpen(false)} />
</span>
</section>
</section>
)}
</div>
)
}

const NavItems = ({
items,
lng,
activeSegment,
mobile,
onClick,
}: {
items: NavItem[]
lng: Lang
activeSegment: string | null
mobile?: boolean
onClick?: () => void
}) => {
return (
<>
{items?.map(
(item, index) =>
item.href && (
<Link
onClick={onClick}
key={index}
href={`/${lng}${item.href}`}
className={cn(
'uppercase flex items-center text-sm font-medium text-muted-foreground hover:text-primary',
mobile && 'justify-center',
activeSegment === item.activeSegment &&
'text-primary font-semibold',
item.disabled && 'cursor-not-allowed opacity-80'
)}
>
{item.title[lng]}
</Link>
)
)}
</>
)
}
2 changes: 1 addition & 1 deletion components/ui/lines.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import '@/styles/lines.css'
import '@/styles/lines.scss'

const Lines = () => {
return (
Expand Down
148 changes: 148 additions & 0 deletions components/ui/sheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import * as React from "react"
import * as SheetPrimitive from "@radix-ui/react-dialog"
import { cva, type VariantProps } from "class-variance-authority"
import { X } from "lucide-react"

import { cn } from "@/assets/utils"

const Sheet = SheetPrimitive.Root

const SheetTrigger = SheetPrimitive.Trigger

const SheetClose = SheetPrimitive.Close

const SheetPortal = ({
className,
...props
}: SheetPrimitive.DialogPortalProps) => (
<SheetPrimitive.Portal className={cn(className)} {...props} />
)
SheetPortal.displayName = SheetPrimitive.Portal.displayName

const SheetOverlay = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Overlay>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Overlay>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Overlay
className={cn(
"fixed inset-0 z-50 bg-background/80 backdrop-blur-sm data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
className
)}
{...props}
ref={ref}
/>
))
SheetOverlay.displayName = SheetPrimitive.Overlay.displayName

const sheetVariants = cva(
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
{
variants: {
side: {
top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top",
bottom:
"inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom",
left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm",
right:
"inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm",
},
},
defaultVariants: {
side: "right",
},
}
)

interface SheetContentProps
extends React.ComponentPropsWithoutRef<typeof SheetPrimitive.Content>,
VariantProps<typeof sheetVariants> {}

const SheetContent = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Content>,
SheetContentProps & {
inset?: boolean
children?: any
}
>(({ side = "right", className, children, ...props }, ref) => (
<SheetPortal>
<SheetOverlay />
<SheetPrimitive.Content
ref={ref}
className={cn(sheetVariants({ side }), className)}
{...props}
>
{children}
<SheetPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-secondary">
<X className="h-4 w-4" />
<span className="sr-only">Close</span>
</SheetPrimitive.Close>
</SheetPrimitive.Content>
</SheetPortal>
))
SheetContent.displayName = SheetPrimitive.Content.displayName

const SheetHeader = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col space-y-2 text-center sm:text-left",
className
)}
{...props}
/>
)
SheetHeader.displayName = "SheetHeader"

const SheetFooter = ({
className,
...props
}: React.HTMLAttributes<HTMLDivElement>) => (
<div
className={cn(
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
className
)}
{...props}
/>
)
SheetFooter.displayName = "SheetFooter"

const SheetTitle = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Title>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Title>
& {
children?: any
}
>(({ className, ...props }, ref) => (
<SheetPrimitive.Title
ref={ref}
className={cn("text-lg font-semibold text-foreground", className)}
{...props}
/>
))
SheetTitle.displayName = SheetPrimitive.Title.displayName

const SheetDescription = React.forwardRef<
React.ElementRef<typeof SheetPrimitive.Description>,
React.ComponentPropsWithoutRef<typeof SheetPrimitive.Description>
>(({ className, ...props }, ref) => (
<SheetPrimitive.Description
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
SheetDescription.displayName = SheetPrimitive.Description.displayName

export {
Sheet,
SheetTrigger,
SheetClose,
SheetContent,
SheetHeader,
SheetFooter,
SheetTitle,
SheetDescription,
}
Loading

0 comments on commit 3046e9a

Please sign in to comment.