1 | <h1 align="center">
|
2 | downshift 🏎
|
3 | <br>
|
4 | <img src="https://cdn.rawgit.com/paypal/downshift/d9e94139/other/logo/downshift.svg" alt="downshift logo" title="downshift logo" width="300">
|
5 | <br>
|
6 | </h1>
|
7 | <p align="center" style="font-size: 1.2rem;">Primitives to build simple, flexible, WAI-ARIA compliant React
|
8 | autocomplete/dropdown/select/combobox components</p>
|
9 |
|
10 | <hr />
|
11 |
|
12 | [![Build Status][build-badge]][build]
|
13 | [![Code Coverage][coverage-badge]][coverage]
|
14 | [![downloads][downloads-badge]][npmcharts]
|
15 | [![version][version-badge]][package]
|
16 | [![MIT License][license-badge]][LICENSE]
|
17 |
|
18 | [![All Contributors](https://img.shields.io/badge/all_contributors-18-orange.svg?style=flat-square)](#contributors)
|
19 | [![PRs Welcome][prs-badge]][prs]
|
20 | [![Chat][chat-badge]][chat]
|
21 | [![Code of Conduct][coc-badge]][coc]
|
22 |
|
23 | [![Supports React and Preact][react-badge]][react]
|
24 | [![size][size-badge]][unpkg-dist]
|
25 | [![gzip size][gzip-badge]][unpkg-dist]
|
26 | [![module formats: umd, cjs, and es][module-formats-badge]][unpkg-dist]
|
27 |
|
28 | [![Watch on GitHub][github-watch-badge]][github-watch]
|
29 | [![Star on GitHub][github-star-badge]][github-star]
|
30 | [![Tweet][twitter-badge]][twitter]
|
31 |
|
32 | ## The problem
|
33 |
|
34 | You need an autocomplete/dropdown/select experience in your application and you
|
35 | want it to be accessible. You also want it to be simple and flexible to account
|
36 | for your use cases.
|
37 |
|
38 | ## This solution
|
39 |
|
40 | This is a component that controls user interactions and state for you so you can
|
41 | create autocomplete/dropdown/select/etc. components. It's based on ideas from
|
42 | the talk ["Compound Components"][compound-components-talk] which effectively
|
43 | gives you maximum flexibility with a minimal API because you are responsible
|
44 | for the rendering of everything and you simply apply props to what you're
|
45 | rendering.
|
46 |
|
47 | This differs from other solutions which render things for their use case and
|
48 | then expose many options to allow for extensibility causing an API that is less
|
49 | easy to use and less flexible as well as making the implementation more
|
50 | complicated and harder to contribute to.
|
51 |
|
52 | > NOTE: The original use case of this component is autocomplete, however the API
|
53 | > is powerful and flexible enough to build things like dropdowns as well.
|
54 |
|
55 | ## Installation
|
56 |
|
57 | **This component is currently released as a Release Candidate. It is quite stable, but not officially released yet.**
|
58 |
|
59 | This module is distributed via [npm][npm] which is bundled with [node][node] and
|
60 | should be installed as one of your project's `dependencies`:
|
61 |
|
62 | ```
|
63 | npm install --save downshift@rc
|
64 | ```
|
65 |
|
66 | > This package also depends on `react` and `prop-types`. Please make sure you
|
67 | > have those installed as well.
|
68 |
|
69 | > Note also this library supports `preact` out of the box. If you are using
|
70 | > `preact` then look in the `dist/` folder and use the module you want with the
|
71 | > `preact` suffix.
|
72 |
|
73 | ## Usage
|
74 |
|
75 | ```jsx
|
76 | import Downshift from 'downshift'
|
77 |
|
78 | function BasicAutocomplete({items, onChange}) {
|
79 | return (
|
80 | <Downshift onChange={onChange}>
|
81 | {({
|
82 | getInputProps,
|
83 | getItemProps,
|
84 | isOpen,
|
85 | inputValue,
|
86 | selectedItem,
|
87 | highlightedIndex
|
88 | }) => (
|
89 | <div>
|
90 | <input {...getInputProps({placeholder: 'Favorite color ?'})} />
|
91 | {isOpen ? (
|
92 | <div style={{border: '1px solid #ccc'}}>
|
93 | {items
|
94 | .filter(
|
95 | i =>
|
96 | !inputValue ||
|
97 | i.toLowerCase().includes(inputValue.toLowerCase()),
|
98 | )
|
99 | .map((item, index) => (
|
100 | <div
|
101 | {...getItemProps({item, index})}
|
102 | key={item}
|
103 | style={{
|
104 | backgroundColor:
|
105 | highlightedIndex === index ? 'gray' : 'white',
|
106 | fontWeight: selectedItem === item ? 'bold' : 'normal',
|
107 | }}
|
108 | >
|
109 | {item}
|
110 | </div>
|
111 | ))}
|
112 | </div>
|
113 | ) : null}
|
114 | </div>
|
115 | )}
|
116 | </Downshift>
|
117 | )
|
118 | }
|
119 |
|
120 | function App() {
|
121 | return (
|
122 | <BasicAutocomplete
|
123 | items={['apple', 'orange', 'carrot']}
|
124 | onChange={selectedItem => console.log(selectedItem)}
|
125 | />
|
126 | )
|
127 | }
|
128 | ```
|
129 |
|
130 | `downshift` is the only component. It doesn't render anything itself, it just
|
131 | calls the child function and renders that. Wrap everything in
|
132 | `<Downshift>{/* your function here! */}</Downshift>`.
|
133 |
|
134 | ## Props:
|
135 |
|
136 | ### defaultSelectedItem
|
137 |
|
138 | > `any` | defaults to `null`
|
139 |
|
140 | Pass an item or an array of items that should be selected by default.
|
141 |
|
142 | ### defaultHighlightedIndex
|
143 |
|
144 | > `number`/`null` | defaults to `null`
|
145 |
|
146 | This is the initial index to highlight when the menu first opens.
|
147 |
|
148 | ### defaultInputValue
|
149 |
|
150 | > `string` | defaults to `''`
|
151 |
|
152 | This is the initial input value.
|
153 |
|
154 | ### defaultIsOpen
|
155 |
|
156 | > `boolean` | defaults to `false`
|
157 |
|
158 | This is the initial `isOpen` value.
|
159 |
|
160 | ### itemToString
|
161 |
|
162 | > `function(item: any)` | defaults to: `i => (i == null ? '' : String(i))`
|
163 |
|
164 | Used to determine the string value for the selected item (which is used to
|
165 | compute the `inputValue`.
|
166 |
|
167 | ### getA11yStatusMessage
|
168 |
|
169 | > `function({/* see below */})` | default messages provided in English
|
170 |
|
171 | This function is passed as props to a `Status` component nested within and
|
172 | allows you to create your own assertive ARIA statuses.
|
173 |
|
174 | A default `getA11yStatusMessage` function is provided that will check
|
175 | `resultCount` and return "No results." or if there are results but no item is
|
176 | highlighted, "`resultCount` results are available, use up and down arrow keys
|
177 | to navigate." If an item is highlighted it will run
|
178 | `itemToString(highlightedItem)` and display the value of the `highlightedItem`.
|
179 |
|
180 | The object you are passed to generate your status message has the following
|
181 | properties:
|
182 |
|
183 |
|
184 |
|
185 | | property | type | description |
|
186 | |-----------------------|-----------------|----------------------------------------------------------------------------------------------|
|
187 | | `highlightedIndex` | `number`/`null` | The currently highlighted index |
|
188 | | `highlightedValue` | `any` | The value of the highlighted item |
|
189 | | `inputValue` | `string` | The current input value |
|
190 | | `isOpen` | `boolean` | The `isOpen` state |
|
191 | | `itemToString` | `function(any)` | The `itemToString` function (see props) for getting the string value from one of the options |
|
192 | | `previousResultCount` | `number` | The total items showing in the dropdown the last time the status was updated |
|
193 | | `resultCount` | `number` | The total items showing in the dropdown |
|
194 | | `selectedItem` | `any` | The value of the currently selected item |
|
195 |
|
196 | ### onChange
|
197 |
|
198 | > `function(selectedItem: any, allState: object)` | optional, no useful default
|
199 |
|
200 | Called when the user selects an item. Called with the item that was selected
|
201 | and the new state of `downshift`. (see `onStateChange` for more info on
|
202 | `allState`).
|
203 |
|
204 | ### onStateChange
|
205 |
|
206 | > `function(changes: object, allState: object)` | optional, no useful default
|
207 |
|
208 | This function is called anytime the internal state changes. This can be useful
|
209 | if you're using downshift as a "controlled" component, where you manage some or
|
210 | all of the state (e.g. isOpen, selectedItem, highlightedIndex, etc) and then
|
211 | pass it as props, rather than letting downshift control all its state itself.
|
212 | The parameters both take the shape of internal state
|
213 | (`{highlightedIndex: number, inputValue: string, isOpen: boolean, selectedItem: any}`)
|
214 | but differ slightly.
|
215 |
|
216 | - `changes`: These are the properties that actually have changed since the last
|
217 | state change
|
218 | - `allState`: This is the full state object of all the state in your `downshift`
|
219 | component.
|
220 |
|
221 | ### itemCount
|
222 |
|
223 | > `number` | optional, defaults the number of times you call getItemProps
|
224 |
|
225 | This is useful if you're using some kind of virtual listing component for
|
226 | "windowing" (like [`react-virtualized`](https://github.com/bvaughn/react-virtualized)).
|
227 |
|
228 | ### highlightedIndex
|
229 |
|
230 | > `number` | **control prop** (read more about this in the "Control Props" section below)
|
231 |
|
232 | The index that should be highlighted
|
233 |
|
234 | ### inputValue
|
235 |
|
236 | > `string` | **control prop** (read more about this in the "Control Props" section below)
|
237 |
|
238 | The value the input should have
|
239 |
|
240 | ### isOpen
|
241 |
|
242 | > `boolean` | **control prop** (read more about this in the "Control Props" section below)
|
243 |
|
244 | Whether the menu should be considered open or closed. Some aspects of the
|
245 | downshift component respond differently based on this value (for example, if
|
246 | `isOpen` is true when the user hits "Enter" on the input field, then the
|
247 | item at the `highlightedIndex` item is selected).
|
248 |
|
249 | ### `selectedItem`
|
250 |
|
251 | > `any`/`Array(any)` | **control prop** (read more about this in the "Control Props" section below)
|
252 |
|
253 | The currently selected item.
|
254 |
|
255 | ### children
|
256 |
|
257 | > `function({})` | *required*
|
258 |
|
259 | This is called with an object. Read more about the properties of this object
|
260 | in the section "Child Callback Function"
|
261 |
|
262 | ## Control Props
|
263 |
|
264 | downshift manages its own state internally and calls your `onChange` and
|
265 | `onStateChange` handlers with any relevant changes. The state that downshift
|
266 | manages includes: `isOpen`, `selectedItem`, `inputValue`, and
|
267 | `highlightedIndex`. Your child callback function (read more below) can be used
|
268 | to manipulate this state from within the render function and can likely support
|
269 | many of your use cases.
|
270 |
|
271 | However, if more control is needed, you can pass any of these pieces of state as
|
272 | a prop (as indicated above) and that state becomes controlled. As soon as
|
273 | `this.props[statePropKey] !== undefined`, internally, `downshift` will determine
|
274 | its state based on your prop's value rather than its own internal state. You
|
275 | will be required to keep the state up to date (this is where `onStateChange`
|
276 | comes in really handy), but you can also control the state from anywhere, be
|
277 | that state from other components, `redux` (example wanted!), `react-router`
|
278 | (example wanted!), or anywhere else.
|
279 |
|
280 | ## Child Callback Function
|
281 |
|
282 | This is where you render whatever you want to based on the state of `downshift`.
|
283 | The function is passed as the child prop:
|
284 | `<Downshift>{/* right here*/}</Downshift>`
|
285 |
|
286 | The properties of this object can be split into three categories as indicated
|
287 | below:
|
288 |
|
289 | ### prop getters
|
290 |
|
291 | These functions are used to apply props to the elements that you render.
|
292 | This gives you maximum flexibility to render what, when, and wherever you like.
|
293 | You call these on the element in question (for example:
|
294 | `<input {...getInputProps()}`)). It's advisable to pass all your props to that
|
295 | function rather than applying them on the element yourself to avoid your props
|
296 | being overridden (or overriding the props returned). For example:
|
297 | `getInputProps({onKeyUp(event) {console.log(event)}})`.
|
298 |
|
299 |
|
300 |
|
301 | | property | type | description |
|
302 | |------------------|----------------|---------------------------------------------------------------------------------------------|
|
303 | | `getButtonProps` | `function({})` | returns the props you should apply to any menu toggle button element you render. |
|
304 | | `getInputProps` | `function({})` | returns the props you should apply to the `input` element that you render. |
|
305 | | `getItemProps` | `function({})` | returns the props you should apply to any menu item elements you render. |
|
306 | | `getLabelProps` | `function({})` | returns the props you should apply to the `label` element that you render. |
|
307 | | `getRootProps` | `function({})` | returns the props you should apply to the root element that you render. It can be optional. |
|
308 |
|
309 | #### `getRootProps`
|
310 |
|
311 | Most of the time, you can just render a `div` yourself and `Downshift` will
|
312 | apply the props it needs to do its job (and you don't need to call this
|
313 | function). However, if you're rendering a composite component (custom component)
|
314 | as the root element, then you'll need to call `getRootProps` and apply that to
|
315 | your root element.
|
316 |
|
317 | Required properties:
|
318 |
|
319 | - `refKey`: if you're rendering a composite component, that component will need
|
320 | to accept a prop which it forwards to the root DOM element. Commonly, folks
|
321 | call this `innerRef`. So you'd call: `getRootProps({refKey: 'innerRef'})`
|
322 | and your composite component would forward like: `<div ref={props.innerRef} />`
|
323 |
|
324 | #### `getInputProps`
|
325 |
|
326 | This method should be applied to the `input` you render. It is recommended that
|
327 | you pass all props as an object to this method which will compose together any
|
328 | of the event handlers you need to apply to the `input` while preserving the
|
329 | ones that `downshift` needs to apply to make the `input` behave.
|
330 |
|
331 | There are no required properties for this method.
|
332 |
|
333 | #### `getLabelProps`
|
334 |
|
335 | This method should be applied to the `label` you render. It is useful for
|
336 | ensuring that the `for` attribute on the `<label>` (`htmlFor` as a react prop)
|
337 | is the same as the `id` that appears on the `input`. If no `htmlFor` is provided
|
338 | then an ID will be generated and used for the `input` and the `label` `for`
|
339 | attribute.
|
340 |
|
341 | There are no required properties for this method.
|
342 |
|
343 | > Note: You can definitely get by without using this (just provide an `id` to
|
344 | > your input and the same `htmlFor` to your `label` and you'll be good with
|
345 | > accessibility). However, we include this so you don't forget and it makes
|
346 | > things a little nicer for you. You're welcome 😀
|
347 |
|
348 | #### `getItemProps`
|
349 |
|
350 | This method should be applied to any menu items you render. You pass it an object
|
351 | and that object must contain `index` (number) and `item` (anything) properties.
|
352 |
|
353 | Required properties:
|
354 |
|
355 | - `index`: this is how `downshift` keeps track of your item when
|
356 | updating the `highlightedIndex` as the user keys around.
|
357 | - `item`: this is the item data that will be selected when the user selects a
|
358 | particular item.
|
359 |
|
360 | #### `getButtonProps`
|
361 |
|
362 | Call this and apply the returned props to a `button`. It allows you to toggle
|
363 | the `Menu` component. You can definitely build something like this yourself
|
364 | (all of the available APIs are exposed to you), but this is nice because it
|
365 | will also apply all of the proper ARIA attributes. The `aria-label` prop is in
|
366 | English. You should probably override this yourself so you can provide
|
367 | translations:
|
368 |
|
369 | ```jsx
|
370 | <button {...getButtonProps({
|
371 | 'aria-label': translateWithId(isOpen ? 'close.menu' : 'open.menu'),
|
372 | })} />
|
373 | ```
|
374 |
|
375 | ### actions
|
376 |
|
377 | These are functions you can call to change the state of the downshift component.
|
378 |
|
379 |
|
380 |
|
381 | | property | type | description |
|
382 | |-------------------------|----------------------------|------------------------------------------------------------------------------------------------------------------|
|
383 | | `clearSelection` | `function()` | clears the selection |
|
384 | | `closeMenu` | `function()` | closes the menu |
|
385 | | `openMenu` | `function()` | opens the menu |
|
386 | | `selectHighlightedItem` | `function()` | selects the item that is currently highlighted |
|
387 | | `selectItem` | `function(item: any)` | selects the given item |
|
388 | | `selectItemAtIndex` | `function(index: number)` | selects the item at the given index |
|
389 | | `setHighlightedIndex` | `function(index: number)` | call to set a new highlighted index |
|
390 | | `toggleMenu` | `function(state: boolean)` | toggle the menu open state (if `state` is not provided, then it will be set to the inverse of the current state) |
|
391 |
|
392 | ### state
|
393 |
|
394 | These are values that represent the current state of the downshift component.
|
395 |
|
396 |
|
397 |
|
398 | | property | type | description |
|
399 | |--------------------|-------------------|------------------------------------------------|
|
400 | | `highlightedIndex` | `number` / `null` | the currently highlighted item |
|
401 | | `inputValue` | `string` / `null` | the current value of the `getInputProps` input |
|
402 | | `isOpen` | `boolean` | the menu open state |
|
403 | | `selectedItem` | `any` | the currently selected item input |
|
404 |
|
405 | ## Examples
|
406 |
|
407 | Examples exist on [codesandbox.io][examples]:
|
408 |
|
409 | - [multiple selection example](https://codesandbox.io/s/W6gyJ30kn) (uses controlled `selectedItem` API).
|
410 | - [downshift Apollo example](https://codesandbox.io/s/j2omZpK3W)
|
411 | - [downshift Spectre.css example](https://codesandbox.io/s/M89KQOBRB)
|
412 | - [Material UI (1.0.0-beta.4) Combobox Using Downshift](https://codesandbox.io/s/QMGq4kAY)
|
413 | - [Integration with `GenieJS`](https://codesandbox.io/s/jRLKrxwgl) ([learn more about `genie` here](https://github.com/kentcdodds/genie))
|
414 | - [Handling and displaying errors](https://codesandbox.io/s/zKE37vorr)
|
415 |
|
416 | If you would like to add an example, follow these steps:
|
417 |
|
418 | 1. Fork [this codesandbox](http://kcd.im/ds-example)
|
419 | 2. Make sure your version (under dependencies) is the latest available version.
|
420 | 3. Update the title and description
|
421 | 4. Update the code for your example (add some form of documentation to explain what it is)
|
422 | 5. Add the tag: `downshift:example`
|
423 |
|
424 | ## Inspiration
|
425 |
|
426 | I was heavily inspired by [Ryan Florence][ryan] and his talk entitled:
|
427 | ["Compound Components"][compound-components-talk]. I also took a few ideas from
|
428 | the code in [`react-autocomplete`][react-autocomplete] and
|
429 | [jQuery UI's Autocomplete][jquery-complete].
|
430 |
|
431 | The `getXProps` functions were inspired by [Jared Forsyth][jared]. That bit of
|
432 | inspiration made a big impact on the flexibility and simplicity of this API.
|
433 |
|
434 | You can watch me build the first iteration of `downshift` on YouTube:
|
435 |
|
436 | - [Part 1](https://www.youtube.com/watch?v=2kzD1IjDy5s&list=PLV5CVI1eNcJh5CTgArGVwANebCrAh2OUE&index=11)
|
437 | - [Part 2](https://www.youtube.com/watch?v=w1Z7Jvj08_s&list=PLV5CVI1eNcJh5CTgArGVwANebCrAh2OUE&index=10)
|
438 |
|
439 | You'll find more recordings of me working on `downshift` on [my livestream YouTube playlist][yt-playlist].
|
440 |
|
441 | ## Other Solutions
|
442 |
|
443 | You can implement these other solutions using `downshift`, but if
|
444 | you'd prefer to use these out of the box solutions, then that's fine too:
|
445 |
|
446 | - [`react-select`](https://github.com/JedWatson/react-select)
|
447 | - [`react-autocomplete`](https://github.com/reactjs/react-autocomplete)
|
448 |
|
449 | ## Contributors
|
450 |
|
451 | Thanks goes to these people ([emoji key][emojis]):
|
452 |
|
453 |
|
454 | | [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub>Kent C. Dodds</sub>](https://kentcdodds.com)<br />[💻](https://github.com/paypal/downshift/commits?author=kentcdodds "Code") [📖](https://github.com/paypal/downshift/commits?author=kentcdodds "Documentation") [🚇](#infra-kentcdodds "Infrastructure (Hosting, Build-Tools, etc)") [⚠️](https://github.com/paypal/downshift/commits?author=kentcdodds "Tests") | [<img src="https://avatars0.githubusercontent.com/u/100200?v=4" width="100px;"/><br /><sub>Ryan Florence</sub>](http://twitter.com/ryanflorence)<br />[🤔](#ideas-ryanflorence "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/112170?v=4" width="100px;"/><br /><sub>Jared Forsyth</sub>](http://jaredforsyth.com)<br />[🤔](#ideas-jaredly "Ideas, Planning, & Feedback") [📖](https://github.com/paypal/downshift/commits?author=jaredly "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/8162598?v=4" width="100px;"/><br /><sub>Jack Moore</sub>](https://github.com/jtmthf)<br />[💡](#example-jtmthf "Examples") | [<img src="https://avatars1.githubusercontent.com/u/2762082?v=4" width="100px;"/><br /><sub>Travis Arnold</sub>](http://travisrayarnold.com)<br />[💻](https://github.com/paypal/downshift/commits?author=souporserious "Code") [📖](https://github.com/paypal/downshift/commits?author=souporserious "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/244704?v=4" width="100px;"/><br /><sub>Jeremy Gayed</sub>](http://www.jeremygayed.com)<br />[💡](#example-tizmagik "Examples") | [<img src="https://avatars3.githubusercontent.com/u/6270048?v=4" width="100px;"/><br /><sub>Haroen Viaene</sub>](https://haroen.me)<br />[💡](#example-Haroenv "Examples") |
|
455 | | :---: | :---: | :---: | :---: | :---: | :---: | :---: |
|
456 | | [<img src="https://avatars2.githubusercontent.com/u/15073300?v=4" width="100px;"/><br /><sub>monssef</sub>](https://github.com/rezof)<br />[💡](#example-rezof "Examples") | [<img src="https://avatars2.githubusercontent.com/u/5382443?v=4" width="100px;"/><br /><sub>Federico Zivolo</sub>](https://fezvrasta.github.io)<br />[📖](https://github.com/paypal/downshift/commits?author=FezVrasta "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/746482?v=4" width="100px;"/><br /><sub>Divyendu Singh</sub>](https://divyendusingh.com)<br />[💡](#example-divyenduz "Examples") | [<img src="https://avatars1.githubusercontent.com/u/841955?v=4" width="100px;"/><br /><sub>Muhammad Salman</sub>](https://github.com/salmanmanekia)<br />[💻](https://github.com/paypal/downshift/commits?author=salmanmanekia "Code") | [<img src="https://avatars3.githubusercontent.com/u/10820159?v=4" width="100px;"/><br /><sub>João Alberto</sub>](https://twitter.com/psicotropidev)<br />[💻](https://github.com/paypal/downshift/commits?author=psicotropicos "Code") | [<img src="https://avatars0.githubusercontent.com/u/16327281?v=4" width="100px;"/><br /><sub>Bernard Lin</sub>](https://github.com/bernard-lin)<br />[💻](https://github.com/paypal/downshift/commits?author=bernard-lin "Code") [📖](https://github.com/paypal/downshift/commits?author=bernard-lin "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/7330124?v=4" width="100px;"/><br /><sub>Geoff Davis</sub>](https://geoffdavis.info)<br />[💡](#example-geoffdavis92 "Examples") |
|
457 | | [<img src="https://avatars0.githubusercontent.com/u/3415488?v=4" width="100px;"/><br /><sub>Anup</sub>](https://github.com/reznord)<br />[📖](https://github.com/paypal/downshift/commits?author=reznord "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/340520?v=4" width="100px;"/><br /><sub>Ferdinand Salis</sub>](http://ferdinandsalis.com)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Aferdinandsalis "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=ferdinandsalis "Code") | [<img src="https://avatars2.githubusercontent.com/u/662750?v=4" width="100px;"/><br /><sub>Kye Hohenberger</sub>](https://github.com/tkh44)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Atkh44 "Bug reports") | [<img src="https://avatars0.githubusercontent.com/u/1443499?v=4" width="100px;"/><br /><sub>Julien Goux</sub>](https://github.com/jgoux)<br />[🐛](https://github.com/paypal/downshift/issues?q=author%3Ajgoux "Bug reports") [💻](https://github.com/paypal/downshift/commits?author=jgoux "Code") [⚠️](https://github.com/paypal/downshift/commits?author=jgoux "Tests") |
|
458 |
|
459 |
|
460 | This project follows the [all-contributors][all-contributors] specification.
|
461 | Contributions of any kind welcome!
|
462 |
|
463 | ## LICENSE
|
464 |
|
465 | MIT
|
466 |
|
467 | [npm]: https://www.npmjs.com/
|
468 | [node]: https://nodejs.org
|
469 | [build-badge]: https://img.shields.io/travis/paypal/downshift.svg?style=flat-square
|
470 | [build]: https://travis-ci.org/paypal/downshift
|
471 | [coverage-badge]: https://img.shields.io/codecov/c/github/paypal/downshift.svg?style=flat-square
|
472 | [coverage]: https://codecov.io/github/paypal/downshift
|
473 | [version-badge]: https://img.shields.io/npm/v/downshift.svg?style=flat-square
|
474 | [package]: https://www.npmjs.com/package/downshift
|
475 | [downloads-badge]: https://img.shields.io/npm/dm/downshift.svg?style=flat-square
|
476 | [npmcharts]: http://npmcharts.com/compare/downshift
|
477 | [license-badge]: https://img.shields.io/npm/l/downshift.svg?style=flat-square
|
478 | [license]: https://github.com/paypal/downshift/blob/master/LICENSE
|
479 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
|
480 | [prs]: http://makeapullrequest.com
|
481 | [chat]: https://gitter.im/paypal/downshift
|
482 | [chat-badge]: https://img.shields.io/gitter/room/paypal/downshift.svg?style=flat-square
|
483 | [coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
|
484 | [coc]: https://github.com/paypal/downshift/blob/master/other/CODE_OF_CONDUCT.md
|
485 | [react-badge]: https://img.shields.io/badge/%E2%9A%9B%EF%B8%8F-(p)react-00d8ff.svg?style=flat-square
|
486 | [react]: https://facebook.github.io/react/
|
487 | [gzip-badge]: http://img.badgesize.io/https://unpkg.com/downshift/dist/downshift.umd.min.js?compression=gzip&label=gzip%20size&style=flat-square
|
488 | [size-badge]: http://img.badgesize.io/https://unpkg.com/downshift/dist/downshift.umd.min.js?label=size&style=flat-square
|
489 | [unpkg-dist]: https://unpkg.com/downshift/dist/
|
490 | [module-formats-badge]: https://img.shields.io/badge/module%20formats-umd%2C%20cjs%2C%20es-green.svg?style=flat-square
|
491 | [github-watch-badge]: https://img.shields.io/github/watchers/paypal/downshift.svg?style=social
|
492 | [github-watch]: https://github.com/paypal/downshift/watchers
|
493 | [github-star-badge]: https://img.shields.io/github/stars/paypal/downshift.svg?style=social
|
494 | [github-star]: https://github.com/paypal/downshift/stargazers
|
495 | [twitter]: https://twitter.com/intent/tweet?text=Check%20out%20downshift!%20https://github.com/paypal/downshift%20%F0%9F%91%8D
|
496 | [twitter-badge]: https://img.shields.io/twitter/url/https/github.com/paypal/downshift.svg?style=social
|
497 | [emojis]: https://github.com/kentcdodds/all-contributors#emoji-key
|
498 | [all-contributors]: https://github.com/kentcdodds/all-contributors
|
499 | [ryan]: https://github.com/ryanflorence
|
500 | [compound-components-talk]: https://www.youtube.com/watch?v=hEGg-3pIHlE
|
501 | [react-autocomplete]: https://www.npmjs.com/package/react-autocomplete
|
502 | [jquery-complete]: https://jqueryui.com/autocomplete/
|
503 | [examples]: https://codesandbox.io/search?refinementList%5Btags%5D%5B0%5D=downshift%3Aexample&page=1
|
504 | [yt-playlist]: https://www.youtube.com/playlist?list=PLV5CVI1eNcJh5CTgArGVwANebCrAh2OUE
|
505 | [jared]: https://github.com/jaredly
|