aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DataVizBox/components/LineChart.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/DataVizBox/components/LineChart.tsx')
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx100
1 files changed, 63 insertions, 37 deletions
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 2b2a5326a..206169c13 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -2,7 +2,7 @@ import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'
import { observer } from 'mobx-react';
import * as React from 'react';
import * as d3 from 'd3';
-import { Doc, DocListCast, StrListCast } from '../../../../../fields/Doc';
+import { Doc, DocListCast, NumListCast, StrListCast } from '../../../../../fields/Doc';
import { Id } from '../../../../../fields/FieldSymbols';
import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
@@ -28,7 +28,7 @@ export interface LineChartProps {
rootDoc: Doc;
layoutDoc: Doc;
axes: string[];
- pairs: { [key: string]: any }[];
+ records: { [key: string]: any }[];
width: number;
height: number;
dataDoc: Doc;
@@ -49,32 +49,35 @@ export class LineChart extends React.Component<LineChartProps> {
@observable _currSelected: SelectedDataPoint | undefined = undefined;
// TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates
+ @computed get _tableDataIds() {
+ return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ }
+ // returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
+ @computed get _tableData() {
+ return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ }
@computed get _lineChartData() {
- var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
+ var guids = StrListCast(this.props.layoutDoc.dataViz_rowIds);
if (this.props.axes.length <= 1) return [];
- return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)])))
- .map(pair => ({ x: Number(pair[this.props.axes[0]]), y: Number(pair[this.props.axes[1]]) }))
- .sort((a, b) => (a.x < b.x ? -1 : 1));
+ return this._tableData.map(record => ({ x: Number(record[this.props.axes[0]]), y: Number(record[this.props.axes[1]]) })).sort((a, b) => (a.x < b.x ? -1 : 1));
}
@computed get graphTitle() {
return this.props.axes[1] + ' vs. ' + this.props.axes[0] + ' Line Chart';
}
- @computed get incomingLinks() {
- return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
- .filter(link => {
- return link.link_anchor_1 == this.props.rootDoc.draggedFrom;
- }) // get links where this chart doc is the target of the link
- .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
+ @computed get parentViz() {
+ return DocCast(this.props.rootDoc.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
+ // .filter(link => {
+ // return link.link_anchor_1 == this.props.rootDoc.dataViz_parentViz;
+ // }) // get links where this chart doc is the target of the link
+ // .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
}
- @computed get incomingSelected() {
+ @computed get incomingHighlited() {
// return selected x and y axes
// otherwise, use the selection of whatever is linked to us
- return this.incomingLinks // all links that are pointing to this node
- .map(anchor => DocumentManager.Instance.getFirstDocumentView(anchor)?.ComponentView as DataVizBox) // get their data viz boxes
- .filter(dvb => dvb)
- .map(dvb => dvb.pairs?.filter(pair => pair['select' + dvb.rootDoc[Id]])) // get all the datapoints they have selected field set by incoming anchor
- .lastElement();
+ const incomingVizBox = DocumentManager.Instance.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox;
+ const highlitedRowIds = (incomingVizBox && incomingVizBox.rootDoc)? NumListCast(incomingVizBox.rootDoc.dataViz_highlitedRows) : [];
+ return this._tableData.filter((record, i) => highlitedRowIds.includes(this._tableDataIds[i])); // get all the datapoints they have selected field set by incoming anchor
}
@computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
return minMaxRange([this._lineChartData]);
@@ -107,23 +110,46 @@ export class LineChart extends React.Component<LineChartProps> {
this._disposers.highlights = reaction(
() => ({
selected: this._currSelected,
- incomingSelected: this.incomingSelected,
+ incomingHighlited: this.incomingHighlited,
}),
- ({ selected, incomingSelected }) => {
+ ({ selected, incomingHighlited }) => {
// redraw annotations when the chart data has changed, or the local or inherited selection has changed
- if (selected){
- 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 (Number(x) === Number(selected.x) && Number(y) === Number(selected.y)) element.classList.add('selected');
- }
- }
+ this.clearAnnotations();
+ selected && this.drawAnnotations(Number(selected.x), Number(selected.y), true);
+ incomingHighlited?.forEach((record: any) => this.drawAnnotations(Number(record[this.props.axes[0]]), Number(record[this.props.axes[1]])));
},
{ fireImmediately: true }
);
- if (!this.props.layoutDoc.dataViz_lineChart) this.props.layoutDoc.dataViz_lineChart = '';
+ };
+
+ // 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
+
+ clearAnnotations = () => {
+ const elements = document.querySelectorAll('.datapoint');
+ for (let i = 0; i < elements.length; i++) {
+ const element = elements[i];
+ element.classList.remove('brushed');
+ element.classList.remove('selected');
+ }
+ };
+ // gets called whenever the "data_annotations" fields gets updated
+ drawAnnotations = (dataX: number, dataY: number, selected?: boolean) => {
+ // 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(selected ? 'selected' : 'brushed');
+ }
+ // TODO: nda - this remove highlight code should go where we remove the links
+ // } else {
+ // }
+ }
};
@action
@@ -162,7 +188,7 @@ export class LineChart extends React.Component<LineChartProps> {
@computed get defaultGraphTitle() {
var ax0 = this.props.axes[0];
var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
- if (this.props.axes.length < 2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1) {
+ if (this.props.axes.length < 2 || !/\d/.test(this.props.records[0][ax0]) || !ax1) {
return ax0 + ' Line Chart';
} else return ax1 + ' by ' + ax0 + ' Line Chart';
}
@@ -186,7 +212,7 @@ export class LineChart extends React.Component<LineChartProps> {
// TODO: nda - get rid of svg element in the list?
if (this._currSelected && this._currSelected.x == x && this._currSelected.y == y) this._currSelected = undefined;
else this._currSelected = x !== undefined && y !== undefined ? { x, y } : undefined;
- this.props.pairs.forEach(pair => pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y && (pair.selected = true));
+ this.props.records.forEach(record => record[this.props.axes[0]] === x && record[this.props.axes[1]] === y && (record.selected = true));
}
drawDataPoints(data: DataPoint[], idx: number, xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>) {
@@ -327,10 +353,10 @@ export class LineChart extends React.Component<LineChartProps> {
else if (this.props.axes.length > 0) titleAccessor = 'dataViz_lineChart_title' + this.props.axes[0];
if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
const selectedPt = this._currSelected ? `{ ${this.props.axes[0]}: ${this._currSelected.x} ${this.props.axes[1]}: ${this._currSelected.y} }` : 'none';
- if (this._lineChartData.length>0 || (!this.incomingLinks || this.incomingLinks.length==0)){
- return this.props.axes.length>=2 && /\d/.test(this.props.pairs[0][this.props.axes[0]]) && /\d/.test(this.props.pairs[0][this.props.axes[1]]) ? (
- <div className="chart-container" >
- <div className="graph-title">
+ if (this._lineChartData.length > 0 || !this.parentViz || this.parentViz.length == 0) {
+ return this.props.axes.length >= 2 && /\d/.test(this.props.records[0][this.props.axes[0]]) && /\d/.test(this.props.records[0][this.props.axes[1]]) ? (
+ <div className="chart-container">
+ <div className="graph-title">
<EditableText
val={StrCast(this.props.layoutDoc[titleAccessor])}
setVal={undoable(