diff --git a/README.md b/README.md index 1e4b842f..e2f33bee 100644 --- a/README.md +++ b/README.md @@ -171,6 +171,13 @@ Default: true If false, the default classNames are removed from the typeahead. +#### props.propagateKeyDownEvents + +Type: `boolean` +Default: false + +If true, allows keyDown events to propagate. This is useful if you want the `tab` key to focus the next element, for example. + #### props.customListComponent Type: `React Component` diff --git a/src/tokenizer/index.js b/src/tokenizer/index.js index c931688a..68a98d2e 100644 --- a/src/tokenizer/index.js +++ b/src/tokenizer/index.js @@ -49,7 +49,8 @@ var TypeaheadTokenizer = React.createClass({ React.PropTypes.func ]), maxVisible: React.PropTypes.number, - defaultClassNames: React.PropTypes.bool + defaultClassNames: React.PropTypes.bool, + propagateKeyDownEvents: React.PropTypes.bool }, getInitialState: function() { @@ -78,6 +79,7 @@ var TypeaheadTokenizer = React.createClass({ onBlur: function(event) {}, onTokenAdd: function() {}, onTokenRemove: function() {} + propagateKeyDownEvents: false, }; }, @@ -126,6 +128,10 @@ var TypeaheadTokenizer = React.createClass({ if (event.keyCode === KeyEvent.DOM_VK_BACK_SPACE) { return this._handleBackspace(event); } + // or tabs + if (event.keyCode === KeyEvent.DOM_VK_TAB) { + this._handleTab(event); + } this.props.onKeyDown(event); }, @@ -135,17 +141,29 @@ var TypeaheadTokenizer = React.createClass({ return; } - // Remove token ONLY when bksp pressed at beginning of line - // without a selection - var entry = this.refs.typeahead.refs.entry; - if (entry.selectionStart == entry.selectionEnd && - entry.selectionStart == 0) { + if (this._inputIsEmpty()) { this._removeTokenForValue( this.state.selected[this.state.selected.length - 1]); event.preventDefault(); } }, + _handleTab: function(event) { + // Intercept tab to prevent focusing on next element, unless + // nothing is selected and the input is empty + var entry = this.refs.typeahead.refs.entry; + if (!this._inputIsEmpty()) { + event.preventDefault(); + } + }, + + _inputIsEmpty: function() { + // beginning of line, without a selection + var entry = this.refs.typeahead.refs.entry; + return entry.selectionStart == entry.selectionEnd && + entry.selectionStart == 0; + }, + _removeTokenForValue: function(value) { var index = this.state.selected.indexOf(value); if (index == -1) { @@ -195,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} + propagateKeyDownEvents={this.props.propagateKeyDownEvents} /> ); } diff --git a/src/typeahead/index.js b/src/typeahead/index.js index c13d2dd0..2ffb672d 100644 --- a/src/typeahead/index.js +++ b/src/typeahead/index.js @@ -51,6 +51,7 @@ var Typeahead = React.createClass({ React.PropTypes.func ]), defaultClassNames: React.PropTypes.bool, + propagateKeyDownEvents: React.PropTypes.bool, customListComponent: React.PropTypes.oneOfType([ React.PropTypes.element, React.PropTypes.func @@ -75,6 +76,7 @@ var Typeahead = React.createClass({ onBlur: function(event) {}, filterOption: null, defaultClassNames: true, + propagateKeyDownEvents: false, customListComponent: TypeaheadSelector }; }, @@ -279,8 +281,10 @@ var Typeahead = React.createClass({ } else { return this.props.onKeyDown(event); } - // Don't propagate the keystroke back to the DOM/browser - event.preventDefault(); + // By default, don't propagate the keystroke back to the DOM/browser + if (!this.props.propagateKeyDownEvents) { + event.preventDefault(); + } }, componentWillReceiveProps: function(nextProps) {