aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DataVizBox/components/Histogram.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/DataVizBox/components/Histogram.tsx')
-rw-r--r--src/client/views/nodes/DataVizBox/components/Histogram.tsx173
1 files changed, 75 insertions, 98 deletions
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index b3bdccbbb..50facf03e 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -1,26 +1,26 @@
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components';
+import * as d3 from 'd3';
+import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
-import { Doc, StrListCast } from '../../../../../fields/Doc';
import * as React from 'react';
-import * as d3 from 'd3';
-import { IReactionDisposer, action, computed, observable, reaction } from 'mobx';
-import { LinkManager } from '../../../../util/LinkManager';
-import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
-import { PinProps, PresBox } from '../../trails';
-import { Docs } from '../../../../documents/Documents';
-import { List } from '../../../../../fields/List';
-import './Chart.scss';
-import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components';
import { FaFillDrip } from 'react-icons/fa';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Doc, NumListCast, StrListCast } from '../../../../../fields/Doc';
+import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
+import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
+import { Docs } from '../../../../documents/Documents';
+import { LinkManager } from '../../../../util/LinkManager';
+import { undoable } from '../../../../util/UndoManager';
+import { PinProps, PresBox } from '../../trails';
import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
-import { undoBatch, undoable } from '../../../../util/UndoManager';
+import './Chart.scss';
export interface HistogramProps {
rootDoc: Doc;
layoutDoc: Doc;
axes: string[];
- pairs: { [key: string]: any }[];
+ records: { [key: string]: any }[];
width: number;
height: number;
dataDoc: Doc;
@@ -46,44 +46,47 @@ export class Histogram extends React.Component<HistogramProps> {
private selectedData: any = undefined; // Selection of selected bar
private hoverOverData: any = undefined; // Selection of bar being hovered over
+ @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]);
+ }
// filters all data to just display selected data if brushed (created from an incoming link)
@computed get _histogramData() {
- var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
if (this.props.axes.length < 1) return [];
if (this.props.axes.length < 2) {
var ax0 = this.props.axes[0];
- if (/\d/.test(this.props.pairs[0][ax0])) {
+ if (/\d/.test(this.props.records[0][ax0])) {
this.numericalXData = true;
}
- 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 => ({ [ax0]: pair[this.props.axes[0]] }));
+ return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]] }));
}
var ax0 = this.props.axes[0];
var ax1 = this.props.axes[1];
- if (/\d/.test(this.props.pairs[0][ax0])) {
+ if (/\d/.test(this.props.records[0][ax0])) {
this.numericalXData = true;
}
- if (/\d/.test(this.props.pairs[0][ax1])) {
+ if (/\d/.test(this.props.records[0][ax1])) {
this.numericalYData = true;
}
- 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 => ({ [ax0]: pair[this.props.axes[0]], [ax1]: pair[this.props.axes[1]] }));
+ return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]], [ax1]: record[this.props.axes[1]] }));
}
@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 || !ax1 || !/\d/.test(this.props.pairs[0][ax1]) || !this.numericalYData) {
+ if (this.props.axes.length < 2 || !ax1 || !/\d/.test(this.props.records[0][ax1]) || !this.numericalYData) {
return ax0 + ' Histogram';
} else return ax0 + ' by ' + ax1 + ' Histogram';
}
- @computed get incomingLinks() {
- return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
- .filter(link => 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 => 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 rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
@@ -100,11 +103,7 @@ export class Histogram extends React.Component<HistogramProps> {
componentDidMount = () => {
this._disposers.chartData = reaction(
() => ({ dataSet: this._histogramData, w: this.width, h: this.height }),
- ({ dataSet, w, h }) => {
- if (dataSet!.length > 0) {
- this.drawChart(dataSet, w, h);
- }
- },
+ ({ dataSet, w, h }) => dataSet!.length > 0 && this.drawChart(dataSet, w, h),
{ fireImmediately: true }
);
};
@@ -114,7 +113,6 @@ export class Histogram extends React.Component<HistogramProps> {
// create a document anchor that stores whatever is needed to reconstruct the viewing state (selection,zoom,etc)
getAnchor = (pinProps?: PinProps) => {
const anchor = Docs.Create.ConfigDocument({
- //
title: 'histogram doc selection' + this._currSelected,
});
PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
@@ -131,21 +129,15 @@ export class Histogram extends React.Component<HistogramProps> {
// cleans data by converting numerical data to numbers and taking out empty cells
data = (dataSet: any) => {
- var validData = dataSet.filter((d: { [x: string]: unknown }) => {
- var valid = true;
- Object.keys(dataSet[0]).map(key => {
- if (!d[key] || Number.isNaN(d[key])) valid = false;
- });
- return valid;
- });
- var field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
- const data = validData.map((d: { [x: string]: any }) => {
- if (this.numericalXData) {
- return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
- }
- return d[field!];
- });
- return data;
+ var validData = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key])));
+ const field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
+ return !field
+ ? []
+ : validData.map((d: { [x: string]: any }) =>
+ !this.numericalXData //
+ ? d[field]
+ : +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')
+ );
};
// outlines the bar selected / hovered over
@@ -185,24 +177,18 @@ export class Histogram extends React.Component<HistogramProps> {
d3.select(this._histogramRef.current).select('svg').remove();
d3.select(this._histogramRef.current).select('.tooltip').remove();
- var data = this.data(dataSet);
- var xAxisTitle = Object.keys(dataSet[0])[0];
- var yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
- let uniqueArr: unknown[] = [...new Set(data)];
+ const data = this.data(dataSet);
+ const xAxisTitle = Object.keys(dataSet[0])[0];
+ const yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
+ const uniqueArr: unknown[] = [...new Set(data)];
var numBins = this.numericalXData && Number.isInteger(data[0]) ? this.rangeVals.xMax! - this.rangeVals.xMin! : uniqueArr.length;
var translateXAxis = !this.numericalXData || numBins < this.maxBins ? width / (numBins + 1) / 2 : 0;
if (numBins > this.maxBins) numBins = this.maxBins;
- var startingPoint = this.numericalXData ? this.rangeVals.xMin! : 0;
- var endingPoint = this.numericalXData ? this.rangeVals.xMax! : numBins;
+ const startingPoint = this.numericalXData ? this.rangeVals.xMin! : 0;
+ const endingPoint = this.numericalXData ? this.rangeVals.xMax! : numBins;
// converts data into Objects
- var histDataSet = dataSet.filter((d: { [x: string]: unknown }) => {
- var valid = true;
- Object.keys(dataSet[0]).map(key => {
- if (!d[key] || Number.isNaN(d[key])) valid = false;
- });
- return valid;
- });
+ var histDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key])));
if (!this.numericalXData) {
var histStringDataSet: { [x: string]: unknown }[] = [];
if (this.numericalYData) {
@@ -321,7 +307,7 @@ export class Histogram extends React.Component<HistogramProps> {
// click/hover
const onPointClick = action((e: any) => this.highlightSelectedBar(true, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet));
const onHover = action((e: any) => {
- const selected = this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet);
+ this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet);
updateHighlights();
});
const mouseOut = action((e: any) => {
@@ -360,10 +346,10 @@ export class Histogram extends React.Component<HistogramProps> {
'transform',
this.numericalYData
? function (d) {
- var eachData = histDataSet.filter((data: { [x: string]: number }) => {
+ const eachData = histDataSet.filter((data: { [x: string]: number }) => {
return data[xAxisTitle] == d[0];
});
- var length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
+ const length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
return 'translate(' + x(d.x0!) + ',' + y(length) + ')';
}
: function (d) {
@@ -374,10 +360,10 @@ export class Histogram extends React.Component<HistogramProps> {
'height',
this.numericalYData
? function (d) {
- var eachData = histDataSet.filter((data: { [x: string]: number }) => {
+ const eachData = histDataSet.filter((data: { [x: string]: number }) => {
return data[xAxisTitle] == d[0];
});
- var length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
+ const length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
return height - y(length);
}
: function (d) {
@@ -389,7 +375,7 @@ export class Histogram extends React.Component<HistogramProps> {
'class',
selected
? function (d) {
- return selected && selected[0] == d[0] ? 'histogram-bar hover' : 'histogram-bar';
+ return selected && selected[0] === d[0] ? 'histogram-bar hover' : 'histogram-bar';
}
: function (d) {
return 'histogram-bar';
@@ -397,11 +383,11 @@ export class Histogram extends React.Component<HistogramProps> {
)
.attr('fill', d => {
var barColor;
- var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
- barColors.map(each => {
+ const barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
+ barColors.forEach(each => {
if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
else {
- var range = StrCast(each[0]).split(' to ');
+ const range = StrCast(each[0]).split(' to ');
if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
}
});
@@ -411,23 +397,19 @@ export class Histogram extends React.Component<HistogramProps> {
@action changeSelectedColor = (color: string) => {
this.curBarSelected.attr('fill', color);
- var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ const barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec('string'), null);
- barColors.map(each => {
- if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1);
- });
+ barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1));
barColors.push(StrCast(barName + '::' + color));
};
@action eraseSelectedColor = () => {
this.curBarSelected.attr('fill', this.props.layoutDoc.defaultHistogramColor);
- var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ const barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
const barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec('string'), null);
- barColors.map(each => {
- if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1);
- });
+ barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1));
};
render() {
@@ -439,25 +421,22 @@ export class Histogram extends React.Component<HistogramProps> {
if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
if (!this.props.layoutDoc.defaultHistogramColor) this.props.layoutDoc.defaultHistogramColor = '#69b3a2';
if (!this.props.layoutDoc.histogramBarColors) this.props.layoutDoc.histogramBarColors = new List<string>();
- var selected: string;
+ var selected = 'none';
if (this._currSelected) {
curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
selected = '{ ';
- Object.keys(this._currSelected).map(key => {
- key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : '';
- });
- selected = selected.substring(0, selected.length - 2);
- selected += ' }';
- } else selected = 'none';
+ Object.keys(this._currSelected).forEach(key =>
+ key //
+ ? (selected += key + ': ' + this._currSelected[key] + ', ')
+ : ''
+ );
+ selected = selected.substring(0, selected.length - 2) + ' }';
+ }
var selectedBarColor;
var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
- barColors.map(each => {
- if (each[0] == curSelectedBarName!) selectedBarColor = each[1];
- });
+ barColors.forEach(each => each[0] === curSelectedBarName && (selectedBarColor = each[1]));
- this.componentDidMount();
-
- if (this._histogramData.length > 0 || (!this.incomingLinks || this.incomingLinks.length==0)) {
+ if (this._histogramData.length > 0 || !this.parentViz) {
return this.props.axes.length >= 1 ? (
<div className="chart-container">
<div className="graph-title">
@@ -514,10 +493,8 @@ export class Histogram extends React.Component<HistogramProps> {
) : (
<span className="chart-container"> {'first use table view to select a column to graph'}</span>
);
- } else
- return (
- // when it is a brushed table and the incoming table doesn't have any rows selected
- <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
- );
+ }
+ // when it is a brushed table and the incoming table doesn't have any rows selected
+ return <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>;
}
-} \ No newline at end of file
+}