Skip to content

Commit 83793a8

Browse files
committed
feat(drawer_menu) finish implementing drawer menu to fix mobile issues
1 parent e251496 commit 83793a8

File tree

7 files changed

+158
-13
lines changed

7 files changed

+158
-13
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import { useCallback, useEffect, useMemo } from 'react'
2+
import { TemplatedNavLink } from '@/features/routing/components/templated-nav-link/templated-nav-link'
3+
import { Urls } from '@/routes/urls'
4+
import { Telescope, FlaskConical, Coins, X, Settings } from 'lucide-react'
5+
import { Search } from '@/features/search/components/search'
6+
import SvgWizard from '@/features/common/components/icons/wizard'
7+
import { cn } from '@/features/common/utils'
8+
import { useSelectedNetwork } from '@/features/network/data'
9+
import { useLayout } from '@/features/settings/data'
10+
import SvgLoraDark from '@/features/common/components/svg/lora-dark'
11+
import SvgLoraLight from '@/features/common/components/svg/lora-light'
12+
import { NetworkSelect } from '@/features/network/components/network-select'
13+
import { ThemeToggle } from '@/features/settings/components/theme-toggle'
14+
15+
const itemBase =
16+
'flex items-center gap-3 rounded-md border border-transparent px-3 py-2 hover:bg-accent hover:text-primary transition-colors'
17+
const itemActive = '[&.active]:bg-accent [&.active]:text-primary [&.active]:border-border'
18+
const iconBox = 'border rounded-md p-2'
19+
20+
export default function DrawerMenu() {
21+
const [selectedNetwork] = useSelectedNetwork()
22+
const [layout, setLayout] = useLayout()
23+
const isOpen = !!layout.isDrawerMenuExpanded
24+
25+
const menuItems = useMemo(
26+
() => [
27+
{ urlTemplate: Urls.Network.Explore, icon: <Telescope />, text: 'Explore' },
28+
{ urlTemplate: Urls.Network.AppLab, icon: <FlaskConical />, text: 'App Lab' },
29+
{ urlTemplate: Urls.Network.TransactionWizard, icon: <SvgWizard width={24} height={24} />, text: 'Txn Wizard' },
30+
{ urlTemplate: Urls.Network.Fund, icon: <Coins />, text: 'Fund' },
31+
],
32+
[]
33+
)
34+
35+
const navTextClassName = cn('visible transition-[visibility] duration-0 delay-100')
36+
37+
const navIconClassName = cn('border rounded-md p-2')
38+
39+
const handleClose = useCallback(() => setLayout((prev: any) => ({ ...prev, isDrawerMenuExpanded: false })), [setLayout])
40+
41+
useEffect(() => {
42+
const onKey = (e: KeyboardEvent) => {
43+
if (e.key === 'Escape') handleClose()
44+
}
45+
if (isOpen) {
46+
document.addEventListener('keydown', onKey)
47+
const prev = document.body.style.overflow
48+
document.body.style.overflow = 'hidden'
49+
return () => {
50+
document.removeEventListener('keydown', onKey)
51+
document.body.style.overflow = prev
52+
}
53+
}
54+
}, [isOpen, handleClose])
55+
56+
return (
57+
<div
58+
className={cn(
59+
'fixed inset-0 z-50 transition-opacity duration-300',
60+
// fade backdrop in/out; disable pointer events when closed
61+
isOpen ? 'opacity-100 pointer-events-auto' : 'opacity-0 pointer-events-none',
62+
// respect reduced motion
63+
'motion-reduce:transition-none'
64+
)}
65+
aria-hidden={!isOpen}
66+
>
67+
{/* Backdrop */}
68+
<button aria-label="Close menu" className="absolute inset-0 bg-black/40" onClick={handleClose} />
69+
70+
<aside
71+
className={cn(
72+
'absolute right-0 top-0 h-full w-full md:w-4/7 bg-card border-r shadow-xl flex flex-col',
73+
'transition-transform duration-300 will-change-transform',
74+
isOpen ? 'translate-x-0' : 'translate-x-full',
75+
'motion-reduce:transition-none'
76+
)}
77+
role="dialog"
78+
aria-modal="true"
79+
>
80+
{/* Header */}
81+
<div className="flex flex-col items-start p-3 gap-4">
82+
<div className="flex items-baseline justify-between w-full">
83+
<TemplatedNavLink urlTemplate={Urls.Network.Explore} className="self-center">
84+
<SvgLoraLight onClick={handleClose} className="block dark:hidden" />
85+
<SvgLoraDark onClick={handleClose} className="hidden dark:block" />
86+
</TemplatedNavLink>
87+
<button className="text-muted-foreground hover:text-foreground p-2" onClick={handleClose} aria-label="Close">
88+
<X />
89+
</button>
90+
</div>
91+
92+
<Search />
93+
</div>
94+
95+
{/* Items */}
96+
<nav className="p-3 space-y-1">
97+
{menuItems.map((item, idx) => (
98+
<div onClick={handleClose}>
99+
<TemplatedNavLink
100+
key={idx}
101+
urlTemplate={item.urlTemplate}
102+
urlParams={{ networkId: selectedNetwork }}
103+
className={cn(itemBase, itemActive)}
104+
end={item.text === 'Explore'}
105+
>
106+
<div className={iconBox}>{item.icon}</div>
107+
<span className="whitespace-nowrap">{item.text}</span>
108+
</TemplatedNavLink>
109+
</div>
110+
))}
111+
</nav>
112+
113+
{/* Footer */}
114+
<div className="mt-auto mb-4 p-3 flex justify-between items-center text-xs text-muted-foreground">
115+
<NetworkSelect />
116+
<div className="mt-6 flex">
117+
<ThemeToggle navTextClassName={navTextClassName} />
118+
<TemplatedNavLink urlTemplate={Urls.Settings} className={'flex flex-col items-center justify-center'}>
119+
<div onClick={handleClose} className={navIconClassName}>
120+
<Settings />
121+
</div>
122+
</TemplatedNavLink>
123+
</div>
124+
</div>
125+
</aside>
126+
</div>
127+
)
128+
}

