1import { createContext, Fragment, useContext } from 'react'
2import { Dialog, Transition } from '@headlessui/react'
3import { motion } from 'framer-motion'
4import { create } from 'zustand'
5
6import { Header } from '@/components/Header'
7import { Navigation } from '@/components/Navigation'
8
9function MenuIcon(props) {
10 return (
11 <svg
12 viewBox="0 0 10 9"
13 fill="none"
14 strokeLinecap="round"
15 aria-hidden="true"
16 {...props}
17 >
18 <path d="M.5 1h9M.5 8h9M.5 4.5h9" />
19 </svg>
20 )
21}
22
23function XIcon(props) {
24 return (
25 <svg
26 viewBox="0 0 10 9"
27 fill="none"
28 strokeLinecap="round"
29 aria-hidden="true"
30 {...props}
31 >
32 <path d="m1.5 1 7 7M8.5 1l-7 7" />
33 </svg>
34 )
35}
36
37const IsInsideMobileNavigationContext = createContext(false)
38
39export function useIsInsideMobileNavigation() {
40 return useContext(IsInsideMobileNavigationContext)
41}
42
43export const useMobileNavigationStore = create((set) => ({
44 isOpen: false,
45 open: () => set({ isOpen: true }),
46 close: () => set({ isOpen: false }),
47 toggle: () => set((state) => ({ isOpen: !state.isOpen })),
48}))
49
50export function MobileNavigation() {
51 let isInsideMobileNavigation = useIsInsideMobileNavigation()
52 let { isOpen, toggle, close } = useMobileNavigationStore()
53 let ToggleIcon = isOpen ? XIcon : MenuIcon
54
55 return (
56 <IsInsideMobileNavigationContext.Provider value={true}>
57 <button
58 type="button"
59 className="flex h-6 w-6 items-center justify-center rounded-md transition hover:bg-zinc-900/5 dark:hover:bg-white/5"
60 aria-label="Toggle navigation"
61 onClick={toggle}
62 >
63 <ToggleIcon className="w-2.5 stroke-zinc-900 dark:stroke-white" />
64 </button>
65 {!isInsideMobileNavigation && (
66 <Transition.Root show={isOpen} as={Fragment}>
67 <Dialog onClose={close} className="fixed inset-0 z-50 lg:hidden">
68 <Transition.Child
69 as={Fragment}
70 enter="duration-300 ease-out"
71 enterFrom="opacity-0"
72 enterTo="opacity-100"
73 leave="duration-200 ease-in"
74 leaveFrom="opacity-100"
75 leaveTo="opacity-0"
76 >
77 <div className="fixed inset-0 top-14 bg-zinc-400/20 backdrop-blur-sm dark:bg-black/40" />
78 </Transition.Child>
79
80 <Dialog.Panel>
81 <Transition.Child
82 as={Fragment}
83 enter="duration-300 ease-out"
84 enterFrom="opacity-0"
85 enterTo="opacity-100"
86 leave="duration-200 ease-in"
87 leaveFrom="opacity-100"
88 leaveTo="opacity-0"
89 >
90 <Header />
91 </Transition.Child>
92
93 <Transition.Child
94 as={Fragment}
95 enter="duration-500 ease-in-out"
96 enterFrom="-translate-x-full"
97 enterTo="translate-x-0"
98 leave="duration-500 ease-in-out"
99 leaveFrom="translate-x-0"
100 leaveTo="-translate-x-full"
101 >
102 <motion.div
103 layoutScroll
104 className="fixed bottom-0 left-0 top-14 w-full overflow-y-auto bg-white px-4 pb-4 pt-6 shadow-lg shadow-zinc-900/10 ring-1 ring-zinc-900/7.5 dark:bg-zinc-900 dark:ring-zinc-800 min-[416px]:max-w-sm sm:px-6 sm:pb-10"
105 >
106 <Navigation />
107 </motion.div>
108 </Transition.Child>
109 </Dialog.Panel>
110 </Dialog>
111 </Transition.Root>
112 )}
113 </IsInsideMobileNavigationContext.Provider>
114 )
115}