goobs-frontend 0.8.5 → 0.8.7
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 +19 -18
- package/src/components/Button/index.tsx +0 -15
- package/src/components/Content/Structure/phoneNumber/usePhoneNumber.tsx +3 -4
- package/src/components/Content/Structure/textfield/useTextField.tsx +5 -29
- package/src/components/Dropdown/index.tsx +144 -47
- package/src/components/Nav/HorizontalVariant/index.tsx +15 -56
- package/src/components/PhoneNumberField/index.tsx +91 -67
- package/src/components/PricingTable/index.tsx +60 -4
- package/src/components/TextField/index.tsx +21 -32
- package/src/index.ts +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goobs-frontend",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
4
|
+
"type": "module",
|
|
4
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.",
|
|
5
6
|
"license": "MIT",
|
|
6
7
|
"main": "./src/index.ts",
|
|
@@ -18,35 +19,35 @@
|
|
|
18
19
|
"lint": "next lint"
|
|
19
20
|
},
|
|
20
21
|
"dependencies": {
|
|
21
|
-
"@emotion/cache": "^11.13.
|
|
22
|
-
"@emotion/react": "^11.13.
|
|
23
|
-
"@emotion/styled": "^11.13.
|
|
24
|
-
"@mui/icons-material": "^6.1.
|
|
25
|
-
"@mui/material": "^6.1.
|
|
26
|
-
"@types/lodash": "^4.17.
|
|
22
|
+
"@emotion/cache": "^11.13.5",
|
|
23
|
+
"@emotion/react": "^11.13.5",
|
|
24
|
+
"@emotion/styled": "^11.13.5",
|
|
25
|
+
"@mui/icons-material": "^6.1.8",
|
|
26
|
+
"@mui/material": "^6.1.8",
|
|
27
|
+
"@types/lodash": "^4.17.13",
|
|
27
28
|
"highlight.js": "^11.10.0",
|
|
28
|
-
"jotai": "^2.10.
|
|
29
|
+
"jotai": "^2.10.3",
|
|
29
30
|
"lodash": "^4.17.21",
|
|
30
|
-
"next": "
|
|
31
|
+
"next": "15.0.3",
|
|
31
32
|
"otplib": "^12.0.1",
|
|
32
33
|
"react-datepicker": "^7.5.0",
|
|
33
34
|
"react-qr-code": "^2.0.15"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"@next/eslint-plugin-next": "^
|
|
37
|
-
"@types/node": "^22.
|
|
38
|
-
"@types/react": "18.3.
|
|
37
|
+
"@next/eslint-plugin-next": "^15.0.3",
|
|
38
|
+
"@types/node": "^22.10.0",
|
|
39
|
+
"@types/react": "18.3.12",
|
|
39
40
|
"@types/react-dom": "^18.3.1",
|
|
40
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
41
|
-
"@typescript-eslint/parser": "^8.
|
|
42
|
-
"eslint": "^9.
|
|
43
|
-
"eslint-config-next": "^
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^8.16.0",
|
|
42
|
+
"@typescript-eslint/parser": "^8.16.0",
|
|
43
|
+
"eslint": "^9.15.0",
|
|
44
|
+
"eslint-config-next": "^15.0.3",
|
|
44
45
|
"eslint-config-prettier": "^9.1.0",
|
|
45
46
|
"eslint-plugin-prettier": "^5.2.1",
|
|
46
|
-
"prettier": "^3.
|
|
47
|
+
"prettier": "^3.4.1",
|
|
47
48
|
"react": "^18.3.1",
|
|
48
49
|
"react-dom": "^18.3.1",
|
|
49
|
-
"typescript": "^5.
|
|
50
|
+
"typescript": "^5.7.2"
|
|
50
51
|
},
|
|
51
52
|
"files": [
|
|
52
53
|
"src"
|
|
@@ -19,7 +19,6 @@ export interface CustomButtonProps extends ButtonProps {
|
|
|
19
19
|
|
|
20
20
|
const CustomButton: React.FC<CustomButtonProps> = React.memo(
|
|
21
21
|
props => {
|
|
22
|
-
console.log('[trace-button] CustomButton: Rendering component', { props })
|
|
23
22
|
const {
|
|
24
23
|
text,
|
|
25
24
|
variant,
|
|
@@ -40,7 +39,6 @@ const CustomButton: React.FC<CustomButtonProps> = React.memo(
|
|
|
40
39
|
const handleButtonClick = (
|
|
41
40
|
event: React.MouseEvent<HTMLButtonElement>
|
|
42
41
|
): void => {
|
|
43
|
-
console.log('[trace-button] CustomButton: Button clicked')
|
|
44
42
|
event.preventDefault()
|
|
45
43
|
onClick?.(event)
|
|
46
44
|
}
|
|
@@ -63,13 +61,6 @@ const CustomButton: React.FC<CustomButtonProps> = React.memo(
|
|
|
63
61
|
})
|
|
64
62
|
: null
|
|
65
63
|
|
|
66
|
-
console.log('[trace-button] CustomButton: Rendering button', {
|
|
67
|
-
variant,
|
|
68
|
-
style: buttonStyle,
|
|
69
|
-
disableButton,
|
|
70
|
-
isDisabled,
|
|
71
|
-
})
|
|
72
|
-
|
|
73
64
|
const buttonContent = (
|
|
74
65
|
<>
|
|
75
66
|
{iconlocation === 'above' && IconComponent}
|
|
@@ -140,15 +131,9 @@ const CustomButton: React.FC<CustomButtonProps> = React.memo(
|
|
|
140
131
|
prevProps.iconlocation === nextProps.iconlocation &&
|
|
141
132
|
prevProps.fontlocation === nextProps.fontlocation
|
|
142
133
|
|
|
143
|
-
console.log('[trace-button] CustomButton: Props comparison', {
|
|
144
|
-
propsAreEqual,
|
|
145
|
-
prevProps: Object.keys(prevProps),
|
|
146
|
-
nextProps: Object.keys(nextProps),
|
|
147
|
-
})
|
|
148
134
|
return propsAreEqual
|
|
149
135
|
}
|
|
150
136
|
)
|
|
151
137
|
|
|
152
138
|
CustomButton.displayName = 'CustomButton'
|
|
153
|
-
console.log('[trace-button] CustomButton: Component defined')
|
|
154
139
|
export default CustomButton
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import React from 'react'
|
|
3
|
-
import PhoneNumberField
|
|
4
|
-
PhoneNumberFieldProps,
|
|
5
|
-
} from './../../../PhoneNumberField'
|
|
3
|
+
import PhoneNumberField from '../../../PhoneNumberField'
|
|
6
4
|
import { columnconfig, cellconfig } from '../../../Grid'
|
|
5
|
+
import { TextFieldProps } from '@mui/material'
|
|
7
6
|
|
|
8
|
-
export
|
|
7
|
+
export type ExtendedPhoneNumberFieldProps = TextFieldProps & {
|
|
9
8
|
columnconfig?: Partial<columnconfig>
|
|
10
9
|
cellconfig?: cellconfig
|
|
11
10
|
}
|
|
@@ -1,15 +1,14 @@
|
|
|
1
|
+
'use client'
|
|
1
2
|
import React from 'react'
|
|
2
|
-
import TextField
|
|
3
|
-
CustomTextFieldProps,
|
|
4
|
-
} from '../../../../components/TextField'
|
|
3
|
+
import TextField from '../../../../components/TextField'
|
|
5
4
|
import { columnconfig, cellconfig } from '../../../Grid'
|
|
5
|
+
import { TextFieldProps } from '@mui/material'
|
|
6
6
|
|
|
7
7
|
type ExtendedColumnConfig = Omit<columnconfig, 'component'> & {
|
|
8
8
|
component?: columnconfig['component']
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export
|
|
12
|
-
extends Omit<CustomTextFieldProps, 'columnconfig'> {
|
|
11
|
+
export type ExtendedTextFieldProps = TextFieldProps & {
|
|
13
12
|
columnconfig?: ExtendedColumnConfig
|
|
14
13
|
cellconfig?: cellconfig
|
|
15
14
|
}
|
|
@@ -24,15 +23,6 @@ const useTextField = (grid: {
|
|
|
24
23
|
index: number
|
|
25
24
|
): columnconfig => {
|
|
26
25
|
const {
|
|
27
|
-
name,
|
|
28
|
-
label,
|
|
29
|
-
placeholder,
|
|
30
|
-
value,
|
|
31
|
-
onChange,
|
|
32
|
-
onFocus,
|
|
33
|
-
onBlur,
|
|
34
|
-
error,
|
|
35
|
-
InputProps,
|
|
36
26
|
columnconfig: itemColumnConfig,
|
|
37
27
|
cellconfig,
|
|
38
28
|
...restProps
|
|
@@ -54,21 +44,7 @@ const useTextField = (grid: {
|
|
|
54
44
|
cellconfig: {
|
|
55
45
|
...cellconfig,
|
|
56
46
|
},
|
|
57
|
-
component:
|
|
58
|
-
<TextField
|
|
59
|
-
key={`textfield-${index}`}
|
|
60
|
-
name={name}
|
|
61
|
-
label={label}
|
|
62
|
-
placeholder={placeholder}
|
|
63
|
-
value={value}
|
|
64
|
-
onChange={onChange}
|
|
65
|
-
onFocus={onFocus}
|
|
66
|
-
onBlur={onBlur}
|
|
67
|
-
error={error}
|
|
68
|
-
InputProps={InputProps}
|
|
69
|
-
{...restProps}
|
|
70
|
-
/>
|
|
71
|
-
),
|
|
47
|
+
component: <TextField key={`textfield-${index}`} {...restProps} />,
|
|
72
48
|
}
|
|
73
49
|
|
|
74
50
|
return mergedConfig
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import React, { useState } from 'react'
|
|
3
|
+
import React, { useState, useEffect } from 'react'
|
|
4
4
|
import {
|
|
5
5
|
Select,
|
|
6
6
|
MenuItem,
|
|
@@ -8,9 +8,12 @@ import {
|
|
|
8
8
|
InputLabel,
|
|
9
9
|
SelectProps,
|
|
10
10
|
FormHelperText,
|
|
11
|
-
|
|
11
|
+
Box,
|
|
12
|
+
CircularProgress,
|
|
12
13
|
} from '@mui/material'
|
|
13
14
|
import { styled } from '@mui/material/styles'
|
|
15
|
+
import { black, white } from '../../styles/palette'
|
|
16
|
+
import Typography from '../Typography'
|
|
14
17
|
|
|
15
18
|
export interface SimpleDropdownOption {
|
|
16
19
|
value: string
|
|
@@ -40,37 +43,62 @@ export interface DropdownProps extends Omit<SelectProps, 'onChange'> {
|
|
|
40
43
|
onFocus?: SelectProps['onFocus']
|
|
41
44
|
}
|
|
42
45
|
|
|
46
|
+
const StyledBox = styled(Box)(() => ({
|
|
47
|
+
position: 'relative',
|
|
48
|
+
width: '100%',
|
|
49
|
+
height: '50px',
|
|
50
|
+
marginTop: '5px',
|
|
51
|
+
}))
|
|
52
|
+
|
|
43
53
|
const StyledFormControl = styled(FormControl)<{
|
|
44
54
|
backgroundcolor?: string
|
|
45
55
|
outlinecolor?: string
|
|
46
|
-
}>(({
|
|
47
|
-
|
|
56
|
+
}>(({ outlinecolor }) => ({
|
|
57
|
+
position: 'absolute',
|
|
58
|
+
top: 0,
|
|
59
|
+
left: 0,
|
|
60
|
+
right: 0,
|
|
61
|
+
bottom: 0,
|
|
48
62
|
'& .MuiOutlinedInput-root': {
|
|
49
|
-
backgroundColor:
|
|
63
|
+
backgroundColor: white.main,
|
|
64
|
+
height: '45px',
|
|
50
65
|
'& fieldset': {
|
|
51
|
-
borderColor: outlinecolor ||
|
|
66
|
+
borderColor: outlinecolor || black.main,
|
|
52
67
|
},
|
|
53
68
|
'&:hover fieldset': {
|
|
54
|
-
borderColor: outlinecolor ||
|
|
69
|
+
borderColor: outlinecolor || black.main,
|
|
55
70
|
},
|
|
56
71
|
'&.Mui-focused fieldset': {
|
|
57
|
-
borderColor: outlinecolor ||
|
|
72
|
+
borderColor: outlinecolor || black.main,
|
|
58
73
|
},
|
|
59
74
|
},
|
|
60
75
|
}))
|
|
61
76
|
|
|
62
77
|
const StyledInputLabel = styled(InputLabel)<{ shrunkfontcolor?: string }>(
|
|
63
|
-
(
|
|
78
|
+
() => ({
|
|
79
|
+
color: black.main,
|
|
64
80
|
'&.Mui-focused': {
|
|
65
|
-
color:
|
|
81
|
+
color: black.main,
|
|
82
|
+
},
|
|
83
|
+
'&.MuiInputLabel-shrink': {
|
|
84
|
+
transform: 'translate(13px, -7px) scale(0.75)',
|
|
85
|
+
color: black.main,
|
|
66
86
|
},
|
|
67
87
|
})
|
|
68
88
|
)
|
|
69
89
|
|
|
70
|
-
const StyledMenuItem = styled(MenuItem)((
|
|
90
|
+
const StyledMenuItem = styled(MenuItem)(() => ({
|
|
71
91
|
display: 'flex',
|
|
72
92
|
flexDirection: 'column',
|
|
73
93
|
alignItems: 'flex-start',
|
|
94
|
+
backgroundColor: white.main,
|
|
95
|
+
}))
|
|
96
|
+
|
|
97
|
+
const LoadingContainer = styled(Box)(() => ({
|
|
98
|
+
display: 'flex',
|
|
99
|
+
justifyContent: 'center',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
height: '50px',
|
|
74
102
|
}))
|
|
75
103
|
|
|
76
104
|
const capitalizeFirstLetter = (string: string) => {
|
|
@@ -94,23 +122,33 @@ const Dropdown: React.FC<DropdownProps> = ({
|
|
|
94
122
|
onFocus,
|
|
95
123
|
...rest
|
|
96
124
|
}) => {
|
|
97
|
-
const [selectedValue, setSelectedValue] = useState(
|
|
125
|
+
const [selectedValue, setSelectedValue] = useState<string>('')
|
|
126
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
98
129
|
const defaultOption = options.find(option => option.value === defaultValue)
|
|
99
|
-
|
|
100
|
-
|
|
130
|
+
setSelectedValue(defaultOption ? defaultOption.value : '')
|
|
131
|
+
setIsLoading(false)
|
|
132
|
+
}, [defaultValue, options])
|
|
101
133
|
|
|
102
134
|
const handleChange: SelectProps['onChange'] = (event, child) => {
|
|
103
135
|
const newValue = event.target.value as string
|
|
104
136
|
setSelectedValue(newValue)
|
|
105
|
-
onChange
|
|
137
|
+
if (onChange) {
|
|
138
|
+
onChange(event, child)
|
|
139
|
+
}
|
|
106
140
|
}
|
|
107
141
|
|
|
108
142
|
const handleBlur: SelectProps['onBlur'] = event => {
|
|
109
|
-
onBlur
|
|
143
|
+
if (onBlur) {
|
|
144
|
+
onBlur(event)
|
|
145
|
+
}
|
|
110
146
|
}
|
|
111
147
|
|
|
112
148
|
const handleFocus: SelectProps['onFocus'] = event => {
|
|
113
|
-
onFocus
|
|
149
|
+
if (onFocus) {
|
|
150
|
+
onFocus(event)
|
|
151
|
+
}
|
|
114
152
|
}
|
|
115
153
|
|
|
116
154
|
const renderMenuItem = (option: DropdownOption) => {
|
|
@@ -118,48 +156,107 @@ const Dropdown: React.FC<DropdownProps> = ({
|
|
|
118
156
|
if (!('attribute1' in option)) {
|
|
119
157
|
return (
|
|
120
158
|
<MenuItem key={option.value} value={option.value}>
|
|
121
|
-
{label}
|
|
159
|
+
<Typography fontvariant="merriparagraph" text={label} />
|
|
122
160
|
</MenuItem>
|
|
123
161
|
)
|
|
124
162
|
} else {
|
|
125
163
|
return (
|
|
126
164
|
<StyledMenuItem key={option.value} value={option.value}>
|
|
127
|
-
<Typography
|
|
128
|
-
<Typography
|
|
129
|
-
|
|
130
|
-
{option.attribute2
|
|
131
|
-
|
|
165
|
+
<Typography fontvariant="merriparagraph" text={label} />
|
|
166
|
+
<Typography
|
|
167
|
+
fontvariant="merriparagraph"
|
|
168
|
+
text={`${option.attribute1}${option.attribute2 ? ` | ${option.attribute2}` : ''}`}
|
|
169
|
+
fontcolor="textSecondary"
|
|
170
|
+
/>
|
|
132
171
|
</StyledMenuItem>
|
|
133
172
|
)
|
|
134
173
|
}
|
|
135
174
|
}
|
|
136
175
|
|
|
176
|
+
if (isLoading) {
|
|
177
|
+
return (
|
|
178
|
+
<LoadingContainer>
|
|
179
|
+
<CircularProgress size={24} />
|
|
180
|
+
</LoadingContainer>
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
|
|
137
184
|
return (
|
|
138
|
-
<
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
{label}
|
|
146
|
-
</StyledInputLabel>
|
|
147
|
-
<Select
|
|
148
|
-
labelId={`${name}-label`}
|
|
149
|
-
value={selectedValue}
|
|
150
|
-
onChange={handleChange}
|
|
151
|
-
onBlur={handleBlur}
|
|
152
|
-
onFocus={handleFocus}
|
|
153
|
-
label={label}
|
|
154
|
-
sx={{ color: fontcolor }}
|
|
155
|
-
name={name}
|
|
156
|
-
aria-labelledby={`${name}-label`}
|
|
157
|
-
{...rest}
|
|
185
|
+
<StyledBox>
|
|
186
|
+
<StyledFormControl
|
|
187
|
+
backgroundcolor={backgroundcolor}
|
|
188
|
+
outlinecolor={outlinecolor}
|
|
189
|
+
error={error}
|
|
190
|
+
required={required}
|
|
191
|
+
fullWidth
|
|
158
192
|
>
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
193
|
+
<StyledInputLabel
|
|
194
|
+
id={`${name}-label`}
|
|
195
|
+
shrunkfontcolor={shrunkfontcolor}
|
|
196
|
+
>
|
|
197
|
+
{label}
|
|
198
|
+
</StyledInputLabel>
|
|
199
|
+
<Select
|
|
200
|
+
labelId={`${name}-label`}
|
|
201
|
+
value={selectedValue}
|
|
202
|
+
onChange={handleChange}
|
|
203
|
+
onBlur={handleBlur}
|
|
204
|
+
onFocus={handleFocus}
|
|
205
|
+
label={label}
|
|
206
|
+
sx={{
|
|
207
|
+
color: fontcolor || black.main,
|
|
208
|
+
height: '45px',
|
|
209
|
+
backgroundColor: white.main,
|
|
210
|
+
'& .MuiSelect-select': {
|
|
211
|
+
paddingTop: '10px',
|
|
212
|
+
paddingBottom: '10px',
|
|
213
|
+
},
|
|
214
|
+
'& .MuiSvgIcon-root': {
|
|
215
|
+
color: black.main,
|
|
216
|
+
},
|
|
217
|
+
'& .MuiOutlinedInput-notchedOutline': {
|
|
218
|
+
borderColor: black.main,
|
|
219
|
+
},
|
|
220
|
+
'&:hover .MuiOutlinedInput-notchedOutline': {
|
|
221
|
+
borderColor: black.main,
|
|
222
|
+
},
|
|
223
|
+
'&.Mui-focused .MuiOutlinedInput-notchedOutline': {
|
|
224
|
+
borderColor: black.main,
|
|
225
|
+
},
|
|
226
|
+
'& .MuiInputBase-input': {
|
|
227
|
+
color: black.main,
|
|
228
|
+
},
|
|
229
|
+
'& .MuiInputBase-input::placeholder': {
|
|
230
|
+
color: black.main,
|
|
231
|
+
opacity: 1,
|
|
232
|
+
},
|
|
233
|
+
'& .MuiPaper-root': {
|
|
234
|
+
backgroundColor: white.main,
|
|
235
|
+
},
|
|
236
|
+
'& .MuiMenu-list': {
|
|
237
|
+
backgroundColor: white.main,
|
|
238
|
+
},
|
|
239
|
+
}}
|
|
240
|
+
MenuProps={{
|
|
241
|
+
PaperProps: {
|
|
242
|
+
sx: {
|
|
243
|
+
backgroundColor: white.main,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
}}
|
|
247
|
+
name={name}
|
|
248
|
+
aria-labelledby={`${name}-label`}
|
|
249
|
+
{...rest}
|
|
250
|
+
>
|
|
251
|
+
{options.map(renderMenuItem)}
|
|
252
|
+
</Select>
|
|
253
|
+
{helperText && (
|
|
254
|
+
<FormHelperText>
|
|
255
|
+
<Typography fontvariant="merriparagraph" text={helperText} />
|
|
256
|
+
</FormHelperText>
|
|
257
|
+
)}
|
|
258
|
+
</StyledFormControl>
|
|
259
|
+
</StyledBox>
|
|
163
260
|
)
|
|
164
261
|
}
|
|
165
262
|
|
|
@@ -2,78 +2,43 @@
|
|
|
2
2
|
import React, { useState, useEffect } from 'react'
|
|
3
3
|
import { Box, Tabs, Tab } from '@mui/material'
|
|
4
4
|
import { NavProps, SubNav, View } from '../index'
|
|
5
|
+
import { usePathname } from 'next/navigation'
|
|
5
6
|
|
|
6
|
-
/**
|
|
7
|
-
* Represents the possible alignment options for the horizontal navigation.
|
|
8
|
-
*/
|
|
9
7
|
type Alignment = 'left' | 'center' | 'right' | 'inherit' | 'justify'
|
|
10
8
|
|
|
11
|
-
/**
|
|
12
|
-
* Represents the structure of an active tab value.
|
|
13
|
-
*/
|
|
14
9
|
export interface ActiveTabValue {
|
|
15
|
-
|
|
16
|
-
tabId: string
|
|
10
|
+
tabId: string | false
|
|
17
11
|
}
|
|
18
12
|
|
|
19
|
-
/**
|
|
20
|
-
* Props for the HorizontalVariant component.
|
|
21
|
-
*/
|
|
22
13
|
export interface HorizontalVariantProps {
|
|
23
|
-
/** An array of navigation items, sub-navigation items, or views. */
|
|
24
14
|
items: (NavProps | SubNav | View)[]
|
|
25
|
-
/** The height of the navigation bar. Defaults to '80px'. */
|
|
26
15
|
height?: string
|
|
27
|
-
/** The alignment of the navigation items. Defaults to 'left'. */
|
|
28
16
|
alignment?: Alignment
|
|
29
|
-
/** A unique name for this navigation component. Used for state management. */
|
|
30
17
|
navname?: string
|
|
31
18
|
}
|
|
32
19
|
|
|
33
|
-
/**
|
|
34
|
-
* HorizontalVariant component that renders a horizontal navigation bar.
|
|
35
|
-
* It supports dynamic tab management, routing, and custom click handlers.
|
|
36
|
-
*
|
|
37
|
-
* @param {HorizontalVariantProps} props - The props for the HorizontalVariant component.
|
|
38
|
-
* @returns {JSX.Element} The rendered HorizontalVariant component.
|
|
39
|
-
*/
|
|
40
20
|
function HorizontalVariant({
|
|
41
21
|
items,
|
|
42
22
|
height = '80px',
|
|
43
23
|
alignment = 'left',
|
|
44
24
|
navname = '',
|
|
45
25
|
}: HorizontalVariantProps) {
|
|
46
|
-
/**
|
|
47
|
-
* State to keep track of active tab values for different navigation components.
|
|
48
|
-
*/
|
|
49
26
|
const [activeTabValues, setActiveTabValues] = useState<
|
|
50
27
|
Record<string, ActiveTabValue>
|
|
51
28
|
>({})
|
|
29
|
+
const pathname = usePathname()
|
|
52
30
|
|
|
53
|
-
/**
|
|
54
|
-
* Effect hook to initialize the active tab values when the component mounts.
|
|
55
|
-
*/
|
|
56
31
|
useEffect(() => {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}, [items, navname, activeTabValues])
|
|
32
|
+
const currentTab = items.find(
|
|
33
|
+
item => 'orientation' in item && item.route === pathname
|
|
34
|
+
) as NavProps | undefined
|
|
35
|
+
|
|
36
|
+
setActiveTabValues(prev => ({
|
|
37
|
+
...prev,
|
|
38
|
+
[navname]: { tabId: currentTab?.title || false },
|
|
39
|
+
}))
|
|
40
|
+
}, [items, navname, pathname])
|
|
69
41
|
|
|
70
|
-
/**
|
|
71
|
-
* Handles tab change events.
|
|
72
|
-
* Updates the active tab values in the state.
|
|
73
|
-
*
|
|
74
|
-
* @param {React.SyntheticEvent} event - The event object.
|
|
75
|
-
* @param {string} newValue - The new value of the selected tab.
|
|
76
|
-
*/
|
|
77
42
|
const handleTabChange = (event: React.SyntheticEvent, newValue: string) => {
|
|
78
43
|
setActiveTabValues(prev => ({
|
|
79
44
|
...prev,
|
|
@@ -81,12 +46,6 @@ function HorizontalVariant({
|
|
|
81
46
|
}))
|
|
82
47
|
}
|
|
83
48
|
|
|
84
|
-
/**
|
|
85
|
-
* Handles click events on individual tabs.
|
|
86
|
-
* Supports different trigger types: route, onClick, and routeonhorizontal.
|
|
87
|
-
*
|
|
88
|
-
* @param {NavProps} tab - The tab object that was clicked.
|
|
89
|
-
*/
|
|
90
49
|
const handleTabClick = (tab: NavProps) => {
|
|
91
50
|
if (tab.trigger === 'route') {
|
|
92
51
|
if (tab.route) {
|
|
@@ -116,7 +75,7 @@ function HorizontalVariant({
|
|
|
116
75
|
}}
|
|
117
76
|
>
|
|
118
77
|
<Tabs
|
|
119
|
-
value={activeTabValues[navname]?.tabId ||
|
|
78
|
+
value={activeTabValues[navname]?.tabId || false}
|
|
120
79
|
onChange={handleTabChange}
|
|
121
80
|
aria-label="nav tabs"
|
|
122
81
|
sx={{
|
|
@@ -136,8 +95,8 @@ function HorizontalVariant({
|
|
|
136
95
|
return (
|
|
137
96
|
<Tab
|
|
138
97
|
key={tab.title}
|
|
139
|
-
value={tab.title}
|
|
140
|
-
label={tab.title}
|
|
98
|
+
value={tab.title || ''}
|
|
99
|
+
label={tab.title || ''}
|
|
141
100
|
onClick={() => handleTabClick(tab)}
|
|
142
101
|
sx={{
|
|
143
102
|
minHeight: 0,
|
|
@@ -1,45 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
import React, {
|
|
3
|
-
import { TextField, TextFieldProps } from '@mui/material'
|
|
4
|
-
import { styled } from '@mui/material/styles'
|
|
5
|
-
|
|
6
|
-
export interface PhoneNumberFieldProps
|
|
7
|
-
extends Omit<TextFieldProps, 'onChange'> {
|
|
8
|
-
initialValue?: string
|
|
9
|
-
onChange?: () => void
|
|
10
|
-
backgroundcolor?: string
|
|
11
|
-
outlinecolor?: string
|
|
12
|
-
fontcolor?: string
|
|
13
|
-
label?: string
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const StyledTextField = styled(TextField)<{
|
|
17
|
-
backgroundcolor?: string
|
|
18
|
-
outlinecolor?: string
|
|
19
|
-
fontcolor?: string
|
|
20
|
-
}>(({ theme, backgroundcolor, outlinecolor, fontcolor }) => ({
|
|
21
|
-
'& .MuiOutlinedInput-root': {
|
|
22
|
-
backgroundColor: backgroundcolor || theme.palette.background.paper,
|
|
23
|
-
'& fieldset': {
|
|
24
|
-
borderColor: outlinecolor || theme.palette.primary.main,
|
|
25
|
-
},
|
|
26
|
-
'&:hover fieldset': {
|
|
27
|
-
borderColor: outlinecolor || theme.palette.primary.main,
|
|
28
|
-
},
|
|
29
|
-
'&.Mui-focused fieldset': {
|
|
30
|
-
borderColor: outlinecolor || theme.palette.primary.main,
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
'& .MuiInputLabel-root': {
|
|
34
|
-
color: fontcolor || theme.palette.text.primary,
|
|
35
|
-
'&.Mui-focused': {
|
|
36
|
-
color: fontcolor || theme.palette.primary.main,
|
|
37
|
-
},
|
|
38
|
-
},
|
|
39
|
-
'& .MuiInputBase-input': {
|
|
40
|
-
color: fontcolor || theme.palette.text.primary,
|
|
41
|
-
},
|
|
42
|
-
}))
|
|
2
|
+
import React, { useCallback, useMemo, useState } from 'react'
|
|
3
|
+
import { Box, TextField as MuiTextField, TextFieldProps } from '@mui/material'
|
|
43
4
|
|
|
44
5
|
const formatPhoneNumber = (value: string): string => {
|
|
45
6
|
const digits = value.replace(/\D/g, '')
|
|
@@ -57,47 +18,110 @@ const formatPhoneNumber = (value: string): string => {
|
|
|
57
18
|
return formattedNumber.trim()
|
|
58
19
|
}
|
|
59
20
|
|
|
60
|
-
const PhoneNumberField: React.FC<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
21
|
+
const PhoneNumberField: React.FC<TextFieldProps> = React.memo(props => {
|
|
22
|
+
const {
|
|
23
|
+
name,
|
|
24
|
+
label = 'Phone Number',
|
|
25
|
+
placeholder,
|
|
26
|
+
onChange,
|
|
27
|
+
onFocus,
|
|
28
|
+
onBlur,
|
|
29
|
+
value = '',
|
|
30
|
+
error,
|
|
31
|
+
...restProps
|
|
32
|
+
} = props
|
|
33
|
+
|
|
69
34
|
const [phoneNumber, setPhoneNumber] = useState(
|
|
70
|
-
formatPhoneNumber(
|
|
35
|
+
formatPhoneNumber(value as string)
|
|
71
36
|
)
|
|
72
37
|
|
|
73
|
-
const
|
|
74
|
-
(e: React.ChangeEvent<HTMLInputElement
|
|
38
|
+
const handleChange = useCallback(
|
|
39
|
+
(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
75
40
|
const input = e.target.value
|
|
76
41
|
let strippedInput = input.replace(/^\+1\s?/, '').replace(/\D/g, '')
|
|
77
42
|
strippedInput = strippedInput.slice(0, 10)
|
|
78
43
|
const formattedValue =
|
|
79
44
|
strippedInput.length > 0 ? formatPhoneNumber(strippedInput) : '+1 '
|
|
80
45
|
setPhoneNumber(formattedValue)
|
|
81
|
-
onChange
|
|
46
|
+
if (onChange) {
|
|
47
|
+
onChange(e)
|
|
48
|
+
}
|
|
82
49
|
},
|
|
83
50
|
[onChange]
|
|
84
51
|
)
|
|
85
52
|
|
|
53
|
+
const handleFocus = useCallback(
|
|
54
|
+
(e: React.FocusEvent<HTMLInputElement>) => {
|
|
55
|
+
if (onFocus) {
|
|
56
|
+
onFocus(e)
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
[onFocus]
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
const handleBlur = useCallback(
|
|
63
|
+
(e: React.FocusEvent<HTMLInputElement>) => {
|
|
64
|
+
if (onBlur) {
|
|
65
|
+
onBlur(e)
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
[onBlur]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
const handleClick = useCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
|
72
|
+
e.preventDefault()
|
|
73
|
+
}, [])
|
|
74
|
+
|
|
75
|
+
const inputProps = useMemo(
|
|
76
|
+
() => ({
|
|
77
|
+
style: {
|
|
78
|
+
height: '40px',
|
|
79
|
+
padding: '8px 14px',
|
|
80
|
+
},
|
|
81
|
+
}),
|
|
82
|
+
[]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const InputProps = useMemo(
|
|
86
|
+
() => ({
|
|
87
|
+
style: {
|
|
88
|
+
height: '45px',
|
|
89
|
+
},
|
|
90
|
+
}),
|
|
91
|
+
[]
|
|
92
|
+
)
|
|
93
|
+
|
|
86
94
|
return (
|
|
87
|
-
<
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
inputProps={{
|
|
96
|
-
maxLength: 16,
|
|
95
|
+
<Box
|
|
96
|
+
sx={{
|
|
97
|
+
display: 'flex',
|
|
98
|
+
flexDirection: 'column',
|
|
99
|
+
justifyContent: 'center',
|
|
100
|
+
width: '100%',
|
|
101
|
+
marginTop: '5px',
|
|
102
|
+
height: '70px',
|
|
97
103
|
}}
|
|
98
|
-
{
|
|
99
|
-
|
|
104
|
+
onClick={handleClick}
|
|
105
|
+
>
|
|
106
|
+
<MuiTextField
|
|
107
|
+
name={name}
|
|
108
|
+
label={label}
|
|
109
|
+
placeholder={placeholder}
|
|
110
|
+
onChange={handleChange}
|
|
111
|
+
onFocus={handleFocus}
|
|
112
|
+
onBlur={handleBlur}
|
|
113
|
+
value={phoneNumber}
|
|
114
|
+
error={error}
|
|
115
|
+
fullWidth
|
|
116
|
+
variant="outlined"
|
|
117
|
+
inputProps={inputProps}
|
|
118
|
+
InputProps={InputProps}
|
|
119
|
+
{...restProps}
|
|
120
|
+
/>
|
|
121
|
+
</Box>
|
|
100
122
|
)
|
|
101
|
-
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
PhoneNumberField.displayName = 'PhoneNumberField'
|
|
102
126
|
|
|
103
127
|
export default PhoneNumberField
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
import React, { useState, useCallback, useMemo } from 'react'
|
|
3
|
-
import { Box, Paper, SelectChangeEvent } from '@mui/material'
|
|
2
|
+
import React, { useState, useCallback, useMemo, useEffect } from 'react'
|
|
3
|
+
import { Box, Paper, SelectChangeEvent, CircularProgress } from '@mui/material'
|
|
4
4
|
import InfoIcon from '@mui/icons-material/Info'
|
|
5
5
|
import CheckCircleIcon from '@mui/icons-material/CheckCircle'
|
|
6
6
|
import { Typography } from '../Typography'
|
|
@@ -72,17 +72,37 @@ export interface PricingProps {
|
|
|
72
72
|
const PricingTable: React.FC<PricingProps> = props => {
|
|
73
73
|
const router = useRouter()
|
|
74
74
|
const [selectedPackageIndex, setSelectedPackageIndex] = useState(0)
|
|
75
|
+
const [selectedPackage, setSelectedPackage] = useState('')
|
|
76
|
+
const [isLoading, setIsLoading] = useState(true)
|
|
75
77
|
|
|
76
78
|
const config = useMemo(() => {
|
|
77
79
|
return { ...defaultConfig, ...props }
|
|
78
80
|
}, [props])
|
|
79
81
|
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
const timer = setTimeout(() => {
|
|
84
|
+
setIsLoading(false)
|
|
85
|
+
}, 100) // Simulating a 100ms loading time
|
|
86
|
+
|
|
87
|
+
return () => clearTimeout(timer)
|
|
88
|
+
}, [])
|
|
89
|
+
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
if (
|
|
92
|
+
config.packagecolumns?.packagenames &&
|
|
93
|
+
config.packagecolumns.packagenames.length > 0
|
|
94
|
+
) {
|
|
95
|
+
setSelectedPackage(config.packagecolumns.packagenames[0])
|
|
96
|
+
}
|
|
97
|
+
}, [config.packagecolumns?.packagenames])
|
|
98
|
+
|
|
80
99
|
const handlePackageChange = useCallback(
|
|
81
100
|
(event: SelectChangeEvent<unknown>) => {
|
|
82
101
|
const newValue = event.target.value as string
|
|
83
102
|
const newIndex =
|
|
84
103
|
config.packagecolumns?.packagenames?.indexOf(newValue) ?? 0
|
|
85
104
|
setSelectedPackageIndex(newIndex)
|
|
105
|
+
setSelectedPackage(newValue)
|
|
86
106
|
console.log('Package selection changed to:', newValue)
|
|
87
107
|
},
|
|
88
108
|
[config.packagecolumns?.packagenames]
|
|
@@ -116,7 +136,7 @@ const PricingTable: React.FC<PricingProps> = props => {
|
|
|
116
136
|
value: name,
|
|
117
137
|
label: name,
|
|
118
138
|
}))}
|
|
119
|
-
defaultValue={
|
|
139
|
+
defaultValue={selectedPackage}
|
|
120
140
|
backgroundcolor={semiTransparentBlack.main}
|
|
121
141
|
outlinecolor={black.main}
|
|
122
142
|
fontcolor={black.main}
|
|
@@ -276,10 +296,46 @@ const PricingTable: React.FC<PricingProps> = props => {
|
|
|
276
296
|
}
|
|
277
297
|
|
|
278
298
|
return { headerColumnConfigs, featureColumnConfigs }
|
|
279
|
-
}, [
|
|
299
|
+
}, [
|
|
300
|
+
config,
|
|
301
|
+
selectedPackageIndex,
|
|
302
|
+
selectedPackage,
|
|
303
|
+
router,
|
|
304
|
+
handlePackageChange,
|
|
305
|
+
])
|
|
280
306
|
|
|
281
307
|
const { headerColumnConfigs, featureColumnConfigs } = renderColumnConfigs()
|
|
282
308
|
|
|
309
|
+
if (isLoading) {
|
|
310
|
+
return (
|
|
311
|
+
<CustomGrid
|
|
312
|
+
gridconfig={{
|
|
313
|
+
gridwidth: '100%',
|
|
314
|
+
alignment: 'center',
|
|
315
|
+
}}
|
|
316
|
+
columnconfig={[
|
|
317
|
+
{
|
|
318
|
+
row: 1,
|
|
319
|
+
column: 1,
|
|
320
|
+
alignment: 'center',
|
|
321
|
+
component: (
|
|
322
|
+
<Box
|
|
323
|
+
display="flex"
|
|
324
|
+
justifyContent="center"
|
|
325
|
+
alignItems="center"
|
|
326
|
+
height="350px"
|
|
327
|
+
width="100%"
|
|
328
|
+
overflow="auto"
|
|
329
|
+
>
|
|
330
|
+
<CircularProgress size={240} thickness={2} />
|
|
331
|
+
</Box>
|
|
332
|
+
),
|
|
333
|
+
},
|
|
334
|
+
]}
|
|
335
|
+
/>
|
|
336
|
+
)
|
|
337
|
+
}
|
|
338
|
+
|
|
283
339
|
return (
|
|
284
340
|
<Paper
|
|
285
341
|
elevation={1}
|
|
@@ -1,11 +1,8 @@
|
|
|
1
|
+
'use client'
|
|
1
2
|
import React, { useCallback, useMemo } from 'react'
|
|
2
3
|
import { Box, TextField as MuiTextField, TextFieldProps } from '@mui/material'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
name: string
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const TextField: React.FC<CustomTextFieldProps> = React.memo(props => {
|
|
5
|
+
const TextField: React.FC<TextFieldProps> = React.memo(props => {
|
|
9
6
|
const {
|
|
10
7
|
name,
|
|
11
8
|
label,
|
|
@@ -16,7 +13,6 @@ const TextField: React.FC<CustomTextFieldProps> = React.memo(props => {
|
|
|
16
13
|
value,
|
|
17
14
|
error,
|
|
18
15
|
sx,
|
|
19
|
-
InputProps,
|
|
20
16
|
...restProps
|
|
21
17
|
} = props
|
|
22
18
|
|
|
@@ -64,31 +60,27 @@ const TextField: React.FC<CustomTextFieldProps> = React.memo(props => {
|
|
|
64
60
|
[]
|
|
65
61
|
)
|
|
66
62
|
|
|
67
|
-
const
|
|
63
|
+
const slotProps = useMemo(
|
|
68
64
|
() => ({
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
...inputStyle,
|
|
72
|
-
...InputProps?.style,
|
|
65
|
+
input: {
|
|
66
|
+
style: inputStyle,
|
|
73
67
|
},
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
top: '9px',
|
|
88
|
-
left: '12px',
|
|
68
|
+
inputLabel: {
|
|
69
|
+
sx: {
|
|
70
|
+
'&.MuiInputLabel-shrink': {
|
|
71
|
+
top: '0px',
|
|
72
|
+
left: '0px',
|
|
73
|
+
},
|
|
74
|
+
'&:not(.MuiInputLabel-shrink)': {
|
|
75
|
+
transform: 'scale(1)',
|
|
76
|
+
transformOrigin: 'top left',
|
|
77
|
+
top: '9px',
|
|
78
|
+
left: '12px',
|
|
79
|
+
},
|
|
80
|
+
},
|
|
89
81
|
},
|
|
90
82
|
}),
|
|
91
|
-
[]
|
|
83
|
+
[inputStyle]
|
|
92
84
|
)
|
|
93
85
|
|
|
94
86
|
return (
|
|
@@ -99,7 +91,7 @@ const TextField: React.FC<CustomTextFieldProps> = React.memo(props => {
|
|
|
99
91
|
justifyContent: 'center',
|
|
100
92
|
width: '100%',
|
|
101
93
|
marginTop: '5px',
|
|
102
|
-
height: '
|
|
94
|
+
height: '70px',
|
|
103
95
|
...sx,
|
|
104
96
|
}}
|
|
105
97
|
onClick={handleClick}
|
|
@@ -113,10 +105,7 @@ const TextField: React.FC<CustomTextFieldProps> = React.memo(props => {
|
|
|
113
105
|
onBlur={handleBlur}
|
|
114
106
|
value={value}
|
|
115
107
|
error={error}
|
|
116
|
-
|
|
117
|
-
InputLabelProps={{
|
|
118
|
-
sx: labelStyle,
|
|
119
|
-
}}
|
|
108
|
+
slotProps={slotProps}
|
|
120
109
|
fullWidth
|
|
121
110
|
variant="outlined"
|
|
122
111
|
{...restProps}
|
package/src/index.ts
CHANGED
|
@@ -71,7 +71,6 @@ import { ExtendedImageProps } from './components/Content/Structure/image/useImag
|
|
|
71
71
|
import { ExtendedConfirmationCodeInputsProps } from './components/Content/Structure/confirmationinput/useConfirmationInput'
|
|
72
72
|
import { ExtendedRadioGroupProps } from './components/Content/Structure/radiogroup/useRadioGroup'
|
|
73
73
|
import { ExtendedPhoneNumberFieldProps } from './components/Content/Structure/phoneNumber/usePhoneNumber'
|
|
74
|
-
import { CustomTextFieldProps } from './components/TextField'
|
|
75
74
|
// Colors
|
|
76
75
|
import {
|
|
77
76
|
moss,
|
|
@@ -268,7 +267,6 @@ export type { CustomToolbarComponentProps }
|
|
|
268
267
|
export type { TransferListComponentProps }
|
|
269
268
|
export type { StyledTooltipComponentProps }
|
|
270
269
|
export type { DropdownOption }
|
|
271
|
-
export type { CustomTextFieldProps }
|
|
272
270
|
|
|
273
271
|
// New type exports
|
|
274
272
|
export type { DateFieldProps }
|