goobs-frontend 0.7.53
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/LICENSE +21 -0
- package/README.md +190 -0
- package/package.json +122 -0
- package/src/app/_app.tsx +8 -0
- package/src/atoms/helperfooter.ts +21 -0
- package/src/components/Button/index.tsx +241 -0
- package/src/components/ConfirmationCodeInput/index.tsx +108 -0
- package/src/components/ConfirmationCodeInput/utils/useCodeConfirmation.tsx +129 -0
- package/src/components/Content/Structure/animations.tsx +106 -0
- package/src/components/Content/Structure/button/useButton.tsx +80 -0
- package/src/components/Content/Structure/confirmationinput/useConfirmationInput.tsx +56 -0
- package/src/components/Content/Structure/image/useImage.tsx +62 -0
- package/src/components/Content/Structure/link/useLink.tsx +60 -0
- package/src/components/Content/Structure/radiogroup/useGridRadioGroup.tsx +62 -0
- package/src/components/Content/Structure/styledcomponent/useStyledComponent.tsx +96 -0
- package/src/components/Content/Structure/typography/useGridTypography.tsx +53 -0
- package/src/components/Content/index.tsx +147 -0
- package/src/components/Form/Popup/index.tsx +121 -0
- package/src/components/Grid/defaultconfig.tsx +131 -0
- package/src/components/Grid/index.tsx +265 -0
- package/src/components/Icons/Calendar.tsx +21 -0
- package/src/components/Icons/DownArrowFilled.tsx +9 -0
- package/src/components/Icons/Drag.tsx +9 -0
- package/src/components/Icons/FavoriteIcon.tsx +24 -0
- package/src/components/Icons/Info.tsx +12 -0
- package/src/components/Icons/Search.tsx +29 -0
- package/src/components/Icons/ShowHideEye.tsx +16 -0
- package/src/components/RadioGroup/index.tsx +96 -0
- package/src/components/StyledComponent/adornments.tsx +118 -0
- package/src/components/StyledComponent/helperfooter/useHelperFooter.tsx +411 -0
- package/src/components/StyledComponent/hooks/useDropdown.tsx +144 -0
- package/src/components/StyledComponent/hooks/usePassword.tsx +23 -0
- package/src/components/StyledComponent/hooks/usePhoneNumber.tsx +75 -0
- package/src/components/StyledComponent/hooks/useSearchbar.tsx +44 -0
- package/src/components/StyledComponent/hooks/useSplitButton.tsx +66 -0
- package/src/components/StyledComponent/index.tsx +404 -0
- package/src/components/Typography/index.tsx +268 -0
- package/src/index.ts +210 -0
- package/src/main.tsx +7 -0
- package/src/styles/Form/index.ts +7 -0
- package/src/styles/StyledComponent/Label/index.ts +76 -0
- package/src/styles/palette.ts +143 -0
- package/src/styles/typography.ts +184 -0
- package/src/types/async-lock.d.ts +35 -0
|
@@ -0,0 +1,411 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback } from 'react'
|
|
4
|
+
import { debounce } from 'lodash'
|
|
5
|
+
import { useAtom } from 'jotai'
|
|
6
|
+
import {
|
|
7
|
+
helperFooterAtom,
|
|
8
|
+
HelperFooterMessage,
|
|
9
|
+
} from '../../../atoms/helperfooter'
|
|
10
|
+
import { get, set } from 'goobs-cache'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* isValidEmailFormat function checks if the provided email string is in a valid email format.
|
|
14
|
+
* @param email The email string to validate.
|
|
15
|
+
* @returns A boolean indicating whether the email is in a valid format.
|
|
16
|
+
*/
|
|
17
|
+
const isValidEmailFormat = (email: string): boolean => {
|
|
18
|
+
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
|
|
19
|
+
const result = emailRegex.test(email)
|
|
20
|
+
return result
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* useHelperFooter hook provides functions for validating form fields and managing helper footer messages.
|
|
25
|
+
* It handles validation for email, password, confirm password, phone number, and generic fields.
|
|
26
|
+
* @returns An object containing the helperFooterAtomValue and validateField function.
|
|
27
|
+
*/
|
|
28
|
+
export const useHelperFooter = () => {
|
|
29
|
+
const [helperFooterAtomValue, setHelperFooterAtomValue] =
|
|
30
|
+
useAtom(helperFooterAtom)
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* handleGenericErrorCreation function handles validation for generic form fields.
|
|
34
|
+
* It checks if the field is required and not empty, and returns an error message if applicable.
|
|
35
|
+
* @param formData The form data object containing field values.
|
|
36
|
+
* @param name The name of the field being validated.
|
|
37
|
+
* @param label The label of the field being validated.
|
|
38
|
+
* @param required A boolean indicating whether the field is required.
|
|
39
|
+
* @param formname The name of the form the field belongs to.
|
|
40
|
+
* @returns A HelperFooterMessage object if an error is detected, otherwise undefined.
|
|
41
|
+
*/
|
|
42
|
+
const handleGenericErrorCreation = useCallback(
|
|
43
|
+
(
|
|
44
|
+
formData: FormData,
|
|
45
|
+
name: string,
|
|
46
|
+
label: string,
|
|
47
|
+
required: boolean,
|
|
48
|
+
formname: string
|
|
49
|
+
): HelperFooterMessage | undefined => {
|
|
50
|
+
const value = formData.get(name) as string
|
|
51
|
+
|
|
52
|
+
if (required && (!value || !value.trim())) {
|
|
53
|
+
return {
|
|
54
|
+
status: 'error',
|
|
55
|
+
statusMessage: `${label} is required. Please enter a ${label.toLowerCase()}.`,
|
|
56
|
+
spreadMessage: `${label} is required.`,
|
|
57
|
+
spreadMessagePriority: 1,
|
|
58
|
+
formname,
|
|
59
|
+
required,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return undefined
|
|
64
|
+
},
|
|
65
|
+
[]
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* handleEmailErrorCreation function handles validation for email fields.
|
|
70
|
+
* It checks if the email is required, not empty, and in a valid format.
|
|
71
|
+
* @param formData The form data object containing field values.
|
|
72
|
+
* @param required A boolean indicating whether the email field is required.
|
|
73
|
+
* @param formname The name of the form the email field belongs to.
|
|
74
|
+
* @returns A HelperFooterMessage object with the validation result.
|
|
75
|
+
*/
|
|
76
|
+
const handleEmailErrorCreation = useCallback(
|
|
77
|
+
(
|
|
78
|
+
formData: FormData,
|
|
79
|
+
required: boolean,
|
|
80
|
+
formname: string
|
|
81
|
+
): HelperFooterMessage | undefined => {
|
|
82
|
+
const email = formData.get('email') as string
|
|
83
|
+
|
|
84
|
+
if (required && (!email || !email.trim())) {
|
|
85
|
+
return {
|
|
86
|
+
status: 'error',
|
|
87
|
+
statusMessage: 'Please enter an email address.',
|
|
88
|
+
spreadMessage: 'Email is required.',
|
|
89
|
+
spreadMessagePriority: 1,
|
|
90
|
+
formname,
|
|
91
|
+
required,
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!email) {
|
|
96
|
+
return undefined
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (email && !isValidEmailFormat(email)) {
|
|
100
|
+
return {
|
|
101
|
+
status: 'error',
|
|
102
|
+
statusMessage: 'Please enter a valid email address.',
|
|
103
|
+
spreadMessage: 'Invalid email format.',
|
|
104
|
+
spreadMessagePriority: 1,
|
|
105
|
+
formname,
|
|
106
|
+
required,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
status: 'success',
|
|
112
|
+
statusMessage: 'Email is valid.',
|
|
113
|
+
spreadMessage: 'Email is valid.',
|
|
114
|
+
spreadMessagePriority: 1,
|
|
115
|
+
formname,
|
|
116
|
+
required,
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
[]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* handlePasswordErrorCreation function handles validation for password fields.
|
|
124
|
+
* It checks if the password is required, not empty, and meets the complexity requirements.
|
|
125
|
+
* @param formData The form data object containing field values.
|
|
126
|
+
* @param required A boolean indicating whether the password field is required.
|
|
127
|
+
* @param formname The name of the form the password field belongs to.
|
|
128
|
+
* @returns A Promise that resolves to a HelperFooterMessage object with the validation result.
|
|
129
|
+
*/
|
|
130
|
+
const handlePasswordErrorCreation = useCallback(
|
|
131
|
+
async (
|
|
132
|
+
formData: FormData,
|
|
133
|
+
required: boolean,
|
|
134
|
+
formname: string
|
|
135
|
+
): Promise<HelperFooterMessage | undefined> => {
|
|
136
|
+
const password = formData.get('verifyPassword') as string
|
|
137
|
+
|
|
138
|
+
// Always store the password in the cache, even if it's invalid
|
|
139
|
+
await set(
|
|
140
|
+
'verifyPassword',
|
|
141
|
+
{ type: 'string', value: password },
|
|
142
|
+
new Date(Date.now() + 30 * 60 * 1000)
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
if (required && (!password || !password.trim())) {
|
|
146
|
+
return {
|
|
147
|
+
status: 'error',
|
|
148
|
+
statusMessage: 'Password is required.',
|
|
149
|
+
spreadMessage: 'Password is required.',
|
|
150
|
+
spreadMessagePriority: 4,
|
|
151
|
+
formname,
|
|
152
|
+
required,
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!password) {
|
|
157
|
+
return undefined
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const passwordRegex =
|
|
161
|
+
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/
|
|
162
|
+
const passwordComplexityStatus: 'error' | 'success' = passwordRegex.test(
|
|
163
|
+
password
|
|
164
|
+
)
|
|
165
|
+
? 'success'
|
|
166
|
+
: 'error'
|
|
167
|
+
|
|
168
|
+
if (passwordComplexityStatus === 'error') {
|
|
169
|
+
return {
|
|
170
|
+
status: 'error',
|
|
171
|
+
statusMessage:
|
|
172
|
+
'Password must include at least 8 characters, one uppercase letter, one lowercase letter, one number, and one special character.',
|
|
173
|
+
spreadMessage: 'Invalid password.',
|
|
174
|
+
spreadMessagePriority: 1,
|
|
175
|
+
formname,
|
|
176
|
+
required,
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
status: 'success',
|
|
182
|
+
statusMessage: 'Password meets all requirements.',
|
|
183
|
+
spreadMessage: 'Password is valid.',
|
|
184
|
+
spreadMessagePriority: 1,
|
|
185
|
+
formname,
|
|
186
|
+
required,
|
|
187
|
+
}
|
|
188
|
+
},
|
|
189
|
+
[]
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* handleConfirmPasswordErrorCreation function handles validation for confirm password fields.
|
|
194
|
+
* It checks if the confirm password is required, not empty, and matches the original password.
|
|
195
|
+
* @param formData The form data object containing field values.
|
|
196
|
+
* @param required A boolean indicating whether the confirm password field is required.
|
|
197
|
+
* @param formname The name of the form the confirm password field belongs to.
|
|
198
|
+
* @returns A Promise that resolves to a HelperFooterMessage object with the validation result.
|
|
199
|
+
*/
|
|
200
|
+
const handleConfirmPasswordErrorCreation = useCallback(
|
|
201
|
+
async (
|
|
202
|
+
formData: FormData,
|
|
203
|
+
required: boolean,
|
|
204
|
+
formname: string
|
|
205
|
+
): Promise<HelperFooterMessage | undefined> => {
|
|
206
|
+
const confirmPassword = formData.get('confirmPassword') as string
|
|
207
|
+
|
|
208
|
+
if (required && (!confirmPassword || !confirmPassword.trim())) {
|
|
209
|
+
return {
|
|
210
|
+
status: 'error',
|
|
211
|
+
statusMessage: 'Please confirm your password.',
|
|
212
|
+
spreadMessage: 'Password confirmation is required.',
|
|
213
|
+
spreadMessagePriority: 4,
|
|
214
|
+
formname,
|
|
215
|
+
required,
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!confirmPassword) {
|
|
220
|
+
return undefined
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const verifyPassword = await get('verifyPassword')
|
|
224
|
+
|
|
225
|
+
if (!verifyPassword) {
|
|
226
|
+
return {
|
|
227
|
+
status: 'error',
|
|
228
|
+
statusMessage: 'Please enter your password first.',
|
|
229
|
+
spreadMessage: 'Password not set.',
|
|
230
|
+
spreadMessagePriority: 4,
|
|
231
|
+
formname,
|
|
232
|
+
required,
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (confirmPassword !== verifyPassword) {
|
|
237
|
+
return {
|
|
238
|
+
status: 'error',
|
|
239
|
+
statusMessage: 'Passwords do not match.',
|
|
240
|
+
spreadMessage: 'Passwords do not match.',
|
|
241
|
+
spreadMessagePriority: 4,
|
|
242
|
+
formname,
|
|
243
|
+
required,
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
status: 'success',
|
|
249
|
+
statusMessage: 'Passwords match.',
|
|
250
|
+
spreadMessage: 'Passwords match.',
|
|
251
|
+
spreadMessagePriority: 4,
|
|
252
|
+
formname,
|
|
253
|
+
required,
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
[]
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* handlePhoneNumberErrorCreation function handles validation for phone number fields.
|
|
261
|
+
* It checks if the phone number is required, not empty, and in a valid format.
|
|
262
|
+
* @param formData The form data object containing field values.
|
|
263
|
+
* @param required A boolean indicating whether the phone number field is required.
|
|
264
|
+
* @param formname The name of the form the phone number field belongs to.
|
|
265
|
+
* @returns A HelperFooterMessage object with the validation result.
|
|
266
|
+
*/
|
|
267
|
+
const handlePhoneNumberErrorCreation = useCallback(
|
|
268
|
+
(
|
|
269
|
+
formData: FormData,
|
|
270
|
+
required: boolean,
|
|
271
|
+
formname: string
|
|
272
|
+
): HelperFooterMessage | undefined => {
|
|
273
|
+
const phoneNumber = formData.get('phoneNumber') as string
|
|
274
|
+
|
|
275
|
+
if (required && (!phoneNumber || !phoneNumber.trim())) {
|
|
276
|
+
return {
|
|
277
|
+
status: 'error',
|
|
278
|
+
statusMessage:
|
|
279
|
+
'Phone number is required. Please enter a phone number.',
|
|
280
|
+
spreadMessage: 'Phone number is required.',
|
|
281
|
+
spreadMessagePriority: 1,
|
|
282
|
+
formname,
|
|
283
|
+
required,
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (!phoneNumber) {
|
|
288
|
+
return undefined
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const digitsOnly = phoneNumber.replace(/[^\d]/g, '')
|
|
292
|
+
const length = digitsOnly.length
|
|
293
|
+
|
|
294
|
+
if (
|
|
295
|
+
(length === 10 && !digitsOnly.startsWith('1')) ||
|
|
296
|
+
(length === 11 && digitsOnly.startsWith('1'))
|
|
297
|
+
) {
|
|
298
|
+
return {
|
|
299
|
+
status: 'success',
|
|
300
|
+
statusMessage: 'Phone number is valid.',
|
|
301
|
+
spreadMessage: 'Phone number is valid.',
|
|
302
|
+
spreadMessagePriority: 1,
|
|
303
|
+
formname,
|
|
304
|
+
required,
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
return {
|
|
308
|
+
status: 'error',
|
|
309
|
+
statusMessage:
|
|
310
|
+
'Please enter a valid 10-digit phone number or a 10-digit number starting with 1.',
|
|
311
|
+
spreadMessage: 'Invalid phone number format.',
|
|
312
|
+
spreadMessagePriority: 1,
|
|
313
|
+
formname,
|
|
314
|
+
required,
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
[]
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* getHelperFooterOption function returns the appropriate validation function based on the field name.
|
|
323
|
+
* @param name The name of the field being validated.
|
|
324
|
+
* @param label The label of the field being validated.
|
|
325
|
+
* @param required A boolean indicating whether the field is required.
|
|
326
|
+
* @param formname The name of the form the field belongs to.
|
|
327
|
+
* @returns The validation function for the specified field.
|
|
328
|
+
*/
|
|
329
|
+
const getHelperFooterOption = useCallback(
|
|
330
|
+
(name: string, label: string, required: boolean, formname: string) => {
|
|
331
|
+
switch (name) {
|
|
332
|
+
case 'email':
|
|
333
|
+
return (formData: FormData) =>
|
|
334
|
+
handleEmailErrorCreation(formData, required, formname)
|
|
335
|
+
case 'verifyPassword':
|
|
336
|
+
return (formData: FormData) =>
|
|
337
|
+
handlePasswordErrorCreation(formData, required, formname)
|
|
338
|
+
case 'confirmPassword':
|
|
339
|
+
return (formData: FormData) =>
|
|
340
|
+
handleConfirmPasswordErrorCreation(formData, required, formname)
|
|
341
|
+
case 'phoneNumber':
|
|
342
|
+
return (formData: FormData) =>
|
|
343
|
+
handlePhoneNumberErrorCreation(formData, required, formname)
|
|
344
|
+
default:
|
|
345
|
+
return (formData: FormData) =>
|
|
346
|
+
handleGenericErrorCreation(
|
|
347
|
+
formData,
|
|
348
|
+
name,
|
|
349
|
+
label,
|
|
350
|
+
required,
|
|
351
|
+
formname
|
|
352
|
+
)
|
|
353
|
+
}
|
|
354
|
+
},
|
|
355
|
+
[
|
|
356
|
+
handleEmailErrorCreation,
|
|
357
|
+
handlePasswordErrorCreation,
|
|
358
|
+
handleConfirmPasswordErrorCreation,
|
|
359
|
+
handlePhoneNumberErrorCreation,
|
|
360
|
+
handleGenericErrorCreation,
|
|
361
|
+
]
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* validateField function validates a specific form field and updates the helper footer atom.
|
|
366
|
+
* It debounces the validation to prevent excessive updates.
|
|
367
|
+
* @param name The name of the field being validated.
|
|
368
|
+
* @param formData The form data object containing field values.
|
|
369
|
+
* @param label The label of the field being validated.
|
|
370
|
+
* @param required A boolean indicating whether the field is required.
|
|
371
|
+
* @param formname The name of the form the field belongs to.
|
|
372
|
+
*/
|
|
373
|
+
const validateField = useCallback(
|
|
374
|
+
(
|
|
375
|
+
name: string,
|
|
376
|
+
formData: FormData,
|
|
377
|
+
label: string,
|
|
378
|
+
required: boolean,
|
|
379
|
+
formname: string
|
|
380
|
+
) => {
|
|
381
|
+
const helperFooterOption = getHelperFooterOption(
|
|
382
|
+
name,
|
|
383
|
+
label,
|
|
384
|
+
required,
|
|
385
|
+
formname
|
|
386
|
+
)
|
|
387
|
+
if (helperFooterOption) {
|
|
388
|
+
const debouncedHelperFooterOption = debounce(async () => {
|
|
389
|
+
const validationResult = await helperFooterOption(formData)
|
|
390
|
+
setHelperFooterAtomValue(prevState => {
|
|
391
|
+
if (validationResult) {
|
|
392
|
+
return { ...prevState, [name]: validationResult }
|
|
393
|
+
} else {
|
|
394
|
+
const newState = { ...prevState }
|
|
395
|
+
delete newState[name]
|
|
396
|
+
return newState
|
|
397
|
+
}
|
|
398
|
+
})
|
|
399
|
+
}, 300)
|
|
400
|
+
|
|
401
|
+
debouncedHelperFooterOption()
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
[getHelperFooterOption, setHelperFooterAtomValue]
|
|
405
|
+
)
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
helperFooterAtomValue,
|
|
409
|
+
validateField,
|
|
410
|
+
}
|
|
411
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react'
|
|
2
|
+
import { StyledComponentProps } from '..'
|
|
3
|
+
import { styled, Menu, MenuItem } from '@mui/material'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
|
|
6
|
+
const StyledSelectMenu = styled(MenuItem)({
|
|
7
|
+
backgroundColor: 'white',
|
|
8
|
+
color: 'black',
|
|
9
|
+
'&:hover': { backgroundColor: '#63B3DD' },
|
|
10
|
+
'&[aria-selected=true]': {
|
|
11
|
+
color: 'white',
|
|
12
|
+
backgroundColor: '#9CE4F8',
|
|
13
|
+
},
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* useDropdown hook provides functionality for rendering a dropdown menu and handling option selection.
|
|
18
|
+
* @param props The props for the dropdown component.
|
|
19
|
+
* @param inputBoxRef A reference to the input box element.
|
|
20
|
+
* @returns An object containing the necessary state and handlers for the dropdown.
|
|
21
|
+
*/
|
|
22
|
+
export const useDropdown = (
|
|
23
|
+
props: StyledComponentProps,
|
|
24
|
+
inputBoxRef: React.RefObject<HTMLDivElement>
|
|
25
|
+
) => {
|
|
26
|
+
const [isDropdownOpen, setIsDropdownOpen] = useState(false)
|
|
27
|
+
const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
|
|
28
|
+
const [filteredOptions, setFilteredOptions] = useState<string[]>([])
|
|
29
|
+
const [selectedOption, setSelectedOption] = useState(props.defaultValue || '')
|
|
30
|
+
const [isDropdownFocused, setIsDropdownFocused] = useState(false)
|
|
31
|
+
const { componentvariant, options, defaultValue, onChange, value } = props
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* useEffect hook to update the filtered options and handle default value selection.
|
|
35
|
+
*/
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
if (componentvariant === 'dropdown' && options) {
|
|
38
|
+
setFilteredOptions([...options])
|
|
39
|
+
if (defaultValue && options.includes(defaultValue) && !selectedOption) {
|
|
40
|
+
setSelectedOption(defaultValue)
|
|
41
|
+
if (onChange) {
|
|
42
|
+
onChange({
|
|
43
|
+
target: { value: defaultValue },
|
|
44
|
+
} as React.ChangeEvent<HTMLInputElement>)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}, [componentvariant, options, defaultValue, onChange, selectedOption])
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* useEffect hook to update the selected option when the value prop changes.
|
|
52
|
+
*/
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
if (value !== undefined) {
|
|
55
|
+
setSelectedOption(value)
|
|
56
|
+
}
|
|
57
|
+
}, [value])
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* handleDropdownClick function toggles the dropdown menu when the input box is clicked.
|
|
61
|
+
*/
|
|
62
|
+
const handleDropdownClick = useCallback(() => {
|
|
63
|
+
if (componentvariant === 'dropdown') {
|
|
64
|
+
setAnchorEl(inputBoxRef.current)
|
|
65
|
+
setIsDropdownOpen(!isDropdownOpen)
|
|
66
|
+
}
|
|
67
|
+
}, [componentvariant, inputBoxRef, isDropdownOpen])
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* handleOptionSelect function is called when an option is selected from the dropdown menu.
|
|
71
|
+
* It updates the selected option and calls the onChange callback.
|
|
72
|
+
* @param option The selected option.
|
|
73
|
+
*/
|
|
74
|
+
const handleOptionSelect = useCallback(
|
|
75
|
+
(option: string) => {
|
|
76
|
+
setSelectedOption(option)
|
|
77
|
+
if (onChange) {
|
|
78
|
+
onChange({
|
|
79
|
+
target: { value: option },
|
|
80
|
+
} as React.ChangeEvent<HTMLInputElement>)
|
|
81
|
+
}
|
|
82
|
+
setIsDropdownOpen(false)
|
|
83
|
+
},
|
|
84
|
+
[onChange]
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* handleInputFocus function updates the isDropdownFocused state when the input box is focused or blurred.
|
|
89
|
+
* @param focused A boolean indicating whether the input box is focused.
|
|
90
|
+
*/
|
|
91
|
+
const handleInputFocus = useCallback(
|
|
92
|
+
(focused: boolean) => {
|
|
93
|
+
if (componentvariant === 'dropdown') {
|
|
94
|
+
setIsDropdownFocused(focused)
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
[componentvariant]
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
if (componentvariant !== 'dropdown') {
|
|
101
|
+
return {}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* renderMenu variable renders the dropdown menu when the anchor element is available and the dropdown is open.
|
|
106
|
+
*/
|
|
107
|
+
const renderMenu = anchorEl !== null && isDropdownOpen !== undefined && (
|
|
108
|
+
<Menu
|
|
109
|
+
anchorEl={anchorEl}
|
|
110
|
+
open={isDropdownOpen}
|
|
111
|
+
onClose={() => setIsDropdownOpen(false)}
|
|
112
|
+
PaperProps={{
|
|
113
|
+
style: {
|
|
114
|
+
minWidth: inputBoxRef.current?.offsetWidth,
|
|
115
|
+
},
|
|
116
|
+
}}
|
|
117
|
+
>
|
|
118
|
+
{filteredOptions && filteredOptions.length > 0 ? (
|
|
119
|
+
filteredOptions.map((option: string) => (
|
|
120
|
+
<StyledSelectMenu
|
|
121
|
+
key={option}
|
|
122
|
+
onClick={() => handleOptionSelect(option)}
|
|
123
|
+
>
|
|
124
|
+
{option}
|
|
125
|
+
</StyledSelectMenu>
|
|
126
|
+
))
|
|
127
|
+
) : (
|
|
128
|
+
<div>No Options Found</div>
|
|
129
|
+
)}
|
|
130
|
+
</Menu>
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
renderMenu,
|
|
135
|
+
isDropdownOpen,
|
|
136
|
+
anchorEl,
|
|
137
|
+
filteredOptions,
|
|
138
|
+
handleDropdownClick,
|
|
139
|
+
selectedOption,
|
|
140
|
+
handleOptionSelect,
|
|
141
|
+
isDropdownFocused,
|
|
142
|
+
handleInputFocus,
|
|
143
|
+
}
|
|
144
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* usePassword hook provides functionality for managing password visibility state.
|
|
5
|
+
* It allows toggling the visibility of the password field between masked and unmasked states.
|
|
6
|
+
* @returns An object containing the passwordVisible state and the togglePasswordVisibility function.
|
|
7
|
+
*/
|
|
8
|
+
export const usePassword = () => {
|
|
9
|
+
const [passwordVisible, setPasswordVisible] = useState(false)
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* togglePasswordVisibility function toggles the visibility state of the password field.
|
|
13
|
+
* It switches between the masked and unmasked states.
|
|
14
|
+
*/
|
|
15
|
+
const togglePasswordVisibility = () => {
|
|
16
|
+
setPasswordVisible(!passwordVisible)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return {
|
|
20
|
+
passwordVisible,
|
|
21
|
+
togglePasswordVisibility,
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { StyledComponentProps } from '..'
|
|
2
|
+
import React, { useCallback } from 'react'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* formatPhoneNumber function formats a given string of digits into a standard US phone number format.
|
|
6
|
+
* It ensures the number starts with "+1" and adds the appropriate dashes for area code and subsequent digits.
|
|
7
|
+
* @param value The string of digits to format.
|
|
8
|
+
* @returns The formatted phone number string.
|
|
9
|
+
*/
|
|
10
|
+
export const formatPhoneNumber = (value: string): string => {
|
|
11
|
+
// Remove all non-digit characters
|
|
12
|
+
const digits = value.replace(/\D/g, '')
|
|
13
|
+
|
|
14
|
+
// Ensure the number starts with +1
|
|
15
|
+
let formattedNumber = '+1 '
|
|
16
|
+
|
|
17
|
+
if (digits.length > 0) {
|
|
18
|
+
// Add the area code
|
|
19
|
+
formattedNumber += digits.slice(0, 3)
|
|
20
|
+
|
|
21
|
+
if (digits.length > 3) {
|
|
22
|
+
// Add first dash and next three digits
|
|
23
|
+
formattedNumber += '-' + digits.slice(3, 6)
|
|
24
|
+
|
|
25
|
+
if (digits.length > 6) {
|
|
26
|
+
// Add second dash and last four digits
|
|
27
|
+
formattedNumber += '-' + digits.slice(6, 10)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return formattedNumber
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* usePhoneNumber hook provides functionality for handling phone number input changes.
|
|
37
|
+
* It formats the input value using the formatPhoneNumber function and calls the onChange callback with the formatted value.
|
|
38
|
+
* @param props The props for the phone number input component.
|
|
39
|
+
* @returns An object containing the handlePhoneNumberChange function.
|
|
40
|
+
*/
|
|
41
|
+
export const usePhoneNumber = (
|
|
42
|
+
props: StyledComponentProps & {
|
|
43
|
+
onChange?: (
|
|
44
|
+
event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
|
|
45
|
+
) => void
|
|
46
|
+
}
|
|
47
|
+
) => {
|
|
48
|
+
/**
|
|
49
|
+
* handlePhoneNumberChange function is called when the phone number input value changes.
|
|
50
|
+
* It formats the input value using the formatPhoneNumber function and calls the onChange callback with the formatted value.
|
|
51
|
+
* @param e The change event triggered by the phone number input.
|
|
52
|
+
*/
|
|
53
|
+
const handlePhoneNumberChange = useCallback(
|
|
54
|
+
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
|
55
|
+
const input = e.target.value
|
|
56
|
+
const digitsOnly = input.replace(/\D/g, '')
|
|
57
|
+
const formattedValue = formatPhoneNumber(digitsOnly)
|
|
58
|
+
|
|
59
|
+
if (props.onChange) {
|
|
60
|
+
props.onChange({
|
|
61
|
+
...e,
|
|
62
|
+
target: {
|
|
63
|
+
...e.target,
|
|
64
|
+
value: formattedValue,
|
|
65
|
+
},
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
[props]
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
handlePhoneNumberChange,
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { useState } from 'react'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
interface UseSearchbarProps {
|
|
5
|
+
options: readonly string[]
|
|
6
|
+
defaultValue?: string
|
|
7
|
+
onChange: (filteredOptions: string[], searchQuery: string) => void
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* useSearchbar hook provides functionality for handling searchbar input and filtering options.
|
|
12
|
+
* It maintains the input value state and calls the onChange callback with the filtered options and search query.
|
|
13
|
+
* @param props The props for the useSearchbar hook.
|
|
14
|
+
* @returns An object containing the inputValue state and the handleOnChange function.
|
|
15
|
+
*/
|
|
16
|
+
export const useSearchbar = ({
|
|
17
|
+
options,
|
|
18
|
+
defaultValue = '',
|
|
19
|
+
onChange,
|
|
20
|
+
}: UseSearchbarProps) => {
|
|
21
|
+
const [inputValue, setInputValue] = useState(defaultValue)
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* handleOnChange function is called when the searchbar input value changes.
|
|
25
|
+
* It updates the inputValue state and filters the options based on the search query.
|
|
26
|
+
* It calls the onChange callback with the filtered options and the search query.
|
|
27
|
+
* @param event The change event triggered by the searchbar input.
|
|
28
|
+
*/
|
|
29
|
+
const handleOnChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
30
|
+
const searchQuery = event.target.value
|
|
31
|
+
setInputValue(searchQuery)
|
|
32
|
+
|
|
33
|
+
const filteredOptions = options.filter(option =>
|
|
34
|
+
option.toLowerCase().includes(searchQuery.toLowerCase())
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
onChange(filteredOptions, searchQuery)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
inputValue,
|
|
42
|
+
handleOnChange,
|
|
43
|
+
}
|
|
44
|
+
}
|