UNPKG

@bigfishtv/cockpit

Version:

204 lines (187 loc) 5.56 kB
import PropTypes from 'prop-types' import React, { Component } from 'react' import deepEqual from 'deep-equal' import keycode from 'keycode' import _debounce from 'lodash/debounce' import Autosuggest from 'react-autosuggest' import { get } from '../../api/xhrUtils' import Button from '../button/Button' import autosuggestTheme from '../../config/autosuggestTheme' import Icon from '../Icon' import Spinner from '../Spinner' // we define this because react-docgen fails when defaultProp directly references an imported component const autosuggestThemeDefault = { ...autosuggestTheme } export default class AutosuggestInput extends Component { static propTypes = { placeholder: PropTypes.string, renderSuggestion: PropTypes.func, queryUrl: PropTypes.string, onChange: PropTypes.func, getSuggestions: PropTypes.func, readOnly: PropTypes.bool, queryParam: PropTypes.string, } static defaultProps = { placeholder: 'Search...', theme: autosuggestThemeDefault, renderSuggestion: (suggestion, { value, valueBeforeUpDown }) => ( <div>{suggestion.name || suggestion.title || suggestion.toString()}</div> ), queryParam: 'q', params: {}, autoCache: true, autoFocus: false, multiSection: false, debounceTime: 100, suggestions: [], onInputFocus: () => {}, onInputBlur: () => {}, onInputChange: () => {}, onSuggestionsChange: () => {}, filterSuggestions: suggestions => suggestions, onChange: () => {}, onCreate: () => {}, beforeSelection: () => true, getSectionSuggestions: section => section.suggestions, renderSectionTitle: section => section.sectionName, Cell: null, } constructor(props) { super(props) this.cache = {} this.focused = props.autoFocus || false this.handleKeyDown = this.handleKeyDown.bind(this) this.getSuggestions = _debounce(this.getSuggestions, props.debounceTime) this.state = { loading: false, inputValue: '', suggestions: props.suggestions || [], } } componentDidMount() { document.addEventListener('keydown', this.handleKeyDown) } componentWillUnmount() { document.removeEventListener('keydown', this.handleKeyDown) } componentWillReceiveProps(nextProps) { if (!deepEqual(this.props.suggestions, nextProps.suggestions)) { this.setState({ suggestions: nextProps.suggestions }) } } handleKeyDown(event) { if (keycode(event) == 'enter' && this.focused && !this.state.suggestions.length) { event.preventDefault() this.props.onCreate(this.state.inputValue) this.setState({ inputValue: '' }) } } // When suggestion is selected either via click/tap or enter key handleSuggestion = (event, { suggestion, suggestionValue, method }) => { event.preventDefault() if (this.props.beforeSelection(suggestion, suggestionValue, method)) { this.props.onChange(suggestion) this.setState({ inputValue: '' }) } } // Called upon 'onSuggestionsUpdateRequested' getSuggestions = ({ value, reason }) => { if (reason == 'type') { if (this.props.autoCache && value in this.cache) { this.applySuggestions(this.cache[value]) } else { this.setState({ loading: true }) get({ url: this.props.queryUrl, params: { ...this.props.params, [this.props.queryParam]: value }, callback: data => { this.setState({ loading: false }) setTimeout(() => this.applySuggestions(data), 10) }, }) } } } // Called once suggestions are fetched, either from cache or via xhr applySuggestions(preFilteredSuggestions) { const suggestions = this.props.filterSuggestions(preFilteredSuggestions) this.setState({ suggestions }) this.props.onSuggestionsChange(suggestions) } render() { const { autoFocus, autoCache, multiSection, getSuggestions, getSectionSuggestions, renderSuggestion, renderSectionTitle, readOnly, placeholder, theme, onInputChange, onInputFocus, onInputBlur, Cell, ...rest } = this.props const { suggestions, inputValue, loading } = this.state const inputProps = { value: inputValue, placeholder, readOnly, autoFocus, onChange: (event, { newValue, method }) => { if (method == 'type') { this.setState({ inputValue: newValue }) onInputChange(newValue) } }, onBlur: event => { this.focused = false onInputBlur(event) }, onFocus: event => { this.focused = true onInputFocus(event) }, } if (!this.props.value) { return ( <div className="autosuggest-container"> <Autosuggest ref="autosuggest" theme={theme} multiSection={multiSection} suggestions={suggestions} onSuggestionsUpdateRequested={getSuggestions || this.getSuggestions} getSuggestionValue={() => ''} getSectionSuggestions={getSectionSuggestions} shouldRenderSuggestions={value => typeof value == 'undefined' || !!value} renderSuggestion={renderSuggestion} renderSectionTitle={renderSectionTitle} onSuggestionSelected={this.handleSuggestion} inputProps={inputProps} /> {loading && <Spinner size={16} />} </div> ) } else { if (Cell) { return <Cell {...rest} value={this.props.value} onRemove={() => this.props.onChange(null)} /> } else { return ( <div className="cell"> <div className="cell-content">{this.props.renderSuggestion(this.props.value)}</div> <div className="cell-control"> <Button size="icon" onClick={() => this.props.onChange(null)}> <Icon name="close" size="18" /> </Button> </div> </div> ) } } } }