From 9136b17ed9021bd9435117f68ff66c4dbb1a1889 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Thu, 14 Jul 2022 16:38:00 -0400 Subject: refactored code into d3 utils --- src/client/views/nodes/DataVizBox/utils/D3Utils.ts | 59 ++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 src/client/views/nodes/DataVizBox/utils/D3Utils.ts (limited to 'src/client/views/nodes/DataVizBox/utils') diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts new file mode 100644 index 000000000..285298472 --- /dev/null +++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts @@ -0,0 +1,59 @@ +import d3 from 'd3'; +import { DataPoint } from '../ChartBox'; + +// TODO: nda - implement function that can handle range for strings + +export const minMaxRange = (dataPts: DataPoint[]) => { + const yMin = d3.min(dataPts, d => d.y); + const yMax = d3.max(dataPts, d => d.y); + + const xMin = d3.min(dataPts, d => d.x); + const xMax = d3.max(dataPts, d => d.x); + + return { xMin, xMax, yMin, yMax }; +}; + +export const scaleCreator = (domA: number, domB: number, rangeA: number, rangeB: number) => { + return d3.scaleLinear().domain([domA, domB]).range([rangeA, rangeB]); +}; + +export const createLineGenerator = (xScale: d3.ScaleLinear, yScale: d3.ScaleLinear) => { + // TODO: nda - look into the different types of curves + return d3 + .line() + .x(d => xScale(d.x)) + .y(d => yScale(d.y)) + .curve(d3.curveMonotoneX); +}; + +export const xAxisCreator = (g: d3.Selection, height: number, xScale: d3.ScaleLinear) => { + g.attr('class', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickSize(15)); +}; + +export const yAxisCreator = (g: d3.Selection, width: number, yScale: d3.ScaleLinear) => { + g.attr('class', 'y-axis').call(d3.axisLeft(yScale)); +}; + +export const xGrid = (g: d3.Selection, height: number, scale: d3.ScaleLinear) => { + g.attr('class', 'grid') + .attr('transform', `translate(0,${height})`) + .call( + d3 + .axisBottom(scale) + .tickSize(-height) + .tickFormat((a, b) => '') + ); +}; + +export const yGrid = (g: d3.Selection, width: number, scale: d3.ScaleLinear) => { + g.attr('class', 'grid').call( + d3 + .axisLeft(scale) + .tickSize(-width) + .tickFormat((a, b) => '') + ); +}; + +export const drawLine = (p: d3.Selection, dataPts: DataPoint[], lineGen: d3.Line) => { + p.datum(dataPts).attr('fill', 'none').attr('stroke', 'rgba(53, 162, 235, 0.5)').attr('stroke-width', 2).attr('class', 'line').attr('d', lineGen); +}; -- cgit v1.2.3-70-g09d2 From 0ae9be249f78ce87301cb833ca7997f5d23ae19c Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Thu, 14 Jul 2022 17:11:18 -0400 Subject: got basic numerical line chart working --- src/client/views/nodes/DataVizBox/LineChart.tsx | 14 ++++++++------ src/client/views/nodes/DataVizBox/utils/D3Utils.ts | 15 +++++++++++---- 2 files changed, 19 insertions(+), 10 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/utils') diff --git a/src/client/views/nodes/DataVizBox/LineChart.tsx b/src/client/views/nodes/DataVizBox/LineChart.tsx index 8a757866e..42e9da3d7 100644 --- a/src/client/views/nodes/DataVizBox/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/LineChart.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { ChartData, DataPoint } from './ChartBox'; // import d3 import * as d3 from 'd3'; -import { minMaxRange, createLineGenerator, xGrid, yGrid, drawLine, scaleCreator } from './utils/D3Utils'; +import { minMaxRange, createLineGenerator, xGrid, yGrid, drawLine, xAxisCreator, yAxisCreator, scaleCreatorNumerical, scaleCreatorCategorical } from './utils/D3Utils'; interface LineChartProps { data: ChartData; @@ -45,8 +45,8 @@ export class LineChart extends React.Component { drawChart() { // clearing tooltip - d3.select('#chart-container').select('svg').remove(); - d3.select('#chart-container').select('.tooltip').remove(); + d3.select(this._chartRef.current).select('svg').remove(); + d3.select(this._chartRef.current).select('.tooltip').remove(); const margin = { top: 50, right: 50, bottom: 50, left: 50 }; const { data } = this.props; @@ -63,9 +63,9 @@ export class LineChart extends React.Component { // TODO: nda - error handle return; } + const xScale = scaleCreatorNumerical(xMin, xMax, 0, this.props.width); // adding x axis - const xScale = scaleCreator(xMin, xMax, 0, this.props.width); - const yScale = scaleCreator(0, yMax, this.props.height, 0); + const yScale = scaleCreatorNumerical(0, yMax, this.props.height, 0); // create a line function that takes in the data.data.x and data.data.y // TODO: nda - fix the types for the d here @@ -74,6 +74,8 @@ export class LineChart extends React.Component { // create x and y grids xGrid(svg.append('g'), this.props.height, xScale); yGrid(svg.append('g'), this.props.width, yScale); + xAxisCreator(svg.append('g'), this.props.height, xScale); + yAxisCreator(svg.append('g'), this.props.width, yScale); // draw the line drawLine(svg.append('path'), data.data, lineGen); @@ -94,7 +96,7 @@ export class LineChart extends React.Component { const focus = svg.append('g').attr('class', 'focus').style('display', 'none'); focus.append('circle').attr('r', 5).attr('class', 'circle'); const tooltip = d3 - .select('#chart-container') + .select(this._chartRef.current) .append('div') .attr('class', 'tooltip') .style('opacity', 0) diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts index 285298472..90ec35f5c 100644 --- a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts +++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts @@ -1,4 +1,4 @@ -import d3 from 'd3'; +import * as d3 from 'd3'; import { DataPoint } from '../ChartBox'; // TODO: nda - implement function that can handle range for strings @@ -13,7 +13,13 @@ export const minMaxRange = (dataPts: DataPoint[]) => { return { xMin, xMax, yMin, yMax }; }; -export const scaleCreator = (domA: number, domB: number, rangeA: number, rangeB: number) => { +export const scaleCreatorCategorical = (labels: string[], range: number[]) => { + const scale = d3.scaleBand().domain(labels).range(range); + + return scale; +}; + +export const scaleCreatorNumerical = (domA: number, domB: number, rangeA: number, rangeB: number) => { return d3.scaleLinear().domain([domA, domB]).range([rangeA, rangeB]); }; @@ -26,11 +32,12 @@ export const createLineGenerator = (xScale: d3.ScaleLinear, height: number, xScale: d3.ScaleLinear) => { +export const xAxisCreator = (g: d3.Selection, height: number, xScale: d3.ScaleLinear) => { + console.log('x axis creator being called'); g.attr('class', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickSize(15)); }; -export const yAxisCreator = (g: d3.Selection, width: number, yScale: d3.ScaleLinear) => { +export const yAxisCreator = (g: d3.Selection, width: number, yScale: d3.ScaleLinear) => { g.attr('class', 'y-axis').call(d3.axisLeft(yScale)); }; -- cgit v1.2.3-70-g09d2 From 3bc49b458c6f7275b5444553ce7e22306fec3ab7 Mon Sep 17 00:00:00 2001 From: Naafiyan Ahmed Date: Mon, 25 Jul 2022 15:11:29 -0400 Subject: fixed graph cut off bug - i hate javascript types --- src/client/views/nodes/DataVizBox/ChartBox.tsx | 59 +++++----- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 67 ++++++++++- src/client/views/nodes/DataVizBox/LineChart.tsx | 128 ++++++++++++++++++--- src/client/views/nodes/DataVizBox/utils/D3Utils.ts | 15 +-- 4 files changed, 209 insertions(+), 60 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/utils') diff --git a/src/client/views/nodes/DataVizBox/ChartBox.tsx b/src/client/views/nodes/DataVizBox/ChartBox.tsx index a03aaae31..92ad76e61 100644 --- a/src/client/views/nodes/DataVizBox/ChartBox.tsx +++ b/src/client/views/nodes/DataVizBox/ChartBox.tsx @@ -1,18 +1,18 @@ 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 { Doc, HeightSym } from '../../../../fields/Doc'; 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 { Cast, NumCast, StrCast } from '../../../../fields/Types'; import { CategoricalChartState } from 'recharts/types/chart/generateCategoricalChart'; import { LineChart } from './LineChart'; export interface ChartBoxProps { rootDoc: Doc; + dataDoc: Doc; pairs: { x: number; y: number }[]; + setChartBox: (chartBox: ChartBox) => void; + getAnchor: () => Doc; } const primaryColor = 'rgba(53, 162, 235, 0.5)'; @@ -38,6 +38,7 @@ export interface ChartData { @observer export class ChartBox extends React.Component { @observable private _chartData: ChartData | undefined = undefined; + @observable private currChart: LineChart | undefined; @computed get currView() { if (this.props.rootDoc._dataVizView) { @@ -54,6 +55,10 @@ export class ChartBox extends React.Component { } } + setCurrChart(chart: LineChart | undefined) { + // this.currChart = chart; + } + @action generateChartData() { if (this.props.rootDoc._chartData) { @@ -107,6 +112,11 @@ export class ChartBox extends React.Component { componentDidMount() { // this.generateChartJsData(); this.generateChartData(); + // setting timeout to allow rerender cycle of the actual chart tof inish + setTimeout(() => { + this.props.setChartBox(this); + }); + // this.props.setChartBox(this); } @action @@ -117,32 +127,6 @@ export class ChartBox extends React.Component { this.props.rootDoc._currChartView = e.currentTarget.value.toLowerCase(); }; - // @action - // onClick = (e: React.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); @@ -158,10 +142,16 @@ export class ChartBox extends React.Component { // } }; + scrollFocus(doc: Doc, smooth: boolean) {} + handleDotClick = (e: any) => { console.log(e); }; + _getAnchor() { + return this.currChart?._getAnchor(); + } + // handleTextClick = (e: any) => { // console.log(e); // } @@ -195,6 +185,11 @@ export class ChartBox extends React.Component { render() { if (this.props.pairs && this._chartData) { + let width = NumCast(this.props.rootDoc._width); + width = width * 0.7; + let height = NumCast(this.props.rootDoc._height); + height = height * 0.7; + console.log(width, height); return (
@@ -204,7 +199,7 @@ export class ChartBox extends React.Component { this.onClick(e)} /> )} */} {/* {this.reLineChart} */} - +