1
1
import { useState , useRef , useEffect } from 'react' ;
2
2
import PropTypes from 'prop-types' ;
3
- import styled , { css } from 'styled-components' ;
4
- import { shadow , normalizeChars } from '../shared' ;
5
- import { baseFontSize , colors , spacing } from '../shared/theme' ;
3
+ import classNames from 'classnames' ;
4
+ import { normalizeChars } from '../shared' ;
6
5
import Icon from '../Icon' ;
7
6
8
7
import {
@@ -13,140 +12,10 @@ import {
13
12
RequiredMark ,
14
13
} from '../Input/sub-components' ;
15
14
import useKeyPress from './SubComponents/UseKeyPress' ;
15
+ import styles from './AutoComplete.module.css' ;
16
16
17
- const ITEM_HEIGHT = '44px' ;
18
- const MAX_ITEMS_VISIBILITY = 7 ;
19
- const DROPITEM_FONT_SIZE = baseFontSize * 0.875 ;
20
17
const NON_FOCUSABLE_SUGGESTION_INDEX = - 1 ;
21
18
22
- const ComponentWrapper = styled . div `
23
- ${ ( {
24
- theme : {
25
- colors : { neutral } ,
26
- } ,
27
- skin = 'default' ,
28
- } ) => css `
29
- position: relative;
30
- color: ${ skin === 'default' ? neutral [ 700 ] : neutral [ 0 ] } ;
31
- ` }
32
- ` ;
33
-
34
- const InputWrapper = styled . div `
35
- position: relative;
36
- ` ;
37
-
38
- const propsNotContainedInTextInput = [ 'theme' ] ;
39
-
40
- const InputText = styled ( TextInput ) . withConfig ( {
41
- shouldForwardProp : ( prop ) => ! propsNotContainedInTextInput . includes ( prop ) ,
42
- } ) `
43
- ${ ( {
44
- theme : {
45
- spacing : { xsmall } ,
46
- } ,
47
- } ) => css `
48
- margin-top: ${ xsmall } px;
49
- ` }
50
- ` ;
51
-
52
- const InputIcon = styled ( Icon ) `
53
- cursor: pointer;
54
- position: absolute;
55
- ${ ( {
56
- theme : {
57
- spacing : { xsmall, medium } ,
58
- } ,
59
- } ) => css `
60
- right: ${ medium } px;
61
- bottom: ${ xsmall * 1.25 } px;
62
- width: ${ baseFontSize * 1.5 } px;
63
- ` }
64
- ` ;
65
-
66
- const InputErrorIcon = styled ( InputIcon ) . attrs ( { name : 'error' } ) `
67
- ${ ( {
68
- theme : {
69
- colors : {
70
- error : { 700 : error700 } ,
71
- } ,
72
- } ,
73
- skin,
74
- } ) => css `
75
- color: ${ skin === 'default' ? error700 : 'inherit' } ;
76
- ` }
77
- ` ;
78
-
79
- const List = styled . ul `
80
- border-radius: 4px;
81
- box-sizing: border-box;
82
- list-style: none;
83
- max-height: calc(${ ITEM_HEIGHT } * ${ MAX_ITEMS_VISIBILITY } );
84
- overflow: auto;
85
- padding: 0;
86
- position: absolute;
87
- width: 100%;
88
- z-index: 1;
89
-
90
- ${ ( { theme } ) => {
91
- const {
92
- spacing : { xxsmall } ,
93
- colors : {
94
- neutral : { 0 : neutral0 , 700 : neutral700 } ,
95
- } ,
96
- } = theme ;
97
- return css `
98
- background-color: ${ neutral0 } ;
99
- margin-top: ${ xxsmall } px;
100
- ${ shadow ( 3 , neutral700 ) ( { theme } ) } ;
101
- ` ;
102
- } }
103
- ` ;
104
-
105
- const ListItem = styled . li `
106
- display: flex;
107
- align-items: center;
108
- justify-content: space-between;
109
- box-sizing: border-box;
110
- cursor: pointer;
111
- height: ${ ITEM_HEIGHT } ;
112
- ${ ( {
113
- theme : {
114
- spacing : { xsmall, medium } ,
115
- colors : {
116
- neutral : { 0 : neutral0 , 700 : neutral700 } ,
117
- } ,
118
- } ,
119
- } ) => css `
120
- color: ${ neutral700 } ;
121
- font-size: ${ DROPITEM_FONT_SIZE } px;
122
- background-color: ${ neutral0 } ;
123
- padding: ${ xsmall } px ${ medium } px;
124
- ` }
125
-
126
- &[aria-selected = 'true' ], &:hover {
127
- ${ ( {
128
- theme : {
129
- colors : {
130
- neutral : { 100 : neutral100 } ,
131
- } ,
132
- } ,
133
- } ) => css `
134
- background-color: ${ neutral100 } ;
135
- ` }
136
- }
137
- ` ;
138
-
139
- const PoliteStatus = styled . div `
140
- border: 0;
141
- clip: rect(0 0 0 0);
142
- height: 1px;
143
- margin: -1px;
144
- overflow: hidden;
145
- padding: 0;
146
- position: absolute;
147
- width: 1px;
148
- ` ;
149
-
150
19
const AutoComplete = ( {
151
20
id = '' ,
152
21
name = '' ,
@@ -157,15 +26,25 @@ const AutoComplete = ({
157
26
helperText = '' ,
158
27
placeholder = 'Select an option' ,
159
28
suggestions,
160
- theme = {
161
- spacing,
162
- colors,
163
- } ,
164
29
onSelectedItem = ( ) => { } ,
165
30
onChange = ( ) => { } ,
166
31
required = false ,
167
32
skin = 'default' ,
168
33
} ) => {
34
+ const wrapperClass = classNames ( styles . wrapper , {
35
+ [ styles [ 'wrapper-dark' ] ] : skin === 'dark' ,
36
+ } ) ;
37
+ const inputWrapperClass = classNames ( styles [ 'input-wrapper' ] ) ;
38
+ const inputTextClass = classNames ( styles [ 'input-text' ] ) ;
39
+ const inputIconClass = classNames ( styles [ 'input-icon' ] ) ;
40
+ const inputErrorIconClass = classNames (
41
+ styles [ 'input-icon' ] ,
42
+ styles [ `input-error-icon-${ skin } ` ] ,
43
+ ) ;
44
+ const listClass = classNames ( styles [ 'list-suggestions' ] , 'shadow-3' ) ;
45
+ const itemClass = classNames ( styles [ 'item-suggestions' ] ) ;
46
+ const politeClass = classNames ( styles [ 'polite-status' ] ) ;
47
+
169
48
const [ userTypedValue , setUserTypedValue ] = useState ( value ) ;
170
49
const [ filterSuggestions , setFilterSuggestions ] = useState ( suggestions ) ;
171
50
const [ filterSuggestionsLength , setFilterSuggestionsLength ] = useState (
@@ -248,27 +127,27 @@ const AutoComplete = ({
248
127
249
128
const generateSuggestions = ( ) =>
250
129
showSuggestions && filterSuggestions ? (
251
- < List
252
- theme = { theme }
130
+ < ul
253
131
role = "listbox"
254
132
id = "autocompleteOptions"
255
133
ref = { listOptions }
256
- skin = { skin }
134
+ className = { listClass }
257
135
>
258
136
{ filterSuggestions . map ( ( item , index ) => (
259
- < ListItem
137
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events
138
+ < li
260
139
key = { item }
261
140
aria-posinset = { index }
262
141
onClick = { ( ) => handleItemClick ( item ) }
263
- theme = { theme }
142
+ className = { itemClass }
264
143
aria-selected = { index === cursor }
265
144
role = "option"
266
145
tabIndex = "-1"
267
146
>
268
147
{ item }
269
- </ ListItem >
148
+ </ li >
270
149
) ) }
271
- </ List >
150
+ </ ul >
272
151
) : null ;
273
152
274
153
const generateAssistiveDescript = ( ) => {
@@ -346,13 +225,13 @@ const AutoComplete = ({
346
225
} , [ suggestions ] ) ;
347
226
348
227
return (
349
- < ComponentWrapper theme = { theme } skin = { skin } >
350
- < InputWrapper ref = { wrapperRef } >
228
+ < div className = { wrapperClass } >
229
+ < div className = { inputWrapperClass } ref = { wrapperRef } >
351
230
< InputLabel htmlFor = { id } >
352
231
{ label }
353
232
{ required && < RequiredMark skin = { skin } > *</ RequiredMark > }
354
233
</ InputLabel >
355
- < InputText
234
+ < TextInput
356
235
id = { id }
357
236
ref = { autoInputRef }
358
237
name = { name }
@@ -370,39 +249,40 @@ const AutoComplete = ({
370
249
onChange = { ( e ) => handleChange ( e . target . value ) }
371
250
skin = { skin }
372
251
required = { required }
373
- theme = { theme }
252
+ className = { inputTextClass }
374
253
/>
375
254
{ userTypedValue && ! error && ! disabled && (
376
- < InputIcon
377
- theme = { theme }
255
+ < Icon
378
256
name = "clear"
379
257
description = "limpar valor"
380
258
onClick = { ( ) => handleClearValue ( ) }
259
+ className = { inputIconClass }
381
260
/>
382
261
) }
383
262
{ generateSuggestions ( ) }
384
263
{ error && (
385
- < InputErrorIcon description = { error } theme = { theme } skin = { skin } />
264
+ < Icon
265
+ name = "error"
266
+ description = { error }
267
+ className = { inputErrorIconClass }
268
+ />
386
269
) }
387
- </ InputWrapper >
270
+ </ div >
388
271
{ helperText && < HelperText > { helperText } </ HelperText > }
389
- { error && (
390
- < InputErrorMessage theme = { theme } skin = { skin } >
391
- { error }
392
- </ InputErrorMessage >
393
- ) }
394
- < PoliteStatus role = "status" aria-atomic = "true" aria-live = "polite" >
272
+ { error && < InputErrorMessage skin = { skin } > { error } </ InputErrorMessage > }
273
+ < div
274
+ role = "status"
275
+ aria-atomic = "true"
276
+ aria-live = "polite"
277
+ className = { politeClass }
278
+ >
395
279
{ generateAssistiveDescript ( ) }
396
- </ PoliteStatus >
397
- </ ComponentWrapper >
280
+ </ div >
281
+ </ div >
398
282
) ;
399
283
} ;
400
284
401
285
AutoComplete . propTypes = {
402
- theme : PropTypes . shape ( {
403
- colors : PropTypes . object ,
404
- spacing : PropTypes . object ,
405
- } ) ,
406
286
/** A list of string or objects with the values to show in component */
407
287
suggestions : PropTypes . arrayOf ( PropTypes . string ) . isRequired ,
408
288
id : PropTypes . string ,
0 commit comments