aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/DataVizBox
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/DataVizBox')
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx174
-rw-r--r--src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx65
-rw-r--r--src/client/views/nodes/DataVizBox/components/Histogram.tsx247
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx153
-rw-r--r--src/client/views/nodes/DataVizBox/components/PieChart.tsx216
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx116
-rw-r--r--src/client/views/nodes/DataVizBox/utils/D3Utils.ts29
7 files changed, 521 insertions, 479 deletions
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index 60c5fdba2..15187b4e4 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -1,34 +1,37 @@
+/* eslint-disable react/jsx-props-no-spreading */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { Checkbox } from '@mui/material';
import { Colors, Toggle, ToggleType, Type } from 'browndash-components';
import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { emptyFunction, returnEmptyString, returnFalse, returnOne, setupMoveUpEvents } from '../../../../Utils';
+import { returnEmptyString, returnFalse, returnOne, setupMoveUpEvents } from '../../../../ClientUtils';
+import { emptyFunction } from '../../../../Utils';
import { Doc, DocListCast, Field, Opt, StrListCast } from '../../../../fields/Doc';
import { InkTool } from '../../../../fields/InkField';
import { List } from '../../../../fields/List';
-import { listSpec } from '../../../../fields/Schema';
import { Cast, CsvCast, DocCast, NumCast, StrCast } from '../../../../fields/Types';
import { CsvField } from '../../../../fields/URLField';
import { TraceMobx } from '../../../../fields/util';
-import { DocUtils, Docs } from '../../../documents/Documents';
-import { DocumentManager } from '../../../util/DocumentManager';
+import { DocUtils } from '../../../documents/DocUtils';
+import { DocumentType } from '../../../documents/DocumentTypes';
+import { Docs } from '../../../documents/Documents';
import { UndoManager, undoable } from '../../../util/UndoManager';
+import { ContextMenu } from '../../ContextMenu';
import { ViewBoxAnnotatableComponent, ViewBoxInterface } from '../../DocComponent';
import { MarqueeAnnotator } from '../../MarqueeAnnotator';
+import { PinProps } from '../../PinFuncs';
import { SidebarAnnos } from '../../SidebarAnnos';
import { AnchorMenu } from '../../pdf/AnchorMenu';
import { GPTPopup, GPTPopupMode } from '../../pdf/GPTPopup/GPTPopup';
import { DocumentView } from '../DocumentView';
-import { FocusViewOptions, FieldView, FieldViewProps } from '../FieldView';
-import { PinProps } from '../trails';
+import { FieldView, FieldViewProps } from '../FieldView';
+import { FocusViewOptions } from '../FocusViewOptions';
import './DataVizBox.scss';
import { Histogram } from './components/Histogram';
import { LineChart } from './components/LineChart';
import { PieChart } from './components/PieChart';
import { TableBox } from './components/TableBox';
-import { Checkbox } from '@mui/material';
-import { ContextMenu } from '../../ContextMenu';
export enum DataVizView {
TABLE = 'table',
@@ -64,9 +67,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
setupMoveUpEvents(
this,
e,
- action(e => {
+ action(moveEv => {
MarqueeAnnotator.clearAnnotations(this._savedAnnotations);
- this._marqueeref.current?.onInitiateSelection([e.clientX, e.clientY]);
+ this._marqueeref.current?.onInitiateSelection([moveEv.clientX, moveEv.clientY]);
return true;
}),
returnFalse,
@@ -97,7 +100,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
// all CSV records in the dataset (that aren't an empty row)
@computed.struct get records() {
- var records = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href);
+ const records = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href);
return records?.filter(record => Object.keys(record).some(key => record[key])) ?? [];
}
@@ -112,11 +115,15 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
@computed.struct get axes() {
return StrListCast(this.layoutDoc._dataViz_axes);
}
- selectAxes = (axes: string[]) => (this.layoutDoc._dataViz_axes = new List<string>(axes));
+ selectAxes = (axes: string[]) => {
+ this.layoutDoc._dataViz_axes = new List<string>(axes);
+ };
@computed.struct get titleCol() {
return StrCast(this.layoutDoc._dataViz_titleCol);
}
- selectTitleCol = (titleCol: string) => (this.layoutDoc._dataViz_titleCol = titleCol);
+ selectTitleCol = (titleCol: string) => {
+ this.layoutDoc._dataViz_titleCol = titleCol;
+ };
@action // pinned / linked anchor doc includes selected rows, graph titles, and graph colors
restoreView = (data: Doc) => {
@@ -126,7 +133,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
this.layoutDoc.dataViz_histogram_barColors = Field.Copy(data.dataViz_histogram_barColors);
this.layoutDoc.dataViz_histogram_defaultColor = data.dataViz_histogram_defaultColor;
this.layoutDoc.dataViz_pie_sliceColors = Field.Copy(data.dataViz_pie_sliceColors);
- Object.keys(this.layoutDoc).map(key => {
+ Object.keys(this.layoutDoc).forEach(key => {
if (key.startsWith('dataViz_histogram_title') || key.startsWith('dataViz_lineChart_title') || key.startsWith('dataViz_pieChart_title')) {
this.layoutDoc['_' + key] = data[key];
}
@@ -152,7 +159,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
annotationOn: this.Document,
// when we clear selection -> we should have it so chartBox getAnchor returns undefined
// this is for when we want the whole doc (so when the chartBox getAnchor returns without a marker)
- /*put in some options*/
+ /* put in some options */
});
anchor.config_dataViz = this.dataVizView;
anchor.config_dataVizAxes = this.axes.length ? new List<string>(this.axes) : undefined;
@@ -160,24 +167,21 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
anchor.dataViz_histogram_barColors = Field.Copy(this.layoutDoc.dataViz_histogram_barColors);
anchor.dataViz_histogram_defaultColor = this.layoutDoc.dataViz_histogram_defaultColor;
anchor.dataViz_pie_sliceColors = Field.Copy(this.layoutDoc.dataViz_pie_sliceColors);
- Object.keys(this.layoutDoc).map(key => {
+ Object.keys(this.layoutDoc).forEach(key => {
if (key.startsWith('dataViz_histogram_title') || key.startsWith('dataViz_lineChart_title') || key.startsWith('dataViz_pieChart_title')) {
anchor[key] = this.layoutDoc[key];
}
});
this.addDocument(anchor);
- //addAsAnnotation && this.addDocument(anchor);
+ // addAsAnnotation && this.addDocument(anchor);
return anchor;
};
createNoteAnnotation = () => {
- const createFunc = undoable(
- action(() => {
- const note = this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false), ['latitude', 'longitude', '-linkedTo']);
- }),
- 'create note annotation'
- );
+ const createFunc = undoable(() => {
+ this._sidebarRef.current?.anchorMenuClick(this.getAnchor(false), ['latitude', 'longitude', '-linkedTo']);
+ }, 'create note annotation');
if (!this.layoutDoc.layout_showSidebar) {
this.toggleSidebar();
setTimeout(createFunc);
@@ -194,7 +198,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
this.layoutDoc._width = this.layoutDoc._layout_showSidebar ? NumCast(this.layoutDoc._width) * 1.2 : Math.max(20, NumCast(this.layoutDoc._width) - prevWidth);
};
@computed get SidebarShown() {
- return this.layoutDoc._layout_showSidebar ? true : false;
+ return !!this.layoutDoc._layout_showSidebar;
}
@computed get sidebarHandle() {
return (
@@ -208,7 +212,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
}}
onPointerDown={this.sidebarBtnDown}>
- <FontAwesomeIcon style={{ color: Colors.WHITE }} icon={'comment-alt'} size="sm" />
+ <FontAwesomeIcon style={{ color: Colors.WHITE }} icon="comment-alt" size="sm" />
</div>
);
}
@@ -220,7 +224,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
setupMoveUpEvents(
this,
e,
- (e, down, delta) =>
+ (moveEv, down, delta) =>
runInAction(() => {
const localDelta = this._props
.ScreenToLocalTransform()
@@ -248,7 +252,9 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
options.didMove = true;
this.toggleSidebar();
}
- return new Promise<Opt<DocumentView>>(res => DocumentManager.Instance.AddViewRenderedCb(doc, dv => res(dv)));
+ return new Promise<Opt<DocumentView>>(res => {
+ DocumentView.addViewRenderedCb(doc, dv => res(dv));
+ });
};
@computed get sidebarWidthPercent() {
return StrCast(this.layoutDoc._layout_sidebarWidthPercent, '0%');
@@ -268,32 +274,32 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey]).url.href)) this.fetchData();
this._disposers.datavis = reaction(
() => {
- if (this.layoutDoc.dataViz_schemaLive == undefined) this.layoutDoc.dataViz_schemaLive = true;
+ if (this.layoutDoc.dataViz_schemaLive === undefined) this.layoutDoc.dataViz_schemaLive = true;
const getFrom = DocCast(this.layoutDoc.dataViz_asSchema);
- const keys = Cast(getFrom?.schema_columnKeys, listSpec('string'))?.filter(key => key != 'text');
- if (!keys) return;
- const children = DocListCast(getFrom[Doc.LayoutFieldKey(getFrom)]);
- var current: { [key: string]: string }[] = [];
+ if (!getFrom?.schema_columnKeys) return undefined;
+ const keys = StrListCast(getFrom?.schema_columnKeys).filter(key => key !== 'text');
+ const children = DocListCast(getFrom?.[Doc.LayoutFieldKey(getFrom)]);
+ const current: { [key: string]: string }[] = [];
children
.filter(child => child)
.forEach(child => {
const row: { [key: string]: string } = {};
keys.forEach(key => {
- var cell = child[key];
- if (cell && (cell as string)) cell = cell.toString().replace(/\,/g, '');
+ let cell = child[key];
+ if (cell && (cell as string)) cell = cell.toString().replace(/,/g, '');
row[key] = StrCast(cell);
});
current.push(row);
});
if (!this.layoutDoc._dataViz_schemaOG) {
// makes a copy of the original table for the "live" toggle
- let csvRows = [];
+ const csvRows = [];
csvRows.push(keys.join(','));
for (let i = 0; i < children.length - 1; i++) {
- let eachRow = [];
+ const eachRow = [];
for (let j = 0; j < keys.length; j++) {
- var cell = children[i][keys[j]];
- if (cell && (cell as string)) cell = cell.toString().replace(/\,/g, '');
+ let cell = children[i][keys[j]];
+ if (cell && (cell as string)) cell = cell.toString().replace(/,/g, '');
eachRow.push(cell);
}
csvRows.push(eachRow);
@@ -307,19 +313,19 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
}
const ogDoc = this.layoutDoc._dataViz_schemaOG as Doc;
const ogHref = CsvCast(ogDoc[this.fieldKey]) ? CsvCast(ogDoc[this.fieldKey]).url.href : undefined;
- const href = CsvCast(this.Document[this.fieldKey]).url.href;
+ const { href } = CsvCast(this.Document[this.fieldKey]).url;
if (ogHref && !DataVizBox.datasetSchemaOG.has(href)) {
// sets original dataset to the var
const lastRow = current.pop();
DataVizBox.datasetSchemaOG.set(href, current);
current.push(lastRow!);
- fetch('/csvData?uri=' + ogHref).then(res => res.json().then(action(res => !res.errno && DataVizBox.datasetSchemaOG.set(href, res))));
+ fetch('/csvData?uri=' + ogHref).then(res => res.json().then(action(jsonRes => !jsonRes.errno && DataVizBox.datasetSchemaOG.set(href, jsonRes))));
}
return current;
},
current => {
if (current) {
- const href = CsvCast(this.Document[this.fieldKey]).url.href;
+ const { href } = CsvCast(this.Document[this.fieldKey]).url;
if (this.layoutDoc.dataViz_schemaLive) DataVizBox.dataset.set(href, current);
else DataVizBox.dataset.set(href, DataVizBox.datasetSchemaOG.get(href)!);
}
@@ -331,8 +337,8 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
fetchData = () => {
if (!this.Document.dataViz_asSchema) {
DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, []); // assign temporary dataset as a lock to prevent duplicate server requests
- fetch('/csvData?uri=' + this.dataUrl?.url.href) //
- .then(res => res.json().then(action(res => !res.errno && DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, res))));
+ fetch('/csvData?uri=' + (this.dataUrl?.url.href ?? '')) //
+ .then(res => res.json().then(action(jsonRes => !jsonRes.errno && DataVizBox.dataset.set(CsvCast(this.dataDoc[this.fieldKey]).url.href, jsonRes))));
}
};
@@ -345,7 +351,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
records: this.records,
axes: this.axes,
titleCol: this.titleCol,
- //width: this.SidebarShown? this._props.PanelWidth()*.9/1.2: this._props.PanelWidth() * 0.9,
+ // width: this.SidebarShown? this._props.PanelWidth()*.9/1.2: this._props.PanelWidth() * 0.9,
height: (this._props.PanelHeight() / scale - 32) /* height of 'change view' button */ * 0.9,
width: ((this._props.PanelWidth() - this.sidebarWidth()) / scale) * 0.9,
margin: { top: 10, right: 25, bottom: 75, left: 45 },
@@ -353,11 +359,13 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
if (!this.records.length) return 'no data/visualization';
switch (this.dataVizView) {
case DataVizView.TABLE: return <TableBox {...sharedProps} docView={this.DocumentView} selectAxes={this.selectAxes} selectTitleCol={this.selectTitleCol}/>;
- case DataVizView.LINECHART: return <LineChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} vizBox={this} />;
- case DataVizView.HISTOGRAM: return <Histogram {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} />;
- case DataVizView.PIECHART: return <PieChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)}
+ case DataVizView.LINECHART: return <LineChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}} vizBox={this} />;
+ case DataVizView.HISTOGRAM: return <Histogram {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}} />;
+ case DataVizView.PIECHART: return <PieChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => {this._vizRenderer = r ?? undefined;}}
margin={{ top: 10, right: 15, bottom: 15, left: 15 }} />;
+ default:
} // prettier-ignore
+ return null;
}
@action
@@ -369,10 +377,13 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
this._marqueeing = [e.clientX, e.clientY];
const target = e.target as any;
if (e.target && (target.className.includes('endOfContent') || (target.parentElement.className !== 'textLayer' && target.parentElement.parentElement?.className !== 'textLayer'))) {
+ /* empty */
} else {
// if textLayer is hit, then we select text instead of using a marquee so clear out the marquee.
setTimeout(
- action(() => (this._marqueeing = undefined)),
+ action(() => {
+ this._marqueeing = undefined;
+ }),
100
); // bcz: hack .. anchor menu is setup within MarqueeAnnotator so we need to at least create the marqueeAnnotator even though we aren't using it.
@@ -402,34 +413,44 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
@action
changeLiveSchemaCheckbox = () => {
- this.layoutDoc.dataViz_schemaLive = !this.layoutDoc.dataViz_schemaLive
- }
+ this.layoutDoc.dataViz_schemaLive = !this.layoutDoc.dataViz_schemaLive;
+ };
- specificContextMenu = (e: React.MouseEvent): void => {
+ specificContextMenu = (): void => {
const cm = ContextMenu.Instance;
const options = cm.findByDescription('Options...');
const optionItems = options && 'subitems' in options ? options.subitems : [];
optionItems.push({ description: `Analyze with AI`, event: () => this.askGPT(), icon: 'lightbulb' });
!options && cm.addItem({ description: 'Options...', subitems: optionItems, icon: 'eye' });
- }
-
-
+ };
askGPT = action(async () => {
GPTPopup.Instance.setSidebarId('data_sidebar');
GPTPopup.Instance.addDoc = this.sidebarAddDocument;
- GPTPopup.Instance.setDataJson("");
+ GPTPopup.Instance.setDataJson('');
GPTPopup.Instance.setMode(GPTPopupMode.DATA);
- let data = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href);
- let input = JSON.stringify(data);
+ const data = DataVizBox.dataset.get(CsvCast(this.dataDoc[this.fieldKey]).url.href);
+ const input = JSON.stringify(data);
GPTPopup.Instance.setDataJson(input);
GPTPopup.Instance.generateDataAnalysis();
});
render() {
const scale = this._props.NativeDimScaling?.() || 1;
+ const toggleBtn = (name: string, type: DataVizView) => (
+ <Toggle
+ text={name}
+ toggleType={ToggleType.BUTTON}
+ type={Type.SEC}
+ color="black"
+ onClick={() => {
+ this.layoutDoc._dataViz = type;
+ }}
+ toggleStatus={this.layoutDoc._dataViz === type}
+ />
+ );
return !this.records.length ? (
// displays how to get data into the DataVizBox if its empty
- <div className="start-message">To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command 'ctrl + p' to bring the data table to your canvas.</div>
+ <div className="start-message">To create a DataViz box, either import / drag a CSV file into your canvas or copy a data table and use the command (ctrl + p) to bring the data table to your canvas.</div>
) : (
<div
className="dataViz-box"
@@ -445,19 +466,19 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
onWheel={e => e.stopPropagation()}
ref={this._mainCont}>
<div className="datatype-button">
- <Toggle text={' TABLE '} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.TABLE)} toggleStatus={this.layoutDoc._dataViz === DataVizView.TABLE} />
- <Toggle text={'LINECHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.LINECHART)} toggleStatus={this.layoutDoc._dataViz === DataVizView.LINECHART} />
- <Toggle text={'HISTOGRAM'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.HISTOGRAM)} toggleStatus={this.layoutDoc._dataViz === DataVizView.HISTOGRAM} />
- <Toggle text={'PIE CHART'} toggleType={ToggleType.BUTTON} type={Type.SEC} color={'black'} onClick={e => (this.layoutDoc._dataViz = DataVizView.PIECHART)} toggleStatus={this.layoutDoc._dataViz == -DataVizView.PIECHART} />
+ {toggleBtn(' TABLE ', DataVizView.TABLE)}
+ {toggleBtn('LINECHART', DataVizView.LINECHART)}
+ {toggleBtn('HISTOGRAM', DataVizView.HISTOGRAM)}
+ {toggleBtn('PIE CHART', DataVizView.PIECHART)}
</div>
- {(this.layoutDoc && this.layoutDoc.dataViz_asSchema)?(
- <div className={'displaySchemaLive'}>
- <div className={'liveSchema-checkBox'} style={{ width: this._props.width }}>
- <Checkbox color="primary" onChange={this.changeLiveSchemaCheckbox} checked={this.layoutDoc.dataViz_schemaLive as boolean} />
- Display Live Updates to Canvas
+ {this.layoutDoc && this.layoutDoc.dataViz_asSchema ? (
+ <div className="displaySchemaLive">
+ <div className="liveSchema-checkBox" style={{ width: this._props.width }}>
+ <Checkbox color="primary" onChange={this.changeLiveSchemaCheckbox} checked={this.layoutDoc.dataViz_schemaLive as boolean} />
+ Display Live Updates to Canvas
+ </div>
</div>
- </div>
) : null}
{this.renderVizView}
@@ -470,7 +491,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
Document={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
- usePanelWidth={true}
+ usePanelWidth
showSidebar={this.SidebarShown}
nativeWidth={NumCast(this.layoutDoc._nativeWidth)}
whenChildContentsActiveChanged={this.whenChildContentsActiveChanged}
@@ -504,3 +525,18 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() im
);
}
}
+Docs.Prototypes.TemplateMap.set(DocumentType.DATAVIZ, {
+ layout: { view: DataVizBox, dataField: 'data' },
+ options: {
+ acl: '',
+ dataViz_title: '',
+ dataViz_line: '',
+ dataViz_pie: '',
+ dataViz_histogram: '',
+ dataViz: 'table',
+ _layout_fitWidth: true,
+ _layout_reflowHorizontal: true,
+ _layout_reflowVertical: true,
+ _layout_nativeDimEditable: true,
+ },
+});
diff --git a/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx b/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx
index 24023077f..60bc8df18 100644
--- a/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx
+++ b/src/client/views/nodes/DataVizBox/SchemaCSVPopUp.tsx
@@ -1,9 +1,12 @@
+/* eslint-disable jsx-a11y/label-has-associated-control */
+/* eslint-disable jsx-a11y/alt-text */
import { IconButton } from 'browndash-components';
import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { CgClose } from 'react-icons/cg';
-import { Utils, emptyFunction, setupMoveUpEvents } from '../../../../Utils';
+import { ClientUtils, setupMoveUpEvents } from '../../../../ClientUtils';
+import { emptyFunction } from '../../../../Utils';
import { Doc } from '../../../../fields/Doc';
import { StrCast } from '../../../../fields/Types';
import { DragManager } from '../../../util/DragManager';
@@ -14,56 +17,52 @@ interface SchemaCSVPopUpProps {}
@observer
export class SchemaCSVPopUp extends React.Component<SchemaCSVPopUpProps> {
+ // eslint-disable-next-line no-use-before-define
static Instance: SchemaCSVPopUp;
- @observable
- public dataVizDoc: Doc | undefined = undefined;
+ @observable public dataVizDoc: Doc | undefined = undefined;
+ @observable public view: DocumentView | undefined = undefined;
+ @observable public target: Doc | undefined = undefined;
+ @observable public visible: boolean = false;
+
+ constructor(props: SchemaCSVPopUpProps) {
+ super(props);
+ makeObservable(this);
+ SchemaCSVPopUp.Instance = this;
+ }
+
@action
public setDataVizDoc = (doc: Doc) => {
this.dataVizDoc = doc;
};
- @observable
- public view: DocumentView | undefined = undefined;
@action
public setView = (docView: DocumentView) => {
this.view = docView;
};
- @observable
- public target: Doc | undefined = undefined;
@action
public setTarget = (doc: Doc) => {
this.target = doc;
};
- @observable
- public visible: boolean = false;
@action
public setVisible = (vis: boolean) => {
this.visible = vis;
};
- constructor(props: SchemaCSVPopUpProps) {
- super(props);
- makeObservable(this);
- SchemaCSVPopUp.Instance = this;
- }
-
- dataBox = () => {
- return (
- <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
- {this.heading('Schema Table as Data Visualization Doc')}
- <div className="image-content-wrapper">
- <div className="img-wrapper">
- <div className="img-container" onPointerDown={e => this.drag(e)}>
- <img width={150} height={150} src={'/assets/dataVizBox.png'} />
- </div>
+ dataBox = () => (
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
+ {this.heading('Schema Table as Data Visualization Doc')}
+ <div className="image-content-wrapper">
+ <div className="img-wrapper">
+ <div className="img-container" onPointerDown={e => this.drag(e)}>
+ <img width={150} height={150} src="/assets/dataVizBox.png" />
</div>
</div>
</div>
- );
- };
+ </div>
+ );
heading = (headingText: string) => (
<div className="summary-heading">
@@ -78,24 +77,22 @@ export class SchemaCSVPopUp extends React.Component<SchemaCSVPopUpProps> {
setupMoveUpEvents(
{},
e,
- e => {
+ moveEv => {
const sourceAnchorCreator = () => this.dataVizDoc!;
- const targetCreator = (annotationOn: Doc | undefined) => {
+ const targetCreator = () => {
const embedding = Doc.MakeEmbedding(this.dataVizDoc!);
return embedding;
};
- if (this.view && sourceAnchorCreator && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
- DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.view, sourceAnchorCreator, targetCreator), downX, downY, {
- dragComplete: e => {
- this.setVisible(false);
- },
+ if (this.view && sourceAnchorCreator && !ClientUtils.isClick(moveEv.clientX, moveEv.clientY, downX, downY, Date.now())) {
+ DragManager.StartAnchorAnnoDrag(moveEv.target instanceof HTMLElement ? [moveEv.target] : [], new DragManager.AnchorAnnoDragData(this.view, sourceAnchorCreator, targetCreator), downX, downY, {
+ dragComplete: () => this.setVisible(false),
});
return true;
}
return false;
},
emptyFunction,
- action(e => {})
+ action(() => {})
);
};
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index 6672603f3..79b3e9541 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -12,7 +12,7 @@ import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
import { Docs } from '../../../../documents/Documents';
import { undoable } from '../../../../util/UndoManager';
import { ObservableReactComponent } from '../../../ObservableReactComponent';
-import { PinProps, PresBox } from '../../trails';
+import { PinProps, PinDocView } from '../../../PinFuncs';
import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
import './Chart.scss';
@@ -63,14 +63,13 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
@computed get _histogramData() {
if (this._props.axes.length < 1) return [];
if (this._props.axes.length < 2) {
- var ax0 = this._props.axes[0];
- if (!/[A-Za-z-:]/.test(this._props.records[0][ax0])){
+ const ax0 = this._props.axes[0];
+ if (!/[A-Za-z-:]/.test(this._props.records[0][ax0])) {
this.numericalXData = true;
}
return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]] }));
}
- var ax0 = this._props.axes[0];
- var ax1 = this._props.axes[1];
+ const [ax0, ax1] = this._props.axes;
if (!/[A-Za-z-:]/.test(this._props.records[0][ax0])) {
this.numericalXData = true;
}
@@ -81,11 +80,11 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
}
@computed get defaultGraphTitle() {
- var ax0 = this._props.axes[0];
- var ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
+ const [ax0, ax1] = this._props.axes;
if (this._props.axes.length < 2 || !ax1 || !/\d/.test(this._props.records[0][ax1]) || !this.numericalYData) {
return ax0 + ' Histogram';
- } else return ax0 + ' by ' + ax1 + ' Histogram';
+ }
+ return ax0 + ' by ' + ax1 + ' Histogram';
}
@computed get parentViz() {
@@ -111,14 +110,13 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
);
}
- @action
- restoreView = (data: Doc) => {};
+ restoreView = () => {};
// create a document anchor that stores whatever is needed to reconstruct the viewing state (selection,zoom,etc)
getAnchor = (pinProps?: PinProps) => {
const anchor = Docs.Create.ConfigDocument({
title: 'histogram doc selection' + this._currSelected,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
+ PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
return anchor;
};
@@ -132,36 +130,36 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
// cleans data by converting numerical data to numbers and taking out empty cells
data = (dataSet: any) => {
- var validData = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key])));
+ const validData = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || isNaN(d[key])));
const field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
return !field
? []
: validData.map((d: { [x: string]: any }) =>
!this.numericalXData //
? d[field]
- : +d[field!].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')
+ : +d[field!].replace(/\$/g, '').replace(/%/g, '').replace(/</g, '')
);
};
// 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;
+ let sameAsCurrent: boolean;
+ let barCounter = -1;
const selected = svg.selectAll('.histogram-bar').filter((d: any) => {
barCounter++; // uses the order of bars and width of each bar to find which one the pointer is over
if (barCounter * eachRectWidth <= pointerX && pointerX <= (barCounter + 1) * eachRectWidth) {
- var showSelected = this.numericalYData
- ? this._histogramData.filter((data: { [x: string]: any }) => StrCast(data[xAxisTitle]).replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') == d[0])[0]
- : histDataSet.filter((data: { [x: string]: any }) => data[xAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') == d[0])[0];
+ let showSelected = this.numericalYData
+ ? this._histogramData.filter((data: { [x: string]: any }) => StrCast(data[xAxisTitle]).replace(/$/g, '').replace(/%/g, '').replace(/</g, '') === d[0])[0]
+ : histDataSet.filter((data: { [x: string]: any }) => data[xAxisTitle].replace(/$/g, '').replace(/%/g, '').replace(/</g, '') === d[0])[0];
if (this.numericalXData) {
// calculating frequency
- if (d[0] && d[1] && d[0] != d[1]) {
+ if (d[0] && d[1] && d[0] !== d[1]) {
showSelected = { [xAxisTitle]: d3.min(d) + ' to ' + d3.max(d), frequency: d.length };
} else if (!this.numericalYData) showSelected = { [xAxisTitle]: showSelected[xAxisTitle], frequency: d.length };
}
if (changeSelectedVariables) {
// for when a bar is selected - not just hovered over
- sameAsCurrent = this._currSelected ? showSelected[xAxisTitle] == this._currSelected![xAxisTitle] && showSelected[yAxisTitle] == this._currSelected![yAxisTitle] : false;
+ sameAsCurrent = this._currSelected ? showSelected[xAxisTitle] === this._currSelected![xAxisTitle] && showSelected[yAxisTitle] === this._currSelected![yAxisTitle] : false;
this._currSelected = sameAsCurrent ? undefined : showSelected;
this.selectedData = sameAsCurrent ? undefined : d;
} else this.hoverOverData = d;
@@ -184,16 +182,16 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
const xAxisTitle = Object.keys(dataSet[0])[0];
const yAxisTitle = this.numericalYData ? Object.keys(dataSet[0])[1] : 'frequency';
const uniqueArr: unknown[] = [...new Set(data)];
- var numBins = this.numericalXData && Number.isInteger(data[0]) ? this.rangeVals.xMax! - this.rangeVals.xMin! : uniqueArr.length;
- var translateXAxis = !this.numericalXData || numBins < this.maxBins ? width / (numBins + 1) / 2 : 0;
+ let numBins = this.numericalXData && Number.isInteger(data[0]) ? this.rangeVals.xMax! - this.rangeVals.xMin! : uniqueArr.length;
+ let translateXAxis = !this.numericalXData || numBins < this.maxBins ? width / (numBins + 1) / 2 : 0;
if (numBins > this.maxBins) numBins = this.maxBins;
const startingPoint = this.numericalXData ? this.rangeVals.xMin! : 0;
const endingPoint = this.numericalXData ? this.rangeVals.xMax! : numBins;
// converts data into Objects
- var histDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key])));
+ let histDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || isNaN(d[key])));
if (!this.numericalXData) {
- var histStringDataSet: { [x: string]: unknown }[] = [];
+ const histStringDataSet: { [x: string]: unknown }[] = [];
if (this.numericalYData) {
for (let i = 0; i < dataSet.length; i++) {
histStringDataSet.push({ [yAxisTitle]: dataSet[i][yAxisTitle], [xAxisTitle]: dataSet[i][xAxisTitle] });
@@ -203,15 +201,15 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
histStringDataSet.push({ [yAxisTitle]: 0, [xAxisTitle]: uniqueArr[i] });
}
for (let i = 0; i < data.length; i++) {
- let barData = histStringDataSet.filter(each => each[xAxisTitle] == data[i]);
- histStringDataSet.filter(each => each[xAxisTitle] == data[i])[0][yAxisTitle] = Number(barData[0][yAxisTitle]) + 1;
+ const barData = histStringDataSet.filter(each => each[xAxisTitle] === data[i]);
+ histStringDataSet.filter(each => each[xAxisTitle] === data[i])[0][yAxisTitle] = Number(barData[0][yAxisTitle]) + 1;
}
}
histDataSet = histStringDataSet;
}
// initial graph and binning data for histogram
- var svg = (this._histogramSvg = d3
+ const svg = (this._histogramSvg = d3
.select(this._histogramRef.current)
.append('svg')
.attr('class', 'graph')
@@ -219,23 +217,21 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
.attr('height', height + this._props.margin.top + this._props.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this._props.margin.left + ',' + this._props.margin.top + ')'));
- var x = d3
+ let x = d3
.scaleLinear()
.domain(this.numericalXData ? [startingPoint!, endingPoint!] : [0, numBins])
.range([0, width]);
- var histogram = d3
+ const histogram = d3
.histogram()
- .value(function (d) {
- return d;
- })
+ .value(d => d)
.domain([startingPoint!, endingPoint!])
.thresholds(x.ticks(numBins));
- var bins = histogram(data);
- var eachRectWidth = width / bins.length;
- var graphStartingPoint = bins[0].x1 && bins[1] ? bins[0].x1! - (bins[1].x1! - bins[1].x0!) : 0;
+ const bins = histogram(data);
+ let eachRectWidth = width / bins.length;
+ const 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]);
- var xAxis;
+ let xAxis;
// more calculations based on bins
// x-axis
@@ -244,9 +240,9 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
// uniqueArr.sort()
histDataSet.sort();
for (let i = 0; i < data.length; i++) {
- var index = 0;
+ let index = 0;
for (let j = 0; j < uniqueArr.length; j++) {
- if (uniqueArr[j] == data[i]) {
+ if (uniqueArr[j] === data[i]) {
index = j;
}
}
@@ -254,7 +250,9 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
}
bins.pop();
eachRectWidth = width / bins.length;
- bins.forEach(d => (d.x0 = d.x0!));
+ bins.forEach(d => {
+ d.x0 = d.x0!;
+ });
xAxis = d3
.axisBottom(x)
.ticks(bins.length > 1 ? bins.length - 1 : 1)
@@ -264,12 +262,12 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
x.domain([0, bins.length - 1]);
translateXAxis = eachRectWidth / 2;
} else {
- var allSame = true;
- for (var i = 0; i < bins.length; i++) {
+ let allSame = true;
+ for (let i = 0; i < bins.length; i++) {
if (bins[i] && bins[i][0]) {
- var compare = bins[i][0];
+ const compare = bins[i][0];
for (let j = 1; j < bins[i].length; j++) {
- if (bins[i][j] != compare) allSame = false;
+ if (bins[i][j] !== compare) allSame = false;
}
}
}
@@ -278,8 +276,8 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
eachRectWidth = width / bins.length;
} else {
eachRectWidth = width / (bins.length + 1);
- var tickDiff = bins.length >= 2 ? bins[bins.length - 2].x1! - bins[bins.length - 2].x0! : 0;
- var curDomain = x.domain();
+ const tickDiff = bins.length >= 2 ? bins[bins.length - 2].x1! - bins[bins.length - 2].x0! : 0;
+ const curDomain = x.domain();
x.domain([curDomain[0], curDomain[0] + tickDiff * bins.length]);
}
@@ -287,16 +285,13 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
x.range([0, width - eachRectWidth]);
}
// y-axis
- const maxFrequency = this.numericalYData
- ? d3.max(histDataSet, function (d: any) {
- return d[yAxisTitle] ? Number(d[yAxisTitle]!.replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '')) : 0;
- })
- : d3.max(bins, function (d) {
- return d.length;
- });
- var y = d3.scaleLinear().range([height, 0]);
+ const maxFrequency = this.numericalYData ?
+ d3.max(histDataSet, (d: any) => (d[yAxisTitle] ? Number(d[yAxisTitle]!.replace(/\$/g, '')
+ .replace(/%/g, '').replace(/</g, '')) : 0)) :
+ d3.max(bins, d => d.length); // prettier-ignore
+ const y = d3.scaleLinear().range([height, 0]);
y.domain([0, +maxFrequency!]);
- var yAxis = d3.axisLeft(y).ticks(maxFrequency!);
+ const yAxis = d3.axisLeft(y).ticks(maxFrequency!);
if (this.numericalYData) {
const yScale = scaleCreatorNumerical(0, Number(maxFrequency), height, 0);
yAxisCreator(svg.append('g'), width, yScale);
@@ -311,18 +306,17 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
const onPointClick = action((e: any) => this.highlightSelectedBar(true, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet));
const onHover = action((e: any) => {
this.highlightSelectedBar(false, svg, eachRectWidth, d3.pointer(e)[0], xAxisTitle, yAxisTitle, histDataSet);
+ // eslint-disable-next-line no-use-before-define
updateHighlights();
});
- const mouseOut = action((e: any) => {
+ const mouseOut = action(() => {
this.hoverOverData = undefined;
+ // eslint-disable-next-line no-use-before-define
updateHighlights();
});
const updateHighlights = () => {
- const hoverOverBar = this.hoverOverData;
- const selectedData = this.selectedData;
- svg.selectAll('rect').attr('class', function (d: any) {
- return (hoverOverBar && hoverOverBar[0] == d[0]) || (selectedData && selectedData[0] == d[0]) ? 'histogram-bar hover' : 'histogram-bar';
- });
+ const { hoverOverData: hoverOverBar, selectedData } = this;
+ svg.selectAll('rect').attr('class', (d: any) => ((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);
@@ -332,7 +326,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
.style('text-anchor', 'middle')
.text(xAxisTitle);
svg.append('text')
- .attr('transform', 'rotate(-90)' + ' ' + 'translate( 0, ' + -10 + ')')
+ .attr('transform', 'rotate(-90) translate( 0, ' + -10 + ')')
.attr('x', -(height / 2))
.attr('y', -20)
.style('text-anchor', 'middle')
@@ -340,7 +334,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
d3.format('.0f');
// draw bars
- var selected = this.selectedData;
+ const selected = this.selectedData;
svg.selectAll('rect')
.data(bins)
.enter()
@@ -348,49 +342,34 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
.attr(
'transform',
this.numericalYData
- ? function (d) {
- const eachData = histDataSet.filter((data: { [x: string]: number }) => {
- return data[xAxisTitle] == d[0];
- });
- const length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
+ ? d => {
+ const eachData = histDataSet.filter((hData: { [x: string]: number }) => hData[xAxisTitle] === d[0]);
+ const length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/%/g, '').replace(/</g, '') : 0;
return 'translate(' + x(d.x0!) + ',' + y(length) + ')';
}
- : function (d) {
- return 'translate(' + x(d.x0!) + ',' + y(d.length) + ')';
- }
+ : d => 'translate(' + x(d.x0!) + ',' + y(d.length) + ')'
)
.attr(
'height',
this.numericalYData
- ? function (d) {
- const eachData = histDataSet.filter((data: { [x: string]: number }) => {
- return data[xAxisTitle] == d[0];
- });
- const length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, '') : 0;
+ ? d => {
+ const eachData = histDataSet.filter((hData: { [x: string]: number }) => hData[xAxisTitle] === d[0]);
+ const length = eachData.length ? eachData[0][yAxisTitle].replace(/\$/g, '').replace(/%/g, '').replace(/</g, '') : 0;
return height - y(length);
}
- : function (d) {
- return height - y(d.length);
- }
+ : d => height - y(d.length)
)
.attr('width', eachRectWidth)
- .attr(
- 'class',
- selected
- ? function (d) {
- return selected && selected[0] === d[0] ? 'histogram-bar hover' : 'histogram-bar';
- }
- : function (d) {
- return 'histogram-bar';
- }
- )
+ .attr('class', selected ? d => (selected && selected[0] === d[0] ? 'histogram-bar hover' : 'histogram-bar') : () => 'histogram-bar')
.attr('fill', d => {
- var barColor;
+ let barColor;
const barColors = StrListCast(this._props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
barColors.forEach(each => {
- if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
+ // eslint-disable-next-line prefer-destructuring
+ if (d[0] && d[0].toString() && each[0] === d[0].toString()) barColor = each[1];
else {
const range = StrCast(each[0]).split(' to ');
+ // eslint-disable-next-line prefer-destructuring
if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
}
});
@@ -400,7 +379,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
@action changeSelectedColor = (color: string) => {
this.curBarSelected.attr('fill', color);
- const barName = StrCast(this._currSelected[this._props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ const barName = StrCast(this._currSelected[this._props.axes[0]].replace(/\$/g, '').replace(/%/g, '').replace(/</g, ''));
const barColors = Cast(this._props.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1));
@@ -409,22 +388,24 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
@action eraseSelectedColor = () => {
this.curBarSelected.attr('fill', this._props.layoutDoc.dataViz_histogram_defaultColor);
- const barName = StrCast(this._currSelected[this._props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ const barName = StrCast(this._currSelected[this._props.axes[0]].replace(/\$/g, '').replace(/%/g, '').replace(/</g, ''));
const barColors = Cast(this._props.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
barColors.forEach(each => each.split('::')[0] === barName && barColors.splice(barColors.indexOf(each), 1));
};
updateBarColors = () => {
- var svg = this._histogramSvg;
+ const svg = this._histogramSvg;
if (svg)
svg.selectAll('rect').attr('fill', (d: any) => {
- var barColor;
+ let barColor;
const barColors = StrListCast(this._props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
barColors.forEach(each => {
- if (d[0] && d[0].toString() && each[0] == d[0].toString()) barColor = each[1];
+ // eslint-disable-next-line prefer-destructuring
+ if (d[0] && d[0].toString() && each[0] === d[0].toString()) barColor = each[1];
else {
const range = StrCast(each[0]).split(' to ');
+ // eslint-disable-next-line prefer-destructuring
if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
}
});
@@ -435,39 +416,41 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
render() {
this.updateBarColors();
this._histogramData;
- var curSelectedBarName = '';
- var titleAccessor: any = 'dataViz_histogram_title';
- if (this._props.axes.length == 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
- else if (this._props.axes.length > 0) titleAccessor = titleAccessor + this._props.axes[0];
+ let curSelectedBarName = '';
+ let titleAccessor: any = 'dataViz_histogram_title';
+ if (this._props.axes.length === 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
+ else if (this._props.axes.length > 0) titleAccessor += this._props.axes[0];
if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
if (!this._props.layoutDoc.dataViz_histogram_defaultColor) this._props.layoutDoc.dataViz_histogram_defaultColor = '#69b3a2';
if (!this._props.layoutDoc.dataViz_histogram_barColors) this._props.layoutDoc.dataViz_histogram_barColors = new List<string>();
- var selected = 'none';
+ let selected = 'none';
if (this._currSelected) {
- curSelectedBarName = StrCast(this._currSelected![this._props.axes[0]].replace(/\$/g, '').replace(/\%/g, '').replace(/\</g, ''));
+ curSelectedBarName = StrCast(this._currSelected![this._props.axes[0]].replace(/\$/g, '').replace(/%/g, '').replace(/</g, ''));
selected = '{ ';
- Object.keys(this._currSelected).forEach(key =>
+ Object.keys(this._currSelected).forEach(key => {
key //
? (selected += key + ': ' + this._currSelected[key] + ', ')
- : ''
- );
+ : '';
+ });
selected = selected.substring(0, selected.length - 2) + ' }';
- if (this._props.titleCol!="" && (!this._currSelected["frequency"] || this._currSelected["frequency"]<10)){
- selected+= "\n" + this._props.titleCol + ": "
+ if (this._props.titleCol !== '' && (!this._currSelected.frequency || this._currSelected.frequency < 10)) {
+ selected += '\n' + this._props.titleCol + ': ';
this._tableData.forEach(each => {
- if (this._currSelected[this._props.axes[0]]==each[this._props.axes[0]]) {
- if (this._props.axes[1]){
- if (this._currSelected[this._props.axes[1]]==each[this._props.axes[1]]) selected+= each[this._props.titleCol] + ", ";
- }
- else selected+= each[this._props.titleCol] + ", ";
+ if (this._currSelected[this._props.axes[0]] === each[this._props.axes[0]]) {
+ if (this._props.axes[1]) {
+ if (this._currSelected[this._props.axes[1]] === each[this._props.axes[1]]) selected += each[this._props.titleCol] + ', ';
+ } else selected += each[this._props.titleCol] + ', ';
}
- })
- selected = selected.slice(0,-1).slice(0,-1);
+ });
+ selected = selected.slice(0, -1).slice(0, -1);
}
}
- var selectedBarColor;
- var barColors = StrListCast(this._props.layoutDoc.histogramBarColors).map(each => each.split('::'));
- barColors.forEach(each => each[0] === curSelectedBarName && (selectedBarColor = each[1]));
+ let selectedBarColor;
+ const barColors = StrListCast(this._props.layoutDoc.histogramBarColors).map(each => each.split('::'));
+ barColors.forEach(each => {
+ // eslint-disable-next-line prefer-destructuring
+ each[0] === curSelectedBarName && (selectedBarColor = each[1]);
+ });
if (this._histogramData.length > 0 || !this.parentViz) {
return this._props.axes.length >= 1 ? (
@@ -476,45 +459,51 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
<EditableText
val={StrCast(this._props.layoutDoc[titleAccessor])}
setVal={undoable(
- action(val => (this._props.layoutDoc[titleAccessor] = val as string)),
+ action(val => {
+ this._props.layoutDoc[titleAccessor] = val as string;
+ }),
'Change Graph Title'
)}
- color={'black'}
+ color="black"
size={Size.LARGE}
fillWidth
/>
&nbsp; &nbsp;
<ColorPicker
- tooltip={'Change Default Bar Color'}
+ tooltip="Change Default Bar Color"
type={Type.SEC}
icon={<FaFillDrip />}
selectedColor={StrCast(this._props.layoutDoc.dataViz_histogram_defaultColor)}
- setFinalColor={undoable(color => (this._props.layoutDoc.dataViz_histogram_defaultColor = color), 'Change Default Bar Color')}
- setSelectedColor={undoable(color => (this._props.layoutDoc.dataViz_histogram_defaultColor = color), 'Change Default Bar Color')}
+ setFinalColor={undoable(color => {
+ this._props.layoutDoc.dataViz_histogram_defaultColor = color;
+ }, 'Change Default Bar Color')}
+ setSelectedColor={undoable(color => {
+ this._props.layoutDoc.dataViz_histogram_defaultColor = color;
+ }, 'Change Default Bar Color')}
size={Size.XSMALL}
/>
</div>
<div ref={this._histogramRef} />
- {selected != 'none' ? (
- <div className={'selected-data'}>
+ {selected !== 'none' ? (
+ <div className="selected-data">
Selected: {selected}
&nbsp; &nbsp;
<ColorPicker
- tooltip={'Change Bar Color'}
+ tooltip="Change Bar Color"
type={Type.SEC}
icon={<FaFillDrip />}
- selectedColor={selectedBarColor ? selectedBarColor : this.curBarSelected.attr('fill')}
+ selectedColor={selectedBarColor || this.curBarSelected.attr('fill')}
setFinalColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Bar Color')}
setSelectedColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Bar Color')}
size={Size.XSMALL}
/>
&nbsp;
<IconButton
- icon={<FontAwesomeIcon icon={'eraser'} />}
+ icon={<FontAwesomeIcon icon="eraser" />}
size={Size.XSMALL}
- color={'black'}
+ color="black"
type={Type.SEC}
- tooltip={'Revert to the default bar color'}
+ tooltip="Revert to the default bar color"
onClick={undoable(
action(() => this.eraseSelectedColor()),
'Change Selected Bar Color'
@@ -524,7 +513,7 @@ export class Histogram extends ObservableReactComponent<HistogramProps> {
) : null}
</div>
) : (
- <span className="chart-container"> {'first use table view to select a column to graph'}</span>
+ <span className="chart-container"> first use table view to select a column to graph</span>
);
}
// when it is a brushed table and the incoming table doesn't have any rows selected
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index e093ec648..bc35ab8c8 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -3,18 +3,19 @@ import * as d3 from 'd3';
import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Doc, DocListCast, NumListCast, StrListCast } from '../../../../../fields/Doc';
+import { Doc, DocListCast, NumListCast } from '../../../../../fields/Doc';
import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
import { Docs } from '../../../../documents/Documents';
-import { DocumentManager } from '../../../../util/DocumentManager';
import { undoable } from '../../../../util/UndoManager';
+import {} from '../../../DocComponent';
import { ObservableReactComponent } from '../../../ObservableReactComponent';
-import { PinProps, PresBox } from '../../trails';
+import { PinProps, PinDocView } from '../../../PinFuncs';
import { DataVizBox } from '../DataVizBox';
import { createLineGenerator, drawLine, minMaxRange, scaleCreatorNumerical, xAxisCreator, xGrid, yAxisCreator, yGrid } from '../utils/D3Utils';
import './Chart.scss';
+import { DocumentView } from '../../DocumentView';
export interface DataPoint {
x: number;
@@ -62,7 +63,6 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
return !this.parentViz ? this._props.records : this._tableDataIds.map(rowId => this._props.records[rowId]);
}
@computed get _lineChartData() {
- var guids = StrListCast(this._props.layoutDoc.dataViz_rowIds);
if (this._props.axes.length <= 1) return [];
return this._tableData.map(record => ({ x: Number(record[this._props.axes[0]]), y: Number(record[this._props.axes[1]]) })).sort((a, b) => (a.x < b.x ? -1 : 1));
}
@@ -71,7 +71,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
}
@computed get parentViz() {
return DocCast(this._props.Document.dataViz_parentViz);
- // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links
+ // return LinkManager.Links(this._props.Document) // out of all links
// .filter(link => {
// return link.link_anchor_1 == this._props.Document.dataViz_parentViz;
// }) // get links where this chart doc is the target of the link
@@ -80,7 +80,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
@computed get incomingHighlited() {
// return selected x and y axes
// otherwise, use the selection of whatever is linked to us
- const incomingVizBox = DocumentManager.Instance.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox;
+ const incomingVizBox = DocumentView.getFirstDocumentView(this.parentViz)?.ComponentView as DataVizBox;
const highlitedRowIds = NumListCast(incomingVizBox?.layoutDoc?.dataViz_highlitedRows);
return this._tableData.filter((record, i) => highlitedRowIds.includes(this._tableDataIds[i])); // get all the datapoints they have selected field set by incoming anchor
}
@@ -102,7 +102,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
);
this._disposers.annos = reaction(
() => DocListCast(this._props.dataDoc[this._props.fieldKey + '_annotations']),
- 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()
@@ -175,9 +175,9 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
getAnchor = (pinProps?: PinProps) => {
const anchor = Docs.Create.ConfigDocument({
//
- title: 'line doc selection' + this._currSelected?.x,
+ title: 'line doc selection' + (this._currSelected?.x ?? ''),
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
+ PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
anchor.config_dataVizSelection = this._currSelected ? new List<number>([this._currSelected.x, this._currSelected.y]) : undefined;
return anchor;
};
@@ -191,11 +191,12 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
}
@computed get defaultGraphTitle() {
- var ax0 = this._props.axes[0];
- var ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
+ const ax0 = this._props.axes[0];
+ const ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
if (this._props.axes.length < 2 || !/\d/.test(this._props.records[0][ax0]) || !ax1) {
return ax0 + ' Line Chart';
- } else return ax1 + ' by ' + ax0 + ' Line Chart';
+ }
+ return ax1 + ' by ' + ax0 + ' Line Chart';
}
setupTooltip() {
@@ -215,9 +216,11 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
@action
setCurrSelected(x?: number, y?: number) {
// TODO: nda - get rid of svg element in the list?
- if (this._currSelected && this._currSelected.x == x && this._currSelected.y == y) this._currSelected = undefined;
+ if (this._currSelected && this._currSelected.x === x && this._currSelected.y === y) this._currSelected = undefined;
else this._currSelected = x !== undefined && y !== undefined ? { x, y } : undefined;
- this._props.records.forEach(record => record[this._props.axes[0]] === x && record[this._props.axes[1]] === y && (record.selected = true));
+ this._props.records.forEach(record => {
+ record[this._props.axes[0]] === x && record[this._props.axes[1]] === y && (record.selected = true);
+ });
}
drawDataPoints(data: DataPoint[], idx: number, xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>) {
@@ -241,13 +244,13 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
d3.select(this._lineChartRef.current).select('svg').remove();
d3.select(this._lineChartRef.current).select('.tooltip').remove();
- var { xMin, xMax, yMin, yMax } = rangeVals;
+ let { xMin, xMax, yMin, yMax } = rangeVals;
if (xMin === undefined || xMax === undefined || yMin === undefined || yMax === undefined) {
return;
}
// adding svg
- const margin = this._props.margin;
+ const { margin } = this._props;
const svg = (this._lineChartSvg = d3
.select(this._lineChartRef.current)
.append('svg')
@@ -257,18 +260,19 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
.append('g')
.attr('transform', `translate(${margin.left}, ${margin.top})`));
- var validSecondData;
- if (this._props.axes.length>2){ // for when there are 2 lines on the chart
- var next = this._tableData.map(record => ({ x: Number(record[this._props.axes[0]]), y: Number(record[this._props.axes[2]]) })).sort((a, b) => (a.x < b.x ? -1 : 1));
+ let validSecondData;
+ if (this._props.axes.length > 2) {
+ // for when there are 2 lines on the chart
+ const next = this._tableData.map(record => ({ x: Number(record[this._props.axes[0]]), y: Number(record[this._props.axes[2]]) })).sort((a, b) => (a.x < b.x ? -1 : 1));
validSecondData = next.filter(d => {
- if (!d.x || Number.isNaN(d.x) || !d.y || Number.isNaN(d.y)) return false;
+ if (!d.x || isNaN(d.x) || !d.y || isNaN(d.y)) return false;
return true;
});
- var secondDataRange = minMaxRange([validSecondData]);
- if (secondDataRange.xMax!>xMax) xMax = secondDataRange.xMax;
- if (secondDataRange.yMax!>yMax) yMax = secondDataRange.yMax;
- if (secondDataRange.xMin!<xMin) xMin = secondDataRange.xMin;
- if (secondDataRange.yMin!<yMin) yMin = secondDataRange.yMin;
+ const secondDataRange = minMaxRange([validSecondData]);
+ if (secondDataRange.xMax! > xMax) xMax = secondDataRange.xMax;
+ if (secondDataRange.yMax! > yMax) yMax = secondDataRange.yMax;
+ if (secondDataRange.xMin! < xMin) xMin = secondDataRange.xMin;
+ if (secondDataRange.yMin! < yMin) yMin = secondDataRange.yMin;
}
// creating the x and y scales
@@ -285,40 +289,34 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
if (validSecondData) {
drawLine(svg.append('path'), validSecondData, lineGen, true);
this.drawDataPoints(validSecondData, 0, xScale, yScale);
- svg.append('path').attr("stroke", "red");
+ svg.append('path').attr('stroke', 'red');
// legend
- var color = d3.scaleOrdinal()
- .range(["black", "blue"])
- .domain([this._props.axes[1], this._props.axes[2]])
- svg.selectAll("mydots")
+ const color: any = d3.scaleOrdinal().range(['black', 'blue']).domain([this._props.axes[1], this._props.axes[2]]);
+ svg.selectAll('mydots')
.data([this._props.axes[1], this._props.axes[2]])
.enter()
- .append("circle")
- .attr("cx", 5)
- .attr("cy", function(d,i){ return -30 + i*15})
- .attr("r", 7)
- .style("fill", function(d){ return color(d)})
- svg.selectAll("mylabels")
+ .append('circle')
+ .attr('cx', 5)
+ .attr('cy', (d, i) => -30 + i * 15)
+ .attr('r', 7)
+ .style('fill', d => color(d));
+ svg.selectAll('mylabels')
.data([this._props.axes[1], this._props.axes[2]])
.enter()
- .append("text")
- .attr("x", 25)
- .attr("y", function(d,i){ return -30 + i*15})
- .style("fill", function(d){ return color(d)})
- .text(function(d){ return d})
- .attr("text-anchor", "left")
- .style("alignment-baseline", "middle")
+ .append('text')
+ .attr('x', 25)
+ .attr('y', (d, i) => -30 + i * 15)
+ .style('fill', d => color(d))
+ .text(d => d)
+ .attr('text-anchor', 'left')
+ .style('alignment-baseline', 'middle');
}
// get valid data points
const data = dataSet[0];
- var validData = data.filter(d => {
- Object.keys(data[0]).map(key => {
- if (!d[key] || Number.isNaN(d[key])) return false;
- });
- return true;
- });
+ const keys = Object.keys(data[0]);
+ const validData = data.filter(d => !keys.some(key => isNaN(d[key])));
// draw the plot line
drawLine(svg.append('path'), validData, lineGen, false);
@@ -345,7 +343,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
const x0 = bisect(data, xScale.invert(xPos - 5)); // shift x by -5 so that you can reach points on the left-side axis
const d0 = data[x0];
// find .circle-d1 with data-x = d0.x and data-y = d0.y
- const selected = svg.selectAll('.datapoint').filter((d: any) => d['data-x'] === d0.x && d['data-y'] === d0.y);
+ svg.selectAll('.datapoint').filter((d: any) => d['data-x'] === d0.x && d['data-y'] === d0.y);
this.setCurrSelected(d0.x, d0.y);
this.updateTooltip(higlightFocusPt, xScale, d0, yScale, tooltip);
});
@@ -368,7 +366,7 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
.style('text-anchor', 'middle')
.text(this._props.axes[0]);
svg.append('text')
- .attr('transform', 'rotate(-90)' + ' ' + 'translate( 0, ' + -10 + ')')
+ .attr('transform', 'rotate(-90) translate(0, -10)')
.attr('x', -(height / 2))
.attr('y', -30)
.attr('height', 20)
@@ -394,57 +392,60 @@ export class LineChart extends ObservableReactComponent<LineChartProps> {
}
render() {
- var titleAccessor: any = 'dataViz_lineChart_title';
- if (this._props.axes.length == 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
- else if (this._props.axes.length > 0) titleAccessor = titleAccessor + this._props.axes[0];
+ let titleAccessor: any = 'dataViz_lineChart_title';
+ if (this._props.axes.length === 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
+ else if (this._props.axes.length > 0) titleAccessor += this._props.axes[0];
if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
const selectedPt = this._currSelected ? `{ ${this._props.axes[0]}: ${this._currSelected.x} ${this._props.axes[1]}: ${this._currSelected.y} }` : 'none';
- var selectedTitle = "";
- if (this._currSelected && this._props.titleCol){
- selectedTitle+= "\n" + this._props.titleCol + ": "
+ let selectedTitle = '';
+ if (this._currSelected && this._props.titleCol) {
+ selectedTitle += '\n' + this._props.titleCol + ': ';
this._tableData.forEach(each => {
- var mapThisEntry = false;
- if (this._currSelected.x==each[this._props.axes[0]] && this._currSelected.y==each[this._props.axes[1]]) mapThisEntry = true;
- else if (this._currSelected.y==each[this._props.axes[0]] && this._currSelected.x==each[this._props.axes[1]]) mapThisEntry = true;
- if (mapThisEntry) selectedTitle += each[this._props.titleCol] + ", ";
- })
- selectedTitle = selectedTitle.slice(0,-1).slice(0,-1);
+ let mapThisEntry = false;
+ if (this._currSelected.x === each[this._props.axes[0]] && this._currSelected.y === each[this._props.axes[1]]) mapThisEntry = true;
+ else if (this._currSelected.y === each[this._props.axes[0]] && this._currSelected.x === each[this._props.axes[1]]) mapThisEntry = true;
+ if (mapThisEntry) selectedTitle += each[this._props.titleCol] + ', ';
+ });
+ selectedTitle = selectedTitle.slice(0, -1).slice(0, -1);
}
- if (this._lineChartData.length > 0 || !this.parentViz || this.parentViz.length == 0) {
+ if (this._lineChartData.length > 0 || !this.parentViz || this.parentViz.length === 0) {
return this._props.axes.length >= 2 && /\d/.test(this._props.records[0][this._props.axes[0]]) && /\d/.test(this._props.records[0][this._props.axes[1]]) ? (
<div className="chart-container" style={{ width: this._props.width + this._props.margin.right }}>
<div className="graph-title">
<EditableText
val={StrCast(this._props.layoutDoc[titleAccessor])}
setVal={undoable(
- action(val => (this._props.layoutDoc[titleAccessor] = val as string)),
+ action(val => {
+ this._props.layoutDoc[titleAccessor] = val as string;
+ }),
'Change Graph Title'
)}
- color={'black'}
+ color="black"
size={Size.LARGE}
fillWidth
/>
</div>
<div ref={this._lineChartRef} />
- {selectedPt != 'none' ? (
- <div className={'selected-data'}>
+ {selectedPt !== 'none' ? (
+ <div className="selected-data">
{`Selected: ${selectedPt}`}
{`${selectedTitle}`}
<Button
- onClick={e => {
+ onClick={() => {
this._props.vizBox.sidebarBtnDown;
this._props.vizBox.sidebarAddDocument;
- }}></Button>
+ }}
+ />
</div>
) : null}
</div>
) : (
- <span className="chart-container"> {'first use table view to select two numerical axes to plot'}</span>
- );
- } else
- return (
- // when it is a brushed table and the incoming table doesn't have any rows selected
- <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
+ <span className="chart-container"> first use table view to select two numerical axes to plot</span>
);
+ }
+ return (
+ // when it is a brushed table and the incoming table doesn't have any rows selected
+ <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
+ );
}
}
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index fc23f47de..ef6d1d412 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -12,7 +12,7 @@ import { Cast, DocCast, StrCast } from '../../../../../fields/Types';
import { Docs } from '../../../../documents/Documents';
import { undoable } from '../../../../util/UndoManager';
import { ObservableReactComponent } from '../../../ObservableReactComponent';
-import { PinProps, PresBox } from '../../trails';
+import { PinProps, PinDocView } from '../../../PinFuncs';
import './Chart.scss';
export interface PieChartProps {
@@ -74,8 +74,8 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
}
@computed get defaultGraphTitle() {
- var ax0 = this._props.axes[0];
- var ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
+ const ax0 = this._props.axes[0];
+ const ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
if (this._props.axes.length < 2 || !/\d/.test(this._props.records[0][ax0]) || !ax1) {
return ax0 + ' Pie Chart';
}
@@ -84,7 +84,7 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
@computed get parentViz() {
return DocCast(this._props.Document.dataViz_parentViz);
- // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links
+ // return LinkManager.Links(this._props.Document) // out of all links
// .filter(link => link.link_anchor_1 == this._props.Document.dataViz_parentViz) // get links where this chart doc is the target of the link
// .map(link => DocCast(link.link_anchor_1)); // then return the source of the link
}
@@ -101,14 +101,14 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
}
@action
- restoreView = (data: Doc) => {};
+ 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,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
+ PinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
return anchor;
};
@@ -122,30 +122,30 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
// cleans data by converting numerical data to numbers and taking out empty cells
data = (dataSet: any) => {
- const validData = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key])));
+ const validData = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || isNaN(d[key])));
const field = dataSet[0] ? Object.keys(dataSet[0])[0] : undefined;
return !field
? undefined
: validData.map((d: { [x: string]: any }) =>
this.byCategory
? d[field] //
- : +d[field].replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\</g, '')
+ : +d[field].replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/</g, '')
);
};
// 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;
+ let index = -1;
+ let sameAsCurrent: boolean;
const selected = svg.selectAll('.slice').filter((d: any) => {
index++;
- var p1 = [0, 0]; // center of pie
- var p3 = [arc.centroid(d)[0] * 2, arc.centroid(d)[1] * 2]; // outward peak of arc
- var p2 = [radius * Math.sin(d.startAngle), -radius * Math.cos(d.startAngle)]; // start of arc
- var p4 = [radius * Math.sin(d.endAngle), -radius * Math.cos(d.endAngle)]; // end of arc
+ const p1 = [0, 0]; // center of pie
+ const p3 = [arc.centroid(d)[0] * 2, arc.centroid(d)[1] * 2]; // outward peak of arc
+ const p2 = [radius * Math.sin(d.startAngle), -radius * Math.cos(d.startAngle)]; // start of arc
+ const 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;
+ let lineCrossCount = 0;
// if for all 4 lines
if (Math.min(p1[1], p2[1]) <= pointer[1] && pointer[1] <= Math.max(p1[1], p2[1])) {
// within y bounds
@@ -160,13 +160,13 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
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];
+ const 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]]
+ ? showSelected[Object.keys(showSelected)[0]] === this._currSelected![Object.keys(showSelected)[0]] && showSelected[Object.keys(showSelected)[1]] === this._currSelected![Object.keys(showSelected)[1]]
: this._currSelected === showSelected;
this._currSelected = sameAsCurrent ? undefined : showSelected;
this.selectedData = sameAsCurrent ? undefined : d;
@@ -186,104 +186,100 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
d3.select(this._piechartRef.current).select('svg').remove();
d3.select(this._piechartRef.current).select('.tooltip').remove();
- var percentField = Object.keys(dataSet[0])[0];
- var descriptionField = Object.keys(dataSet[0])[1]!;
- var radius = Math.min(width, height - this._props.margin.top - this._props.margin.bottom) / 2;
+ let percentField = Object.keys(dataSet[0])[0];
+ let descriptionField = Object.keys(dataSet[0])[1]!;
+ const radius = Math.min(width, height - this._props.margin.top - this._props.margin.bottom) / 2;
// converts data into Objects
- var data = this.data(dataSet);
- var pieDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || Number.isNaN(d[key])));
+ let data = this.data(dataSet);
+ let pieDataSet = dataSet.filter((d: { [x: string]: unknown }) => !Object.keys(dataSet[0]).some(key => !d[key] || isNaN(d[key])));
if (this.byCategory) {
- let uniqueCategories = [...new Set(data)];
- var pieStringDataSet: { frequency: number }[] = [];
+ const uniqueCategories = [...new Set(data)];
+ const pieStringDataSet: { frequency: number }[] = [];
for (let i = 0; i < uniqueCategories.length; i++) {
pieStringDataSet.push({ frequency: 0, [percentField]: uniqueCategories[i] });
}
for (let i = 0; i < data.length; i++) {
- let sliceData = pieStringDataSet.filter((each: any) => each[percentField] == data[i]);
- sliceData[0].frequency = sliceData[0].frequency + 1;
+ // eslint-disable-next-line no-loop-func
+ const sliceData = pieStringDataSet.filter((each: any) => each[percentField] === data[i]);
+ sliceData[0].frequency += 1;
}
pieDataSet = pieStringDataSet;
- percentField = Object.keys(pieDataSet[0])[0];
- descriptionField = Object.keys(pieDataSet[0])[1]!;
+ [percentField, descriptionField] = Object.keys(pieDataSet[0]);
data = this.data(pieStringDataSet);
}
- var trackDuplicates: { [key: string]: any } = {};
- data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null));
+ let trackDuplicates: { [key: string]: any } = {};
+ data.forEach((eachData: any) => {
+ !trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null;
+ });
// initial chart
- var svg = (this._piechartSvg = d3
+ const 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);
+ const g = svg.append('g').attr('transform', 'translate(' + (width / 2 + this._props.margin.left) + ',' + height / 2 + ')');
+ const pie = d3.pie();
+ const arc = d3.arc().innerRadius(0).outerRadius(radius);
+ const updateHighlights = () => {
+ const hoverOverSlice = this.hoverOverData;
+ const { selectedData } = this;
+ svg.selectAll('path').attr('class', (d: any) =>
+ (selectedData && d.startAngle === selectedData.startAngle && d.endAngle === selectedData.endAngle) || (hoverOverSlice && d.startAngle === hoverOverSlice.startAngle && d.endAngle === hoverOverSlice.endAngle) ? 'slice hover' : 'slice'
+ );
+ };
// click/hover
const onPointClick = action((e: any) => this.highlightSelectedSlice(true, svg, arc, radius, d3.pointer(e), pieDataSet));
const onHover = action((e: any) => {
this.highlightSelectedSlice(false, svg, arc, radius, d3.pointer(e), pieDataSet);
updateHighlights();
});
- const mouseOut = action((e: any) => {
+ const mouseOut = action(() => {
this.hoverOverData = undefined;
updateHighlights();
});
- const updateHighlights = () => {
- const hoverOverSlice = this.hoverOverData;
- const selectedData = this.selectedData;
- svg.selectAll('path').attr('class', function (d: any) {
- return (selectedData && d.startAngle == selectedData.startAngle && d.endAngle == selectedData.endAngle) || (hoverOverSlice && d.startAngle == hoverOverSlice.startAngle && d.endAngle == hoverOverSlice.endAngle)
- ? 'slice hover'
- : 'slice';
- });
- };
// drawing the slices
- var selected = this.selectedData;
- var arcs = g.selectAll('arc').data(pie(data)).enter().append('g');
+ const selected = this.selectedData;
+ const arcs = g.selectAll('arc').data(pie(data)).enter().append('g');
const possibleDataPointVals: { [x: string]: any }[] = [];
pieDataSet.forEach((each: { [x: string]: any | { valueOf(): number } }) => {
- var dataPointVal: { [x: string]: any } = {};
+ const dataPointVal: { [x: string]: any } = {};
dataPointVal[percentField] = each[percentField];
if (descriptionField) dataPointVal[descriptionField] = each[descriptionField];
try {
- dataPointVal[percentField] = Number(dataPointVal[percentField].replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\</g, ''));
- } catch (error) {}
+ dataPointVal[percentField] = Number(dataPointVal[percentField].replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/</g, ''));
+ } catch (error) {
+ /* empty */
+ }
possibleDataPointVals.push(dataPointVal);
});
const sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
arcs.append('path')
.attr('fill', (d, i) => {
- var dataPoint;
+ let dataPoint;
const possibleDataPoints = possibleDataPointVals.filter((pval: any) => pval[percentField] === Number(d.data));
- if (possibleDataPoints.length == 1) dataPoint = possibleDataPoints[0];
+ if (possibleDataPoints.length === 1) [dataPoint] = possibleDataPoints;
else {
dataPoint = possibleDataPoints[trackDuplicates[d.data.toString()]];
trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1;
}
- var sliceColor;
+ let sliceColor;
if (dataPoint) {
const sliceTitle = dataPoint[this._props.axes[0]];
- const accessByName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\</g, '') : sliceTitle;
- sliceColors.forEach(each => each[0] == accessByName && (sliceColor = each[1]));
+ const accessByName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/</g, '') : sliceTitle;
+ sliceColors.forEach(each => {
+ // eslint-disable-next-line prefer-destructuring
+ each[0] === accessByName && (sliceColor = each[1]);
+ });
}
return sliceColor ? StrCast(sliceColor) : d3.schemeSet3[i] ? d3.schemeSet3[i] : d3.schemeSet3[i % d3.schemeSet3.length];
})
- .attr(
- 'class',
- selected
- ? function (d) {
- return selected && d.startAngle == selected.startAngle && d.endAngle == selected.endAngle ? 'slice hover' : 'slice';
- }
- : function (d) {
- return 'slice';
- }
- )
+ .attr('class', selected ? d => (selected && d.startAngle === selected.startAngle && d.endAngle === selected.endAngle ? 'slice hover' : 'slice') : () => 'slice')
// @ts-ignore
.attr('d', arc)
.on('click', onPointClick)
@@ -292,20 +288,22 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
// adding labels
trackDuplicates = {};
- data.forEach((eachData: any) => (!trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null));
+ data.forEach((eachData: any) => {
+ !trackDuplicates[eachData] ? (trackDuplicates[eachData] = 0) : null;
+ });
arcs.size() < 100 &&
arcs
.append('text')
- .attr('transform', function (d) {
- var centroid = arc.centroid(d as unknown as d3.DefaultArcObject);
- var heightOffset = (centroid[1] / radius) * Math.abs(centroid[1]);
+ .attr('transform', d => {
+ const centroid = arc.centroid(d as unknown as d3.DefaultArcObject);
+ const heightOffset = (centroid[1] / radius) * Math.abs(centroid[1]);
return 'translate(' + (centroid[0] + centroid[0] / (radius * 0.02)) + ',' + (centroid[1] + heightOffset) + ')';
})
.attr('text-anchor', 'middle')
- .text(function (d) {
- var dataPoint;
+ .text(d => {
+ let dataPoint;
const possibleDataPoints = possibleDataPointVals.filter((pval: any) => pval[percentField] === Number(d.data));
- if (possibleDataPoints.length == 1) dataPoint = pieDataSet[possibleDataPointVals.indexOf(possibleDataPoints[0])];
+ if (possibleDataPoints.length === 1) dataPoint = pieDataSet[possibleDataPointVals.indexOf(possibleDataPoints[0])];
else {
dataPoint = pieDataSet[possibleDataPointVals.indexOf(possibleDataPoints[trackDuplicates[d.data.toString()]])];
trackDuplicates[d.data.toString()] = trackDuplicates[d.data.toString()] + 1;
@@ -317,11 +315,11 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
@action changeSelectedColor = (color: string) => {
this.curSliceSelected.attr('fill', color);
const sliceTitle = this._currSelected[this._props.axes[0]];
- const sliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\</g, '') : sliceTitle;
+ const sliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/</g, '') : sliceTitle;
const sliceColors = Cast(this._props.layoutDoc.dataViz_pie_sliceColors, listSpec('string'), null);
- sliceColors.map(each => {
- if (each.split('::')[0] == sliceName) sliceColors.splice(sliceColors.indexOf(each), 1);
+ sliceColors.forEach(each => {
+ if (each.split('::')[0] === sliceName) sliceColors.splice(sliceColors.indexOf(each), 1);
});
sliceColors.push(StrCast(sliceName + '::' + color));
};
@@ -332,39 +330,39 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
};
render() {
- var titleAccessor: any = 'dataViz_pie_title';
- if (this._props.axes.length == 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
- else if (this._props.axes.length > 0) titleAccessor = titleAccessor + this._props.axes[0];
+ let titleAccessor: any = 'dataViz_pie_title';
+ if (this._props.axes.length === 2) titleAccessor = titleAccessor + this._props.axes[0] + '-' + this._props.axes[1];
+ else if (this._props.axes.length > 0) titleAccessor += this._props.axes[0];
if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
if (!this._props.layoutDoc.dataViz_pie_sliceColors) this._props.layoutDoc.dataViz_pie_sliceColors = new List<string>();
- var selected: string;
- var curSelectedSliceName = '';
+ let selected: string;
+ let curSelectedSliceName = '';
if (this._currSelected) {
selected = '{ ';
const sliceTitle = this._currSelected[this._props.axes[0]];
- curSelectedSliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\</g, '') : sliceTitle;
- Object.keys(this._currSelected).map(key => {
- key != '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : '';
+ curSelectedSliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/%/g, '').replace(/#/g, '').replace(/</g, '') : sliceTitle;
+ Object.keys(this._currSelected).forEach(key => {
+ key !== '' ? (selected += key + ': ' + this._currSelected[key] + ', ') : '';
});
selected = selected.substring(0, selected.length - 2);
selected += ' }';
- if (this._props.titleCol!="" && (!this._currSelected["frequency"] || this._currSelected["frequency"]<10)){
- selected+= "\n" + this._props.titleCol + ": "
+ if (this._props.titleCol !== '' && (!this._currSelected.frequency || this._currSelected.frequency < 10)) {
+ selected += '\n' + this._props.titleCol + ': ';
this._tableData.forEach(each => {
- if (this._currSelected[this._props.axes[0]]==each[this._props.axes[0]]) {
- if (this._props.axes[1]){
- if (this._currSelected[this._props.axes[1]]==each[this._props.axes[1]]) selected+= each[this._props.titleCol] + ", ";
- }
- else selected+= each[this._props.titleCol] + ", ";
+ if (this._currSelected[this._props.axes[0]] === each[this._props.axes[0]]) {
+ if (this._props.axes[1]) {
+ if (this._currSelected[this._props.axes[1]] === each[this._props.axes[1]]) selected += each[this._props.titleCol] + ', ';
+ } else selected += each[this._props.titleCol] + ', ';
}
- })
- selected = selected.slice(0,-1).slice(0,-1);
+ });
+ selected = selected.slice(0, -1).slice(0, -1);
}
} else selected = 'none';
- var selectedSliceColor;
- var sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
+ let selectedSliceColor;
+ const sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
sliceColors.forEach(each => {
- if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1];
+ // eslint-disable-next-line prefer-destructuring
+ if (each[0] === curSelectedSliceName!) selectedSliceColor = each[1];
});
if (this._pieChartData.length > 0 || !this.parentViz) {
@@ -374,30 +372,32 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
<EditableText
val={StrCast(this._props.layoutDoc[titleAccessor])}
setVal={undoable(
- action(val => (this._props.layoutDoc[titleAccessor] = val as string)),
+ action(val => {
+ this._props.layoutDoc[titleAccessor] = val as string;
+ }),
'Change Graph Title'
)}
- color={'black'}
+ color="black"
size={Size.LARGE}
fillWidth
/>
</div>
{this._props.axes.length === 1 && /\d/.test(this._props.records[0][this._props.axes[0]]) ? (
- <div className={'asHistogram-checkBox'} style={{ width: this._props.width }}>
+ <div className="asHistogram-checkBox" style={{ width: this._props.width }}>
<Checkbox color="primary" onChange={this.changeHistogramCheckBox} checked={this._props.layoutDoc.dataViz_pie_asHistogram as boolean} />
Organize data as histogram
</div>
) : null}
<div ref={this._piechartRef} />
- {selected != 'none' ? (
- <div className={'selected-data'}>
+ {selected !== 'none' ? (
+ <div className="selected-data">
Selected: {selected}
&nbsp; &nbsp;
<ColorPicker
- tooltip={'Change Slice Color'}
+ tooltip="Change Slice Color"
type={Type.SEC}
icon={<FaFillDrip />}
- selectedColor={selectedSliceColor ? selectedSliceColor : this.curSliceSelected.attr('fill')}
+ selectedColor={selectedSliceColor || this.curSliceSelected.attr('fill')}
setFinalColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Slice Color')}
setSelectedColor={undoable(color => this.changeSelectedColor(color), 'Change Selected Slice Color')}
size={Size.XSMALL}
@@ -406,12 +406,12 @@ export class PieChart extends ObservableReactComponent<PieChartProps> {
) : null}
</div>
) : (
- <span className="chart-container"> {'first use table view to select a column to graph'}</span>
- );
- } else
- return (
- // when it is a brushed table and the incoming table doesn't have any rows selected
- <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
+ <span className="chart-container"> first use table view to select a column to graph</span>
);
+ }
+ return (
+ // when it is a brushed table and the incoming table doesn't have any rows selected
+ <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
+ );
}
}
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 67e1c67bd..5cd77e274 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -1,19 +1,25 @@
+/* eslint-disable jsx-a11y/no-noninteractive-tabindex */
+/* eslint-disable jsx-a11y/no-static-element-interactions */
import { Button, Type } from 'browndash-components';
import { IReactionDisposer, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
-import { Utils, emptyFunction, setupMoveUpEvents } from '../../../../../Utils';
+import { ClientUtils, setupMoveUpEvents } from '../../../../../ClientUtils';
+import { emptyFunction } from '../../../../../Utils';
import { Doc, Field, NumListCast } from '../../../../../fields/Doc';
+import { DocData } from '../../../../../fields/DocSymbols';
import { List } from '../../../../../fields/List';
import { listSpec } from '../../../../../fields/Schema';
import { Cast, DocCast } from '../../../../../fields/Types';
import { DragManager } from '../../../../util/DragManager';
+import { undoable } from '../../../../util/UndoManager';
import { ObservableReactComponent } from '../../../ObservableReactComponent';
import { DocumentView } from '../../DocumentView';
import { DataVizView } from '../DataVizBox';
import './Chart.scss';
-import { undoable } from '../../../../util/UndoManager';
+
const { DATA_VIZ_TABLE_ROW_HEIGHT } = require('../../../global/globalCssVariables.module.scss'); // prettier-ignore
+
interface TableBoxProps {
Document: Doc;
layoutDoc: Doc;
@@ -77,7 +83,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
}
@computed get columns() {
- return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header != '' && header != undefined) : [];
+ return this._tableData.length ? Array.from(Object.keys(this._tableData[0])).filter(header => header !== '' && header !== undefined) : [];
}
// updates the 'dataViz_selectedRows' and 'dataViz_highlightedRows' fields to no longer include rows that aren't in the table
@@ -114,15 +120,13 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1);
else highlited?.push(rowId);
if (!selected?.includes(rowId)) selected?.push(rowId);
- } else {
+ } else if (selected?.includes(rowId)) {
// selecting a row
- if (selected?.includes(rowId)) {
- if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1);
- selected.splice(selected.indexOf(rowId), 1);
- } else selected?.push(rowId);
- }
+ if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1);
+ selected.splice(selected.indexOf(rowId), 1);
+ } else selected?.push(rowId);
e.stopPropagation();
- this.hasRowsToFilter = selected.length > 0 ? true : false;
+ this.hasRowsToFilter = selected.length > 0;
};
columnPointerDown = (e: React.PointerEvent, col: string) => {
@@ -131,7 +135,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
setupMoveUpEvents(
{},
e,
- e => {
+ moveEv => {
// dragging off a column to create a brushed DataVizBox
const sourceAnchorCreator = () => this._props.docView?.()!.Document!;
const targetCreator = (annotationOn: Doc | undefined) => {
@@ -145,15 +149,13 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
embedding.pieSliceColors = Field.Copy(this._props.layoutDoc.pieSliceColors);
return embedding;
};
- if (this._props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
- DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], 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.linkDocument.link_displayArrow = true;
- // e.annoDragData.linkSourceDoc.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this._props.Document;
- // e.annoDragData.linkSourceDoc.followLinkZoom = false;
+ if (this._props.docView?.() && !ClientUtils.isClick(moveEv.clientX, moveEv.clientY, downX, downY, Date.now())) {
+ DragManager.StartAnchorAnnoDrag(moveEv.target instanceof HTMLElement ? [moveEv.target] : [], new DragManager.AnchorAnnoDragData(this._props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
+ dragComplete: completeEv => {
+ if (!completeEv.aborted && completeEv.annoDragData && completeEv.annoDragData.linkSourceDoc && completeEv.annoDragData.dropDocument && completeEv.linkDocument) {
+ completeEv.linkDocument[DocData].link_matchEmbeddings = true;
+ completeEv.linkDocument[DocData].stroke_startMarker = true;
+ this._props.docView?.()?._props.addDocument?.(completeEv.linkDocument);
}
},
});
@@ -162,10 +164,10 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
return false;
},
emptyFunction,
- action(e => {
- if (e.shiftKey || this.settingTitle) {
+ action(moveEv => {
+ if (moveEv.shiftKey || this.settingTitle) {
if (this.settingTitle) this.settingTitle = false;
- if (this._props.titleCol == col) this._props.titleCol = '';
+ if (this._props.titleCol === col) this._props.titleCol = '';
else this._props.titleCol = col;
this._props.selectTitleCol(this._props.titleCol);
} else {
@@ -183,16 +185,16 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
* These functions handle the filtering popup for when the "filter" button is pressed to select rows
*/
filter = undoable((e: any) => {
- var start: any;
- var end: any;
- if (this.filteringType == 'Range') {
+ let start: any;
+ let end: any;
+ if (this.filteringType === 'Range') {
start = (this.filteringVal[0] as Number) ? Number(this.filteringVal[0]) : this.filteringVal[0];
end = (this.filteringVal[1] as Number) ? Number(this.filteringVal[1]) : this.filteringVal[0];
}
this._tableDataIds.forEach(rowID => {
- if (this.filteringType == 'Value') {
- if (this._props.records[rowID][this.filteringColumn] == this.filteringVal[0]) {
+ if (this.filteringType === 'Value') {
+ if (this._props.records[rowID][this.filteringColumn] === this.filteringVal[0]) {
if (!NumListCast(this._props.layoutDoc.dataViz_selectedRows).includes(rowID)) {
this.tableRowClick(e, rowID);
}
@@ -229,12 +231,12 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
this.filteringVal[1] = e.target.value;
});
@computed get renderFiltering() {
- if (this.filteringColumn === '') this.filteringColumn = this.columns[0];
+ if (this.filteringColumn === '') [this.filteringColumn] = this.columns;
return (
<div className="tableBox-filterPopup" style={{ right: this._props.width * 0.05 }}>
<div className="tableBox-filterPopup-selectColumn">
Column:
- <select className="tableBox-filterPopup-selectColumn-each" value={this.filteringColumn != '' ? this.filteringColumn : this.columns[0]} onChange={e => this.setFilterColumn(e)}>
+ <select className="tableBox-filterPopup-selectColumn-each" value={this.filteringColumn !== '' ? this.filteringColumn : this.columns[0]} onChange={e => this.setFilterColumn(e)}>
{this.columns.map(column => (
<option className="" key={column} value={column}>
{' '}
@@ -245,17 +247,17 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
</div>
<div className="tableBox-filterPopup-setValue">
<select className="tableBox-filterPopup-setValue-each" value={this.filteringType} onChange={e => this.setFilterType(e)}>
- <option className="" key={'Value'} value={'Value'}>
+ <option className="" key="Value" value="Value">
{' '}
{'Value'}{' '}
</option>
- <option className="" key={'Range'} value={'Range'}>
+ <option className="" key="Range" value="Range">
{' '}
{'Range'}{' '}
</option>
</select>
:
- {this.filteringType == 'Value' ? (
+ {this.filteringType === 'Value' ? (
<input
className="tableBox-filterPopup-setValue-input"
defaultValue=""
@@ -301,7 +303,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
)}
</div>
<div className="tableBox-filterPopup-setFilter">
- <Button onClick={action(e => this.filter(e))} text="Set Filter" type={Type.SEC} color={'black'} />
+ <Button onClick={action(e => this.filter(e))} text="Set Filter" type={Type.SEC} color="black" />
</div>
</div>
);
@@ -322,11 +324,25 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
}}>
<div className="tableBox-selectButtons">
<div className="tableBox-selectTitle">
- <Button onClick={action(() => (this.settingTitle = !this.settingTitle))} text="Select Title Column" type={Type.SEC} color={'black'} />
+ <Button
+ onClick={action(() => {
+ this.settingTitle = !this.settingTitle;
+ })}
+ text="Select Title Column"
+ type={Type.SEC}
+ color="black"
+ />
</div>
<div className="tableBox-filtering">
{this.filtering ? this.renderFiltering : null}
- <Button onClick={action(() => (this.filtering = !this.filtering))} text="Filter" type={Type.SEC} color={'black'} />
+ <Button
+ onClick={action(() => {
+ this.filtering = !this.filtering;
+ })}
+ text="Filter"
+ type={Type.SEC}
+ color="black"
+ />
<div className="tableBox-filterAll">
{this.hasRowsToFilter ? (
<Button
@@ -336,7 +352,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
})}
text="Deselect All"
type={Type.SEC}
- color={'black'}
+ color="black"
tooltip="Select rows to be displayed in any DataViz boxes dragged off of this one."
/>
) : (
@@ -347,7 +363,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
})}
text="Select All"
type={Type.SEC}
- color={'black'}
+ color="black"
tooltip="Select rows to be displayed in any DataViz boxes dragged off of this one."
/>
)}
@@ -391,7 +407,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
? 'darkgreen'
: this._props.axes.length > 2 && this._props.axes.lastElement() === col
? 'darkred'
- : this._props.axes.lastElement() === col || (this._props.axes.length > 2 && this._props.axes[1] == col)
+ : this._props.axes.lastElement() === col || (this._props.axes.length > 2 && this._props.axes[1] === col)
? 'darkblue'
: undefined,
background: this.settingTitle
@@ -400,7 +416,7 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
? '#E3fbdb'
: this._props.axes.length > 2 && this._props.axes.lastElement() === col
? '#Fbdbdb'
- : this._props.axes.lastElement() === col || (this._props.axes.length > 2 && this._props.axes[1] == col)
+ : this._props.axes.lastElement() === col || (this._props.axes.length > 2 && this._props.axes[1] === col)
? '#c6ebf7'
: undefined,
fontWeight: 'bolder',
@@ -424,11 +440,11 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
background: NumListCast(this._props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this._props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '',
}}>
{this.columns.map(col => {
- var colSelected = false;
- if (this._props.axes.length > 2) colSelected = this._props.axes[0] == col || this._props.axes[1] == col || this._props.axes[2] == col;
- else if (this._props.axes.length > 1) colSelected = this._props.axes[0] == col || this._props.axes[1] == col;
- else if (this._props.axes.length > 0) colSelected = this._props.axes[0] == col;
- if (this._props.titleCol == col) colSelected = true;
+ let colSelected = false;
+ if (this._props.axes.length > 2) colSelected = this._props.axes[0] === col || this._props.axes[1] === col || this._props.axes[2] === col;
+ else if (this._props.axes.length > 1) colSelected = this._props.axes[0] === col || this._props.axes[1] === col;
+ else if (this._props.axes.length > 0) colSelected = this._props.axes[0] === col;
+ if (this._props.titleCol === col) colSelected = true;
return (
<td key={this.columns.indexOf(col)} style={{ border: colSelected ? '3px solid black' : '1px solid black', fontWeight: colSelected ? 'bolder' : 'normal' }}>
<div className="tableBox-cell">{this._props.records[rowId][col]}</div>
@@ -443,10 +459,10 @@ export class TableBox extends ObservableReactComponent<TableBoxProps> {
</div>
</div>
);
- } else
- return (
- // when it is a brushed table and the incoming table doesn't have any rows selected
- <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
- );
+ }
+ return (
+ // when it is a brushed table and the incoming table doesn't have any rows selected
+ <div className="chart-container">Selected rows of data from the incoming DataVizBox to display.</div>
+ );
}
}
diff --git a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
index 336935d23..be05c3529 100644
--- a/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
+++ b/src/client/views/nodes/DataVizBox/utils/D3Utils.ts
@@ -5,11 +5,11 @@ import { DataPoint } from '../components/LineChart';
export const minMaxRange = (dataPts: DataPoint[][]) => {
// find the max and min of all the data points
- const yMin = d3.min(dataPts, d => d3.min(d, d => Number(d.y)));
- const yMax = d3.max(dataPts, d => d3.max(d, d => Number(d.y)));
+ const yMin = d3.min(dataPts, d => d3.min(d, m => Number(m.y)));
+ const yMax = d3.max(dataPts, d => d3.max(d, m => Number(m.y)));
- const xMin = d3.min(dataPts, d => d3.min(d, d => Number(d.x)));
- const xMax = d3.max(dataPts, d => d3.max(d, d => Number(d.x)));
+ const xMin = d3.min(dataPts, d => d3.min(d, m => Number(m.x)));
+ const xMax = d3.max(dataPts, d => d3.max(d, m => Number(m.x)));
return { xMin, xMax, yMin, yMax };
};
@@ -20,18 +20,15 @@ export const scaleCreatorCategorical = (labels: string[], range: number[]) => {
return scale;
};
-export const scaleCreatorNumerical = (domA: number, domB: number, rangeA: number, rangeB: number) => {
- return d3.scaleLinear().domain([domA, domB]).range([rangeA, rangeB]);
-};
+export const scaleCreatorNumerical = (domA: number, domB: number, rangeA: number, rangeB: number) => d3.scaleLinear().domain([domA, domB]).range([rangeA, rangeB]);
-export const createLineGenerator = (xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>) => {
+export const createLineGenerator = (xScale: d3.ScaleLinear<number, number, never>, yScale: d3.ScaleLinear<number, number, never>) =>
// TODO: nda - look into the different types of curves
- return d3
+ d3
.line<DataPoint>()
.x(d => xScale(d.x))
.y(d => yScale(d.y))
.curve(d3.curveMonotoneX);
-};
export const xAxisCreator = (g: d3.Selection<SVGGElement, unknown, null, undefined>, height: number, xScale: d3.ScaleLinear<number, number, never>) => {
g.attr('class', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickSize(15));
@@ -48,7 +45,7 @@ export const xGrid = (g: d3.Selection<SVGGElement, unknown, null, undefined>, he
d3
.axisBottom(scale)
.tickSize(-height)
- .tickFormat((a, b) => '')
+ .tickFormat((/* a, b */) => '')
);
};
@@ -57,10 +54,16 @@ export const yGrid = (g: d3.Selection<SVGGElement, unknown, null, undefined>, wi
d3
.axisLeft(scale)
.tickSize(-width)
- .tickFormat((a, b) => '')
+ .tickFormat((/* a, b */) => '')
);
};
export const drawLine = (p: d3.Selection<SVGPathElement, unknown, null, undefined>, dataPts: DataPoint[], lineGen: d3.Line<DataPoint>, extra: boolean) => {
- p.datum(dataPts).attr('fill', 'none').attr('stroke', 'rgba(53, 162, 235, 0.5)').attr('stroke-width', 2).attr('stroke', extra? 'blue' : 'black').attr('class', 'line').attr('d', lineGen);
+ p.datum(dataPts)
+ .attr('fill', 'none')
+ .attr('stroke', 'rgba(53, 162, 235, 0.5)')
+ .attr('stroke-width', 2)
+ .attr('stroke', extra ? 'blue' : 'black')
+ .attr('class', 'line')
+ .attr('d', lineGen);
};