goobs-frontend 0.8.10 → 0.8.11
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 +1 -1
- package/src/components/Accordion/index.tsx +64 -25
- package/src/components/Button/index.tsx +3 -4
- package/src/components/Card/index.tsx +9 -19
- package/src/components/Content/Structure/searchableDropdown/useSearchableDropdown.tsx +65 -0
- package/src/components/Content/Structure/stepper/useStepper.tsx +5 -14
- package/src/components/Content/index.tsx +7 -0
- package/src/components/Grid/index.tsx +18 -15
- package/src/components/Nav/VerticalVariant/index.tsx +38 -58
- package/src/components/Nav/index.tsx +16 -13
- package/src/components/SearchableDropdown/index.tsx +378 -0
- package/src/components/Stepper/index.tsx +88 -53
- package/src/components/TextField/index.tsx +4 -2
- package/src/index.ts +6 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goobs-frontend",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "A comprehensive React-based UI library built on Material-UI, offering a wide range of customizable components including grids, typography, buttons, cards, forms, navigation, pricing tables, steppers, tooltips, accordions, and more. Designed for building responsive and consistent user interfaces with advanced features like form validation, theming, and code syntax highlighting.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -1,44 +1,83 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import React from 'react'
|
|
3
|
+
import React, { JSX } from 'react'
|
|
4
4
|
import { styled } from '@mui/material/styles'
|
|
5
5
|
import MuiAccordion, { AccordionProps } from '@mui/material/Accordion'
|
|
6
6
|
import MuiAccordionSummary, {
|
|
7
7
|
AccordionSummaryProps,
|
|
8
8
|
} from '@mui/material/AccordionSummary'
|
|
9
|
-
import MuiAccordionDetails
|
|
9
|
+
import MuiAccordionDetails, {
|
|
10
|
+
AccordionDetailsProps,
|
|
11
|
+
} from '@mui/material/AccordionDetails'
|
|
10
12
|
import { ExpandMore } from '@mui/icons-material'
|
|
11
13
|
import { black } from '../../styles/palette'
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
<
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
15
|
+
type CustomAccordionProps = Omit<AccordionProps, 'children'> & {
|
|
16
|
+
children: NonNullable<React.ReactNode>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
type CustomAccordionSummaryProps = Omit<AccordionSummaryProps, 'children'> & {
|
|
20
|
+
children: NonNullable<React.ReactNode>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
type CustomAccordionDetailsProps = Omit<AccordionDetailsProps, 'children'> & {
|
|
24
|
+
children: NonNullable<React.ReactNode>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const StyledAccordion = styled(MuiAccordion)({
|
|
28
|
+
'&.MuiAccordion-root': {
|
|
29
|
+
'&:before': {
|
|
30
|
+
display: 'none',
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const StyledAccordionSummary = styled(MuiAccordionSummary)({
|
|
31
36
|
fontSize: '20px',
|
|
32
37
|
fontFamily: 'merriweather',
|
|
33
38
|
fontWeight: 500,
|
|
34
39
|
})
|
|
35
40
|
|
|
36
|
-
|
|
37
|
-
* Custom styled AccordionDetails component.
|
|
38
|
-
* Wraps MUI's AccordionDetails with custom padding based on the theme's spacing.
|
|
39
|
-
*/
|
|
40
|
-
const AccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
|
|
41
|
+
const StyledAccordionDetails = styled(MuiAccordionDetails)(({ theme }) => ({
|
|
41
42
|
padding: theme.spacing(2),
|
|
42
43
|
}))
|
|
43
44
|
|
|
45
|
+
function Accordion({ children, ...props }: CustomAccordionProps): JSX.Element {
|
|
46
|
+
return (
|
|
47
|
+
<StyledAccordion disableGutters {...props}>
|
|
48
|
+
{children}
|
|
49
|
+
</StyledAccordion>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function AccordionSummary({
|
|
54
|
+
children,
|
|
55
|
+
...props
|
|
56
|
+
}: CustomAccordionSummaryProps): JSX.Element {
|
|
57
|
+
return (
|
|
58
|
+
<StyledAccordionSummary
|
|
59
|
+
expandIcon={<ExpandMore sx={{ color: black.main }} />}
|
|
60
|
+
{...props}
|
|
61
|
+
>
|
|
62
|
+
{children}
|
|
63
|
+
</StyledAccordionSummary>
|
|
64
|
+
)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function AccordionDetails({
|
|
68
|
+
children,
|
|
69
|
+
...props
|
|
70
|
+
}: CustomAccordionDetailsProps): JSX.Element {
|
|
71
|
+
return <StyledAccordionDetails {...props}>{children}</StyledAccordionDetails>
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Accordion.displayName = 'Accordion'
|
|
75
|
+
AccordionSummary.displayName = 'AccordionSummary'
|
|
76
|
+
AccordionDetails.displayName = 'AccordionDetails'
|
|
77
|
+
|
|
44
78
|
export { Accordion, AccordionSummary, AccordionDetails }
|
|
79
|
+
export type {
|
|
80
|
+
CustomAccordionProps,
|
|
81
|
+
CustomAccordionSummaryProps,
|
|
82
|
+
CustomAccordionDetailsProps,
|
|
83
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
import React from 'react'
|
|
2
|
+
import React, { JSX } from 'react'
|
|
3
3
|
import { Button, Box, ButtonProps } from '@mui/material'
|
|
4
4
|
import Typography from '../Typography'
|
|
5
5
|
import { SvgIconProps } from '@mui/material/SvgIcon'
|
|
@@ -84,7 +84,7 @@ const ContentWrapper = styled(Box)<{
|
|
|
84
84
|
gap: '8px',
|
|
85
85
|
}))
|
|
86
86
|
|
|
87
|
-
|
|
87
|
+
function CustomButton({
|
|
88
88
|
text,
|
|
89
89
|
variant = 'contained',
|
|
90
90
|
fontvariant = 'merriparagraph',
|
|
@@ -101,7 +101,7 @@ const CustomButton: React.FC<CustomButtonProps> = ({
|
|
|
101
101
|
fontlocation = 'center',
|
|
102
102
|
sx,
|
|
103
103
|
...restProps
|
|
104
|
-
})
|
|
104
|
+
}: CustomButtonProps): JSX.Element {
|
|
105
105
|
const handleButtonClick = (
|
|
106
106
|
event: React.MouseEvent<HTMLButtonElement>
|
|
107
107
|
): void => {
|
|
@@ -159,5 +159,4 @@ const CustomButton: React.FC<CustomButtonProps> = ({
|
|
|
159
159
|
)
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
CustomButton.displayName = 'CustomButton'
|
|
163
162
|
export default CustomButton
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { JSX } from 'react'
|
|
2
4
|
import { BoxProps } from '@mui/material'
|
|
3
5
|
import { CustomStepperProps } from '../Stepper'
|
|
4
6
|
import InventoryCard from './variants/inventory'
|
|
@@ -10,11 +12,7 @@ import DefaultCard from './variants/defaultconfig'
|
|
|
10
12
|
import { columnconfig } from '../Grid'
|
|
11
13
|
import { CustomButtonProps } from '../Button'
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
* Props for the Card component.
|
|
15
|
-
* Extends BoxProps from Material-UI and includes additional custom properties.
|
|
16
|
-
*/
|
|
17
|
-
export interface CardProps extends BoxProps {
|
|
15
|
+
type CardProps = Omit<BoxProps, 'children'> & {
|
|
18
16
|
/** Title of the card */
|
|
19
17
|
title?: string
|
|
20
18
|
/** Whether to show an underline for the title */
|
|
@@ -124,11 +122,7 @@ export interface CardProps extends BoxProps {
|
|
|
124
122
|
columnconfig?: columnconfig
|
|
125
123
|
}
|
|
126
124
|
|
|
127
|
-
|
|
128
|
-
* Card component that renders different card variants based on the provided props.
|
|
129
|
-
* It supports various card types including default, inventory, pricing summary, product, and more.
|
|
130
|
-
*/
|
|
131
|
-
const Card: React.FC<CardProps> = ({
|
|
125
|
+
function Card({
|
|
132
126
|
title,
|
|
133
127
|
titleUnderline = true,
|
|
134
128
|
body,
|
|
@@ -154,8 +148,7 @@ const Card: React.FC<CardProps> = ({
|
|
|
154
148
|
productProps,
|
|
155
149
|
productSummaryProps,
|
|
156
150
|
...rest
|
|
157
|
-
})
|
|
158
|
-
// Render the default card variant
|
|
151
|
+
}: CardProps): JSX.Element | null {
|
|
159
152
|
if (variant === 'default') {
|
|
160
153
|
return (
|
|
161
154
|
<DefaultCard
|
|
@@ -182,7 +175,6 @@ const Card: React.FC<CardProps> = ({
|
|
|
182
175
|
)
|
|
183
176
|
}
|
|
184
177
|
|
|
185
|
-
// Render the inventory card variant
|
|
186
178
|
if (variant === 'inventory') {
|
|
187
179
|
return (
|
|
188
180
|
<InventoryCard
|
|
@@ -196,7 +188,6 @@ const Card: React.FC<CardProps> = ({
|
|
|
196
188
|
)
|
|
197
189
|
}
|
|
198
190
|
|
|
199
|
-
// Render the simple pricing summary card variant
|
|
200
191
|
if (variant === 'pricingsummary') {
|
|
201
192
|
return (
|
|
202
193
|
<SimplePricingSummary
|
|
@@ -208,7 +199,6 @@ const Card: React.FC<CardProps> = ({
|
|
|
208
199
|
)
|
|
209
200
|
}
|
|
210
201
|
|
|
211
|
-
// Render the detailed pricing summary card variant
|
|
212
202
|
if (variant === 'detailedpricingsummary') {
|
|
213
203
|
return (
|
|
214
204
|
<DetailedPricingSummary
|
|
@@ -220,14 +210,12 @@ const Card: React.FC<CardProps> = ({
|
|
|
220
210
|
)
|
|
221
211
|
}
|
|
222
212
|
|
|
223
|
-
// Render the product card variant
|
|
224
213
|
if (variant === 'product') {
|
|
225
214
|
return (
|
|
226
215
|
<ProductCard width={width} height={height} {...productProps} {...rest} />
|
|
227
216
|
)
|
|
228
217
|
}
|
|
229
218
|
|
|
230
|
-
// Render the product summary card variant
|
|
231
219
|
if (variant === 'productsummary') {
|
|
232
220
|
return (
|
|
233
221
|
<ProductSummaryCard
|
|
@@ -244,8 +232,10 @@ const Card: React.FC<CardProps> = ({
|
|
|
244
232
|
)
|
|
245
233
|
}
|
|
246
234
|
|
|
247
|
-
// Return null if no matching variant is found
|
|
248
235
|
return null
|
|
249
236
|
}
|
|
250
237
|
|
|
238
|
+
Card.displayName = 'Card'
|
|
239
|
+
|
|
251
240
|
export default Card
|
|
241
|
+
export type { CardProps }
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import SearchableDropdown, {
|
|
4
|
+
SearchableDropdownProps,
|
|
5
|
+
} from './../../../SearchableDropdown'
|
|
6
|
+
import { columnconfig, cellconfig } from '../../../Grid'
|
|
7
|
+
|
|
8
|
+
export interface ExtendedSearchableDropdownProps
|
|
9
|
+
extends SearchableDropdownProps {
|
|
10
|
+
columnconfig?: Partial<columnconfig>
|
|
11
|
+
cellconfig?: cellconfig
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const useSearchableDropdown = (grid: {
|
|
15
|
+
searchableDropdown?:
|
|
16
|
+
| ExtendedSearchableDropdownProps
|
|
17
|
+
| ExtendedSearchableDropdownProps[]
|
|
18
|
+
}) => {
|
|
19
|
+
if (!grid.searchableDropdown) return null
|
|
20
|
+
|
|
21
|
+
const renderSearchableDropdown = (
|
|
22
|
+
searchableDropdownItem: ExtendedSearchableDropdownProps,
|
|
23
|
+
index: number
|
|
24
|
+
): columnconfig => {
|
|
25
|
+
const {
|
|
26
|
+
columnconfig: itemColumnConfig,
|
|
27
|
+
cellconfig,
|
|
28
|
+
...restProps
|
|
29
|
+
} = searchableDropdownItem
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
!itemColumnConfig ||
|
|
33
|
+
typeof itemColumnConfig !== 'object' ||
|
|
34
|
+
typeof itemColumnConfig.row !== 'number' ||
|
|
35
|
+
typeof itemColumnConfig.column !== 'number'
|
|
36
|
+
) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
'columnconfig must be an object with row and column as numbers'
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const mergedConfig: columnconfig = {
|
|
43
|
+
...(itemColumnConfig as columnconfig),
|
|
44
|
+
cellconfig: {
|
|
45
|
+
...cellconfig,
|
|
46
|
+
},
|
|
47
|
+
component: (
|
|
48
|
+
<SearchableDropdown
|
|
49
|
+
key={`searchable-dropdown-${index}`}
|
|
50
|
+
{...restProps}
|
|
51
|
+
/>
|
|
52
|
+
),
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return mergedConfig
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (Array.isArray(grid.searchableDropdown)) {
|
|
59
|
+
return grid.searchableDropdown.map(renderSearchableDropdown)
|
|
60
|
+
} else {
|
|
61
|
+
return [renderSearchableDropdown(grid.searchableDropdown, 0)]
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default useSearchableDropdown
|
|
@@ -1,18 +1,15 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
1
3
|
import React from 'react'
|
|
2
|
-
import {
|
|
3
|
-
CustomStepper,
|
|
4
|
-
Step,
|
|
5
|
-
StepLabel,
|
|
6
|
-
} from './../../../../components/Stepper'
|
|
4
|
+
import { CustomStepper, CustomStepperProps } from '../../../Stepper'
|
|
7
5
|
import { columnconfig, cellconfig } from '../../../Grid'
|
|
8
|
-
import { CustomStepperProps as BaseCustomStepperProps } from './../../../../components/Stepper'
|
|
9
6
|
|
|
10
7
|
type ExtendedColumnConfig = Omit<columnconfig, 'component'> & {
|
|
11
8
|
component?: columnconfig['component']
|
|
12
9
|
}
|
|
13
10
|
|
|
14
11
|
export interface ExtendedStepperProps
|
|
15
|
-
extends Omit<
|
|
12
|
+
extends Omit<CustomStepperProps, 'columnconfig'> {
|
|
16
13
|
cellconfig?: cellconfig
|
|
17
14
|
columnconfig?: ExtendedColumnConfig
|
|
18
15
|
}
|
|
@@ -50,13 +47,7 @@ const useStepper = (grid: {
|
|
|
50
47
|
...cellconfig,
|
|
51
48
|
},
|
|
52
49
|
component: (
|
|
53
|
-
<CustomStepper key={`stepper-${index}`} steps={steps} {...restProps}
|
|
54
|
-
{steps.map(step => (
|
|
55
|
-
<Step key={step.label}>
|
|
56
|
-
<StepLabel>{step.label}</StepLabel>
|
|
57
|
-
</Step>
|
|
58
|
-
))}
|
|
59
|
-
</CustomStepper>
|
|
50
|
+
<CustomStepper key={`stepper-${index}`} steps={steps} {...restProps} />
|
|
60
51
|
),
|
|
61
52
|
}
|
|
62
53
|
return mergedConfig
|
|
@@ -66,6 +66,9 @@ import useCheckbox, {
|
|
|
66
66
|
import useComplexEditor, {
|
|
67
67
|
ExtendedComplexEditorProps,
|
|
68
68
|
} from './Structure/complexeditor/useComplexEditor'
|
|
69
|
+
import useSearchableDropdown, {
|
|
70
|
+
ExtendedSearchableDropdownProps,
|
|
71
|
+
} from './Structure/searchableDropdown/useSearchableDropdown'
|
|
69
72
|
|
|
70
73
|
/**
|
|
71
74
|
* Props for the ContentSection component.
|
|
@@ -79,6 +82,9 @@ export interface ContentSectionProps {
|
|
|
79
82
|
confirmationcodeinput?:
|
|
80
83
|
| ExtendedConfirmationCodeInputsProps
|
|
81
84
|
| ExtendedConfirmationCodeInputsProps[]
|
|
85
|
+
searchableDropdown?:
|
|
86
|
+
| ExtendedSearchableDropdownProps
|
|
87
|
+
| ExtendedSearchableDropdownProps[]
|
|
82
88
|
complexeditor?: ExtendedComplexEditorProps | ExtendedComplexEditorProps[]
|
|
83
89
|
typography?: TypographyProps | TypographyProps[]
|
|
84
90
|
radiogroup?: ExtendedRadioGroupProps | ExtendedRadioGroupProps[]
|
|
@@ -143,6 +149,7 @@ const RenderContent: React.FC<
|
|
|
143
149
|
addToColumnConfigs(useTransferList(props))
|
|
144
150
|
addToColumnConfigs(useCard(props))
|
|
145
151
|
addToColumnConfigs(useCodeCopy(props))
|
|
152
|
+
addToColumnConfigs(useSearchableDropdown(props))
|
|
146
153
|
addToColumnConfigs(useTextField(props))
|
|
147
154
|
addToColumnConfigs(useDateField(props))
|
|
148
155
|
addToColumnConfigs(useCheckbox(props))
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import React from 'react'
|
|
3
|
+
import React, { JSX } from 'react'
|
|
4
4
|
import Grid2, { Grid2Props } from '@mui/material/Grid2'
|
|
5
5
|
import { useMediaQuery, useTheme } from '@mui/material'
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
type Alignment = 'left' | 'center' | 'right' | 'inherit' | 'justify'
|
|
8
|
+
type BorderProp = 'none' | 'solid'
|
|
9
|
+
type WrapProp = 'nowrap' | 'wrap'
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
type columnconfig = {
|
|
12
12
|
row: number
|
|
13
13
|
column: number
|
|
14
14
|
gridname?: string
|
|
@@ -26,7 +26,7 @@ export interface columnconfig {
|
|
|
26
26
|
computerwidth?: string
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
type gridconfig = {
|
|
30
30
|
gridname?: string
|
|
31
31
|
alignment?: Alignment
|
|
32
32
|
margintop?: number
|
|
@@ -37,13 +37,7 @@ export interface gridconfig {
|
|
|
37
37
|
bordercolor?: string
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
gridconfig?: gridconfig
|
|
42
|
-
columnconfig?: columnconfig[]
|
|
43
|
-
cellconfig?: cellconfig
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export interface cellconfig {
|
|
40
|
+
type cellconfig = {
|
|
47
41
|
border?: BorderProp
|
|
48
42
|
minHeight?: string
|
|
49
43
|
maxHeight?: string
|
|
@@ -57,12 +51,18 @@ export interface cellconfig {
|
|
|
57
51
|
wrap?: WrapProp
|
|
58
52
|
}
|
|
59
53
|
|
|
60
|
-
|
|
54
|
+
type CustomGridProps = Omit<Grid2Props, 'children'> & {
|
|
55
|
+
gridconfig?: gridconfig
|
|
56
|
+
columnconfig?: columnconfig[]
|
|
57
|
+
cellconfig?: cellconfig
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function CustomGrid({
|
|
61
61
|
gridconfig,
|
|
62
62
|
columnconfig,
|
|
63
63
|
cellconfig,
|
|
64
64
|
...rest
|
|
65
|
-
})
|
|
65
|
+
}: CustomGridProps): JSX.Element {
|
|
66
66
|
const gridConfigValues = Array.isArray(gridconfig)
|
|
67
67
|
? gridconfig[0]
|
|
68
68
|
: gridconfig
|
|
@@ -217,4 +217,7 @@ const CustomGrid: React.FC<CustomGridProps> = ({
|
|
|
217
217
|
)
|
|
218
218
|
}
|
|
219
219
|
|
|
220
|
+
CustomGrid.displayName = 'CustomGrid'
|
|
221
|
+
|
|
220
222
|
export default CustomGrid
|
|
223
|
+
export type { CustomGridProps, columnconfig, gridconfig, cellconfig }
|
|
@@ -11,62 +11,56 @@ import {
|
|
|
11
11
|
AccordionSummary,
|
|
12
12
|
AccordionDetails,
|
|
13
13
|
List,
|
|
14
|
-
SelectChangeEvent,
|
|
15
14
|
} from '@mui/material'
|
|
16
|
-
import
|
|
17
|
-
import Searchbar from '../../Searchbar'
|
|
15
|
+
import SearchableDropdown from '../../SearchableDropdown'
|
|
18
16
|
import ExpandMoreIcon from '@mui/icons-material/ExpandMore'
|
|
19
17
|
import Link from 'next/link'
|
|
20
18
|
import { useRouter } from 'next/navigation'
|
|
21
19
|
import { NavProps, SubNav, View } from '../index'
|
|
22
|
-
import {
|
|
23
|
-
white,
|
|
24
|
-
black,
|
|
25
|
-
ocean,
|
|
26
|
-
semiTransparentWhite,
|
|
27
|
-
} from '../../../styles/palette'
|
|
20
|
+
import { white, ocean, semiTransparentWhite } from '../../../styles/palette'
|
|
28
21
|
import { Typography } from './../../Typography'
|
|
29
22
|
|
|
30
23
|
export interface VerticalVariantProps {
|
|
31
24
|
items: (NavProps | SubNav | View)[]
|
|
32
|
-
|
|
33
|
-
showDropdown: boolean
|
|
25
|
+
showSearchableNav: boolean
|
|
34
26
|
showTitle: boolean
|
|
35
27
|
showLine: boolean
|
|
36
28
|
verticalNavTitle: string
|
|
37
|
-
|
|
38
|
-
searchbarLabel: string
|
|
29
|
+
searchableNavLabel: string
|
|
39
30
|
anchor: 'left' | 'right'
|
|
40
31
|
expandedNavs: string[]
|
|
41
32
|
setExpandedNavs: React.Dispatch<React.SetStateAction<string[]>>
|
|
42
33
|
expandedSubnavs: string[]
|
|
43
34
|
setExpandedSubnavs: React.Dispatch<React.SetStateAction<string[]>>
|
|
44
35
|
verticalNavWidth: string
|
|
36
|
+
backgroundcolor?: string
|
|
37
|
+
shrunkfontcolor?: string
|
|
38
|
+
unshrunkfontcolor?: string
|
|
45
39
|
}
|
|
46
40
|
|
|
47
41
|
function VerticalVariant({
|
|
48
42
|
items,
|
|
49
|
-
|
|
50
|
-
showDropdown,
|
|
43
|
+
showSearchableNav,
|
|
51
44
|
showTitle,
|
|
52
45
|
showLine,
|
|
53
46
|
verticalNavTitle,
|
|
54
|
-
|
|
55
|
-
searchbarLabel,
|
|
47
|
+
searchableNavLabel,
|
|
56
48
|
anchor,
|
|
57
49
|
expandedNavs,
|
|
58
50
|
setExpandedNavs,
|
|
59
51
|
expandedSubnavs,
|
|
60
52
|
setExpandedSubnavs,
|
|
61
53
|
verticalNavWidth,
|
|
54
|
+
backgroundcolor,
|
|
55
|
+
shrunkfontcolor = white.main,
|
|
56
|
+
unshrunkfontcolor = white.main,
|
|
62
57
|
}: VerticalVariantProps) {
|
|
63
58
|
const router = useRouter()
|
|
64
59
|
const [selectedNav, setSelectedNav] = useState<string | null>(null)
|
|
65
|
-
const [searchValue, setSearchValue] = useState('')
|
|
66
60
|
|
|
67
61
|
const navOptions = items
|
|
68
62
|
.filter((item): item is NavProps => 'title' in item && 'subnavs' in item)
|
|
69
|
-
.map(nav => ({ value: nav.title ?? ''
|
|
63
|
+
.map(nav => ({ value: nav.title ?? '' }))
|
|
70
64
|
|
|
71
65
|
const handleNavClick = useCallback(
|
|
72
66
|
(nav: NavProps) => {
|
|
@@ -84,20 +78,12 @@ function VerticalVariant({
|
|
|
84
78
|
[router]
|
|
85
79
|
)
|
|
86
80
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
[]
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
const handleSearchChange = useCallback(
|
|
97
|
-
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
98
|
-
const newValue = e.target.value
|
|
99
|
-
setSearchValue(newValue)
|
|
100
|
-
console.log('Search value changed to:', newValue)
|
|
81
|
+
// Adjusted handler to match the SearchableDropdown onChange signature
|
|
82
|
+
const handleSearchableNavChange = useCallback(
|
|
83
|
+
(newValue: { value: string } | null) => {
|
|
84
|
+
const newVal = newValue ? newValue.value : null
|
|
85
|
+
setSelectedNav(newVal)
|
|
86
|
+
console.log('SearchableNav selection changed to:', newVal)
|
|
101
87
|
},
|
|
102
88
|
[]
|
|
103
89
|
)
|
|
@@ -379,35 +365,29 @@ function VerticalVariant({
|
|
|
379
365
|
</Link>
|
|
380
366
|
</Box>
|
|
381
367
|
)}
|
|
382
|
-
{
|
|
383
|
-
<Stack mt={
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
/>
|
|
397
|
-
</Box>
|
|
398
|
-
)}
|
|
399
|
-
{showSearchbar && (
|
|
400
|
-
<Searchbar
|
|
401
|
-
label={searchbarLabel}
|
|
402
|
-
backgroundcolor={semiTransparentWhite.main}
|
|
403
|
-
iconcolor={white.main}
|
|
368
|
+
{showSearchableNav && (
|
|
369
|
+
<Stack mt={0} spacing={0}>
|
|
370
|
+
<Box
|
|
371
|
+
sx={{
|
|
372
|
+
position: 'relative',
|
|
373
|
+
zIndex: theme => theme.zIndex.drawer + 1,
|
|
374
|
+
width: '100%',
|
|
375
|
+
minHeight: '40px',
|
|
376
|
+
}}
|
|
377
|
+
>
|
|
378
|
+
<SearchableDropdown
|
|
379
|
+
label={searchableNavLabel}
|
|
380
|
+
options={navOptions}
|
|
381
|
+
backgroundcolor={backgroundcolor || semiTransparentWhite.main}
|
|
404
382
|
outlinecolor="none"
|
|
405
383
|
fontcolor={white.main}
|
|
384
|
+
shrunkfontcolor={shrunkfontcolor}
|
|
385
|
+
unshrunkfontcolor={unshrunkfontcolor}
|
|
386
|
+
shrunklabelposition="aboveNotch"
|
|
387
|
+
onChange={handleSearchableNavChange}
|
|
406
388
|
placeholder="Search..."
|
|
407
|
-
value={searchValue}
|
|
408
|
-
onChange={handleSearchChange}
|
|
409
389
|
/>
|
|
410
|
-
|
|
390
|
+
</Box>
|
|
411
391
|
</Stack>
|
|
412
392
|
)}
|
|
413
393
|
</Box>
|
|
@@ -12,13 +12,11 @@ type Alignment = 'left' | 'center' | 'right' | 'inherit' | 'justify'
|
|
|
12
12
|
*/
|
|
13
13
|
export interface NavProps {
|
|
14
14
|
items?: (NavProps | SubNav | View)[] // Array of navigation items
|
|
15
|
-
|
|
16
|
-
showDropdown?: boolean // Flag to show/hide dropdown
|
|
15
|
+
showSearchableNav?: boolean // Flag to show/hide searchable navigation
|
|
17
16
|
showTitle?: boolean // Flag to show/hide title
|
|
18
17
|
showLine?: boolean // Flag to show/hide divider line
|
|
19
18
|
verticalNavTitle?: string // Title for vertical navigation
|
|
20
|
-
|
|
21
|
-
searchbarLabel?: string // Label for search bar
|
|
19
|
+
searchableNavLabel?: string // Label for searchable navigation
|
|
22
20
|
anchor?: 'left' | 'right' // Position of vertical navigation
|
|
23
21
|
orientation?: 'vertical' | 'horizontal' // Orientation of navigation
|
|
24
22
|
height?: string // Height of navigation (for horizontal)
|
|
@@ -31,6 +29,9 @@ export interface NavProps {
|
|
|
31
29
|
hasleftborder?: string // Flag for left border
|
|
32
30
|
hasrightborder?: string // Flag for right border
|
|
33
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
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
/**
|
|
@@ -61,23 +62,24 @@ export type View = {
|
|
|
61
62
|
*/
|
|
62
63
|
function Nav({
|
|
63
64
|
items = [],
|
|
64
|
-
|
|
65
|
-
showDropdown = true,
|
|
65
|
+
showSearchableNav = true, // Combined showSearchbar and showDropdown
|
|
66
66
|
showTitle = true,
|
|
67
67
|
showLine = true,
|
|
68
68
|
verticalNavTitle = 'Navigation',
|
|
69
|
-
|
|
70
|
-
searchbarLabel = 'Search your navs',
|
|
69
|
+
searchableNavLabel = 'Search or select a nav', // Combined label
|
|
71
70
|
anchor = 'left',
|
|
72
71
|
orientation,
|
|
73
72
|
height = '80px',
|
|
74
73
|
alignment = 'left',
|
|
75
74
|
navname,
|
|
75
|
+
shrunkfontcolor = 'black',
|
|
76
|
+
unshrunkfontcolor = 'black',
|
|
77
|
+
backgroundcolor,
|
|
76
78
|
}: NavProps): JSX.Element {
|
|
77
79
|
// State for expanded navigation items
|
|
78
80
|
const [expandedNavs, setExpandedNavs] = useState<string[]>([])
|
|
79
81
|
const [expandedSubnavs, setExpandedSubnavs] = useState<string[]>([])
|
|
80
|
-
const [verticalNavWidth] = useState<number>(250) // Default width
|
|
82
|
+
const [verticalNavWidth] = useState<number>(250) // Default width
|
|
81
83
|
|
|
82
84
|
// Memoized navigation items
|
|
83
85
|
const navs = useMemo(() => {
|
|
@@ -101,19 +103,20 @@ function Nav({
|
|
|
101
103
|
return (
|
|
102
104
|
<VerticalVariant
|
|
103
105
|
items={navs}
|
|
104
|
-
|
|
105
|
-
showDropdown={showDropdown}
|
|
106
|
+
showSearchableNav={showSearchableNav}
|
|
106
107
|
showTitle={showTitle}
|
|
107
108
|
showLine={showLine}
|
|
108
109
|
verticalNavTitle={verticalNavTitle}
|
|
109
|
-
|
|
110
|
-
searchbarLabel={searchbarLabel}
|
|
110
|
+
searchableNavLabel={searchableNavLabel}
|
|
111
111
|
anchor={anchor}
|
|
112
112
|
expandedNavs={expandedNavs}
|
|
113
113
|
setExpandedNavs={setExpandedNavs}
|
|
114
114
|
expandedSubnavs={expandedSubnavs}
|
|
115
115
|
setExpandedSubnavs={setExpandedSubnavs}
|
|
116
116
|
verticalNavWidth={`${verticalNavWidth}px`}
|
|
117
|
+
shrunkfontcolor={shrunkfontcolor}
|
|
118
|
+
unshrunkfontcolor={unshrunkfontcolor}
|
|
119
|
+
backgroundcolor={backgroundcolor} // Pass backgroundcolor to VerticalVariant
|
|
117
120
|
/>
|
|
118
121
|
)
|
|
119
122
|
} else {
|
|
@@ -0,0 +1,378 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect, SyntheticEvent } from 'react'
|
|
4
|
+
import {
|
|
5
|
+
Autocomplete,
|
|
6
|
+
Box,
|
|
7
|
+
CircularProgress,
|
|
8
|
+
FilterOptionsState,
|
|
9
|
+
AutocompleteChangeReason,
|
|
10
|
+
AutocompleteChangeDetails,
|
|
11
|
+
} from '@mui/material'
|
|
12
|
+
import { styled } from '@mui/material/styles'
|
|
13
|
+
import { black, white } from '../../styles/palette'
|
|
14
|
+
import Typography from '../Typography'
|
|
15
|
+
import TextField from '../TextField'
|
|
16
|
+
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown'
|
|
17
|
+
|
|
18
|
+
interface DropdownOption {
|
|
19
|
+
value: string
|
|
20
|
+
attribute1?: string
|
|
21
|
+
attribute2?: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface SearchableDropdownProps {
|
|
25
|
+
label: string
|
|
26
|
+
options: DropdownOption[]
|
|
27
|
+
defaultValue?: string
|
|
28
|
+
backgroundcolor?: string
|
|
29
|
+
outlinecolor?: string
|
|
30
|
+
fontcolor?: string
|
|
31
|
+
inputfontcolor?: string // New prop for input text color
|
|
32
|
+
shrunkfontcolor?: string
|
|
33
|
+
unshrunkfontcolor?: string
|
|
34
|
+
placeholdercolor?: string
|
|
35
|
+
shrunklabelposition?: 'onNotch' | 'aboveNotch'
|
|
36
|
+
onChange?: (value: DropdownOption | null) => void
|
|
37
|
+
error?: boolean
|
|
38
|
+
helperText?: string
|
|
39
|
+
name?: string
|
|
40
|
+
required?: boolean
|
|
41
|
+
placeholder?: string
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const StyledBox = styled(Box)(() => ({
|
|
45
|
+
position: 'relative',
|
|
46
|
+
width: '100%',
|
|
47
|
+
marginTop: '10px',
|
|
48
|
+
}))
|
|
49
|
+
|
|
50
|
+
const StyledAutocomplete = styled(
|
|
51
|
+
Autocomplete<DropdownOption, false, false, true>
|
|
52
|
+
)<{
|
|
53
|
+
backgroundcolor?: string
|
|
54
|
+
outlinecolor?: string
|
|
55
|
+
fontcolor?: string
|
|
56
|
+
inputfontcolor?: string
|
|
57
|
+
shrunkfontcolor?: string
|
|
58
|
+
shrunklabelposition?: 'onNotch' | 'aboveNotch'
|
|
59
|
+
}>(
|
|
60
|
+
({
|
|
61
|
+
outlinecolor,
|
|
62
|
+
fontcolor,
|
|
63
|
+
inputfontcolor,
|
|
64
|
+
shrunkfontcolor,
|
|
65
|
+
shrunklabelposition,
|
|
66
|
+
}) => ({
|
|
67
|
+
'& .MuiOutlinedInput-root': {
|
|
68
|
+
overflow: 'visible',
|
|
69
|
+
minHeight: '40px',
|
|
70
|
+
'& fieldset': {
|
|
71
|
+
borderColor: outlinecolor || black.main,
|
|
72
|
+
...(shrunklabelposition === 'aboveNotch' && {
|
|
73
|
+
legend: {
|
|
74
|
+
width: '0px !important',
|
|
75
|
+
},
|
|
76
|
+
}),
|
|
77
|
+
},
|
|
78
|
+
'&:hover fieldset': {
|
|
79
|
+
borderColor: outlinecolor || black.main,
|
|
80
|
+
},
|
|
81
|
+
'&.Mui-focused fieldset': {
|
|
82
|
+
borderColor: outlinecolor || black.main,
|
|
83
|
+
...(shrunklabelposition === 'aboveNotch' && {
|
|
84
|
+
legend: {
|
|
85
|
+
width: '0px !important',
|
|
86
|
+
},
|
|
87
|
+
}),
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
'& .MuiAutocomplete-input': {
|
|
91
|
+
color: inputfontcolor || fontcolor || black.main,
|
|
92
|
+
height: 'auto',
|
|
93
|
+
paddingTop: '10px',
|
|
94
|
+
paddingBottom: '10px',
|
|
95
|
+
},
|
|
96
|
+
'& .MuiFormLabel-root': {
|
|
97
|
+
zIndex: 1,
|
|
98
|
+
'&.MuiInputLabel-shrink, &.Mui-focused': {
|
|
99
|
+
color: `${shrunkfontcolor} !important`,
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
})
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
const SearchableDropdown: React.FC<SearchableDropdownProps> = ({
|
|
106
|
+
label,
|
|
107
|
+
options,
|
|
108
|
+
defaultValue,
|
|
109
|
+
backgroundcolor,
|
|
110
|
+
outlinecolor,
|
|
111
|
+
fontcolor = black.main,
|
|
112
|
+
inputfontcolor = black.main,
|
|
113
|
+
shrunkfontcolor = black.main,
|
|
114
|
+
unshrunkfontcolor = black.main,
|
|
115
|
+
placeholdercolor = black.main,
|
|
116
|
+
shrunklabelposition = 'onNotch',
|
|
117
|
+
onChange,
|
|
118
|
+
error = false,
|
|
119
|
+
helperText,
|
|
120
|
+
name,
|
|
121
|
+
required = false,
|
|
122
|
+
placeholder,
|
|
123
|
+
}) => {
|
|
124
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
125
|
+
const [value, setValue] = useState<DropdownOption | string | null>(null)
|
|
126
|
+
const [inputValue, setInputValue] = useState('')
|
|
127
|
+
const [isFocused, setIsFocused] = useState(false)
|
|
128
|
+
|
|
129
|
+
useEffect(() => {
|
|
130
|
+
const defaultOption = options.find(option => option.value === defaultValue)
|
|
131
|
+
if (defaultOption) {
|
|
132
|
+
const displayText =
|
|
133
|
+
defaultOption.value.replace(/_/g, ' ').charAt(0).toUpperCase() +
|
|
134
|
+
defaultOption.value.replace(/_/g, ' ').slice(1)
|
|
135
|
+
setValue(defaultOption)
|
|
136
|
+
setInputValue(displayText)
|
|
137
|
+
}
|
|
138
|
+
setIsLoading(false)
|
|
139
|
+
}, [defaultValue, options])
|
|
140
|
+
|
|
141
|
+
const handleChange = (
|
|
142
|
+
event: SyntheticEvent<Element, Event>,
|
|
143
|
+
newValue: DropdownOption | string | null,
|
|
144
|
+
_reason: AutocompleteChangeReason,
|
|
145
|
+
_details?: AutocompleteChangeDetails<DropdownOption>
|
|
146
|
+
) => {
|
|
147
|
+
// Use _reason and _details to avoid unused variable errors
|
|
148
|
+
console.debug(_reason, _details)
|
|
149
|
+
|
|
150
|
+
if (typeof newValue === 'string') {
|
|
151
|
+
setValue(newValue)
|
|
152
|
+
setInputValue(newValue)
|
|
153
|
+
if (onChange) {
|
|
154
|
+
onChange(null)
|
|
155
|
+
}
|
|
156
|
+
} else {
|
|
157
|
+
setValue(newValue)
|
|
158
|
+
if (newValue) {
|
|
159
|
+
const displayText =
|
|
160
|
+
newValue.value.replace(/_/g, ' ').charAt(0).toUpperCase() +
|
|
161
|
+
newValue.value.replace(/_/g, ' ').slice(1)
|
|
162
|
+
setInputValue(displayText)
|
|
163
|
+
if (onChange) {
|
|
164
|
+
onChange(newValue)
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
setInputValue('')
|
|
168
|
+
if (onChange) {
|
|
169
|
+
onChange(null)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const handleFocus = () => {
|
|
176
|
+
setIsFocused(true)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const handleBlur = () => {
|
|
180
|
+
if (!value && !inputValue) {
|
|
181
|
+
setIsFocused(false)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (isLoading) {
|
|
186
|
+
return (
|
|
187
|
+
<Box
|
|
188
|
+
display="flex"
|
|
189
|
+
justifyContent="center"
|
|
190
|
+
alignItems="center"
|
|
191
|
+
height="50px"
|
|
192
|
+
>
|
|
193
|
+
<CircularProgress size={24} />
|
|
194
|
+
</Box>
|
|
195
|
+
)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const labelTransformNotFocused = 'translate(12px, 10px) scale(1) !important'
|
|
199
|
+
const labelTransformShrunkAboveNotch =
|
|
200
|
+
'translate(0px, -17px) scale(0.75) !important'
|
|
201
|
+
const labelTransformShrunkOnNotch =
|
|
202
|
+
'translate(13px, -4px) scale(0.75) !important'
|
|
203
|
+
|
|
204
|
+
const focusedTransform =
|
|
205
|
+
shrunklabelposition === 'aboveNotch'
|
|
206
|
+
? labelTransformShrunkAboveNotch
|
|
207
|
+
: labelTransformShrunkOnNotch
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<StyledBox>
|
|
211
|
+
<StyledAutocomplete
|
|
212
|
+
id={name}
|
|
213
|
+
options={options}
|
|
214
|
+
freeSolo
|
|
215
|
+
value={value}
|
|
216
|
+
onChange={handleChange}
|
|
217
|
+
inputValue={inputValue}
|
|
218
|
+
onInputChange={(_e, newInputValue) => {
|
|
219
|
+
setInputValue(newInputValue)
|
|
220
|
+
}}
|
|
221
|
+
onFocus={handleFocus}
|
|
222
|
+
onBlur={handleBlur}
|
|
223
|
+
popupIcon={null}
|
|
224
|
+
disablePortal
|
|
225
|
+
backgroundcolor={backgroundcolor}
|
|
226
|
+
outlinecolor={outlinecolor}
|
|
227
|
+
fontcolor={fontcolor}
|
|
228
|
+
inputfontcolor={inputfontcolor}
|
|
229
|
+
shrunkfontcolor={shrunkfontcolor}
|
|
230
|
+
shrunklabelposition={shrunklabelposition}
|
|
231
|
+
filterOptions={(
|
|
232
|
+
opts: DropdownOption[],
|
|
233
|
+
state: FilterOptionsState<DropdownOption>
|
|
234
|
+
) => {
|
|
235
|
+
const input = state.inputValue.toLowerCase()
|
|
236
|
+
return opts.filter(o => o.value.toLowerCase().includes(input))
|
|
237
|
+
}}
|
|
238
|
+
// @ts-ignore - MUI freeSolo mode and TS defs conflict; runtime is fine
|
|
239
|
+
getOptionLabel={(option: DropdownOption | string) => {
|
|
240
|
+
if (typeof option === 'string') {
|
|
241
|
+
return option
|
|
242
|
+
}
|
|
243
|
+
return (
|
|
244
|
+
option.value.replace(/_/g, ' ').charAt(0).toUpperCase() +
|
|
245
|
+
option.value.replace(/_/g, ' ').slice(1)
|
|
246
|
+
)
|
|
247
|
+
}}
|
|
248
|
+
renderOption={(liProps, option) => {
|
|
249
|
+
// Extract the key and remove it from liProps, then apply it separately
|
|
250
|
+
const { key, ...otherLiProps } = liProps
|
|
251
|
+
const opt = option as DropdownOption
|
|
252
|
+
return (
|
|
253
|
+
<li key={key} {...otherLiProps} style={{ color: black.main }}>
|
|
254
|
+
<Typography
|
|
255
|
+
fontvariant="merriparagraph"
|
|
256
|
+
text={opt.value.replace(/_/g, ' ')}
|
|
257
|
+
fontcolor={black.main}
|
|
258
|
+
/>
|
|
259
|
+
{opt.attribute1 && (
|
|
260
|
+
<Typography
|
|
261
|
+
fontvariant="merriparagraph"
|
|
262
|
+
text={`${opt.attribute1}${
|
|
263
|
+
opt.attribute2 ? ` | ${opt.attribute2}` : ''
|
|
264
|
+
}`}
|
|
265
|
+
fontcolor={black.main}
|
|
266
|
+
/>
|
|
267
|
+
)}
|
|
268
|
+
</li>
|
|
269
|
+
)
|
|
270
|
+
}}
|
|
271
|
+
renderInput={params => (
|
|
272
|
+
<TextField
|
|
273
|
+
{...params}
|
|
274
|
+
required={required}
|
|
275
|
+
error={error}
|
|
276
|
+
helperText={helperText}
|
|
277
|
+
label={label}
|
|
278
|
+
placeholder={placeholder}
|
|
279
|
+
onFocus={handleFocus}
|
|
280
|
+
onBlur={handleBlur}
|
|
281
|
+
backgroundcolor={backgroundcolor}
|
|
282
|
+
endAdornment={<ArrowDropDownIcon sx={{ color: black.main }} />}
|
|
283
|
+
slotProps={{
|
|
284
|
+
inputLabel: {
|
|
285
|
+
shrink: isFocused || !!value || !!inputValue,
|
|
286
|
+
sx: {
|
|
287
|
+
color: isFocused ? shrunkfontcolor : unshrunkfontcolor,
|
|
288
|
+
transform: isFocused
|
|
289
|
+
? focusedTransform
|
|
290
|
+
: labelTransformNotFocused,
|
|
291
|
+
pointerEvents: 'none',
|
|
292
|
+
'&.MuiInputLabel-shrink': {
|
|
293
|
+
transform: focusedTransform,
|
|
294
|
+
color: shrunkfontcolor,
|
|
295
|
+
},
|
|
296
|
+
zIndex: 1,
|
|
297
|
+
overflow: 'visible',
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
input: {
|
|
301
|
+
...params.InputProps,
|
|
302
|
+
notched:
|
|
303
|
+
shrunklabelposition === 'onNotch' &&
|
|
304
|
+
(isFocused || !!value || !!inputValue),
|
|
305
|
+
},
|
|
306
|
+
}}
|
|
307
|
+
sx={{
|
|
308
|
+
overflow: 'visible',
|
|
309
|
+
'& .MuiOutlinedInput-root': {
|
|
310
|
+
backgroundColor: backgroundcolor || white.main,
|
|
311
|
+
color: fontcolor,
|
|
312
|
+
minHeight: '40px',
|
|
313
|
+
overflow: 'visible',
|
|
314
|
+
'& fieldset': {
|
|
315
|
+
borderColor: outlinecolor || black.main,
|
|
316
|
+
overflow: 'visible',
|
|
317
|
+
...(shrunklabelposition === 'aboveNotch' && {
|
|
318
|
+
legend: {
|
|
319
|
+
width: '0px !important',
|
|
320
|
+
},
|
|
321
|
+
}),
|
|
322
|
+
},
|
|
323
|
+
'&:hover fieldset': {
|
|
324
|
+
borderColor: outlinecolor || black.main,
|
|
325
|
+
},
|
|
326
|
+
'&.Mui-focused fieldset': {
|
|
327
|
+
borderColor: outlinecolor || black.main,
|
|
328
|
+
},
|
|
329
|
+
'& input': {
|
|
330
|
+
backgroundColor: backgroundcolor || white.main,
|
|
331
|
+
color: inputfontcolor,
|
|
332
|
+
'&::placeholder': {
|
|
333
|
+
color: placeholdercolor,
|
|
334
|
+
opacity: 1,
|
|
335
|
+
},
|
|
336
|
+
paddingTop: '16px',
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
'& .MuiInputLabel-root': {
|
|
340
|
+
color: unshrunkfontcolor,
|
|
341
|
+
overflow: 'visible',
|
|
342
|
+
zIndex: 1,
|
|
343
|
+
},
|
|
344
|
+
'& .MuiAutocomplete-clearIndicator': {
|
|
345
|
+
color: fontcolor,
|
|
346
|
+
},
|
|
347
|
+
'& .MuiAutocomplete-input': {
|
|
348
|
+
color: inputfontcolor,
|
|
349
|
+
height: 'auto',
|
|
350
|
+
paddingTop: '16px',
|
|
351
|
+
paddingBottom: '10px',
|
|
352
|
+
backgroundColor: backgroundcolor || white.main,
|
|
353
|
+
'&::placeholder': {
|
|
354
|
+
color: placeholdercolor,
|
|
355
|
+
opacity: 1,
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
}}
|
|
359
|
+
/>
|
|
360
|
+
)}
|
|
361
|
+
sx={{
|
|
362
|
+
'& .MuiPaper-root': {
|
|
363
|
+
backgroundColor: backgroundcolor || white.main,
|
|
364
|
+
color: black.main,
|
|
365
|
+
},
|
|
366
|
+
'& .MuiAutocomplete-option': {
|
|
367
|
+
color: black.main,
|
|
368
|
+
},
|
|
369
|
+
'& .MuiAutocomplete-option[aria-selected="true"]': {
|
|
370
|
+
backgroundColor: `${black.main}08`,
|
|
371
|
+
},
|
|
372
|
+
}}
|
|
373
|
+
/>
|
|
374
|
+
</StyledBox>
|
|
375
|
+
)
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
export default SearchableDropdown
|
|
@@ -1,13 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import React, { JSX } from 'react'
|
|
2
4
|
import { styled } from '@mui/material/styles'
|
|
3
5
|
import MuiStepper, { StepperProps } from '@mui/material/Stepper'
|
|
4
6
|
import MuiStep, { StepProps } from '@mui/material/Step'
|
|
5
7
|
import MuiStepLabel, { StepLabelProps } from '@mui/material/StepLabel'
|
|
6
8
|
import CustomButton from '../Button'
|
|
7
9
|
import StepConnector from '@mui/material/StepConnector'
|
|
8
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
Check,
|
|
12
|
+
CircleOutlined,
|
|
13
|
+
LockOutlined,
|
|
14
|
+
InfoOutlined,
|
|
15
|
+
} from '@mui/icons-material'
|
|
16
|
+
import {
|
|
17
|
+
Box,
|
|
18
|
+
Tooltip,
|
|
19
|
+
IconButton,
|
|
20
|
+
tooltipClasses,
|
|
21
|
+
TooltipProps,
|
|
22
|
+
} from '@mui/material'
|
|
9
23
|
|
|
10
|
-
// Styled components for Stepper, Step, and StepLabel
|
|
11
24
|
const StyledStepper = styled((props: StepperProps) => (
|
|
12
25
|
<MuiStepper {...props} />
|
|
13
26
|
))({})
|
|
@@ -17,81 +30,74 @@ const Step = styled((props: StepProps) => <MuiStep {...props} />)({})
|
|
|
17
30
|
const StepLabel = styled((props: StepLabelProps) => (
|
|
18
31
|
<MuiStepLabel
|
|
19
32
|
{...props}
|
|
20
|
-
|
|
33
|
+
slots={{ stepIcon: props.slots?.stepIcon }}
|
|
21
34
|
sx={{
|
|
22
35
|
'.MuiStepLabel-labelContainer': {
|
|
23
36
|
display: 'flex',
|
|
24
37
|
alignItems: 'center',
|
|
38
|
+
width: '100%',
|
|
25
39
|
'.MuiStepLabel-label': {
|
|
26
40
|
marginLeft: '15px',
|
|
41
|
+
flex: 1,
|
|
27
42
|
},
|
|
28
43
|
},
|
|
29
44
|
}}
|
|
30
45
|
/>
|
|
31
46
|
))({})
|
|
32
47
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
48
|
+
const BlackTooltip = styled(({ className, ...props }: TooltipProps) => (
|
|
49
|
+
<Tooltip {...props} classes={{ popper: className }} />
|
|
50
|
+
))({
|
|
51
|
+
[`& .${tooltipClasses.tooltip}`]: {
|
|
52
|
+
backgroundColor: 'black',
|
|
53
|
+
color: 'white',
|
|
54
|
+
fontSize: 14,
|
|
55
|
+
},
|
|
56
|
+
[`& .${tooltipClasses.arrow}`]: {
|
|
57
|
+
color: 'black',
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
type CustomStepperProps = Omit<StepperProps, 'children'> & {
|
|
37
62
|
steps: {
|
|
38
63
|
stepNumber: number
|
|
39
64
|
label: string
|
|
40
65
|
stepLink: string
|
|
41
66
|
status: 'completed' | 'active' | 'error' | 'inactive'
|
|
42
67
|
statusLink?: string
|
|
68
|
+
description?: string
|
|
43
69
|
}[]
|
|
44
70
|
activeStep: number
|
|
45
71
|
}
|
|
46
72
|
|
|
47
|
-
|
|
48
|
-
* CustomStepper component renders a customized stepper with clickable steps
|
|
49
|
-
* @param {CustomStepperProps} props - The props for the CustomStepper component
|
|
50
|
-
* @returns {JSX.Element} The rendered CustomStepper component
|
|
51
|
-
*/
|
|
52
|
-
const CustomStepper: React.FC<CustomStepperProps> = ({
|
|
73
|
+
function CustomStepper({
|
|
53
74
|
steps,
|
|
54
75
|
activeStep,
|
|
55
76
|
...rest
|
|
56
|
-
})
|
|
57
|
-
/**
|
|
58
|
-
* Get the appropriate icon for each step based on its status
|
|
59
|
-
* @param {string} status - The status of the step
|
|
60
|
-
* @returns {JSX.Element} The icon component for the step
|
|
61
|
-
*/
|
|
77
|
+
}: CustomStepperProps): JSX.Element {
|
|
62
78
|
const getStepIcon = (
|
|
63
79
|
status: 'completed' | 'active' | 'error' | 'inactive'
|
|
64
|
-
) => {
|
|
80
|
+
): JSX.Element => {
|
|
65
81
|
switch (status) {
|
|
66
82
|
case 'completed':
|
|
67
|
-
return <Check />
|
|
83
|
+
return <Check sx={{ color: 'black' }} />
|
|
68
84
|
case 'error':
|
|
69
|
-
return <CircleOutlined
|
|
85
|
+
return <CircleOutlined sx={{ color: 'black' }} />
|
|
70
86
|
case 'inactive':
|
|
71
|
-
return <LockOutlined />
|
|
87
|
+
return <LockOutlined sx={{ color: 'black' }} />
|
|
72
88
|
default:
|
|
73
|
-
return <CircleOutlined />
|
|
89
|
+
return <CircleOutlined sx={{ color: 'black' }} />
|
|
74
90
|
}
|
|
75
91
|
}
|
|
76
92
|
|
|
77
|
-
|
|
78
|
-
* Get the appropriate link for each step
|
|
79
|
-
* @param {Object} step - The step object
|
|
80
|
-
* @returns {string} The link for the step
|
|
81
|
-
*/
|
|
82
|
-
const getStepLink = (step: CustomStepperProps['steps'][0]) => {
|
|
93
|
+
const getStepLink = (step: CustomStepperProps['steps'][0]): string => {
|
|
83
94
|
if (step.statusLink) {
|
|
84
95
|
return step.statusLink
|
|
85
96
|
}
|
|
86
97
|
return step.stepLink
|
|
87
98
|
}
|
|
88
99
|
|
|
89
|
-
|
|
90
|
-
* Determine if a step is clickable
|
|
91
|
-
* @param {Object} step - The step object
|
|
92
|
-
* @returns {boolean} Whether the step is clickable
|
|
93
|
-
*/
|
|
94
|
-
const isStepClickable = (step: CustomStepperProps['steps'][0]) => {
|
|
100
|
+
const isStepClickable = (step: CustomStepperProps['steps'][0]): boolean => {
|
|
95
101
|
return step.status !== 'inactive'
|
|
96
102
|
}
|
|
97
103
|
|
|
@@ -99,23 +105,49 @@ const CustomStepper: React.FC<CustomStepperProps> = ({
|
|
|
99
105
|
<StyledStepper {...rest} connector={<StepConnector />}>
|
|
100
106
|
{steps.map(step => (
|
|
101
107
|
<Step key={step.label} active={step.stepNumber === activeStep}>
|
|
102
|
-
<StepLabel
|
|
103
|
-
<
|
|
104
|
-
text={step.label}
|
|
105
|
-
variant="text"
|
|
106
|
-
fontcolor="black"
|
|
107
|
-
fontlocation="left"
|
|
108
|
-
href={isStepClickable(step) ? getStepLink(step) : undefined}
|
|
108
|
+
<StepLabel slots={{ stepIcon: () => getStepIcon(step.status) }}>
|
|
109
|
+
<Box
|
|
109
110
|
sx={{
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
'&:hover': {
|
|
115
|
-
backgroundColor: 'transparent',
|
|
116
|
-
},
|
|
111
|
+
display: 'flex',
|
|
112
|
+
alignItems: 'center',
|
|
113
|
+
width: '100%',
|
|
114
|
+
gap: 1,
|
|
117
115
|
}}
|
|
118
|
-
|
|
116
|
+
>
|
|
117
|
+
<CustomButton
|
|
118
|
+
text={step.label}
|
|
119
|
+
variant="text"
|
|
120
|
+
fontcolor="black"
|
|
121
|
+
fontlocation="left"
|
|
122
|
+
href={isStepClickable(step) ? getStepLink(step) : undefined}
|
|
123
|
+
sx={{
|
|
124
|
+
padding: 0,
|
|
125
|
+
minWidth: 0,
|
|
126
|
+
flex: 1,
|
|
127
|
+
justifyContent: 'flex-start',
|
|
128
|
+
textTransform: 'none',
|
|
129
|
+
'&:hover': {
|
|
130
|
+
backgroundColor: 'transparent',
|
|
131
|
+
},
|
|
132
|
+
}}
|
|
133
|
+
/>
|
|
134
|
+
{step.description && (
|
|
135
|
+
<BlackTooltip title={step.description} arrow placement="right">
|
|
136
|
+
<IconButton
|
|
137
|
+
size="small"
|
|
138
|
+
sx={{
|
|
139
|
+
padding: 0,
|
|
140
|
+
color: 'black',
|
|
141
|
+
'&:hover': {
|
|
142
|
+
backgroundColor: 'transparent',
|
|
143
|
+
},
|
|
144
|
+
}}
|
|
145
|
+
>
|
|
146
|
+
<InfoOutlined fontSize="small" />
|
|
147
|
+
</IconButton>
|
|
148
|
+
</BlackTooltip>
|
|
149
|
+
)}
|
|
150
|
+
</Box>
|
|
119
151
|
</StepLabel>
|
|
120
152
|
</Step>
|
|
121
153
|
))}
|
|
@@ -123,4 +155,7 @@ const CustomStepper: React.FC<CustomStepperProps> = ({
|
|
|
123
155
|
)
|
|
124
156
|
}
|
|
125
157
|
|
|
126
|
-
|
|
158
|
+
CustomStepper.displayName = 'CustomStepper'
|
|
159
|
+
|
|
160
|
+
export { CustomStepper }
|
|
161
|
+
export type { CustomStepperProps }
|
|
@@ -30,6 +30,7 @@ export type TextFieldProps = (
|
|
|
30
30
|
top?: number
|
|
31
31
|
left?: number
|
|
32
32
|
}
|
|
33
|
+
backgroundcolor?: string
|
|
33
34
|
sx?: MuiTextFieldProps['sx']
|
|
34
35
|
slotProps?: MuiTextFieldProps['slotProps']
|
|
35
36
|
}
|
|
@@ -79,18 +80,19 @@ const TextField = React.memo<TextFieldProps>(props => {
|
|
|
79
80
|
endAdornment,
|
|
80
81
|
textAlign = 'left',
|
|
81
82
|
slotProps: customSlotProps = {},
|
|
83
|
+
backgroundcolor,
|
|
82
84
|
...restProps
|
|
83
85
|
} = props
|
|
84
86
|
|
|
85
87
|
const inputStyle = useMemo<React.CSSProperties>(
|
|
86
88
|
() => ({
|
|
87
|
-
backgroundColor: 'inherit',
|
|
89
|
+
backgroundColor: backgroundcolor || 'inherit',
|
|
88
90
|
width: '100%',
|
|
89
91
|
cursor: 'text',
|
|
90
92
|
boxSizing: 'border-box',
|
|
91
93
|
borderRadius: 5,
|
|
92
94
|
}),
|
|
93
|
-
[]
|
|
95
|
+
[backgroundcolor]
|
|
94
96
|
)
|
|
95
97
|
|
|
96
98
|
const handleChange = useCallback(
|
package/src/index.ts
CHANGED
|
@@ -3,8 +3,6 @@ import React from 'react'
|
|
|
3
3
|
// Components
|
|
4
4
|
import CustomButton from './components/Button'
|
|
5
5
|
import CustomGrid, {
|
|
6
|
-
Alignment,
|
|
7
|
-
BorderProp,
|
|
8
6
|
columnconfig,
|
|
9
7
|
gridconfig,
|
|
10
8
|
cellconfig,
|
|
@@ -37,6 +35,9 @@ import CustomToolbar, { ToolbarProps } from './components/Toolbar'
|
|
|
37
35
|
import TransferList, { TransferListProps } from './components/TransferList'
|
|
38
36
|
import StyledTooltip, { CustomTooltipProps } from './components/Tooltip'
|
|
39
37
|
import QRCodeComponent, { QRCodeProps } from './components/QRCode'
|
|
38
|
+
import SearchableDropdown, {
|
|
39
|
+
SearchableDropdownProps,
|
|
40
|
+
} from './components/SearchableDropdown'
|
|
40
41
|
|
|
41
42
|
// New imports
|
|
42
43
|
import DateField from './components/DateField'
|
|
@@ -208,6 +209,7 @@ export { TransferList }
|
|
|
208
209
|
export { StyledTooltip }
|
|
209
210
|
export { formContainerStyle }
|
|
210
211
|
export { QRCodeComponent }
|
|
212
|
+
export { SearchableDropdown }
|
|
211
213
|
|
|
212
214
|
// New named exports
|
|
213
215
|
export { DateField }
|
|
@@ -227,6 +229,7 @@ export type { FormDataGridProps }
|
|
|
227
229
|
export type { DatagridProps }
|
|
228
230
|
export type { ColumnDef, RowData }
|
|
229
231
|
export type { CellParams, HeaderParams }
|
|
232
|
+
export type { SearchableDropdownProps }
|
|
230
233
|
|
|
231
234
|
// Exporting ExtendedButtonProps
|
|
232
235
|
export type { ExtendedButtonProps }
|
|
@@ -250,7 +253,7 @@ export type { ExtendedComplexEditorProps }
|
|
|
250
253
|
// Type exports
|
|
251
254
|
export type { CustomButtonProps }
|
|
252
255
|
export type { CustomGridProps }
|
|
253
|
-
export type {
|
|
256
|
+
export type { columnconfig, gridconfig, cellconfig }
|
|
254
257
|
export type { FontFamily, TypographyVariant, TypographyProps }
|
|
255
258
|
export type { ConfirmationCodeInputsProps }
|
|
256
259
|
export type { RadioOption, RadioGroupProps }
|