summaryrefslogtreecommitdiff
path: root/scraper/client/common
diff options
context:
space:
mode:
authorJules Laplace <julescarbon@gmail.com>2019-02-13 21:21:20 +0100
committerJules Laplace <julescarbon@gmail.com>2019-02-13 21:21:20 +0100
commit21d179c762d87e0790206fe59b08ffd49bb4a53b (patch)
tree2e1183aec6a34cd85e3358e7240a5aa1731e5d6d /scraper/client/common
parent5c176ce457f195dfad15d0c7d01d36fc2f9fdbdd (diff)
autocompleting
Diffstat (limited to 'scraper/client/common')
-rw-r--r--scraper/client/common/autocomplete.component.js118
-rw-r--r--scraper/client/common/autocomplete.css39
-rw-r--r--scraper/client/common/common.css2
-rw-r--r--scraper/client/common/index.js1
4 files changed, 103 insertions, 57 deletions
diff --git a/scraper/client/common/autocomplete.component.js b/scraper/client/common/autocomplete.component.js
index 12419cf1..03039b1c 100644
--- a/scraper/client/common/autocomplete.component.js
+++ b/scraper/client/common/autocomplete.component.js
@@ -47,29 +47,33 @@ class Autocomplete extends Component {
this.handleCancel = this.handleCancel.bind(this)
}
- componentWillMount() {
+ componentDidMount() {
// build index based on what's in the hierarchy
- const { nodes } = this.props.hierarchy
+ const { entities, lookup } = this.props.institutions
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]))
+ 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.handleChange({ target: { value: this.props.value }})
+ }
}
handleKeyDown(e) {
@@ -101,6 +105,7 @@ class Autocomplete extends Component {
default:
break
}
+ return null
}
handleChange(e) {
@@ -115,31 +120,42 @@ class Autocomplete extends Component {
})
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])
+ 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: matches.slice(0, 10),
+ matches,
})
}
- handleSelect(id) {
- const { nodes } = this.props.hierarchy
- const node = nodes[id]
- if (this.props.onSelect) this.props.onSelect(node)
+ handleSelect(name) {
+ if (this.props.onSelect) this.props.onSelect(name)
this.setState({ q: "", selected: 0, matches: [] })
}
@@ -149,28 +165,14 @@ class Autocomplete extends Component {
}
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)
- }
+ const label = formatLabel(match, q)
return (
<div
key={i}
className={selected === i ? 'selected' : ''}
- onClick={() => this.handleSelect(node.id)}
+ onClick={() => this.handleSelect(match)}
onMouseEnter={() => this.setState({ selected: i })}
>
{label}
@@ -185,14 +187,17 @@ class Autocomplete extends Component {
value={this.state.q}
onKeyDown={this.handleKeyDown}
onChange={this.handleChange}
- autoFocus
+ autoFocus={this.props.autoFocus}
autoCapitalize="off"
autoComplete="off"
- placeholder="Start typing a name"
+ placeholder={this.props.placeholder}
+ ref={ref => this._el = ref}
/>
- <div className="matches">
- {matches}
- </div>
+ {!!matches.length &&
+ <div className="matches">
+ {matches}
+ </div>
+ }
</div>
)
}
@@ -200,6 +205,7 @@ class Autocomplete extends Component {
const mapStateToProps = (state, ownProps) => ({
onSelect: ownProps.onSelect,
+ institutions: state.api.institutions,
})
const mapDispatchToProps = (dispatch) => ({
diff --git a/scraper/client/common/autocomplete.css b/scraper/client/common/autocomplete.css
new file mode 100644
index 00000000..ac90ea72
--- /dev/null
+++ b/scraper/client/common/autocomplete.css
@@ -0,0 +1,39 @@
+.autocomplete {
+ position: relative;
+ margin-bottom: 4px;
+}
+.autocomplete input[type=text],
+.autocomplete .matches {
+ width: 400px;
+}
+.autocomplete div {
+ background: white;
+ cursor: pointer;
+}
+.autocomplete .matches {
+ position: absolute;
+ top: 100%;
+ left: 0;
+ border: 1px solid #888;
+ max-height: 220px;
+ overflow-y: auto;
+ z-index: 1;
+}
+.autocomplete .matches div {
+ padding: 3px;
+}
+.autocomplete .matches div:nth-child(even) {
+ background: #eee;
+}
+.autocomplete .matches div:nth-child(odd) {
+ background: #fff;
+}
+.autocomplete .matches div:nth-child(even).selected,
+.autocomplete .matches div:nth-child(odd).selected {
+ color: #fff;
+ background: #000;
+ cursor: pointer;
+}
+.autocomplete .selected {
+ background: rgba(128,128,128,0.2);
+} \ No newline at end of file
diff --git a/scraper/client/common/common.css b/scraper/client/common/common.css
index b014541a..fb31aefb 100644
--- a/scraper/client/common/common.css
+++ b/scraper/client/common/common.css
@@ -7,7 +7,7 @@ html,body {
}
body {
font-family: Helvetica, sans-serif;
- font-weight: 300;
+ color: #333;
}
h1 {
diff --git a/scraper/client/common/index.js b/scraper/client/common/index.js
index 8ced56b3..8925aae0 100644
--- a/scraper/client/common/index.js
+++ b/scraper/client/common/index.js
@@ -5,6 +5,7 @@ import Gate from './gate.component'
import Autocomplete from './autocomplete.component'
import { TableObject, TableArray, TableTuples, TableRow, TableCell } from './table.component'
import './common.css'
+import './autocomplete.css'
export {
Header,