{"size": 2997, "ext": "js", "max_stars_repo_path": "ui/app/rif/components/genericSearch/index.js", "max_stars_repo_name": "anarancio/nifty-wallet", "max_stars_repo_stars_event_min_datetime": "2020-10-15T15:11:42.000Z", "max_stars_repo_stars_event_max_datetime": "2021-03-07T18:39:58.000Z", "max_issues_repo_path": "ui/app/rif/components/genericSearch/index.js", "max_issues_repo_name": "anarancio/nifty-wallet", "max_issues_repo_issues_event_min_datetime": "2020-04-02T15:40:39.000Z", "max_issues_repo_issues_event_max_datetime": "2020-07-24T15:43:14.000Z", "max_forks_repo_path": "ui/app/rif/components/genericSearch/index.js", "max_forks_repo_name": "rsksmart/rconnect", "max_forks_repo_forks_event_min_datetime": "2020-06-05T20:16:11.000Z", "max_forks_repo_forks_event_max_datetime": "2020-06-05T20:16:11.000Z"}
{"max_stars_count": 2, "max_issues_count": 17, "max_forks_count": 1, "avg_line_length": 29.3823529412, "max_line_length": 99, "alphanum_fraction": 0.6509843177}
import React, {Component} from 'react';
import PropTypes from 'prop-types';

/**
 * This search bar has 3 ways of working <br>
 * Simple filter => Filter in an array by a string <br>
 * Object filter => Filter in an array of objects a property of X object <br>
 * Custom filter => Use a function that receives the typed value, may be asynchronous <br>
 * In the first 2 cases, the resultSetFunction must be called to receive the filtered data <br>
 * In the last case, the resultSetFunction is optional <br>
 */
class GenericSearch extends Component {

  static propTypes = {
    customFilterFunction: PropTypes.func,
    resultSetFunction: PropTypes.func,
    filterProperty: PropTypes.string,
    data: PropTypes.array,
    placeholder: PropTypes.string,
    onlyFilterOnEnter: PropTypes.bool,
  }

  constructor(props) {
    super(props);
    this.state = {
      value: ''
    }
  }

  includesCriteria(element, criteria) {
    const lowerString = v => String(v).toLowerCase()
    const lowerElement = lowerString(element);
    const lowerCriteria = lowerString(criteria);
    return lowerElement.includes(lowerCriteria);
  }

  clear() {
    this.setState({value: ''})
  }

  handleOnChange(e) {
    const {value} = e.target;
    this.setState({value})
    const {onlyFilterOnEnter} = this.props;
    const enterPressed = e.key === 'Enter';

    if (!onlyFilterOnEnter && !enterPressed) {
      this.filter(value);
    }
  }

  async handleOnKeyDown(e) {
    const {value} = e.target;
    const {onlyFilterOnEnter} = this.props;
    const enterPressed = e.key === 'Enter';
    if (onlyFilterOnEnter && enterPressed) {
      await this.filter(value);
      this.clear();
    }
  }

  async filter(value) {
      const {customFilterFunction, resultSetFunction} = this.props;

      if (customFilterFunction) {
        const result = await customFilterFunction(value);
        if (resultSetFunction) await resultSetFunction(result);
        return;
      }

      const {filterProperty, data} = this.props;
      // If we don't have a criteria, return the original data.
      if (!value) return resultSetFunction(data);
      // Simple filter in elements of array (no objects)
      if (!filterProperty) {
        const result = data.filter(element => this.includesCriteria(element, value));
        return resultSetFunction(result);
      }
      // Filter of 1st level, with the property to check
      const result = data.filter(element => this.includesCriteria(element[filterProperty], value));
      return resultSetFunction(result);
  }

  render () {
    const {placeholder, onlyFilterOnEnter} = this.props;
    const { value } = this.state;
    return (
      <div className="search-bar-container">
        <input
          placeholder={placeholder || ''}
          className={'search-bar'}
          onChange={(e) => this.handleOnChange(e)}
          onKeyDown={(e) => this.handleOnKeyDown(e)}
          value={value}
        />
      </div>
    )
  }
}

export default GenericSearch;
