UNPKG

react-typeahead-tokenizer

Version:

React-based typeahead and typeahead-tokenizer

206 lines (177 loc) 6.19 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Token from './token'; import Typeahead from '../typeahead'; import classNames from 'classnames'; const _arraysAreDifferent = (array1, array2) => { if (array1.length != array2.length){ return true; } for (var i = array2.length - 1; i >= 0; i--) { if (array2[i] !== array1[i]){ return true; } } } class TypeaheadTokenizer extends Component { static defaultProps = { options: [], defaultSelected: [], customClasses: {}, allowCustomValues: 0, initialValue: '', placeholder: '', disabled: false, inputProps: {}, defaultClassNames: true, onKeyDown: (event) => {}, onKeyPress: (event) => {}, onKeyUp: (event) => {}, onFocus: (event) => {}, onBlur: (event) => {}, onTokenAdd: () => {}, onTokenRemove: () => {} } constructor(props, defaultProps) { super(props, defaultProps); this.state = { // We need to copy this to avoid incorrect sharing // of state across instances (e.g., via getDefaultProps()) selected: props.defaultSelected.slice(0) } } componentWillReceiveProps(nextProps) { // if we get new defaultProps, update selected if (_arraysAreDifferent(this.props.defaultSelected, nextProps.defaultSelected)){ this.setState({ selected: nextProps.defaultSelected.slice(0) }); } } focus() { this.typeahead.focus(); } getSelectedTokens() { return this.state.selected; } _renderTokens() { const classList = classNames({ [this.props.customClasses.token]: !!this.props.customClasses.token }); return this.state.selected.map((selected, index) => ( <Token key={index} className={classList} onRemove={this._removeTokenForValue} object={selected} value={selected} name={this.props.name}> {selected} </Token> )); } _getOptionsForTypeahead() { // return this.props.options without this.selected return this.props.options; } _onKeyDown = (event) => { // We only care about intercepting backspaces if (event.keyCode === 8) { return this._handleBackspace(event); } this.props.onKeyDown(event); } _handleBackspace = (event) => { // No tokens if (!this.state.selected.length) { return; } // Remove token ONLY when bksp pressed at beginning of line // without a selection const entry = this.typeahead.entry; if (entry.selectionStart == entry.selectionEnd && entry.selectionStart == 0) { this._removeTokenForValue(this.state.selected[this.state.selected.length - 1]); event.preventDefault(); } } _removeTokenForValue = (value) => { const index = this.state.selected.indexOf(value); if (index == -1) { return; } this.state.selected.splice(index, 1); this.setState({ selected: this.state.selected }); this.props.onTokenRemove(value); return; } _addTokenForValue = (value) => { if (this.state.selected.indexOf(value) != -1) { return; } this.state.selected.push(value); this.setState({ selected: this.state.selected }); this.typeahead.setEntryText(''); this.typeahead.entry.blur(); this.typeahead.entry.focus(); this.props.onTokenAdd(value); } render() { const classList = classNames({ [this.props.customClasses.typeahead]: !!this.props.customClasses.typeahead }); let tokenizerClasses = [this.props.defaultClassNames && 'typeahead-tokenizer']; tokenizerClasses[this.props.className] = !!this.props.className; const tokenizerClassList = classNames(tokenizerClasses) return ( <div className={tokenizerClassList}> { this._renderTokens() } <Typeahead ref={typeahead => this.typeahead = typeahead} className={classList} placeholder={this.props.placeholder} disabled={this.props.disabled} inputProps={this.props.inputProps} allowCustomValues={this.props.allowCustomValues} customClasses={this.props.customClasses} options={this._getOptionsForTypeahead()} initialValue={this.props.initialValue} maxVisible={this.props.maxVisible} resultsTruncatedMessage={this.props.resultsTruncatedMessage} onOptionSelected={this._addTokenForValue} onKeyDown={this._onKeyDown} onKeyPress={this.props.onKeyPress} onKeyUp={this.props.onKeyUp} onFocus={this.props.onFocus} onBlur={this.props.onBlur} defaultClassNames={this.props.defaultClassNames} /> </div> ); } } TypeaheadTokenizer.propTypes = { name: PropTypes.string, options: PropTypes.array, customClasses: PropTypes.object, allowCustomValues: PropTypes.number, defaultSelected: PropTypes.array, initialValue: PropTypes.string, placeholder: PropTypes.string, disabled: PropTypes.bool, inputProps: PropTypes.object, onTokenRemove: PropTypes.func, onKeyDown: PropTypes.func, onKeyPress: PropTypes.func, onKeyUp: PropTypes.func, onTokenAdd: PropTypes.func, onFocus: PropTypes.func, onBlur: PropTypes.func, maxVisible: PropTypes.number, resultsTruncatedMessage: PropTypes.string, defaultClassNames: PropTypes.bool } export default TypeaheadTokenizer;