From 21d179c762d87e0790206fe59b08ffd49bb4a53b Mon Sep 17 00:00:00 2001 From: Jules Laplace Date: Wed, 13 Feb 2019 21:21:20 +0100 Subject: autocompleting --- scraper/client/common/autocomplete.component.js | 118 +++++++++++++----------- scraper/client/common/autocomplete.css | 39 ++++++++ scraper/client/common/common.css | 2 +- scraper/client/common/index.js | 1 + scraper/client/paper/paper.address.js | 110 +++++++++++++++++++--- scraper/client/paper/paper.css | 15 ++- 6 files changed, 215 insertions(+), 70 deletions(-) create mode 100644 scraper/client/common/autocomplete.css (limited to 'scraper/client') 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 = ( - - {formatLabel(parent.name, q)} - {' '}{'('}{formatLabel(node.name, q)}{')'} - - ) - } else { - label = formatLabel(node.name, q) - } + const label = formatLabel(match, q) return (
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} /> -
- {matches} -
+ {!!matches.length && +
+ {matches} +
+ }
) } @@ -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, diff --git a/scraper/client/paper/paper.address.js b/scraper/client/paper/paper.address.js index 6d02c3db..0da353c9 100644 --- a/scraper/client/paper/paper.address.js +++ b/scraper/client/paper/paper.address.js @@ -49,7 +49,7 @@ class PaperAddress extends Component { citation, }) } else { - console.log(paper) + // console.log(paper) const citation = this.getCitation(sha256) this.setState({ citation, @@ -71,29 +71,115 @@ class PaperAddress extends Component { if (!citation) { citation = (paperInfo.citations || []).find(f => f.id === sha256) } - console.log(sha256, citation) + // console.log(sha256, citation) return citation } + save() { + console.log(this.state) + } + render() { - // if (!this.props.match.params.key) return null - // if (this.props.api.address.loading) return - // if (!this.props.api.paperInfo.dataset) return
Metadata not found
- let { paperInfo, unknownCitations } = this.props.api - if (paperInfo.loading || unknownCitations.loading) return + let { institutions, paperInfo, unknownCitations } = this.props.api + if (institutions.loading || paperInfo.loading || unknownCitations.loading) return console.log(this.state) + console.log(institutions) const { citation } = this.state if (!citation) { return
Citation not found in this paper
- } + }console.log(this) return (

{citation.title}

{citation.id}
- {this.state.institution_1} - {/* -