Last active
December 20, 2016 05:50
-
-
Save CarsonF/d6796ed6d5b7a94f40caac03d29a5994 to your computer and use it in GitHub Desktop.
Material UI AutoComplete Forced Selection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import ReactDOM from 'react-dom'; | |
import { FormattedMessage } from 'react-intl'; | |
import { AutoComplete as MuiAutoComplete } from 'material-ui'; | |
class AutoComplete extends MuiAutoComplete { | |
/** | |
* Added callback param | |
*/ | |
close(cb?: () => any) { | |
this.setState({ | |
open: false, | |
anchorEl: null | |
}, cb); | |
if (this.props.onClose) { | |
this.props.onClose(); | |
} | |
} | |
/** | |
* After calling close, dispatch onClosedAndBlurred (for validation) | |
*/ | |
handleBlur = (event) => { | |
if (this.state.focusTextField && this.timerTouchTapCloseId === null) { | |
this.timerBlurClose = setTimeout(() => { | |
this.close(() => this.props.onClose(true)); | |
}, 0); | |
} | |
if (this.props.onBlur) { | |
this.props.onBlur(event); | |
} | |
}; | |
/** | |
* Change enter to be the same as down arrow. | |
* AKA force selection instead of closing box. | |
*/ | |
handleKeyDown = (event) => { | |
if (this.props.onKeyDown) this.props.onKeyDown(event); | |
switch (event.which) { | |
case 27: // escape | |
this.close(); | |
break; | |
case 13: // enter | |
case 40: // down | |
event.preventDefault(); | |
this.setState({ | |
open: true, | |
focusTextField: false, | |
anchorEl: ReactDOM.findDOMNode(this.refs['searchTextField']), | |
}); | |
break; | |
} | |
}; | |
/** | |
* Refocus text field to encourage dropdown selection. | |
*/ | |
handleEscKeyDown = () => { | |
this.close(); | |
this.focus(); | |
} | |
} | |
export class ForcedSelectionAutoComplete extends React.Component { | |
constructor(props, context) { | |
super(props, context); | |
this.state = { | |
data: [], | |
searchText: '', | |
textIsValidSelection: true, | |
blurredWithoutSelection: false, | |
}; | |
} | |
componentWillReceiveProps(nextProps, nextContext) { | |
if (nextProps.searchText && nextProps.searchText !== this.props.searchText) { | |
this.setState({searchText: nextProps.searchText}); | |
} | |
} | |
render() { | |
let errorText; | |
if (this.state.errorText) { | |
errorText = this.state.errorText; | |
} else if (this.state.blurredWithoutSelection && !this.state.textIsValidSelection) { | |
errorText = <FormattedMessage id='dropdown.make_choice' defaultMessage='Select an item from the dropdown' />; | |
} | |
// Created this way because definition is crap. | |
// - dataSourceConfig is missing | |
// - text props are strings instead of nodes | |
// - dataSource and onNewRequest don't have to be the definitions given (due to dataSourceConfig). | |
return React.createElement(AutoComplete, Object.assign({ | |
dataSource: this.state.data, | |
filter: AutoComplete.noFilter, | |
onUpdateInput: this.handleUpdateInput, | |
onNewRequest: this.handleNewRequest, | |
onFocus: this.handleFocus, | |
onClose: this.handleClose, | |
openOnFocus: true, | |
errorText | |
}, this.props, { | |
searchText: this.state.searchText, | |
})); | |
} | |
protected handleClose = (blurred = false) => { | |
if (blurred) { | |
this.handleCloseAndBlurred(); | |
} | |
}; | |
handleCloseAndBlurred = () => { | |
this.setState({ | |
blurredWithoutSelection: true, | |
}); | |
if (this.state.searchText.length == 0) { | |
this.props.onNewSelection && this.props.onNewSelection(null); | |
} | |
}; | |
handleFocus = (e) => { | |
this.setState({ | |
blurredWithoutSelection: false, | |
}); | |
if (this.props.onFocus) { | |
this.props.onFocus(e); | |
} | |
}; | |
handleUpdateInput = (value: string) => { | |
this.setState({ | |
searchText: value, | |
errorText: null, | |
textIsValidSelection: value.length == 0, | |
}); | |
if (value.length < 1) { | |
return; | |
} | |
// do logic, set state | |
}; | |
handleNewRequest = (chosenItem, index) => { | |
this.setState({ | |
textIsValidSelection: true, | |
}); | |
// do logic | |
if (this.props.onNewSelection) { | |
this.props.onNewSelection(chosenItem, index); | |
} | |
}; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Features:
onNewSelection
prop