@@ -7,7 +7,6 @@ import Button from '@/components/shared/button';
7
7
import Link from '@/components/shared/link' ;
8
8
import MobileMenu from '@/components/shared/mobile-menu' ;
9
9
10
- import { MENU } from '@/lib/menus' ;
11
10
import Route from '@/lib/route' ;
12
11
13
12
import AboutIcon from '@/svgs/about.inline.svg' ;
@@ -56,11 +55,16 @@ const icons: {
56
55
tutorial : TutorialsIcon ,
57
56
} ;
58
57
58
+ type Menu = {
59
+ items : MenuItem [ ] ;
60
+ title ?: string ;
61
+ } ;
62
+
59
63
type MenuItem = {
60
64
name : string ;
61
- description : string ;
62
- iconName : string ;
63
65
linkUrl : string ;
66
+ description ?: string ;
67
+ iconName ?: string ;
64
68
} ;
65
69
66
70
type HighlightItem = {
@@ -74,10 +78,136 @@ type HighlightItem = {
74
78
type Header = {
75
79
title : string ;
76
80
href ?: string ;
77
- items ?: MenuItem [ ] ;
81
+ menus ?: Menu [ ] ;
78
82
highlight ?: HighlightItem ;
79
83
} ;
80
84
85
+ export const HEADER_MENU : Header [ ] = [
86
+ { title : 'WHY Bytebase' , href : Route . DOCS } ,
87
+ {
88
+ title : 'Solutions' ,
89
+ menus : [
90
+ {
91
+ title : 'By Use Case' ,
92
+ items : [
93
+ {
94
+ name : 'Database CI/CD' ,
95
+ linkUrl : Route . DOCS_DATABASE_CI_CD ,
96
+ } ,
97
+ {
98
+ name : 'Multi-tenant, multi-region deployment' ,
99
+ linkUrl : Route . DOCS_MULTI_TENANCY_DEPLOYMENT ,
100
+ } ,
101
+ {
102
+ name : 'Headless database workflow backend' ,
103
+ linkUrl : Route . DOCS_API_OVERVIEW ,
104
+ } ,
105
+ ] ,
106
+ } ,
107
+ {
108
+ title : 'By Industry' ,
109
+ items : [
110
+ {
111
+ name : 'Financial Services' ,
112
+ linkUrl : Route . INDUSTRY_FINANCIAL_SERVICES ,
113
+ } ,
114
+ {
115
+ name : 'Technology' ,
116
+ linkUrl : Route . INDUSTRY_TECHNOLOGY ,
117
+ } ,
118
+ {
119
+ name : 'Manufacturing' ,
120
+ linkUrl : Route . INDUSTRY_MANUFACTURING ,
121
+ } ,
122
+ {
123
+ name : 'Gaming' ,
124
+ linkUrl : Route . INDUSTRY_GAMING ,
125
+ } ,
126
+ {
127
+ name : 'Web3' ,
128
+ linkUrl : Route . INDUSTRY_WEB3 ,
129
+ } ,
130
+ ] ,
131
+ } ,
132
+ ] ,
133
+ } ,
134
+ {
135
+ title : 'Features' ,
136
+ menus : [
137
+ {
138
+ items : [
139
+ {
140
+ name : 'Schema Migration' ,
141
+ description : 'GUI-based, database CI/CD with GitOps' ,
142
+ linkUrl : Route . SCHEMA_MIGRATION ,
143
+ iconName : 'migrate' ,
144
+ } ,
145
+ {
146
+ name : 'Permission-based SQL Editor' ,
147
+ description : 'Bastion-less human-to-database permission control' ,
148
+ linkUrl : Route . SQL_EDITOR ,
149
+ iconName : 'editor' ,
150
+ } ,
151
+ {
152
+ name : 'Dynamic Data Masking' ,
153
+ description : 'Role-based multi-level masking policy' ,
154
+ linkUrl : Route . DATA_MASKING ,
155
+ iconName : 'mask' ,
156
+ } ,
157
+ {
158
+ name : 'Batch Change' ,
159
+ description : 'Multi-environments, multi-regions, multi-tenants' ,
160
+ linkUrl : Route . BATCH_CHANGE ,
161
+ iconName : 'batch' ,
162
+ } ,
163
+ ] ,
164
+ } ,
165
+ ] ,
166
+ } ,
167
+ {
168
+ title : 'Resources' ,
169
+ menus : [
170
+ {
171
+ items : [
172
+ {
173
+ name : 'Docs' ,
174
+ linkUrl : Route . DOCS ,
175
+ iconName : 'intro' ,
176
+ } ,
177
+ {
178
+ name : 'Supported Databases' ,
179
+ linkUrl : Route . DOCS_DB ,
180
+ iconName : 'db' ,
181
+ } ,
182
+ {
183
+ name : 'Case Study' ,
184
+ linkUrl : Route . BLOG_CASE_STUDY ,
185
+ iconName : 'casestudy' ,
186
+ } ,
187
+ {
188
+ name : 'Blog' ,
189
+ linkUrl : Route . BLOG ,
190
+ iconName : 'blog' ,
191
+ } ,
192
+ {
193
+ name : 'Company' ,
194
+ linkUrl : Route . ABOUT ,
195
+ iconName : 'about' ,
196
+ } ,
197
+ ] ,
198
+ } ,
199
+ ] ,
200
+ highlight : {
201
+ name : 'Tutorial' ,
202
+ description : 'Step-by-step guide through common features.' ,
203
+ linkUrl : Route . TUTORIAL ,
204
+ cta : 'Start Learning' ,
205
+ iconName : 'tutorial' ,
206
+ } ,
207
+ } ,
208
+ { title : 'Pricing' , href : Route . PRICING } ,
209
+ ] ;
210
+
81
211
const Header = ( { hasBanner = false } : { hasBanner ?: boolean } ) => {
82
212
const topBanner = PROMO_DATA . TOP_BANNER ;
83
213
const [ canShowSubmenu , setCanShowSubmenu ] = useState ( true ) ;
@@ -118,10 +248,10 @@ const Header = ({ hasBanner = false }: { hasBanner?: boolean }) => {
118
248
loading = "eager"
119
249
/>
120
250
</ Link >
121
- < ul className = "ml-9 mt-0.5 flex items-center gap-1 md:hidden" >
122
- { MENU . header . map ( ( { title, href = '' , items , highlight } : Header ) => {
251
+ < ul className = "ml-8 mt-0.5 flex items-center gap-1 md:hidden" >
252
+ { HEADER_MENU . map ( ( { title, href = '' , menus , highlight } : Header ) => {
123
253
return (
124
- < li key = { title } className = "group relative inline-block hover:cursor-pointer " >
254
+ < li key = { title } className = "group relative inline-block" >
125
255
{ href ? (
126
256
< Link
127
257
className = "px-3 py-2.5 text-16 font-medium tracking-wider"
@@ -138,36 +268,52 @@ const Header = ({ hasBanner = false }: { hasBanner?: boolean }) => {
138
268
< ChevronIcon className = "h-3 w-3 transition-transform duration-200 group-hover:-rotate-180" />
139
269
</ button >
140
270
) }
141
- { items ?. length && canShowSubmenu && (
142
- < div className = "invisible absolute -left-5 top-6 pt-6 opacity-0 transition-[opacity,visibility] duration-200 group-hover:visible group-hover:opacity-100" >
143
- < div className = "relative flex items-center gap-x-[30px] rounded-lg border border-gray-80 bg-white p-4 pl-8 shadow-menu before:absolute before:-top-[8.5px] before:left-11 before:h-4 before:w-4 before:rotate-45 before:rounded-tl before:border-l before:border-t before:border-gray-80 before:bg-white" >
144
- < ul className = "flex flex-col" >
145
- { items ?. map ( ( { name, linkUrl, description, iconName } ) => {
146
- const Icon = iconName ? icons [ iconName ] : null ;
147
- return (
148
- < li key = { name } className = "pt-6 first:pt-2" >
149
- < Link
150
- className = "group/link block whitespace-nowrap"
151
- size = "md"
152
- theme = "gray"
153
- href = { linkUrl }
154
- prefetch = { false }
155
- onClick = { handleSubmenuClick }
156
- >
157
- < div className = "flex flex-col gap-y-2.5" >
158
- < div className = "flex items-center gap-x-2 group-hover/link:text-primary-1" >
159
- { Icon && < Icon className = "h-5 w-5 shrink-0" /> }
160
- < span className = "font-medium tracking-tight" > { name } </ span >
161
- </ div >
162
- < span className = "text-16 leading-normal text-gray-40" >
163
- { description }
164
- </ span >
165
- </ div >
166
- </ Link >
167
- </ li >
168
- ) ;
169
- } ) }
170
- </ ul >
271
+ { menus ?. length && canShowSubmenu && (
272
+ < div className = "invisible absolute left-0 top-6 pt-4 opacity-0 transition-[opacity,visibility] duration-200 group-hover:visible group-hover:opacity-100" >
273
+ < div className = "relative -left-1/3 flex items-start gap-x-8 rounded-lg border border-gray-80 bg-white p-6 shadow-menu" >
274
+ { menus . map ( ( { items, title : subtitle } ) => (
275
+ < div
276
+ key = { `${ title } -${ subtitle } ` }
277
+ className = "flex h-full flex-col items-start justify-start"
278
+ >
279
+ { subtitle && (
280
+ < p className = "pb-3 pt-1 text-16 font-medium leading-none text-gray-60" >
281
+ { subtitle }
282
+ </ p >
283
+ ) }
284
+ < ul className = "flex flex-col justify-between" >
285
+ { items ?. map ( ( { name, linkUrl, description, iconName } ) => {
286
+ const Icon = iconName ? icons [ iconName ] : null ;
287
+ return (
288
+ < li key = { name } className = "pb-2 pt-1" >
289
+ < Link
290
+ className = "group/link block whitespace-nowrap"
291
+ size = "md"
292
+ theme = "gray"
293
+ href = { linkUrl }
294
+ prefetch = { false }
295
+ onClick = { handleSubmenuClick }
296
+ >
297
+ < div className = "flex flex-col" >
298
+ < div className = "flex items-center gap-x-2 group-hover/link:text-primary-1" >
299
+ { Icon && (
300
+ < Icon className = "inline-block h-5 w-5 shrink-0 opacity-80" />
301
+ ) }
302
+ < span className = "font-medium tracking-tight" > { name } </ span >
303
+ </ div >
304
+ { description && (
305
+ < span className = "pt-1 text-16 leading-normal text-gray-40" >
306
+ { description }
307
+ </ span >
308
+ ) }
309
+ </ div >
310
+ </ Link >
311
+ </ li >
312
+ ) ;
313
+ } ) }
314
+ </ ul >
315
+ </ div >
316
+ ) ) }
171
317
{ highlight && (
172
318
< Link
173
319
className = "group/box flex h-full min-h-[272px] w-[244px] grow flex-col justify-between rounded-md bg-tutorials p-5 text-gray-15"
0 commit comments