goobs-frontend 0.8.7 → 0.8.9
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 +15 -15
- package/src/components/Button/index.tsx +91 -109
- package/src/components/Checkbox/index.tsx +79 -0
- package/src/components/ConfirmationCodeInput/index.tsx +120 -46
- package/src/components/Content/Structure/checkbox/useCheckbox.tsx +65 -0
- package/src/components/Content/Structure/typography/useGridTypography.tsx +2 -0
- package/src/components/Content/index.tsx +5 -0
- package/src/components/DataGrid/Footer/index.tsx +169 -0
- package/src/components/DataGrid/Jotai/atom.ts +91 -0
- package/src/components/DataGrid/ManageColumn/index.tsx +211 -0
- package/src/components/DataGrid/ManageRow/index.tsx +348 -0
- package/src/components/DataGrid/Table/index.tsx +275 -0
- package/src/components/DataGrid/VerticalDivider/index.tsx +6 -0
- package/src/components/DataGrid/index.tsx +296 -0
- package/src/components/DataGrid/utils/useManageColumn.tsx +138 -0
- package/src/components/DataGrid/utils/useSearchbar.tsx +122 -0
- package/src/components/Dropdown/index.tsx +63 -14
- package/src/components/Form/DataGrid/index.tsx +72 -0
- package/src/components/Form/Popup/index.tsx +30 -63
- package/src/components/Grid/index.tsx +83 -101
- package/src/components/Nav/VerticalVariant/index.tsx +14 -10
- package/src/components/Searchbar/index.tsx +98 -42
- package/src/components/TextField/index.tsx +0 -1
- package/src/components/Toolbar/index.tsx +37 -19
- package/src/components/Typography/index.tsx +8 -4
- package/src/index.ts +16 -1
- package/src/components/ConfirmationCodeInput/utils/useCodeConfirmation.tsx +0 -150
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "goobs-frontend",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.9",
|
|
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",
|
|
@@ -22,31 +22,31 @@
|
|
|
22
22
|
"@emotion/cache": "^11.13.5",
|
|
23
23
|
"@emotion/react": "^11.13.5",
|
|
24
24
|
"@emotion/styled": "^11.13.5",
|
|
25
|
-
"@mui/icons-material": "^6.1.
|
|
26
|
-
"@mui/material": "^6.1.
|
|
25
|
+
"@mui/icons-material": "^6.1.10",
|
|
26
|
+
"@mui/material": "^6.1.10",
|
|
27
27
|
"@types/lodash": "^4.17.13",
|
|
28
28
|
"highlight.js": "^11.10.0",
|
|
29
29
|
"jotai": "^2.10.3",
|
|
30
30
|
"lodash": "^4.17.21",
|
|
31
|
-
"next": "15.0.
|
|
31
|
+
"next": "15.0.4",
|
|
32
32
|
"otplib": "^12.0.1",
|
|
33
33
|
"react-datepicker": "^7.5.0",
|
|
34
34
|
"react-qr-code": "^2.0.15"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@next/eslint-plugin-next": "^15.0.
|
|
38
|
-
"@types/node": "^22.10.
|
|
39
|
-
"@types/react": "
|
|
40
|
-
"@types/react-dom": "^
|
|
41
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
42
|
-
"@typescript-eslint/parser": "^8.
|
|
43
|
-
"eslint": "^9.
|
|
44
|
-
"eslint-config-next": "^15.0.
|
|
37
|
+
"@next/eslint-plugin-next": "^15.0.4",
|
|
38
|
+
"@types/node": "^22.10.1",
|
|
39
|
+
"@types/react": "19.0.1",
|
|
40
|
+
"@types/react-dom": "^19.0.1",
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^8.17.0",
|
|
42
|
+
"@typescript-eslint/parser": "^8.17.0",
|
|
43
|
+
"eslint": "^9.16.0",
|
|
44
|
+
"eslint-config-next": "^15.0.4",
|
|
45
45
|
"eslint-config-prettier": "^9.1.0",
|
|
46
46
|
"eslint-plugin-prettier": "^5.2.1",
|
|
47
|
-
"prettier": "^3.4.
|
|
48
|
-
"react": "^
|
|
49
|
-
"react-dom": "^
|
|
47
|
+
"prettier": "^3.4.2",
|
|
48
|
+
"react": "^19.0.0",
|
|
49
|
+
"react-dom": "^19.0.0",
|
|
50
50
|
"typescript": "^5.7.2"
|
|
51
51
|
},
|
|
52
52
|
"files": [
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
import React from 'react'
|
|
3
|
-
import { Button, Box, ButtonProps } from '@mui/material'
|
|
3
|
+
import { Button, Box, ButtonProps, SxProps, Theme } from '@mui/material'
|
|
4
4
|
import Typography from '../Typography'
|
|
5
|
+
import { SvgIconProps } from '@mui/material/SvgIcon'
|
|
5
6
|
|
|
6
7
|
export interface CustomButtonProps extends ButtonProps {
|
|
7
8
|
text?: string
|
|
@@ -9,131 +10,112 @@ export interface CustomButtonProps extends ButtonProps {
|
|
|
9
10
|
fontcolor?: string
|
|
10
11
|
fontvariant?: 'merriparagraph' | 'merrihelperfooter'
|
|
11
12
|
width?: string
|
|
13
|
+
height?: string
|
|
12
14
|
disableButton?: 'true' | 'false'
|
|
13
|
-
icon?: React.
|
|
15
|
+
icon?: React.ReactElement<SvgIconProps>
|
|
14
16
|
iconcolor?: string
|
|
15
17
|
iconsize?: string
|
|
16
18
|
iconlocation?: 'left' | 'right' | 'above'
|
|
17
19
|
fontlocation?: 'left' | 'center' | 'right'
|
|
18
20
|
}
|
|
19
21
|
|
|
20
|
-
const CustomButton: React.FC<CustomButtonProps> =
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const buttonStyle = {
|
|
47
|
-
backgroundColor: backgroundcolor,
|
|
48
|
-
width: width,
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const isDisabled = disableButton === 'true'
|
|
22
|
+
const CustomButton: React.FC<CustomButtonProps> = ({
|
|
23
|
+
text,
|
|
24
|
+
variant,
|
|
25
|
+
fontvariant = 'merriparagraph',
|
|
26
|
+
onClick,
|
|
27
|
+
fontcolor,
|
|
28
|
+
backgroundcolor,
|
|
29
|
+
width,
|
|
30
|
+
height,
|
|
31
|
+
disableButton,
|
|
32
|
+
icon,
|
|
33
|
+
iconcolor,
|
|
34
|
+
iconsize,
|
|
35
|
+
iconlocation = 'left',
|
|
36
|
+
fontlocation = 'center',
|
|
37
|
+
sx,
|
|
38
|
+
...restProps
|
|
39
|
+
}) => {
|
|
40
|
+
const handleButtonClick = (
|
|
41
|
+
event: React.MouseEvent<HTMLButtonElement>
|
|
42
|
+
): void => {
|
|
43
|
+
event.preventDefault()
|
|
44
|
+
onClick?.(event)
|
|
45
|
+
}
|
|
52
46
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
47
|
+
const buttonSx: SxProps<Theme> = {
|
|
48
|
+
...(backgroundcolor && { backgroundColor: backgroundcolor }),
|
|
49
|
+
...(width && { width }),
|
|
50
|
+
...(height && { height }),
|
|
51
|
+
flexDirection: iconlocation === 'above' ? 'column' : 'row',
|
|
52
|
+
justifyContent:
|
|
53
|
+
fontlocation === 'left'
|
|
54
|
+
? 'flex-start'
|
|
55
|
+
: fontlocation === 'right'
|
|
56
|
+
? 'flex-end'
|
|
57
|
+
: 'center',
|
|
58
|
+
...(sx as object),
|
|
59
|
+
}
|
|
57
60
|
|
|
58
|
-
|
|
59
|
-
? React.cloneElement(icon as React.ReactElement, {
|
|
60
|
-
style: { ...iconStyle, ...(icon as React.ReactElement).props.style },
|
|
61
|
-
})
|
|
62
|
-
: null
|
|
61
|
+
const isDisabled = disableButton === 'true'
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
? 'flex-start'
|
|
73
|
-
: fontlocation === 'right'
|
|
74
|
-
? 'flex-end'
|
|
75
|
-
: 'center'
|
|
76
|
-
}
|
|
77
|
-
width="100%"
|
|
78
|
-
>
|
|
79
|
-
{iconlocation === 'left' && IconComponent}
|
|
80
|
-
<Typography
|
|
81
|
-
fontvariant={fontvariant}
|
|
82
|
-
fontcolor={isDisabled ? 'grey' : fontcolor}
|
|
83
|
-
text={text || ''}
|
|
84
|
-
/>
|
|
85
|
-
{iconlocation === 'right' && IconComponent}
|
|
86
|
-
</Box>
|
|
87
|
-
</>
|
|
88
|
-
)
|
|
63
|
+
const IconComponent = icon
|
|
64
|
+
? React.cloneElement(icon, {
|
|
65
|
+
sx: {
|
|
66
|
+
color: iconcolor,
|
|
67
|
+
fontSize: iconsize,
|
|
68
|
+
},
|
|
69
|
+
} as Partial<SvgIconProps>)
|
|
70
|
+
: null
|
|
89
71
|
|
|
90
|
-
|
|
72
|
+
const buttonContent = (
|
|
73
|
+
<>
|
|
74
|
+
{iconlocation === 'above' && IconComponent}
|
|
91
75
|
<Box
|
|
92
76
|
display="flex"
|
|
93
|
-
flexDirection="column"
|
|
94
77
|
alignItems="center"
|
|
95
|
-
|
|
78
|
+
justifyContent={
|
|
79
|
+
fontlocation === 'left'
|
|
80
|
+
? 'flex-start'
|
|
81
|
+
: fontlocation === 'right'
|
|
82
|
+
? 'flex-end'
|
|
83
|
+
: 'center'
|
|
84
|
+
}
|
|
85
|
+
width="100%"
|
|
86
|
+
height="100%"
|
|
96
87
|
>
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
justifyContent:
|
|
105
|
-
fontlocation === 'left'
|
|
106
|
-
? 'flex-start'
|
|
107
|
-
: fontlocation === 'right'
|
|
108
|
-
? 'flex-end'
|
|
109
|
-
: 'center',
|
|
110
|
-
}}
|
|
111
|
-
disabled={isDisabled}
|
|
112
|
-
>
|
|
113
|
-
{buttonContent}
|
|
114
|
-
</Button>
|
|
88
|
+
{iconlocation === 'left' && IconComponent}
|
|
89
|
+
<Typography
|
|
90
|
+
fontvariant={fontvariant}
|
|
91
|
+
fontcolor={isDisabled ? 'grey' : fontcolor}
|
|
92
|
+
text={text || ''}
|
|
93
|
+
/>
|
|
94
|
+
{iconlocation === 'right' && IconComponent}
|
|
115
95
|
</Box>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
(prevProps, nextProps) => {
|
|
119
|
-
const propsAreEqual =
|
|
120
|
-
prevProps.text === nextProps.text &&
|
|
121
|
-
prevProps.variant === nextProps.variant &&
|
|
122
|
-
prevProps.fontvariant === nextProps.fontvariant &&
|
|
123
|
-
prevProps.onClick === nextProps.onClick &&
|
|
124
|
-
prevProps.fontcolor === nextProps.fontcolor &&
|
|
125
|
-
prevProps.backgroundcolor === nextProps.backgroundcolor &&
|
|
126
|
-
prevProps.width === nextProps.width &&
|
|
127
|
-
prevProps.disableButton === nextProps.disableButton &&
|
|
128
|
-
prevProps.icon === nextProps.icon &&
|
|
129
|
-
prevProps.iconcolor === nextProps.iconcolor &&
|
|
130
|
-
prevProps.iconsize === nextProps.iconsize &&
|
|
131
|
-
prevProps.iconlocation === nextProps.iconlocation &&
|
|
132
|
-
prevProps.fontlocation === nextProps.fontlocation
|
|
96
|
+
</>
|
|
97
|
+
)
|
|
133
98
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
99
|
+
return (
|
|
100
|
+
<Box
|
|
101
|
+
display="flex"
|
|
102
|
+
flexDirection="column"
|
|
103
|
+
alignItems="center"
|
|
104
|
+
width={width}
|
|
105
|
+
height={height}
|
|
106
|
+
>
|
|
107
|
+
<Button
|
|
108
|
+
{...restProps}
|
|
109
|
+
variant={variant}
|
|
110
|
+
onClick={handleButtonClick}
|
|
111
|
+
sx={buttonSx}
|
|
112
|
+
disabled={isDisabled}
|
|
113
|
+
>
|
|
114
|
+
{buttonContent}
|
|
115
|
+
</Button>
|
|
116
|
+
</Box>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
137
119
|
|
|
138
120
|
CustomButton.displayName = 'CustomButton'
|
|
139
121
|
export default CustomButton
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { Checkbox } from '@mui/material'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import * as palette from '../../styles/palette'
|
|
6
|
+
|
|
7
|
+
interface DataGridCheckboxProps {
|
|
8
|
+
onClick?: (event: React.MouseEvent) => void
|
|
9
|
+
checked?: boolean
|
|
10
|
+
indeterminate?: boolean
|
|
11
|
+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
12
|
+
disabled?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function DataGridCheckbox({
|
|
16
|
+
onClick,
|
|
17
|
+
checked,
|
|
18
|
+
indeterminate,
|
|
19
|
+
onChange,
|
|
20
|
+
disabled,
|
|
21
|
+
...props
|
|
22
|
+
}: DataGridCheckboxProps) {
|
|
23
|
+
console.log('DataGridCheckbox render:', { checked, indeterminate, disabled })
|
|
24
|
+
|
|
25
|
+
const handleClick = (event: React.MouseEvent) => {
|
|
26
|
+
console.log('Checkbox clicked:', {
|
|
27
|
+
checked,
|
|
28
|
+
indeterminate,
|
|
29
|
+
eventTarget: event.target,
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
if (onClick) {
|
|
33
|
+
onClick(event)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
38
|
+
event.stopPropagation()
|
|
39
|
+
|
|
40
|
+
console.log('Checkbox changed:', {
|
|
41
|
+
newChecked: event.target.checked,
|
|
42
|
+
previousChecked: checked,
|
|
43
|
+
indeterminate,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
if (onChange) {
|
|
47
|
+
onChange(event)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<Checkbox
|
|
53
|
+
sx={{
|
|
54
|
+
color: palette.marine.main,
|
|
55
|
+
'&.Mui-checked': {
|
|
56
|
+
color: palette.marine.main,
|
|
57
|
+
},
|
|
58
|
+
'&.Mui-indeterminate': {
|
|
59
|
+
color: palette.marine.main,
|
|
60
|
+
},
|
|
61
|
+
'&.Mui-disabled': {
|
|
62
|
+
color: palette.greyborder.main,
|
|
63
|
+
},
|
|
64
|
+
'&:hover': {
|
|
65
|
+
backgroundColor: palette.marine.light,
|
|
66
|
+
opacity: 0.1,
|
|
67
|
+
},
|
|
68
|
+
}}
|
|
69
|
+
checked={checked}
|
|
70
|
+
indeterminate={indeterminate}
|
|
71
|
+
onClick={handleClick}
|
|
72
|
+
onChange={handleChange}
|
|
73
|
+
disabled={disabled}
|
|
74
|
+
{...props}
|
|
75
|
+
/>
|
|
76
|
+
)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default DataGridCheckbox
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
|
-
import React, { ChangeEvent, KeyboardEvent, useState } from 'react'
|
|
2
|
+
import React, { ChangeEvent, KeyboardEvent, useState, useCallback } from 'react'
|
|
3
3
|
import { Input, Box } from '@mui/material'
|
|
4
|
-
import {
|
|
5
|
-
import { columnconfig } from '../../components/Grid'
|
|
4
|
+
import { columnconfig } from '../Grid'
|
|
6
5
|
import { red, green } from '../../styles/palette'
|
|
7
6
|
|
|
8
7
|
export interface ConfirmationCodeInputsProps {
|
|
@@ -13,59 +12,138 @@ export interface ConfirmationCodeInputsProps {
|
|
|
13
12
|
'aria-label'?: string
|
|
14
13
|
'aria-required'?: boolean
|
|
15
14
|
'aria-invalid'?: boolean
|
|
15
|
+
onChange?: (value: string) => void
|
|
16
|
+
value?: string
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface UseCodeConfirmationProps {
|
|
20
|
+
codeLength: number
|
|
21
|
+
onChange?: (value: string) => void
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const useCodeConfirmation = ({
|
|
25
|
+
codeLength,
|
|
26
|
+
onChange,
|
|
27
|
+
}: UseCodeConfirmationProps) => {
|
|
28
|
+
const [code, setCode] = useState<Record<string, string>>(
|
|
29
|
+
Object.fromEntries(
|
|
30
|
+
Array.from({ length: codeLength }, (_, i) => [`code${i + 1}`, ''])
|
|
31
|
+
)
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const handleCodeChange = useCallback(
|
|
35
|
+
(event: React.ChangeEvent<HTMLInputElement>, index: number) => {
|
|
36
|
+
const value = event.target.value.replace(/\D/g, '') // Only keep digits
|
|
37
|
+
if (value.length <= 1) {
|
|
38
|
+
// Only process if it's a single digit or empty
|
|
39
|
+
setCode(prevCode => {
|
|
40
|
+
const newCode = {
|
|
41
|
+
...prevCode,
|
|
42
|
+
[`code${index + 1}`]: value,
|
|
43
|
+
}
|
|
44
|
+
const combinedValue = Object.values(newCode).join('')
|
|
45
|
+
onChange?.(combinedValue)
|
|
46
|
+
return newCode
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
[onChange]
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
const handleKeyDown = useCallback(
|
|
54
|
+
(event: React.KeyboardEvent<HTMLInputElement>, index: number) => {
|
|
55
|
+
// Allow only numeric keys, navigation keys, and backspace
|
|
56
|
+
const allowedKeys = [
|
|
57
|
+
'Backspace',
|
|
58
|
+
'ArrowLeft',
|
|
59
|
+
'ArrowRight',
|
|
60
|
+
'Tab',
|
|
61
|
+
'Delete',
|
|
62
|
+
'Home',
|
|
63
|
+
'End',
|
|
64
|
+
]
|
|
65
|
+
|
|
66
|
+
if (!allowedKeys.includes(event.key) && !/^\d$/.test(event.key)) {
|
|
67
|
+
event.preventDefault()
|
|
68
|
+
return
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (event.key === 'Backspace' && !code[`code${index + 1}`] && index > 0) {
|
|
72
|
+
setCode(prevCode => {
|
|
73
|
+
const newCode = {
|
|
74
|
+
...prevCode,
|
|
75
|
+
[`code${index}`]: '',
|
|
76
|
+
}
|
|
77
|
+
const combinedValue = Object.values(newCode).join('')
|
|
78
|
+
onChange?.(combinedValue)
|
|
79
|
+
return newCode
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
const prevInput = document.querySelector(
|
|
83
|
+
`input[name=code${index}]`
|
|
84
|
+
) as HTMLInputElement | null
|
|
85
|
+
if (prevInput) {
|
|
86
|
+
prevInput.focus()
|
|
87
|
+
}
|
|
88
|
+
} else if (event.key === 'ArrowLeft' && index > 0) {
|
|
89
|
+
const prevInput = document.querySelector(
|
|
90
|
+
`input[name=code${index}]`
|
|
91
|
+
) as HTMLInputElement | null
|
|
92
|
+
if (prevInput) {
|
|
93
|
+
prevInput.focus()
|
|
94
|
+
}
|
|
95
|
+
} else if (event.key === 'ArrowRight' && index < codeLength - 1) {
|
|
96
|
+
const nextInput = document.querySelector(
|
|
97
|
+
`input[name=code${index + 2}]`
|
|
98
|
+
) as HTMLInputElement | null
|
|
99
|
+
if (nextInput) {
|
|
100
|
+
nextInput.focus()
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
[code, codeLength, onChange]
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
handleCodeChange,
|
|
109
|
+
handleKeyDown,
|
|
110
|
+
}
|
|
16
111
|
}
|
|
17
112
|
|
|
18
|
-
/**
|
|
19
|
-
* ConfirmationCodeInputs component renders a set of input fields for entering a confirmation code.
|
|
20
|
-
* It uses the useCodeConfirmation hook to handle code changes and key events.
|
|
21
|
-
* @param props The props for the ConfirmationCodeInputs component.
|
|
22
|
-
* @returns The rendered ConfirmationCodeInputs component.
|
|
23
|
-
*/
|
|
24
113
|
const ConfirmationCodeInputs: React.FC<ConfirmationCodeInputsProps> = ({
|
|
25
114
|
codeLength = 6,
|
|
26
115
|
isValid,
|
|
116
|
+
onChange,
|
|
117
|
+
value,
|
|
27
118
|
'aria-label': ariaLabel,
|
|
28
119
|
'aria-required': ariaRequired,
|
|
29
120
|
'aria-invalid': ariaInvalid,
|
|
30
121
|
...props
|
|
31
122
|
}) => {
|
|
32
|
-
const
|
|
123
|
+
const { handleCodeChange, handleKeyDown } = useCodeConfirmation({
|
|
124
|
+
codeLength,
|
|
125
|
+
onChange,
|
|
126
|
+
})
|
|
33
127
|
|
|
34
|
-
const { handleCodeChange, handleKeyDown, combinedCode } = useCodeConfirmation(
|
|
35
|
-
{
|
|
36
|
-
codeLength,
|
|
37
|
-
isValid,
|
|
38
|
-
}
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* handleChange function is called when the value of an input field changes.
|
|
43
|
-
* It updates the code state using the handleCodeChange function from the useCodeConfirmation hook.
|
|
44
|
-
* If the input field has a value, it focuses on the next input field.
|
|
45
|
-
* @param event The change event triggered by the input field.
|
|
46
|
-
* @param index The index of the input field.
|
|
47
|
-
*/
|
|
48
128
|
const handleChange = (
|
|
49
129
|
event: ChangeEvent<HTMLInputElement>,
|
|
50
130
|
index: number
|
|
51
131
|
) => {
|
|
52
|
-
|
|
53
|
-
if (
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
|
|
132
|
+
const inputValue = event.target.value.replace(/\D/g, '') // Only keep digits
|
|
133
|
+
if (inputValue.length <= 1) {
|
|
134
|
+
// Only process if it's a single digit or empty
|
|
135
|
+
handleCodeChange(event, index)
|
|
136
|
+
if (inputValue) {
|
|
137
|
+
const nextInput = document.querySelector(
|
|
138
|
+
`input[name=code${index + 2}]`
|
|
139
|
+
) as HTMLInputElement | null
|
|
140
|
+
if (nextInput) {
|
|
141
|
+
nextInput.focus()
|
|
142
|
+
}
|
|
59
143
|
}
|
|
60
144
|
}
|
|
61
145
|
}
|
|
62
146
|
|
|
63
|
-
/**
|
|
64
|
-
* handleKeyDownWrapper function is a wrapper for the handleKeyDown function from the useCodeConfirmation hook.
|
|
65
|
-
* It is called when a key is pressed while an input field is focused.
|
|
66
|
-
* @param event The keyboard event triggered by the input field.
|
|
67
|
-
* @param index The index of the input field.
|
|
68
|
-
*/
|
|
69
147
|
const handleKeyDownWrapper = (
|
|
70
148
|
event: KeyboardEvent<HTMLInputElement>,
|
|
71
149
|
index: number
|
|
@@ -73,15 +151,8 @@ const ConfirmationCodeInputs: React.FC<ConfirmationCodeInputsProps> = ({
|
|
|
73
151
|
handleKeyDown(event, index)
|
|
74
152
|
}
|
|
75
153
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
* It sets the code whenever the combinedCode changes and the code is valid.
|
|
79
|
-
*/
|
|
80
|
-
React.useEffect(() => {
|
|
81
|
-
if (isValid) {
|
|
82
|
-
setVerificationCode(combinedCode)
|
|
83
|
-
}
|
|
84
|
-
}, [combinedCode, isValid])
|
|
154
|
+
// Split the value into individual digits
|
|
155
|
+
const digits = value?.split('') || Array(codeLength).fill('')
|
|
85
156
|
|
|
86
157
|
return (
|
|
87
158
|
<Box
|
|
@@ -96,8 +167,11 @@ const ConfirmationCodeInputs: React.FC<ConfirmationCodeInputsProps> = ({
|
|
|
96
167
|
<Input
|
|
97
168
|
key={index}
|
|
98
169
|
name={`code${index + 1}`}
|
|
170
|
+
value={digits[index] || ''}
|
|
99
171
|
inputProps={{
|
|
100
172
|
maxLength: 1,
|
|
173
|
+
pattern: '[0-9]*',
|
|
174
|
+
inputMode: 'numeric',
|
|
101
175
|
'aria-label': `Code Digit ${index + 1}`,
|
|
102
176
|
'aria-required': ariaRequired,
|
|
103
177
|
'aria-invalid': ariaInvalid,
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { columnconfig, cellconfig } from '../../../Grid'
|
|
4
|
+
import DataGridCheckbox from './../../../../components/Checkbox'
|
|
5
|
+
|
|
6
|
+
type ExtendedColumnConfig = Omit<columnconfig, 'component'> & {
|
|
7
|
+
component?: columnconfig['component']
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ExtendedCheckboxProps {
|
|
11
|
+
columnconfig?: ExtendedColumnConfig
|
|
12
|
+
cellconfig?: cellconfig
|
|
13
|
+
onClick?: (event: React.MouseEvent) => void
|
|
14
|
+
checked?: boolean
|
|
15
|
+
indeterminate?: boolean
|
|
16
|
+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
17
|
+
disabled?: boolean
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const useCheckbox = (grid: {
|
|
21
|
+
checkbox?: ExtendedCheckboxProps | ExtendedCheckboxProps[]
|
|
22
|
+
}): columnconfig | columnconfig[] | null => {
|
|
23
|
+
if (!grid.checkbox) return null
|
|
24
|
+
|
|
25
|
+
const renderCheckbox = (
|
|
26
|
+
checkboxProps: ExtendedCheckboxProps,
|
|
27
|
+
index: number
|
|
28
|
+
): columnconfig => {
|
|
29
|
+
const {
|
|
30
|
+
columnconfig: itemColumnConfig,
|
|
31
|
+
cellconfig,
|
|
32
|
+
...restProps
|
|
33
|
+
} = checkboxProps
|
|
34
|
+
|
|
35
|
+
if (
|
|
36
|
+
!itemColumnConfig ||
|
|
37
|
+
typeof itemColumnConfig !== 'object' ||
|
|
38
|
+
typeof itemColumnConfig.row !== 'number' ||
|
|
39
|
+
typeof itemColumnConfig.column !== 'number'
|
|
40
|
+
) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
'columnconfig must be an object with row and column as numbers'
|
|
43
|
+
)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Merge the existing columnconfig with the new props
|
|
47
|
+
const mergedConfig: columnconfig = {
|
|
48
|
+
...itemColumnConfig,
|
|
49
|
+
cellconfig: {
|
|
50
|
+
...cellconfig,
|
|
51
|
+
},
|
|
52
|
+
component: <DataGridCheckbox key={`checkbox-${index}`} {...restProps} />,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return mergedConfig
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (Array.isArray(grid.checkbox)) {
|
|
59
|
+
return grid.checkbox.map(renderCheckbox)
|
|
60
|
+
} else {
|
|
61
|
+
return renderCheckbox(grid.checkbox, 0)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export default useCheckbox
|
|
@@ -27,6 +27,7 @@ const useGridTypography = (grid: {
|
|
|
27
27
|
fontvariant,
|
|
28
28
|
columnconfig: itemColumnConfig,
|
|
29
29
|
cellconfig,
|
|
30
|
+
component,
|
|
30
31
|
...restProps
|
|
31
32
|
} = typographyItem
|
|
32
33
|
|
|
@@ -53,6 +54,7 @@ const useGridTypography = (grid: {
|
|
|
53
54
|
text={text}
|
|
54
55
|
fontvariant={fontvariant}
|
|
55
56
|
fontcolor={fontcolor}
|
|
57
|
+
component={component || 'span'}
|
|
56
58
|
{...restProps}
|
|
57
59
|
/>
|
|
58
60
|
),
|
|
@@ -60,6 +60,9 @@ import useQRCode, { ExtendedQRCodeProps } from './Structure/qrcode/useQRCode'
|
|
|
60
60
|
import usePhoneNumber, {
|
|
61
61
|
ExtendedPhoneNumberFieldProps,
|
|
62
62
|
} from './Structure/phoneNumber/usePhoneNumber'
|
|
63
|
+
import useCheckbox, {
|
|
64
|
+
ExtendedCheckboxProps,
|
|
65
|
+
} from './Structure/checkbox/useCheckbox'
|
|
63
66
|
/**
|
|
64
67
|
* Props for the ContentSection component.
|
|
65
68
|
* Includes configuration for various content elements.
|
|
@@ -95,6 +98,7 @@ export interface ContentSectionProps {
|
|
|
95
98
|
phoneNumberField?:
|
|
96
99
|
| ExtendedPhoneNumberFieldProps
|
|
97
100
|
| ExtendedPhoneNumberFieldProps[]
|
|
101
|
+
checkbox?: ExtendedCheckboxProps | ExtendedCheckboxProps[]
|
|
98
102
|
}>
|
|
99
103
|
width?: number
|
|
100
104
|
}
|
|
@@ -135,6 +139,7 @@ const RenderContent: React.FC<
|
|
|
135
139
|
addToColumnConfigs(useCodeCopy(props))
|
|
136
140
|
addToColumnConfigs(useTextField(props))
|
|
137
141
|
addToColumnConfigs(useDateField(props))
|
|
142
|
+
addToColumnConfigs(useCheckbox(props))
|
|
138
143
|
addToColumnConfigs(usePhoneNumber(props))
|
|
139
144
|
addToColumnConfigs(useDropdown(props))
|
|
140
145
|
addToColumnConfigs(
|