@liguelead/design-system 0.0.25 → 0.0.26
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/components/Button/Button.sizes.ts +3 -3
- package/components/Button/Button.styles.ts +2 -2
- package/components/Button/Button.tsx +2 -2
- package/components/Button/Button.types.ts +1 -1
- package/components/IconButton/IconButton.tsx +2 -2
- package/components/LinkButton/LinkButton.style.ts +1 -1
- package/components/LinkButton/LinkButton.tsx +2 -2
- package/components/LinkButton/LinkButton.types.ts +1 -1
- package/components/Select/Select.sizes.ts +3 -0
- package/components/TextField/TextField.sizes.ts +5 -2
- package/components/TextField/TextField.states.tsx +44 -0
- package/components/TextField/TextField.styles.ts +47 -4
- package/components/TextField/TextField.tsx +122 -12
- package/components/TextField/TextField.types.ts +3 -1
- package/package.json +1 -1
|
@@ -15,7 +15,7 @@ export const ButtonSizes = (size: ButtonSizeTypes) => {
|
|
|
15
15
|
font-weight: ${fontWeight.fontWeight500};
|
|
16
16
|
line-height: ${lineHeight.lineHeight22}px;
|
|
17
17
|
gap: ${spacing.spacing8}px;
|
|
18
|
-
|
|
18
|
+
height: ${spacing.spacing32}px;
|
|
19
19
|
border-radius: ${radius.radius4}px;
|
|
20
20
|
`,
|
|
21
21
|
md: `
|
|
@@ -24,7 +24,7 @@ export const ButtonSizes = (size: ButtonSizeTypes) => {
|
|
|
24
24
|
font-weight: ${fontWeight.fontWeight500};
|
|
25
25
|
line-height: ${lineHeight.lineHeight22}px;
|
|
26
26
|
gap: ${spacing.spacing8}px;
|
|
27
|
-
|
|
27
|
+
height: ${spacing.spacing36}px;
|
|
28
28
|
border-radius: ${radius.radius4}px;
|
|
29
29
|
`,
|
|
30
30
|
lg: `
|
|
@@ -33,7 +33,7 @@ export const ButtonSizes = (size: ButtonSizeTypes) => {
|
|
|
33
33
|
font-weight: ${fontWeight.fontWeight500};
|
|
34
34
|
line-height: ${lineHeight.lineHeight24}px;
|
|
35
35
|
gap: ${spacing.spacing8}px;
|
|
36
|
-
|
|
36
|
+
height: ${spacing.spacing40}px;
|
|
37
37
|
border-radius: ${radius.radius4}px;
|
|
38
38
|
`
|
|
39
39
|
}
|
|
@@ -5,7 +5,7 @@ import { shadow } from '@liguelead/foundation'
|
|
|
5
5
|
export interface StyledButtonProps extends ButtonProps {
|
|
6
6
|
$variant: string
|
|
7
7
|
$buttonSize: string
|
|
8
|
-
$
|
|
8
|
+
$fullWidth: boolean
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
export const StyledButton = styled.button<StyledButtonProps>`
|
|
@@ -14,7 +14,7 @@ export const StyledButton = styled.button<StyledButtonProps>`
|
|
|
14
14
|
outline: none !important;
|
|
15
15
|
justify-content: center;
|
|
16
16
|
align-items: center;
|
|
17
|
-
width: ${({ $
|
|
17
|
+
width: ${({ $fullWidth }) => ($fullWidth ? '100%' : 'auto')};
|
|
18
18
|
${({ $buttonSize }) => $buttonSize}
|
|
19
19
|
overflow: hidden;
|
|
20
20
|
cursor: pointer;
|
|
@@ -10,7 +10,7 @@ const Button: React.FC<ButtonProps> = ({
|
|
|
10
10
|
className,
|
|
11
11
|
color = 'primary',
|
|
12
12
|
disabled,
|
|
13
|
-
|
|
13
|
+
fullWidth = false,
|
|
14
14
|
size = 'md',
|
|
15
15
|
onClick,
|
|
16
16
|
type = 'button',
|
|
@@ -47,7 +47,7 @@ const Button: React.FC<ButtonProps> = ({
|
|
|
47
47
|
<StyledButton
|
|
48
48
|
disabled={disabled}
|
|
49
49
|
className={className}
|
|
50
|
-
$
|
|
50
|
+
$fullWidth={fullWidth}
|
|
51
51
|
$variant={buttonVariant}
|
|
52
52
|
onClick={handleClick}
|
|
53
53
|
$buttonSize={buttonSize}
|
|
@@ -8,7 +8,7 @@ export interface ButtonProps {
|
|
|
8
8
|
children: React.ReactNode
|
|
9
9
|
className?: string
|
|
10
10
|
disabled?: boolean
|
|
11
|
-
|
|
11
|
+
fullWidth?: boolean
|
|
12
12
|
size?: ButtonSizeTypes
|
|
13
13
|
color?: colorType
|
|
14
14
|
onClick?: (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void
|
|
@@ -10,7 +10,7 @@ const IconButton: React.FC<ButtonProps> = ({
|
|
|
10
10
|
className,
|
|
11
11
|
color = 'primary',
|
|
12
12
|
disabled,
|
|
13
|
-
|
|
13
|
+
fullWidth = false,
|
|
14
14
|
size = 'md',
|
|
15
15
|
onClick,
|
|
16
16
|
...rest
|
|
@@ -42,7 +42,7 @@ const IconButton: React.FC<ButtonProps> = ({
|
|
|
42
42
|
<StyledButton
|
|
43
43
|
disabled={disabled}
|
|
44
44
|
className={className}
|
|
45
|
-
$
|
|
45
|
+
$fullWidth={fullWidth}
|
|
46
46
|
$variant={buttonVariant}
|
|
47
47
|
onClick={handleClick}
|
|
48
48
|
$buttonSize={buttonSize}
|
|
@@ -32,7 +32,7 @@ export const StyledLinkButton = styled.button<StyledButtonProps>`
|
|
|
32
32
|
display: flex;
|
|
33
33
|
outline: none !important;
|
|
34
34
|
|
|
35
|
-
width: ${({ $
|
|
35
|
+
width: ${({ $fullWidth }) => ($fullWidth ? '100%' : 'auto')};
|
|
36
36
|
${({ $buttonSize }) => $buttonSize}
|
|
37
37
|
overflow: hidden;
|
|
38
38
|
cursor: pointer;
|
|
@@ -12,7 +12,7 @@ const LinkButton: React.FC<LinkButtonProps> = ({
|
|
|
12
12
|
disabled = false,
|
|
13
13
|
color = 'primary',
|
|
14
14
|
variant = 'ghost',
|
|
15
|
-
|
|
15
|
+
fullWidth = false,
|
|
16
16
|
leftIcon,
|
|
17
17
|
rightIcon,
|
|
18
18
|
onClick,
|
|
@@ -47,7 +47,7 @@ const LinkButton: React.FC<LinkButtonProps> = ({
|
|
|
47
47
|
onClick={handleClick}
|
|
48
48
|
$variant={buttonVariant}
|
|
49
49
|
$buttonSize={buttonSize}
|
|
50
|
-
$
|
|
50
|
+
$fullWidth={fullWidth}
|
|
51
51
|
aria-disabled={disabled}
|
|
52
52
|
rel={rest.target === '_blank' ? 'noopener noreferrer' : rest.rel}
|
|
53
53
|
{...rest}
|
|
@@ -6,7 +6,7 @@ export interface LinkButtonProps extends React.AnchorHTMLAttributes<HTMLAnchorEl
|
|
|
6
6
|
disabled?: boolean
|
|
7
7
|
color?: colorType
|
|
8
8
|
variant?: 'ghost'
|
|
9
|
-
|
|
9
|
+
fullWidth?: boolean
|
|
10
10
|
leftIcon?: React.ReactNode
|
|
11
11
|
rightIcon?: React.ReactNode
|
|
12
12
|
size?: 'sm' | 'md' | 'lg'
|
|
@@ -18,6 +18,7 @@ export const textFieldSizes = (
|
|
|
18
18
|
input: `
|
|
19
19
|
font-size: ${fontSize.fontSize14}px;
|
|
20
20
|
line-height: ${lineHeight.lineHeight22}px;
|
|
21
|
+
height: ${spacing.spacing32}px;
|
|
21
22
|
padding: ${spacing.spacing8}px ${spacing.spacing12}px;
|
|
22
23
|
padding-left: ${leftIcon ? withIconPadding.sm : spacing.spacing12}px;
|
|
23
24
|
padding-right: ${rightIcon ? withIconPadding.sm : spacing.spacing12}px;
|
|
@@ -32,6 +33,7 @@ export const textFieldSizes = (
|
|
|
32
33
|
font-size: ${fontSize.fontSize14}px;
|
|
33
34
|
line-height: ${lineHeight.lineHeight20}px;
|
|
34
35
|
padding: ${spacing.spacing12}px ${spacing.spacing16}px;
|
|
36
|
+
height: ${spacing.spacing36}px;
|
|
35
37
|
padding-left: ${leftIcon ? withIconPadding.md : spacing.spacing16}px;
|
|
36
38
|
padding-right: ${rightIcon ? withIconPadding.md : spacing.spacing16}px;
|
|
37
39
|
`,
|
|
@@ -43,6 +45,7 @@ export const textFieldSizes = (
|
|
|
43
45
|
font-size: ${fontSize.fontSize16}px;
|
|
44
46
|
line-height: ${lineHeight.lineHeight24}px;
|
|
45
47
|
padding: ${spacing.spacing12}px ${spacing.spacing16}px;
|
|
48
|
+
height: ${spacing.spacing40}px;
|
|
46
49
|
padding-left: ${leftIcon ? withIconPadding.lg : spacing.spacing16}px;
|
|
47
50
|
padding-right: ${rightIcon ? withIconPadding.lg : spacing.spacing16}px;
|
|
48
51
|
`,
|
|
@@ -18,8 +18,9 @@ export const textFieldSizes = (size: TextFieldSize, leftIcon: boolean, rightIcon
|
|
|
18
18
|
const sizes = {
|
|
19
19
|
sm: {
|
|
20
20
|
input: `
|
|
21
|
-
font-size: ${fontSize.fontSize14}px;
|
|
22
|
-
line-height: ${lineHeight.lineHeight22}px;
|
|
21
|
+
font-size: ${fontSize.fontSize14}px;
|
|
22
|
+
line-height: ${lineHeight.lineHeight22}px;
|
|
23
|
+
height: ${spacing.spacing32}px;
|
|
23
24
|
padding: ${spacing.spacing8}px ${spacing.spacing12}px;
|
|
24
25
|
padding-left: ${leftIcon ? withIconPadding.sm : spacing.spacing12}px;
|
|
25
26
|
padding-right: ${rightIcon ? withIconPadding.sm : spacing.spacing12}px;
|
|
@@ -31,6 +32,7 @@ export const textFieldSizes = (size: TextFieldSize, leftIcon: boolean, rightIcon
|
|
|
31
32
|
font-size: ${fontSize.fontSize14}px;
|
|
32
33
|
line-height: ${lineHeight.lineHeight20}px;
|
|
33
34
|
padding: ${spacing.spacing12}px ${spacing.spacing16}px;
|
|
35
|
+
height: ${spacing.spacing36}px;
|
|
34
36
|
padding-left: ${leftIcon ? withIconPadding.md : spacing.spacing16}px;
|
|
35
37
|
padding-right: ${rightIcon ? withIconPadding.md : spacing.spacing16}px;
|
|
36
38
|
`,
|
|
@@ -41,6 +43,7 @@ export const textFieldSizes = (size: TextFieldSize, leftIcon: boolean, rightIcon
|
|
|
41
43
|
font-size: ${fontSize.fontSize16}px;
|
|
42
44
|
line-height: ${lineHeight.lineHeight24}px;
|
|
43
45
|
padding: ${spacing.spacing12}px ${spacing.spacing16}px;
|
|
46
|
+
height: ${spacing.spacing40}px;
|
|
44
47
|
padding-left: ${leftIcon ? withIconPadding.lg : spacing.spacing16}px;
|
|
45
48
|
padding-right: ${rightIcon ? withIconPadding.lg : spacing.spacing16}px;
|
|
46
49
|
`,
|
|
@@ -5,6 +5,8 @@ export interface StateInterface {
|
|
|
5
5
|
input: string
|
|
6
6
|
label?: string
|
|
7
7
|
helperText: string
|
|
8
|
+
fileButton?: string
|
|
9
|
+
fileName?: string
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
interface TextFieldStates {
|
|
@@ -27,6 +29,21 @@ export const TextFieldStates = (state: keyof TextFieldStates) => {
|
|
|
27
29
|
`,
|
|
28
30
|
label: `color: ${parseColor(theme.colors.textDark)};`,
|
|
29
31
|
helperText: `color: ${parseColor(theme.colors.danger200)};`,
|
|
32
|
+
fileButton: `
|
|
33
|
+
border: 1px solid ${parseColor(theme.colors.danger200)};
|
|
34
|
+
background: transparent;
|
|
35
|
+
border-right: none;
|
|
36
|
+
color: ${parseColor(theme.colors.primary)};
|
|
37
|
+
&:hover {
|
|
38
|
+
background: transparent;
|
|
39
|
+
}
|
|
40
|
+
`,
|
|
41
|
+
fileName: `
|
|
42
|
+
border: 1px solid ${parseColor(theme.colors.danger200)};
|
|
43
|
+
color: ${parseColor(theme.colors.textDark)};
|
|
44
|
+
background: ${parseColor(theme.colors.white)};
|
|
45
|
+
border-left: none;
|
|
46
|
+
`,
|
|
30
47
|
},
|
|
31
48
|
default: {
|
|
32
49
|
input: `
|
|
@@ -39,6 +56,18 @@ export const TextFieldStates = (state: keyof TextFieldStates) => {
|
|
|
39
56
|
`,
|
|
40
57
|
label: `color: ${parseColor(theme.colors.textDark)};`,
|
|
41
58
|
helperText: `color: ${parseColor(theme.colors.textMedium)};`,
|
|
59
|
+
fileButton: `
|
|
60
|
+
border: 1px solid ${parseColor(theme.colors.neutral400)};
|
|
61
|
+
background: transparent;
|
|
62
|
+
border-right: none;
|
|
63
|
+
color: ${parseColor(theme.colors.primary)};
|
|
64
|
+
`,
|
|
65
|
+
fileName: `
|
|
66
|
+
border: 1px solid ${parseColor(theme.colors.neutral400)};
|
|
67
|
+
color: ${parseColor(theme.colors.textDark)};
|
|
68
|
+
background: ${parseColor(theme.colors.white)};
|
|
69
|
+
border-left: none;
|
|
70
|
+
`,
|
|
42
71
|
},
|
|
43
72
|
disabled: {
|
|
44
73
|
input: `
|
|
@@ -48,6 +77,21 @@ export const TextFieldStates = (state: keyof TextFieldStates) => {
|
|
|
48
77
|
`,
|
|
49
78
|
label: `color: ${parseColor(theme.colors.textDark)};`,
|
|
50
79
|
helperText: `color: ${parseColor(theme.colors.neutral500)};`,
|
|
80
|
+
fileButton: `
|
|
81
|
+
border: 1px solid ${parseColor(theme.colors.neutral300)};
|
|
82
|
+
background: transparent;
|
|
83
|
+
border-right: none;
|
|
84
|
+
color: ${parseColor(theme.colors.primary)};
|
|
85
|
+
cursor: not-allowed;
|
|
86
|
+
opacity: 0.5;
|
|
87
|
+
`,
|
|
88
|
+
fileName: `
|
|
89
|
+
border: 1px solid ${parseColor(theme.colors.neutral300)};
|
|
90
|
+
background: ${parseColor(theme.colors.neutral50)};
|
|
91
|
+
color: ${parseColor(theme.colors.neutral500)};
|
|
92
|
+
opacity: 0.5;
|
|
93
|
+
border-left: none;
|
|
94
|
+
`,
|
|
51
95
|
}
|
|
52
96
|
}
|
|
53
97
|
return states[state]
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
fontSize,
|
|
5
5
|
fontWeight,
|
|
6
6
|
lineHeight,
|
|
7
|
+
shadow,
|
|
7
8
|
spacing
|
|
8
9
|
} from '@liguelead/foundation'
|
|
9
10
|
import { StateInterface } from './TextField.states'
|
|
@@ -14,9 +15,13 @@ interface StyledInputProps {
|
|
|
14
15
|
$themefication: StateInterface
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
export const InputWrapper = styled.div
|
|
18
|
+
export const InputWrapper = styled.div<{ $isDragging?: boolean }>`
|
|
18
19
|
position: relative;
|
|
19
20
|
width: 100%;
|
|
21
|
+
|
|
22
|
+
box-shadow: ${({ $isDragging }) => ($isDragging ? shadow.focusShadow : 'none')};
|
|
23
|
+
border-radius: 4px;
|
|
24
|
+
background: transparent;
|
|
20
25
|
`
|
|
21
26
|
|
|
22
27
|
export const Label = styled.label`
|
|
@@ -55,15 +60,14 @@ export const StyledInput = styled.input.withConfig({
|
|
|
55
60
|
width: 100%;
|
|
56
61
|
border-radius: 4px;
|
|
57
62
|
outline: none;
|
|
58
|
-
border: 1px solid
|
|
63
|
+
border: 1px solid ${parseColor('neutral400')};
|
|
59
64
|
transition: border-color 0.2s ease;
|
|
60
65
|
background: transparent;
|
|
61
66
|
`
|
|
62
67
|
|
|
63
|
-
|
|
64
68
|
export const Wrapper = styled.div<StyledInputProps>`
|
|
65
69
|
position: relative;
|
|
66
|
-
width:
|
|
70
|
+
max-width: 600px;
|
|
67
71
|
display: flex;
|
|
68
72
|
flex-direction: column;
|
|
69
73
|
gap: ${spacing.spacing4}px;
|
|
@@ -79,5 +83,44 @@ export const Wrapper = styled.div<StyledInputProps>`
|
|
|
79
83
|
${HelperText} {
|
|
80
84
|
${$themefication.helperText}
|
|
81
85
|
}
|
|
86
|
+
${FileButton} {
|
|
87
|
+
${$themefication.fileButton || $themefication.input}
|
|
88
|
+
${size.input}
|
|
89
|
+
}
|
|
90
|
+
${FileName} {
|
|
91
|
+
${$themefication.fileName || $themefication.input}
|
|
92
|
+
${size.input}
|
|
93
|
+
}
|
|
94
|
+
${FileInputContainer} {
|
|
95
|
+
${StyledInput} {
|
|
96
|
+
${$themefication.input}
|
|
97
|
+
${size.input}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
82
100
|
`}
|
|
83
101
|
`
|
|
102
|
+
|
|
103
|
+
export const FileButton = styled.span`
|
|
104
|
+
display: inline-flex;
|
|
105
|
+
align-items: center;
|
|
106
|
+
font-weight: 500;
|
|
107
|
+
white-space: nowrap;
|
|
108
|
+
cursor: pointer;
|
|
109
|
+
border-radius: 4px 0 0 4px;
|
|
110
|
+
transition: all 0.2s ease;
|
|
111
|
+
`
|
|
112
|
+
|
|
113
|
+
export const FileName = styled.span`
|
|
114
|
+
flex: 1;
|
|
115
|
+
border-radius: 0 4px 4px 0;
|
|
116
|
+
border-left: none;
|
|
117
|
+
transition: all 0.2s ease;
|
|
118
|
+
display: flex;
|
|
119
|
+
align-items: center;
|
|
120
|
+
`
|
|
121
|
+
|
|
122
|
+
export const FileInputContainer = styled.div`
|
|
123
|
+
display: flex;
|
|
124
|
+
width: 100%;
|
|
125
|
+
position: relative;
|
|
126
|
+
`
|
|
@@ -2,6 +2,9 @@ import React, { forwardRef, useState } from 'react'
|
|
|
2
2
|
import { TextFieldProps } from './TextField.types'
|
|
3
3
|
import { StateInterface, TextFieldStates } from './TextField.states'
|
|
4
4
|
import {
|
|
5
|
+
FileButton,
|
|
6
|
+
FileInputContainer,
|
|
7
|
+
FileName,
|
|
5
8
|
HelperText,
|
|
6
9
|
IconWrapper,
|
|
7
10
|
InputWrapper,
|
|
@@ -9,9 +12,10 @@ import {
|
|
|
9
12
|
StyledInput,
|
|
10
13
|
Wrapper
|
|
11
14
|
} from './TextField.styles'
|
|
15
|
+
|
|
12
16
|
import { textFieldSizes } from './TextField.sizes'
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
17
|
+
import { EyeIcon, EyeClosedIcon } from '@phosphor-icons/react'
|
|
18
|
+
import getState from './utils/getState'
|
|
15
19
|
import RequiredAsterisk from '../RequiredAsterisk'
|
|
16
20
|
|
|
17
21
|
const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
@@ -23,6 +27,8 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
23
27
|
handleLeftIcon,
|
|
24
28
|
handleRightIcon,
|
|
25
29
|
helperText,
|
|
30
|
+
filePlaceholder = 'Nenhum arquivo selecionado',
|
|
31
|
+
fileButtonLabel = 'Escolher arquivo',
|
|
26
32
|
label,
|
|
27
33
|
leftIcon,
|
|
28
34
|
onChange,
|
|
@@ -37,17 +43,39 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
37
43
|
},
|
|
38
44
|
ref
|
|
39
45
|
) => {
|
|
40
|
-
const [passwordVisible, setPasswordVisible] = useState(
|
|
41
|
-
|
|
42
|
-
)
|
|
46
|
+
const [passwordVisible, setPasswordVisible] = useState(false)
|
|
47
|
+
const [selectedFileName, setSelectedFileName] = useState('')
|
|
43
48
|
const state = getState(disabled, !!error)
|
|
44
49
|
const textFieldState: StateInterface = TextFieldStates(state)
|
|
45
50
|
const textFieldSize = textFieldSizes(size, !!leftIcon, !!rightIcon)
|
|
51
|
+
const [isDragging, setIsDragging] = useState(false)
|
|
52
|
+
|
|
53
|
+
const getCurrentInputType = () => {
|
|
54
|
+
if (type === 'password') {
|
|
55
|
+
return passwordVisible ? 'text' : 'password'
|
|
56
|
+
}
|
|
57
|
+
return type
|
|
58
|
+
}
|
|
46
59
|
|
|
47
60
|
const togglePasswordVisibility = () => {
|
|
48
61
|
setPasswordVisible(!passwordVisible)
|
|
49
62
|
}
|
|
50
63
|
|
|
64
|
+
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
65
|
+
const files = e.target.files
|
|
66
|
+
if (files && files.length > 0) {
|
|
67
|
+
const fileNames = Array.from(files)
|
|
68
|
+
.map(file => file.name)
|
|
69
|
+
.join(', ')
|
|
70
|
+
setSelectedFileName(fileNames)
|
|
71
|
+
} else {
|
|
72
|
+
setSelectedFileName('')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
register?.onChange(e)
|
|
76
|
+
onChange?.(e)
|
|
77
|
+
}
|
|
78
|
+
|
|
51
79
|
const transformRightIcon = (
|
|
52
80
|
type: string,
|
|
53
81
|
rightIcon: React.ReactNode
|
|
@@ -55,14 +83,97 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
55
83
|
if (type === 'password') {
|
|
56
84
|
return (
|
|
57
85
|
<IconWrapper onClick={togglePasswordVisibility} $right>
|
|
58
|
-
{passwordVisible ? <
|
|
86
|
+
{passwordVisible ? <EyeIcon /> : <EyeClosedIcon />}
|
|
87
|
+
</IconWrapper>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
if (rightIcon) {
|
|
91
|
+
return (
|
|
92
|
+
<IconWrapper onClick={handleRightIcon} $right>
|
|
93
|
+
{rightIcon}
|
|
59
94
|
</IconWrapper>
|
|
60
95
|
)
|
|
61
96
|
}
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const handleDrop = (e: React.DragEvent<HTMLLabelElement>) => {
|
|
101
|
+
e.preventDefault()
|
|
102
|
+
e.stopPropagation()
|
|
103
|
+
setIsDragging(false)
|
|
104
|
+
|
|
105
|
+
const files = e.dataTransfer.files
|
|
106
|
+
if (!files || files.length === 0) return
|
|
107
|
+
|
|
108
|
+
const fileNames = Array.from(files)
|
|
109
|
+
.map(f => f.name)
|
|
110
|
+
.join(', ')
|
|
111
|
+
setSelectedFileName(fileNames)
|
|
112
|
+
|
|
113
|
+
const event = {
|
|
114
|
+
target: {
|
|
115
|
+
files
|
|
116
|
+
}
|
|
117
|
+
} as unknown as React.ChangeEvent<HTMLInputElement>
|
|
118
|
+
|
|
119
|
+
register?.onChange(event)
|
|
120
|
+
onChange?.(event)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const handleDragOver = (e: React.DragEvent) => {
|
|
124
|
+
e.preventDefault()
|
|
125
|
+
e.stopPropagation()
|
|
126
|
+
setIsDragging(true)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const handleDragLeave = (e: React.DragEvent) => {
|
|
130
|
+
e.preventDefault()
|
|
131
|
+
e.stopPropagation()
|
|
132
|
+
setIsDragging(false)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (type === 'file') {
|
|
62
136
|
return (
|
|
63
|
-
<
|
|
64
|
-
{
|
|
65
|
-
|
|
137
|
+
<Wrapper
|
|
138
|
+
className={className}
|
|
139
|
+
size={textFieldSize}
|
|
140
|
+
$themefication={textFieldState}>
|
|
141
|
+
<Label>
|
|
142
|
+
{label} {requiredSymbol && <RequiredAsterisk />}
|
|
143
|
+
</Label>
|
|
144
|
+
<InputWrapper
|
|
145
|
+
as="label"
|
|
146
|
+
onDragOver={handleDragOver}
|
|
147
|
+
onDragLeave={handleDragLeave}
|
|
148
|
+
onDrop={handleDrop}
|
|
149
|
+
$isDragging={isDragging}
|
|
150
|
+
>
|
|
151
|
+
{leftIcon && (
|
|
152
|
+
<IconWrapper onClick={handleLeftIcon}>
|
|
153
|
+
{leftIcon}
|
|
154
|
+
</IconWrapper>
|
|
155
|
+
)}
|
|
156
|
+
<FileInputContainer>
|
|
157
|
+
<FileButton>{fileButtonLabel}</FileButton>
|
|
158
|
+
<FileName>
|
|
159
|
+
{selectedFileName || filePlaceholder}
|
|
160
|
+
</FileName>
|
|
161
|
+
</FileInputContainer>
|
|
162
|
+
{transformRightIcon(type, rightIcon)}
|
|
163
|
+
<StyledInput
|
|
164
|
+
ref={ref}
|
|
165
|
+
type="file"
|
|
166
|
+
hidden
|
|
167
|
+
disabled={disabled}
|
|
168
|
+
{...props}
|
|
169
|
+
{...register}
|
|
170
|
+
onChange={handleFileChange}
|
|
171
|
+
/>
|
|
172
|
+
</InputWrapper>
|
|
173
|
+
{(helperText || error) && (
|
|
174
|
+
<HelperText>{error?.message || helperText}</HelperText>
|
|
175
|
+
)}
|
|
176
|
+
</Wrapper>
|
|
66
177
|
)
|
|
67
178
|
}
|
|
68
179
|
|
|
@@ -70,8 +181,7 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
70
181
|
<Wrapper
|
|
71
182
|
className={className}
|
|
72
183
|
size={textFieldSize}
|
|
73
|
-
$themefication={textFieldState}
|
|
74
|
-
>
|
|
184
|
+
$themefication={textFieldState}>
|
|
75
185
|
<Label>
|
|
76
186
|
{label} {requiredSymbol && <RequiredAsterisk />}
|
|
77
187
|
</Label>
|
|
@@ -84,7 +194,7 @@ const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
|
|
|
84
194
|
{transformRightIcon(type, rightIcon)}
|
|
85
195
|
<StyledInput
|
|
86
196
|
ref={ref}
|
|
87
|
-
type={
|
|
197
|
+
type={getCurrentInputType()}
|
|
88
198
|
value={value}
|
|
89
199
|
disabled={disabled}
|
|
90
200
|
placeholder={placeholder}
|
|
@@ -10,6 +10,8 @@ export interface TextFieldProps<TFieldValues extends FieldValues = FieldValues>
|
|
|
10
10
|
handleLeftIcon?: () => void
|
|
11
11
|
handleRightIcon?: () => void
|
|
12
12
|
helperText?: string
|
|
13
|
+
filePlaceholder?: string
|
|
14
|
+
fileButtonLabel?: string
|
|
13
15
|
size?: TextFieldSize
|
|
14
16
|
className?: string
|
|
15
17
|
placeholder?: string
|
|
@@ -18,6 +20,6 @@ export interface TextFieldProps<TFieldValues extends FieldValues = FieldValues>
|
|
|
18
20
|
rightIcon?: React.ReactNode
|
|
19
21
|
error?: TFieldValues
|
|
20
22
|
requiredSymbol?: boolean
|
|
21
|
-
type?: 'text' | 'password' | 'email' | 'number'
|
|
23
|
+
type?: 'text' | 'password' | 'email' | 'number' | 'file'
|
|
22
24
|
register?: UseFormRegisterReturn<string>
|
|
23
25
|
}
|