@instructure/ui-selectable 10.4.2-snapshot-11 → 10.4.2-snapshot-13
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/CHANGELOG.md +1 -1
- package/package.json +9 -9
- package/src/Selectable/README.md +407 -179
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
-
## [10.4.2-snapshot-
|
|
6
|
+
## [10.4.2-snapshot-13](https://github.com/instructure/instructure-ui/compare/v10.4.1...v10.4.2-snapshot-13) (2024-11-07)
|
|
7
7
|
|
|
8
8
|
**Note:** Version bump only for package @instructure/ui-selectable
|
|
9
9
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@instructure/ui-selectable",
|
|
3
|
-
"version": "10.4.2-snapshot-
|
|
3
|
+
"version": "10.4.2-snapshot-13",
|
|
4
4
|
"description": "A UI component library made by Instructure Inc.",
|
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
|
6
6
|
"module": "./es/index.js",
|
|
@@ -23,17 +23,17 @@
|
|
|
23
23
|
},
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"@instructure/ui-babel-preset": "10.4.2-snapshot-
|
|
27
|
-
"@instructure/ui-test-utils": "10.4.2-snapshot-
|
|
26
|
+
"@instructure/ui-babel-preset": "10.4.2-snapshot-13",
|
|
27
|
+
"@instructure/ui-test-utils": "10.4.2-snapshot-13"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@babel/runtime": "^7.25.6",
|
|
31
|
-
"@instructure/console": "10.4.2-snapshot-
|
|
32
|
-
"@instructure/shared-types": "10.4.2-snapshot-
|
|
33
|
-
"@instructure/ui-dom-utils": "10.4.2-snapshot-
|
|
34
|
-
"@instructure/ui-react-utils": "10.4.2-snapshot-
|
|
35
|
-
"@instructure/ui-testable": "10.4.2-snapshot-
|
|
36
|
-
"@instructure/ui-utils": "10.4.2-snapshot-
|
|
31
|
+
"@instructure/console": "10.4.2-snapshot-13",
|
|
32
|
+
"@instructure/shared-types": "10.4.2-snapshot-13",
|
|
33
|
+
"@instructure/ui-dom-utils": "10.4.2-snapshot-13",
|
|
34
|
+
"@instructure/ui-react-utils": "10.4.2-snapshot-13",
|
|
35
|
+
"@instructure/ui-testable": "10.4.2-snapshot-13",
|
|
36
|
+
"@instructure/ui-utils": "10.4.2-snapshot-13",
|
|
37
37
|
"keycode": "^2",
|
|
38
38
|
"prop-types": "^15.8.1"
|
|
39
39
|
},
|
package/src/Selectable/README.md
CHANGED
|
@@ -4,153 +4,384 @@ describes: Selectable
|
|
|
4
4
|
|
|
5
5
|
`Selectable` is a low level utility component that can be used to create combobox widgets. Before composing your own component, make sure an existing component, like [Select](#Select), can't be adapted for your use case.
|
|
6
6
|
|
|
7
|
-
```javascript
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
selectedOptionId: this.props.options[0].id,
|
|
17
|
-
inputValue: this.props.options[0].label,
|
|
18
|
-
filteredOptions: this.props.options
|
|
19
|
-
}
|
|
7
|
+
- ```javascript
|
|
8
|
+
class CustomSelect extends React.Component {
|
|
9
|
+
state = {
|
|
10
|
+
isShowingOptions: false,
|
|
11
|
+
highlightedOptionId: this.props.options[0].id,
|
|
12
|
+
selectedOptionId: this.props.options[0].id,
|
|
13
|
+
inputValue: this.props.options[0].label,
|
|
14
|
+
filteredOptions: this.props.options
|
|
15
|
+
}
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
17
|
+
filterOptions = (value) => {
|
|
18
|
+
return this.props.options.filter((option) =>
|
|
19
|
+
option.label.toLowerCase().startsWith(value.toLowerCase())
|
|
20
|
+
)
|
|
21
|
+
}
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
matchValue() {
|
|
24
|
+
const { filteredOptions, inputValue, selectedOptionId } = this.state
|
|
25
|
+
if (filteredOptions.length === 1) {
|
|
26
|
+
if (
|
|
27
|
+
filteredOptions[0].label.toLowerCase() === inputValue.toLowerCase()
|
|
28
|
+
) {
|
|
29
|
+
return {
|
|
30
|
+
inputValue: filteredOptions[0].label,
|
|
31
|
+
selectedOptionId: filteredOptions[0].id
|
|
32
|
+
}
|
|
34
33
|
}
|
|
35
34
|
}
|
|
35
|
+
const index = this.getOptionIndex(
|
|
36
|
+
null,
|
|
37
|
+
selectedOptionId,
|
|
38
|
+
this.props.options
|
|
39
|
+
)
|
|
40
|
+
return { inputValue: this.props.options[index].label }
|
|
36
41
|
}
|
|
37
|
-
const index = this.getOptionIndex(null, selectedOptionId, this.props.options)
|
|
38
|
-
return { inputValue: this.props.options[index].label }
|
|
39
|
-
}
|
|
40
42
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
getInputStyles() {
|
|
44
|
+
return {
|
|
45
|
+
display: 'block',
|
|
46
|
+
width: '250px',
|
|
47
|
+
padding: '5px'
|
|
48
|
+
}
|
|
46
49
|
}
|
|
47
|
-
}
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
51
|
+
getListStyles() {
|
|
52
|
+
const { isShowingOptions } = this.state
|
|
53
|
+
return {
|
|
54
|
+
background: 'white',
|
|
55
|
+
listStyle: 'none',
|
|
56
|
+
padding: 0,
|
|
57
|
+
margin: 0,
|
|
58
|
+
border: isShowingOptions && 'solid 1px lightgray'
|
|
59
|
+
}
|
|
57
60
|
}
|
|
58
|
-
}
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
62
|
+
getOptionStyles(option) {
|
|
63
|
+
const { selectedOptionId, highlightedOptionId } = this.state
|
|
64
|
+
const selected = selectedOptionId === option.id
|
|
65
|
+
const highlighted = highlightedOptionId === option.id
|
|
66
|
+
let background = 'transparent'
|
|
67
|
+
if (selected) {
|
|
68
|
+
background = 'lightgray'
|
|
69
|
+
} else if (highlighted) {
|
|
70
|
+
background = '#eeeeee'
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
background,
|
|
74
|
+
padding: '0 10px'
|
|
75
|
+
}
|
|
73
76
|
}
|
|
74
|
-
}
|
|
75
77
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
78
|
+
getOptionIndex(direction, id, from) {
|
|
79
|
+
const { filteredOptions, highlightedOptionId } = this.state
|
|
80
|
+
const options = from ? from : filteredOptions
|
|
81
|
+
let index
|
|
82
|
+
|
|
83
|
+
for (let i = 0; i <= options.length - 1; i++) {
|
|
84
|
+
if (typeof id === 'undefined') {
|
|
85
|
+
if (highlightedOptionId === options[i].id) {
|
|
86
|
+
index = i + direction
|
|
87
|
+
if (index < 0) {
|
|
88
|
+
index = 0
|
|
89
|
+
} else if (index >= options.length - 1) {
|
|
90
|
+
index = options.length - 1
|
|
91
|
+
}
|
|
92
|
+
break
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
if (id === options[i].id) {
|
|
96
|
+
index = i
|
|
97
|
+
break
|
|
89
98
|
}
|
|
90
|
-
break
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
if (id === options[i].id) {
|
|
94
|
-
index = i
|
|
95
|
-
break
|
|
96
99
|
}
|
|
97
100
|
}
|
|
101
|
+
return index
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
getHandlers() {
|
|
105
|
+
return this.props.isDisabled
|
|
106
|
+
? {}
|
|
107
|
+
: {
|
|
108
|
+
onRequestShowOptions: (e) =>
|
|
109
|
+
this.setState((state) => ({
|
|
110
|
+
isShowingOptions: true,
|
|
111
|
+
highlightedOptionId: state.filteredOptions[0].id
|
|
112
|
+
})),
|
|
113
|
+
onRequestHideOptions: (e) => {
|
|
114
|
+
const index = this.getOptionIndex(
|
|
115
|
+
null,
|
|
116
|
+
this.state.selectedOptionId,
|
|
117
|
+
this.props.options
|
|
118
|
+
)
|
|
119
|
+
this.setState((state) => ({
|
|
120
|
+
isShowingOptions: false,
|
|
121
|
+
inputValue: this.props.options[index].label,
|
|
122
|
+
filteredOptions: this.props.options,
|
|
123
|
+
highlightedOptionId: null
|
|
124
|
+
}))
|
|
125
|
+
},
|
|
126
|
+
onRequestHighlightOption: (e, { id, direction }) => {
|
|
127
|
+
let index = this.getOptionIndex(direction, id)
|
|
128
|
+
this.setState((state) => ({
|
|
129
|
+
highlightedOptionId: state.filteredOptions[index]
|
|
130
|
+
? state.filteredOptions[index].id
|
|
131
|
+
: null,
|
|
132
|
+
inputValue:
|
|
133
|
+
direction && state.filteredOptions[index]
|
|
134
|
+
? state.filteredOptions[index].label
|
|
135
|
+
: state.inputValue
|
|
136
|
+
}))
|
|
137
|
+
},
|
|
138
|
+
onRequestSelectOption: (e, { id }) => {
|
|
139
|
+
const index = this.getOptionIndex(null, id)
|
|
140
|
+
this.setState((state) => ({
|
|
141
|
+
selectedOptionId: id,
|
|
142
|
+
inputValue: state.filteredOptions[index].label,
|
|
143
|
+
filteredOptions: this.props.options,
|
|
144
|
+
isShowingOptions: false,
|
|
145
|
+
highlightedOptionId: null
|
|
146
|
+
}))
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
render() {
|
|
152
|
+
const {
|
|
153
|
+
isShowingOptions,
|
|
154
|
+
inputValue,
|
|
155
|
+
highlightedOptionId,
|
|
156
|
+
selectedOptionId,
|
|
157
|
+
filteredOptions
|
|
158
|
+
} = this.state
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<Selectable
|
|
162
|
+
isShowingOptions={isShowingOptions}
|
|
163
|
+
highlightedOptionId={highlightedOptionId}
|
|
164
|
+
selectedOptionId={selectedOptionId ? selectedOptionId : null}
|
|
165
|
+
{...this.getHandlers()}
|
|
166
|
+
>
|
|
167
|
+
{({
|
|
168
|
+
getRootProps,
|
|
169
|
+
getLabelProps,
|
|
170
|
+
getInputProps,
|
|
171
|
+
getTriggerProps,
|
|
172
|
+
getListProps,
|
|
173
|
+
getOptionProps
|
|
174
|
+
}) => (
|
|
175
|
+
<span
|
|
176
|
+
style={{ display: 'inline-block' }}
|
|
177
|
+
{...getRootProps({ ref: (el) => (this.rootRef = el) })}
|
|
178
|
+
>
|
|
179
|
+
<label {...getLabelProps()}>Selectable Example</label>
|
|
180
|
+
<input
|
|
181
|
+
style={this.getInputStyles()}
|
|
182
|
+
{...getInputProps()}
|
|
183
|
+
{...getTriggerProps({
|
|
184
|
+
type: 'text',
|
|
185
|
+
value: inputValue,
|
|
186
|
+
onChange: (e) => {
|
|
187
|
+
const newOptions = this.filterOptions(e.target.value)
|
|
188
|
+
this.setState({
|
|
189
|
+
inputValue: e.target.value,
|
|
190
|
+
filteredOptions: newOptions,
|
|
191
|
+
isShowingOptions: true,
|
|
192
|
+
highlightedOptionId: newOptions[0]
|
|
193
|
+
? newOptions[0].id
|
|
194
|
+
: null
|
|
195
|
+
})
|
|
196
|
+
},
|
|
197
|
+
onBlur: (e) =>
|
|
198
|
+
this.setState({
|
|
199
|
+
filteredOptions: this.props.options,
|
|
200
|
+
highlightedOptionId: null,
|
|
201
|
+
isShowingOptions: false,
|
|
202
|
+
...this.matchValue()
|
|
203
|
+
})
|
|
204
|
+
})}
|
|
205
|
+
/>
|
|
206
|
+
<ul style={this.getListStyles()} {...getListProps()}>
|
|
207
|
+
{isShowingOptions &&
|
|
208
|
+
filteredOptions.map((option) => (
|
|
209
|
+
<li
|
|
210
|
+
key={option.id}
|
|
211
|
+
style={this.getOptionStyles(option)}
|
|
212
|
+
{...getOptionProps({ id: option.id })}
|
|
213
|
+
>
|
|
214
|
+
{option.label}
|
|
215
|
+
</li>
|
|
216
|
+
))}
|
|
217
|
+
</ul>
|
|
218
|
+
</span>
|
|
219
|
+
)}
|
|
220
|
+
</Selectable>
|
|
221
|
+
)
|
|
98
222
|
}
|
|
99
|
-
return index
|
|
100
223
|
}
|
|
101
224
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
225
|
+
render(
|
|
226
|
+
<View>
|
|
227
|
+
<CustomSelect
|
|
228
|
+
options={[
|
|
229
|
+
{ id: 'opt0', value: '0', label: 'Alabama' },
|
|
230
|
+
{ id: 'opt1', value: '1', label: 'Alaska' },
|
|
231
|
+
{ id: 'opt2', value: '2', label: 'American Samoa' },
|
|
232
|
+
{ id: 'opt3', value: '3', label: 'Arizona' },
|
|
233
|
+
{ id: 'opt4', value: '4', label: 'Arkansas' },
|
|
234
|
+
{ id: 'opt5', value: '5', label: 'California' },
|
|
235
|
+
{ id: 'opt6', value: '6', label: 'Colorado' },
|
|
236
|
+
{ id: 'opt7', value: '7', label: 'Connecticut' },
|
|
237
|
+
{ id: 'opt8', value: '8', label: 'Delaware' },
|
|
238
|
+
{ id: 'opt9', value: '9', label: 'District Of Columbia' },
|
|
239
|
+
{ id: 'opt10', value: '10', label: 'Federated States Of Micronesia' },
|
|
240
|
+
{ id: 'opt11', value: '11', label: 'Florida' },
|
|
241
|
+
{ id: 'opt12', value: '12', label: 'Georgia' },
|
|
242
|
+
{ id: 'opt13', value: '13', label: 'Guam' },
|
|
243
|
+
{ id: 'opt14', value: '14', label: 'Hawaii' },
|
|
244
|
+
{ id: 'opt15', value: '15', label: 'Idaho' },
|
|
245
|
+
{ id: 'opt16', value: '16', label: 'Illinois' }
|
|
246
|
+
]}
|
|
247
|
+
/>
|
|
248
|
+
</View>
|
|
249
|
+
)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
- ```js
|
|
253
|
+
const CustomSelect = ({ options, isDisabled }) => {
|
|
254
|
+
const [isShowingOptions, setIsShowingOptions] = useState(false)
|
|
255
|
+
const [highlightedOptionId, setHighlightedOptionId] = useState(
|
|
256
|
+
options[0].id
|
|
257
|
+
)
|
|
258
|
+
const [selectedOptionId, setSelectedOptionId] = useState(options[0].id)
|
|
259
|
+
const [inputValue, setInputValue] = useState(options[0].label)
|
|
260
|
+
const [filteredOptions, setFilteredOptions] = useState(options)
|
|
261
|
+
|
|
262
|
+
const rootRef = useRef(null)
|
|
263
|
+
|
|
264
|
+
const filterOptions = (value) => {
|
|
265
|
+
return options.filter((option) =>
|
|
266
|
+
option.label.toLowerCase().startsWith(value.toLowerCase())
|
|
267
|
+
)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const matchValue = () => {
|
|
271
|
+
if (filteredOptions.length === 1) {
|
|
272
|
+
if (
|
|
273
|
+
filteredOptions[0].label.toLowerCase() === inputValue.toLowerCase()
|
|
274
|
+
) {
|
|
275
|
+
setInputValue(filteredOptions[0].label)
|
|
276
|
+
setsSelectedOptionId(filteredOptions[0].id)
|
|
277
|
+
return
|
|
278
|
+
}
|
|
135
279
|
}
|
|
280
|
+
const index = getOptionIndex(null, selectedOptionId, options)
|
|
281
|
+
setInputValue(options[index].label)
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
const getInputStyles = () => {
|
|
285
|
+
return {
|
|
286
|
+
display: 'block',
|
|
287
|
+
width: '250px',
|
|
288
|
+
padding: '5px'
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const getListStyles = () => {
|
|
293
|
+
return {
|
|
294
|
+
background: 'white',
|
|
295
|
+
listStyle: 'none',
|
|
296
|
+
padding: 0,
|
|
297
|
+
margin: 0,
|
|
298
|
+
border: isShowingOptions && 'solid 1px lightgray'
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const getOptionStyles = (option) => {
|
|
303
|
+
const selected = selectedOptionId === option.id
|
|
304
|
+
const highlighted = highlightedOptionId === option.id
|
|
305
|
+
let background = 'transparent'
|
|
306
|
+
if (selected) {
|
|
307
|
+
background = 'lightgray'
|
|
308
|
+
} else if (highlighted) {
|
|
309
|
+
background = '#eeeeee'
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
background,
|
|
313
|
+
padding: '0 10px'
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const getOptionIndex = (direction, id, from) => {
|
|
318
|
+
const options = from ? from : filteredOptions
|
|
319
|
+
let index
|
|
320
|
+
|
|
321
|
+
for (let i = 0; i <= options.length - 1; i++) {
|
|
322
|
+
if (typeof id === 'undefined') {
|
|
323
|
+
if (highlightedOptionId === options[i].id) {
|
|
324
|
+
index = i + direction
|
|
325
|
+
if (index < 0) {
|
|
326
|
+
index = 0
|
|
327
|
+
} else if (index >= options.length - 1) {
|
|
328
|
+
index = options.length - 1
|
|
329
|
+
}
|
|
330
|
+
break
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
if (id === options[i].id) {
|
|
334
|
+
index = i
|
|
335
|
+
break
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return index
|
|
136
340
|
}
|
|
137
|
-
}
|
|
138
341
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
342
|
+
const getHandlers = () => {
|
|
343
|
+
return isDisabled
|
|
344
|
+
? {}
|
|
345
|
+
: {
|
|
346
|
+
onRequestShowOptions: (e) => {
|
|
347
|
+
setIsShowingOptions(true)
|
|
348
|
+
setHighlightedOptionId(filteredOptions[0].id)
|
|
349
|
+
},
|
|
350
|
+
onRequestHideOptions: (e) => {
|
|
351
|
+
const index = getOptionIndex(null, selectedOptionId, options)
|
|
352
|
+
setIsShowingOptions(false)
|
|
353
|
+
setInputValue(options[index].label)
|
|
354
|
+
setFilteredOptions(options)
|
|
355
|
+
setHighlightedOptionId(null)
|
|
356
|
+
},
|
|
357
|
+
onRequestHighlightOption: (e, { id, direction }) => {
|
|
358
|
+
let index = getOptionIndex(direction, id)
|
|
359
|
+
setHighlightedOptionId(
|
|
360
|
+
filteredOptions[index] ? filteredOptions[index].id : null
|
|
361
|
+
)
|
|
362
|
+
setInputValue(
|
|
363
|
+
direction && filteredOptions[index]
|
|
364
|
+
? filteredOptions[index].label
|
|
365
|
+
: inputValue
|
|
366
|
+
)
|
|
367
|
+
},
|
|
368
|
+
onRequestSelectOption: (e, { id }) => {
|
|
369
|
+
const index = getOptionIndex(null, id)
|
|
370
|
+
setSelectedOptionId(id)
|
|
371
|
+
setInputValue(filteredOptions[index].label)
|
|
372
|
+
setFilteredOptions(options)
|
|
373
|
+
setIsShowingOptions(false)
|
|
374
|
+
setHighlightedOptionId(null)
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
147
378
|
|
|
148
379
|
return (
|
|
149
380
|
<Selectable
|
|
150
381
|
isShowingOptions={isShowingOptions}
|
|
151
382
|
highlightedOptionId={highlightedOptionId}
|
|
152
383
|
selectedOptionId={selectedOptionId ? selectedOptionId : null}
|
|
153
|
-
{...
|
|
384
|
+
{...getHandlers()}
|
|
154
385
|
>
|
|
155
386
|
{({
|
|
156
387
|
getRootProps,
|
|
@@ -161,80 +392,77 @@ class CustomSelect extends React.Component {
|
|
|
161
392
|
getOptionProps
|
|
162
393
|
}) => (
|
|
163
394
|
<span
|
|
164
|
-
style={{display: 'inline-block'}}
|
|
165
|
-
{...getRootProps({ref:
|
|
395
|
+
style={{ display: 'inline-block' }}
|
|
396
|
+
{...getRootProps({ ref: rootRef })}
|
|
166
397
|
>
|
|
167
398
|
<label {...getLabelProps()}>Selectable Example</label>
|
|
168
399
|
<input
|
|
169
|
-
style={
|
|
400
|
+
style={getInputStyles()}
|
|
170
401
|
{...getInputProps()}
|
|
171
402
|
{...getTriggerProps({
|
|
172
403
|
type: 'text',
|
|
173
404
|
value: inputValue,
|
|
174
405
|
onChange: (e) => {
|
|
175
|
-
const newOptions =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
406
|
+
const newOptions = filterOptions(e.target.value)
|
|
407
|
+
setInputValue(e.target.valu)
|
|
408
|
+
setFilteredOptions(newOptions)
|
|
409
|
+
setIsShowingOptions(true)
|
|
410
|
+
setHighlightedOptionId(
|
|
411
|
+
newOptions[0] ? newOptions[0].id : null
|
|
412
|
+
)
|
|
182
413
|
},
|
|
183
|
-
onBlur: (e) =>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
}
|
|
189
|
-
})
|
|
190
|
-
|
|
191
|
-
<ul
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
</li>
|
|
203
|
-
))}
|
|
414
|
+
onBlur: (e) => {
|
|
415
|
+
setFilteredOptions(options)
|
|
416
|
+
setHighlightedOptionId(null)
|
|
417
|
+
setIsShowingOptions(false)
|
|
418
|
+
matchValue()
|
|
419
|
+
}
|
|
420
|
+
})}
|
|
421
|
+
/>
|
|
422
|
+
<ul style={getListStyles()} {...getListProps()}>
|
|
423
|
+
{isShowingOptions &&
|
|
424
|
+
filteredOptions.map((option) => (
|
|
425
|
+
<li
|
|
426
|
+
key={option.id}
|
|
427
|
+
style={getOptionStyles(option)}
|
|
428
|
+
{...getOptionProps({ id: option.id })}
|
|
429
|
+
>
|
|
430
|
+
{option.label}
|
|
431
|
+
</li>
|
|
432
|
+
))}
|
|
204
433
|
</ul>
|
|
205
434
|
</span>
|
|
206
435
|
)}
|
|
207
436
|
</Selectable>
|
|
208
437
|
)
|
|
209
438
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
```
|
|
439
|
+
|
|
440
|
+
render(
|
|
441
|
+
<View>
|
|
442
|
+
<CustomSelect
|
|
443
|
+
options={[
|
|
444
|
+
{ id: 'opt0', value: '0', label: 'Alabama' },
|
|
445
|
+
{ id: 'opt1', value: '1', label: 'Alaska' },
|
|
446
|
+
{ id: 'opt2', value: '2', label: 'American Samoa' },
|
|
447
|
+
{ id: 'opt3', value: '3', label: 'Arizona' },
|
|
448
|
+
{ id: 'opt4', value: '4', label: 'Arkansas' },
|
|
449
|
+
{ id: 'opt5', value: '5', label: 'California' },
|
|
450
|
+
{ id: 'opt6', value: '6', label: 'Colorado' },
|
|
451
|
+
{ id: 'opt7', value: '7', label: 'Connecticut' },
|
|
452
|
+
{ id: 'opt8', value: '8', label: 'Delaware' },
|
|
453
|
+
{ id: 'opt9', value: '9', label: 'District Of Columbia' },
|
|
454
|
+
{ id: 'opt10', value: '10', label: 'Federated States Of Micronesia' },
|
|
455
|
+
{ id: 'opt11', value: '11', label: 'Florida' },
|
|
456
|
+
{ id: 'opt12', value: '12', label: 'Georgia' },
|
|
457
|
+
{ id: 'opt13', value: '13', label: 'Guam' },
|
|
458
|
+
{ id: 'opt14', value: '14', label: 'Hawaii' },
|
|
459
|
+
{ id: 'opt15', value: '15', label: 'Idaho' },
|
|
460
|
+
{ id: 'opt16', value: '16', label: 'Illinois' }
|
|
461
|
+
]}
|
|
462
|
+
/>
|
|
463
|
+
</View>
|
|
464
|
+
)
|
|
465
|
+
```
|
|
238
466
|
|
|
239
467
|
Selectable has very few opinions about how a combobox component should be composed. It mostly aims to ensure all the proper WAI-ARIA roles and attributes are set on the right elements at the right times. Selectable uses a combination of controllable props and prop getters to set these attributes and provide accessible behavior.
|
|
240
468
|
|