Skip to content

Commit

Permalink
Make Typeahead a controlled component. Fixes #166
Browse files Browse the repository at this point in the history
  • Loading branch information
nosilleg authored and Jason Ellison committed Mar 30, 2016
1 parent 7b1617d commit 4cafd97
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 23 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@
},
"dependencies": {
"classnames": "^1.2.0",
"fuzzy": "^0.1.0"
"fuzzy": "^0.1.0",
"react-addons-update": "^0.14.7"
},
"peerDependencies": {
"react": ">= 0.14.0"
Expand Down
35 changes: 24 additions & 11 deletions src/tokenizer/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var Token = require('./token');
var KeyEvent = require('../keyevent');
var Typeahead = require('../typeahead');
var classNames = require('classnames');
var update = require('react-addons-update');

function _arraysAreDifferent(array1, array2) {
if (array1.length != array2.length){
Expand Down Expand Up @@ -58,7 +59,8 @@ var TypeaheadTokenizer = React.createClass({
return {
// We need to copy this to avoid incorrect sharing
// of state across instances (e.g., via getDefaultProps())
selected: this.props.defaultSelected.slice(0)
selected: this.props.defaultSelected.slice(0),
value: this.props.defaultValue
};
},

Expand Down Expand Up @@ -88,7 +90,9 @@ var TypeaheadTokenizer = React.createClass({
componentWillReceiveProps: function(nextProps){
// if we get new defaultProps, update selected
if (_arraysAreDifferent(this.props.defaultSelected, nextProps.defaultSelected)){
this.setState({selected: nextProps.defaultSelected.slice(0)})
this.setState({
selected: nextProps.defaultSelected.slice(0)
});
}
},

Expand Down Expand Up @@ -144,8 +148,7 @@ var TypeaheadTokenizer = React.createClass({
var entry = this.refs.typeahead.refs.entry;
if (entry.selectionStart == entry.selectionEnd &&
entry.selectionStart == 0) {
this._removeTokenForValue(
this.state.selected[this.state.selected.length - 1]);
this._removeTokenForValue(this.state.selected[this.state.selected.length - 1]);
event.preventDefault();
}
},
Expand All @@ -156,22 +159,30 @@ var TypeaheadTokenizer = React.createClass({
return;
}

this.state.selected.splice(index, 1);
this.setState({selected: this.state.selected});
this.setState({
selected: update(this.state.selected, {$splice: [[index, 1]]})
});
this.props.onTokenRemove(value);
return;
},

_addTokenForValue: function(value) {
if (this.state.selected.indexOf(value) != -1) {
if (this.state.selected.indexOf(value) !== -1) {
return;
}
this.state.selected.push(value);
this.setState({selected: this.state.selected});
this.refs.typeahead.setEntryText("");
this.setState({
selected: update(this.state.selected, {$push: [value]}),
value: ""
});
this.props.onTokenAdd(value);
},

_onChange: function(event) {
this.setState({
value: event.target.value
});
},

render: function() {
var classes = {};
classes[this.props.customClasses.typeahead] = !!this.props.customClasses.typeahead;
Expand All @@ -193,6 +204,7 @@ var TypeaheadTokenizer = React.createClass({
options={this._getOptionsForTypeahead()}
defaultValue={this.props.defaultValue}
maxVisible={this.props.maxVisible}
onChange={this._onChange}
onOptionSelected={this._addTokenForValue}
onKeyDown={this._onKeyDown}
onKeyPress={this.props.onKeyPress}
Expand All @@ -201,7 +213,8 @@ var TypeaheadTokenizer = React.createClass({
onBlur={this.props.onBlur}
displayOption={this.props.displayOption}
defaultClassNames={this.props.defaultClassNames}
filterOption={this.props.filterOption} />
filterOption={this.props.filterOption}
value={this.state.value} />
</div>
);
}
Expand Down
29 changes: 18 additions & 11 deletions src/typeahead/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ var Typeahead = React.createClass({
_hasCustomValue: function() {
if (this.props.allowCustomValues > 0 &&
this.state.entryValue.length >= this.props.allowCustomValues &&
this.state.visible.indexOf(this.state.entryValue) < 0) {
this.state.visible.indexOf(this.state.entryValue) === -1) {
return true;
}
return false;
Expand Down Expand Up @@ -188,17 +188,21 @@ var Typeahead = React.createClass({
var formInputOptionString = formInputOption(option);

nEntry.value = optionString;
this.setState({visible: this.getOptionsForValue(optionString, this.props.options),
selection: formInputOptionString,
entryValue: optionString});
this.setState({
visible: this.getOptionsForValue(optionString, this.props.options),
selection: formInputOptionString,
entryValue: optionString
});
return this.props.onOptionSelected(option, event);
},

_onTextEntryUpdated: function() {
var value = this.refs.entry.value;
this.setState({visible: this.getOptionsForValue(value, this.props.options),
selection: null,
entryValue: value});
this.setState({
visible: this.getOptionsForValue(value, this.props.options),
selection: null,
entryValue: value
});
},

_onEnter: function(event) {
Expand All @@ -220,7 +224,7 @@ var Typeahead = React.createClass({
var option = selection ?
selection : (this.state.visible.length > 0 ? this.state.visible[0] : null);

if (option === null && this._hasCustomValue()) {
if (option === null) {
option = this._getCustomValue();
}

Expand Down Expand Up @@ -257,7 +261,9 @@ var Typeahead = React.createClass({
newIndex -= length;
}

this.setState({selectionIndex: newIndex});
this.setState({
selectionIndex: newIndex
});
},

navDown: function() {
Expand Down Expand Up @@ -297,7 +303,8 @@ var Typeahead = React.createClass({

componentWillReceiveProps: function(nextProps) {
this.setState({
visible: this.getOptionsForValue(this.state.entryValue, nextProps.options)
visible: this.getOptionsForValue(nextProps.value, nextProps.options),
entryValue: nextProps.value
});
},

Expand All @@ -322,14 +329,14 @@ var Typeahead = React.createClass({
placeholder={this.props.placeholder}
disabled={this.props.disabled}
className={inputClassList}
value={this.state.entryValue}
defaultValue={this.props.defaultValue}
onChange={this._onChange}
onKeyDown={this._onKeyDown}
onKeyPress={this.props.onKeyPress}
onKeyUp={this.props.onKeyUp}
onFocus={this.props.onFocus}
onBlur={this.props.onBlur}
value={this.props.value}
/>
{ this._renderIncrementalSearchResults() }
</div>
Expand Down

0 comments on commit 4cafd97

Please sign in to comment.