diff options
| author | Jules Laplace <julescarbon@gmail.com> | 2019-02-12 19:21:29 +0100 |
|---|---|---|
| committer | Jules Laplace <julescarbon@gmail.com> | 2019-02-12 19:21:29 +0100 |
| commit | d97107ed5b242a3913df0d88ef4d8374b66ca25c (patch) | |
| tree | 510d8fc249b7ca761a137c61a1a5e1af1c5e3bbe /client | |
| parent | b1df7ef2090f1f3c9e0aeb49f5ffa6a33f4b8226 (diff) | |
begin geocode client
Diffstat (limited to 'client')
| -rw-r--r-- | client/geocode/autocomplete.component.js | 208 | ||||
| -rw-r--r-- | client/geocode/geocode.component.js | 16 | ||||
| -rw-r--r-- | client/geocode/index.js | 10 |
3 files changed, 234 insertions, 0 deletions
diff --git a/client/geocode/autocomplete.component.js b/client/geocode/autocomplete.component.js new file mode 100644 index 00000000..12419cf1 --- /dev/null +++ b/client/geocode/autocomplete.component.js @@ -0,0 +1,208 @@ +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 ( + <span> + { + 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(<b key={i}>{label.substr(len, value.length)}</b>, current) + len += value.length + current.length + return ret + }, []) + } + </span> + ) +} + +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) + } + + componentWillMount() { + // build index based on what's in the hierarchy + const { nodes } = this.props.hierarchy + let index = [] + this.index = index + Object.keys(nodes).forEach(key => { + const node = nodes[key] + if (!key || !node || !node.name || !node.parent) return + let { name } = node + let prefixName = name + if (node.is_attribute) { + const parent = nodes[node.parent] + if (parent) { + prefixName = parent.name + " (" + name + ")" + } + } + index.push([sanitizeForAutocomplete(prefixName), name, node.id]) + node.synonyms + .split("\n") + .map(word => word = word.trim()) + .filter(word => !!word) + .forEach(word => index.push([prefixName, name, node.id])) + }) + } + + 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 + } + } + + 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 + } + const re = new RegExp(value) + let matches = [] + let seen = {} + this.index.forEach(pair => { + if (seen[pair[2]]) return + if (pair[0].match(re)) { + seen[pair[2]] = true + if (pair[1].indexOf(value) === 0) { + matches.unshift(pair[2]) + } else { + matches.push(pair[2]) + } + } + }) + this.setState({ + q, + selected: 0, + matches: matches.slice(0, 10), + }) + } + + handleSelect(id) { + const { nodes } = this.props.hierarchy + const node = nodes[id] + if (this.props.onSelect) this.props.onSelect(node) + this.setState({ q: "", selected: 0, matches: [] }) + } + + handleCancel() { + if (this.props.onCancel) this.props.onCancel() + this.setState({ q: "", selected: 0, matches: [] }) + } + + render() { + // const suggestions = this.state.suggestions.map((suggestion)) + const { nodes } = this.props.hierarchy + const { q, selected } = this.state + const matches = this.state.matches.map((match, i) => { + const node = nodes[match] + const parent = nodes[node.parent] + let label + if (node.is_attribute) { + label = ( + <span> + {formatLabel(parent.name, q)} + {' '}<small>{'('}{formatLabel(node.name, q)}{')'}</small> + </span> + ) + } else { + label = formatLabel(node.name, q) + } + return ( + <div + key={i} + className={selected === i ? 'selected' : ''} + onClick={() => this.handleSelect(node.id)} + onMouseEnter={() => this.setState({ selected: i })} + > + {label} + </div> + ) + }) + return ( + <div className="autocomplete"> + <input + type="text" + name="q" + value={this.state.q} + onKeyDown={this.handleKeyDown} + onChange={this.handleChange} + autoFocus + autoCapitalize="off" + autoComplete="off" + placeholder="Start typing a name" + /> + <div className="matches"> + {matches} + </div> + </div> + ) + } +} + +const mapStateToProps = (state, ownProps) => ({ + onSelect: ownProps.onSelect, +}) + +const mapDispatchToProps = (dispatch) => ({ +}) + +export default connect(mapStateToProps, mapDispatchToProps)(Autocomplete) diff --git a/client/geocode/geocode.component.js b/client/geocode/geocode.component.js new file mode 100644 index 00000000..a6766bec --- /dev/null +++ b/client/geocode/geocode.component.js @@ -0,0 +1,16 @@ +import React, { Component } from 'react' + +import Autocomplete from './autocomplete.component' + +class GeocodeContainer extends Component { + render() { + // const { } = this.props + return ( + <div className=''> + </div> + ) + } +} + + +export default GeocodeContainer diff --git a/client/geocode/index.js b/client/geocode/index.js new file mode 100644 index 00000000..4f0da3f6 --- /dev/null +++ b/client/geocode/index.js @@ -0,0 +1,10 @@ +import React from 'react' +import ReactDOM from 'react-dom' + +// import { toArray } from '../util' + +import GeocodeContainer from './autocomplete.component' + +ReactDOM.render( + <GeocodeContainer />, document.querySelector('#container') +) |
