goobs-frontend 0.7.67 → 0.7.69

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.
Files changed (42) hide show
  1. package/package.json +14 -16
  2. package/src/app/_app.tsx +8 -8
  3. package/src/components/Button/index.tsx +95 -203
  4. package/src/components/ConfirmationCodeInput/index.tsx +4 -6
  5. package/src/components/Content/Structure/button/useButton.tsx +1 -35
  6. package/src/components/Content/Structure/datefield/useDateField.tsx +55 -0
  7. package/src/components/Content/Structure/dropdown/useDropdown.tsx +55 -0
  8. package/src/components/Content/Structure/incremementNumberField/useIncremementNumberField.tsx +65 -0
  9. package/src/components/Content/Structure/numberField/useNumberField.tsx +55 -0
  10. package/src/components/Content/Structure/passwordField/usePasswordField.tsx +57 -0
  11. package/src/components/Content/Structure/phoneNumber/usePhoneNumber.tsx +0 -0
  12. package/src/components/Content/Structure/qrcode/useQRCode.tsx +69 -0
  13. package/src/components/Content/Structure/searchbar/useSearchbar.tsx +55 -0
  14. package/src/components/Content/Structure/textfield/useTextField.tsx +84 -0
  15. package/src/components/Content/index.tsx +44 -7
  16. package/src/components/DateField/index.tsx +112 -0
  17. package/src/components/Dropdown/index.tsx +91 -0
  18. package/src/components/Form/Popup/index.tsx +1 -1
  19. package/src/components/IncrementNumberField/index.tsx +123 -0
  20. package/src/components/Nav/HorizontalVariant/index.tsx +18 -12
  21. package/src/components/Nav/VerticalVariant/index.tsx +14 -10
  22. package/src/components/NumberField/index.tsx +95 -0
  23. package/src/components/PasswordField/index.tsx +105 -0
  24. package/src/components/PhoneNumberField/index.tsx +102 -0
  25. package/src/components/PricingTable/index.tsx +10 -8
  26. package/src/components/QRCode/index.tsx +105 -0
  27. package/src/components/Searchbar/index.tsx +77 -0
  28. package/src/components/TextField/index.tsx +130 -0
  29. package/src/components/Toolbar/index.tsx +11 -10
  30. package/src/index.ts +54 -9
  31. package/src/components/Button/hook/useHelperFooter.tsx +0 -214
  32. package/src/components/Content/Structure/styledcomponent/useStyledComponent.tsx +0 -104
  33. package/src/components/StyledComponent/adornment/index.tsx +0 -125
  34. package/src/components/StyledComponent/hooks/useDropdown.tsx +0 -150
  35. package/src/components/StyledComponent/hooks/useInputHelperFooter.tsx +0 -524
  36. package/src/components/StyledComponent/hooks/usePhoneNumber.tsx +0 -99
  37. package/src/components/StyledComponent/hooks/useRequiredFieldsValidator.tsx +0 -190
  38. package/src/components/StyledComponent/hooks/useSearchbar.tsx +0 -46
  39. package/src/components/StyledComponent/hooks/useSplitButton.tsx +0 -70
  40. package/src/components/StyledComponent/index.tsx +0 -473
  41. package/src/components/StyledComponent/useCallbacks/index.tsx +0 -46
  42. package/src/styles/StyledComponent/Label/index.ts +0 -76
