aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DataVizBox/components/LineChart.tsx
diff options
context:
space:
mode:
authorbobzel <zzzman@gmail.com>2023-04-10 11:32:44 -0400
committerbobzel <zzzman@gmail.com>2023-04-10 11:32:44 -0400
commit68c6c36af823255824a6b0692e8c33618c2d7ca2 (patch)
treebad87616a8678b1bb1d88415912e3e2e8417aec2 /src/client/views/nodes/DataVizBox/components/LineChart.tsx
parentbfa2a11f63fc373dd78836753c61441a13ee5d0a (diff)
fixed brushing of fonticon boxes with dropdowns. made line charts use computed values instead of observables
Diffstat (limited to 'src/client/views/nodes/DataVizBox/components/LineChart.tsx')
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx89
1 files changed, 49 insertions, 40 deletions
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 308ef4cba..bc9f0be77 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -4,17 +4,17 @@ import * as React from 'react';
// import d3
import * as d3 from 'd3';
import { Doc, DocListCast } from '../../../../../fields/Doc';
+import { Id } from '../../../../../fields/FieldSymbols';
import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
-import { Cast, DocCast, NumCast } from '../../../../../fields/Types';
+import { Cast, DocCast } from '../../../../../fields/Types';
import { Docs } from '../../../../documents/Documents';
+import { DocumentManager } from '../../../../util/DocumentManager';
+import { LinkManager } from '../../../../util/LinkManager';
import { PinProps, PresBox } from '../../trails';
+import { DataVizBox } from '../DataVizBox';
import { createLineGenerator, drawLine, minMaxRange, scaleCreatorNumerical, xAxisCreator, xGrid, yAxisCreator, yGrid } from '../utils/D3Utils';
import './Chart.scss';
-import { LinkManager } from '../../../../util/LinkManager';
-import { DocumentManager } from '../../../../util/DocumentManager';
-import { Id } from '../../../../../fields/FieldSymbols';
-import { DataVizBox } from '../DataVizBox';
export interface DataPoint {
x: number;
@@ -44,30 +44,44 @@ export class LineChart extends React.Component<LineChartProps> {
private _disposers: { [key: string]: IReactionDisposer } = {};
private _lineChartRef: React.RefObject<HTMLDivElement> = React.createRef();
private _lineChartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
- private _rangeVals: { xMin?: number; xMax?: number; yMin?: number; yMax?: number } = {};
@observable _currSelected: SelectedDataPoint | undefined = undefined;
- @observable _lineChartData: DataPoint[][] | 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 _lineChartData() {
+ if (this.props.axes.length <= 1) return [];
+ return this.props.pairs
+ ?.filter(pair => (!this.incomingLinks.length ? true : Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select'))))
+ .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));
+ }
+ @computed get incomingLinks() {
+ return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
+ .filter(link => link.anchor1 !== this.props.rootDoc) // get links where this chart doc is the target of the link
+ .map(link => DocCast(link.anchor1)); // then return the source of the link
+ }
+ @computed get incomingSelected() {
+ 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();
+ }
+ @computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
+ return minMaxRange([this._lineChartData]);
+ }
componentWillUnmount() {
Array.from(Object.keys(this._disposers)).forEach(key => this._disposers[key]());
}
componentDidMount = () => {
- this._disposers.chartdata = reaction(
- () => this.props.axes.slice(),
- axes => {
- if (axes.length > 1) {
- this._lineChartData = [this.props.pairs?.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))];
- }
- },
- { fireImmediately: true }
- );
this._disposers.chartData = reaction(
- () => ({ dataSet: this._lineChartData, axes: this.props.axes.slice(), w: this.props.width, h: this.props.height }),
- vals => {
- if (vals.dataSet) {
- this._rangeVals = minMaxRange(vals.dataSet);
- this.drawChart(vals.dataSet);
+ () => ({ dataSet: this._lineChartData, w: this.props.width, h: this.props.height }),
+ ({ dataSet, w, h }) => {
+ if (dataSet) {
+ this.drawChart([dataSet], this.rangeVals, w, h);
+ // redraw annotations when the chart data has changed, or the local or inherited selection has changed
+ this.clearAnnotations();
+ this._currSelected && this.drawAnnotations(Number(this._currSelected.x), Number(this._currSelected.y), true);
+ this.incomingSelected?.forEach((pair: any) => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]])));
}
},
{ fireImmediately: true }
@@ -87,18 +101,13 @@ export class LineChart extends React.Component<LineChartProps> {
this._disposers.highlights = reaction(
() => ({
selected: this._currSelected,
- pairs: LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc)
- .filter(link => link.anchor1 !== this.props.rootDoc) // all links that are pointing to this node
- .map(link => DocCast(link.anchor1)) // get the documents 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(),
+ incomingSelected: this.incomingSelected,
}),
- ({ selected, pairs }) => {
- this.clearAnnoations();
+ ({ selected, incomingSelected }) => {
+ // redraw annotations when the chart data has changed, or the local or inherited selection has changed
+ this.clearAnnotations();
selected && this.drawAnnotations(Number(selected.x), Number(selected.y), true);
- pairs.forEach(pair => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]])));
+ incomingSelected?.forEach((pair: any) => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]])));
},
{ fireImmediately: true }
);
@@ -106,7 +115,7 @@ export class LineChart extends React.Component<LineChartProps> {
// 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
- clearAnnoations = () => {
+ clearAnnotations = () => {
const elements = document.querySelectorAll('.datapoint');
for (let i = 0; i < elements.length; i++) {
const element = elements[i];
@@ -207,12 +216,12 @@ export class LineChart extends React.Component<LineChartProps> {
}
// TODO: nda - can use d3.create() to create html element instead of appending
- drawChart = (dataSet: DataPoint[][]) => {
+ drawChart = (dataSet: DataPoint[][], rangeVals: { xMin?: number; xMax?: number; yMin?: number; yMax?: number }, width: number, height: number) => {
// clearing tooltip and the current chart
d3.select(this._lineChartRef.current).select('svg').remove();
d3.select(this._lineChartRef.current).select('.tooltip').remove();
- const { xMin, xMax, yMin, yMax } = this._rangeVals;
+ const { xMin, xMax, yMin, yMax } = rangeVals;
if (xMin === undefined || xMax === undefined || yMin === undefined || yMax === undefined) {
return;
}
@@ -226,16 +235,16 @@ export class LineChart extends React.Component<LineChartProps> {
const svg = (this._lineChartSvg = d3
.select(this._lineChartRef.current)
.append('svg')
- .attr('width', `${this.width + margin.right + margin.left}`)
- .attr('height', `${this.height + margin.top + margin.bottom}`)
+ .attr('width', `${width + margin.right + margin.left}`)
+ .attr('height', `${height + margin.top + margin.bottom}`)
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`));
// create x and y grids
- xGrid(svg.append('g'), this.height, xScale);
- yGrid(svg.append('g'), this.width, yScale);
- xAxisCreator(svg.append('g'), this.height, xScale);
- yAxisCreator(svg.append('g'), this.width, yScale);
+ xGrid(svg.append('g'), height, xScale);
+ yGrid(svg.append('g'), width, yScale);
+ xAxisCreator(svg.append('g'), height, xScale);
+ yAxisCreator(svg.append('g'), width, yScale);
// draw the plot line
const data = dataSet[0];