UNPKG

focus-components-v3

Version:

Focus web components to build applications (based on Material Design)

196 lines (176 loc) 6.76 kB
import React, {Component, PropTypes} from 'react'; import ReactDOM from 'react-dom'; import i18next from 'i18next'; import MDBehaviour from '../behaviours/material'; import {InputBehaviour} from '../behaviours/input-component'; import debounce from 'lodash/debounce'; @MDBehaviour('materialInput') @InputBehaviour class AutocompleteTextEdit extends Component { constructor(props) { super(props); this.state = { inputValue: this.props.rawInputValue, suggestions: [], hasSuggestions: false, error: this.props.error, hasFocus: false, isLoading: false }; } componentWillReceiveProps({error}) { if(error) { this.setState({error: error}); } } componentDidMount() { const {inputTimeout} = this.props; this._debouncedQuerySearcher = debounce(this._querySearcher, inputTimeout); } // Returns the state's inputValue getValue = () => { const {inputValue} = this.state; if(inputValue !== undefined) { return inputValue; } else { return null; } }; // Gets the defined props' querySearch and returns the object given by the promise // Sets the hasSuggestions' state if the given object has a none empty array _querySearcher = value => { const {querySearcher} = this.props; const {hasSuggestions} = this.state; querySearcher(value).then(({data, totalCount}) => { if(totalCount > 0) { this.setState({hasSuggestions: true, suggestions: data, error: ''}); } this.refs.loader.classList.remove('mdl-progress__indeterminate'); this.setState({isLoading: false}); }).catch(err => { this.refs.loader.classList.remove('mdl-progress__indeterminate'); this.setState({error: JSON.stringify(err), isLoading: false}); this.refs.materialInput.classList.add('is-invalid'); }); }; // Sets the state's inputValue when the user is typing _handleonChange = ({target: {value}}) => { const {onChange} = this.props; this.setState({inputValue: value}); if(value.trim() == '') { this.setState({hasSuggestions: false}); } else { this.refs.loader.classList.add('mdl-progress__indeterminate'); this.setState({isLoading: true}); this._debouncedQuerySearcher(value); if (onChange) onChange(value); } }; // Sets the value input with the selected suggestion and hides the dropdown onResultClick(value) { const {onChange} = this.props; this.refs.inputText.value = value; this.setState({inputValue: value, hasSuggestions: false, suggestions: []}); if (onChange) onChange(value); return value; }; // Returns an html list whith the Suggestions renderSuggestions = () => { const {suggestions} = this.state; const allSuggestions = suggestions.map(({key, label}) => <li key={key} onMouseDown={(e) => {this.onResultClick(label); e.preventDefault();}} data-focus='option' >{label}</li>); return( <ul ref='suggestions' data-focus='options'> {allSuggestions} </ul> ); }; // Behaviour when onFocus and onBlur are triggered _handleonFocus = e => { const {hasSuggestions, hasFocus} = this.state; const {showAtFocus, emptyShowAll} = this.props; this.setState({hasFocus: !this.state.hasFocus}); if(hasSuggestions && !showAtFocus && hasFocus === false) { this.setState({hasSuggestions: false}); } if(!hasSuggestions && e.target.value.trim() === '' && emptyShowAll && hasFocus === false) { // Doing a global search here this._querySearcher(''); } return true; }; // Maybe give the option for the floating label render() { const {inputValue, hasSuggestions, hasFocus, isLoading} = this.state; const validInputProps = this._checkProps(this.props); const {inputTimeout, error, placeholder} = this.props; validInputProps.value = inputValue === undefined || inputValue === null ? '' : inputValue; validInputProps.onBlur = this._handleonFocus; const inputProps = {...validInputProps}; return( <div data-focus='autocompleteText'> <div className={`mdl-textfield mdl-js-textfield${error ? ' is-invalid' : ''}`} ref='materialInput'> <div data-focus='loading' data-loading={isLoading} className='mdl-progress mdl-js-progress' ref='loader'/> <input className='mdl-textfield__input' type='text' ref='inputText' {...inputProps}/> <label className="mdl-textfield__label">{i18next.t(placeholder)}</label> <span className="mdl-textfield__error" ref='errorMessage'>{i18next.t(error)}</span> </div> {hasSuggestions && hasFocus && this.renderSuggestions() } </div> ); } } AutocompleteTextEdit.displayName = 'AutocompleteTextEdit'; AutocompleteTextEdit.defaultProps = { emptyShowAll: false, inputTimeout: 200, placeholder: 'Search here...', showAtFocus: false }; AutocompleteTextEdit.propTypes = { emptyShowAll: PropTypes.bool, //Defines if it shows suggestions on focus when the input is empty. error: PropTypes.string, //Error showed message. inputTimeout : PropTypes.number.isRequired, //Timeout to execute the debounce function. onChange: PropTypes.func, //Launches the querySearcher. placeholder: PropTypes.string, //Placeholder field. querySearcher: PropTypes.func.isRequired, //Returns a promise which is connected to the web service. rawInputValue: PropTypes.string, //Field value. showAtFocus: PropTypes.bool //Defines it shows suggestions on focus. }; export default AutocompleteTextEdit; /* EXAMPLE const _querySearcher = query => { let data = [ { key: 'JL', label: 'Joh Lickeur' }, { key: 'GK', label: 'Guénolé Kikabou' }, { key: 'YL', label: 'Yannick Lounivis' } ]; return new Promise((resolve, reject) => { setTimeout(() => { resolve({ data, totalCount: data.length }); }, 500); }); }; <AutocompleteText isEdit={isEdit} querySearcher={_querySearcher} placeholder={'Your search...'} error{Something wrong happend. Retry please...} /> */