diff options
Diffstat (limited to 'client')
| -rw-r--r-- | client/chart/constants.js | 63 | ||||
| -rw-r--r-- | client/chart/singlePie.chart.js | 36 | ||||
| -rw-r--r-- | client/common/loader.component.js | 7 | ||||
| -rw-r--r-- | client/index.js | 9 | ||||
| -rw-r--r-- | client/table/citations.table.js | 22 | ||||
| -rw-r--r-- | client/table/file.table.js | 66 | ||||
| -rw-r--r-- | client/table/tabulator.css | 12 |
7 files changed, 144 insertions, 71 deletions
diff --git a/client/chart/constants.js b/client/chart/constants.js index b916cbd2..0f23f06b 100644 --- a/client/chart/constants.js +++ b/client/chart/constants.js @@ -15,6 +15,24 @@ export const rainbow = [ // '#888888', ] +export const bigRainbow = [ + '#9e0142', + '#d53e4f', + '#f46d43', + '#fdae61', + '#fee08b', + '#ffffbf', + '#e6f598', + '#abdda4', + '#66c2a5', + '#3288bd', + '#5e4fa2', + '#8744ae', + '#a044ae', + '#b342a4', + '#b34287', + // '#888888', +] export const colorblindSafeRainbow = [ '#a50026', '#d73027', @@ -30,12 +48,35 @@ export const colorblindSafeRainbow = [ // '#888888', ] +export const categoryRainbow = [ + '#5e4fa2', + '#66c2a5', + '#d53e4f', + '#f46d43', + '#9e0142', + '#3288bd', + '#fee08b', + '#abdda4', + '#e6f598', + '#fdae61', + '#ffffbf', + // '#888888', +] + export const institutionColors = [ '#f2f293', // edu (yellow) '#3264f6', // company (blue) '#f30000', // gov/mil (red) ] +export const colorTable = { + rainbow, + bigRainbow, + colorblindSafeRainbow, + institutionColors, + categoryRainbow, +} + /* stuff for a 'countries' legend */ export const topCountryCount = 10 @@ -45,20 +86,20 @@ export const otherCountriesLabel = 'Other Countries' /* institution tuples, labels and templates */ export const initialInstitutionLookup = { - 'edu': 0, - 'company': 0, - 'gov': 0, + edu: 0, + company: 0, + gov: 0, } export const institutionOrder = { - 'edu': 0, - 'company': 1, - 'gov': 2, + edu: 0, + company: 1, + gov: 2, } export const institutionLabels = { - 'edu': 'Academic', - 'company': 'Commercial', - 'gov': 'Military / Government', - 'mil': 'Military / Government', -}
\ No newline at end of file + edu: 'Academic', + company: 'Commercial', + gov: 'Military / Government', + mil: 'Military / Government', +} diff --git a/client/chart/singlePie.chart.js b/client/chart/singlePie.chart.js index fcff3214..2e770bd7 100644 --- a/client/chart/singlePie.chart.js +++ b/client/chart/singlePie.chart.js @@ -2,41 +2,28 @@ import React, { Component } from 'react' import csv from 'parse-csv' import C3Chart from 'react-c3js' -import { toTuples } from '../util' - import 'c3/c3.css' import './chart.css' import { - rainbow, otherCountriesLabel, - institutionOrder, institutionLabels, - initialInstitutionLookup, + rainbow, bigRainbow, colorTable } from './constants' class SinglePieChart extends Component { state = { keys: [], data: [], - fields: {}, } componentDidMount() { const { payload } = this.props - console.log(payload) - console.log(payload.fields) - const fields = {} - payload.fields.forEach(field => { - const [k, v] = field.split(': ') - fields[k] = v - }) - fetch(payload.url, { mode: 'cors' }) .then(r => r.text()) .then(text => { try { const keys = text.split('\n')[0].split(',').map(s => s.trim().replace(/"/, '')) const data = csv.toJSON(text, { headers: { included: true } }) - this.setState({ keys, data, fields }) + this.setState({ keys, data }) } catch (e) { console.error("error making json:", payload.url) console.error(e) @@ -45,9 +32,9 @@ class SinglePieChart extends Component { } render() { - const { payload } = this.props - const { keys, data, fields } = this.state - console.log(keys, data) + const { fields } = this.props.payload + const { keys, data } = this.state + // console.log(keys, data) const [labelField, numberField] = keys if (!data.length) return null @@ -59,15 +46,16 @@ class SinglePieChart extends Component { return [label, number] }).sort((a, b) => b[1] - a[1]) - console.log(rows) - let chartRows = rows.slice(0, rowsToDisplay) - let otherCount = rows.slice(rowsToDisplay).reduce((a, b) => { return a + b[1] }, 0) + let otherCount = rows.slice(rowsToDisplay).reduce((a, b) => a + b[1], 0) if (otherCount > 0) { chartRows.push([fields.OtherLabel, otherCount]) } - console.log(rowsToDisplay, chartRows) + const height = chartRows.length < 6 ? 316 : + chartRows.length < 10 ? 336 : 356 + + const pattern = colorTable[fields.Colors] || (chartRows.length < 10 ? rainbow : bigRainbow) return ( <div className='chart'> @@ -78,7 +66,7 @@ class SinglePieChart extends Component { type: 'pie', }} color={{ - pattern: rainbow, + pattern, }} tooltip={{ format: { @@ -86,7 +74,7 @@ class SinglePieChart extends Component { } }} size={{ - height: chartRows.length < 4 ? 316 : 336, + height, }} /> <span className='chartCaption'>{fields.Caption}</span> diff --git a/client/common/loader.component.js b/client/common/loader.component.js index df25dd39..b24b31b1 100644 --- a/client/common/loader.component.js +++ b/client/common/loader.component.js @@ -5,9 +5,14 @@ export default function Loader() { const spinCfg = { width: 5, radius: 20, + speed: 1, color: 'white', } - return <Spinner config={spinCfg} /> + return ( + <div style={{ position: 'relative' }}> + <Spinner config={spinCfg} /> + </div> + ) // return ( // <div className='loaderWrapper'> // <div className='loader'> diff --git a/client/index.js b/client/index.js index 835d859c..9644ba5c 100644 --- a/client/index.js +++ b/client/index.js @@ -73,6 +73,15 @@ function runApplets() { let opt = null payload.cmd = cmd payload.partz = cmdPartz + const fields = {} + if (payload.fields) { + payload.fields.forEach(field => { + const [k, v] = field.split(': ') + fields[k] = v + }) + } + payload.fields = fields + if (payload.cmd === 'load_file' || payload.cmd === 'single_pie_chart') { payload.url = 'https://nyc3.digitaloceanspaces.com/megapixels/v1' + cmdPartz.shift() return [el, payload] diff --git a/client/table/citations.table.js b/client/table/citations.table.js index 8fe46b69..c1c71906 100644 --- a/client/table/citations.table.js +++ b/client/table/citations.table.js @@ -1,11 +1,9 @@ import React, { Component } from 'react' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' import { ReactTabulator } from 'react-tabulator' import { saveAs } from 'file-saver' import { Loader } from '../common' -import { toArray, toTuples, domainFromUrl } from '../util' +import { domainFromUrl } from '../util' export const citationsColumns = [ { title: 'Title', field: 'title', sorter: 'string' }, @@ -64,12 +62,12 @@ class CitationsTable extends Component { if (!q.length) { this.setState({ q, filteredCitations: formattedCitations }) } else { - let q_re = new RegExp('(' + q.replace(/\s+/g, ' ').trim().replace(' ', '|') + ')', 'gi') + let qRe = new RegExp('(' + q.replace(/\s+/g, ' ').trim().replace(' ', '|') + ')', 'gi') let filteredCitations = formattedCitations.filter(citation => ( - citation.title.match(q_re) || - citation.institution.match(q_re) || - citation.country.match(q_re) - )) + citation.title.match(qRe) || + citation.institution.match(qRe) || + citation.country.match(qRe) + )) this.setState({ q, filteredCitations }) } } @@ -85,7 +83,7 @@ class CitationsTable extends Component { case 'number': return String(data) default: - return '\"' + String(data) + '\"' + return '"' + String(data) + '"' } }) return row.join(",") @@ -96,8 +94,8 @@ class CitationsTable extends Component { titles.join(','), ...rows, ].join('\n') - ], {type: "text/csv;charset=utf-8"}); - saveAs(blob, fn); + ], { type: "text/csv;charset=utf-8" }) + saveAs(blob, fn) } render() { @@ -111,7 +109,7 @@ class CitationsTable extends Component { value={this.state.q} onChange={e => this.updateFilter(e.target.value)} className='q' - placeholder='Enter text to search citations...' + placeholder='Enter text to search citations' /> <span className='download' onClick={() => this.download()}>Download CSV</span> </div> diff --git a/client/table/file.table.js b/client/table/file.table.js index ff6c0af6..c195b09d 100644 --- a/client/table/file.table.js +++ b/client/table/file.table.js @@ -1,18 +1,17 @@ import React, { Component } from 'react' -import { bindActionCreators } from 'redux' -import { connect } from 'react-redux' import { ReactTabulator } from 'react-tabulator' +import csv from 'parse-csv' -import { toArray, toTuples, domainFromUrl } from '../util' +import { domainFromUrl } from '../util' import { Loader } from '../common' -import csv from 'parse-csv' - class FileTable extends Component { state = { keys: [], data: [], + filteredData: [], columns: [], + q: '', } componentDidMount() { @@ -26,7 +25,7 @@ class FileTable extends Component { const data = csv.toJSON(text, { headers: { included: true } }) // console.log(data) const columns = this.getColumns(keys, data, payload.fields) - this.setState({ keys, data, columns }) + this.setState({ keys, data, filteredData: data, columns }) } catch (e) { console.error("error making json:", payload.url) console.error(e) @@ -35,10 +34,11 @@ class FileTable extends Component { } getColumns(keys, data, fields) { - let titles = fields.length ? fields[0].split(', ') : keys - let numberFields = [] + let titles = fields.Headings ? fields.Headings.split(', ') : keys + // let numberFields = [] let columns = keys.map((field, i) => { const title = titles[i] || field + let widthGrow = 1 if (field.match('url')) { let textField = field.replace('url', 'label') data.forEach(el => el[textField] = domainFromUrl(el[field])) @@ -46,36 +46,64 @@ class FileTable extends Component { title, field: textField, formatter: 'link', - formatterParams: { target: "_blank", urlField: field, }, + formatterParams: { target: "_blank", urlField: field }, sorter: 'string' } } + if (title === 'Embassy') { + widthGrow = 2 + } switch (field) { case 'images': case 'year': return { title, field: field.toLowerCase(), sorter: 'number' } default: - return { title, field: field.toLowerCase(), sorter: 'string' } + return { title, field: field.toLowerCase(), sorter: 'string', widthGrow } } }) return columns } + updateFilter(q) { + const { keys, data } = this.state + if (!q.length) { + this.setState({ q, filteredData: data }) + } else { + let qRe = new RegExp('(' + q.replace(/\s+/g, ' ').trim().replace(' ', '|') + ')', 'gi') + let filteredData = data.filter(row => keys.some(key => row[key].match(qRe))) + this.setState({ q, filteredData }) + } + } + render() { const { payload } = this.props + const { q, columns, data, filteredData } = this.state if (!this.state.data.length) { return <Loader /> } + const fn = payload.url.split('/').pop() return ( - <ReactTabulator - columns={this.state.columns} - data={this.state.data} - options={{ - height: Math.min(37 * this.state.data.length + 29, 311), - layout: 'fitColumns', - placeholder: 'No Data Set', - }} - /> + <div className='citationBrowser'> + <div className='citationHeader'> + <input + type="text" + value={q} + onChange={e => this.updateFilter(e.target.value)} + className='q' + placeholder='Enter text to search data' + /> + <a className='download' href={payload.url} filename={fn}>Download CSV</a> + </div> + <ReactTabulator + columns={columns} + data={filteredData} + options={{ + height: Math.min(37 * data.length + 29, 311), + layout: 'fitColumns', + placeholder: 'No Data Set', + }} + /> + </div> ) } } diff --git a/client/table/tabulator.css b/client/table/tabulator.css index 95768976..0ea81974 100644 --- a/client/table/tabulator.css +++ b/client/table/tabulator.css @@ -40,7 +40,7 @@ max-width: 400px; margin-bottom: 10px; background-image: url(/assets/img/icon-search.png); - background-position: 380px center; + background-position: 378px center; background-repeat: no-repeat; box-shadow: 0px 2px 4px rgba(0,0,0,0.2); border: 0; @@ -53,17 +53,21 @@ align-items: flex-start; justify-content: space-between; } -span.download { +.download { display: block; font-size: 13px; color: #ddd; cursor: pointer; background: #333; - padding: 5px 8px; + padding: 5px 8px 5px 8px; border-radius: 5px; transition: all 0.2s; + border: 0 !important; } -.desktop span.download:hover { +.content a.download { + padding: 5px 8px 5px 8px; +} +.desktop .download:hover { color: #fff; background: #666; }
\ No newline at end of file |
