goobs-frontend 0.8.13 → 0.8.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +13 -13
- package/src/components/Button/index.tsx +44 -27
- package/src/components/DataGrid/Footer/index.tsx +1 -0
- package/src/components/Form/DataGrid/index.tsx +2 -0
- package/src/components/Nav/VerticalVariant/mainNav/index.tsx +163 -0
- package/src/components/Nav/VerticalVariant/subNav/index.tsx +156 -0
- package/src/components/Nav/VerticalVariant/viewNav/index.tsx +75 -0
- package/src/components/Nav/index.tsx +313 -119
- package/src/components/PricingTable/defaultconfig.tsx +9 -0
- package/src/components/Tabs/index.tsx +195 -0
- package/src/index.ts +41 -12
- package/src/components/Nav/HorizontalVariant/index.tsx +0 -146
- package/src/components/Nav/VerticalVariant/index.tsx +0 -445
|
@@ -1,149 +1,343 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
import React, { useState,
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import
|
|
2
|
+
import React, { useState, useCallback, FC } from 'react'
|
|
3
|
+
import { useRouter } from 'next/navigation'
|
|
4
|
+
import { Drawer, Box, Stack, Divider } from '@mui/material'
|
|
5
|
+
import Link from 'next/link'
|
|
6
|
+
import { Typography } from '../Typography'
|
|
7
|
+
import SearchableDropdown from '../SearchableDropdown'
|
|
8
|
+
import { white, ocean, semiTransparentWhite } from '../../styles/palette'
|
|
6
9
|
|
|
7
|
-
//
|
|
8
|
-
|
|
10
|
+
// Sub-components for mainNav, subNav, viewNav
|
|
11
|
+
import MainNav from './VerticalVariant/mainNav'
|
|
12
|
+
import SubNavComponent from './VerticalVariant/subNav'
|
|
13
|
+
import ViewNav from './VerticalVariant/viewNav'
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
*/
|
|
13
|
-
export interface NavProps {
|
|
14
|
-
items?: (NavProps | SubNav | View)[] // Array of navigation items
|
|
15
|
-
showSearchableNav?: boolean // Flag to show/hide searchable navigation
|
|
16
|
-
showTitle?: boolean // Flag to show/hide title
|
|
17
|
-
showLine?: boolean // Flag to show/hide divider line
|
|
18
|
-
verticalNavTitle?: string // Title for vertical navigation
|
|
19
|
-
searchableNavLabel?: string // Label for searchable navigation
|
|
20
|
-
anchor?: 'left' | 'right' // Position of vertical navigation
|
|
21
|
-
orientation?: 'vertical' | 'horizontal' // Orientation of navigation
|
|
22
|
-
height?: string // Height of navigation (for horizontal)
|
|
23
|
-
alignment?: Alignment // Alignment of items (for horizontal)
|
|
24
|
-
navname?: string // Name of the navigation
|
|
25
|
-
title?: string // Title of the navigation item
|
|
26
|
-
route?: string // Route for the navigation item
|
|
27
|
-
subnavs?: SubNav[] // Sub-navigation items
|
|
28
|
-
onClick?: () => void // Click handler for the item
|
|
29
|
-
hasleftborder?: string // Flag for left border
|
|
30
|
-
hasrightborder?: string // Flag for right border
|
|
31
|
-
trigger?: 'route' | 'onClick' | 'routeonhorizontal' // Trigger type for the item
|
|
32
|
-
backgroundcolor?: string
|
|
33
|
-
shrunkfontcolor?: string // Color of the label when shrunk
|
|
34
|
-
unshrunkfontcolor?: string // Color of the label when not shrunk
|
|
35
|
-
titleUrl?: string // Changed from url to titleUrl
|
|
36
|
-
mobileOpen?: boolean // Controls mobile drawer open state
|
|
37
|
-
onClose?: () => void // Handler for closing mobile drawer
|
|
38
|
-
variant?: 'temporary' | 'permanent' // Drawer variant for mobile/desktop
|
|
39
|
-
spacingfromtopofscreen?: string // Spacing from top of screen
|
|
40
|
-
}
|
|
15
|
+
/* -------------------------------------------------------------------------- */
|
|
16
|
+
/* INTERFACES */
|
|
17
|
+
/* -------------------------------------------------------------------------- */
|
|
41
18
|
|
|
42
19
|
/**
|
|
43
|
-
*
|
|
20
|
+
* A single interface that covers all vertical nav items:
|
|
21
|
+
* - navType = 'mainNav' => can have subnavs
|
|
22
|
+
* - navType = 'subNav' => can have views
|
|
23
|
+
* - navType = 'viewNav' => no children
|
|
44
24
|
*/
|
|
45
|
-
export
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
25
|
+
export interface NavItem {
|
|
26
|
+
navType: 'mainNav' | 'subNav' | 'viewNav'
|
|
27
|
+
title: string
|
|
28
|
+
route?: string
|
|
29
|
+
trigger?: 'route' | 'onClick'
|
|
30
|
+
onClick?: () => void
|
|
31
|
+
// For mainNav items only:
|
|
32
|
+
subnavs?: NavItem[]
|
|
33
|
+
// For subNav items only:
|
|
34
|
+
views?: NavItem[]
|
|
51
35
|
}
|
|
52
36
|
|
|
53
37
|
/**
|
|
54
|
-
*
|
|
38
|
+
* NavProps for the vertical nav component.
|
|
55
39
|
*/
|
|
56
|
-
export
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
40
|
+
export interface NavProps {
|
|
41
|
+
/** The entire nav data array (mainNav, subNav, viewNav items). */
|
|
42
|
+
items?: NavItem[]
|
|
43
|
+
|
|
44
|
+
/** Whether to show the search box. */
|
|
45
|
+
showSearchableNav?: boolean
|
|
46
|
+
|
|
47
|
+
/** Whether to show the nav title. */
|
|
48
|
+
showTitle?: boolean
|
|
49
|
+
|
|
50
|
+
/** Whether to show a horizontal divider line. */
|
|
51
|
+
showLine?: boolean
|
|
52
|
+
|
|
53
|
+
/** Title text for the nav. */
|
|
54
|
+
verticalNavTitle?: string
|
|
55
|
+
|
|
56
|
+
/** Label for the search box. */
|
|
57
|
+
searchableNavLabel?: string
|
|
58
|
+
|
|
59
|
+
/** Side on which the Drawer anchors. */
|
|
60
|
+
anchor?: 'left' | 'right'
|
|
61
|
+
|
|
62
|
+
/** Background color (drawer or items). */
|
|
63
|
+
backgroundcolor?: string
|
|
64
|
+
|
|
65
|
+
/** Label color when shrunk (search box). */
|
|
66
|
+
shrunkfontcolor?: string
|
|
67
|
+
|
|
68
|
+
/** Label color when not shrunk (search box). */
|
|
69
|
+
unshrunkfontcolor?: string
|
|
70
|
+
|
|
71
|
+
/** Destination route if user clicks the nav title. */
|
|
72
|
+
titleUrl?: string
|
|
73
|
+
|
|
74
|
+
/** Controls mobile drawer open state. */
|
|
75
|
+
mobileOpen?: boolean
|
|
76
|
+
|
|
77
|
+
/** Handler for closing the mobile drawer. */
|
|
78
|
+
onClose?: () => void
|
|
79
|
+
|
|
80
|
+
/** MUI Drawer variant: 'temporary' or 'permanent'. */
|
|
81
|
+
variant?: 'temporary' | 'permanent'
|
|
82
|
+
|
|
83
|
+
/** Spacing from the top of the screen. */
|
|
84
|
+
spacingfromtopofscreen?: string
|
|
85
|
+
|
|
86
|
+
/** Margin above the nav title. */
|
|
87
|
+
marginabovetitle?: string
|
|
88
|
+
|
|
89
|
+
/** Margin below the nav title. */
|
|
90
|
+
marginbelowtitle?: string
|
|
61
91
|
}
|
|
62
92
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
function Nav({
|
|
93
|
+
/* -------------------------------------------------------------------------- */
|
|
94
|
+
/* SINGLE CONST NAV COMPONENT */
|
|
95
|
+
/* -------------------------------------------------------------------------- */
|
|
96
|
+
|
|
97
|
+
const Nav: FC<NavProps> = ({
|
|
69
98
|
items = [],
|
|
70
|
-
showSearchableNav = true,
|
|
99
|
+
showSearchableNav = true,
|
|
71
100
|
showTitle = true,
|
|
72
101
|
showLine = true,
|
|
73
102
|
verticalNavTitle = 'Navigation',
|
|
74
|
-
searchableNavLabel = 'Search or select a nav',
|
|
103
|
+
searchableNavLabel = 'Search or select a nav',
|
|
75
104
|
anchor = 'left',
|
|
76
|
-
orientation,
|
|
77
|
-
height = '80px',
|
|
78
|
-
alignment = 'left',
|
|
79
|
-
navname,
|
|
80
105
|
shrunkfontcolor = 'black',
|
|
81
106
|
unshrunkfontcolor = 'black',
|
|
82
107
|
backgroundcolor,
|
|
83
|
-
titleUrl,
|
|
108
|
+
titleUrl,
|
|
84
109
|
mobileOpen = false,
|
|
85
110
|
onClose,
|
|
86
111
|
variant = 'permanent',
|
|
87
112
|
spacingfromtopofscreen,
|
|
88
|
-
|
|
89
|
-
|
|
113
|
+
marginabovetitle = '0px',
|
|
114
|
+
marginbelowtitle = '0px',
|
|
115
|
+
}) => {
|
|
116
|
+
// States for expanded mainNavs and subNavs
|
|
90
117
|
const [expandedNavs, setExpandedNavs] = useState<string[]>([])
|
|
91
118
|
const [expandedSubnavs, setExpandedSubnavs] = useState<string[]>([])
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
119
|
+
|
|
120
|
+
// Default width for the vertical nav
|
|
121
|
+
const [verticalNavWidth] = useState<string>('250px')
|
|
122
|
+
|
|
123
|
+
// For route triggers
|
|
124
|
+
const router = useRouter()
|
|
125
|
+
|
|
126
|
+
// For search dropdown
|
|
127
|
+
const [selectedNav, setSelectedNav] = useState<string | null>(null)
|
|
128
|
+
|
|
129
|
+
// Build search dropdown options from mainNav items
|
|
130
|
+
const navOptions = items
|
|
131
|
+
.filter(item => item.navType === 'mainNav')
|
|
132
|
+
.map(item => ({ value: item.title }))
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Handle route or onClick triggers for mainNav/subNav/viewNav
|
|
136
|
+
*/
|
|
137
|
+
const handleNavClick = useCallback(
|
|
138
|
+
(item: NavItem) => {
|
|
139
|
+
if (item.trigger === 'route' && item.route) {
|
|
140
|
+
router.push(item.route)
|
|
141
|
+
if (variant === 'temporary' && onClose) {
|
|
142
|
+
onClose()
|
|
143
|
+
}
|
|
144
|
+
} else if (item.trigger === 'onClick' && item.onClick) {
|
|
145
|
+
item.onClick()
|
|
146
|
+
if (variant === 'temporary' && onClose) {
|
|
147
|
+
onClose()
|
|
148
|
+
}
|
|
106
149
|
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
150
|
+
},
|
|
151
|
+
[router, variant, onClose]
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* When user selects a mainNav from the search dropdown
|
|
156
|
+
*/
|
|
157
|
+
const handleSearchableNavChange = useCallback(
|
|
158
|
+
(newValue: { value: string } | null) => {
|
|
159
|
+
setSelectedNav(newValue?.value || null)
|
|
160
|
+
},
|
|
161
|
+
[]
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Recursively render mainNav -> subNav -> viewNav
|
|
166
|
+
*/
|
|
167
|
+
const renderItem = useCallback(
|
|
168
|
+
(
|
|
169
|
+
item: NavItem,
|
|
170
|
+
level: number,
|
|
171
|
+
activeAndHoverColor = semiTransparentWhite.main
|
|
172
|
+
) => {
|
|
173
|
+
switch (item.navType) {
|
|
174
|
+
case 'mainNav': {
|
|
175
|
+
const hasChildren = !!item.subnavs?.length
|
|
176
|
+
return (
|
|
177
|
+
<MainNav
|
|
178
|
+
key={item.title}
|
|
179
|
+
title={item.title}
|
|
180
|
+
hasChildren={hasChildren}
|
|
181
|
+
expandedNavs={expandedNavs}
|
|
182
|
+
setExpandedNavs={setExpandedNavs}
|
|
183
|
+
onClick={() => handleNavClick(item)}
|
|
184
|
+
level={level}
|
|
185
|
+
activeAndHoverColor={activeAndHoverColor}
|
|
186
|
+
>
|
|
187
|
+
{item.subnavs?.map(subItem =>
|
|
188
|
+
renderItem(subItem, level + 1, activeAndHoverColor)
|
|
189
|
+
)}
|
|
190
|
+
</MainNav>
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
case 'subNav': {
|
|
194
|
+
const hasChildren = !!item.views?.length
|
|
195
|
+
return (
|
|
196
|
+
<SubNavComponent
|
|
197
|
+
key={item.title}
|
|
198
|
+
title={item.title}
|
|
199
|
+
route={item.route}
|
|
200
|
+
trigger={item.trigger}
|
|
201
|
+
expandedSubnavs={expandedSubnavs}
|
|
202
|
+
setExpandedSubnavs={setExpandedSubnavs}
|
|
203
|
+
activeAndHoverColor={activeAndHoverColor}
|
|
204
|
+
onClose={onClose}
|
|
205
|
+
variant={variant}
|
|
206
|
+
hasChildren={hasChildren}
|
|
207
|
+
>
|
|
208
|
+
{item.views?.map(view =>
|
|
209
|
+
renderItem(view, level + 2, activeAndHoverColor)
|
|
210
|
+
)}
|
|
211
|
+
</SubNavComponent>
|
|
212
|
+
)
|
|
213
|
+
}
|
|
214
|
+
case 'viewNav': {
|
|
215
|
+
return (
|
|
216
|
+
<ViewNav
|
|
217
|
+
key={item.title}
|
|
218
|
+
title={item.title}
|
|
219
|
+
route={item.route}
|
|
220
|
+
trigger={item.trigger}
|
|
221
|
+
onClick={item.onClick}
|
|
222
|
+
level={level}
|
|
223
|
+
activeAndHoverColor={activeAndHoverColor}
|
|
224
|
+
onClose={onClose}
|
|
225
|
+
variant={variant}
|
|
226
|
+
/>
|
|
227
|
+
)
|
|
228
|
+
}
|
|
229
|
+
default:
|
|
230
|
+
return null
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
[
|
|
234
|
+
expandedNavs,
|
|
235
|
+
setExpandedNavs,
|
|
236
|
+
expandedSubnavs,
|
|
237
|
+
setExpandedSubnavs,
|
|
238
|
+
handleNavClick,
|
|
239
|
+
onClose,
|
|
240
|
+
variant,
|
|
241
|
+
]
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
// Drawer Content: Title, optional search, optional divider, then items
|
|
245
|
+
const drawerContent = (
|
|
246
|
+
<>
|
|
247
|
+
<Box px="15px" sx={{ whiteSpace: 'nowrap' /* no text wrapping */ }}>
|
|
248
|
+
{showTitle && (
|
|
249
|
+
<Box mt={marginabovetitle} mb={marginbelowtitle}>
|
|
250
|
+
<Link
|
|
251
|
+
href={titleUrl || '/'}
|
|
252
|
+
passHref
|
|
253
|
+
style={{ textDecoration: 'none' }}
|
|
254
|
+
onClick={variant === 'temporary' ? onClose : undefined}
|
|
255
|
+
>
|
|
256
|
+
<Typography
|
|
257
|
+
fontvariant="merrih4"
|
|
258
|
+
fontcolor={white.main}
|
|
259
|
+
text={verticalNavTitle}
|
|
260
|
+
/>
|
|
261
|
+
</Link>
|
|
262
|
+
</Box>
|
|
263
|
+
)}
|
|
264
|
+
|
|
265
|
+
{showSearchableNav && (
|
|
266
|
+
<Stack mt={{ xs: '10px', md: '10px', lg: 0 }} spacing={0}>
|
|
267
|
+
<Box
|
|
268
|
+
sx={{
|
|
269
|
+
position: 'relative',
|
|
270
|
+
zIndex: theme => theme.zIndex.drawer + 1,
|
|
271
|
+
width: '100%',
|
|
272
|
+
minHeight: '40px',
|
|
273
|
+
whiteSpace: 'nowrap',
|
|
274
|
+
}}
|
|
275
|
+
>
|
|
276
|
+
<SearchableDropdown
|
|
277
|
+
label={searchableNavLabel}
|
|
278
|
+
options={navOptions}
|
|
279
|
+
backgroundcolor={backgroundcolor || semiTransparentWhite.main}
|
|
280
|
+
outlinecolor="none"
|
|
281
|
+
fontcolor={white.main}
|
|
282
|
+
shrunkfontcolor={shrunkfontcolor}
|
|
283
|
+
unshrunkfontcolor={unshrunkfontcolor}
|
|
284
|
+
shrunklabelposition="aboveNotch"
|
|
285
|
+
onChange={handleSearchableNavChange}
|
|
286
|
+
placeholder="Search..."
|
|
287
|
+
/>
|
|
288
|
+
</Box>
|
|
289
|
+
</Stack>
|
|
290
|
+
)}
|
|
291
|
+
</Box>
|
|
292
|
+
|
|
293
|
+
{showLine && (
|
|
294
|
+
<Divider
|
|
295
|
+
sx={{
|
|
296
|
+
width: '100%',
|
|
297
|
+
backgroundColor: white.main,
|
|
298
|
+
mt: 2.5,
|
|
299
|
+
}}
|
|
300
|
+
/>
|
|
301
|
+
)}
|
|
302
|
+
|
|
303
|
+
{selectedNav
|
|
304
|
+
? items.filter(i => i.title === selectedNav).map(i => renderItem(i, 0))
|
|
305
|
+
: items.map(i => renderItem(i, 0))}
|
|
306
|
+
</>
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
// Render the Drawer
|
|
310
|
+
return (
|
|
311
|
+
<Drawer
|
|
312
|
+
variant={variant}
|
|
313
|
+
anchor={anchor}
|
|
314
|
+
open={variant === 'temporary' ? mobileOpen : true}
|
|
315
|
+
onClose={onClose}
|
|
316
|
+
elevation={0}
|
|
317
|
+
sx={{
|
|
318
|
+
width: 'auto',
|
|
319
|
+
height: '100%',
|
|
320
|
+
flexShrink: 0,
|
|
321
|
+
'& .MuiDrawer-paper': {
|
|
322
|
+
minWidth: verticalNavWidth,
|
|
323
|
+
width: 'auto',
|
|
324
|
+
whiteSpace: 'nowrap',
|
|
325
|
+
overflowX: 'auto',
|
|
326
|
+
border: 0,
|
|
327
|
+
zIndex: theme =>
|
|
328
|
+
variant === 'temporary'
|
|
329
|
+
? theme.zIndex.drawer + 2
|
|
330
|
+
: theme.zIndex.drawer - 1,
|
|
331
|
+
backgroundColor: ocean.main,
|
|
332
|
+
pt: '17px',
|
|
333
|
+
boxSizing: 'border-box',
|
|
334
|
+
marginTop: spacingfromtopofscreen,
|
|
335
|
+
},
|
|
336
|
+
}}
|
|
337
|
+
>
|
|
338
|
+
{drawerContent}
|
|
339
|
+
</Drawer>
|
|
340
|
+
)
|
|
147
341
|
}
|
|
148
342
|
|
|
149
343
|
export default Nav
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React, { useState, useEffect } from 'react'
|
|
3
|
+
import { AppBar, Toolbar, Box, Tabs as MuiTabs, Tab } from '@mui/material'
|
|
4
|
+
import { usePathname } from 'next/navigation'
|
|
5
|
+
|
|
6
|
+
/** Represents the shape of each tab item in the horizontal nav. */
|
|
7
|
+
export interface TabsItem {
|
|
8
|
+
/** The text/title displayed on the tab */
|
|
9
|
+
title?: string
|
|
10
|
+
|
|
11
|
+
/** The URL route */
|
|
12
|
+
route?: string
|
|
13
|
+
|
|
14
|
+
/** The trigger type: 'route' | 'onClick' */
|
|
15
|
+
trigger?: 'route' | 'onClick'
|
|
16
|
+
|
|
17
|
+
/** OnClick callback */
|
|
18
|
+
onClick?: () => void
|
|
19
|
+
|
|
20
|
+
/** Optional left border */
|
|
21
|
+
hasleftborder?: string
|
|
22
|
+
|
|
23
|
+
/** Optional right border */
|
|
24
|
+
hasrightborder?: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Represents the currently active/selected tab */
|
|
28
|
+
export interface ActiveTabValue {
|
|
29
|
+
tabId: string | false
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Props for the horizontal Tabs component. */
|
|
33
|
+
export interface TabsProps {
|
|
34
|
+
/** Array of horizontal items (TabsItem). */
|
|
35
|
+
items: TabsItem[]
|
|
36
|
+
|
|
37
|
+
/** Height of the tabs bar. */
|
|
38
|
+
height?: string
|
|
39
|
+
|
|
40
|
+
/** MUI alignment. */
|
|
41
|
+
alignment?: 'left' | 'center' | 'right' | 'inherit' | 'justify'
|
|
42
|
+
|
|
43
|
+
/** Unique name for this tab set (for managing active tab). */
|
|
44
|
+
navname?: string
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* A horizontal navigation component, built with MUI Tabs.
|
|
49
|
+
*/
|
|
50
|
+
function Tabs({
|
|
51
|
+
items,
|
|
52
|
+
height = '48px',
|
|
53
|
+
alignment = 'left',
|
|
54
|
+
navname = '',
|
|
55
|
+
}: TabsProps) {
|
|
56
|
+
const [activeTabValues, setActiveTabValues] = useState<
|
|
57
|
+
Record<string, ActiveTabValue>
|
|
58
|
+
>({})
|
|
59
|
+
const pathname = usePathname()
|
|
60
|
+
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
/**
|
|
63
|
+
* Find the item whose route matches the current path
|
|
64
|
+
*/
|
|
65
|
+
const currentTab = items.find(item => item.route === pathname)
|
|
66
|
+
|
|
67
|
+
setActiveTabValues(prev => ({
|
|
68
|
+
...prev,
|
|
69
|
+
[navname]: { tabId: currentTab?.title || false },
|
|
70
|
+
}))
|
|
71
|
+
}, [items, navname, pathname])
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* When user changes tab via click,
|
|
75
|
+
* update the activeTabValues record.
|
|
76
|
+
*/
|
|
77
|
+
const handleTabChange = (event: React.SyntheticEvent, newValue: string) => {
|
|
78
|
+
setActiveTabValues(prev => ({
|
|
79
|
+
...prev,
|
|
80
|
+
[navname]: { tabId: newValue },
|
|
81
|
+
}))
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Called when a tab is clicked:
|
|
86
|
+
* - if trigger='route', navigate to the route
|
|
87
|
+
* - if trigger='onClick', call onClick
|
|
88
|
+
*/
|
|
89
|
+
const handleTabClick = (tab: TabsItem) => {
|
|
90
|
+
if (tab.trigger === 'route' && tab.route) {
|
|
91
|
+
window.location.href = tab.route
|
|
92
|
+
} else if (tab.trigger === 'onClick' && tab.onClick) {
|
|
93
|
+
tab.onClick()
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<AppBar
|
|
99
|
+
position="sticky"
|
|
100
|
+
sx={{
|
|
101
|
+
backgroundColor: 'black',
|
|
102
|
+
color: 'white',
|
|
103
|
+
}}
|
|
104
|
+
>
|
|
105
|
+
<Toolbar
|
|
106
|
+
disableGutters
|
|
107
|
+
sx={{
|
|
108
|
+
paddingLeft: 0,
|
|
109
|
+
paddingRight: 0,
|
|
110
|
+
height: `${height} !important`,
|
|
111
|
+
minHeight: `${height} !important`,
|
|
112
|
+
borderTop: '1px solid white',
|
|
113
|
+
}}
|
|
114
|
+
>
|
|
115
|
+
<Box
|
|
116
|
+
sx={{
|
|
117
|
+
width: '100%',
|
|
118
|
+
display: 'flex',
|
|
119
|
+
flexDirection: 'row',
|
|
120
|
+
justifyContent: alignment === 'left' ? 'flex-start' : alignment,
|
|
121
|
+
}}
|
|
122
|
+
>
|
|
123
|
+
<Box
|
|
124
|
+
sx={{
|
|
125
|
+
flexGrow: 1,
|
|
126
|
+
display: 'flex',
|
|
127
|
+
height: height,
|
|
128
|
+
justifyContent: 'flex-start',
|
|
129
|
+
paddingLeft: 0,
|
|
130
|
+
paddingRight: 0,
|
|
131
|
+
}}
|
|
132
|
+
>
|
|
133
|
+
<MuiTabs
|
|
134
|
+
value={activeTabValues[navname]?.tabId || false}
|
|
135
|
+
onChange={handleTabChange}
|
|
136
|
+
aria-label="nav tabs"
|
|
137
|
+
variant="fullWidth"
|
|
138
|
+
sx={{
|
|
139
|
+
height: height,
|
|
140
|
+
'& .MuiTabs-flexContainer': {
|
|
141
|
+
height: '100%',
|
|
142
|
+
},
|
|
143
|
+
'& .MuiTab-root': {
|
|
144
|
+
height: '100%',
|
|
145
|
+
minHeight: 'unset',
|
|
146
|
+
},
|
|
147
|
+
}}
|
|
148
|
+
>
|
|
149
|
+
{items.map(item => (
|
|
150
|
+
<Tab
|
|
151
|
+
key={item.title}
|
|
152
|
+
value={item.title || ''}
|
|
153
|
+
label={item.title || ''}
|
|
154
|
+
onClick={() => handleTabClick(item)}
|
|
155
|
+
sx={{
|
|
156
|
+
flex: 1,
|
|
157
|
+
textTransform: 'none',
|
|
158
|
+
boxSizing: 'border-box',
|
|
159
|
+
backgroundColor: 'black',
|
|
160
|
+
color: '#fff',
|
|
161
|
+
fontWeight: 500,
|
|
162
|
+
fontFamily: 'Merriweather',
|
|
163
|
+
fontSize: 16,
|
|
164
|
+
height: height,
|
|
165
|
+
'&:hover': {
|
|
166
|
+
backgroundColor: 'rgba(255, 255, 255, 0.1)',
|
|
167
|
+
},
|
|
168
|
+
'& .MuiTouchRipple-root': {
|
|
169
|
+
color: '#fff',
|
|
170
|
+
},
|
|
171
|
+
'&.Mui-selected': {
|
|
172
|
+
color: '#fff',
|
|
173
|
+
backgroundColor: 'rgba(255, 255, 255, 0.2)',
|
|
174
|
+
},
|
|
175
|
+
'& .MuiSvgIcon-root': {
|
|
176
|
+
color: '#fff',
|
|
177
|
+
},
|
|
178
|
+
...(item.hasleftborder === 'true' && {
|
|
179
|
+
borderLeft: '1px solid white',
|
|
180
|
+
}),
|
|
181
|
+
...(item.hasrightborder === 'true' && {
|
|
182
|
+
borderRight: '1px solid white',
|
|
183
|
+
}),
|
|
184
|
+
}}
|
|
185
|
+
/>
|
|
186
|
+
))}
|
|
187
|
+
</MuiTabs>
|
|
188
|
+
</Box>
|
|
189
|
+
</Box>
|
|
190
|
+
</Toolbar>
|
|
191
|
+
</AppBar>
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export default Tabs
|