2
2
3
3
import { Fragment , useState } from 'react' ;
4
4
5
+ import { Card , CardContent } from '@/components/ui/card' ;
6
+ import { Collapsible , CollapsibleContent , CollapsibleTrigger } from '@/components/ui/collapsible.tsx' ;
7
+ import {
8
+ DropdownMenu ,
9
+ DropdownMenuContent ,
10
+ DropdownMenuItem ,
11
+ DropdownMenuTrigger ,
12
+ } from '@/components/ui/dropdown-menu.tsx' ;
5
13
import { cn } from '@/lib/utils' ;
6
- import { MenuIcon , XIcon } from 'lucide- react' ;
14
+ import { RiArrowDownSLine , RiCloseLargeLine , RiMenuLine } from '@remixicon/ react' ;
7
15
import Link from 'next/link' ;
8
16
import { usePathname } from 'next/navigation' ;
9
17
10
- import { Card , CardContent , CardHeader } from './ui/card' ;
18
+ type HeaderMenuLink = {
19
+ title : string ;
20
+ href : string ;
21
+ target ?: '_blank' ;
22
+ } ;
23
+
24
+ type HeaderMenuGroupLink = {
25
+ title : string ;
26
+ subitems : Omit < HeaderMenuLink , 'target' > [ ] ;
27
+ } ;
11
28
12
- const items = [
13
- { title : 'Blocks' , href : '/blocks' } ,
14
- { title : 'Extrinsics' , href : '/extrinsics' } ,
15
- { title : 'Events' , href : '/events' } ,
16
- { title : 'EVM Transactions' , href : '/evm-transactions' } ,
29
+ const items : Array < HeaderMenuLink | HeaderMenuGroupLink > = [
30
+ {
31
+ title : 'Blockchain' ,
32
+ subitems : [
33
+ { title : 'Blocks' , href : '/blocks' } ,
34
+ { title : 'Extrinsics' , href : '/extrinsics' } ,
35
+ { title : 'Events' , href : '/events' } ,
36
+ { title : 'EVM Transactions' , href : '/evm-transactions' } ,
37
+ ] ,
38
+ } ,
39
+ {
40
+ title : 'Track' ,
41
+ subitems : [
42
+ { title : 'Bridge' , href : '/bridge' } ,
43
+ { title : 'DEX' , href : '/dex' } ,
44
+ { title : 'Staking' , href : '/staking' } ,
45
+ ] ,
46
+ } ,
17
47
{ title : 'Addresses' , href : '/addresses' } ,
18
- { title : 'Bridge' , href : '/bridge' } ,
19
48
{ title : 'Tokens' , href : '/tokens' } ,
20
- { title : 'DEX' , href : '/dex' } ,
21
- { title : 'Staking' , href : '/staking' } ,
22
49
{ title : 'Verified Contracts' , href : '/verified-contracts' } ,
23
50
{ title : 'Ecosystem' , href : '/ecosystem' } ,
24
- { title : 'API' , href : 'https://build.rootscan.io' , newTab : true } ,
51
+ { title : 'API Portal ' , href : 'https://build.rootscan.io' , target : '_blank' } ,
25
52
] ;
26
53
27
54
export function Navigation ( ) {
@@ -30,19 +57,52 @@ export function Navigation() {
30
57
31
58
return (
32
59
< Fragment >
33
- < div className = "hidden items-center gap-4 px-4 lg:flex" >
34
- { items . map ( ( item , _ ) => (
35
- < Link href = { item . href } key = { _ } target = { item . newTab ? '_blank' : '_self' } >
36
- < div
60
+ < div className = "hidden items-center gap-0.5 px-4 lg:flex" >
61
+ { items . map ( ( item , _ ) => {
62
+ const { title } = item ;
63
+ const { href, target } = item as HeaderMenuLink ;
64
+ const { subitems } = item as HeaderMenuGroupLink ;
65
+ const isLink = ! ! href ;
66
+
67
+ const headerMenuLink = (
68
+ < Link
69
+ href = { isLink ? href : '#' }
70
+ target = { target ? '_blank' : '_self' }
37
71
className = { cn ( [
38
- 'text-muted-foreground hover:text-primary flex items-center text-sm font-bold duration-150 ease-in' ,
39
- formattedPathname === item . href ? 'text-primary' : '' ,
72
+ 'group font-semibold gap-2 inline-flex items-center py-1.5 px-3 transition-colors text-[14px]/[20px] text-muted-foreground data-[state=open]:text-primary hover:text-primary focus:outline-0' ,
73
+ ! isLink && 'pr-2.5' ,
74
+ formattedPathname === href ? 'text-primary' : '' ,
40
75
] ) }
41
76
>
42
- { item . title }
43
- </ div >
44
- </ Link >
45
- ) ) }
77
+ { title }
78
+ { ! isLink && < RiArrowDownSLine className = "size-4 transition-all group-data-[state=open]:rotate-180" /> }
79
+ </ Link >
80
+ ) ;
81
+
82
+ return (
83
+ < Fragment key = { _ } >
84
+ { isLink ? (
85
+ headerMenuLink
86
+ ) : (
87
+ < DropdownMenu >
88
+ < DropdownMenuTrigger asChild > { headerMenuLink } </ DropdownMenuTrigger >
89
+ < DropdownMenuContent className = "flex flex-col gap-1 rounded-[12px] bg-popover p-2" >
90
+ { subitems ?. map ( ( subitem , i ) => (
91
+ < DropdownMenuItem key = { i } asChild >
92
+ < Link
93
+ href = { subitem . href }
94
+ className = "cursor-pointer rounded-[4px] px-3 py-2 text-[14px]/[20px] font-semibold"
95
+ >
96
+ { subitem . title }
97
+ </ Link >
98
+ </ DropdownMenuItem >
99
+ ) ) }
100
+ </ DropdownMenuContent >
101
+ </ DropdownMenu >
102
+ ) }
103
+ </ Fragment >
104
+ ) ;
105
+ } ) }
46
106
</ div >
47
107
< MobileMenu />
48
108
</ Fragment >
@@ -52,25 +112,72 @@ export function Navigation() {
52
112
export const MobileMenu = ( ) => {
53
113
const [ open , setOpen ] = useState < boolean > ( false ) ;
54
114
115
+ const pathname = usePathname ( ) ;
116
+
117
+ const isHomePage = pathname === '/' ;
118
+
55
119
return (
56
120
< Fragment >
57
121
< div className = "block lg:hidden" >
58
- < div className = "duration-300 animate-in animate-out fade-in fade-out" onClick = { ( ) => setOpen ( ! open ) } >
59
- { open ? < XIcon /> : < MenuIcon /> }
122
+ < div className = "p-1 duration-300 animate-in animate-out fade-in fade-out" onClick = { ( ) => setOpen ( ! open ) } >
123
+ { open ? < RiCloseLargeLine className = "size-6" /> : < RiMenuLine className = "size-6" /> }
60
124
</ div >
61
125
</ div >
62
- < div className = { cn ( [ open ? 'absolute left-0 top-[64px] !m-0 w-full' : 'hidden' ] ) } >
63
- < Card className = "rounded-b-2xl rounded-t-none" >
64
- < CardHeader className = "pb-0" />
65
- < CardContent >
66
- < div className = "flex flex-col gap-4" >
67
- { items . map ( ( item , _ ) => (
68
- < Link href = { item . href } onClick = { ( ) => setOpen ( false ) } key = { _ } target = { item . newTab ? '_blank' : '_self' } >
69
- < div className = "flex items-center text-sm font-bold text-muted-foreground duration-150 ease-in hover:text-primary" >
70
- { item . title }
71
- </ div >
72
- </ Link >
73
- ) ) }
126
+ < div
127
+ className = { cn (
128
+ 'absolute left-0 top-[64px] !m-0 w-full' ,
129
+ ! open && 'hidden' ,
130
+ isHomePage ? 'top-[96px]' : 'top-[128px]' ,
131
+ ) }
132
+ >
133
+ < Card className = "rounded-b-2xl rounded-t-none dark:bg-black" >
134
+ < CardContent className = "p-2" >
135
+ < div className = "flex flex-col gap-0.5" >
136
+ { items . map ( ( item , _ ) => {
137
+ const { title } = item ;
138
+ const { href } = item as HeaderMenuLink ;
139
+ const { subitems } = item as HeaderMenuGroupLink ;
140
+ const isLink = ! ! href ;
141
+
142
+ const headerMenuLink = (
143
+ < Link
144
+ className = "group flex items-center justify-between py-3.5 pl-2 pr-3.5 text-[14px]/[20px] font-semibold transition-all"
145
+ href = { isLink ? href : '#' }
146
+ onClick = { ( ) => isLink && setOpen ( false ) }
147
+ >
148
+ { title }
149
+ { ! isLink && (
150
+ < RiArrowDownSLine className = "size-5 transition-all group-data-[state=open]:rotate-180" />
151
+ ) }
152
+ </ Link >
153
+ ) ;
154
+
155
+ return (
156
+ < Fragment key = { _ } >
157
+ { isLink ? (
158
+ headerMenuLink
159
+ ) : (
160
+ < Collapsible >
161
+ < CollapsibleTrigger asChild > { headerMenuLink } </ CollapsibleTrigger >
162
+ < CollapsibleContent >
163
+ < div className = "flex flex-col gap-1 rounded-[12px] border p-2" >
164
+ { subitems ?. map ( ( subitem , i ) => (
165
+ < Link
166
+ key = { i }
167
+ href = { subitem . href }
168
+ className = "cursor-pointer rounded-[4px] px-3 py-2 text-[14px]/[20px] font-semibold"
169
+ onClick = { ( ) => setOpen ( false ) }
170
+ >
171
+ { subitem . title }
172
+ </ Link >
173
+ ) ) }
174
+ </ div >
175
+ </ CollapsibleContent >
176
+ </ Collapsible >
177
+ ) }
178
+ </ Fragment >
179
+ ) ;
180
+ } ) }
74
181
</ div >
75
182
</ CardContent >
76
183
</ Card >
0 commit comments