diff options
-rw-r--r-- | package-lock.json | 83 | ||||
-rw-r--r-- | package.json | 1 | ||||
-rw-r--r-- | src/client/views/nodes/DataViz.tsx | 20 | ||||
-rw-r--r-- | src/client/views/nodes/DataVizBox/ChartBox.tsx | 296 |
4 files changed, 276 insertions, 124 deletions
diff --git a/package-lock.json b/package-lock.json index 7d34ec01f..731599c73 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4997,6 +4997,11 @@ "postcss-value-parser": "^3.3.0" } }, + "css-unit-converter": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/css-unit-converter/-/css-unit-converter-1.1.2.tgz", + "integrity": "sha512-IiJwMC8rdZE0+xiEZHeru6YoONC4rfPMqGm2W85jMIbkFvv5nFTwJVFHam2eFrN6txmoUYFAFXiv8ICVeTO0MA==" + }, "css-vendor": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", @@ -5280,6 +5285,11 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=" }, + "decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==" + }, "decode-uri-component": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", @@ -7509,8 +7519,7 @@ "eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "dev": true + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==" }, "events": { "version": "3.3.0", @@ -7961,6 +7970,11 @@ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==", "dev": true }, + "fast-equals": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-2.0.4.tgz", + "integrity": "sha512-caj/ZmjHljPrZtbzJ3kfH5ia/k4mTJe/qSiXAGzxZWRZgsgDV0cvNaQULqUX8t0/JVlzzEdYOwCN5DmzTxoD4w==" + }, "fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -17857,6 +17871,14 @@ "resolved": "https://registry.npmjs.org/react-resizable-rotatable-draggable/-/react-resizable-rotatable-draggable-0.2.0.tgz", "integrity": "sha512-F8TPx3z7/AcmRViySbYV3LpUWXFpHlGAmKmNcYMgPlS+h1eYFazRG3xYS8Z6e48hWY1EcCny/YNrwRNUrap8CQ==" }, + "react-resize-detector": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/react-resize-detector/-/react-resize-detector-7.1.2.tgz", + "integrity": "sha512-zXnPJ2m8+6oq9Nn8zsep/orts9vQv3elrpA+R8XTcW7DVVUJ9vwDwMXaBtykAYjMnkCIaOoK9vObyR7ZgFNlOw==", + "requires": { + "lodash": "^4.17.21" + } + }, "react-reveal": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/react-reveal/-/react-reveal-1.2.2.tgz", @@ -17917,6 +17939,28 @@ } } }, + "react-smooth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-2.0.1.tgz", + "integrity": "sha512-Own9TA0GPPf3as4vSwFhDouVfXP15ie/wIHklhyKBH5AN6NFtdk0UpHBnonV11BtqDkAWlt40MOUc+5srmW7NA==", + "requires": { + "fast-equals": "^2.0.0", + "react-transition-group": "2.9.0" + }, + "dependencies": { + "react-transition-group": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz", + "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==", + "requires": { + "dom-helpers": "^3.4.0", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2", + "react-lifecycles-compat": "^3.0.4" + } + } + } + }, "react-table": { "version": "6.11.5", "resolved": "https://registry.npmjs.org/react-table/-/react-table-6.11.5.tgz", @@ -18091,6 +18135,32 @@ "resolved": "https://registry.npmjs.org/readline/-/readline-1.3.0.tgz", "integrity": "sha1-xYDXfvLPyHUrEySYBg3JeTp6wBw=" }, + "recharts": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.1.12.tgz", + "integrity": "sha512-dAzEuc9AjM+IF0A33QzEdBEUnyGKJcGUPa0MYm0vd38P3WouQjrj2egBrCNInE7ZcQwN+z3MoT7Rw03u8nP9HA==", + "requires": { + "classnames": "^2.2.5", + "d3-interpolate": "^2.0.0", + "d3-scale": "^3.0.0", + "d3-shape": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.19", + "react-is": "^16.10.2", + "react-resize-detector": "^7.1.2", + "react-smooth": "^2.0.1", + "recharts-scale": "^0.4.4", + "reduce-css-calc": "^2.1.8" + } + }, + "recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "requires": { + "decimal.js-light": "^2.4.1" + } + }, "rechoir": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", @@ -18126,6 +18196,15 @@ "strip-indent": "^1.0.1" } }, + "reduce-css-calc": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/reduce-css-calc/-/reduce-css-calc-2.1.8.tgz", + "integrity": "sha512-8liAVezDmUcH+tdzoEGrhfbGcP7nOV4NkGE3a74+qqvE7nt9i4sKLGBuZNOnpI4WiGksiNPklZxva80061QiPg==", + "requires": { + "css-unit-converter": "^1.1.1", + "postcss-value-parser": "^3.3.0" + } + }, "reduce-flatten": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-1.0.1.tgz", diff --git a/package.json b/package.json index 488375b03..1e43390d8 100644 --- a/package.json +++ b/package.json @@ -283,6 +283,7 @@ "react-table": "^6.11.5", "react-transition-group": "^4.4.2", "readline": "^1.3.0", + "recharts": "^2.1.12", "request": "^2.88.2", "request-promise": "^4.2.6", "reveal.js": "^4.3.0", diff --git a/src/client/views/nodes/DataViz.tsx b/src/client/views/nodes/DataViz.tsx deleted file mode 100644 index df4c8f937..000000000 --- a/src/client/views/nodes/DataViz.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import { observer } from 'mobx-react'; -import * as React from 'react'; -import { ViewBoxBaseComponent } from '../DocComponent'; -import './DataViz.scss'; -import { FieldView, FieldViewProps } from './FieldView'; - -@observer -export class DataVizBox extends ViewBoxBaseComponent<FieldViewProps>() { - public static LayoutString(fieldKey: string) { - return FieldView.LayoutString(DataVizBox, fieldKey); - } - - render() { - return ( - <div> - <div>Hi</div> - </div> - ); - } -} diff --git a/src/client/views/nodes/DataVizBox/ChartBox.tsx b/src/client/views/nodes/DataVizBox/ChartBox.tsx index 07f754637..42bd8a7f6 100644 --- a/src/client/views/nodes/DataVizBox/ChartBox.tsx +++ b/src/client/views/nodes/DataVizBox/ChartBox.tsx @@ -1,27 +1,17 @@ -import { observer } from "mobx-react"; -import * as React from "react"; -import { Doc } from "../../../../fields/Doc"; -import { - Chart as ChartJS, - CategoryScale, - LinearScale, - BarElement, - Title, - Tooltip, - Legend, - InteractionItem, - PointElement, - LineElement, - } from 'chart.js'; - import { Bar, getDatasetAtEvent, getElementAtEvent, Line } from 'react-chartjs-2'; -import { ChartJSOrUndefined } from "react-chartjs-2/dist/types"; -import { action, computed, observable } from "mobx"; -import { Cast, StrCast } from "../../../../fields/Types"; - +import { observer } from 'mobx-react'; +import * as React from 'react'; +import { Doc } from '../../../../fields/Doc'; +// import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, InteractionItem, PointElement, LineElement } from 'chart.js'; +// import { Bar, getDatasetAtEvent, getElementAtEvent, Line } from 'react-chartjs-2'; +import { ChartJSOrUndefined } from 'react-chartjs-2/dist/types'; +import { action, computed, observable } from 'mobx'; +import { Cast, StrCast } from '../../../../fields/Types'; +import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; +import { CategoricalChartState } from 'recharts/types/chart/generateCategoricalChart'; export interface ChartBoxProps { rootDoc: Doc; - pairs: {x: number, y:number}[]; + pairs: { x: number; y: number }[]; } export interface ChartJsData { @@ -30,85 +20,127 @@ export interface ChartJsData { label: string; data: number[]; backgroundColor: string[]; - }[] + }[]; } -ChartJS.register( - CategoryScale, - LinearScale, - BarElement, - Title, - Tooltip, - Legend, - PointElement, - LineElement - ); - +// ChartJS.register(CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend, PointElement, LineElement); + const primaryColor = 'rgba(53, 162, 235, 0.5)'; const selectedColor = 'rgba(255, 99, 132, 0.5)'; +const data = [ + { + name: 'Page A', + uv: 4000, + pv: 2400, + amt: 2400, + }, + { + name: 'Page B', + uv: 3000, + pv: 1398, + amt: 2210, + }, + { + name: 'Page C', + uv: 2000, + pv: 9800, + amt: 2290, + }, + { + name: 'Page D', + uv: 2780, + pv: 3908, + amt: 2000, + }, + { + name: 'Page E', + uv: 1890, + pv: 4800, + amt: 2181, + }, + { + name: 'Page F', + uv: 2390, + pv: 3800, + amt: 2500, + }, + { + name: 'Page G', + uv: 3490, + pv: 4300, + amt: 2100, + }, +]; + +export interface RechartData { + name: string | number; + y: number; +} + @observer export class ChartBox extends React.Component<ChartBoxProps> { - private _chartRef: any = React.createRef<ChartJSOrUndefined<"bar", number[], string>>(); - - @observable private _prevColor: string | undefined= undefined; - @observable private _prevIndex: { dIndex: number, index: number} | undefined = undefined; - @observable private _chartJsData: ChartJsData | undefined= undefined; + @observable private _chartData: RechartData[] = []; @computed get currView() { if (this.props.rootDoc._dataVizView) { return StrCast(this.props.rootDoc._currChartView); } else { - return "table"; + return 'table'; } } constructor(props: any) { super(props); if (!this.props.rootDoc._currChartView) { - this.props.rootDoc._currChartView = "bar"; - } - } - - @computed get options() { - return { - responsive: true, - plugins: { - legend: { - position: 'top' as const, - }, - title: { - display: true, - text: 'Bar Chart', - }, - }, + this.props.rootDoc._currChartView = 'bar'; } } @action - generateChartJsData() { + generateChartData() { if (this.props.rootDoc._chartData) { - // parse the string into a json object - this._chartJsData = JSON.parse(StrCast(this.props.rootDoc._chartData)); - this._prevColor = StrCast(this.props.rootDoc._prevColor); - this._prevIndex = JSON.parse(StrCast(this.props.rootDoc._prevIndex)); + this._chartData = JSON.parse(StrCast(this.props.rootDoc._chartData)); return; } - const labels = this.props.pairs.map(p => p.x); - const dataset = { - label: 'Dataset 1', - data: this.props.pairs.map(p => p.y), - backgroundColor: this.props.pairs.map(p => primaryColor), - } - const data = { - labels, - datasets: [dataset] - }; - this._chartJsData = data; + + // generate the chart data according to the RechartData type from the pairs prop + const chartData: RechartData[] = []; + this.props.pairs.forEach(pair => { + chartData.push({ + name: pair.x, + y: pair.y, + }); + }); + + this._chartData = chartData; } - + + // @action + // generateChartJsData() { + // if (this.props.rootDoc._chartData) { + // // parse the string into a json object + // this._chartJsData = JSON.parse(StrCast(this.props.rootDoc._chartData)); + // this._prevColor = StrCast(this.props.rootDoc._prevColor); + // this._prevIndex = JSON.parse(StrCast(this.props.rootDoc._prevIndex)); + // return; + // } + // const labels = this.props.pairs.map(p => p.x); + // const dataset = { + // label: 'Dataset 1', + // data: this.props.pairs.map(p => p.y), + // backgroundColor: this.props.pairs.map(p => primaryColor), + // }; + // const data = { + // labels, + // datasets: [dataset], + // }; + // this._chartJsData = data; + // } + componentDidMount() { - this.generateChartJsData(); + // this.generateChartJsData(); + this.generateChartData(); } @action @@ -117,46 +149,106 @@ export class ChartBox extends React.Component<ChartBoxProps> { e.stopPropagation(); console.log(e.currentTarget.value); this.props.rootDoc._currChartView = e.currentTarget.value.toLowerCase(); - } + }; - @action - onClick = (e: any) => { - e.preventDefault(); - if (getDatasetAtEvent(this._chartRef.current, e).length == 0) return; - if (!this._chartJsData) return; - if (this._prevIndex && this._prevColor) { - this._chartJsData.datasets[this._prevIndex.dIndex].backgroundColor[this._prevIndex.index] = this._prevColor; + // @action + // onClick = (e: React.MouseEvent<HTMLCanvasElement, MouseEvent>) => { + // e.preventDefault(); + // console.log(e); + + // if (getDatasetAtEvent(this._chartRef.current, e).length == 0) return; + // if (!this._chartJsData) return; + // if (this._prevIndex && this._prevColor) { + // this._chartJsData.datasets[this._prevIndex.dIndex].backgroundColor[this._prevIndex.index] = this._prevColor; + // } + + // const currSelected = getElementAtEvent(this._chartRef.current, e); + // // TODO - nda: the currSelected might not have the updated color variables so look into that + // this._currSelected = currSelected; + // const index = { datasetIndex: currSelected[0].datasetIndex, index: currSelected[0].index }; + // this._prevIndex = { dIndex: index.datasetIndex, index: index.index }; + // this._prevColor = this._chartJsData.datasets[index.datasetIndex].backgroundColor[index.index]; + // this._chartJsData.datasets[index.datasetIndex].backgroundColor[index.index] = selectedColor; + // this._chartRef.current.update(); + // // stringify this._chartJsData + // const strData = JSON.stringify(this._chartJsData); + // this.props.rootDoc._chartData = strData; + // this.props.rootDoc._prevColor = this._prevColor; + // this.props.rootDoc._prevIndex = JSON.stringify(this._prevIndex); + // }; + + onMouseMove = (e: CategoricalChartState) => { + console.log(e); + + // pops up at 526 when it should be at 263 + // at 100%scale it pops up at 526 but mouse shows 263 at 50% scale + if (e.chartX && e.chartY) { + e.chartX += 263; + e.chartY += 263; } + // if (e.chartX && e.chartY) { + // e.chartX -= 200; + // e.chartY -= 200; + // } + }; - const currSelected = getElementAtEvent(this._chartRef.current, e); - const index = { datasetIndex: currSelected[0].datasetIndex, index: currSelected[0].index }; - this._prevIndex = { dIndex: index.datasetIndex, index: index.index }; - this._prevColor = this._chartJsData.datasets[index.datasetIndex].backgroundColor[index.index]; - this._chartJsData.datasets[index.datasetIndex].backgroundColor[index.index] = selectedColor; - this._chartRef.current.update(); - // stringify this._chartJsData - const strData = JSON.stringify(this._chartJsData); - this.props.rootDoc._chartData = strData; - this.props.rootDoc._prevColor = this._prevColor; - this.props.rootDoc._prevIndex = JSON.stringify(this._prevIndex); + handleDotClick = (e: any) => { + console.log(e); + }; + + // handleTextClick = (e: any) => { + // console.log(e); + // } + + @computed get reLineChart() { + return ( + <ResponsiveContainer width="80%" height="80%"> + <LineChart + width={500} + height={300} + data={this._chartData} + margin={{ + top: 5, + right: 30, + left: 20, + bottom: 5, + }} + onMouseDown={e => console.log(e)} + onMouseMove={e => this.onMouseMove(e)}> + <CartesianGrid strokeDasharray="3 3" /> + <XAxis dataKey="name" onMouseDown={e => console.log(e)} /> + <YAxis /> + <Tooltip /> + <Legend /> + <Line type="monotone" dataKey="y" stroke="#8884d8" activeDot={{ r: 8, onClick: this.handleDotClick }} /> + {/* <Line type="monotone" dataKey="uv" stroke="#82ca9d" /> */} + </LineChart> + </ResponsiveContainer> + ); } render() { - if (this.props.pairs && this._chartJsData) { + if (this.props.pairs && this._chartData.length > 0) { return ( <div> <div> - {this.props.rootDoc._currChartView == "line" ? - (<Line ref={this._chartRef} options={this.options} data={this._chartJsData} onClick={(e) => this.onClick(e)} />) : - (<Bar ref={this._chartRef} options={this.options} data={this._chartJsData} onClick={(e) => this.onClick(e)} />) - } + {/*{this.props.rootDoc._currChartView == 'line' ? ( + <Line ref={this._chartRef} options={this.options} data={this._chartJsData} onClick={e => this.onClick(e)} /> + ) : ( + <Bar ref={this._chartRef} options={this.options} data={this._chartJsData} onClick={e => this.onClick(e)} /> + )} */} + {this.reLineChart} </div> - <button onClick={(e) => this.onClickChangeChart(e)} value="line">Line</button> - <button onClick={(e) => this.onClickChangeChart(e)} value="bar">Bar</button> + <button onClick={e => this.onClickChangeChart(e)} value="line"> + Line + </button> + <button onClick={e => this.onClickChangeChart(e)} value="bar"> + Bar + </button> </div> - ) + ); } else { - return <div></div> + return <div></div>; } } -}
\ No newline at end of file +} |