aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DataVizBox/components
diff options
context:
space:
mode:
authorsrichman333 <sarah_n_richman@brown.edu>2023-08-17 10:54:35 -0400
committersrichman333 <sarah_n_richman@brown.edu>2023-08-17 10:54:35 -0400
commitce7d1b25a384a3919ce547e5dd33fd2a0ebd3111 (patch)
treed10387bb81d83172fa380e9805a9861e12ffb5f0 /src/client/views/nodes/DataVizBox/components
parent18b3b9e5eab4a7902390033feb0a1c7e379cf7d6 (diff)
parent0eb9d37a9dd1a6539f331f953d5f20c761d5f940 (diff)
Merge branch 'master' into data-visualization-sarah
Diffstat (limited to 'src/client/views/nodes/DataVizBox/components')
-rw-r--r--src/client/views/nodes/DataVizBox/components/Histogram.tsx525
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx99
-rw-r--r--src/client/views/nodes/DataVizBox/components/PieChart.tsx399
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx79
4 files changed, 582 insertions, 520 deletions
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index e078043c1..954569910 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -1,20 +1,20 @@
-import { observer } from "mobx-react";
-import { Doc, StrListCast } from "../../../../../fields/Doc";
+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 { 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 { listSpec } from "../../../../../fields/Schema";
-import { scaleCreatorNumerical, yAxisCreator } from "../utils/D3Utils";
-import { undoBatch, undoable } from "../../../../util/UndoManager";
+import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components';
+import { FaFillDrip } from 'react-icons/fa';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { listSpec } from '../../../../../fields/Schema';
+import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
+import { undoBatch, undoable } from '../../../../util/UndoManager';
export interface HistogramProps {
rootDoc: Doc;
@@ -35,7 +35,6 @@ export interface HistogramProps {
@observer
export class Histogram extends React.Component<HistogramProps> {
-
private _disposers: { [key: string]: IReactionDisposer } = {};
private _histogramRef: React.RefObject<HTMLDivElement> = React.createRef();
private _histogramSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@@ -49,32 +48,37 @@ export class Histogram extends React.Component<HistogramProps> {
// filters all data to just display selected data if brushed (created from an incoming link)
@computed get _histogramData() {
- var guids = StrListCast(this.props.layoutDoc.rowGuids);
+ 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])){ this.numericalXData = true }
+ if (/\d/.test(this.props.pairs[0][ax0])) {
+ this.numericalXData = true;
+ }
return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
- .map(pair => ({ [ax0]: (pair[this.props.axes[0]])}))
- };
+ ?.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]] }));
+ }
var ax0 = this.props.axes[0];
var ax1 = this.props.axes[1];
- if (/\d/.test(this.props.pairs[0][ax0])) { this.numericalXData = true;}
- if (/\d/.test(this.props.pairs[0][ax1]) ) { this.numericalYData = true;}
+ if (/\d/.test(this.props.pairs[0][ax0])) {
+ this.numericalXData = true;
+ }
+ if (/\d/.test(this.props.pairs[0][ax1])) {
+ this.numericalYData = true;
+ }
return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
- .map(pair => ({ [ax0]: (pair[this.props.axes[0]]), [ax1]: (pair[this.props.axes[1]]) }))
+ ?.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]] }));
}
- @computed get defaultGraphTitle(){
+ @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){
- return ax0 + " Histogram";
- }
- else return ax0 + " by " + ax1 + " Histogram";
- }
+ 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) {
+ return ax0 + ' Histogram';
+ } else return ax0 + ' by ' + ax1 + ' Histogram';
+ }
@computed get incomingLinks() {
return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
@@ -83,11 +87,11 @@ export class Histogram extends React.Component<HistogramProps> {
}
@computed get rangeVals(): { xMin?: number; xMax?: number; yMin?: number; yMax?: number } {
- if (this.numericalXData){
+ if (this.numericalXData) {
const data = this.data(this._histogramData);
- return {xMin: Math.min.apply(null, data), xMax: Math.max.apply(null, data), yMin:0, yMax:0}
+ return { xMin: Math.min.apply(null, data), xMax: Math.max.apply(null, data), yMin: 0, yMax: 0 };
}
- return {xMin:0, xMax:0, yMin:0, yMax:0}
+ return { xMin: 0, xMax: 0, yMin: 0, yMax: 0 };
}
componentWillUnmount() {
@@ -97,7 +101,7 @@ export class Histogram extends React.Component<HistogramProps> {
this._disposers.chartData = reaction(
() => ({ dataSet: this._histogramData, w: this.width, h: this.height }),
({ dataSet, w, h }) => {
- if (dataSet!.length>0) {
+ if (dataSet!.length > 0) {
this.drawChart(dataSet, w, h);
}
},
@@ -113,7 +117,7 @@ export class Histogram extends React.Component<HistogramProps> {
//
title: 'histogram doc selection' + this._currSelected,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
return anchor;
};
@@ -127,20 +131,22 @@ 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 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!]
- })
+ });
+ 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;
- }
+ };
// outlines the bar selected / hovered over
highlightSelectedBar = (changeSelectedVariables: boolean, svg: any, eachRectWidth: any, pointerX: any, xAxisTitle: any, yAxisTitle: any, histDataSet: any) => {
@@ -148,35 +154,31 @@ export class Histogram extends React.Component<HistogramProps> {
var barCounter = -1;
const selected = svg.selectAll('.histogram-bar').filter((d: any) => {
barCounter++; // uses the order of bars and width of each bar to find which one the pointer is over
- if ((barCounter*eachRectWidth ) <= pointerX && pointerX <= ((barCounter+1)*eachRectWidth)){
- var showSelected = this.numericalYData? this._histogramData.filter((data: { [x: string]: any; }) => StrCast(data[xAxisTitle]).replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')==d[0])[0]
- : histDataSet.filter((data: { [x: string]: any; }) => data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')==d[0])[0];
- if (this.numericalXData){
+ if (barCounter * eachRectWidth <= pointerX && pointerX <= (barCounter + 1) * eachRectWidth) {
+ var showSelected = this.numericalYData
+ ? this._histogramData.filter((data: { [x: string]: any }) => StrCast(data[xAxisTitle]).replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') == d[0])[0]
+ : histDataSet.filter((data: { [x: string]: any }) => data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') == d[0])[0];
+ if (this.numericalXData) {
// calculating frequency
- if (d[0] && d[1] && d[0]!=d[1]){
- showSelected = {[xAxisTitle]: (d3.min(d) + " to " + d3.max(d)), frequency: d.length}
- }
- else if (!this.numericalYData) showSelected = {[xAxisTitle]: showSelected[xAxisTitle], frequency: d.length}
- }
- if (changeSelectedVariables){
- // for when a bar is selected - not just hovered over
- sameAsCurrent = this._currSelected?
- (showSelected[xAxisTitle]==this._currSelected![xAxisTitle]
- && showSelected[yAxisTitle]==this._currSelected![yAxisTitle])
- : false;
- this._currSelected = sameAsCurrent? undefined: showSelected;
- this.selectedData = sameAsCurrent? undefined: d;
+ if (d[0] && d[1] && d[0] != d[1]) {
+ showSelected = { [xAxisTitle]: d3.min(d) + ' to ' + d3.max(d), frequency: d.length };
+ } else if (!this.numericalYData) showSelected = { [xAxisTitle]: showSelected[xAxisTitle], frequency: d.length };
}
- else this.hoverOverData = d;
- return true
+ if (changeSelectedVariables) {
+ // for when a bar is selected - not just hovered over
+ sameAsCurrent = this._currSelected ? showSelected[xAxisTitle] == this._currSelected![xAxisTitle] && showSelected[yAxisTitle] == this._currSelected![yAxisTitle] : false;
+ this._currSelected = sameAsCurrent ? undefined : showSelected;
+ this.selectedData = sameAsCurrent ? undefined : d;
+ } else this.hoverOverData = d;
+ return true;
}
return false;
});
- if (changeSelectedVariables){
+ if (changeSelectedVariables) {
if (sameAsCurrent!) this.curBarSelected = undefined;
else this.curBarSelected = selected;
}
- }
+ };
// draws the histogram
drawChart = (dataSet: any, width: number, height: number) => {
@@ -184,248 +186,274 @@ export class Histogram extends React.Component<HistogramProps> {
d3.select(this._histogramRef.current).select('.tooltip').remove();
var data = this.data(dataSet);
- var xAxisTitle = Object.keys(dataSet[0])[0]
+ var xAxisTitle = Object.keys(dataSet[0])[0];
var yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
- let 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;
+ let 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;
// converts data into Objects
- var histDataSet = dataSet.filter((d: { [x: string]: unknown; }) => {
+ 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;
});
if (!this.numericalXData) {
- var histStringDataSet: { [x: string]: unknown; }[] = [];
- if (this.numericalYData){
- for (let i=0; i<dataSet.length; i++){
- histStringDataSet.push({[yAxisTitle]: dataSet[i][yAxisTitle], [xAxisTitle]: dataSet[i][xAxisTitle]})
+ var histStringDataSet: { [x: string]: unknown }[] = [];
+ if (this.numericalYData) {
+ for (let i = 0; i < dataSet.length; i++) {
+ histStringDataSet.push({ [yAxisTitle]: dataSet[i][yAxisTitle], [xAxisTitle]: dataSet[i][xAxisTitle] });
}
- }
- else{
- for (let i=0; i<uniqueArr.length; i++){
- histStringDataSet.push({[yAxisTitle]: 0, [xAxisTitle]: uniqueArr[i]})
+ } else {
+ for (let i = 0; i < uniqueArr.length; i++) {
+ histStringDataSet.push({ [yAxisTitle]: 0, [xAxisTitle]: uniqueArr[i] });
}
- for (let i=0; i<data.length; i++){
- let barData = histStringDataSet.filter(each => each[xAxisTitle]==data[i])
- histStringDataSet.filter(each => each[xAxisTitle]==data[i])[0][yAxisTitle] = Number(barData[0][yAxisTitle]) + 1;
+ for (let i = 0; i < data.length; i++) {
+ let barData = histStringDataSet.filter(each => each[xAxisTitle] == data[i]);
+ histStringDataSet.filter(each => each[xAxisTitle] == data[i])[0][yAxisTitle] = Number(barData[0][yAxisTitle]) + 1;
}
}
- histDataSet = histStringDataSet
+ histDataSet = histStringDataSet;
}
// initial graph and binning data for histogram
var svg = (this._histogramSvg = d3
.select(this._histogramRef.current)
- .append("svg")
- .attr("class", "graph")
- .attr("width", width + this.props.margin.right + this.props.margin.left)
- .attr("height", height + this.props.margin.top + this.props.margin.bottom)
- .append("g")
- .attr("transform",
- "translate(" + this.props.margin.left + "," + this.props.margin.top + ")"));
- var x = d3.scaleLinear()
- .domain(this.numericalXData? [startingPoint!, endingPoint!] : [0, numBins])
- .range([0, width ]);
- var histogram = d3.histogram()
- .value(function(d) {return d})
+ .append('svg')
+ .attr('class', 'graph')
+ .attr('width', width + this.props.margin.right + this.props.margin.left)
+ .attr('height', height + this.props.margin.top + this.props.margin.bottom)
+ .append('g')
+ .attr('transform', 'translate(' + this.props.margin.left + ',' + this.props.margin.top + ')'));
+ var x = d3
+ .scaleLinear()
+ .domain(this.numericalXData ? [startingPoint!, endingPoint!] : [0, numBins])
+ .range([0, width]);
+ var histogram = d3
+ .histogram()
+ .value(function (d) {
+ return d;
+ })
.domain([startingPoint!, endingPoint!])
- .thresholds(x.ticks(numBins))
- var bins = histogram(data)
- var eachRectWidth = width/(bins.length)
- var graphStartingPoint = (bins[0].x1 && bins[1])? bins[0].x1! - (bins[1].x1! - bins[1].x0!) : 0;
+ .thresholds(x.ticks(numBins));
+ var bins = histogram(data);
+ var eachRectWidth = width / bins.length;
+ var graphStartingPoint = bins[0].x1 && bins[1] ? bins[0].x1! - (bins[1].x1! - bins[1].x0!) : 0;
bins[0].x0 = graphStartingPoint;
- x = x.domain([graphStartingPoint, endingPoint])
- .range([0, Number.isInteger(this.rangeVals.xMin!)? (width-eachRectWidth) : width ])
+ x = x.domain([graphStartingPoint, endingPoint]).range([0, Number.isInteger(this.rangeVals.xMin!) ? width - eachRectWidth : width]);
var xAxis;
// more calculations based on bins
// x-axis
- if (!this.numericalXData) { // reorganize to match data if the data is strings rather than numbers
+ if (!this.numericalXData) {
+ // reorganize to match data if the data is strings rather than numbers
// uniqueArr.sort()
- histDataSet.sort()
- for (let i=0; i<data.length; i++){
- var index = 0
- for (let j=0; j<uniqueArr.length; j++){
- if (uniqueArr[j] == data[i]){
+ histDataSet.sort();
+ for (let i = 0; i < data.length; i++) {
+ var index = 0;
+ for (let j = 0; j < uniqueArr.length; j++) {
+ if (uniqueArr[j] == data[i]) {
index = j;
}
}
- if (bins[index]) bins[index].push(data[i])
+ if (bins[index]) bins[index].push(data[i]);
}
bins.pop();
- eachRectWidth = width/(bins.length)
- bins.forEach(d => d.x0 = d.x0!)
- xAxis = d3.axisBottom(x)
- .ticks(bins.length>1? bins.length-1: 1)
- .tickFormat( i => uniqueArr[i.valueOf()] as string)
- .tickPadding(10)
- x.range([0, width-eachRectWidth])
- x.domain([0, bins.length-1])
+ eachRectWidth = width / bins.length;
+ bins.forEach(d => (d.x0 = d.x0!));
+ xAxis = d3
+ .axisBottom(x)
+ .ticks(bins.length > 1 ? bins.length - 1 : 1)
+ .tickFormat(i => uniqueArr[i.valueOf()] as string)
+ .tickPadding(10);
+ x.range([0, width - eachRectWidth]);
+ x.domain([0, bins.length - 1]);
translateXAxis = eachRectWidth / 2;
- }
- else {
+ } else {
var allSame = true;
- for (var i=0; i<bins.length; i++){
- if (bins[i] && bins[i][0]){
+ for (var i = 0; i < bins.length; i++) {
+ if (bins[i] && bins[i][0]) {
var compare = bins[i][0];
- for (let j=1; j<bins[i].length; j++){
+ for (let j = 1; j < bins[i].length; j++) {
if (bins[i][j] != compare) allSame = false;
}
}
}
- if (allSame){
+ if (allSame) {
translateXAxis = eachRectWidth / 2;
- eachRectWidth = width/(bins.length)
- }
- else {
- eachRectWidth = width/(bins.length+1)
- var tickDiff = (bins.length>=2? (bins[bins.length-2].x1!-bins[bins.length-2].x0!): 0)
+ eachRectWidth = width / bins.length;
+ } else {
+ eachRectWidth = width / (bins.length + 1);
+ var tickDiff = bins.length >= 2 ? bins[bins.length - 2].x1! - bins[bins.length - 2].x0! : 0;
var curDomain = x.domain();
- x.domain([curDomain[0], curDomain[0] + tickDiff*bins.length])
+ x.domain([curDomain[0], curDomain[0] + tickDiff * bins.length]);
}
- xAxis = d3.axisBottom(x)
- .ticks(bins.length-1)
- x.range([0, width-eachRectWidth])
+ xAxis = d3.axisBottom(x).ticks(bins.length - 1);
+ x.range([0, width - eachRectWidth]);
}
// y-axis
- const maxFrequency = this.numericalYData? d3.max(histDataSet, function(d: any) {
- return d[yAxisTitle]? Number(d[yAxisTitle]!.replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) : 0})
- : d3.max(bins, function(d) { return d.length; })
- var y = d3.scaleLinear()
- .range([height, 0]);
- y.domain([0, +maxFrequency!]);
- var yAxis = d3.axisLeft(y)
- .ticks(maxFrequency!)
- if (this.numericalYData){
+ const maxFrequency = this.numericalYData
+ ? d3.max(histDataSet, function (d: any) {
+ return d[yAxisTitle] ? Number(d[yAxisTitle]!.replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) : 0;
+ })
+ : d3.max(bins, function (d) {
+ return d.length;
+ });
+ var y = d3.scaleLinear().range([height, 0]);
+ y.domain([0, +maxFrequency!]);
+ var yAxis = d3.axisLeft(y).ticks(maxFrequency!);
+ if (this.numericalYData) {
const yScale = scaleCreatorNumerical(0, Number(maxFrequency), height, 0);
yAxisCreator(svg.append('g'), width, yScale);
+ } else {
+ svg.append('g').call(yAxis);
}
- else{
- svg.append("g")
- .call(yAxis);
- }
- svg.append("g")
- .attr("transform", "translate(" + translateXAxis + ", " + height + ")")
- .call(xAxis)
+ svg.append('g')
+ .attr('transform', 'translate(' + translateXAxis + ', ' + height + ')')
+ .call(xAxis);
// 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)
+ const selected = this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet);
updateHighlights();
- })
+ });
const mouseOut = action((e: any) => {
this.hoverOverData = undefined;
updateHighlights();
- })
+ });
const updateHighlights = () => {
const hoverOverBar = this.hoverOverData;
const selectedData = this.selectedData;
- svg.selectAll('rect').attr("class", function(d: any) { return ((hoverOverBar && hoverOverBar[0]==d[0]) || selectedData && selectedData[0]==d[0])? 'histogram-bar hover' : 'histogram-bar'; })
- }
- svg.on('click', onPointClick)
- .on('mouseover', onHover)
- .on('mouseout', mouseOut)
+ svg.selectAll('rect').attr('class', function (d: any) {
+ return (hoverOverBar && hoverOverBar[0] == d[0]) || (selectedData && selectedData[0] == d[0]) ? 'histogram-bar hover' : 'histogram-bar';
+ });
+ };
+ svg.on('click', onPointClick).on('mouseover', onHover).on('mouseout', mouseOut);
// axis titles
- svg.append("text")
- .attr("transform", "translate(" + (width/2) + " ," + (height+40) + ")")
- .style("text-anchor", "middle")
+ svg.append('text')
+ .attr('transform', 'translate(' + width / 2 + ' ,' + (height + 40) + ')')
+ .style('text-anchor', 'middle')
.text(xAxisTitle);
- svg.append("text")
- .attr("transform", "rotate(-90)" + " " + "translate( 0, " + -10 + ")")
- .attr("x", -(height/2))
- .attr("y", -20)
- .style("text-anchor", "middle")
+ svg.append('text')
+ .attr('transform', 'rotate(-90)' + ' ' + 'translate( 0, ' + -10 + ')')
+ .attr('x', -(height / 2))
+ .attr('y', -20)
+ .style('text-anchor', 'middle')
.text(yAxisTitle);
- d3.format('.0f')
+ d3.format('.0f');
// draw bars
var selected = this.selectedData;
- svg.selectAll("rect")
+ svg.selectAll('rect')
.data(bins)
.enter()
- .append("rect")
- .attr("transform", this.numericalYData?
- function (d) {
- var 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;
- return "translate(" + x(d.x0!) + "," + y(length) + ")";
- }
- : function(d) { return "translate(" + x(d.x0!) + "," + y(d.length) + ")"; })
- .attr("height", this.numericalYData?
- function(d) {
- var 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;
- return height-y(length)}
- : function(d) {
- return height - y(d.length)})
- .attr("width", eachRectWidth)
- .attr("class", selected?
- function(d) {
- return (selected && selected[0]==d[0])? 'histogram-bar hover' : 'histogram-bar';
- }: function(d) {return 'histogram-bar'})
- .attr("fill", (d)=>{
+ .append('rect')
+ .attr(
+ 'transform',
+ this.numericalYData
+ ? function (d) {
+ var 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;
+ return 'translate(' + x(d.x0!) + ',' + y(length) + ')';
+ }
+ : function (d) {
+ return 'translate(' + x(d.x0!) + ',' + y(d.length) + ')';
+ }
+ )
+ .attr(
+ 'height',
+ this.numericalYData
+ ? function (d) {
+ var 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;
+ return height - y(length);
+ }
+ : function (d) {
+ return height - y(d.length);
+ }
+ )
+ .attr('width', eachRectWidth)
+ .attr(
+ 'class',
+ selected
+ ? function (d) {
+ return selected && selected[0] == d[0] ? 'histogram-bar hover' : 'histogram-bar';
+ }
+ : function (d) {
+ return 'histogram-bar';
+ }
+ )
+ .attr('fill', d => {
var barColor;
var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
- barColors.map(each => {
- if (d[0] && d[0].toString() && each[0]==d[0].toString()) barColor = each[1];
+ barColors.map(each => {
+ if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
else {
- var range = StrCast(each[0]).split(" to ");
- if (Number(range[0])<=d[0] && d[0]<=Number(range[1])) barColor = each[1];
+ var range = StrCast(each[0]).split(' to ');
+ if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
}
});
- return barColor? StrCast(barColor) : StrCast(this.props.layoutDoc.defaultHistogramColor)})
+ return barColor ? StrCast(barColor) : StrCast(this.props.layoutDoc.defaultHistogramColor);
+ });
};
@action changeSelectedColor = (color: string) => {
- this.curBarSelected.attr("fill", color);
- var barName = StrCast(this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
+ this.curBarSelected.attr('fill', color);
+ var 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) });
+ 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.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 barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec("string"), null);
- barColors.map(each => { if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1) });
+ @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 barColors = Cast(this.props.layoutDoc.histogramBarColors, listSpec('string'), null);
+ barColors.map(each => {
+ if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1);
+ });
};
render() {
- this._histogramData
- var curSelectedBarName;
- var titleAccessor: any='';
- if (this.props.axes.length==2) titleAccessor = 'histogram-title-'+this.props.axes[0]+'-'+this.props.axes[1];
- else if (this.props.axes.length>0) titleAccessor = 'histogram-title-'+this.props.axes[0];
+ this._histogramData;
+ var curSelectedBarName = '';
+ var titleAccessor: any = '';
+ if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_histogram_' + this.props.axes[0] + '-' + this.props.axes[1];
+ else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_histogram_' + this.props.axes[0];
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;
- if (this._currSelected){
- curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
+ 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);
+ key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : '';
+ });
+ selected = selected.substring(0, selected.length - 2);
selected += ' }';
- }
- else selected = 'none';
+ } else selected = 'none';
var selectedBarColor;
var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
- barColors.map(each => {if (each[0]==curSelectedBarName!) selectedBarColor = each[1]});
+ barColors.map(each => {
+ if (each[0] == curSelectedBarName!) selectedBarColor = each[1];
+ });
this.componentDidMount();
@@ -436,54 +464,61 @@ export class Histogram extends React.Component<HistogramProps> {
<div className="graph-title">
<EditableText
val={StrCast(this.props.layoutDoc[titleAccessor])}
- setVal={undoable (action(val => this.props.layoutDoc[titleAccessor] = val as string), "Change Graph Title")}
- color={"black"}
+ setVal={undoable(
+ action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+ 'Change Graph Title'
+ )}
+ color={'black'}
size={Size.LARGE}
fillWidth
/>
- &nbsp; &nbsp;
+ &nbsp; &nbsp;
<ColorPicker
- tooltip={'Change Default Bar Color'}
- type={Type.SEC}
- icon={<FaFillDrip/>}
+ tooltip={'Change Default Bar Color'}
+ type={Type.SEC}
+ icon={<FaFillDrip />}
selectedColor={StrCast(this.props.layoutDoc.defaultHistogramColor)}
- setSelectedColor={undoable (color => this.props.layoutDoc.defaultHistogramColor = color, "Change Default Bar Color")}
+ setFinalColor={undoable(color => (this.props.layoutDoc.defaultHistogramColor = color), 'Change Default Bar Color')}
+ setSelectedColor={undoable(color => (this.props.layoutDoc.defaultHistogramColor = color), 'Change Default Bar Color')}
size={Size.XSMALL}
/>
</div>
<div ref={this._histogramRef} />
- {selected != 'none' ?
+ {selected != 'none' ? (
<div className={'selected-data'}>
Selected: {selected}
&nbsp; &nbsp;
<ColorPicker
- tooltip={'Change Bar Color'}
- type={Type.SEC}
- icon={<FaFillDrip/>}
- selectedColor={selectedBarColor? selectedBarColor : this.curBarSelected.attr("fill")}
- setSelectedColor={undoable (color => this.changeSelectedColor(color), "Change Selected Bar Color")}
+ tooltip={'Change Bar Color'}
+ type={Type.SEC}
+ icon={<FaFillDrip />}
+ selectedColor={selectedBarColor ? selectedBarColor : this.curBarSelected.attr('fill')}
+ setFinalColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Bar Color')}
+ setSelectedColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Bar Color')}
size={Size.XSMALL}
/>
&nbsp;
<IconButton
- icon={<FontAwesomeIcon icon={'eraser'} />}
- size={Size.XSMALL}
- color={'black'}
+ icon={<FontAwesomeIcon icon={'eraser'} />}
+ size={Size.XSMALL}
+ color={'black'}
type={Type.SEC}
tooltip={'Revert to the default bar color'}
- onClick={undoable (action(() => this.eraseSelectedColor()), "Change Selected Bar Color")}
+ onClick={undoable(
+ action(() => this.eraseSelectedColor()),
+ 'Change Selected Bar Color'
+ )}
/>
</div>
- : null}
+ ) : null}
</div>
- ) : <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+ ) : (
+ <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>
);
- }
- 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>
- )
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 468780185..46cf27705 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -50,20 +50,21 @@ export class LineChart extends React.Component<LineChartProps> {
// 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() {
- var guids = StrListCast(this.props.layoutDoc.rowGuids);
+ var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
if (this.props.axes.length <= 1) return [];
return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
+ ?.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));
}
- @computed get graphTitle(){
- return this.props.axes[1] + " vs. " + this.props.axes[0] + " Line Chart";
- }
+ @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
+ 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 incomingSelected() {
@@ -158,7 +159,7 @@ export class LineChart extends React.Component<LineChartProps> {
@action
restoreView = (data: Doc) => {
- const coords = Cast(data.presDataVizSelection, listSpec('number'), null);
+ const coords = Cast(data.config_dataVizSelection, listSpec('number'), null);
if (coords?.length > 1 && (this._currSelected?.x !== coords[0] || this._currSelected?.y !== coords[1])) {
this.setCurrSelected(coords[0], coords[1]);
return true;
@@ -176,8 +177,8 @@ export class LineChart extends React.Component<LineChartProps> {
//
title: 'line doc selection' + this._currSelected?.x,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
- anchor.presDataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
+ anchor.config_dataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
return anchor;
};
@@ -189,14 +190,13 @@ export class LineChart extends React.Component<LineChartProps> {
return this.props.width - this.props.margin.left - this.props.margin.right;
}
- @computed get defaultGraphTitle(){
+ @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){
- return ax0 + " Line Chart";
- }
- else return ax1 + " by " + ax0 + " Line Chart";
- }
+ 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) {
+ return ax0 + ' Line Chart';
+ } else return ax1 + ' by ' + ax0 + ' Line Chart';
+ }
setupTooltip() {
return d3
@@ -215,7 +215,7 @@ export class LineChart extends React.Component<LineChartProps> {
@action
setCurrSelected(x?: number, y?: number) {
// TODO: nda - get rid of svg element in the list?
- if (this._currSelected && this._currSelected.x==x && this._currSelected.y==y) this._currSelected = undefined;
+ 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));
}
@@ -256,7 +256,7 @@ export class LineChart extends React.Component<LineChartProps> {
const svg = (this._lineChartSvg = d3
.select(this._lineChartRef.current)
.append('svg')
- .attr("class", "graph")
+ .attr('class', 'graph')
.attr('width', `${width + margin.left + margin.right}`)
.attr('height', `${height + margin.top + margin.bottom}`)
.append('g')
@@ -271,13 +271,13 @@ export class LineChart extends React.Component<LineChartProps> {
// get valid data points
const data = dataSet[0];
const lineGen = createLineGenerator(xScale, yScale);
- var validData = data.filter((d => {
+ var validData = data.filter(d => {
var valid = true;
Object.keys(data[0]).map(key => {
if (!d[key] || Number.isNaN(d[key])) valid = false;
- })
+ });
return valid;
- }))
+ });
// draw the plot line
drawLine(svg.append('path'), validData, lineGen);
// draw the datapoint circle
@@ -321,17 +321,17 @@ export class LineChart extends React.Component<LineChartProps> {
.on('click', onPointClick);
// axis titles
- svg.append("text")
- .attr("transform", "translate(" + (width/2) + " ," + (height+40) + ")")
- .style("text-anchor", "middle")
+ svg.append('text')
+ .attr('transform', 'translate(' + width / 2 + ' ,' + (height + 40) + ')')
+ .style('text-anchor', 'middle')
.text(this.props.axes[0]);
- svg.append("text")
- .attr("transform", "rotate(-90)" + " " + "translate( 0, " + -10 + ")")
- .attr("x", -(height/2))
- .attr("y", -20)
- .attr("height", 20)
- .attr("width", 20)
- .style("text-anchor", "middle")
+ svg.append('text')
+ .attr('transform', 'rotate(-90)' + ' ' + 'translate( 0, ' + -10 + ')')
+ .attr('x', -(height / 2))
+ .attr('y', -20)
+ .attr('height', 20)
+ .attr('width', 20)
+ .style('text-anchor', 'middle')
.text(this.props.axes[1]);
};
@@ -348,42 +348,41 @@ export class LineChart extends React.Component<LineChartProps> {
tooltip
.html(() => `<b>(${d0.x},${d0.y})</b>`) // text content for tooltip
.style('pointer-events', 'none')
- .style('transform', `translate(${xScale(d0.x)-this.width}px,${yScale(d0.y)}px)`);
+ .style('transform', `translate(${xScale(d0.x) - this.width}px,${yScale(d0.y)}px)`);
}
render() {
this.componentDidMount();
- var titleAccessor:any = '';
- if (this.props.axes.length==2) titleAccessor = 'lineChart-title-'+this.props.axes[0]+'-'+this.props.axes[1];
- else if (this.props.axes.length>0) titleAccessor = 'lineChart-title-'+this.props.axes[0];
+ var titleAccessor: any = '';
+ if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_lineChart_' + this.props.axes[0] + '-' + this.props.axes[1];
+ else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_lineChart_' + 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]]) ? (
+ 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">
<EditableText
val={StrCast(this.props.layoutDoc[titleAccessor])}
- setVal={undoable (action(val => this.props.layoutDoc[titleAccessor] = val as string), "Change Graph Title")}
- color={"black"}
+ setVal={undoable(
+ action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+ 'Change Graph Title'
+ )}
+ color={'black'}
size={Size.LARGE}
fillWidth
/>
</div>
<div ref={this._lineChartRef} />
- {selectedPt!='none'?
- <div className={'selected-data'}> {`Selected: ${selectedPt}`}</div>
- : null}
+ {selectedPt != 'none' ? <div className={'selected-data'}> {`Selected: ${selectedPt}`}</div> : null}
</div>
- ) : <span className="chart-container"> {'first use table view to select two numerical axes to plot'}</span>
+ ) : (
+ <span className="chart-container"> {'first use table view to select two numerical axes to plot'}</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>
);
- }
- 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>
- )
}
}
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index 657d496aa..213baa8a4 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -1,18 +1,18 @@
-import { observer } from "mobx-react";
-import { Doc, StrListCast } from "../../../../../fields/Doc";
+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 { 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, Size, Type } from "browndash-components";
-import { FaFillDrip } from "react-icons/fa";
-import { listSpec } from "../../../../../fields/Schema";
-import { undoable } from "../../../../util/UndoManager";
+import { ColorPicker, EditableText, Size, Type } from 'browndash-components';
+import { FaFillDrip } from 'react-icons/fa';
+import { listSpec } from '../../../../../fields/Schema';
+import { undoable } from '../../../../util/UndoManager';
export interface PieChartProps {
rootDoc: Doc;
@@ -33,7 +33,6 @@ export interface PieChartProps {
@observer
export class PieChart extends React.Component<PieChartProps> {
-
private _disposers: { [key: string]: IReactionDisposer } = {};
private _piechartRef: React.RefObject<HTMLDivElement> = React.createRef();
private _piechartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@@ -45,31 +44,34 @@ export class PieChart extends React.Component<PieChartProps> {
// filters all data to just display selected data if brushed (created from an incoming link)
@computed get _piechartData() {
- var guids = StrListCast(this.props.layoutDoc.rowGuids);
+ 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])){ this.byCategory = false }
+ if (/\d/.test(this.props.pairs[0][ax0])) {
+ this.byCategory = false;
+ }
return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
- .map(pair => ({ [ax0]: (pair[this.props.axes[0]])}))
- };
+ ?.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]] }));
+ }
var ax0 = this.props.axes[0];
var ax1 = this.props.axes[1];
- if (/\d/.test(this.props.pairs[0][ax0])) { this.byCategory = false; }
+ if (/\d/.test(this.props.pairs[0][ax0])) {
+ this.byCategory = false;
+ }
return this.props.pairs
- ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)])))
- .map(pair => ({ [ax0]: (pair[this.props.axes[0]]), [ax1]: (pair[this.props.axes[1]]) }))
+ ?.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]] }));
}
- @computed get defaultGraphTitle(){
+ @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){
- return ax0 + " Pie Chart";
- }
- else return ax1 + " by " + ax0 + " Pie Chart";
- }
+ 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) {
+ return ax0 + ' Pie Chart';
+ } else return ax1 + ' by ' + ax0 + ' Pie Chart';
+ }
@computed get incomingLinks() {
return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links
@@ -84,7 +86,7 @@ export class PieChart extends React.Component<PieChartProps> {
this._disposers.chartData = reaction(
() => ({ dataSet: this._piechartData, w: this.width, h: this.height }),
({ dataSet, w, h }) => {
- if (dataSet!.length>0) {
+ if (dataSet!.length > 0) {
this.drawChart(dataSet, w, h);
}
},
@@ -100,7 +102,7 @@ export class PieChart extends React.Component<PieChartProps> {
//
title: 'piechart doc selection' + this._currSelected,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.rootDoc);
return anchor;
};
@@ -114,167 +116,177 @@ export class PieChart extends React.Component<PieChartProps> {
// 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 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.byCategory) { return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') }
- return d[field!]
- })
+ });
+ var field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
+ const data = validData.map((d: { [x: string]: any }) => {
+ if (!this.byCategory) {
+ return +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
+ }
+ return d[field!];
+ });
return data;
- }
+ };
// outlines the slice selected / hovered over
highlightSelectedSlice = (changeSelectedVariables: boolean, svg: any, arc: any, radius: any, pointer: any, pieDataSet: any) => {
- var index = -1;
- var sameAsCurrent: boolean;
- const selected = svg.selectAll('.slice').filter((d: any) => {
- index++;
- var p1 = [0,0]; // center of pie
- var p3 = [arc.centroid(d)[0]*2, arc.centroid(d)[1]*2]; // outward peak of arc
- var p2 = [radius*Math.sin(d.startAngle), -radius*Math.cos(d.startAngle)]; // start of arc
- var p4 = [radius*Math.sin(d.endAngle), -radius*Math.cos(d.endAngle)]; // end of arc
+ var index = -1;
+ var sameAsCurrent: boolean;
+ const selected = svg.selectAll('.slice').filter((d: any) => {
+ index++;
+ var p1 = [0, 0]; // center of pie
+ var p3 = [arc.centroid(d)[0] * 2, arc.centroid(d)[1] * 2]; // outward peak of arc
+ var p2 = [radius * Math.sin(d.startAngle), -radius * Math.cos(d.startAngle)]; // start of arc
+ var p4 = [radius * Math.sin(d.endAngle), -radius * Math.cos(d.endAngle)]; // end of arc
- // draw an imaginary horizontal line from the pointer to see how many times it crosses a slice edge
- var lineCrossCount = 0;
- // if for all 4 lines
- if (Math.min(p1[1], p2[1])<=pointer[1] && pointer[1]<=Math.max(p1[1], p2[1])){ // within y bounds
- if (pointer[0] <= (pointer[1]-p1[1])*(p2[0]-p1[0])/(p2[1]-p1[1])+p1[0]) lineCrossCount++; } // intercepts x
- if (Math.min(p2[1], p3[1])<=pointer[1] && pointer[1]<=Math.max(p2[1], p3[1])){
- if (pointer[0] <= (pointer[1]-p2[1])*(p3[0]-p2[0])/(p3[1]-p2[1])+p2[0]) lineCrossCount++; }
- if (Math.min(p3[1], p4[1])<=pointer[1] && pointer[1]<=Math.max(p3[1], p4[1])){
- if (pointer[0] <= (pointer[1]-p3[1])*(p4[0]-p3[0])/(p4[1]-p3[1])+p3[0]) lineCrossCount++; }
- if (Math.min(p4[1], p1[1])<=pointer[1] && pointer[1]<=Math.max(p4[1], p1[1])){
- if (pointer[0] <= (pointer[1]-p4[1])*(p1[0]-p4[0])/(p1[1]-p4[1])+p4[0]) lineCrossCount++; }
- if (lineCrossCount % 2 != 0) { // inside the slice of it crosses an odd number of edges
- var showSelected = this.byCategory? pieDataSet[index] : this._piechartData[index];
- if (changeSelectedVariables){
- // for when a bar is selected - not just hovered over
- sameAsCurrent = (this._currSelected)?
- (showSelected[Object.keys(showSelected)[0]]==this._currSelected![Object.keys(showSelected)[0]]
- && showSelected[Object.keys(showSelected)[1]]==this._currSelected![Object.keys(showSelected)[1]])
- :
- this._currSelected===showSelected;
- this._currSelected = sameAsCurrent? undefined: showSelected;
- this.selectedData = sameAsCurrent? undefined: d;
- }
- else this.hoverOverData = d;
- return true;
- }
- return false;
- });
- if (changeSelectedVariables){
- if (sameAsCurrent!) this.curSliceSelected = undefined;
- else this.curSliceSelected = selected;
+ // draw an imaginary horizontal line from the pointer to see how many times it crosses a slice edge
+ var lineCrossCount = 0;
+ // if for all 4 lines
+ if (Math.min(p1[1], p2[1]) <= pointer[1] && pointer[1] <= Math.max(p1[1], p2[1])) {
+ // within y bounds
+ if (pointer[0] <= ((pointer[1] - p1[1]) * (p2[0] - p1[0])) / (p2[1] - p1[1]) + p1[0]) lineCrossCount++;
+ } // intercepts x
+ if (Math.min(p2[1], p3[1]) <= pointer[1] && pointer[1] <= Math.max(p2[1], p3[1])) {
+ if (pointer[0] <= ((pointer[1] - p2[1]) * (p3[0] - p2[0])) / (p3[1] - p2[1]) + p2[0]) lineCrossCount++;
}
- }
+ if (Math.min(p3[1], p4[1]) <= pointer[1] && pointer[1] <= Math.max(p3[1], p4[1])) {
+ if (pointer[0] <= ((pointer[1] - p3[1]) * (p4[0] - p3[0])) / (p4[1] - p3[1]) + p3[0]) lineCrossCount++;
+ }
+ if (Math.min(p4[1], p1[1]) <= pointer[1] && pointer[1] <= Math.max(p4[1], p1[1])) {
+ if (pointer[0] <= ((pointer[1] - p4[1]) * (p1[0] - p4[0])) / (p1[1] - p4[1]) + p4[0]) lineCrossCount++;
+ }
+ if (lineCrossCount % 2 != 0) {
+ // inside the slice of it crosses an odd number of edges
+ var showSelected = this.byCategory ? pieDataSet[index] : this._piechartData[index];
+ if (changeSelectedVariables) {
+ // for when a bar is selected - not just hovered over
+ sameAsCurrent = this._currSelected
+ ? showSelected[Object.keys(showSelected)[0]] == this._currSelected![Object.keys(showSelected)[0]] && showSelected[Object.keys(showSelected)[1]] == this._currSelected![Object.keys(showSelected)[1]]
+ : this._currSelected === showSelected;
+ this._currSelected = sameAsCurrent ? undefined : showSelected;
+ this.selectedData = sameAsCurrent ? undefined : d;
+ } else this.hoverOverData = d;
+ return true;
+ }
+ return false;
+ });
+ if (changeSelectedVariables) {
+ if (sameAsCurrent!) this.curSliceSelected = undefined;
+ else this.curSliceSelected = selected;
+ }
+ };
// draws the pie chart
drawChart = (dataSet: any, width: number, height: number) => {
d3.select(this._piechartRef.current).select('svg').remove();
d3.select(this._piechartRef.current).select('.tooltip').remove();
-
- var percentField = Object.keys(dataSet[0])[0]
- var descriptionField = Object.keys(dataSet[0])[1]!
- var radius = Math.min(width, height-this.props.margin.top-this.props.margin.bottom) /2
+
+ var percentField = Object.keys(dataSet[0])[0];
+ var descriptionField = Object.keys(dataSet[0])[1]!;
+ var radius = Math.min(width, height - this.props.margin.top - this.props.margin.bottom) / 2;
// converts data into Objects
var data = this.data(dataSet);
- var pieDataSet = dataSet.filter((d: { [x: string]: unknown; }) => {
+ var pieDataSet = 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;
});
- if (this.byCategory){
- let uniqueCategories = [...new Set(data)]
+ if (this.byCategory) {
+ let uniqueCategories = [...new Set(data)];
var pieStringDataSet: { frequency: number }[] = [];
- for (let i=0; i<uniqueCategories.length; i++){
- pieStringDataSet.push({frequency: 0, [percentField]: uniqueCategories[i]})
+ for (let i = 0; i < uniqueCategories.length; i++) {
+ pieStringDataSet.push({ frequency: 0, [percentField]: uniqueCategories[i] });
}
- for (let i=0; i<data.length; i++){
- let sliceData = pieStringDataSet.filter((each: any) => each[percentField]==data[i])
+ for (let i = 0; i < data.length; i++) {
+ let sliceData = pieStringDataSet.filter((each: any) => each[percentField] == data[i]);
sliceData[0].frequency = sliceData[0].frequency + 1;
}
- pieDataSet = pieStringDataSet
- percentField = Object.keys(pieDataSet[0])[0]
- descriptionField = Object.keys(pieDataSet[0])[1]!
- data = this.data(pieStringDataSet)
+ pieDataSet = pieStringDataSet;
+ percentField = Object.keys(pieDataSet[0])[0];
+ descriptionField = Object.keys(pieDataSet[0])[1]!;
+ data = this.data(pieStringDataSet);
}
- var trackDuplicates : {[key: string]: any} = {};
- data.forEach((eachData: any) => !trackDuplicates[eachData]? trackDuplicates[eachData] = 0: null)
+ var trackDuplicates: { [key: string]: any } = {};
+ data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null));
// initial chart
var svg = (this._piechartSvg = d3
.select(this._piechartRef.current)
- .append("svg")
- .attr("class", "graph")
- .attr("width", width + this.props.margin.right + this.props.margin.left)
- .attr("height", height + this.props.margin.top + this.props.margin.bottom)
- .append("g"));
- let g = svg.append("g")
- .attr("transform",
- "translate(" + (width/2 + this.props.margin.left) + "," + height/2 + ")");
+ .append('svg')
+ .attr('class', 'graph')
+ .attr('width', width + this.props.margin.right + this.props.margin.left)
+ .attr('height', height + this.props.margin.top + this.props.margin.bottom)
+ .append('g'));
+ let g = svg.append('g').attr('transform', 'translate(' + (width / 2 + this.props.margin.left) + ',' + height / 2 + ')');
var pie = d3.pie();
- var arc = d3.arc()
- .innerRadius(0)
- .outerRadius(radius);
+ var arc = d3.arc().innerRadius(0).outerRadius(radius);
// click/hover
const onPointClick = action((e: any) => this.highlightSelectedSlice(true, svg, arc, radius, d3.pointer(e), pieDataSet));
const onHover = action((e: any) => {
- const selected = this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet)
+ const selected = this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet);
updateHighlights();
- })
+ });
const mouseOut = action((e: any) => {
this.hoverOverData = undefined;
updateHighlights();
- })
+ });
const updateHighlights = () => {
const hoverOverSlice = this.hoverOverData;
const selectedData = this.selectedData;
- svg.selectAll('path').attr("class", function(d: any) {
- return ((selectedData && d.startAngle==selectedData.startAngle && d.endAngle==selectedData.endAngle)
- || ((hoverOverSlice && d.startAngle==hoverOverSlice.startAngle && d.endAngle==hoverOverSlice.endAngle)))? 'slice hover' : 'slice'; })
- }
+ svg.selectAll('path').attr('class', function (d: any) {
+ return (selectedData && d.startAngle == selectedData.startAngle && d.endAngle == selectedData.endAngle) || (hoverOverSlice && d.startAngle == hoverOverSlice.startAngle && d.endAngle == hoverOverSlice.endAngle)
+ ? 'slice hover'
+ : 'slice';
+ });
+ };
// drawing the slices
var selected = this.selectedData;
- var arcs = g.selectAll("arc")
- .data(pie(data))
- .enter()
- .append("g")
- arcs.append("path")
- .attr("fill", (d, i)=>{
- var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number; }; }) => {
+ var arcs = g.selectAll('arc').data(pie(data)).enter().append('g');
+ arcs.append('path')
+ .attr('fill', (d, i) => {
+ var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number } }) => {
try {
- return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))==Number(d.data)
+ return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) == Number(d.data);
} catch (error) {
- return each[percentField]==d.data
- }})
+ return each[percentField] == d.data;
+ }
+ });
var dataPoint;
- if (possibleDataPoints.length==1) dataPoint = possibleDataPoints[0];
- else{
- dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]
+ if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0];
+ else {
+ dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]];
trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1;
}
var sliceColor;
- if (dataPoint){
+ if (dataPoint) {
var accessByName = dataPoint[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
var sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::'));
- sliceColors.map(each => {if (each[0]==StrCast(accessByName)) sliceColor = each[1]});
+ sliceColors.map(each => {
+ if (each[0] == StrCast(accessByName)) sliceColor = each[1];
+ });
}
- return sliceColor? StrCast(sliceColor) : d3.schemeSet3[i]? d3.schemeSet3[i]: d3.schemeSet3[i%(d3.schemeSet3.length)] })
- .attr("class", selected?
- function(d) {
- return (selected && d.startAngle==selected.startAngle && d.endAngle==selected.endAngle)? 'slice hover' : 'slice';
- }: function(d) {return 'slice'})
+ return sliceColor ? StrCast(sliceColor) : d3.schemeSet3[i] ? d3.schemeSet3[i] : d3.schemeSet3[i % d3.schemeSet3.length];
+ })
+ .attr(
+ 'class',
+ selected
+ ? function (d) {
+ return selected && d.startAngle == selected.startAngle && d.endAngle == selected.endAngle ? 'slice hover' : 'slice';
+ }
+ : function (d) {
+ return 'slice';
+ }
+ )
.attr('d', arc)
.on('click', onPointClick)
.on('mouseover', onHover)
@@ -282,99 +294,106 @@ export class PieChart extends React.Component<PieChartProps> {
// adding labels
trackDuplicates = {};
- data.forEach((eachData: any) => !trackDuplicates[eachData]? trackDuplicates[eachData] = 0: null)
- arcs.append("text")
- .attr("transform",function(d){
- var centroid = arc.centroid(d as unknown as d3.DefaultArcObject)
- var heightOffset = (centroid[1]/radius) * Math.abs(centroid[1])
- return "translate("+ (centroid[0]+centroid[0]/(radius*.02)) + "," + (centroid[1]+heightOffset) + ")"; })
- .attr("text-anchor", "middle")
- .text(function(d){
- var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number; }; }) => {
+ data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null));
+ arcs.append('text')
+ .attr('transform', function (d) {
+ var centroid = arc.centroid(d as unknown as d3.DefaultArcObject);
+ var heightOffset = (centroid[1] / radius) * Math.abs(centroid[1]);
+ return 'translate(' + (centroid[0] + centroid[0] / (radius * 0.02)) + ',' + (centroid[1] + heightOffset) + ')';
+ })
+ .attr('text-anchor', 'middle')
+ .text(function (d) {
+ var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number } }) => {
try {
- return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))==Number(d.data)
+ return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) == Number(d.data);
} catch (error) {
- return each[percentField]==d.data
- }})
+ return each[percentField] == d.data;
+ }
+ });
var dataPoint;
- if (possibleDataPoints.length==1) dataPoint = possibleDataPoints[0];
- else{
- dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]]
+ if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0];
+ else {
+ dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]];
trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1;
}
- return dataPoint? dataPoint[percentField]! + (!descriptionField? '' : (' - ' + dataPoint[descriptionField]))! : ''})
+ return dataPoint ? dataPoint[percentField]! + (!descriptionField ? '' : ' - ' + dataPoint[descriptionField])! : '';
+ });
};
@action changeSelectedColor = (color: string) => {
- this.curSliceSelected.attr("fill", color);
- var sliceName = this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')
-
- const sliceColors = Cast(this.props.layoutDoc.pieSliceColors, listSpec("string"), null);
- sliceColors.map(each => { if (each.split('::')[0] == sliceName) sliceColors.splice(sliceColors.indexOf(each), 1) });
+ this.curSliceSelected.attr('fill', color);
+ var sliceName = this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '');
+
+ const sliceColors = Cast(this.props.layoutDoc.pieSliceColors, listSpec('string'), null);
+ sliceColors.map(each => {
+ if (each.split('::')[0] == sliceName) sliceColors.splice(sliceColors.indexOf(each), 1);
+ });
sliceColors.push(StrCast(sliceName + '::' + color));
};
render() {
this.componentDidMount();
- var titleAccessor: any='';
- if (this.props.axes.length==2) titleAccessor = 'pieChart-title-'+this.props.axes[0]+'-'+this.props.axes[1];
- else if (this.props.axes.length>0) titleAccessor = 'pieChart-title-'+this.props.axes[0];
+ var titleAccessor: any = '';
+ if (this.props.axes.length == 2) titleAccessor = 'dataViz_title_pieChart_' + this.props.axes[0] + '-' + this.props.axes[1];
+ else if (this.props.axes.length > 0) titleAccessor = 'dataViz_title_pieChart_' + this.props.axes[0];
if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
if (!this.props.layoutDoc.pieSliceColors) this.props.layoutDoc.pieSliceColors = new List<string>();
var selected: string;
- var curSelectedSliceName;
- if (this._currSelected){
- curSelectedSliceName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''))
+ var curSelectedSliceName = '';
+ if (this._currSelected) {
+ curSelectedSliceName = 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);
+ key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : '';
+ });
+ selected = selected.substring(0, selected.length - 2);
selected += ' }';
- }
- else selected = 'none';
+ } else selected = 'none';
var selectedSliceColor;
var sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::'));
- sliceColors.map(each => {if (each[0]==curSelectedSliceName!) selectedSliceColor = each[1]});
+ sliceColors.map(each => {
+ if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1];
+ });
if (this._piechartData.length>0 || (!this.incomingLinks || this.incomingLinks.length==0)){
- return (
- this.props.axes.length >= 1 ? (
+ return this.props.axes.length >= 1 ? (
<div className="chart-container">
- <div className="graph-title">
+ <div className="graph-title">
<EditableText
val={StrCast(this.props.layoutDoc[titleAccessor])}
- setVal={undoable (action(val => this.props.layoutDoc[titleAccessor] = val as string), "Change Graph Title")}
- color={"black"}
+ setVal={undoable(
+ action(val => (this.props.layoutDoc[titleAccessor] = val as string)),
+ 'Change Graph Title'
+ )}
+ color={'black'}
size={Size.LARGE}
fillWidth
/>
</div>
<div ref={this._piechartRef} />
- {selected != 'none' ?
+ {selected != 'none' ? (
<div className={'selected-data'}>
Selected: {selected}
&nbsp; &nbsp;
<ColorPicker
- tooltip={'Change Slice Color'}
- type={Type.SEC}
- icon={<FaFillDrip/>}
- selectedColor={selectedSliceColor? selectedSliceColor : this.curSliceSelected.attr("fill")}
- setSelectedColor={undoable (color => this.changeSelectedColor(color), "Change Selected Slice Color")}
+ tooltip={'Change Slice Color'}
+ type={Type.SEC}
+ icon={<FaFillDrip />}
+ selectedColor={selectedSliceColor ? selectedSliceColor : this.curSliceSelected.attr('fill')}
+ setFinalColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Slice Color')}
+ setSelectedColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Slice Color')}
size={Size.XSMALL}
/>
</div>
- : null}
+ ) : null}
</div>
- ) : <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+ ) : (
+ <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>
);
- }
- 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>
- )
}
-
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 7b3e700ac..edec72cc5 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -1,4 +1,4 @@
-import { action, computed, } from 'mobx';
+import { action, computed } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, Field, StrListCast } from '../../../../../fields/Doc';
@@ -31,44 +31,45 @@ interface TableBoxProps {
@observer
export class TableBox extends React.Component<TableBoxProps> {
-
// filters all data to just display selected data if brushed (created from an incoming link)
@computed get _tableData() {
if (this.incomingLinks.length! <= 0) return this.props.pairs;
- var guids = StrListCast(this.props.layoutDoc.rowGuids);
- return this.props.pairs?.filter(pair => this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(guids[this.props.pairs.indexOf(pair)]))
+ var guids = StrListCast(this.props.layoutDoc.dataViz_rowGuids);
+ return this.props.pairs?.filter(pair => this.incomingLinks[0]!.dataViz_selectedRows && StrListCast(this.incomingLinks[0].dataViz_selectedRows).includes(guids[this.props.pairs.indexOf(pair)]));
}
@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
+ 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 columns() {
- if (!this.props.layoutDoc.rowGuids) this.props.layoutDoc.rowGuids = new List<string>();
- const guids = Cast(this.props.layoutDoc.rowGuids, listSpec("string"), null);
- if (guids.length==0) this.props.pairs.map(row => guids.push(Utils.GenerateGuid()));
- return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header!='' && header!=undefined) : [];
+ if (!this.props.layoutDoc.dataViz_rowGuids) this.props.layoutDoc.dataViz_rowGuids = new List<string>();
+ const guids = Cast(this.props.layoutDoc.dataViz_rowGuids, listSpec('string'), null);
+ if (guids.length == 0) this.props.pairs.map(row => guids.push(Utils.GenerateGuid()));
+ return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header != '' && header != undefined) : [];
}
// updates the 'selected' field to no longer include rows that aren't in the table
filterSelectedRowsDown() {
- if (!this.props.layoutDoc.selected) this.props.layoutDoc.selected = new List<string>();
- const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null);
- const incomingSelected = this.incomingLinks.length? StrListCast(this.incomingLinks[0].selected) : undefined;
- if (incomingSelected){
+ if (!this.props.layoutDoc.dataViz_selectedRows) this.props.layoutDoc.dataViz_selectedRows = new List<string>();
+ const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('string'), null);
+ const incomingSelected = this.incomingLinks.length ? StrListCast(this.incomingLinks[0].dataViz_selectedRows) : undefined;
+ if (incomingSelected) {
selected.map(guid => {
- if (!incomingSelected.includes(guid)) selected.splice(selected.indexOf(guid), 1)}); // filters through selected to remove guids that were removed in the incoming data
+ if (!incomingSelected.includes(guid)) selected.splice(selected.indexOf(guid), 1);
+ }); // filters through selected to remove guids that were removed in the incoming data
}
}
render() {
this.filterSelectedRowsDown();
- if (this._tableData.length>0){
+ if (this._tableData.length > 0) {
return (
- <div className="table-container" style={{height: this.props.height}}>
+ <div className="table-container" style={{ height: this.props.height }}>
<table className="table">
<thead>
<tr className="table-row">
@@ -83,7 +84,8 @@ export class TableBox extends React.Component<TableBoxProps> {
style={{
color: this.props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this.props.axes.lastElement() === col ? 'darkred' : undefined,
background: this.props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this.props.axes.lastElement() === col ? '#Fbdbdb' : undefined,
- fontWeight: 'bolder', border: '3px solid black'
+ fontWeight: 'bolder',
+ border: '3px solid black',
}}
onPointerDown={e => {
const downX = e.clientX;
@@ -91,12 +93,13 @@ export class TableBox extends React.Component<TableBoxProps> {
setupMoveUpEvents(
{},
e,
- e => { // dragging off a column to create a brushed DataVizBox
+ e => {
+ // dragging off a column to create a brushed DataVizBox
const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!;
const targetCreator = (annotationOn: Doc | undefined) => {
const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!);
- embedding._dataVizView = DataVizView.TABLE;
- embedding._data_vizAxes = new List<string>([col, col]);
+ embedding._dataViz = DataVizView.TABLE;
+ embedding._dataViz_axes = new List<string>([col, col]);
embedding._draggedFrom = this.props.docView?.()!.rootDoc!;
embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!;
embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors);
@@ -142,17 +145,24 @@ export class TableBox extends React.Component<TableBoxProps> {
<tbody>
{this._tableData?.map((p, i) => {
var containsData = false;
- var guid = StrListCast(this.props.layoutDoc.rowGuids)![this.props.pairs.indexOf(p)]
- this.columns.map(col => {if (p[col]!='' && p[col]!=null && p[col]!=undefined) containsData = true})
- if (containsData){
+ var guid = StrListCast(this.props.layoutDoc.dataViz_rowGuids)![this.props.pairs.indexOf(p)];
+ this.columns.map(col => {
+ if (p[col] != '' && p[col] != null && p[col] != undefined) containsData = true;
+ });
+ if (containsData) {
return (
- <tr key={i} className="table-row" onClick={action(e => {
+ <tr
+ key={i}
+ className="table-row"
+ onClick={action(e => {
// selecting a row
- const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null);
+ const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('string'), null);
if (selected.includes(guid)) selected.splice(selected.indexOf(guid), 1);
else {
- selected.push(guid)};
- })} style={{ background: StrListCast(this.props.layoutDoc.selected).includes(guid) ? 'lightgrey' : '', width: '110%' }}>
+ selected.push(guid);
+ }
+ })}
+ style={{ background: StrListCast(this.props.layoutDoc.dataViz_selectedRows).includes(guid) ? 'lightgrey' : '', width: '110%' }}>
{this.columns.map(col => {
// each cell
var colSelected = this.props.axes.length>1? (this.props.axes[0]==col || this.props.axes[1]==col) : this.props.axes.length>0? this.props.axes[0]==col : false;
@@ -160,7 +170,8 @@ export class TableBox extends React.Component<TableBoxProps> {
<td key={this.columns.indexOf(col)} style={{border: colSelected? '3px solid black' : '1px solid black', fontWeight: colSelected? 'bolder' : 'normal'}}>
{p[col]}
</td>
- )})}
+ );
+ })}
</tr>
);
}
@@ -169,12 +180,10 @@ export class TableBox extends React.Component<TableBoxProps> {
</table>
</div>
);
- }
- 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>
- )
+ } 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>
+ );
}
}