1
1
import { faEye , faEyeSlash } from "@fortawesome/free-regular-svg-icons" ;
2
2
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome" ;
3
- import React , { useState } from "react" ;
3
+ import React from "react" ;
4
+ import { useIntl } from "react-intl" ;
5
+ import { useToggle } from "react-use" ;
4
6
import styled from "styled-components" ;
5
7
import { Theme } from "theme" ;
6
8
@@ -18,72 +20,98 @@ const getBackgroundColor = (props: IStyleProps) => {
18
20
return props . theme . greyColor0 ;
19
21
} ;
20
22
21
- export type InputProps = {
23
+ export interface InputProps extends React . InputHTMLAttributes < HTMLInputElement > {
22
24
error ?: boolean ;
23
25
light ?: boolean ;
24
- } & React . InputHTMLAttributes < HTMLInputElement > ;
26
+ }
25
27
26
- const InputComponent = styled . input < InputProps > `
27
- outline: none;
28
+ const InputContainer = styled . div < InputProps > `
28
29
width: 100%;
29
- padding: 7px 18px 7px 8px;
30
- border-radius: 4px;
31
- font-size: 14px;
32
- line-height: 20px;
33
- font-weight: normal;
34
- border: 1px solid ${ ( props ) => ( props . error ? props . theme . dangerColor : props . theme . greyColor0 ) } ;
30
+ position: relative;
35
31
background: ${ ( props ) => getBackgroundColor ( props ) } ;
36
- color: ${ ( { theme } ) => theme . textColor } ;
37
- caret-color: ${ ( { theme } ) => theme . primaryColor } ;
38
-
39
- &::placeholder {
40
- color: ${ ( { theme } ) => theme . greyColor40 } ;
41
- }
32
+ border: 1px solid ${ ( props ) => ( props . error ? props . theme . dangerColor : props . theme . greyColor0 ) } ;
33
+ border-radius: 4px;
42
34
43
35
&:hover {
44
36
background: ${ ( { theme, light } ) => ( light ? theme . whiteColor : theme . greyColor20 ) } ;
45
37
border-color: ${ ( props ) => ( props . error ? props . theme . dangerColor : props . theme . greyColor20 ) } ;
46
38
}
47
39
48
- &:focus {
40
+ &.input-container--focused {
49
41
background: ${ ( { theme, light } ) => ( light ? theme . whiteColor : theme . primaryColor12 ) } ;
50
42
border-color: ${ ( { theme } ) => theme . primaryColor } ;
51
43
}
44
+ ` ;
45
+
46
+ const InputComponent = styled . input < InputProps & { isPassword ?: boolean } > `
47
+ outline: none;
48
+ width: ${ ( { isPassword } ) => ( isPassword ? "calc(100% - 22px)" : "100%" ) } ;
49
+ padding: 7px 8px 7px 8px;
50
+ font-size: 14px;
51
+ line-height: 20px;
52
+ font-weight: normal;
53
+ border: none;
54
+ background: none;
55
+ color: ${ ( { theme } ) => theme . textColor } ;
56
+ caret-color: ${ ( { theme } ) => theme . primaryColor } ;
57
+
58
+ &::placeholder {
59
+ color: ${ ( { theme } ) => theme . greyColor40 } ;
60
+ }
52
61
53
62
&:disabled {
54
63
pointer-events: none;
55
64
color: ${ ( { theme } ) => theme . greyColor55 } ;
56
65
}
57
66
` ;
58
67
59
- const Container = styled . div `
60
- width: 100%;
61
- position: relative;
62
- ` ;
63
-
64
68
const VisibilityButton = styled ( Button ) `
65
69
position: absolute;
66
- right: 2px;
67
- top: 7px;
70
+ right: 0px;
71
+ top: 0;
72
+ display: flex;
73
+ height: 100%;
74
+ width: 30px;
75
+ align-items: center;
76
+ justify-content: center;
77
+ border: none;
68
78
` ;
69
79
70
80
const Input : React . FC < InputProps > = ( props ) => {
71
- const [ isContentVisible , setIsContentVisible ] = useState ( false ) ;
72
-
73
- if ( props . type === "password" ) {
74
- return (
75
- < Container >
76
- < InputComponent { ...props } type = { isContentVisible ? "text" : "password" } />
77
- { props . disabled ? null : (
78
- < VisibilityButton iconOnly onClick = { ( ) => setIsContentVisible ( ! isContentVisible ) } type = "button" >
79
- < FontAwesomeIcon icon = { isContentVisible ? faEyeSlash : faEye } />
80
- </ VisibilityButton >
81
- ) }
82
- </ Container >
83
- ) ;
84
- }
85
-
86
- return < InputComponent { ...props } /> ;
81
+ const { formatMessage } = useIntl ( ) ;
82
+ const [ isContentVisible , setIsContentVisible ] = useToggle ( false ) ;
83
+ const [ focused , toggleFocused ] = useToggle ( false ) ;
84
+
85
+ const isPassword = props . type === "password" ;
86
+ const isVisibilityButtonVisible = isPassword && ! props . disabled ;
87
+ const type = isPassword ? ( isContentVisible ? "text" : "password" ) : props . type ;
88
+ const onInputFocusChange = ( ) => toggleFocused ( ) ;
89
+
90
+ return (
91
+ < InputContainer { ...props } className = { focused ? "input-container--focused" : undefined } >
92
+ < InputComponent
93
+ { ...props }
94
+ type = { type }
95
+ isPassword = { isPassword }
96
+ onFocus = { onInputFocusChange }
97
+ onBlur = { onInputFocusChange }
98
+ data-testid = "input"
99
+ />
100
+ { isVisibilityButtonVisible ? (
101
+ < VisibilityButton
102
+ iconOnly
103
+ onClick = { ( ) => setIsContentVisible ( ) }
104
+ type = "button"
105
+ aria-label = { formatMessage ( {
106
+ id : `ui.input.${ isContentVisible ? "hide" : "show" } Password` ,
107
+ } ) }
108
+ data-testid = "toggle-password-visibility-button"
109
+ >
110
+ < FontAwesomeIcon icon = { isContentVisible ? faEyeSlash : faEye } fixedWidth />
111
+ </ VisibilityButton >
112
+ ) : null }
113
+ </ InputContainer >
114
+ ) ;
87
115
} ;
88
116
89
117
export default Input ;
0 commit comments