import React, { Component } from 'react' // import PropTypes from 'prop-types' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' function formatLabel(label, value) { if (!value) { return label } let len = 0 return ( { label.split(new RegExp(value.replace(/[-\[\]\(\)\+\*\\\^\$\{\}\.\?\&\|\<\>]/g, ''), "i")) // eslint-disable-line .reduce((prev, current, i) => { if (!i) { len += current.length return [current] } const ret = prev.concat({label.substr(len, value.length)}, current) len += value.length + current.length return ret }, []) } ) } function sanitizeForAutocomplete(s) { return (s || "") .toLowerCase() .replace(/[^a-zA-Z0-9 ]/g, '') .trim() .replace(/\\/g, '') } class Autocomplete extends Component { constructor(props) { super() this.state = { q: props.q || "", selected: 0, matches: [] } this.handleKeyDown = this.handleKeyDown.bind(this) this.handleChange = this.handleChange.bind(this) this.handleCancel = this.handleCancel.bind(this) } componentDidMount() { // build index based on what's in the hierarchy const { entities, lookup } = this.props.institutions let index = [] this.index = index Object.keys(entities).forEach(name => { if (!name) return index.push([sanitizeForAutocomplete(name), name]) }) Object.keys(lookup).forEach(name => { if (!name) return index.push([sanitizeForAutocomplete(name), lookup[name]]) }) // console.log(index) // node.synonyms // .split("\n") // .map(word => word = word.trim()) // .filter(word => !!word) // .forEach(word => index.push([prefixName, name, node.id])) } componentDidUpdate(oldProps) { if (this.props.vetting !== oldProps.vetting) { this.handleChange({ target: { value: this.props.vetting } }) } else if (this.props.value !== oldProps.value) { this.setState({ q: '' }) } } handleKeyDown(e) { let id switch (e.keyCode) { case 27: // escape e.preventDefault() this.handleCancel() break case 37: // left case 38: // up e.preventDefault() this.setState({ selected: (this.state.matches.length + this.state.selected - 1) % this.state.matches.length }) return false case 39: // right case 40: // down e.preventDefault() this.setState({ selected: (this.state.selected + 1) % this.state.matches.length }) return false case 13: // enter id = this.state.matches[this.state.selected] e.preventDefault() this.handleSelect(id) return false default: break } return null } handleChange(e) { // search for the given string in our index const q = e.target.value let value = sanitizeForAutocomplete(q) if (!value.length) { this.setState({ q, selected: 0, matches: [], }) return } let seen = {} let matches = [] value.split(' ').forEach(word => { const re = new RegExp(word) this.index.forEach(([synonym, term]) => { if (synonym.match(re)) { if (synonym.indexOf(value) === 0) { if (term in seen) { seen[term] += 4 } else { seen[term] = 4 } } else if (term in seen) { seen[term] += 1 } else { seen[term] = 1 } } }) matches = Object.keys(seen) .map(term => [seen[term], term]) .sort((a, b) => { return b[0] - a[0] }) .slice(0, 100) .map(pair => pair[1]) }) this.setState({ q, selected: 0, matches, }) } handleSelect(name) { console.log('select', name) if (this.props.onSelect) this.props.onSelect(name) this.setState({ q: name, selected: 0, matches: [] }) } handleCancel() { if (this.props.onCancel) this.props.onCancel() this.setState({ q: '', selected: 0, matches: [] }) } render() { const { q, selected } = this.state const matches = this.state.matches.map((match, i) => { const label = formatLabel(match, q) return (