aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/DataVizBox/ChartBox.tsx101
-rw-r--r--src/client/views/nodes/DataVizBox/ChartInterface.ts33
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx2
-rw-r--r--src/client/views/nodes/DataVizBox/DrawHelper.ts247
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramBox.scss18
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramBox.tsx162
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.scss0
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.tsx16
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.scss0
-rw-r--r--src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.tsx13
-rw-r--r--src/client/views/nodes/DataVizBox/components/Chart.scss10
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx (renamed from src/client/views/nodes/DataVizBox/LineChart.tsx)151
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.scss (renamed from src/client/views/nodes/DataVizBox/TableBox.scss)0
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx (renamed from src/client/views/nodes/DataVizBox/TableBox.tsx)0
-rw-r--r--src/client/views/nodes/DataVizBox/utils/D3Utils.ts12
15 files changed, 133 insertions, 632 deletions
diff --git a/src/client/views/nodes/DataVizBox/ChartBox.tsx b/src/client/views/nodes/DataVizBox/ChartBox.tsx
index f2450bc7c..a2e4fd73c 100644
--- a/src/client/views/nodes/DataVizBox/ChartBox.tsx
+++ b/src/client/views/nodes/DataVizBox/ChartBox.tsx
@@ -1,11 +1,10 @@
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, HeightSym } from '../../../../fields/Doc';
-import { ChartJSOrUndefined } from 'react-chartjs-2/dist/types';
import { action, computed, observable } from 'mobx';
import { Cast, NumCast, StrCast } from '../../../../fields/Types';
-import { CategoricalChartState } from 'recharts/types/chart/generateCategoricalChart';
-import { LineChart } from './LineChart';
+import { LineChart } from './components/LineChart';
+import { Chart, ChartData } from './ChartInterface';
export interface ChartBoxProps {
rootDoc: Doc;
@@ -15,26 +14,11 @@ export interface ChartBoxProps {
getAnchor: () => Doc;
}
-const primaryColor = 'rgba(53, 162, 235, 0.5)';
-const selectedColor = 'rgba(255, 99, 132, 0.5)';
-
-export interface RechartData {
- name: string | number;
- y: number;
-}
-
export interface DataPoint {
x: number;
y: number;
}
-export interface ChartData {
- xLabel: string;
- yLabel: string;
- data: DataPoint[];
- tooltipContent: (data: DataPoint) => string;
-}
-
@observer
export class ChartBox extends React.Component<ChartBoxProps> {
@observable private _chartData: ChartData | undefined = undefined;
@@ -55,7 +39,7 @@ export class ChartBox extends React.Component<ChartBoxProps> {
}
}
- setCurrChart(chart: LineChart | undefined) {
+ setCurrChart(chart: Chart | undefined) {
// this.currChart = chart;
}
@@ -69,17 +53,15 @@ export class ChartBox extends React.Component<ChartBoxProps> {
const data: ChartData = {
xLabel: '',
yLabel: '',
- data: [],
- tooltipContent: (data: DataPoint) => {
- return `<b>x: ${data.x} y: ${data.y}</b>`;
- },
+ data: [[]],
};
if (this.props.pairs && this.props.pairs.length > 0) {
data.xLabel = 'x';
data.yLabel = 'y';
this.props.pairs.forEach(pair => {
- data.data.push({ x: pair.x, y: pair.y });
+ // TODO: nda - add actual support for multiple sets of data
+ data.data[0].push({ x: pair.x, y: pair.y });
});
}
@@ -87,30 +69,7 @@ export class ChartBox extends React.Component<ChartBoxProps> {
this.props.rootDoc._chartData = JSON.stringify(data);
}
- // @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.generateChartData();
// setting timeout to allow rerender cycle of the actual chart tof inish
setTimeout(() => {
@@ -127,21 +86,6 @@ export class ChartBox extends React.Component<ChartBoxProps> {
this.props.rootDoc._currChartView = e.currentTarget.value.toLowerCase();
};
- 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;
- // }
- };
-
scrollFocus(doc: Doc, smooth: boolean) {}
handleDotClick = (e: any) => {
@@ -152,37 +96,6 @@ export class ChartBox extends React.Component<ChartBoxProps> {
return this.currChart?._getAnchor();
}
- // 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._chartData) {
let width = NumCast(this.props.rootDoc._width);
@@ -200,7 +113,7 @@ export class ChartBox extends React.Component<ChartBoxProps> {
<Bar ref={this._chartRef} options={this.options} data={this._chartJsData} onClick={e => this.onClick(e)} />
)} */}
{/* {this.reLineChart} */}
- <LineChart margin={margin} width={width} height={height} data={this._chartData} setCurrChart={this.setCurrChart} dataDoc={this.props.dataDoc} fieldKey={'data'} getAnchor={this.props.getAnchor} />
+ <LineChart margin={margin} width={width} height={height} chartData={this._chartData} setCurrChart={this.setCurrChart} dataDoc={this.props.dataDoc} fieldKey={'data'} getAnchor={this.props.getAnchor} />
</div>
<button onClick={e => this.onClickChangeChart(e)} value="line">
Line
diff --git a/src/client/views/nodes/DataVizBox/ChartInterface.ts b/src/client/views/nodes/DataVizBox/ChartInterface.ts
new file mode 100644
index 000000000..6e37f966c
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/ChartInterface.ts
@@ -0,0 +1,33 @@
+import { Doc } from '../../../../fields/Doc';
+import { DataPoint } from './ChartBox';
+import { LineChart } from './components/LineChart';
+
+export interface Chart {
+ tooltipContent: (data: DataPoint) => string;
+ drawChart: () => void;
+ height: number;
+ width: number;
+}
+
+export interface ChartProps {
+ chartData: ChartData;
+ width: number;
+ height: number;
+ dataDoc: Doc;
+ fieldKey: string;
+ // returns linechart component but should be generic chart
+ setCurrChart: (chart: Chart) => void;
+ getAnchor: () => Doc;
+ margin: {
+ top: number;
+ right: number;
+ bottom: number;
+ left: number;
+ };
+}
+
+export interface ChartData {
+ xLabel: string;
+ yLabel: string;
+ data: DataPoint[][];
+}
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 4883385bb..1d9b309ce 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -9,7 +9,7 @@ import { ViewBoxAnnotatableComponent, ViewBoxBaseComponent } from '../../DocComp
import { FieldViewProps, FieldView } from '../FieldView';
import { ChartBox } from './ChartBox';
import './DataVizBox.scss';
-import { TableBox } from './TableBox';
+import { TableBox } from './components/TableBox';
enum DataVizView {
TABLE = 'table',
diff --git a/src/client/views/nodes/DataVizBox/DrawHelper.ts b/src/client/views/nodes/DataVizBox/DrawHelper.ts
deleted file mode 100644
index 595cecebf..000000000
--- a/src/client/views/nodes/DataVizBox/DrawHelper.ts
+++ /dev/null
@@ -1,247 +0,0 @@
-export class PIXIPoint {
- public get x() { return this.coords[0]; }
- public get y() { return this.coords[1]; }
- public set x(value: number) { this.coords[0] = value; }
- public set y(value: number) { this.coords[1] = value; }
- public coords: number[] = [0, 0];
- constructor(x: number, y: number) {
- this.coords[0] = x;
- this.coords[1] = y;
- }
-}
-
-export class PIXIRectangle {
- public x: number;
- public y: number;
- public width: number;
- public height: number;
- public get left() { return this.x; }
- public get right() { return this.x + this.width; }
- public get top() { return this.y; }
- public get bottom() { return this.top + this.height; }
- public static get EMPTY() { return new PIXIRectangle(0, 0, -1, -1); }
- constructor(x: number, y: number, width: number, height: number) {
- this.x = x;
- this.y = y;
- this.width = width;
- this.height = height;
- }
-}
-
-export class MathUtil {
-
- public static EPSILON: number = 0.001;
-
- public static Sign(value: number): number {
- return value >= 0 ? 1 : -1;
- }
-
- public static AddPoint(p1: PIXIPoint, p2: PIXIPoint, inline: boolean = false): PIXIPoint {
- if (inline) {
- p1.x += p2.x;
- p1.y += p2.y;
- return p1;
- }
- else {
- return new PIXIPoint(p1.x + p2.x, p1.y + p2.y);
- }
- }
-
- public static Perp(p1: PIXIPoint): PIXIPoint {
- return new PIXIPoint(-p1.y, p1.x);
- }
-
- public static DividePoint(p1: PIXIPoint, by: number, inline: boolean = false): PIXIPoint {
- if (inline) {
- p1.x /= by;
- p1.y /= by;
- return p1;
- }
- else {
- return new PIXIPoint(p1.x / by, p1.y / by);
- }
- }
-
- public static MultiplyConstant(p1: PIXIPoint, by: number, inline: boolean = false) {
- if (inline) {
- p1.x *= by;
- p1.y *= by;
- return p1;
- }
- else {
- return new PIXIPoint(p1.x * by, p1.y * by);
- }
- }
-
- public static SubtractPoint(p1: PIXIPoint, p2: PIXIPoint, inline: boolean = false): PIXIPoint {
- if (inline) {
- p1.x -= p2.x;
- p1.y -= p2.y;
- return p1;
- }
- else {
- return new PIXIPoint(p1.x - p2.x, p1.y - p2.y);
- }
- }
-
- public static Area(rect: PIXIRectangle): number {
- return rect.width * rect.height;
- }
-
- public static DistToLineSegment(v: PIXIPoint, w: PIXIPoint, p: PIXIPoint) {
- // Return minimum distance between line segment vw and point p
- var l2 = MathUtil.DistSquared(v, w); // i.e. |w-v|^2 - avoid a sqrt
- if (l2 === 0.0) return MathUtil.Dist(p, v); // v === w case
- // Consider the line extending the segment, parameterized as v + t (w - v).
- // We find projection of point p onto the line.
- // It falls where t = [(p-v) . (w-v)] / |w-v|^2
- // We clamp t from [0,1] to handle points outside the segment vw.
- var dot = MathUtil.Dot(
- MathUtil.SubtractPoint(p, v),
- MathUtil.SubtractPoint(w, v)) / l2;
- var t = Math.max(0, Math.min(1, dot));
- // Projection falls on the segment
- var projection = MathUtil.AddPoint(v,
- MathUtil.MultiplyConstant(
- MathUtil.SubtractPoint(w, v), t));
- return MathUtil.Dist(p, projection);
- }
-
- public static LineSegmentIntersection(ps1: PIXIPoint, pe1: PIXIPoint, ps2: PIXIPoint, pe2: PIXIPoint): PIXIPoint | undefined {
- var a1 = pe1.y - ps1.y;
- var b1 = ps1.x - pe1.x;
-
- var a2 = pe2.y - ps2.y;
- var b2 = ps2.x - pe2.x;
-
- var delta = a1 * b2 - a2 * b1;
- if (delta === 0) {
- return undefined;
- }
- var c2 = a2 * ps2.x + b2 * ps2.y;
- var c1 = a1 * ps1.x + b1 * ps1.y;
- var invdelta = 1 / delta;
- return new PIXIPoint((b2 * c1 - b1 * c2) * invdelta, (a1 * c2 - a2 * c1) * invdelta);
- }
-
- public static PointInPIXIRectangle(p: PIXIPoint, rect: PIXIRectangle): boolean {
- if (p.x < rect.left - this.EPSILON) {
- return false;
- }
- if (p.x > rect.right + this.EPSILON) {
- return false;
- }
- if (p.y < rect.top - this.EPSILON) {
- return false;
- }
- if (p.y > rect.bottom + this.EPSILON) {
- return false;
- }
-
- return true;
- }
-
- public static LinePIXIRectangleIntersection(lineFrom: PIXIPoint, lineTo: PIXIPoint, rect: PIXIRectangle): Array<PIXIPoint> {
- var r1 = new PIXIPoint(rect.left, rect.top);
- var r2 = new PIXIPoint(rect.right, rect.top);
- var r3 = new PIXIPoint(rect.right, rect.bottom);
- var r4 = new PIXIPoint(rect.left, rect.bottom);
- var ret = new Array<PIXIPoint>();
- var dist = this.Dist(lineFrom, lineTo);
- var inter = this.LineSegmentIntersection(lineFrom, lineTo, r1, r2);
- if (inter && this.PointInPIXIRectangle(inter, rect) &&
- this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
- ret.push(inter);
- }
- inter = this.LineSegmentIntersection(lineFrom, lineTo, r2, r3);
- if (inter && this.PointInPIXIRectangle(inter, rect) &&
- this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
- ret.push(inter);
- }
- inter = this.LineSegmentIntersection(lineFrom, lineTo, r3, r4);
- if (inter && this.PointInPIXIRectangle(inter, rect) &&
- this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
- ret.push(inter);
- }
- inter = this.LineSegmentIntersection(lineFrom, lineTo, r4, r1);
- if (inter && this.PointInPIXIRectangle(inter, rect) &&
- this.Dist(inter, lineFrom) < dist && this.Dist(inter, lineTo) < dist) {
- ret.push(inter);
- }
- return ret;
- }
-
- public static Intersection(rect1: PIXIRectangle, rect2: PIXIRectangle): PIXIRectangle {
- const left = Math.max(rect1.x, rect2.x);
- const right = Math.min(rect1.x + rect1.width, rect2.x + rect2.width);
- const top = Math.max(rect1.y, rect2.y);
- const bottom = Math.min(rect1.y + rect1.height, rect2.y + rect2.height);
- return new PIXIRectangle(left, top, right - left, bottom - top);
- }
-
- public static Dist(p1: PIXIPoint, p2: PIXIPoint): number {
- return Math.sqrt(MathUtil.DistSquared(p1, p2));
- }
-
- public static Dot(p1: PIXIPoint, p2: PIXIPoint): number {
- return p1.x * p2.x + p1.y * p2.y;
- }
-
- public static Normalize(p1: PIXIPoint) {
- var d = this.Length(p1);
- return new PIXIPoint(p1.x / d, p1.y / d);
- }
-
- public static Length(p1: PIXIPoint): number {
- return Math.sqrt(p1.x * p1.x + p1.y * p1.y);
- }
-
- public static DistSquared(p1: PIXIPoint, p2: PIXIPoint): number {
- const a = p1.x - p2.x;
- const b = p1.y - p2.y;
- return (a * a + b * b);
- }
-
- public static RectIntersectsRect(r1: PIXIRectangle, r2: PIXIRectangle): boolean {
- return !(r2.x > r1.x + r1.width ||
- r2.x + r2.width < r1.x ||
- r2.y > r1.y + r1.height ||
- r2.y + r2.height < r1.y);
- }
-
- public static ArgMin(temp: number[]): number {
- let index = 0;
- let value = temp[0];
- for (let i = 1; i < temp.length; i++) {
- if (temp[i] < value) {
- value = temp[i];
- index = i;
- }
- }
- return index;
- }
-
- public static ArgMax(temp: number[]): number {
- let index = 0;
- let value = temp[0];
- for (let i = 1; i < temp.length; i++) {
- if (temp[i] > value) {
- value = temp[i];
- index = i;
- }
- }
- return index;
- }
-
- public static Combinations<T>(chars: T[]) {
- let result = new Array<T>();
- let f = (prefix: any, chars: any) => {
- for (let i = 0; i < chars.length; i++) {
- result.push(prefix.concat(chars[i]));
- f(prefix.concat(chars[i]), chars.slice(i + 1));
- }
- };
- f([], chars);
- return result;
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/HistogramBox.scss b/src/client/views/nodes/DataVizBox/HistogramBox.scss
deleted file mode 100644
index 5aac9dc77..000000000
--- a/src/client/views/nodes/DataVizBox/HistogramBox.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-// change the stroke color of line-svg class
-.svgLine {
- position: absolute;
- background: darkGray;
- stroke: #000;
- stroke-width: 1px;
- width:100%;
- height:100%;
- opacity: 0.4;
-}
-
-.svgContainer {
- position: absolute;
- top:0;
- left:0;
- width:100%;
- height: 100%;
-} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/HistogramBox.tsx b/src/client/views/nodes/DataVizBox/HistogramBox.tsx
deleted file mode 100644
index 4488323fe..000000000
--- a/src/client/views/nodes/DataVizBox/HistogramBox.tsx
+++ /dev/null
@@ -1,162 +0,0 @@
-import { action, computed, observable } from "mobx";
-import { observer } from "mobx-react";
-import * as React from "react";
-import { Doc } from "../../../../fields/Doc";
-import { NumCast } from "../../../../fields/Types";
-import { DragManager } from "../../../util/DragManager";
-import "./HistogramBox.scss";
-
-interface HistogramBoxProps {
- rootDoc: Doc;
- pairs: {
- x: number,
- y: number
- }[]
-}
-
-
-export class HistogramBox extends React.Component<HistogramBoxProps> {
- private _dropXDispoer?: DragManager.DragDropDisposer;
- private _dropYDispoer?: DragManager.DragDropDisposer;
-
- private origin = {x: 0.1 * this.width, y: 0.9 * this.height};
-
- @computed get width() {
- return NumCast(this.props.rootDoc.width);
- }
-
- @computed get height() {
- return NumCast(this.props.rootDoc.height);
- }
-
- @computed get x() {
- return NumCast(this.props.rootDoc.x);
- }
-
- @computed get y() {
- return NumCast(this.props.rootDoc.y);
- }
-
- @computed get generatePoints() {
- // evenly distribute points along the x axis
- const xVals: number[] = this.props.pairs.map(p => p.x);
- const yVals: number[] = this.props.pairs.map(p => p.y);
-
- const xMin = Math.min(...xVals);
- const xMax = Math.max(...xVals);
- const yMin = Math.min(...yVals);
- const yMax = Math.max(...yVals);
-
- const xRange = xMax - xMin;
- const yRange = yMax - yMin;
-
- const xScale = this.width / xRange;
- const yScale = this.height / yRange;
-
- const xOffset = (this.x + (0.1 * this.width)) - xMin * xScale;
- const yOffset = (this.y + (0.25 * this.height)) - yMin * yScale;
-
- const points: {
- x: number,
- y: number
- }[] = this.props.pairs.map(p => {
- return {
- x: (p.x * xScale + xOffset) + this.origin.x,
- y: (p.y * yScale + yOffset)
- }
- });
-
- return points;
- }
-
- @computed get generateGraphLine() {
- const points = this.generatePoints;
- // loop through points and create a line from each point to the next
- let lines: {
- x1: number,
- y1: number,
- x2: number,
- y2: number
- }[] = [];
- for (let i = 0; i < points.length - 1; i++) {
- lines.push({
- x1: points[i].x,
- y1: points[i].y,
- x2: points[i + 1].x,
- y2: points[i + 1].y
- });
- }
- // generate array of svg with lines
- let svgLines: JSX.Element[] = [];
- for (let i = 0; i < lines.length; i++) {
- svgLines.push(
- <line
- className="svgLine"
- key={i}
- x1={lines[i].x1}
- y1={lines[i].y1}
- x2={lines[i].x2}
- y2={lines[i].y2}
- stroke="black"
- strokeWidth={2}
- />
- );
- }
-
- let res = [];
- for (let i = 0; i < svgLines.length; i++) {
- res.push(<svg className="svgContainer">{svgLines[i]}</svg>)
- }
- return res;
- }
-
- @computed get generateAxes() {
-
- const xAxis = {
- x1: 0.1 * this.width,
- x2: 0.9 * this.width,
- y1: 0.9 * this.height,
- y2: 0.9 * this.height,
- };
-
- const yAxis = {
- x1: 0.1 * this.width,
- x2: 0.1 * this.width,
- y1: 0.25 * this.height,
- y2: 0.9 * this.height,
- };
-
-
- return (
- [
- (<svg className="svgContainer">
- {/* <line className="svgLine" x1={yAxis} y1={xAxis} x2={this.width - (0.1 * this.width)} y2={xAxis} /> */}
- <line className="svgLine" x1={xAxis.x1} y1={xAxis.y1} x2={xAxis.x2} y2={xAxis.y2}/>
-
- {/* <line className="svgLine" x1={yAxis} y1={xAxis} x2={yAxis} y2={this.y + 50} /> */}
- </svg>),
- (
- <svg className="svgContainer">
- <line className="svgLine" x1={yAxis.x1} y1={yAxis.y1} x2={yAxis.x2} y2={yAxis.y2} />
- {/* <line className="svgLine" x1={yAxis} y1={xAxis} x2={yAxis} y2={this.y + 50} /> */}
- </svg>)
- ]
- )
- }
-
-
- render() {
- return (
- <div>histogram box
- {/* <svg className="svgContainer">
- {this.generateSVGLine}
- </svg> */}
- {this.generateAxes[0]}
- {this.generateAxes[1]}
- {this.generateGraphLine.map(line => line)}
- </div>
- )
-
- }
-
-} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.scss b/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.scss
deleted file mode 100644
index e69de29bb..000000000
--- a/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.scss
+++ /dev/null
diff --git a/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.tsx b/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.tsx
deleted file mode 100644
index ac1b7699b..000000000
--- a/src/client/views/nodes/DataVizBox/HistogramBoxPrimitives.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { observer } from "mobx-react";
-import * as React from "react";
-import { HistogramBox } from "./HistogramBox";
-
-export interface HistogramPrimitivesProps {
- HistoBox: HistogramBox;
-}
-
-@observer
-export class HistogramBoxPrimitives extends React.Component<HistogramPrimitivesProps> {
- render() {
- return <div className="histogramboxprimitives-container">
-
- </div>
- }
-} \ No newline at end of file
diff --git a/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.scss b/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.scss
deleted file mode 100644
index e69de29bb..000000000
--- a/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.scss
+++ /dev/null
diff --git a/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.tsx b/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.tsx
deleted file mode 100644
index d3440dea1..000000000
--- a/src/client/views/nodes/DataVizBox/HistogramLabelPrimitives.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { observer } from "mobx-react";
-import * as React from "react";
-import { HistogramPrimitivesProps } from "./HistogramBoxPrimitives";
-import "./HistogramLabelPrimitives.scss";
-
-@observer
-export class HistogramLabelPrimitives extends React.Component<HistogramPrimitivesProps> {
- render() {
- return <div className="histogramLabelPrimitives-container">
-
- </div>
- }
-}
diff --git a/src/client/views/nodes/DataVizBox/components/Chart.scss b/src/client/views/nodes/DataVizBox/components/Chart.scss
new file mode 100644
index 000000000..7792a2758
--- /dev/null
+++ b/src/client/views/nodes/DataVizBox/components/Chart.scss
@@ -0,0 +1,10 @@
+.tooltip {
+ // make the height width bigger
+ width: 50px;
+ height: 50px;
+}
+
+.highlight {
+ // change the color of the circle element to be red
+ fill: red;
+}
diff --git a/src/client/views/nodes/DataVizBox/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 0aeaacf34..88598be3d 100644
--- a/src/client/views/nodes/DataVizBox/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -1,29 +1,14 @@
import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { ChartData, DataPoint } from './ChartBox';
+import { DataPoint } from '../ChartBox';
// import d3
import * as d3 from 'd3';
-import { minMaxRange, createLineGenerator, xGrid, yGrid, drawLine, xAxisCreator, yAxisCreator, scaleCreatorNumerical, scaleCreatorCategorical } from './utils/D3Utils';
-import { Docs } from '../../../documents/Documents';
-import { Doc, DocListCast } from '../../../../fields/Doc';
-
-interface LineChartProps {
- data: ChartData;
- width: number;
- height: number;
- dataDoc: Doc;
- fieldKey: string;
- // returns linechart component but should be generic chart
- setCurrChart: (chart: LineChart | undefined) => void;
- getAnchor: () => Doc;
- margin: {
- top: number;
- right: number;
- bottom: number;
- left: number;
- };
-}
+import { minMaxRange, createLineGenerator, xGrid, yGrid, drawLine, xAxisCreator, yAxisCreator, scaleCreatorNumerical, scaleCreatorCategorical } from '../utils/D3Utils';
+import { Docs } from '../../../../documents/Documents';
+import { Doc, DocListCast } from '../../../../../fields/Doc';
+import { Chart, ChartProps } from '../ChartInterface';
+import './Chart.scss';
type minMaxRange = {
xMin: number | undefined;
@@ -39,7 +24,7 @@ interface SelectedDataPoint {
}
@observer
-export class LineChart extends React.Component<LineChartProps> {
+export class LineChart extends React.Component<ChartProps> implements Chart {
private _dataReactionDisposer: IReactionDisposer | undefined = undefined;
private _heightReactionDisposer: IReactionDisposer | undefined = undefined;
private _widthReactionDisposer: IReactionDisposer | undefined;
@@ -55,17 +40,17 @@ export class LineChart extends React.Component<LineChartProps> {
yMax: undefined,
};
+ private _chartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
+
// anything that doesn't need to be recalculated should just be stored as drawCharts (i.e. computed values) and drawChart is gonna iterate over these observables and generate svgs based on that
// write the getanchor function that gets whatever I want as the link anchor
componentDidMount() {
- // this._rangeVals = minMaxRange(this.props.data.data);
- // this.drawChart();
this._dataReactionDisposer = reaction(
- () => this.props.data,
- data => {
- this._rangeVals = minMaxRange(data.data);
+ () => this.props.chartData,
+ chartData => {
+ this._rangeVals = minMaxRange(chartData.data);
this.drawChart();
},
{ fireImmediately: true }
@@ -77,6 +62,7 @@ export class LineChart extends React.Component<LineChartProps> {
annotations => {
// modify how d3 renders so that anything in this annotations list would be potentially highlighted in some way
// could be blue colored to make it look like anchor
+ this.drawAnnotations(this.props.chartData.data[0][1].x, this.props.chartData.data[0][1].y);
},
{ fireImmediately: true }
);
@@ -87,7 +73,23 @@ export class LineChart extends React.Component<LineChartProps> {
}
// gets called whenever the "data-annotations" fields gets updated
- drawAnnotations() {}
+ drawAnnotations(dataX: number, dataY: number) {
+ // TODO: nda - can optimize this by having some sort of mapping of the x and y values to the individual circle elements
+ // loop through all html elements with class .circle-d1 and find the one that has "data-x" and "data-y" attributes that match the dataX and dataY
+ // if it exists, then highlight it
+ // if it doesn't exist, then remove the highlight
+ const elements = document.querySelectorAll('datapoint');
+ for (let i = 0; i < elements.length; i++) {
+ const element = elements[i];
+ const x = element.getAttribute('data-x');
+ const y = element.getAttribute('data-y');
+ if (x === dataX.toString() && y === dataY.toString()) {
+ element.classList.add('highlight');
+ } else {
+ element.classList.remove('highlight');
+ }
+ }
+ }
_getAnchor() {
// store whatever information would allow me to reselect the same thing (store parameters I would pass to get the exact same element)
@@ -128,22 +130,41 @@ export class LineChart extends React.Component<LineChartProps> {
return this.props.width - this.props.margin.left - this.props.margin.right;
}
- @computed get svgContainer() {
- const { margin } = this.props;
- const svg = d3
- .create('svg')
- .attr('width', `${this.width + margin.right + margin.left}`)
- .attr('height', `${this.height + margin.top + margin.bottom}`)
- .append('g')
- .attr('transform', `translate(${margin.left}, ${margin.top})`);
- return svg;
+ setupTooltip() {
+ const tooltip = d3
+ .select(this._chartRef.current)
+ .append('div')
+ .attr('class', 'tooltip')
+ .style('opacity', 0)
+ .style('background', '#fff')
+ .style('border', '1px solid #ccc')
+ .style('padding', '5px')
+ .style('position', 'absolute')
+ .style('font-size', '12px');
+ return tooltip;
+ }
+
+ drawDataPoints(data: DataPoint[], idx: number, xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>) {
+ if (this._chartSvg) {
+ const circleClass = '.circle-' + idx;
+ this._chartSvg
+ .selectAll(circleClass)
+ .data(data)
+ .join('circle') // enter append
+ .attr('class', `${circleClass} datapoint`)
+ .attr('r', '3') // radius
+ .attr('cx', d => xScale(d.x))
+ .attr('cy', d => yScale(d.y))
+ .attr('data-x', d => d.x)
+ .attr('data-y', d => d.y);
+ }
}
// TODO: nda - can use d3.create() to create html element instead of appending
drawChart() {
- const { data, margin } = this.props;
- console.log(this.height, this.width);
- // clearing tooltip
+ const { chartData, margin } = this.props;
+ const data = chartData.data[0];
+ // clearing tooltip and the current chart
d3.select(this._chartRef.current).select('svg').remove();
d3.select(this._chartRef.current).select('.tooltip').remove();
@@ -152,7 +173,7 @@ export class LineChart extends React.Component<LineChartProps> {
const { xMin, xMax, yMin, yMax } = this._rangeVals;
// const svg = d3.select(this._chartRef.current).append(this.svgContainer.html());
// adding svg
- const svg = d3
+ this._chartSvg = d3
.select(this._chartRef.current)
.append('svg')
.attr('width', `${this.width + margin.right + margin.left}`)
@@ -160,6 +181,8 @@ export class LineChart extends React.Component<LineChartProps> {
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`);
+ const svg = this._chartSvg;
+
if (xMin == undefined || xMax == undefined || yMin == undefined || yMax == undefined) {
// TODO: nda - error handle
return;
@@ -180,54 +203,34 @@ export class LineChart extends React.Component<LineChartProps> {
yAxisCreator(svg.append('g'), this.width, yScale);
// draw the line
- drawLine(svg.append('path'), data.data, lineGen);
+ drawLine(svg.append('path'), data, lineGen);
- // draw the datapoint circles
- svg.selectAll('.circle-d1')
- .data(data.data)
- .join('circle') // enter append
- .attr('class', 'circle-d1')
- .attr('r', '3') // radius
- .attr('cx', d => xScale(d.x))
- .attr('cy', d => yScale(d.y))
- .attr('data-x', d => d.x)
- .attr('data-y', d => d.y);
+ // draw the datapoint circle
+ this.drawDataPoints(data, 0, xScale, yScale);
const focus = svg.append('g').attr('class', 'focus').style('display', 'none');
focus.append('circle').attr('r', 5).attr('class', 'circle');
- const tooltip = d3
- .select(this._chartRef.current)
- .append('div')
- .attr('class', 'tooltip')
- .style('opacity', 0)
- .style('background', '#fff')
- .style('border', '1px solid #ccc')
- .style('padding', '5px')
- .style('position', 'absolute')
- .style('font-size', '12px');
+ const tooltip = this.setupTooltip();
// add all the tooltipContent to the tooltip
- // @action
const mousemove = action((e: any) => {
const bisect = d3.bisector((d: DataPoint) => d.x).left;
const xPos = d3.pointer(e)[0];
- const x0 = bisect(data.data, xScale.invert(xPos));
- const d0 = data.data[x0];
+ const x0 = bisect(data, xScale.invert(xPos));
+ const d0 = data[x0];
this._x = d0.x;
this._y = d0.y;
focus.attr('transform', `translate(${xScale(d0.x)},${yScale(d0.y)})`);
// TODO: nda - implement tooltips
tooltip.transition().duration(300).style('opacity', 0.9);
// TODO: nda - updating the inner html could be deadly cause injection attacks!
- // 0 = 30px => -597px (-this.width - margin.left - margin.right)
- // 1 =
- tooltip.html(() => this.tooltipContent(d0)).style('transform', `translate(${xScale(d0.x) - (this.width + margin.left + margin.right)}px,${yScale(d0.y) + 30}px)`);
+ tooltip.html(() => this.tooltipContent(d0)).style('transform', `translate(${xScale(d0.x) - (this.width + margin.left + margin.right) + 30}px,${yScale(d0.y) + 30}px)`);
});
const onPointClick = action((e: any) => {
const bisect = d3.bisector((d: DataPoint) => d.x).left;
const xPos = d3.pointer(e)[0];
- const x0 = bisect(data.data, xScale.invert(xPos));
- const d0 = data.data[x0];
+ const x0 = bisect(data, xScale.invert(xPos));
+ const d0 = data[x0];
this._x = d0.x;
this._y = d0.y;
// find .circle-d1 with data-x = d0.x and data-y = d0.y
@@ -239,7 +242,8 @@ export class LineChart extends React.Component<LineChartProps> {
console.log(this._currSelected);
});
- svg.append('rect')
+ this._chartSvg
+ .append('rect')
.attr('class', 'overlay')
.attr('width', this.width)
.attr('height', this.height + margin.top + margin.bottom)
@@ -259,10 +263,7 @@ export class LineChart extends React.Component<LineChartProps> {
render() {
return (
<div ref={this._chartRef} className="chart-container">
- <span>
- x: {this._x} y: {this._y}
- Curr Selected: {this._currSelected ? `x: ${this._currSelected.x} y: ${this._currSelected.y}` : 'none'}
- </span>
+ <span>Curr Selected: {this._currSelected ? `x: ${this._currSelected.x} y: ${this._currSelected.y}` : 'none'}</span>
</div>
);
}
diff --git a/src/client/views/nodes/DataVizBox/TableBox.scss b/src/client/views/nodes/DataVizBox/components/TableBox.scss
index 1264d6a46..1264d6a46 100644
--- a/src/client/views/nodes/DataVizBox/TableBox.scss
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.scss
diff --git a/src/client/views/nodes/DataVizBox/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index dfa8262d8..dfa8262d8 100644
--- a/src/client/views/nodes/DataVizBox/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
index 5c7f9bce1..2bb091999 100644
--- a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
+++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
@@ -3,13 +3,13 @@ import { DataPoint } from '../ChartBox';
// TODO: nda - implement function that can handle range for strings
-export const minMaxRange = (dataPts: DataPoint[]) => {
- console.log(Number(dataPts[4].y));
- const yMin = d3.min(dataPts, d => Number(d.y));
- const yMax = d3.max(dataPts, d => Number(d.y));
+export const minMaxRange = (dataPts: DataPoint[][]) => {
+ // find the max and min of all the data points
+ const yMin = d3.min(dataPts, d => d3.min(d, d => Number(d.y)));
+ const yMax = d3.max(dataPts, d => d3.max(d, d => Number(d.y)));
- const xMin = d3.min(dataPts, d => Number(d.x));
- const xMax = d3.max(dataPts, d => Number(d.x));
+ const xMin = d3.min(dataPts, d => d3.min(d, d => Number(d.x)));
+ const xMax = d3.max(dataPts, d => d3.max(d, d => Number(d.x)));
return { xMin, xMax, yMin, yMax };
};