From 65ab8c985a86f18ebb51a269f20d7865dcfc6589 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Tue, 18 Jul 2023 10:50:16 -0400 Subject: click on histograms --- src/client/views/nodes/DataVizBox/components/TableBox.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index d84e34d52..8c8264861 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -33,6 +33,7 @@ export class TableBox extends React.Component { const header = React.createRef(); return ( { {this.props.pairs?.map((p, i) => { return ( - (p['select' + this.props.docView?.()?.rootDoc![Id]] = !p['select' + this.props.docView?.()?.rootDoc![Id]]))}> + (p['select' + this.props.docView?.()?.rootDoc![Id]] = !p['select' + this.props.docView?.()?.rootDoc![Id]]))}> {this.columns.map(col => ( - {p[col]} + {p[col]} ))} ); -- cgit v1.2.3-70-g09d2 From 900efdb5c08397e53e1d00555fc6eac208eff5a1 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Tue, 18 Jul 2023 16:24:57 -0400 Subject: brushed graph has can have multiple links + still link to the correct graph for data --- src/client/views/nodes/DataVizBox/DataVizBox.scss | 1 + src/client/views/nodes/DataVizBox/components/LineChart.tsx | 4 ++-- src/client/views/nodes/DataVizBox/components/TableBox.tsx | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.scss b/src/client/views/nodes/DataVizBox/DataVizBox.scss index b3cbc89aa..5e7230271 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.scss +++ b/src/client/views/nodes/DataVizBox/DataVizBox.scss @@ -1,6 +1,7 @@ .dataviz { overflow: hidden; height: 100%; + width: 100%; .datatype-button{ margin: 0; diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index cb6ba6fe7..637ed0ead 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -1,7 +1,6 @@ import { action, computed, IReactionDisposer, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -// import d3 import * as d3 from 'd3'; import { Doc, DocListCast } from '../../../../../fields/Doc'; import { Id } from '../../../../../fields/FieldSymbols'; @@ -56,7 +55,8 @@ export class LineChart extends React.Component { } @computed get incomingLinks() { return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - .filter(link => link.link_anchor_1 !== this.props.rootDoc) // get links where this chart doc is the target of the link + .filter(link => { + return link.link_anchor_1 == this.props.rootDoc.draggedFrom}) // get links where this chart doc is the target of the link .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @computed get incomingSelected() { diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 8c8264861..ff43f67d9 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -51,6 +51,7 @@ export class TableBox extends React.Component { const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!); embedding._dataVizView = DataVizView.LINECHART; embedding._data_vizAxes = new List([col, col]); + embedding._draggedFrom = this.props.docView?.()!.rootDoc!; embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!; return embedding; }; -- cgit v1.2.3-70-g09d2 From 27441b60e7af8ea93f277ab3bdc2990ad25ea326 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 19 Jul 2023 19:03:59 -0400 Subject: fixed showing of link lines for data visualization views. --- src/client/views/nodes/DataVizBox/components/TableBox.tsx | 5 ++++- src/client/views/nodes/DocumentView.tsx | 8 ++++---- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index ff43f67d9..ca888e13f 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -60,6 +60,7 @@ export class TableBox extends React.Component { dragComplete: e => { if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { e.linkDocument.link_displayLine = true; + e.linkDocument.link_matchEmbeddings = true; // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; // e.annoDragData.linkSourceDoc.followLinkZoom = false; } @@ -94,7 +95,9 @@ export class TableBox extends React.Component { return ( (p['select' + this.props.docView?.()?.rootDoc![Id]] = !p['select' + this.props.docView?.()?.rootDoc![Id]]))}> {this.columns.map(col => ( - {p[col]} + + {p[col]} + ))} ); diff --git a/src/client/views/nodes/DocumentView.tsx b/src/client/views/nodes/DocumentView.tsx index d7b4ca317..4211b8a81 100644 --- a/src/client/views/nodes/DocumentView.tsx +++ b/src/client/views/nodes/DocumentView.tsx @@ -975,8 +975,8 @@ export class DocumentViewInternal extends DocComponent - Doc.AreProtosEqual(link.link_anchor_1 as Doc, this.rootDoc) || - Doc.AreProtosEqual(link.link_anchor_2 as Doc, this.rootDoc) || + (link.link_matchEmbeddings ? link.link_anchor_1 === this.rootDoc : Doc.AreProtosEqual(link.link_anchor_1 as Doc, this.rootDoc)) || + (link.link_matchEmbeddings ? link.link_anchor_2 === this.rootDoc : Doc.AreProtosEqual(link.link_anchor_2 as Doc, this.rootDoc)) || ((link.link_anchor_1 as Doc)?.layout_unrendered && Doc.AreProtosEqual((link.link_anchor_1 as Doc)?.annotationOn as Doc, this.rootDoc)) || ((link.link_anchor_2 as Doc)?.layout_unrendered && Doc.AreProtosEqual((link.link_anchor_2 as Doc)?.annotationOn as Doc, this.rootDoc)) ); @@ -1237,7 +1237,7 @@ export class DocumentViewInternal extends DocComponent { return this.docView?._componentView; } get allLinks() { - return this.docView?.allLinks || []; + return (this.docView?.allLinks || []).filter(link => !link.link_matchEmbeddings || link.link_anchor_1 === this.rootDoc || link.link_anchor_2 === this.rootDoc); } get LayoutFieldKey() { return this.docView?.LayoutFieldKey || 'layout'; -- cgit v1.2.3-70-g09d2 From 371f0309ec6b10a86b4a456d233be5b53cf93356 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Thu, 20 Jul 2023 17:58:24 -0400 Subject: components + links + brushed TableBox --- src/client/views/nodes/DataVizBox/DataVizBox.scss | 4 ++-- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 26 +++++++++++++++++----- .../nodes/DataVizBox/components/Histogram.tsx | 3 ++- .../views/nodes/DataVizBox/components/PieChart.tsx | 4 ++-- .../views/nodes/DataVizBox/components/TableBox.tsx | 22 ++++++++++++++++-- 5 files changed, 46 insertions(+), 13 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.scss b/src/client/views/nodes/DataVizBox/DataVizBox.scss index 5e7230271..32c0bbfc1 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.scss +++ b/src/client/views/nodes/DataVizBox/DataVizBox.scss @@ -4,7 +4,7 @@ width: 100%; .datatype-button{ - margin: 0; - border: 1px solid white; + display: flex; + flex-direction: row; } } diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 25098baf1..4ddebb833 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -14,6 +14,7 @@ import { TableBox } from './components/TableBox'; import './DataVizBox.scss'; import { Histogram } from './components/Histogram'; import { PieChart } from './components/PieChart'; +import { Toggle, ToggleType } from 'browndash-components'; export enum DataVizView { TABLE = 'table', @@ -103,7 +104,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { if (!this.pairs) return 'no data'; // prettier-ignore switch (this.dataVizView) { - case DataVizView.TABLE: return ; + case DataVizView.TABLE: return ; case DataVizView.LINECHART: return (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />; case DataVizView.HISTOGRAM: return ; case DataVizView.PIECHART: return ; @@ -150,11 +151,24 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { { passive: false } ) }> - {/* */} - - - - +
+ this.layoutDoc._dataVizView = DataVizView.TABLE} + toggleStatus={this.layoutDoc._dataVizView == DataVizView.TABLE} + /> + this.layoutDoc._dataVizView = DataVizView.LINECHART} + toggleStatus={this.layoutDoc._dataVizView == DataVizView.LINECHART} + /> + this.layoutDoc._dataVizView = DataVizView.HISTOGRAM} + toggleStatus={this.layoutDoc._dataVizView == DataVizView.HISTOGRAM} + /> + this.layoutDoc._dataVizView = DataVizView.PIECHART} + toggleStatus={this.layoutDoc._dataVizView == DataVizView.PIECHART} + /> +
{this.selectView} ); diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index b8be9bd13..01e6709fa 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -65,7 +65,8 @@ export class Histogram extends React.Component { } @computed get incomingLinks() { return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - .filter(link => link.link_anchor_1 !== this.props.rootDoc) // get links where this chart doc is the target of the link + .filter(link => { + return link.link_anchor_1 == this.props.rootDoc.draggedFrom}) // get links where this chart doc is the target of the link .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @computed get incomingSelected() { diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 6241e6221..05a2f1588 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -65,7 +65,8 @@ export class PieChart extends React.Component { } @computed get incomingLinks() { return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links - .filter(link => link.link_anchor_1 !== this.props.rootDoc) // get links where this chart doc is the target of the link + .filter(link => { + return link.link_anchor_1 == this.props.rootDoc.draggedFrom}) // get links where this chart doc is the target of the link .map(link => DocCast(link.link_anchor_1)); // then return the source of the link } @computed get incomingSelected() { @@ -359,7 +360,6 @@ export class PieChart extends React.Component { }; render() { - var selected: string; if (this._currSelected){ selected = '{ '; diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index ca888e13f..500e7b639 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -8,8 +8,11 @@ import { emptyFunction, returnFalse, setupMoveUpEvents, Utils } from '../../../. import { DragManager } from '../../../../util/DragManager'; import { DocumentView } from '../../DocumentView'; import { DataVizView } from '../DataVizBox'; +import { LinkManager } from '../../../../util/LinkManager'; +import { DocCast } from '../../../../../fields/Types'; interface TableBoxProps { + rootDoc: Doc; pairs: { [key: string]: any }[]; selectAxes: (axes: string[]) => void; axes: string[]; @@ -18,9 +21,24 @@ interface TableBoxProps { @observer export class TableBox extends React.Component { + + @computed get _tableData() { + if (this.incomingLinks.length! <= 0) return this.props.pairs; + return this.props.pairs?.filter(pair => (Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select')))) + } + + @computed get incomingLinks() { + return LinkManager.Instance.getAllRelatedLinks(this.props.rootDoc) // out of all links + .filter(link => { + return link.link_anchor_1 == this.props.rootDoc.draggedFrom}) // get links where this chart doc is the target of the link + .map(link => DocCast(link.link_anchor_1)); // then return the source of the link + } + @computed get columns() { - return this.props.pairs.length ? Array.from(Object.keys(this.props.pairs[0])) : []; + // return this.props.pairs.length ? Array.from(Object.keys(this.props.pairs[0])) : []; + return this._tableData.length ? Array.from(Object.keys(this._tableData[0])) : []; } + render() { return (
@@ -91,7 +109,7 @@ export class TableBox extends React.Component { - {this.props.pairs?.map((p, i) => { + {this._tableData?.map((p, i) => { return ( (p['select' + this.props.docView?.()?.rootDoc![Id]] = !p['select' + this.props.docView?.()?.rootDoc![Id]]))}> {this.columns.map(col => ( -- cgit v1.2.3-70-g09d2 From 710cb3aa93ea30799479ca7c79444f05aeab2209 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Mon, 31 Jul 2023 18:49:44 -0400 Subject: TableBox editable headers start --- .../nodes/DataVizBox/components/Histogram.tsx | 23 ++--- .../views/nodes/DataVizBox/components/PieChart.tsx | 24 ++--- .../views/nodes/DataVizBox/components/TableBox.tsx | 108 ++++++++++++++++++++- 3 files changed, 125 insertions(+), 30 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index 6d0a8bf75..64e61fca8 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -443,21 +443,8 @@ export class Histogram extends React.Component { this.defaultBarColor = color; }; - @computed get editableTitle() { - const title = this.graphTitle; - return ( - this.graphTitle = val as string)} - color={"black"} - size={Size.LARGE} - fillWidth - /> - ); - } - render() { - + const title = this.graphTitle; var selected: string; if (this._currSelected){ selected = '{ '; @@ -472,7 +459,13 @@ export class Histogram extends React.Component { this.props.axes.length >= 1 ? (
- {this.editableTitle} + this.graphTitle = val as string)} + color={"black"} + size={Size.LARGE} + fillWidth + />     { this.sliceColors[this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ this.graphTitle = val as string)} - color={"black"} - size={Size.LARGE} - fillWidth - /> - ); - } - render() { + const title = this.graphTitle; var selected: string; if (this._currSelected){ selected = '{ '; @@ -409,7 +397,15 @@ export class PieChart extends React.Component { return ( this.props.axes.length >= 1 ? (
-
{this.editableTitle}
+
+ this.graphTitle = val as string)} + color={"black"} + size={Size.LARGE} + fillWidth + /> +
{selected != 'none' ?
Selected: {selected} diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 500e7b639..aaedba202 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, observable } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc } from '../../../../../fields/Doc'; @@ -10,6 +10,7 @@ import { DocumentView } from '../../DocumentView'; import { DataVizView } from '../DataVizBox'; import { LinkManager } from '../../../../util/LinkManager'; import { DocCast } from '../../../../../fields/Types'; +import { EditableText, Size, Type } from 'browndash-components'; interface TableBoxProps { rootDoc: Doc; @@ -21,6 +22,8 @@ interface TableBoxProps { @observer export class TableBox extends React.Component { + @observable editableHeaders = this.columns; + @observable editableCells = this._tableData; @computed get _tableData() { if (this.incomingLinks.length! <= 0) return this.props.pairs; @@ -39,6 +42,106 @@ export class TableBox extends React.Component { return this._tableData.length ? Array.from(Object.keys(this._tableData[0])) : []; } + // render() { + // return ( + //
+ // + // + // + // {this.editableHeaders + // .filter(col => !col.startsWith('select')) + // .map(col => { + // const header = React.createRef(); + // const displayColName = col; + // return ( + // + // ); + // })} + // + // + // + // {this._tableData?.map((p, i) => { + // return ( + // (p['select' + this.props.docView?.()?.rootDoc![Id]] = !p['select' + this.props.docView?.()?.rootDoc![Id]]))}> + // {this.editableHeaders.map(col => ( + // + // ))} + // + // ); + // })} + // + //
{ + // const downX = e.clientX; + // const downY = e.clientY; + // setupMoveUpEvents( + // {}, + // e, + // e => { + // const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!; + // const targetCreator = (annotationOn: Doc | undefined) => { + // const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!); + // embedding._dataVizView = DataVizView.LINECHART; + // embedding._data_vizAxes = new List([col, col]); + // embedding._draggedFrom = this.props.docView?.()!.rootDoc!; + // embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!; + // return embedding; + // }; + // if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { + // DragManager.StartAnchorAnnoDrag([header.current!], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { + // dragComplete: e => { + // if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { + // e.linkDocument.link_displayLine = true; + // e.linkDocument.link_matchEmbeddings = true; + // // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; + // // e.annoDragData.linkSourceDoc.followLinkZoom = false; + // } + // }, + // }); + // return true; + // } + // return false; + // }, + // emptyFunction, + // action(e => { + // const newAxes = this.props.axes; + // if (newAxes.includes(col)) { + // newAxes.splice(newAxes.indexOf(col), 1); + // } else if (newAxes.length >= 1) { + // newAxes[1] = col; + // } else { + // newAxes[0] = col; + // } + // this.props.selectAxes(newAxes); + // }) + // ); + // }}> + // { + // this.editableHeaders[this.editableHeaders.indexOf(col)] = val as string + // this.props.pairs.map(pair => { + // pair[val as string] = pair[col]; + // delete pair[col] + // }) + // })} + // color={"black"} + // size={Size.LARGE} + // /> + //
+ // {p[col]} + //
+ //
+ // ); + // } + + render() { return (
@@ -125,4 +228,7 @@ export class TableBox extends React.Component {
); } + + + } -- cgit v1.2.3-70-g09d2 From 591533a40c847f84e23428ab757b8822edbc2a61 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Tue, 1 Aug 2023 17:41:09 -0400 Subject: things save: editable title for all 3 + color for histogram --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 42 ++++++---------------- .../views/nodes/DataVizBox/components/Chart.scss | 3 ++ .../nodes/DataVizBox/components/Histogram.tsx | 37 ++++++++++--------- .../nodes/DataVizBox/components/LineChart.tsx | 28 +++++++++++++-- .../views/nodes/DataVizBox/components/PieChart.tsx | 20 ++++++----- .../views/nodes/DataVizBox/components/TableBox.tsx | 26 +++++++++++--- 6 files changed, 95 insertions(+), 61 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 12aa2ae34..710c049a2 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -14,7 +14,7 @@ import { TableBox } from './components/TableBox'; import './DataVizBox.scss'; import { Histogram } from './components/Histogram'; import { PieChart } from './components/PieChart'; -import { Toggle, ToggleType } from 'browndash-components'; +import { Toggle, ToggleType, Type } from 'browndash-components'; export enum DataVizView { TABLE = 'table', @@ -25,6 +25,7 @@ export enum DataVizView { @observer export class DataVizBox extends ViewBoxAnnotatableComponent() { + public static LayoutString(fieldKey: string) { return FieldView.LayoutString(DataVizBox, fieldKey); } @@ -97,37 +98,16 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { } selectAxes = (axes: string[]) => (this.layoutDoc.data_vizAxes = new List(axes)); - @computed get table(){ - if (!this.pairs) return 'no data'; - return ; - } - @computed get lineChart(){ - const width = this.props.PanelWidth() * 0.9; - const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9; - const margin = { top: 10, right: 25, bottom: 50, left: 25 }; - if (!this.pairs) return 'no data'; - return (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />; - } - @computed get histogram(){ - const width = this.props.PanelWidth() * 0.9; - const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9; - const margin = { top: 10, right: 25, bottom: 50, left: 25 }; - if (!this.pairs) return 'no data'; - return ; - } - @computed get pieChart(){ + @computed get selectView() { const width = this.props.PanelWidth() * 0.9; const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9; const margin = { top: 10, right: 25, bottom: 50, left: 25 }; if (!this.pairs) return 'no data'; - return ; - } - @computed get selectView() { switch (this.dataVizView) { - case DataVizView.TABLE: return this.table; - case DataVizView.LINECHART: return this.lineChart; - case DataVizView.HISTOGRAM: return this.histogram; - case DataVizView.PIECHART: return this.pieChart; + case DataVizView.TABLE: return ; + case DataVizView.LINECHART: return (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />; + case DataVizView.HISTOGRAM: return ; + case DataVizView.PIECHART: return ; } } @computed get dataUrl() { @@ -176,19 +156,19 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { ) }>
- this.layoutDoc._dataVizView = DataVizView.TABLE} toggleStatus={this.layoutDoc._dataVizView == DataVizView.TABLE} /> - this.layoutDoc._dataVizView = DataVizView.LINECHART} toggleStatus={this.layoutDoc._dataVizView == DataVizView.LINECHART} /> - this.layoutDoc._dataVizView = DataVizView.HISTOGRAM} toggleStatus={this.layoutDoc._dataVizView == DataVizView.HISTOGRAM} /> - this.layoutDoc._dataVizView = DataVizView.PIECHART} toggleStatus={this.layoutDoc._dataVizView == DataVizView.PIECHART} /> diff --git a/src/client/views/nodes/DataVizBox/components/Chart.scss b/src/client/views/nodes/DataVizBox/components/Chart.scss index fc0c4cea3..6c87241b8 100644 --- a/src/client/views/nodes/DataVizBox/components/Chart.scss +++ b/src/client/views/nodes/DataVizBox/components/Chart.scss @@ -75,3 +75,6 @@ fill: red; } } +.table-container{ + overflow: scroll; +} \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index 64e61fca8..740ee6e3a 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -1,10 +1,10 @@ import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../../../fields/Doc"; +import { Doc, DocListCast, FieldResult } 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} from "../../../../../fields/Types"; +import { Cast, DocCast, StrCast} from "../../../../../fields/Types"; import { DataPoint, SelectedDataPoint } from "./LineChart"; import { DocumentManager } from "../../../../util/DocumentManager"; import { Id } from "../../../../../fields/FieldSymbols"; @@ -19,6 +19,7 @@ import { FaFillDrip } from "react-icons/fa"; export interface HistogramProps { rootDoc: Doc; + layoutDoc: Doc; axes: string[]; pairs: { [key: string]: any }[]; width: number; @@ -44,9 +45,6 @@ export class Histogram extends React.Component { private maxBins = 15; // maximum number of bins that is readable on a normal sized doc @observable _currSelected: any | undefined = undefined; private curBarSelected: any = undefined; - private barColors: any = {}; - private defaultBarColor: string = '#69b3a2'; - @observable graphTitle: string = this.defaultGraphTitle; // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates @computed get _histogramData() { @@ -75,7 +73,7 @@ export class Histogram extends React.Component { @computed get defaultGraphTitle(){ var ax0 = this.props.axes[0]; var ax1 = (this.props.axes.length>1)? this.props.axes[1] : undefined; - if (this.props.axes.length<2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1){ + if (this.props.axes.length<2 || !/\d/.test(this.props.pairs[0][ax0]) || !ax1 || !this.numericalYData){ return ax0 + " Histogram"; } else return ax1 + " by " + ax0 + " Histogram"; @@ -285,8 +283,8 @@ export class Histogram extends React.Component { histStringDataSet.push({[yAxisTitle]: 0, [xAxisTitle]: uniqueArr[i]}) } for (let i=0; i each[xAxisTitle]==data[i]) - sliceData[0][yAxisTitle] = sliceData[0][yAxisTitle] + 1; + let barData = histStringDataSet.filter(each => each[xAxisTitle]==data[i]) + barData[0][yAxisTitle] = barData[0][yAxisTitle] + 1; } } histDataSet = histStringDataSet @@ -426,27 +424,33 @@ export class Histogram extends React.Component { return height - y(d.length)}) .attr("width", eachRectWidth) .attr("class", 'histogram-bar') - .attr("fill", (d)=>{ return this.barColors[d[0]]? this.barColors[d[0]] : this.defaultBarColor}) + .attr("fill", (d)=>{ return this.props.layoutDoc['histogramBarColors-'+d[0]]? StrCast(this.props.layoutDoc['histogramBarColors-'+d[0]]) : this.props.layoutDoc.defaultHistogramColor? StrCast(this.props.layoutDoc.defaultHistogramColor): '#69b3a2'}) }; @action changeSelectedColor = (color: string) => { this.curBarSelected.attr("fill", color); - this.barColors[this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { const defaultColorBars = this._histogramSvg!.selectAll('.histogram-bar').filter((d: any) => { - if (this.barColors[d[0]]) return false; + if (this.props.layoutDoc['histogramBarColors-'+d[0]]) return false; else return true; }) defaultColorBars.attr("fill", color); - this.defaultBarColor = color; + this.props.layoutDoc.defaultHistogramColor = color; }; render() { - const title = this.graphTitle; + var curSelectedBarName; + var titleAccessor: any=''; + if (this.props.axes.length==2) titleAccessor = StrCast(this.props.layoutDoc['histogram-title-'+this.props.axes[0]+'-'+this.props.axes[1]]); + else if (this.props.axes.length>0) titleAccessor = StrCast(this.props.layoutDoc['histogram-title-'+this.props.axes[0]]); + const title = titleAccessor? titleAccessor : this.defaultGraphTitle; var selected: string; if (this._currSelected){ + curSelectedBarName = StrCast(this._currSelected![this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { key!=''? selected += key + ': ' + this._currSelected[key] + ', ': ''; @@ -461,7 +465,8 @@ export class Histogram extends React.Component {
this.graphTitle = val as string)} + setVal={action(val => {this.props.axes.length>1? this.props.layoutDoc['histogram-title-'+this.props.axes[0]+"-"+this.props.axes[1]] = val as string + : this.props.layoutDoc['histogram-title-'+this.props.axes[0]] = val as string})} color={"black"} size={Size.LARGE} fillWidth @@ -471,7 +476,7 @@ export class Histogram extends React.Component { tooltip={'Change Default Bar Color'} type={Type.SEC} icon={} - selectedColor={this.defaultBarColor} + selectedColor={this.props.layoutDoc.defaultHistogramColor? StrCast(this.props.layoutDoc.defaultHistogramColor): '#69b3a2'} setSelectedColor={color => this.changeDefaultColor(color)} size={Size.XSMALL} /> @@ -484,7 +489,7 @@ export class Histogram extends React.Component { tooltip={'Change Slice Color'} type={Type.SEC} icon={} - selectedColor={this.curBarSelected.attr("fill")} + selectedColor={this.props.layoutDoc['histogramBarColors-'+curSelectedBarName]? this.props.layoutDoc['histogramBarColors-'+curSelectedBarName] : this.curBarSelected.attr("fill")} setSelectedColor={color => this.changeSelectedColor(color)} size={Size.XSMALL} /> diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index da79df476..0142e96ad 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -6,7 +6,7 @@ import { Doc, DocListCast } from '../../../../../fields/Doc'; import { Id } from '../../../../../fields/FieldSymbols'; import { List } from '../../../../../fields/List'; import { listSpec } from '../../../../../fields/Schema'; -import { Cast, DocCast } from '../../../../../fields/Types'; +import { Cast, DocCast, StrCast } from '../../../../../fields/Types'; import { Docs } from '../../../../documents/Documents'; import { DocumentManager } from '../../../../util/DocumentManager'; import { LinkManager } from '../../../../util/LinkManager'; @@ -14,6 +14,7 @@ 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 { EditableText, Size } from 'browndash-components'; export interface DataPoint { x: number; @@ -24,6 +25,7 @@ export interface SelectedDataPoint extends DataPoint { } export interface LineChartProps { rootDoc: Doc; + layoutDoc: Doc; axes: string[]; pairs: { [key: string]: any }[]; width: number; @@ -185,6 +187,15 @@ export class LineChart extends React.Component { return this.props.width - this.props.margin.left - this.props.margin.right; } + @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"; + } + setupTooltip() { return d3 .select(this._lineChartRef.current) @@ -339,11 +350,24 @@ export class LineChart extends React.Component { } render() { + var titleAccessor:any = ''; + if (this.props.axes.length==2) titleAccessor = StrCast(this.props.layoutDoc['lineChart-title-'+this.props.axes[0]+'-'+this.props.axes[1]]); + else if (this.props.axes.length>0) titleAccessor = StrCast(this.props.layoutDoc['lineChart-title-'+this.props.axes[0]]); + const title = titleAccessor? titleAccessor : this.defaultGraphTitle; const selectedPt = this._currSelected ? `x: ${this._currSelected.x} y: ${this._currSelected.y}` : 'none'; return ( this.props.axes.length >= 2 ? (
-
{this.graphTitle}
+
+ {this.props.axes.length>1? this.props.layoutDoc['lineChart-title-'+this.props.axes[0]+"-"+this.props.axes[1]] = val as string + : this.props.layoutDoc['lineChart-title-'+this.props.axes[0]] = val as string})} + color={"black"} + size={Size.LARGE} + fillWidth + /> +
{`Selected: ${selectedPt}`}
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 47d4fb23e..f0c27866d 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -4,7 +4,7 @@ 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} from "../../../../../fields/Types"; +import { Cast, DocCast, StrCast} from "../../../../../fields/Types"; import { DataPoint, SelectedDataPoint } from "./LineChart"; import { DocumentManager } from "../../../../util/DocumentManager"; import { Id } from "../../../../../fields/FieldSymbols"; @@ -19,6 +19,7 @@ import { FaFillDrip } from "react-icons/fa"; export interface PieChartProps { rootDoc: Doc; + layoutDoc: Doc; axes: string[]; pairs: { [key: string]: any }[]; width: number; @@ -42,8 +43,6 @@ export class PieChart extends React.Component { private byCategory: boolean = true; // whether the data is organized by category or by specified number percentages/ratios @observable _currSelected: any | undefined = undefined; private curSliceSelected: any = undefined; - private sliceColors: any = {}; - @observable graphTitle: string = this.defaultGraphTitle; // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates @computed get _piechartData() { @@ -254,7 +253,7 @@ export class PieChart extends React.Component { var percentField = Object.keys(dataSet[0])[0] var descriptionField = Object.keys(dataSet[0])[1]! - var radius = Math.min(width, height) / 2 - Math.max(this.props.margin.top, this.props.margin.bottom, this.props.margin.left, this.props.margin.right) + var radius = Math.min(width, height-this.props.margin.top-this.props.margin.bottom) /2 var svg = (this._piechartSvg = d3 .select(this._piechartRef.current) .append("svg") @@ -349,7 +348,7 @@ export class PieChart extends React.Component { .enter() .append("g") arcs.append("path") - .attr("fill", (data, i)=>{ return this.sliceColors[data.data.valueOf()]? this.sliceColors[data.data.valueOf()] : d3.schemeSet3[i]? d3.schemeSet3[i]: d3.schemeSet3[i%12] }) + .attr("fill", (data, i)=>{ return this.props.layoutDoc['pieSliceColors-'+data.data.valueOf()]? StrCast(this.props.layoutDoc['pieSliceColors-'+data.data.valueOf()]) : d3.schemeSet3[i]? d3.schemeSet3[i]: d3.schemeSet3[i%12] }) .attr("class", 'slice') .attr("d", arc) .on('click', onPointClick) @@ -379,11 +378,15 @@ export class PieChart extends React.Component { @action changeSelectedColor = (color: string) => { this.curSliceSelected.attr("fill", color); - this.sliceColors[this._currSelected[this.props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\0) titleAccessor = StrCast(this.props.layoutDoc['pieChart-title-'+this.props.axes[0]]); + const title = titleAccessor? titleAccessor : this.defaultGraphTitle; var selected: string; if (this._currSelected){ selected = '{ '; @@ -400,7 +403,8 @@ export class PieChart extends React.Component {
this.graphTitle = val as string)} + setVal={action(val => {this.props.axes.length>1? this.props.layoutDoc['pieChart-title-'+this.props.axes[0]+"-"+this.props.axes[1]] = val as string + : this.props.layoutDoc['pieChart-title-'+this.props.axes[0]] = val as string})} color={"black"} size={Size.LARGE} fillWidth diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index aaedba202..64c6dc940 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -9,14 +9,24 @@ import { DragManager } from '../../../../util/DragManager'; import { DocumentView } from '../../DocumentView'; import { DataVizView } from '../DataVizBox'; import { LinkManager } from '../../../../util/LinkManager'; -import { DocCast } from '../../../../../fields/Types'; +import { Cast, DocCast } from '../../../../../fields/Types'; import { EditableText, Size, Type } from 'browndash-components'; +import './Chart.scss'; +import { listSpec } from '../../../../../fields/Schema'; interface TableBoxProps { rootDoc: Doc; pairs: { [key: string]: any }[]; selectAxes: (axes: string[]) => void; axes: string[]; + width: number; + height: number; + margin: { + top: number; + right: number; + bottom: number; + left: number; + }; docView?: () => DocumentView | undefined; } @@ -27,6 +37,7 @@ export class TableBox extends React.Component { @computed get _tableData() { if (this.incomingLinks.length! <= 0) return this.props.pairs; + /// StrListCast(this.incomingLinks[0].anchor_1.selected) ==> list of guids that the parent has selected return this.props.pairs?.filter(pair => (Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select')))) } @@ -44,7 +55,7 @@ export class TableBox extends React.Component { // render() { // return ( - //
+ //
// // // @@ -144,7 +155,7 @@ export class TableBox extends React.Component { render() { return ( -
+
@@ -214,7 +225,14 @@ export class TableBox extends React.Component { {this._tableData?.map((p, i) => { return ( - (p['select' + this.props.docView?.()?.rootDoc![Id]] = !p['select' + this.props.docView?.()?.rootDoc![Id]]))}> + { + // if (!this.props.docView?.()!.layoutDoc.selected) + // this.props.docView!.()!.layoutDoc.selected = new List(); + // const selected = Cast(this.props.docView?.()!.layoutDoc.selected, listSpec("string"), null); + // // StrListCast(this.props.docView?.()!.layoutDoc.selected) + // selected.push(p.guid); + (p['select' + this.props.docView?.()?.rootDoc![Id]] = !p['select' + this.props.docView?.()?.rootDoc![Id]]) + })}> {this.columns.map(col => ( {this._tableData?.map((p, i) => { - return ( - { - if (!this.props.layoutDoc.selected) this.props.layoutDoc.selected = new List(); - const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null); - if (selected.includes(p.guid)) selected.splice(selected.indexOf(p.guid), 1); - else { - selected.push(p.guid)}; - })} style={ - { fontWeight: StrListCast(this.props.layoutDoc.selected).includes(p.guid) ? 'bold' : '' , width: '110%', - background: StrListCast(this.props.layoutDoc.selected).includes(p.guid) ? 'lightgrey' : '' }}> - {this.columns.map(col => ( - (this.props.layoutDoc.selected)? - - : - ))} - - ); + var containsData = false; + this.columns.map(col => {if (p[col]!='' && p[col]!=null && p[col]!=undefined) containsData = true}) + if (containsData){ + return ( + { + if (!this.props.layoutDoc.selected) this.props.layoutDoc.selected = new List(); + const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null); + if (selected.includes(p.guid)) selected.splice(selected.indexOf(p.guid), 1); + else { + selected.push(p.guid)}; + })} style={ + { fontWeight: StrListCast(this.props.layoutDoc.selected).includes(p.guid) ? 'bold' : '' , width: '110%', + background: StrListCast(this.props.layoutDoc.selected).includes(p.guid) ? 'lightgrey' : '' }}> + {this.columns.map(col => ( + (this.props.layoutDoc.selected)? + + : + ))} + + ); + } })}
{p[col]} -- cgit v1.2.3-70-g09d2 From 23e0ee2dcad8df2bc3467647e05c433f27787d54 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Thu, 3 Aug 2023 14:38:47 -0400 Subject: table fixes (selected by guid not extra row + ui changes) --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 17 +- .../views/nodes/DataVizBox/components/Chart.scss | 1 + .../nodes/DataVizBox/components/Histogram.tsx | 56 +--- .../nodes/DataVizBox/components/LineChart.tsx | 4 +- .../views/nodes/DataVizBox/components/PieChart.tsx | 47 +--- .../views/nodes/DataVizBox/components/TableBox.tsx | 310 +++++++-------------- 6 files changed, 135 insertions(+), 300 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 9a4de3c36..80586d7c7 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -1,9 +1,9 @@ -import { action, computed, observable, ObservableMap, ObservableSet } from 'mobx'; +import { action, computed, ObservableMap, ObservableSet } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { Doc, StrListCast } from '../../../../fields/Doc'; import { List } from '../../../../fields/List'; -import { Cast, CsvCast, NumCast, StrCast } from '../../../../fields/Types'; +import { Cast, CsvCast, StrCast } from '../../../../fields/Types'; import { CsvField } from '../../../../fields/URLField'; import { Docs } from '../../../documents/Documents'; import { ViewBoxAnnotatableComponent } from '../../DocComponent'; @@ -15,6 +15,7 @@ import './DataVizBox.scss'; import { Histogram } from './components/Histogram'; import { PieChart } from './components/PieChart'; import { Toggle, ToggleType, Type } from 'browndash-components'; +import { Utils } from '../../../../Utils'; export enum DataVizView { TABLE = 'table', @@ -35,9 +36,11 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { // @observable private pairs: { [key: string]: FieldResult }[] = []; static pairSet = new ObservableMap(); @computed.struct get pairs() { - return DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); + var pairs = DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); + pairs?.map(pair => {if (!pair.guid) pair.guid = Utils.GenerateGuid()}) + return pairs; } - private _chartRenderer: LineChart | undefined; + private _chartRenderer: LineChart | Histogram | PieChart | undefined; // // another way would be store a schema that defines the type of data we are expecting from an imported doc // method1() { @@ -116,10 +119,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { const margin = { top: 10, right: 25, bottom: 50, left: 25 }; if (!this.pairs) return 'no data'; switch (this.dataVizView) { - case DataVizView.TABLE: return ; + case DataVizView.TABLE: return ; case DataVizView.LINECHART: return (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />; - case DataVizView.HISTOGRAM: return ; - case DataVizView.PIECHART: return ; + case DataVizView.HISTOGRAM: return (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />; + case DataVizView.PIECHART: return (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />; } } @computed get dataUrl() { diff --git a/src/client/views/nodes/DataVizBox/components/Chart.scss b/src/client/views/nodes/DataVizBox/components/Chart.scss index 6c87241b8..996183cb8 100644 --- a/src/client/views/nodes/DataVizBox/components/Chart.scss +++ b/src/client/views/nodes/DataVizBox/components/Chart.scss @@ -77,4 +77,5 @@ } .table-container{ overflow: scroll; + margin: 10px; } \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index 89dcf87db..efe17297b 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -1,15 +1,13 @@ import { observer } from "mobx-react"; -import { Doc, DocListCast, FieldResult } from "../../../../../fields/Doc"; +import { Doc, DocListCast, 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 { DataPoint, SelectedDataPoint } from "./LineChart"; +import { DocCast, StrCast} from "../../../../../fields/Types"; import { DocumentManager } from "../../../../util/DocumentManager"; import { Id } from "../../../../../fields/FieldSymbols"; import { DataVizBox } from "../DataVizBox"; -import { listSpec } from "../../../../../fields/Schema"; import { PinProps, PresBox } from "../../trails"; import { Docs } from "../../../../documents/Documents"; import { List } from "../../../../../fields/List"; @@ -54,7 +52,7 @@ export class Histogram extends React.Component { var ax0 = this.props.axes[0]; if (/\d/.test(this.props.pairs[0][ax0])){ this.numericalXData = true } return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select')))) + ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(pair.guid))) .map(pair => ({ [ax0]: (pair[this.props.axes[0]])})) }; @@ -63,13 +61,8 @@ export class Histogram extends React.Component { if (/\d/.test(this.props.pairs[0][ax0])) { this.numericalXData = true;} if (/\d/.test(this.props.pairs[0][ax1]) && this.props.pairs.length < this.maxBins) { this.numericalYData = true;} return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select')))) + ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(pair.guid))) .map(pair => ({ [ax0]: (pair[this.props.axes[0]]), [ax1]: (pair[this.props.axes[1]]) })) - // .sort((a, b) => (a[ax0] < b[ax0] ? -1 : 1)); - 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 defaultGraphTitle(){ var ax0 = this.props.axes[0]; @@ -181,27 +174,16 @@ export class Histogram extends React.Component { } @action - restoreView = (data: Doc) => { - const coords = Cast(data.presDataVizSelection, 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; - } - if (this._currSelected) { - this.setCurrSelected(); - return true; - } - return false; - }; + restoreView = (data: Doc) => {}; // 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?.x, + title: 'histogram doc selection' + this._currSelected, }); PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc); - anchor.presDataVizSelection = this._currSelected ? new List([this._currSelected.x, this._currSelected.y]) : undefined; + anchor.presDataVizSelection = this._currSelected ? new List([this._currSelected]) : undefined; return anchor; }; @@ -213,28 +195,6 @@ export class Histogram extends React.Component { return this.props.width - this.props.margin.left - this.props.margin.right; } - setupTooltip() { - return d3 - .select(this._histogramRef.current) - .append('div') - .attr('class', 'tooltip') - .style('opacity', 0) - .style('background', '#fff') - .style('border', '1px solid #ccc') - .style('padding', '5px') - .style('position', 'absolute') - .style('font-size', '12px'); - } - - // TODO: nda - use this everyewhere we update currSelected? - @action - setCurrSelected(x?: number, y?: number) { - // TODO: nda - get rid of svg element in the list? - this._currSelected = x !== undefined && y !== undefined ? { x, y } : undefined; - this.props.pairs.forEach(pair => pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y && (pair.selected = true)); - this.props.pairs.forEach(pair => (pair.selected = pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y ? true : undefined)); - } - data = (dataSet: any) => { var validData = dataSet.filter((d: { [x: string]: unknown; }) => { var valid = true; @@ -364,7 +324,7 @@ export class Histogram extends React.Component { const selected = svg.selectAll('.histogram-bar').filter((d: any) => { barCounter++; if ((barCounter*eachRectWidth ) <= pointerX && pointerX <= ((barCounter+1)*eachRectWidth)){ - var showSelected = this.numericalYData? this.props.pairs.filter((data: { [x: string]: any; }) => data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\ data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\ data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { @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')))) + ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(pair.guid))) .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)); } diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index d01d4429f..f3a72a53b 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -1,15 +1,13 @@ import { observer } from "mobx-react"; -import { Doc, DocListCast } from "../../../../../fields/Doc"; +import { Doc, DocListCast, 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 { DataPoint, SelectedDataPoint } from "./LineChart"; +import { DocCast, StrCast} from "../../../../../fields/Types"; import { DocumentManager } from "../../../../util/DocumentManager"; import { Id } from "../../../../../fields/FieldSymbols"; import { DataVizBox } from "../DataVizBox"; -import { listSpec } from "../../../../../fields/Schema"; import { PinProps, PresBox } from "../../trails"; import { Docs } from "../../../../documents/Documents"; import { List } from "../../../../../fields/List"; @@ -52,7 +50,7 @@ export class PieChart extends React.Component { var ax0 = this.props.axes[0]; if (/\d/.test(this.props.pairs[0][ax0])){ this.byCategory = false } return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select')))) + ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(pair.guid))) .map(pair => ({ [ax0]: (pair[this.props.axes[0]])})) }; @@ -60,13 +58,8 @@ export class PieChart extends React.Component { var ax1 = this.props.axes[1]; if (/\d/.test(this.props.pairs[0][ax0])) { this.byCategory = false; } return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select')))) + ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(pair.guid))) .map(pair => ({ [ax0]: (pair[this.props.axes[0]]), [ax1]: (pair[this.props.axes[1]]) })) - // .sort((a, b) => (a[ax0] < b[ax0] ? -1 : 1)); - 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 defaultGraphTitle(){ var ax0 = this.props.axes[0]; @@ -178,27 +171,16 @@ export class PieChart extends React.Component { } @action - restoreView = (data: Doc) => { - const coords = Cast(data.presDataVizSelection, 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; - } - if (this._currSelected) { - this.setCurrSelected(); - return true; - } - return false; - }; + restoreView = (data: Doc) => {}; // 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: 'piechart doc selection' + this._currSelected?.x, + title: 'piechart doc selection' + this._currSelected, }); PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc); - anchor.presDataVizSelection = this._currSelected ? new List([this._currSelected.x, this._currSelected.y]) : undefined; + anchor.presDataVizSelection = this._currSelected ? new List([this._currSelected]) : undefined; return anchor; }; @@ -210,19 +192,6 @@ export class PieChart extends React.Component { return this.props.width - this.props.margin.left - this.props.margin.right; } - setupTooltip() { - return d3 - .select(this._piechartRef.current) - .append('div') - .attr('class', 'tooltip') - .style('opacity', 0) - .style('background', '#fff') - .style('border', '1px solid #ccc') - .style('padding', '5px') - .style('position', 'absolute') - .style('font-size', '12px'); - } - // TODO: nda - use this everyewhere we update currSelected? @action setCurrSelected(x?: number, y?: number) { @@ -322,7 +291,7 @@ export class PieChart extends React.Component { 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) { - var showSelected = this.byCategory? pieDataSet[index] : this.props.pairs[index]; + var showSelected = this.byCategory? pieDataSet[index] : this._piechartData[index]; sameAsCurrent = (this.byCategory && this._currSelected)? (showSelected[Object.keys(showSelected)[0]]==this._currSelected![Object.keys(showSelected)[0]] && showSelected[Object.keys(showSelected)[1]]==this._currSelected![Object.keys(showSelected)[1]]) diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 64c6dc940..5653adbce 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -1,21 +1,20 @@ -import { action, computed, observable } from 'mobx'; +import { action, computed, } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc } from '../../../../../fields/Doc'; -import { Id } from '../../../../../fields/FieldSymbols'; +import { Doc, StrListCast } from '../../../../../fields/Doc'; import { List } from '../../../../../fields/List'; -import { emptyFunction, returnFalse, setupMoveUpEvents, Utils } from '../../../../../Utils'; +import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../../Utils'; import { DragManager } from '../../../../util/DragManager'; import { DocumentView } from '../../DocumentView'; import { DataVizView } from '../DataVizBox'; import { LinkManager } from '../../../../util/LinkManager'; import { Cast, DocCast } from '../../../../../fields/Types'; -import { EditableText, Size, Type } from 'browndash-components'; import './Chart.scss'; import { listSpec } from '../../../../../fields/Schema'; interface TableBoxProps { rootDoc: Doc; + layoutDoc: Doc; pairs: { [key: string]: any }[]; selectAxes: (axes: string[]) => void; axes: string[]; @@ -32,13 +31,10 @@ interface TableBoxProps { @observer export class TableBox extends React.Component { - @observable editableHeaders = this.columns; - @observable editableCells = this._tableData; @computed get _tableData() { if (this.incomingLinks.length! <= 0) return this.props.pairs; - /// StrListCast(this.incomingLinks[0].anchor_1.selected) ==> list of guids that the parent has selected - return this.props.pairs?.filter(pair => (Array.from(Object.keys(pair)).some(key => pair[key] && key.startsWith('select')))) + return this.props.pairs?.filter(pair => this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(pair.guid)) } @computed get incomingLinks() { @@ -49,204 +45,110 @@ export class TableBox extends React.Component { } @computed get columns() { - // return this.props.pairs.length ? Array.from(Object.keys(this.props.pairs[0])) : []; - return this._tableData.length ? Array.from(Object.keys(this._tableData[0])) : []; + return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header!='guid' && header!='') : []; } - // render() { - // return ( - //
- // - // - // - // {this.editableHeaders - // .filter(col => !col.startsWith('select')) - // .map(col => { - // const header = React.createRef(); - // const displayColName = col; - // return ( - // - // ); - // })} - // - // - // - // {this._tableData?.map((p, i) => { - // return ( - // (p['select' + this.props.docView?.()?.rootDoc![Id]] = !p['select' + this.props.docView?.()?.rootDoc![Id]]))}> - // {this.editableHeaders.map(col => ( - // - // ))} - // - // ); - // })} - // - //
{ - // const downX = e.clientX; - // const downY = e.clientY; - // setupMoveUpEvents( - // {}, - // e, - // e => { - // const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!; - // const targetCreator = (annotationOn: Doc | undefined) => { - // const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!); - // embedding._dataVizView = DataVizView.LINECHART; - // embedding._data_vizAxes = new List([col, col]); - // embedding._draggedFrom = this.props.docView?.()!.rootDoc!; - // embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!; - // return embedding; - // }; - // if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { - // DragManager.StartAnchorAnnoDrag([header.current!], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { - // dragComplete: e => { - // if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { - // e.linkDocument.link_displayLine = true; - // e.linkDocument.link_matchEmbeddings = true; - // // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; - // // e.annoDragData.linkSourceDoc.followLinkZoom = false; - // } - // }, - // }); - // return true; - // } - // return false; - // }, - // emptyFunction, - // action(e => { - // const newAxes = this.props.axes; - // if (newAxes.includes(col)) { - // newAxes.splice(newAxes.indexOf(col), 1); - // } else if (newAxes.length >= 1) { - // newAxes[1] = col; - // } else { - // newAxes[0] = col; - // } - // this.props.selectAxes(newAxes); - // }) - // ); - // }}> - // { - // this.editableHeaders[this.editableHeaders.indexOf(col)] = val as string - // this.props.pairs.map(pair => { - // pair[val as string] = pair[col]; - // delete pair[col] - // }) - // })} - // color={"black"} - // size={Size.LARGE} - // /> - //
- // {p[col]} - //
- //
- // ); - // } - - render() { - return ( -
- - - - {this.columns - .filter(col => !col.startsWith('select')) - .map(col => { - const header = React.createRef(); - return ( - - ); - })} - - - - {this._tableData?.map((p, i) => { - return ( - { - // if (!this.props.docView?.()!.layoutDoc.selected) - // this.props.docView!.()!.layoutDoc.selected = new List(); - // const selected = Cast(this.props.docView?.()!.layoutDoc.selected, listSpec("string"), null); - // // StrListCast(this.props.docView?.()!.layoutDoc.selected) - // selected.push(p.guid); - (p['select' + this.props.docView?.()?.rootDoc![Id]] = !p['select' + this.props.docView?.()?.rootDoc![Id]]) - })}> - {this.columns.map(col => ( - - ))} - - ); - })} - -
{ - const downX = e.clientX; - const downY = e.clientY; - setupMoveUpEvents( - {}, - e, - e => { - const sourceAnchorCreator = () => this.props.docView?.()!.rootDoc!; - const targetCreator = (annotationOn: Doc | undefined) => { - const embedding = Doc.MakeEmbedding(this.props.docView?.()!.rootDoc!); - embedding._dataVizView = DataVizView.LINECHART; - embedding._data_vizAxes = new List([col, col]); - embedding._draggedFrom = this.props.docView?.()!.rootDoc!; - embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!; - return embedding; - }; - if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { - DragManager.StartAnchorAnnoDrag([header.current!], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { - dragComplete: e => { - if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { - e.linkDocument.link_displayLine = true; - e.linkDocument.link_matchEmbeddings = true; - // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; - // e.annoDragData.linkSourceDoc.followLinkZoom = false; - } - }, - }); - return true; - } - return false; - }, - emptyFunction, - action(e => { - const newAxes = this.props.axes; - if (newAxes.includes(col)) { - newAxes.splice(newAxes.indexOf(col), 1); - } else if (newAxes.length >= 1) { - newAxes[1] = col; - } else { - newAxes[0] = col; - } - this.props.selectAxes(newAxes); - }) - ); - }}> - {col} -
- {p[col]} -
+ if (this._tableData.length>0){ + return ( +
+ + + + {this.columns + .filter(col => !col.startsWith('select')) + .map(col => { + const header = React.createRef(); + return ( + + ); + })} + + + + {this._tableData?.map((p, i) => { + return ( + { + if (!this.props.layoutDoc.selected) this.props.layoutDoc.selected = new List(); + const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null); + if (selected.includes(p.guid)) selected.splice(selected.indexOf(p.guid), 1); + else { + selected.push(p.guid)}; + })} style={ + { fontWeight: StrListCast(this.props.layoutDoc.selected).includes(p.guid) ? 'bold' : '' , width: '110%', + background: StrListCast(this.props.layoutDoc.selected).includes(p.guid) ? 'lightgrey' : '' }}> + {this.columns.map(col => ( + (this.props.layoutDoc.selected)? + + : + ))} + + ); + })} + +
{ + const downX = e.clientX; + const downY = e.clientY; + setupMoveUpEvents( + {}, + e, + e => { + 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([col, col]); + embedding._draggedFrom = this.props.docView?.()!.rootDoc!; + embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!; + return embedding; + }; + if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { + DragManager.StartAnchorAnnoDrag([header.current!], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, { + dragComplete: e => { + if (!e.aborted && e.annoDragData && e.annoDragData.linkSourceDoc && e.annoDragData.dropDocument && e.linkDocument) { + e.linkDocument.link_displayLine = true; + e.linkDocument.link_matchEmbeddings = true; + // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this.props.rootDoc; + // e.annoDragData.linkSourceDoc.followLinkZoom = false; + } + }, + }); + return true; + } + return false; + }, + emptyFunction, + action(e => { + const newAxes = this.props.axes; + if (newAxes.includes(col)) { + newAxes.splice(newAxes.indexOf(col), 1); + } else if (newAxes.length >= 1) { + newAxes[1] = col; + } else { + newAxes[0] = col; + } + this.props.selectAxes(newAxes); + }) + ); + }}> + {col} +
+ {p[col]} + {p[col]}
+
+ ); + } + else return ( +
+ Selected rows of data from the incoming DataVizBox to display.
- ); + ) } - - - } -- cgit v1.2.3-70-g09d2 From 8bdae208c4ae44766241c285f2f7234027b9b80f Mon Sep 17 00:00:00 2001 From: srichman333 Date: Thu, 3 Aug 2023 15:56:11 -0400 Subject: histogram small data fix --- .../nodes/DataVizBox/components/Histogram.tsx | 6 ++-- .../views/nodes/DataVizBox/components/TableBox.tsx | 42 ++++++++++++---------- 2 files changed, 26 insertions(+), 22 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index efe17297b..1077df844 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -267,10 +267,10 @@ export class Histogram extends React.Component { var histogram = d3.histogram() .value(function(d) {return d}) .domain([startingPoint!, endingPoint!]) - .thresholds(x.ticks(numBins-1)) + .thresholds(x.ticks(numBins)) var bins = histogram(data) var eachRectWidth = width/(bins.length) - var graphStartingPoint = bins[0].x1? bins[0].x1! - (bins[1].x1! - bins[1].x0!) : 0; + 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 ]) @@ -286,7 +286,7 @@ export class Histogram extends React.Component { index = j; } } - bins[index].push(data[i]) + if (bins[index]) bins[index].push(data[i]) } bins.pop(); eachRectWidth = width/(bins.length) diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 5653adbce..f244502a4 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -120,25 +120,29 @@ export class TableBox extends React.Component {
- {p[col]} - {p[col]}
+ {p[col]} + {p[col]}
-- cgit v1.2.3-70-g09d2 From bee66361d878c366e8c753ca844abc2f78fbf7f3 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Fri, 4 Aug 2023 14:52:35 -0400 Subject: better row guids so selected rows stay on refresh --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 5 +---- .../views/nodes/DataVizBox/components/Histogram.tsx | 5 +++-- .../views/nodes/DataVizBox/components/LineChart.tsx | 3 ++- .../views/nodes/DataVizBox/components/PieChart.tsx | 5 +++-- .../views/nodes/DataVizBox/components/TableBox.tsx | 17 +++++++++++------ 5 files changed, 20 insertions(+), 15 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index a92fc1eb9..8b951a002 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -15,7 +15,6 @@ import './DataVizBox.scss'; import { Histogram } from './components/Histogram'; import { PieChart } from './components/PieChart'; import { Toggle, ToggleType, Type } from 'browndash-components'; -import { Utils } from '../../../../Utils'; export enum DataVizView { TABLE = 'table', @@ -36,9 +35,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { // @observable private pairs: { [key: string]: FieldResult }[] = []; static pairSet = new ObservableMap(); @computed.struct get pairs() { - var pairs = DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); - pairs?.map(pair => {if (!pair.guid) pair.guid = Utils.GenerateGuid()}) - return pairs; + return DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); } private _chartRenderer: LineChart | Histogram | PieChart | undefined; // // another way would be store a schema that defines the type of data we are expecting from an imported doc diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index 02f1ddbbb..a9be151bc 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -49,12 +49,13 @@ export class Histogram extends React.Component { // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates @computed get _histogramData() { + var guids = StrListCast(this.props.layoutDoc.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 } return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(pair.guid))) + ?.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]])})) }; @@ -63,7 +64,7 @@ export class Histogram extends React.Component { if (/\d/.test(this.props.pairs[0][ax0])) { this.numericalXData = true;} if (/\d/.test(this.props.pairs[0][ax1]) && this.props.pairs.length < this.maxBins) { this.numericalYData = true;} return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(pair.guid))) + ?.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]]) })) } @computed get defaultGraphTitle(){ diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index da5f7dbbb..77b3acf47 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -49,9 +49,10 @@ export class LineChart extends React.Component { // 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); 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(pair.guid))) + ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).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)); } diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 8fdead3d7..cc5cc231b 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -46,12 +46,13 @@ export class PieChart extends React.Component { // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates @computed get _piechartData() { + var guids = StrListCast(this.props.layoutDoc.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 } return this.props.pairs - ?.filter(pair => (!this.incomingLinks.length ? true : this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(pair.guid))) + ?.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]])})) }; @@ -59,7 +60,7 @@ export class PieChart extends React.Component { var ax1 = this.props.axes[1]; 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(pair.guid))) + ?.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]]) })) } @computed get defaultGraphTitle(){ diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index f244502a4..7d6f934b9 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -34,7 +34,8 @@ export class TableBox extends React.Component { @computed get _tableData() { if (this.incomingLinks.length! <= 0) return this.props.pairs; - return this.props.pairs?.filter(pair => this.incomingLinks[0]!.selected && StrListCast(this.incomingLinks[0].selected).includes(pair.guid)) + 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)])) } @computed get incomingLinks() { @@ -45,7 +46,10 @@ export class TableBox extends React.Component { } @computed get columns() { - return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header!='guid' && header!='') : []; + if (!this.props.layoutDoc.rowGuids) this.props.layoutDoc.rowGuids = new List(); + 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!='') : []; } render() { @@ -121,18 +125,19 @@ export class TableBox extends React.Component { {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){ return ( { if (!this.props.layoutDoc.selected) this.props.layoutDoc.selected = new List(); const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null); - if (selected.includes(p.guid)) selected.splice(selected.indexOf(p.guid), 1); + if (selected.includes(guid)) selected.splice(selected.indexOf(guid), 1); else { - selected.push(p.guid)}; + selected.push(guid)}; })} style={ - { fontWeight: StrListCast(this.props.layoutDoc.selected).includes(p.guid) ? 'bold' : '' , width: '110%', - background: StrListCast(this.props.layoutDoc.selected).includes(p.guid) ? 'lightgrey' : '' }}> + { fontWeight: StrListCast(this.props.layoutDoc.selected).includes(guid) ? 'bold' : '' , width: '110%', + background: StrListCast(this.props.layoutDoc.selected).includes(guid) ? 'lightgrey' : '' }}> {this.columns.map(col => ( (this.props.layoutDoc.selected)? -- cgit v1.2.3-70-g09d2 From 8cbfb72751a3f8814c0dbda54c8ed22c8bb58783 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Fri, 4 Aug 2023 15:17:25 -0400 Subject: color stays on dragged charts + erase bar color is on individual bars --- .../nodes/DataVizBox/components/Histogram.tsx | 28 ++++++++++++++-------- .../views/nodes/DataVizBox/components/TableBox.tsx | 5 +++- 2 files changed, 22 insertions(+), 11 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index a9be151bc..2a47abf32 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -400,6 +400,14 @@ export class Histogram extends React.Component { 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(/\ { if (each.split('::')[0] == barName) barColors.splice(barColors.indexOf(each), 1) }); + }; render() { var curSelectedBarName; @@ -445,28 +453,28 @@ export class Histogram extends React.Component { setSelectedColor={color => this.props.layoutDoc.defaultHistogramColor = color} size={Size.XSMALL} /> -   - } - size={Size.XSMALL} - color={'black'} - type={Type.SEC} - tooltip={'Revert all bars to the default color'} - onClick={action(() => {this.props.layoutDoc.histogramBarColors = new List()})} - />
{selected != 'none' ?
Selected: {selected}     } selectedColor={selectedBarColor? selectedBarColor : this.curBarSelected.attr("fill")} setSelectedColor={color => this.changeSelectedColor(color)} size={Size.XSMALL} /> +   + } + size={Size.XSMALL} + color={'black'} + type={Type.SEC} + tooltip={'Revert to the default bar color'} + onClick={action(() => this.eraseSelectedColor())} + />
: null}
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 7d6f934b9..8a99d332f 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -1,7 +1,7 @@ import { action, computed, } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; -import { Doc, StrListCast } from '../../../../../fields/Doc'; +import { Doc, Field, StrListCast } from '../../../../../fields/Doc'; import { List } from '../../../../../fields/List'; import { emptyFunction, setupMoveUpEvents, Utils } from '../../../../../Utils'; import { DragManager } from '../../../../util/DragManager'; @@ -85,6 +85,9 @@ export class TableBox extends React.Component { embedding._data_vizAxes = new List([col, col]); embedding._draggedFrom = this.props.docView?.()!.rootDoc!; embedding.annotationOn = annotationOn; //this.props.docView?.()!.rootDoc!; + embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors); + embedding.defaultHistogramColor = this.props.layoutDoc.defaultHistogramColor; + embedding.pieSliceColors = Field.Copy(this.props.layoutDoc.pieSliceColors); return embedding; }; if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) { -- cgit v1.2.3-70-g09d2 From 1bfa61ae441458cdcd860eb39e617417d013f1fa Mon Sep 17 00:00:00 2001 From: srichman333 Date: Mon, 7 Aug 2023 10:53:09 -0400 Subject: Issues when you remove a highlighted row more than 1 chart up the tree fixed --- src/client/views/nodes/DataVizBox/components/TableBox.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 8a99d332f..a7cc3f2fb 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -52,7 +52,18 @@ export class TableBox extends React.Component { return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header!='') : []; } + filterSelectedRowsDown() { + if (!this.props.layoutDoc.selected) this.props.layoutDoc.selected = new List(); + const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null); + const incomingSelected = this.incomingLinks.length? StrListCast(this.incomingLinks[0].selected) : 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 + } + } + render() { + this.filterSelectedRowsDown(); if (this._tableData.length>0){ return (
@@ -133,7 +144,6 @@ export class TableBox extends React.Component { if (containsData){ return ( { - if (!this.props.layoutDoc.selected) this.props.layoutDoc.selected = new List(); const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null); if (selected.includes(guid)) selected.splice(selected.indexOf(guid), 1); else { -- cgit v1.2.3-70-g09d2 From 85f91733a21c7b41829eb9280ce33e90783c926d Mon Sep 17 00:00:00 2001 From: srichman333 Date: Mon, 7 Aug 2023 13:30:40 -0400 Subject: selected data at bottom of graph --- src/client/views/nodes/DataVizBox/DataVizBox.scss | 2 +- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 2 +- src/client/views/nodes/DataVizBox/components/Chart.scss | 7 +++++-- src/client/views/nodes/DataVizBox/components/Histogram.tsx | 2 +- src/client/views/nodes/DataVizBox/components/LineChart.tsx | 5 +++-- src/client/views/nodes/DataVizBox/components/PieChart.tsx | 2 +- src/client/views/nodes/DataVizBox/components/TableBox.tsx | 4 ++-- 7 files changed, 14 insertions(+), 10 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.scss b/src/client/views/nodes/DataVizBox/DataVizBox.scss index ab2f19726..a69881b7c 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.scss +++ b/src/client/views/nodes/DataVizBox/DataVizBox.scss @@ -1,5 +1,5 @@ .dataviz { - overflow: hidden; + overflow: scroll; height: 100%; width: 100%; diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 8b951a002..9a4546900 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -116,7 +116,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { @computed get selectView() { const width = this.props.PanelWidth() * 0.9; const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9; - const margin = { top: 10, right: 25, bottom: 50, left: 25 }; + const margin = { top: 10, right: 25, bottom: 75, left: 45 }; if (!this.pairs) return 'no data'; switch (this.dataVizView) { case DataVizView.TABLE: return ; diff --git a/src/client/views/nodes/DataVizBox/components/Chart.scss b/src/client/views/nodes/DataVizBox/components/Chart.scss index 996183cb8..35e5187b2 100644 --- a/src/client/views/nodes/DataVizBox/components/Chart.scss +++ b/src/client/views/nodes/DataVizBox/components/Chart.scss @@ -4,6 +4,7 @@ align-items: center; cursor: default; margin-top: 10px; + overflow-y: visible; .graph{ overflow: visible; @@ -22,8 +23,8 @@ display: flex; flex-direction: row; margin: 10px; - margin-top: 0px; - margin-bottom: -5px; + margin-top: -25px; + margin-bottom: 5px; } .slice { &.hover { @@ -78,4 +79,6 @@ .table-container{ overflow: scroll; margin: 10px; + margin-left: 25px; + margin-top: 25px; } \ No newline at end of file diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index 2a47abf32..cb882cf4a 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -454,6 +454,7 @@ export class Histogram extends React.Component { size={Size.XSMALL} />
+
{selected != 'none' ?
Selected: {selected} @@ -477,7 +478,6 @@ export class Histogram extends React.Component { />
: null} -
) : {'first use table view to select a column to graph'} ); diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index 77b3acf47..3a416c401 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -216,7 +216,6 @@ export class LineChart extends React.Component { // TODO: nda - get rid of svg element in the list? this._currSelected = x !== undefined && y !== undefined ? { x, y } : undefined; this.props.pairs.forEach(pair => pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y && (pair.selected = true)); - this.props.pairs.forEach(pair => (pair.selected = pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y ? true : undefined)); } drawDataPoints(data: DataPoint[], idx: number, xScale: d3.ScaleLinear, yScale: d3.ScaleLinear) { @@ -369,8 +368,10 @@ export class LineChart extends React.Component { fillWidth />
-
{`Selected: ${selectedPt}`}
+ {selectedPt!='none'? +
{`Selected: ${selectedPt}`}
+ : null}
) : {'first use table view to select two axes to plot'} ); diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index cc5cc231b..ca93a2942 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -410,6 +410,7 @@ export class PieChart extends React.Component { fillWidth />
+
{selected != 'none' ?
Selected: {selected} @@ -424,7 +425,6 @@ export class PieChart extends React.Component { />
: null} -
) : {'first use table view to select a column to graph'} ); diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index a7cc3f2fb..38dd62d8d 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -49,7 +49,7 @@ export class TableBox extends React.Component { if (!this.props.layoutDoc.rowGuids) this.props.layoutDoc.rowGuids = new List(); 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!='') : []; + return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header!='' && header!=undefined) : []; } filterSelectedRowsDown() { @@ -66,7 +66,7 @@ export class TableBox extends React.Component { this.filterSelectedRowsDown(); if (this._tableData.length>0){ return ( -
+
-- cgit v1.2.3-70-g09d2 From 318d56e1dff94204b100f5636e1a3288724aaffc Mon Sep 17 00:00:00 2001 From: srichman333 Date: Tue, 8 Aug 2023 16:15:36 -0400 Subject: comments + cleanups --- src/client/views/nodes/DataVizBox/DataVizBox.tsx | 47 +------ .../nodes/DataVizBox/components/Histogram.tsx | 120 ++++------------ .../views/nodes/DataVizBox/components/PieChart.tsx | 155 +++++---------------- .../views/nodes/DataVizBox/components/TableBox.tsx | 7 +- src/client/views/nodes/DataVizBox/utils/D3Utils.ts | 1 - 5 files changed, 78 insertions(+), 252 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx index 9a4546900..e71739231 100644 --- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx +++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx @@ -26,45 +26,20 @@ export enum DataVizView { @observer export class DataVizBox extends ViewBoxAnnotatableComponent() { - public static LayoutString(fieldKey: string) { - return FieldView.LayoutString(DataVizBox, fieldKey); - } - // says we have an object and any string - // 2 ways of doing it - // @observable private pairs: { [key: string]: number | string | undefined }[] = []; - // @observable private pairs: { [key: string]: FieldResult }[] = []; + // all data static pairSet = new ObservableMap(); @computed.struct get pairs() { return DataVizBox.pairSet.get(CsvCast(this.rootDoc[this.fieldKey]).url.href); } - private _chartRenderer: LineChart | Histogram | PieChart | undefined; - // // another way would be store a schema that defines the type of data we are expecting from an imported doc - - // method1() { - // this.pairs[0].x = 3; - // } - - // method() { - // // this.pairs[0].x = 3; - // // go through the pairs - // const x = this.pairs[0].x; - // if (typeof x == 'number') { - // let x1 = Number(x); - // // let x1 = NumCast(x); - // } - // } - // could use field result - // [key: string]: FieldResult; - // instead of numeric x,y in there, - - // TODO: nda - use onmousedown and onmouseup when dragging and changing height and width to update the height and width props only when dragging stops + private _chartRenderer: LineChart | Histogram | PieChart | undefined; + // current displayed chart type @computed get dataVizView(): DataVizView { return StrCast(this.layoutDoc._dataVizView, 'table') as DataVizView; } - @action + @action // pinned / linked anchor doc includes selected rows, graph titles, and graph colors restoreView = (data: Doc) => { const changedView = this.dataVizView !== data.presDataVizView && (this.layoutDoc._dataVizView = data.presDataVizView); const changedAxes = this.axes.join('') !== StrListCast(data.presDataVizAxes).join('') && (this.layoutDoc._data_vizAxes = new List(StrListCast(data.presDataVizAxes))); @@ -75,7 +50,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { Object.keys(this.layoutDoc).map(key => { if (key.startsWith('histogram-title') || key.startsWith('lineChart-title') || key.startsWith('pieChart-title')){ this.layoutDoc['_'+key] = data[key]; } }) - const func = () => this._chartRenderer?.restoreView(data); if (changedView || changedAxes) { setTimeout(func, 100); @@ -83,7 +57,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { } return func() ?? false; }; - getAnchor = (addAsAnnotation?: boolean, pinProps?: PinProps) => { const anchor = !pinProps ? this.rootDoc @@ -93,7 +66,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { // this is for when we want the whole doc (so when the chartBox getAnchor returns without a marker) /*put in some options*/ }); - anchor.presDataVizView = this.dataVizView; anchor.presDataVizAxes = this.axes.length ? new List(this.axes) : undefined; anchor.selected = Field.Copy(this.layoutDoc.selected); @@ -103,7 +75,6 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { Object.keys(this.layoutDoc).map(key => { if (key.startsWith('histogram-title') || key.startsWith('lineChart-title') || key.startsWith('pieChart-title')){ anchor[key] = this.layoutDoc[key]; } }) - this.addDocument(anchor); return anchor; }; @@ -113,6 +84,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { } selectAxes = (axes: string[]) => (this.layoutDoc.data_vizAxes = new List(axes)); + // toggles for user to decide which chart type to view the data in @computed get selectView() { const width = this.props.PanelWidth() * 0.9; const height = (this.props.PanelHeight() - 32) /* height of 'change view' button */ * 0.9; @@ -125,6 +97,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { case DataVizView.PIECHART: return (this._chartRenderer = r ?? undefined)} height={height} width={width} fieldKey={this.fieldKey} margin={margin} rootDoc={this.rootDoc} axes={this.axes} pairs={this.pairs} dataDoc={this.dataDoc} />; } } + @computed get dataUrl() { return Cast(this.dataDoc[this.fieldKey], CsvField); } @@ -141,16 +114,10 @@ export class DataVizBox extends ViewBoxAnnotatableComponent() { .then(res => res.json().then(action(res => !res.errno && DataVizBox.pairSet.set(CsvCast(this.rootDoc[this.fieldKey]).url.href, res)))); } - // handle changing the view using a button - @action changeViewHandler(e: React.MouseEvent) { - e.preventDefault(); - e.stopPropagation(); - this.layoutDoc._dataVizView = this.dataVizView === DataVizView.TABLE ? DataVizView.LINECHART : DataVizView.TABLE; - } - render() { if (!this.layoutDoc._dataVizView) this.layoutDoc._dataVizView = this.dataVizView; return !this.pairs?.length ? ( + // displays how to get data into the DataVizBox if its empty
To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command 'ctrl + t' to bring the data table diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index 26c40045c..0b35f2856 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -44,12 +44,12 @@ export class Histogram extends React.Component { private numericalXData: boolean = false; // whether the data is organized by numbers rather than categoreis private numericalYData: boolean = false; // whether the y axis is controlled by provided data rather than frequency private maxBins = 15; // maximum number of bins that is readable on a normal sized doc - @observable _currSelected: any | undefined = undefined; - private curBarSelected: any = undefined; - private selectedData: any = undefined; - private hoverOverData: any = undefined; - // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates + @observable _currSelected: any | undefined = undefined; // Object of selected bar + private curBarSelected: any = undefined; // histogram bin of selected bar + private selectedData: any = undefined; // Selection of selected bar + private hoverOverData: any = undefined; // Selection of bar being hovered over + // 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); if (this.props.axes.length < 1) return []; @@ -60,7 +60,6 @@ export class Histogram extends React.Component { ?.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]])})) }; - var ax0 = this.props.axes[0]; var ax1 = this.props.axes[1]; if (/\d/.test(this.props.pairs[0][ax0])) { this.numericalXData = true;} @@ -69,6 +68,7 @@ export class Histogram extends React.Component { ?.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]]) })) } + @computed get defaultGraphTitle(){ var ax0 = this.props.axes[0]; var ax1 = (this.props.axes.length>1)? this.props.axes[1] : undefined; @@ -77,21 +77,13 @@ export class Histogram extends React.Component { } else return ax1 + " by " + ax0 + " Histogram"; } + @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 + .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 incomingSelected() { - // return selected x and y axes - // otherwise, use the selection of whatever is linked to us - return this.incomingLinks // all links that are pointing to this node - .map(anchor => DocumentManager.Instance.getFirstDocumentView(anchor)?.ComponentView as DataVizBox) // get their data viz boxes - .filter(dvb => dvb) - .map(dvb => dvb.pairs?.filter((pair: { [x: string]: any; }) => 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 } { if (this.numericalXData){ const data = this.data(this._histogramData); @@ -99,6 +91,7 @@ export class Histogram extends React.Component { } return {xMin:0, xMax:0, yMin:0, yMax:0} } + componentWillUnmount() { Array.from(Object.keys(this._disposers)).forEach(key => this._disposers[key]()); } @@ -108,79 +101,14 @@ export class Histogram extends React.Component { ({ dataSet, w, h }) => { if (dataSet!.length>0) { this.drawChart(dataSet, 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 } ); - this._disposers.annos = reaction( - () => DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), - annotations => { - // modify how d3 renders so that anything in this annotations list would be potentially highlighted in some way - // could be blue colored to make it look like anchor - // this.drawAnnotations() - // loop through annotations and draw them - annotations.forEach(a => this.drawAnnotations(Number(a.x), Number(a.y))); - // this.drawAnnotations(annotations.x, annotations.y); - }, - { fireImmediately: true } - ); - this._disposers.highlights = reaction( - () => ({ - selected: this._currSelected, - incomingSelected: this.incomingSelected, - }), - ({ 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); - incomingSelected?.forEach((pair: any) => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]]))); - }, - { fireImmediately: true } - ); }; - // anything that doesn't need to be recalculated should just be stored as drawCharts (i.e. computed values) and drawChart is gonna iterate over these observables and generate svgs based on that - - clearAnnotations = () => { - const elements = document.querySelectorAll('.datapoint'); - for (let i = 0; i < elements.length; i++) { - const element = elements[i]; - element.classList.remove('brushed'); - element.classList.remove('selected'); - } - }; - // gets called whenever the "data_annotations" fields gets updated - drawAnnotations = (dataX: number, dataY: number, selected?: boolean) => { - // TODO: nda - can optimize this by having some sort of mapping of the x and y values to the individual circle elements - // loop through all html elements with class .circle-d1 and find the one that has "data-x" and "data-y" attributes that match the dataX and dataY - // if it exists, then highlight it - // if it doesn't exist, then remove the highlight - const elements = document.querySelectorAll('.datapoint'); - for (let i = 0; i < elements.length; i++) { - const element = elements[i]; - const x = element.getAttribute('data-x'); - const y = element.getAttribute('data-y'); - if (x === dataX.toString() && y === dataY.toString()) { - element.classList.add(selected ? 'selected' : 'brushed'); - } - // TODO: nda - this remove highlight code should go where we remove the links - // } else { - // } - } - }; - - removeAnnotations(dataX: number, dataY: number) { - // loop through and remove any annotations that no longer exist - } - @action restoreView = (data: Doc) => {}; - // 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({ @@ -188,7 +116,6 @@ export class Histogram extends React.Component { title: 'histogram doc selection' + this._currSelected, }); PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc); - // anchor.presDataVizSelection = this._currSelected ? new List([this._currSelected]) : undefined; return anchor; }; @@ -200,6 +127,7 @@ export class Histogram extends React.Component { return this.props.width - this.props.margin.left - this.props.margin.right; } + // 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; @@ -216,21 +144,24 @@ export class Histogram extends React.Component { return data; } + // outlines the bar selected / hovered over highlightSelectedBar = (changeSelectedVariables: boolean, svg: any, eachRectWidth: any, pointerX: any, xAxisTitle: any, yAxisTitle: any, histDataSet: any) => { var sameAsCurrent: boolean; var barCounter = -1; const selected = svg.selectAll('.histogram-bar').filter((d: any) => { - barCounter++; + 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; }) => data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\ data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { } } + // draws the histogram drawChart = (dataSet: any, width: number, height: number) => { 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)] @@ -263,6 +194,8 @@ export class Histogram extends React.Component { 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 valid = true; Object.keys(dataSet[0]).map(key => { @@ -289,6 +222,7 @@ export class Histogram extends React.Component { histDataSet = histStringDataSet } + // initial graph and binning data for histogram var svg = (this._histogramSvg = d3 .select(this._histogramRef.current) .append("svg") @@ -298,7 +232,6 @@ export class Histogram extends React.Component { .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 ]); @@ -314,6 +247,8 @@ export class Histogram extends React.Component { .range([0, Number.isInteger(this.rangeVals.xMin!)? (width-eachRectWidth) : width ]) var xAxis; + // more calculations based on bins + // x-axis if (!this.numericalXData) { // reorganize if the data is strings rather than numbers // uniqueArr.sort() histDataSet.sort() @@ -331,7 +266,7 @@ export class Histogram extends React.Component { bins.forEach(d => d.x0 = d.x0!) xAxis = d3.axisBottom(x) .ticks(bins.length-1) - .tickFormat( i => uniqueArr[i]) + .tickFormat( i => uniqueArr[i.valueOf()] as string) .tickPadding(10) x.range([0, width-eachRectWidth]) x.domain([0, bins.length-1]) @@ -343,10 +278,10 @@ export class Histogram extends React.Component { xAxis = d3.axisBottom(x) .ticks(numBins-1) } + // y-axis const maxFrequency = this.numericalYData? d3.max(histDataSet, function(d: any) { return Number(d[yAxisTitle]!.replace(/\$/g, '').replace(/\%/g, '').replace(/\ { 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) @@ -378,12 +314,11 @@ export class Histogram extends React.Component { 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) - var selected = this.selectedData; + // axis titles svg.append("text") .attr("transform", "translate(" + (width/2) + " ," + (height+40) + ")") .style("text-anchor", "middle") @@ -395,6 +330,9 @@ export class Histogram extends React.Component { .style("text-anchor", "middle") .text(yAxisTitle); d3.format('.0f') + + // draw bars + var selected = this.selectedData; svg.selectAll("rect") .data(bins) .enter() diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx index 0fd4f6b54..d4570dee2 100644 --- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx @@ -40,12 +40,12 @@ export class PieChart extends React.Component { private _piechartRef: React.RefObject = React.createRef(); private _piechartSvg: d3.Selection | undefined; private byCategory: boolean = true; // whether the data is organized by category or by specified number percentages/ratios - @observable _currSelected: any | undefined = undefined; - private curSliceSelected: any = undefined; - private selectedData: any = undefined; - private hoverOverData: any = undefined; - // TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates + @observable _currSelected: any | undefined = undefined; // Object of selected slice + private curSliceSelected: any = undefined; // d3 data of selected slice + private selectedData: any = undefined; // Selection of selected slice + private hoverOverData: any = undefined; // Selection of slice being hovered over + // 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); if (this.props.axes.length < 1) return []; @@ -56,7 +56,6 @@ export class PieChart extends React.Component { ?.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]])})) }; - var ax0 = this.props.axes[0]; var ax1 = this.props.axes[1]; if (/\d/.test(this.props.pairs[0][ax0])) { this.byCategory = false; } @@ -64,6 +63,7 @@ export class PieChart extends React.Component { ?.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]]) })) } + @computed get defaultGraphTitle(){ var ax0 = this.props.axes[0]; var ax1 = (this.props.axes.length>1)? this.props.axes[1] : undefined; @@ -72,28 +72,13 @@ export class PieChart extends React.Component { } else return ax1 + " by " + ax0 + " Pie 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 + .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 incomingSelected() { - // return selected x and y axes - // otherwise, use the selection of whatever is linked to us - return this.incomingLinks // all links that are pointing to this node - .map(anchor => DocumentManager.Instance.getFirstDocumentView(anchor)?.ComponentView as DataVizBox) // get their data viz boxes - .filter(dvb => dvb) - .map(dvb => dvb.pairs?.filter((pair: { [x: string]: any; }) => 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 } { - if (!this.byCategory){ - const data = this.data(this._piechartData); - 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} - } + componentWillUnmount() { Array.from(Object.keys(this._disposers)).forEach(key => this._disposers[key]()); } @@ -103,79 +88,14 @@ export class PieChart extends React.Component { ({ dataSet, w, h }) => { if (dataSet!.length>0) { this.drawChart(dataSet, 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 } ); - this._disposers.annos = reaction( - () => DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']), - annotations => { - // modify how d3 renders so that anything in this annotations list would be potentially highlighted in some way - // could be blue colored to make it look like anchor - // this.drawAnnotations() - // loop through annotations and draw them - annotations.forEach(a => this.drawAnnotations(Number(a.x), Number(a.y))); - // this.drawAnnotations(annotations.x, annotations.y); - }, - { fireImmediately: true } - ); - this._disposers.highlights = reaction( - () => ({ - selected: this._currSelected, - incomingSelected: this.incomingSelected, - }), - ({ 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); - incomingSelected?.forEach((pair: any) => this.drawAnnotations(Number(pair[this.props.axes[0]]), Number(pair[this.props.axes[1]]))); - }, - { fireImmediately: true } - ); - }; - - // anything that doesn't need to be recalculated should just be stored as drawCharts (i.e. computed values) and drawChart is gonna iterate over these observables and generate svgs based on that - - clearAnnotations = () => { - const elements = document.querySelectorAll('.datapoint'); - for (let i = 0; i < elements.length; i++) { - const element = elements[i]; - element.classList.remove('brushed'); - element.classList.remove('selected'); - } - }; - // gets called whenever the "data_annotations" fields gets updated - drawAnnotations = (dataX: number, dataY: number, selected?: boolean) => { - // TODO: nda - can optimize this by having some sort of mapping of the x and y values to the individual circle elements - // loop through all html elements with class .circle-d1 and find the one that has "data-x" and "data-y" attributes that match the dataX and dataY - // if it exists, then highlight it - // if it doesn't exist, then remove the highlight - const elements = document.querySelectorAll('.datapoint'); - for (let i = 0; i < elements.length; i++) { - const element = elements[i]; - const x = element.getAttribute('data-x'); - const y = element.getAttribute('data-y'); - if (x === dataX.toString() && y === dataY.toString()) { - element.classList.add(selected ? 'selected' : 'brushed'); - } - // TODO: nda - this remove highlight code should go where we remove the links - // } else { - // } - } }; - removeAnnotations(dataX: number, dataY: number) { - // loop through and remove any annotations that no longer exist - } - @action restoreView = (data: Doc) => {}; - // 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({ @@ -183,7 +103,6 @@ export class PieChart extends React.Component { title: 'piechart doc selection' + this._currSelected, }); PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.dataDoc); - // anchor.presDataVizSelection = this._currSelected ? new List([this._currSelected]) : undefined; return anchor; }; @@ -195,15 +114,7 @@ export class PieChart extends React.Component { return this.props.width - this.props.margin.left - this.props.margin.right; } - // TODO: nda - use this everyewhere we update currSelected? - @action - setCurrSelected(x?: number, y?: number) { - // TODO: nda - get rid of svg element in the list? - this._currSelected = x !== undefined && y !== undefined ? { x, y } : undefined; - this.props.pairs.forEach(pair => pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y && (pair.selected = true)); - this.props.pairs.forEach(pair => (pair.selected = pair[this.props.axes[0]] === x && pair[this.props.axes[1]] === y ? true : undefined)); - } - + // 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; @@ -220,15 +131,16 @@ export class PieChart extends React.Component { 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]; - var p3 = [arc.centroid(d)[0]*2, arc.centroid(d)[1]*2]; - var p2 = [radius*Math.sin(d.startAngle), -radius*Math.cos(d.startAngle)]; - var p4 = [radius*Math.sin(d.endAngle), -radius*Math.cos(d.endAngle)]; + 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; @@ -241,10 +153,10 @@ export class PieChart extends React.Component { 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) { + 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]]) @@ -264,6 +176,7 @@ export class PieChart extends React.Component { } } + // 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(); @@ -271,18 +184,8 @@ export class PieChart extends React.Component { 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 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 + ")"); + // converts data into Objects var data = this.data(dataSet); var pieDataSet = dataSet.filter((d: { [x: string]: unknown; }) => { var valid = true; @@ -293,12 +196,12 @@ export class PieChart extends React.Component { }); if (this.byCategory){ let uniqueCategories = [...new Set(data)] - var pieStringDataSet: { frequency: number, [percentField]: string }[] = []; + var pieStringDataSet: { frequency: number }[] = []; for (let i=0; i each[percentField]==data[i]) + let sliceData = pieStringDataSet.filter((each: any) => each[percentField]==data[i]) sliceData[0].frequency = sliceData[0].frequency + 1; } pieDataSet = pieStringDataSet @@ -309,11 +212,23 @@ export class PieChart extends React.Component { 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 + ")"); var pie = d3.pie(); 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) @@ -331,6 +246,7 @@ export class PieChart extends React.Component { || ((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)) @@ -359,11 +275,12 @@ export class PieChart extends React.Component { function(d) { return (selected && d.startAngle==selected.startAngle && d.endAngle==selected.endAngle)? 'slice hover' : 'slice'; }: function(d) {return 'slice'}) - .attr("d", arc) + .attr('d', arc) .on('click', onPointClick) .on('mouseover', onHover) .on('mouseout', mouseOut); + // adding labels trackDuplicates = {}; data.forEach((eachData: any) => !trackDuplicates[eachData]? trackDuplicates[eachData] = 0: null) arcs.append("text") diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 38dd62d8d..277ee83ec 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -32,6 +32,7 @@ interface TableBoxProps { @observer export class TableBox extends React.Component { + // 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); @@ -52,6 +53,7 @@ export class TableBox extends React.Component { 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(); const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null); @@ -88,7 +90,7 @@ export class TableBox extends React.Component { setupMoveUpEvents( {}, e, - e => { + 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!); @@ -144,6 +146,7 @@ export class TableBox extends React.Component { if (containsData){ return (
{ + // selecting a row const selected = Cast(this.props.layoutDoc.selected, listSpec("string"), null); if (selected.includes(guid)) selected.splice(selected.indexOf(guid), 1); else { @@ -153,6 +156,7 @@ export class TableBox extends React.Component { background: StrListCast(this.props.layoutDoc.selected).includes(guid) ? 'lightgrey' : '' }}> {this.columns.map(col => ( (this.props.layoutDoc.selected)? + // each cell @@ -168,6 +172,7 @@ export class TableBox extends React.Component { ); } else return ( + // when it is a brushed table and the incoming table doesn't have any rows selected
Selected rows of data from the incoming DataVizBox to display.
diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts index e1ff6f8eb..10bfb0c64 100644 --- a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts +++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts @@ -34,7 +34,6 @@ export const createLineGenerator = (xScale: d3.ScaleLinear, height: number, xScale: d3.ScaleLinear) => { - console.log('x axis creator being called'); g.attr('class', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickSize(15)); }; -- cgit v1.2.3-70-g09d2 From 481a847b1074c9544e947e409b96778ac4d4f852 Mon Sep 17 00:00:00 2001 From: srichman333 Date: Wed, 9 Aug 2023 15:24:03 -0400 Subject: numerical y axis data fix --- .../views/nodes/DataVizBox/components/Histogram.tsx | 11 ++++------- .../views/nodes/DataVizBox/components/PieChart.tsx | 21 +++++++++------------ .../views/nodes/DataVizBox/components/TableBox.tsx | 4 ++-- 3 files changed, 15 insertions(+), 21 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx index e04d37094..ed663006f 100644 --- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx +++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx @@ -1,13 +1,10 @@ import { observer } from "mobx-react"; -import { Doc, DocListCast, StrListCast } from "../../../../../fields/Doc"; +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 { DocumentManager } from "../../../../util/DocumentManager"; -import { Id } from "../../../../../fields/FieldSymbols"; -import { DataVizBox } from "../DataVizBox"; import { PinProps, PresBox } from "../../trails"; import { Docs } from "../../../../documents/Documents"; import { List } from "../../../../../fields/List"; @@ -299,7 +296,7 @@ export class Histogram extends React.Component { } // y-axis const maxFrequency = this.numericalYData? d3.max(histDataSet, function(d: any) { - return Number(d[yAxisTitle]!.replace(/\$/g, '').replace(/\%/g, '').replace(/\ { .attr("transform", this.numericalYData? function (d) { var eachData = histDataSet.filter((data: { [x: string]: number; }) => {return data[xAxisTitle]==d[0]}) - var length = eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\ {return data[xAxisTitle]==d[0]}) - var length = eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { .attr("fill", (d, i)=>{ var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number; }; }) => { try { - return each[percentField].replace(/[^0-9]/g,"")==d.data.toString().replace(/[^0-9]/g,"") + return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]] trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1; } - var accessByName = descriptionField? dataPoint[descriptionField] : dataPoint[percentField]; var sliceColor; - var sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::')); - sliceColors.map(each => {if (each[0]==StrCast(accessByName)) sliceColor = each[1]}); + if (dataPoint){ + var accessByName = descriptionField? dataPoint[descriptionField] : dataPoint[percentField]; + var sliceColors = StrListCast(this.props.layoutDoc.pieSliceColors).map(each => each.split('::')); + 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) { @@ -292,7 +291,7 @@ export class PieChart extends React.Component { .text(function(d){ var possibleDataPoints = pieDataSet.filter((each: { [x: string]: any | { valueOf(): number; }; }) => { try { - return each[percentField].replace(/[^0-9]/g,"")==d.data.toString().replace(/[^0-9]/g,"") + return Number(each[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\ { dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]] trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1; } - return dataPoint[percentField]! - + (!descriptionField? '' : (' - ' + dataPoint[descriptionField]))!}) - + return dataPoint? dataPoint[percentField]! + (!descriptionField? '' : (' - ' + dataPoint[descriptionField]))! : ''}) }; @action changeSelectedColor = (color: string) => { diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index 277ee83ec..f80cbdf99 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -123,10 +123,10 @@ export class TableBox extends React.Component { const newAxes = this.props.axes; if (newAxes.includes(col)) { newAxes.splice(newAxes.indexOf(col), 1); - } else if (newAxes.length >= 1) { + } else if (newAxes.length > 1) { newAxes[1] = col; } else { - newAxes[0] = col; + newAxes.push(col); } this.props.selectAxes(newAxes); }) -- cgit v1.2.3-70-g09d2 From da893c2739c9589bac04c00df3d22ebbfd78c09d Mon Sep 17 00:00:00 2001 From: srichman333 Date: Sun, 13 Aug 2023 17:13:31 -0400 Subject: LineChart ui fixes + selected columns bolded --- .../views/nodes/DataVizBox/components/LineChart.tsx | 4 ++-- .../views/nodes/DataVizBox/components/TableBox.tsx | 17 ++++++++--------- 2 files changed, 10 insertions(+), 11 deletions(-) (limited to 'src/client/views/nodes/DataVizBox/components/TableBox.tsx') diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx index 49ef577e4..d27aaafbc 100644 --- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx +++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx @@ -348,7 +348,7 @@ export class LineChart extends React.Component { tooltip .html(() => `(${d0.x},${d0.y})`) // text content for tooltip .style('pointer-events', 'none') - .style('transform', `translate(${xScale(d0.x) - this.width / 2}px,${yScale(d0.y) - 30}px)`); + .style('transform', `translate(${xScale(d0.x)-this.width}px,${yScale(d0.y)}px)`); } render() { @@ -357,7 +357,7 @@ export class LineChart extends React.Component { 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]; if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle; - const selectedPt = this._currSelected ? `x: ${this._currSelected.x} y: ${this._currSelected.y}` : 'none'; + const selectedPt = this._currSelected ? `{ ${this.props.axes[0]}: ${this._currSelected.x} ${this.props.axes[1]}: ${this._currSelected.y} }` : 'none'; 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]]) ? (
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx index f80cbdf99..f56d34fa6 100644 --- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx +++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx @@ -81,7 +81,8 @@ export class TableBox extends React.Component { key={this.columns.indexOf(col)} ref={header as any} style={{ - color: this.props.axes.slice().reverse().lastElement() === col ? 'green' : this.props.axes.lastElement() === col ? 'red' : undefined, + 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' }} onPointerDown={e => { @@ -151,17 +152,15 @@ export class TableBox extends React.Component { if (selected.includes(guid)) selected.splice(selected.indexOf(guid), 1); else { selected.push(guid)}; - })} style={ - { fontWeight: StrListCast(this.props.layoutDoc.selected).includes(guid) ? 'bold' : '' , width: '110%', - background: StrListCast(this.props.layoutDoc.selected).includes(guid) ? 'lightgrey' : '' }}> - {this.columns.map(col => ( - (this.props.layoutDoc.selected)? + })} style={{ background: StrListCast(this.props.layoutDoc.selected).includes(guid) ? 'lightgrey' : '', width: '110%' }}> + {this.columns.map(col => { // each cell -
- : - ))} + )})} ); } -- cgit v1.2.3-70-g09d2
{p[col]} + var colSelected = this.props.axes[0]==col || this.props.axes[1]==col; + return ( + {p[col]} {p[col]}