src/features/layout/components/header.tsx

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,24 @@ import { useNetworkConfig } from '@/features/network/data'
77
import { ConnectWalletButton } from '@/features/wallet/components/connect-wallet-button'
88
import { Urls } from '@/routes/urls'
99
import { NetworkSelect } from '@/features/network/components/network-select'
10+
import { HamburgerMenuIcon } from '@radix-ui/react-icons'
11+
import DrawerMenu from './drawer-menu'
12+
13+
import { useCallback } from 'react'
14+
import { useLayout } from '@/features/settings/data'
1015

1116
type Props = {
1217
className?: string
1318
}
1419

1520
export function Header({ className }: Props) {
1621
const networkConfig = useNetworkConfig()
22+
const [layout, setLayout] = useLayout()
23+
24+
const openDrawerMenu = useCallback(
25+
() => setLayout((prev) => ({ ...prev, isDrawerMenuExpanded: !prev.isDrawerMenuExpanded })),
26+
[setLayout]
27+
)
1728

1829
return (
1930
<header className={cn('bg-card text-card-foreground flex h-20 px-4 justify-start border-b', className)}>
@@ -22,11 +33,13 @@ export function Header({ className }: Props) {
2233
<SvgLoraLight className="block dark:hidden" />
2334
<SvgLoraDark className="hidden dark:block" />
2435
</TemplatedNavLink>
25-
<Search />
36+
<Search className="hidden lg:flex" />
2637
</div>
27-
<div className={cn('flex items-center gap-2 ml-auto overflow-hidden pl-1')}>
28-
<NetworkSelect showLabel={false} />
38+
<div className={cn('flex items-center gap-3 ml-auto overflow-hidden')}>
39+
<NetworkSelect className="hidden lg:flex" showLabel={false} />
2940
<ConnectWalletButton />
41+
<HamburgerMenuIcon onClick={openDrawerMenu} className="lg:hidden text-xl w-8 h-8" />
42+
<DrawerMenu />
3043
</div>
3144
</header>
3245
)

src/features/layout/components/left-side-bar-menu.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export function LeftSideBarMenu({ className }: Props) {
5353
return (
5454
<aside
5555
className={cn(
56-
'flex flex-col bg-card border-r transition-[width] duration-300',
56+
'lg:flex flex-col bg-card border-r transition-[width] duration-300 hidden',
5757
className,
5858
layout.isLeftSideBarExpanded ? 'w-56' : 'w-18'
5959
)}

src/features/network/components/network-select.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import { useLocation, useNavigate, useParams } from 'react-router-dom'
77

88
type NetworkSelectProps = {
99
showLabel?: boolean
10+
className?: string
1011
}
1112

12-
export function NetworkSelect({ showLabel = true }: NetworkSelectProps) {
13+
export function NetworkSelect({ showLabel = true, className }: NetworkSelectProps) {
1314
const [selectedNetwork, setSelectedNetwork] = useSelectedNetwork()
1415
const networkConfigs = useNetworkConfigs()
1516
const { networkId: currentNetworkId } = useParams()
@@ -29,7 +30,7 @@ export function NetworkSelect({ showLabel = true }: NetworkSelectProps) {
2930
)
3031

3132
return (
32-
<div className={cn('flex flex-col')}>
33+
<div className={cn('flex flex-col', className)}>
3334
{showLabel && (
3435
<Label htmlFor="network" className={cn('ml-0.5 mb-2')}>
3536
Active network

src/features/search/components/search.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ import { isMacOs } from '@/utils/is-mac-platform'
1919
export const searchPlaceholderLabel = `Search by ID or Address ${isMacOs ? '(⌘K)' : '(Ctrl+K)'}`
2020
export const noSearchResultsMessage = 'No results.'
2121

22-
export function Search() {
22+
type Props = {
23+
className?: string
24+
}
25+
26+
export function Search({ className }: Props) {
2327
const navigate = useNavigate()
2428
const [term, setTerm, loadableResults] = useSearch()
2529
const searchInputRef = useRef<HTMLInputElement>(null)
@@ -59,10 +63,7 @@ export function Search() {
5963

6064
return (
6165
<Command
62-
className={cn(
63-
'hidden md:flex bg-popover text-popover-foreground w-88 h-auto z-20 border border-input mt-[1.2rem]',
64-
term && 'shadow-md'
65-
)}
66+
className={cn('bg-popover text-popover-foreground w-88 h-auto z-20 border border-input mt-[1.2rem]', term && 'shadow-md', className)}
6667
label="Search by ID or Address"
6768
shouldFilter={false}
6869
loop

src/features/settings/components/theme-toggle.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ export function ThemeToggle({ navTextClassName }: Props) {
2424
<Button
2525
variant="no-style"
2626
aria-label={themeTogglelabel}
27-
className="my-1.5 flex items-center gap-2 whitespace-nowrap px-2 text-base font-normal hover:text-primary"
27+
className="flex items-center gap-2 whitespace-nowrap text-base font-normal hover:text-primary"
2828
>
29-
<div className="ml-[0.4rem] flex rounded-md border p-2">
29+
<div className="flex rounded-md border p-2">
3030
<Sun className="rotate-0 scale-100 transition-transform dark:-rotate-90 dark:scale-0" />
3131
<Moon className="absolute rotate-90 scale-0 transition-transform dark:rotate-0 dark:scale-100" />
3232
</div>

src/features/settings/data/layout.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import { settingsStore } from './settings'
44

55
export type LayoutConfig = {
66
isLeftSideBarExpanded: boolean
7+
isDrawerMenuExpanded: boolean
78
}
89

910
const layoutConfigAtom = atomWithStorage<LayoutConfig>(
1011
'layout-config',
1112
{
13+
isDrawerMenuExpanded: false,
1214
isLeftSideBarExpanded: true,
1315
},
1416
undefined,

0 commit comments

Comments
 (0)