@@ -1,473 +0,0 @@
1
- 'use client'
2
-
3
- import React, { useRef, useCallback, useMemo } from 'react'
4
- import { Box, InputLabel, OutlinedInput, styled } from '@mui/material'
5
- import { session } from 'goobs-cache'
6
- import { useDropdown } from './hooks/useDropdown'
7
- import { usePhoneNumber } from './hooks/usePhoneNumber'
8
- import { useSplitButton } from './hooks/useSplitButton'
9
- import { Typography } from './../Typography'
10
- import { red, green } from '../../styles/palette'
11
- import { StartAdornment, EndAdornment } from './adornment'
12
- import {
13
- useInputHelperFooter,
14
- HelperFooterMessage,
15
- } from './hooks/useInputHelperFooter'
16
- import { useRequiredFieldsValidator } from './hooks/useRequiredFieldsValidator'
17
- import labelStyles from '../../styles/StyledComponent/Label'
18
- import { usePreventAutocomplete } from './useCallbacks'
19
- import { ClientLogger } from 'goobs-testing'
20
-
21
- export interface StyledComponentProps
22
- extends React.ComponentPropsWithoutRef<'div'> {
23
- name?: string
24
- outlinecolor?: string
25
- iconcolor?: string
26
- backgroundcolor?: string
27
- notched?: boolean
28
- combinedfontcolor?: string
29
- unshrunkfontcolor?: string
30
- shrunkfontcolor?: string
31
- autoComplete?: string
32
- componentvariant?:
33
- | 'multilinetextfield'
34
- | 'dropdown'
35
- | 'searchbar'
36
- | 'textfield'
37
- | 'phonenumber'
38
- | 'password'
39
- | 'ip-address'
40
- | 'email'
41
- | 'url'
42
- | 'credit-card'
43
- | 'number'
44
- | 'hostname'
45
- | 'domain'
46
- | 'time'
47
- | 'date'
48
- | 'splitbutton'
49
- options?: readonly string[]
50
- defaultOption?: string
51
- helperfooter?: HelperFooterMessage
52
- placeholder?: string
53
- minRows?: number
54
- formname?: string
55
- label?: string
56
- shrunklabellocation?: 'onnotch' | 'above'
57
- value?: string
58
- valuestatus?: boolean
59
- focused?: boolean
60
- required?: boolean
61
- formSubmitted?: boolean
62
- 'aria-label'?: string
63
- 'aria-required'?: boolean
64
- 'aria-invalid'?: boolean
65
- 'aria-describedby'?: string
66
- onOptionSelect?: (option: string) => void
67
- onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
68
- spreadMessagePriority?: number
69
- }
70
-
71
- const NoAutofillOutlinedInput = styled(OutlinedInput)(() => ({
72
- '& .MuiInputBase-input': {
73
- '&:-webkit-autofill': {
74
- transition: 'background-color 600000s 0s, color 600000s 0s',
75
- },
76
- '&:-webkit-autofill:hover': {
77
- transition: 'background-color 600000s 0s, color 600000s 0s',
78
- },
79
- '&:-webkit-autofill:focus': {
80
- transition: 'background-color 600000s 0s, color 600000s 0s',
81
- },
82
- '&:-webkit-autofill:active': {
83
- transition: 'background-color 600000s 0s, color 600000s 0s',
84
- },
85
- },
86
- }))
87
-
88
- const StyledComponent: React.FC<StyledComponentProps> = React.memo(props => {
89
- ClientLogger.debug('StyledComponent render', { props })
90
-
91
- const {
92
- label,
93
- componentvariant,
94
- name,
95
- backgroundcolor,
96
- iconcolor,
97
- unshrunkfontcolor,
98
- combinedfontcolor,
99
- shrunkfontcolor,
100
- shrunklabellocation,
101
- value,
102
- placeholder,
103
- formname,
104
- formSubmitted = false,
105
- 'aria-label': ariaLabel,
106
- 'aria-required': ariaRequired,
107
- 'aria-invalid': ariaInvalid,
108
- 'aria-describedby': ariaDescribedBy,
109
- onOptionSelect,
110
- onChange,
111
- spreadMessagePriority,
112
- minRows,
113
- ...restProps
114
- } = props
115
-
116
- const helperFooterAtom = useMemo(
117
- () => session.atom<Record<string, HelperFooterMessage>>({}),
118
- []
119
- )
120
- const [helperFooters] = session.useAtom(helperFooterAtom)
121
- const { validateField, useShowErrorEffect } =
122
- useInputHelperFooter(helperFooterAtom)
123
-
124
- const inputValueAtom = useMemo(() => session.atom(value || ''), [value])
125
- const [inputValue, setInputValue] = session.useAtom(inputValueAtom)
126
- const hasInput = useMemo(() => !!inputValue, [inputValue])
127
- const hasInputRef = useRef(hasInput)
128
- hasInputRef.current = hasInput
129
-
130
- const memoizedRequiredFieldsProps = useMemo(() => [props], [props])
131
- useRequiredFieldsValidator(
132
- formname || '',
133
- memoizedRequiredFieldsProps,
134
- hasInputRef,
135
- helperFooterAtom
136
- )
137
-
138
- const isFocusedAtom = useMemo(() => session.atom(false), [])
139
- const [isFocused, setIsFocused] = session.useAtom(isFocusedAtom)
140
- const passwordVisibleAtom = useMemo(() => session.atom(false), [])
141
- const [passwordVisible, setPasswordVisible] =
142
- session.useAtom(passwordVisibleAtom)
143
- const inputRefInternal = useRef<HTMLInputElement | null>(null)
144
- const inputBoxRef = useRef<HTMLDivElement>(null)
145
-
146
- const memoizedDropdownProps = useMemo(
147
- () => ({
148
- componentvariant,
149
- options: props.options,
150
- value: inputValue,
151
- defaultOption: props.defaultOption,
152
- }),
153
- [componentvariant, props.options, inputValue, props.defaultOption]
154
- )
155
-
156
- const {
157
- renderMenu,
158
- selectedOption,
159
- handleDropdownClick,
160
- updateDropdownState,
161
- } = useDropdown(memoizedDropdownProps, inputBoxRef, onOptionSelect)
162
-
163
- const { phoneNumber, handlePhoneNumberChange, checkAndUpdatePhoneNumber } =
164
- usePhoneNumber(inputValue, componentvariant)
165
-
166
- const memoizedSplitButtonProps = useMemo(
167
- () => ({ value: inputValue }),
168
- [inputValue]
169
- )
170
- const {
171
- value: splitButtonValue,
172
- handleIncrement,
173
- handleDecrement,
174
- updateValueFromProps,
175
- } = useSplitButton(memoizedSplitButtonProps)
176
-
177
- const setInputAttributes = usePreventAutocomplete()
178
-
179
- const inputRef = useCallback(
180
- (node: HTMLInputElement | null) => {
181
- inputRefInternal.current = node
182
- if (node) {
183
- setInputAttributes(node)
184
- }
185
- },
186
- [setInputAttributes]
187
- )
188
-
189
- const showError = useShowErrorEffect(formSubmitted, hasInput, isFocused)
190
-
191
- const handleChange = useCallback(
192
- (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
193
- ClientLogger.debug('handleChange', {
194
- componentvariant,
195
- value: e.target.value,
196
- name: e.target.name,
197
- })
198
-
199
- setInputValue(e.target.value)
200
-
201
- if (componentvariant === 'phonenumber') {
202
- handlePhoneNumberChange(e)
203
- if (onChange) {
204
- onChange(e as React.ChangeEvent<HTMLInputElement>)
205
- }
206
- } else if (componentvariant === 'splitbutton') {
207
- const numValue = e.target.value.replace(/[^0-9]/g, '')
208
- e.target.value = numValue
209
- if (onChange) {
210
- onChange(e as React.ChangeEvent<HTMLInputElement>)
211
- }
212
- } else if (onChange) {
213
- onChange(e as React.ChangeEvent<HTMLInputElement>)
214
- }
215
-
216
- const newHasInput = !!e.target.value
217
- hasInputRef.current = newHasInput
218
-
219
- const formData = new FormData()
220
- formData.append(e.target.name, e.target.value)
221
- if (name && label && formname) {
222
- validateField(name, formData, label, formname, spreadMessagePriority)
223
- }
224
-
225
- if (componentvariant === 'dropdown') {
226
- updateDropdownState()
227
- } else if (componentvariant === 'phonenumber') {
228
- checkAndUpdatePhoneNumber()
229
- } else if (componentvariant === 'splitbutton') {
230
- updateValueFromProps()
231
- }
232
- },
233
- [
234
- componentvariant,
235
- handlePhoneNumberChange,
236
- onChange,
237
- name,
238
- label,
239
- formname,
240
- validateField,
241
- spreadMessagePriority,
242
- updateDropdownState,
243
- checkAndUpdatePhoneNumber,
244
- updateValueFromProps,
245
- setInputValue,
246
- ]
247
- )
248
-
249
- const handleFocus = useCallback(() => {
250
- ClientLogger.debug('handleFocus')
251
- setIsFocused(true)
252
- }, [setIsFocused])
253
-
254
- const handleBlur = useCallback(() => {
255
- ClientLogger.debug('handleBlur', {
256
- name,
257
- label,
258
- hasInput: hasInputRef.current,
259
- formname,
260
- })
261
- setIsFocused(false)
262
- if (name && label && !hasInputRef.current && formname) {
263
- const formData = new FormData()
264
- formData.append(name, '')
265
- validateField(name, formData, label, formname, spreadMessagePriority)
266
- }
267
- }, [
268
- name,
269
- label,
270
- formname,
271
- validateField,
272
- spreadMessagePriority,
273
- setIsFocused,
274
- ])
275
-
276
- const togglePasswordVisibility = useCallback(() => {
277
- setPasswordVisible(prev => {
278
- ClientLogger.debug('togglePasswordVisibility', { passwordVisible: !prev })
279
- return !prev
280
- })
281
- }, [setPasswordVisible])
282
-
283
- const isDropdownVariant = useMemo(
284
- () => componentvariant === 'dropdown',
285
- [componentvariant]
286
- )
287
- const isSplitButtonVariant = useMemo(
288
- () => componentvariant === 'splitbutton',
289
- [componentvariant]
290
- )
291
- const isNotchedVariant = useMemo(
292
- () =>
293
- !isDropdownVariant &&
294
- !isSplitButtonVariant &&
295
- shrunklabellocation !== 'above' &&
296
- !!label,
297
- [isDropdownVariant, isSplitButtonVariant, shrunklabellocation, label]
298
- )
299
- const hasPlaceholder = useMemo(() => !!placeholder, [placeholder])
300
-
301
- const shouldShrinkLabel = useMemo(
302
- () => !!inputValue || isFocused || hasPlaceholder,
303
- [inputValue, isFocused, hasPlaceholder]
304
- )
305
-
306
- const shouldNotch = useMemo(
307
- () => shouldShrinkLabel && isNotchedVariant,
308
- [shouldShrinkLabel, isNotchedVariant]
309
- )
310
-
311
- ClientLogger.debug('Rendering StyledComponent', {
312
- isDropdownVariant,
313
- isSplitButtonVariant,
314
- isNotchedVariant,
315
- hasPlaceholder,
316
- shouldShrinkLabel,
317
- shouldNotch,
318
- componentvariant,
319
- name,
320
- label,
321
- inputValue,
322
- phoneNumber,
323
- selectedOption,
324
- splitButtonValue,
325
- showError,
326
- currentHelperFooter: name ? helperFooters[name]?.statusMessage : undefined,
327
- })
328
-
329
- return (
330
- <Box
331
- {...restProps}
332
- sx={{
333
- boxSizing: 'border-box',
334
- display: 'flex',
335
- flexDirection: 'column',
336
- alignItems: 'flex-start',
337
- width: '100%',
338
- position: 'relative',
339
- margin: 0,
340
- padding: 0,
341
- overflow: 'hidden',
342
- }}
343
- >
344
- <Box
345
- sx={{
346
- position: 'relative',
347
- width: '100%',
348
- paddingTop: shrunklabellocation === 'above' ? '20px' : '5px',
349
- }}
350
- >
351
- {label && (
352
- <InputLabel
353
- style={labelStyles({
354
- componentvariant,
355
- unshrunkfontcolor,
356
- shrunkfontcolor,
357
- shrunklabellocation,
358
- combinedfontcolor,
359
- focused: shouldShrinkLabel,
360
- })}
361
- shrink={shouldShrinkLabel}
362
- htmlFor={name}
363
- >
364
- {label}
365
- </InputLabel>
366
- )}
367
- <Box ref={inputBoxRef} sx={{ width: '100%' }}>
368
- <NoAutofillOutlinedInput
369
- inputRef={inputRef}
370
- minRows={minRows}
371
- style={{
372
- backgroundColor: backgroundcolor || 'inherit',
373
- width: '100%',
374
- height: 40,
375
- cursor: isSplitButtonVariant
376
- ? 'default'
377
- : isDropdownVariant
378
- ? 'pointer'
379
- : 'text',
380
- boxSizing: 'border-box',
381
- borderRadius: 5,
382
- marginTop: 'auto',
383
- paddingRight: 6,
384
- }}
385
- inputProps={{
386
- style: {
387
- width: '100%',
388
- color: combinedfontcolor || unshrunkfontcolor || 'inherit',
389
- height: '100%',
390
- cursor: isSplitButtonVariant
391
- ? 'default'
392
- : isDropdownVariant
393
- ? 'pointer'
394
- : 'text',
395
- },
396
- placeholder: placeholder || '',
397
- 'aria-label': ariaLabel,
398
- 'aria-invalid': ariaInvalid,
399
- 'aria-required': ariaRequired,
400
- 'aria-describedby':
401
- ariaDescribedBy || (name && helperFooters[name]?.statusMessage)
402
- ? `${name}-helper-text`
403
- : undefined,
404
- }}
405
- type={
406
- componentvariant === 'password' && !passwordVisible
407
- ? 'password'
408
- : 'text'
409
- }
410
- startAdornment={
411
- <StartAdornment
412
- componentvariant={componentvariant || ''}
413
- iconcolor={iconcolor}
414
- />
415
- }
416
- endAdornment={
417
- <EndAdornment
418
- componentvariant={componentvariant || ''}
419
- passwordVisible={passwordVisible}
420
- togglePasswordVisibility={togglePasswordVisibility}
421
- iconcolor={iconcolor}
422
- handleIncrement={handleIncrement}
423
- handleDecrement={handleDecrement}
424
- />
425
- }
426
- onChange={handleChange}
427
- onFocus={handleFocus}
428
- onBlur={handleBlur}
429
- onClick={isDropdownVariant ? handleDropdownClick : undefined}
430
- fullWidth
431
- multiline={componentvariant === 'multilinetextfield'}
432
- label={label}
433
- autoComplete="off"
434
- name={name}
435
- value={
436
- componentvariant === 'phonenumber'
437
- ? phoneNumber
438
- : isDropdownVariant
439
- ? selectedOption
440
- : isSplitButtonVariant
441
- ? splitButtonValue
442
- : inputValue
443
- }
444
- readOnly={isDropdownVariant}
445
- notched={shouldNotch}
446
- />
447
- {isDropdownVariant && renderMenu}
448
- </Box>
449
- </Box>
450
- {showError && name && helperFooters[name]?.statusMessage && (
451
- <Typography
452
- id={`${name}-helper-text`}
453
- fontvariant="merrihelperfooter"
454
- fontcolor={
455
- helperFooters[name]?.status === 'error'
456
- ? red.main
457
- : helperFooters[name]?.status === 'success'
458
- ? green.dark
459
- : undefined
460
- }
461
- marginTop={0.5}
462
- marginBottom={0}
463
- align="left"
464
- text={helperFooters[name]?.statusMessage}
465
- />
466
- )}
467
- </Box>
468
- )
469
- })
470
-
471
- StyledComponent.displayName = 'StyledComponent'
472
-
473
- export default StyledComponent
@@ -1,46 +0,0 @@
1
- import { useCallback } from 'react'
2
-
3
- /**
4
- * Custom hook that tracks whether an input field has a value or a specific status.
5
- *
6
- * @param {string | undefined} value - The current value of the input field.
7
- * @param {boolean | undefined} valuestatus - A boolean status associated with the input field.
8
- *
9
- * @returns {boolean} - Whether the input field has a value or a specific status.
10
- *
11
- * @example
12
- * const hasInput = useHasInput(inputValue, inputStatus);
13
- */
14
- export const useHasInput = (
15
- value: string | undefined,
16
- valuestatus: boolean | undefined
17
- ): boolean => {
18
- return !!value || !!valuestatus
19
- }
20
-
21
- /**
22
- * Custom hook that prevents autocomplete, autocorrect, autocapitalize, and spellcheck on an input field.
23
- * This is particularly useful for password fields or other sensitive inputs.
24
- *
25
- * @param {React.RefObject<HTMLInputElement>} inputRefInternal - React ref object for the input element.
26
- *
27
- * @returns {(input: HTMLInputElement | null) => void} - A callback function to set the attributes on the input element.
28
- *
29
- * @example
30
- * const inputRef = useRef<HTMLInputElement>(null);
31
- * const setInputAttributes = usePreventAutocomplete();
32
- * // In your JSX:
33
- * <input ref={el => { inputRef.current = el; setInputAttributes(el); }} ... />
34
- */
35
- export const usePreventAutocomplete = (): ((
36
- input: HTMLInputElement | null
37
- ) => void) => {
38
- return useCallback((input: HTMLInputElement | null) => {
39
- if (input) {
40
- input.setAttribute('autocomplete', 'new-password')
41
- input.setAttribute('autocorrect', 'off')
42
- input.setAttribute('autocapitalize', 'none')
43
- input.setAttribute('spellcheck', 'false')
44
- }
45
- }, [])
46
- }
@@ -1,76 +0,0 @@
1
- import { StyledComponentProps } from '../../../components/StyledComponent'
2
- import React from 'react'
3
-
4
- /**
5
- * labelStyles function generates the styles for the input label based on the provided props.
6
- * It adjusts the label's position, color, and transform based on the component variant, font colors, and focus state.
7
- * @param props The props for the labelStyles function, including componentvariant, unshrunkfontcolor, shrunkfontcolor, combinedfontcolor, shrunklabellocation, and focused.
8
- * @returns The styles for the input label.
9
- */
10
- const labelStyles = (
11
- props: Pick<
12
- StyledComponentProps,
13
- | 'componentvariant'
14
- | 'unshrunkfontcolor'
15
- | 'shrunkfontcolor'
16
- | 'combinedfontcolor'
17
- | 'shrunklabellocation'
18
- | 'focused'
19
- >
20
- ): React.CSSProperties => {
21
- const {
22
- componentvariant,
23
- unshrunkfontcolor,
24
- shrunkfontcolor,
25
- combinedfontcolor,
26
- shrunklabellocation = 'onnotch',
27
- focused,
28
- } = props
29
-
30
- /**
31
- * unshrunkStyles object contains the styles for the unshrunk label state.
32
- */
33
- const unshrunkStyles: React.CSSProperties = {
34
- color: combinedfontcolor || unshrunkfontcolor || 'black',
35
- transform: 'scale(1)',
36
- transformOrigin: 'top left',
37
- top: '13px',
38
- left: '12px',
39
- ...(componentvariant === 'searchbar' && {
40
- transform: 'translate(25px, 2px) scale(1)',
41
- }),
42
- }
43
-
44
- /**
45
- * shrunkStyles object contains the styles for the shrunk label state.
46
- */
47
- const shrunkStyles: React.CSSProperties = {
48
- color: combinedfontcolor || shrunkfontcolor || 'black',
49
- transform: 'scale(0.75)',
50
- transformOrigin: 'top left',
51
- ...(shrunklabellocation === 'onnotch' && {
52
- top: '-4px',
53
- left: '12px',
54
- }),
55
- ...(shrunklabellocation === 'above' && {
56
- top: '3px',
57
- left: '0',
58
- }),
59
- }
60
-
61
- /**
62
- * Return the combined styles for the label, including the base styles, unshrunk styles, and shrunk styles if focused.
63
- */
64
- return {
65
- position: 'absolute',
66
- top: 0,
67
- left: 0,
68
- height: 'auto',
69
- padding: '0 4px',
70
- minHeight: '20px',
71
- ...unshrunkStyles,
72
- ...(focused && shrunkStyles),
73
- }
74
- }
75
-
76
- export default labelStyles