aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/client/views/nodes/DataVizBox/DataVizBox.tsx14
-rw-r--r--src/client/views/nodes/DataVizBox/components/Histogram.tsx102
-rw-r--r--src/client/views/nodes/DataVizBox/components/LineChart.tsx66
-rw-r--r--src/client/views/nodes/DataVizBox/components/PieChart.tsx88
-rw-r--r--src/client/views/nodes/DataVizBox/components/TableBox.tsx79
-rw-r--r--src/client/views/nodes/MapBox/AnimationUtility.ts482
-rw-r--r--src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx69
-rw-r--r--src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx66
8 files changed, 456 insertions, 510 deletions
diff --git a/src/client/views/nodes/DataVizBox/DataVizBox.tsx b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
index a626772e4..e31c04c40 100644
--- a/src/client/views/nodes/DataVizBox/DataVizBox.tsx
+++ b/src/client/views/nodes/DataVizBox/DataVizBox.tsx
@@ -98,7 +98,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
};
componentDidMount() {
- this.props.setContentView?.(this);
+ this._props.setContentView?.(this);
if (!DataVizBox.dataset.has(CsvCast(this.dataDoc[this.fieldKey]).url.href)) this.fetchData();
}
@@ -110,19 +110,19 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
// toggles for user to decide which chart type to view the data in
@computed get renderVizView() {
- const scale = this.props.NativeDimScaling?.() || 1;
+ const scale = this._props.NativeDimScaling?.() || 1;
const sharedProps = {
Document: this.Document,
layoutDoc: this.layoutDoc,
records: this.records,
axes: this.axes,
- height: (this.props.PanelHeight() / scale - 32) /* height of 'change view' button */ * 0.9,
- width: (this.props.PanelWidth() / scale) * 0.9,
+ height: (this._props.PanelHeight() / scale - 32) /* height of 'change view' button */ * 0.9,
+ width: (this._props.PanelWidth() / scale) * 0.9,
margin: { top: 10, right: 25, bottom: 75, left: 45 },
};
if (!this.records.length) return 'no data/visualization';
switch (this.dataVizView) {
- case DataVizView.TABLE: return <TableBox {...sharedProps} docView={this.props.DocumentView} selectAxes={this.selectAxes} />;
+ case DataVizView.TABLE: return <TableBox {...sharedProps} docView={this._props.DocumentView} selectAxes={this.selectAxes} />;
case DataVizView.LINECHART: return <LineChart {...sharedProps} dataDoc={this.dataDoc} fieldKey={this.fieldKey} ref={r => (this._vizRenderer = r ?? undefined)} />;
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)}
@@ -131,7 +131,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
}
render() {
- const scale = this.props.NativeDimScaling?.() || 1;
+ const scale = this._props.NativeDimScaling?.() || 1;
return !this.records.length ? (
// displays how to get data into the DataVizBox if its empty
@@ -140,7 +140,7 @@ export class DataVizBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
<div
className="dataViz"
style={{
- pointerEvents: this.props.isContentActive() === true ? 'all' : 'none',
+ pointerEvents: this._props.isContentActive() === true ? 'all' : 'none',
width: `${100 / scale}%`,
height: `${100 / scale}%`,
transform: `scale(${scale})`,
diff --git a/src/client/views/nodes/DataVizBox/components/Histogram.tsx b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
index a1bd316f0..d786e84ad 100644
--- a/src/client/views/nodes/DataVizBox/components/Histogram.tsx
+++ b/src/client/views/nodes/DataVizBox/components/Histogram.tsx
@@ -1,7 +1,7 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ColorPicker, EditableText, IconButton, Size, Type } from 'browndash-components';
import * as d3 from 'd3';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { FaFillDrip } from 'react-icons/fa';
@@ -14,6 +14,7 @@ import { undoable } from '../../../../util/UndoManager';
import { PinProps, PresBox } from '../../trails';
import { scaleCreatorNumerical, yAxisCreator } from '../utils/D3Utils';
import './Chart.scss';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
export interface HistogramProps {
Document: Doc;
@@ -33,7 +34,7 @@ export interface HistogramProps {
}
@observer
-export class Histogram extends React.Component<HistogramProps> {
+export class Histogram extends ObservableReactComponent<HistogramProps> {
private _disposers: { [key: string]: IReactionDisposer } = {};
private _histogramRef: React.RefObject<HTMLDivElement> = React.createRef();
private _histogramSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@@ -45,46 +46,51 @@ export class Histogram extends React.Component<HistogramProps> {
private selectedData: any = undefined; // Selection of selected bar
private hoverOverData: any = undefined; // Selection of bar being hovered over
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get _tableDataIds() {
- return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ return !this.parentViz ? this._props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
}
// returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
@computed get _tableData() {
- return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ return !this.parentViz ? this._props.records : this._tableDataIds.map(rowId => this._props.records[rowId]);
}
// filters all data to just display selected data if brushed (created from an incoming link)
@computed get _histogramData() {
- if (this.props.axes.length < 1) return [];
- if (this.props.axes.length < 2) {
- var ax0 = this.props.axes[0];
- if (/\d/.test(this.props.records[0][ax0])) {
+ if (this._props.axes.length < 1) return [];
+ if (this._props.axes.length < 2) {
+ var ax0 = this._props.axes[0];
+ if (/\d/.test(this._props.records[0][ax0])) {
this.numericalXData = true;
}
- return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]] }));
+ return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]] }));
}
- var ax0 = this.props.axes[0];
- var ax1 = this.props.axes[1];
- if (/\d/.test(this.props.records[0][ax0])) {
+ var ax0 = this._props.axes[0];
+ var ax1 = this._props.axes[1];
+ if (/\d/.test(this._props.records[0][ax0])) {
this.numericalXData = true;
}
- if (/\d/.test(this.props.records[0][ax1])) {
+ if (/\d/.test(this._props.records[0][ax1])) {
this.numericalYData = true;
}
- return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]], [ax1]: record[this.props.axes[1]] }));
+ return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]], [ax1]: record[this._props.axes[1]] }));
}
@computed get defaultGraphTitle() {
- var ax0 = this.props.axes[0];
- var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
- if (this.props.axes.length < 2 || !ax1 || !/\d/.test(this.props.records[0][ax1]) || !this.numericalYData) {
+ var ax0 = this._props.axes[0];
+ var ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
+ if (this._props.axes.length < 2 || !ax1 || !/\d/.test(this._props.records[0][ax1]) || !this.numericalYData) {
return ax0 + ' Histogram';
} else return ax0 + ' by ' + ax1 + ' Histogram';
}
@computed get parentViz() {
- return DocCast(this.props.Document.dataViz_parentViz);
- // return LinkManager.Instance.getAllRelatedLinks(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
+ return DocCast(this._props.Document.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(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
}
@@ -114,16 +120,16 @@ export class Histogram extends React.Component<HistogramProps> {
const anchor = Docs.Create.ConfigDocument({
title: 'histogram doc selection' + this._currSelected,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.Document);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
return anchor;
};
@computed get height() {
- return this.props.height - this.props.margin.top - this.props.margin.bottom;
+ return this._props.height - this._props.margin.top - this._props.margin.bottom;
}
@computed get width() {
- return this.props.width - this.props.margin.left - this.props.margin.right;
+ return this._props.width - this._props.margin.left - this._props.margin.right;
}
// cleans data by converting numerical data to numbers and taking out empty cells
@@ -211,10 +217,10 @@ export class Histogram extends React.Component<HistogramProps> {
.select(this._histogramRef.current)
.append('svg')
.attr('class', 'graph')
- .attr('width', width + this.props.margin.right + this.props.margin.left)
- .attr('height', height + this.props.margin.top + this.props.margin.bottom)
+ .attr('width', width + this._props.margin.right + this._props.margin.left)
+ .attr('height', height + this._props.margin.top + this._props.margin.bottom)
.append('g')
- .attr('transform', 'translate(' + this.props.margin.left + ',' + this.props.margin.top + ')'));
+ .attr('transform', 'translate(' + this._props.margin.left + ',' + this._props.margin.top + ')'));
var x = d3
.scaleLinear()
.domain(this.numericalXData ? [startingPoint!, endingPoint!] : [0, numBins])
@@ -382,7 +388,7 @@ export class Histogram extends React.Component<HistogramProps> {
)
.attr('fill', d => {
var barColor;
- const barColors = StrListCast(this.props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
+ 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];
else {
@@ -390,24 +396,24 @@ export class Histogram extends React.Component<HistogramProps> {
if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
}
});
- return barColor ? StrCast(barColor) : StrCast(this.props.layoutDoc.dataViz_histogram_defaultColor);
+ return barColor ? StrCast(barColor) : StrCast(this._props.layoutDoc.dataViz_histogram_defaultColor);
});
};
@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);
+ 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));
barColors.push(StrCast(barName + '::' + color));
};
@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, ''));
+ 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 barColors = Cast(this.props.layoutDoc.dataViz_histogram_barColors, listSpec('string'), null);
+ 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));
};
@@ -416,7 +422,7 @@ export class Histogram extends React.Component<HistogramProps> {
if (svg)
svg.selectAll('rect').attr('fill', (d: any) => {
var barColor;
- const barColors = StrListCast(this.props.layoutDoc.dataViz_histogram_barColors).map(each => each.split('::'));
+ 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];
else {
@@ -424,7 +430,7 @@ export class Histogram extends React.Component<HistogramProps> {
if (Number(range[0]) <= d[0] && d[0] <= Number(range[1])) barColor = each[1];
}
});
- return barColor ? StrCast(barColor) : StrCast(this.props.layoutDoc.dataViz_histogram_defaultColor);
+ return barColor ? StrCast(barColor) : StrCast(this._props.layoutDoc.dataViz_histogram_defaultColor);
});
};
@@ -433,14 +439,14 @@ export class Histogram extends React.Component<HistogramProps> {
this._histogramData;
var curSelectedBarName = '';
var titleAccessor: any = '';
- if (this.props.axes.length == 2) titleAccessor = 'dataViz_histogram_title' + this.props.axes[0] + '-' + this.props.axes[1];
- else if (this.props.axes.length > 0) titleAccessor = 'dataViz_histogram_title' + 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>();
+ if (this._props.axes.length == 2) titleAccessor = 'dataViz_histogram_title' + this._props.axes[0] + '-' + this._props.axes[1];
+ else if (this._props.axes.length > 0) titleAccessor = 'dataViz_histogram_title' + 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';
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 =>
key //
@@ -450,17 +456,17 @@ export class Histogram extends React.Component<HistogramProps> {
selected = selected.substring(0, selected.length - 2) + ' }';
}
var selectedBarColor;
- var barColors = StrListCast(this.props.layoutDoc.histogramBarColors).map(each => each.split('::'));
+ var barColors = StrListCast(this._props.layoutDoc.histogramBarColors).map(each => each.split('::'));
barColors.forEach(each => each[0] === curSelectedBarName && (selectedBarColor = each[1]));
if (this._histogramData.length > 0 || !this.parentViz) {
- return this.props.axes.length >= 1 ? (
+ return this._props.axes.length >= 1 ? (
<div className="chart-container">
<div className="graph-title">
<EditableText
- val={StrCast(this.props.layoutDoc[titleAccessor])}
+ 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'}
@@ -472,9 +478,9 @@ export class Histogram extends React.Component<HistogramProps> {
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')}
+ 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')}
size={Size.XSMALL}
/>
</div>
diff --git a/src/client/views/nodes/DataVizBox/components/LineChart.tsx b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
index 7e0391879..abb95aa4c 100644
--- a/src/client/views/nodes/DataVizBox/components/LineChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/LineChart.tsx
@@ -1,6 +1,6 @@
import { EditableText, Size } from 'browndash-components';
import * as d3 from 'd3';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, DocListCast, NumListCast, StrListCast } from '../../../../../fields/Doc';
@@ -14,6 +14,7 @@ import { PinProps, PresBox } from '../../trails';
import { DataVizBox } from '../DataVizBox';
import { createLineGenerator, drawLine, minMaxRange, scaleCreatorNumerical, xAxisCreator, xGrid, yAxisCreator, yGrid } from '../utils/D3Utils';
import './Chart.scss';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
export interface DataPoint {
x: number;
@@ -40,33 +41,38 @@ export interface LineChartProps {
}
@observer
-export class LineChart extends React.Component<LineChartProps> {
+export class LineChart extends ObservableReactComponent<LineChartProps> {
private _disposers: { [key: string]: IReactionDisposer } = {};
private _lineChartRef: React.RefObject<HTMLDivElement> = React.createRef();
private _lineChartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@observable _currSelected: SelectedDataPoint | undefined = undefined;
// TODO: nda - some sort of mapping that keeps track of the annotated points so we can easily remove when annotations list updates
+ constructor(props:any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get _tableDataIds() {
- return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ return !this.parentViz ? this._props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
}
// returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
@computed get _tableData() {
- return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ 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));
+ 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));
}
@computed get graphTitle() {
- return this.props.axes[1] + ' vs. ' + this.props.axes[0] + ' Line Chart';
+ return this._props.axes[1] + ' vs. ' + this._props.axes[0] + ' Line Chart';
}
@computed get parentViz() {
- return DocCast(this.props.Document.dataViz_parentViz);
- // return LinkManager.Instance.getAllRelatedLinks(this.props.Document) // out of all links
+ return DocCast(this._props.Document.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(this._props.Document) // out of all links
// .filter(link => {
- // return link.link_anchor_1 == this.props.Document.dataViz_parentViz;
+ // return 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
}
@@ -94,7 +100,7 @@ export class LineChart extends React.Component<LineChartProps> {
{ fireImmediately: true }
);
this._disposers.annos = reaction(
- () => DocListCast(this.props.dataDoc[this.props.fieldKey + '_annotations']),
+ () => DocListCast(this._props.dataDoc[this._props.fieldKey + '_annotations']),
annotations => {
// modify how d3 renders so that anything in this annotations list would be potentially highlighted in some way
// could be blue colored to make it look like anchor
@@ -114,7 +120,7 @@ export class LineChart extends React.Component<LineChartProps> {
// redraw annotations when the chart data has changed, or the local or inherited selection has changed
this.clearAnnotations();
selected && this.drawAnnotations(Number(selected.x), Number(selected.y), true);
- incomingHighlited?.forEach((record: any) => this.drawAnnotations(Number(record[this.props.axes[0]]), Number(record[this.props.axes[1]])));
+ incomingHighlited?.forEach((record: any) => this.drawAnnotations(Number(record[this._props.axes[0]]), Number(record[this._props.axes[1]])));
},
{ fireImmediately: true }
);
@@ -170,23 +176,23 @@ export class LineChart extends React.Component<LineChartProps> {
//
title: 'line doc selection' + this._currSelected?.x,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.Document);
+ PresBox.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;
};
@computed get height() {
- return this.props.height - this.props.margin.top - this.props.margin.bottom;
+ return this._props.height - this._props.margin.top - this._props.margin.bottom;
}
@computed get width() {
- return this.props.width - this.props.margin.left - this.props.margin.right;
+ return this._props.width - this._props.margin.left - this._props.margin.right;
}
@computed get defaultGraphTitle() {
- var ax0 = this.props.axes[0];
- var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
- if (this.props.axes.length < 2 || !/\d/.test(this.props.records[0][ax0]) || !ax1) {
+ var ax0 = this._props.axes[0];
+ var ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
+ if (this._props.axes.length < 2 || !/\d/.test(this._props.records[0][ax0]) || !ax1) {
return ax0 + ' Line Chart';
} else return ax1 + ' by ' + ax0 + ' Line Chart';
}
@@ -210,7 +216,7 @@ export class LineChart extends React.Component<LineChartProps> {
// TODO: nda - get rid of svg element in the list?
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>) {
@@ -245,7 +251,7 @@ export class LineChart extends React.Component<LineChartProps> {
const yScale = scaleCreatorNumerical(0, yMax, height, 0);
// adding svg
- const margin = this.props.margin;
+ const margin = this._props.margin;
const svg = (this._lineChartSvg = d3
.select(this._lineChartRef.current)
.append('svg')
@@ -317,7 +323,7 @@ export class LineChart extends React.Component<LineChartProps> {
svg.append('text')
.attr('transform', 'translate(' + width / 2 + ' ,' + (height + 40) + ')')
.style('text-anchor', 'middle')
- .text(this.props.axes[0]);
+ .text(this._props.axes[0]);
svg.append('text')
.attr('transform', 'rotate(-90)' + ' ' + 'translate( 0, ' + -10 + ')')
.attr('x', -(height / 2))
@@ -325,7 +331,7 @@ export class LineChart extends React.Component<LineChartProps> {
.attr('height', 20)
.attr('width', 20)
.style('text-anchor', 'middle')
- .text(this.props.axes[1]);
+ .text(this._props.axes[1]);
};
private updateTooltip(
@@ -346,18 +352,18 @@ export class LineChart extends React.Component<LineChartProps> {
render() {
var titleAccessor: any = '';
- if (this.props.axes.length == 2) titleAccessor = 'dataViz_lineChart_title' + this.props.axes[0] + '-' + this.props.axes[1];
- else if (this.props.axes.length > 0) titleAccessor = 'dataViz_lineChart_title' + this.props.axes[0];
- if (!this.props.layoutDoc[titleAccessor]) this.props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
- const selectedPt = this._currSelected ? `{ ${this.props.axes[0]}: ${this._currSelected.x} ${this.props.axes[1]}: ${this._currSelected.y} }` : 'none';
+ if (this._props.axes.length == 2) titleAccessor = 'dataViz_lineChart_title' + this._props.axes[0] + '-' + this._props.axes[1];
+ else if (this._props.axes.length > 0) titleAccessor = 'dataViz_lineChart_title' + this._props.axes[0];
+ if (!this._props.layoutDoc[titleAccessor]) this._props.layoutDoc[titleAccessor] = this.defaultGraphTitle;
+ const selectedPt = this._currSelected ? `{ ${this._props.axes[0]}: ${this._currSelected.x} ${this._props.axes[1]}: ${this._currSelected.y} }` : 'none';
if (this._lineChartData.length > 0 || !this.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]]) ? (
+ 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">
<div className="graph-title">
<EditableText
- val={StrCast(this.props.layoutDoc[titleAccessor])}
+ 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'}
diff --git a/src/client/views/nodes/DataVizBox/components/PieChart.tsx b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
index c2e03744e..9e7265b26 100644
--- a/src/client/views/nodes/DataVizBox/components/PieChart.tsx
+++ b/src/client/views/nodes/DataVizBox/components/PieChart.tsx
@@ -1,7 +1,7 @@
import { Checkbox } from '@mui/material';
import { ColorPicker, EditableText, Size, Type } from 'browndash-components';
import * as d3 from 'd3';
-import { action, computed, IReactionDisposer, observable, reaction } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { FaFillDrip } from 'react-icons/fa';
@@ -13,6 +13,7 @@ import { Docs } from '../../../../documents/Documents';
import { undoable } from '../../../../util/UndoManager';
import { PinProps, PresBox } from '../../trails';
import './Chart.scss';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
export interface PieChartProps {
Document: Doc;
@@ -32,7 +33,7 @@ export interface PieChartProps {
}
@observer
-export class PieChart extends React.Component<PieChartProps> {
+export class PieChart extends ObservableReactComponent<PieChartProps> {
private _disposers: { [key: string]: IReactionDisposer } = {};
private _piechartRef: React.RefObject<HTMLDivElement> = React.createRef();
private _piechartSvg: d3.Selection<SVGGElement, unknown, null, undefined> | undefined;
@@ -41,44 +42,49 @@ export class PieChart extends React.Component<PieChartProps> {
private hoverOverData: any = undefined; // Selection of slice being hovered over
@observable _currSelected: any | undefined = undefined; // Object of selected slice
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
+
@computed get _tableDataIds() {
- return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ return !this.parentViz ? this._props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
}
// returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
@computed get _tableData() {
- return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ return !this.parentViz ? this._props.records : this._tableDataIds.map(rowId => this._props.records[rowId]);
}
// organized by specified number percentages/ratios if one column is selected and it contains numbers
// otherwise, assume data is organized by categories
@computed get byCategory() {
- return !/\d/.test(this.props.records[0][this.props.axes[0]]) || this.props.layoutDoc.dataViz_pie_asHistogram;
+ return !/\d/.test(this._props.records[0][this._props.axes[0]]) || this._props.layoutDoc.dataViz_pie_asHistogram;
}
// filters all data to just display selected data if brushed (created from an incoming link)
@computed get _pieChartData() {
- if (this.props.axes.length < 1) return [];
+ if (this._props.axes.length < 1) return [];
- const ax0 = this.props.axes[0];
- if (this.props.axes.length < 2) {
- return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]] }));
+ const ax0 = this._props.axes[0];
+ if (this._props.axes.length < 2) {
+ return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]] }));
}
- const ax1 = this.props.axes[1];
- return this._tableData.map(record => ({ [ax0]: record[this.props.axes[0]], [ax1]: record[this.props.axes[1]] }));
+ const ax1 = this._props.axes[1];
+ return this._tableData.map(record => ({ [ax0]: record[this._props.axes[0]], [ax1]: record[this._props.axes[1]] }));
}
@computed get defaultGraphTitle() {
- var ax0 = this.props.axes[0];
- var ax1 = this.props.axes.length > 1 ? this.props.axes[1] : undefined;
- if (this.props.axes.length < 2 || !/\d/.test(this.props.records[0][ax0]) || !ax1) {
+ var ax0 = this._props.axes[0];
+ var ax1 = this._props.axes.length > 1 ? this._props.axes[1] : undefined;
+ if (this._props.axes.length < 2 || !/\d/.test(this._props.records[0][ax0]) || !ax1) {
return ax0 + ' Pie Chart';
}
return ax1 + ' by ' + ax0 + ' Pie Chart';
}
@computed get parentViz() {
- return DocCast(this.props.Document.dataViz_parentViz);
- // return LinkManager.Instance.getAllRelatedLinks(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
+ return DocCast(this._props.Document.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(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,16 +107,16 @@ export class PieChart extends React.Component<PieChartProps> {
//
title: 'piechart doc selection' + this._currSelected,
});
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this.props.Document);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: pinProps?.pinData }, this._props.Document);
return anchor;
};
@computed get height() {
- return this.props.height - this.props.margin.top - this.props.margin.bottom;
+ return this._props.height - this._props.margin.top - this._props.margin.bottom;
}
@computed get width() {
- return this.props.width - this.props.margin.left - this.props.margin.right;
+ return this._props.width - this._props.margin.left - this._props.margin.right;
}
// cleans data by converting numerical data to numbers and taking out empty cells
@@ -181,7 +187,7 @@ export class PieChart extends React.Component<PieChartProps> {
var percentField = Object.keys(dataSet[0])[0];
var descriptionField = Object.keys(dataSet[0])[1]!;
- var radius = Math.min(width, height - this.props.margin.top - this.props.margin.bottom) / 2;
+ var radius = Math.min(width, height - this._props.margin.top - this._props.margin.bottom) / 2;
// converts data into Objects
var data = this.data(dataSet);
@@ -209,10 +215,10 @@ export class PieChart extends React.Component<PieChartProps> {
.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)
+ .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 + ')');
+ 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);
@@ -249,7 +255,7 @@ export class PieChart extends React.Component<PieChartProps> {
} catch (error) {}
possibleDataPointVals.push(dataPointVal);
});
- const sliceColors = StrListCast(this.props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
+ const sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
arcs.append('path')
.attr('fill', (d, i) => {
var dataPoint;
@@ -261,7 +267,7 @@ export class PieChart extends React.Component<PieChartProps> {
}
var sliceColor;
if (dataPoint) {
- const sliceTitle = dataPoint[this.props.axes[0]];
+ 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]));
}
@@ -309,10 +315,10 @@ export class PieChart extends React.Component<PieChartProps> {
@action changeSelectedColor = (color: string) => {
this.curSliceSelected.attr('fill', color);
- const sliceTitle = this._currSelected[this.props.axes[0]];
+ const sliceTitle = this._currSelected[this._props.axes[0]];
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);
+ 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);
});
@@ -320,20 +326,20 @@ export class PieChart extends React.Component<PieChartProps> {
};
@action changeHistogramCheckBox = () => {
- this.props.layoutDoc.dataViz_pie_asHistogram = !this.props.layoutDoc.dataViz_pie_asHistogram;
+ this._props.layoutDoc.dataViz_pie_asHistogram = !this._props.layoutDoc.dataViz_pie_asHistogram;
this.drawChart(this._pieChartData, this.width, this.height);
};
render() {
var titleAccessor: any = '';
- if (this.props.axes.length == 2) titleAccessor = 'dataViz_pie_title' + this.props.axes[0] + '-' + this.props.axes[1];
- else if (this.props.axes.length > 0) titleAccessor = 'dataViz_pie_title' + 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>();
+ if (this._props.axes.length == 2) titleAccessor = 'dataViz_pie_title' + this._props.axes[0] + '-' + this._props.axes[1];
+ else if (this._props.axes.length > 0) titleAccessor = 'dataViz_pie_title' + 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 = '';
if (this._currSelected) {
- const sliceTitle = this._currSelected[this.props.axes[0]];
+ const sliceTitle = this._currSelected[this._props.axes[0]];
curSelectedSliceName = StrCast(sliceTitle) ? StrCast(sliceTitle).replace(/\$/g, '').replace(/\%/g, '').replace(/\#/g, '').replace(/\</g, '') : sliceTitle;
selected = '{ ';
Object.keys(this._currSelected).map(key => {
@@ -343,19 +349,19 @@ export class PieChart extends React.Component<PieChartProps> {
selected += ' }';
} else selected = 'none';
var selectedSliceColor;
- var sliceColors = StrListCast(this.props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
+ var sliceColors = StrListCast(this._props.layoutDoc.dataViz_pie_sliceColors).map(each => each.split('::'));
sliceColors.forEach(each => {
if (each[0] == curSelectedSliceName!) selectedSliceColor = each[1];
});
if (this._pieChartData.length > 0 || !this.parentViz) {
- return this.props.axes.length >= 1 ? (
+ return this._props.axes.length >= 1 ? (
<div className="chart-container">
<div className="graph-title">
<EditableText
- val={StrCast(this.props.layoutDoc[titleAccessor])}
+ 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'}
@@ -363,9 +369,9 @@ export class PieChart extends React.Component<PieChartProps> {
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 }}>
- <Checkbox color="primary" onChange={this.changeHistogramCheckBox} checked={this.props.layoutDoc.dataViz_pie_asHistogram as boolean} />
+ {this._props.axes.length === 1 && /\d/.test(this._props.records[0][this._props.axes[0]]) ? (
+ <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}
diff --git a/src/client/views/nodes/DataVizBox/components/TableBox.tsx b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
index 3e7d3af8c..f77c138df 100644
--- a/src/client/views/nodes/DataVizBox/components/TableBox.tsx
+++ b/src/client/views/nodes/DataVizBox/components/TableBox.tsx
@@ -1,5 +1,5 @@
import { Button, Type } from 'browndash-components';
-import { action, computed, IReactionDisposer, observable, reaction, trace } from 'mobx';
+import { action, computed, IReactionDisposer, makeObservable, observable, reaction, trace } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { Doc, Field, NumListCast } from '../../../../../fields/Doc';
@@ -11,6 +11,7 @@ import { DragManager } from '../../../../util/DragManager';
import { DocumentView } from '../../DocumentView';
import { DataVizView } from '../DataVizBox';
import './Chart.scss';
+import { ObservableReactComponent } from '../../../ObservableReactComponent';
const { default: { DATA_VIZ_TABLE_ROW_HEIGHT } } = require('../../../global/globalCssVariables.module.scss'); // prettier-ignore
interface TableBoxProps {
Document: Doc;
@@ -30,13 +31,17 @@ interface TableBoxProps {
}
@observer
-export class TableBox extends React.Component<TableBoxProps> {
+export class TableBox extends ObservableReactComponent<TableBoxProps> {
_inputChangedDisposer?: IReactionDisposer;
_containerRef: HTMLDivElement | null = null;
@observable _scrollTop = -1;
@observable _tableHeight = 0;
@observable _tableContainerHeight = 0;
+ constructor(props: any) {
+ super(props);
+ makeObservable(this);
+ }
componentDidMount() {
// if the tableData changes (ie., when records are selected by the parent (input) visulization),
@@ -48,17 +53,17 @@ export class TableBox extends React.Component<TableBoxProps> {
this._inputChangedDisposer?.();
}
@computed get _tableDataIds() {
- return !this.parentViz ? this.props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
+ return !this.parentViz ? this._props.records.map((rec, i) => i) : NumListCast(this.parentViz.dataViz_selectedRows);
}
// returns all the data records that will be rendered by only returning those records that have been selected by the parent visualization (or all records if there is no parent)
@computed get _tableData() {
- return !this.parentViz ? this.props.records : this._tableDataIds.map(rowId => this.props.records[rowId]);
+ return !this.parentViz ? this._props.records : this._tableDataIds.map(rowId => this._props.records[rowId]);
}
@computed get parentViz() {
- return DocCast(this.props.Document.dataViz_parentViz);
- // return LinkManager.Instance.getAllRelatedLinks(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
+ return DocCast(this._props.Document.dataViz_parentViz);
+ // return LinkManager.Instance.getAllRelatedLinks(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
}
@@ -68,14 +73,14 @@ export class TableBox extends React.Component<TableBoxProps> {
// updates the 'dataViz_selectedRows' and 'dataViz_highlightedRows' fields to no longer include rows that aren't in the table
filterSelectedRowsDown = () => {
- const selected = NumListCast(this.props.layoutDoc.dataViz_selectedRows);
- this.props.layoutDoc.dataViz_selectedRows = new List<number>(selected.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data
- const highlighted = NumListCast(this.props.layoutDoc.dataViz_highlitedRows);
- this.props.layoutDoc.dataViz_highlitedRows = new List<number>(highlighted.filter(rowId => this._tableDataIds.includes(rowId))); // filters through highlighted to remove guids that were removed in the incoming data
+ const selected = NumListCast(this._props.layoutDoc.dataViz_selectedRows);
+ this._props.layoutDoc.dataViz_selectedRows = new List<number>(selected.filter(rowId => this._tableDataIds.includes(rowId))); // filters through selected to remove guids that were removed in the incoming data
+ const highlighted = NumListCast(this._props.layoutDoc.dataViz_highlitedRows);
+ this._props.layoutDoc.dataViz_highlitedRows = new List<number>(highlighted.filter(rowId => this._tableDataIds.includes(rowId))); // filters through highlighted to remove guids that were removed in the incoming data
};
@computed get viewScale() {
- return this.props.docView?.()?._props.ScreenToLocalTransform().Scale || 1;
+ return this._props.docView?.()?._props.ScreenToLocalTransform().Scale || 1;
}
@computed get rowHeight() {
console.log('scale = ' + this.viewScale + ' table = ' + this._tableHeight + ' ids = ' + this._tableDataIds.length);
@@ -89,14 +94,14 @@ export class TableBox extends React.Component<TableBoxProps> {
return Math.ceil(this.startID + (this._tableContainerHeight * this.viewScale) / (this.rowHeight || 1));
}
@action handleScroll = () => {
- if (!this.props.docView?.()?.ContentDiv?.hidden) {
+ if (!this._props.docView?.()?.ContentDiv?.hidden) {
this._scrollTop = this._containerRef?.scrollTop ?? 0;
}
};
@action
tableRowClick = (e: React.MouseEvent, rowId: number) => {
- const highlited = Cast(this.props.layoutDoc.dataViz_highlitedRows, listSpec('number'), null);
- const selected = Cast(this.props.layoutDoc.dataViz_selectedRows, listSpec('number'), null);
+ const highlited = Cast(this._props.layoutDoc.dataViz_highlitedRows, listSpec('number'), null);
+ const selected = Cast(this._props.layoutDoc.dataViz_selectedRows, listSpec('number'), null);
if (e.metaKey) {
// highlighting a row
if (highlited?.includes(rowId)) highlited.splice(highlited.indexOf(rowId), 1);
@@ -120,26 +125,26 @@ export class TableBox extends React.Component<TableBoxProps> {
e,
e => {
// dragging off a column to create a brushed DataVizBox
- const sourceAnchorCreator = () => this.props.docView?.()!.Document!;
+ const sourceAnchorCreator = () => this._props.docView?.()!.Document!;
const targetCreator = (annotationOn: Doc | undefined) => {
- const embedding = Doc.MakeEmbedding(this.props.docView?.()!.Document!);
+ const embedding = Doc.MakeEmbedding(this._props.docView?.()!.Document!);
embedding._dataViz = DataVizView.TABLE;
embedding._dataViz_axes = new List<string>([col, col]);
- embedding._dataViz_parentViz = this.props.Document;
+ embedding._dataViz_parentViz = this._props.Document;
embedding.annotationOn = annotationOn;
- embedding.histogramBarColors = Field.Copy(this.props.layoutDoc.histogramBarColors);
- embedding.defaultHistogramColor = this.props.layoutDoc.defaultHistogramColor;
- embedding.pieSliceColors = Field.Copy(this.props.layoutDoc.pieSliceColors);
+ embedding.histogramBarColors = Field.Copy(this._props.layoutDoc.histogramBarColors);
+ embedding.defaultHistogramColor = this._props.layoutDoc.defaultHistogramColor;
+ embedding.pieSliceColors = Field.Copy(this._props.layoutDoc.pieSliceColors);
return embedding;
};
- if (this.props.docView?.() && !Utils.isClick(e.clientX, e.clientY, downX, downY, Date.now())) {
- DragManager.StartAnchorAnnoDrag(e.target instanceof HTMLElement ? [e.target] : [], new DragManager.AnchorAnnoDragData(this.props.docView()!, sourceAnchorCreator, targetCreator), downX, downY, {
+ 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.followLinkToggle = e.annoDragData.dropDocument.annotationOn === this._props.Document;
// e.annoDragData.linkSourceDoc.followLinkZoom = false;
}
},
@@ -150,11 +155,11 @@ export class TableBox extends React.Component<TableBoxProps> {
},
emptyFunction,
action(e => {
- const newAxes = this.props.axes;
+ const newAxes = this._props.axes;
if (newAxes.includes(col)) newAxes.splice(newAxes.indexOf(col), 1);
else if (newAxes.length > 1) newAxes[1] = col;
else newAxes.push(col);
- this.props.selectAxes(newAxes);
+ this._props.selectAxes(newAxes);
})
);
};
@@ -166,14 +171,14 @@ export class TableBox extends React.Component<TableBoxProps> {
className="tableBox"
tabIndex={0}
onKeyDown={e => {
- if (this.props.layoutDoc && e.key === 'a' && (e.ctrlKey || e.metaKey)) {
+ if (this._props.layoutDoc && e.key === 'a' && (e.ctrlKey || e.metaKey)) {
e.stopPropagation();
- this.props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds);
+ this._props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds);
}
}}>
<div className="selectAll-buttons">
- <Button onClick={action(() => (this.props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds)))} text="Select All" type={Type.SEC} color={'black'} />
- <Button onClick={action(() => (this.props.layoutDoc.dataViz_selectedRows = new List<number>()))} text="Deselect All" type={Type.SEC} color={'black'} />
+ <Button onClick={action(() => (this._props.layoutDoc.dataViz_selectedRows = new List<number>(this._tableDataIds)))} text="Select All" type={Type.SEC} color={'black'} />
+ <Button onClick={action(() => (this._props.layoutDoc.dataViz_selectedRows = new List<number>()))} text="Deselect All" type={Type.SEC} color={'black'} />
</div>
<div
className={`tableBox-container ${this.columns[0]}`}
@@ -181,7 +186,7 @@ export class TableBox extends React.Component<TableBoxProps> {
onScroll={this.handleScroll}
ref={action((r: HTMLDivElement | null) => {
this._containerRef = r;
- if (!this.props.docView?.()?.ContentDiv?.hidden && r) {
+ if (!this._props.docView?.()?.ContentDiv?.hidden && r) {
this._tableContainerHeight = r.getBoundingClientRect().height ?? 0;
r.addEventListener(
'wheel', // if scrollTop is 0, then don't let wheel trigger scroll on any container (which it would since onScroll won't be triggered on this)
@@ -196,7 +201,7 @@ export class TableBox extends React.Component<TableBoxProps> {
<table
className="tableBox-table"
ref={action((r: HTMLTableElement | null) => {
- if (!this.props.docView?.()?.ContentDiv?.hidden && r) {
+ if (!this._props.docView?.()?.ContentDiv?.hidden && r) {
this._tableHeight = r?.getBoundingClientRect().height ?? 0;
}
})}>
@@ -207,8 +212,8 @@ export class TableBox extends React.Component<TableBoxProps> {
<th
key={this.columns.indexOf(col)}
style={{
- color: this.props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this.props.axes.lastElement() === col ? 'darkred' : undefined,
- background: this.props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this.props.axes.lastElement() === col ? '#Fbdbdb' : undefined,
+ color: this._props.axes.slice().reverse().lastElement() === col ? 'darkgreen' : this._props.axes.lastElement() === col ? 'darkred' : undefined,
+ background: this._props.axes.slice().reverse().lastElement() === col ? '#E3fbdb' : this._props.axes.lastElement() === col ? '#Fbdbdb' : undefined,
fontWeight: 'bolder',
border: '3px solid black',
}}
@@ -227,13 +232,13 @@ export class TableBox extends React.Component<TableBoxProps> {
className={`tableBox-row ${this.columns[0]}`}
onClick={e => this.tableRowClick(e, rowId)}
style={{
- background: NumListCast(this.props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this.props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '',
+ background: NumListCast(this._props.layoutDoc.dataViz_highlitedRows).includes(rowId) ? 'lightYellow' : NumListCast(this._props.layoutDoc.dataViz_selectedRows).includes(rowId) ? 'lightgrey' : '',
}}>
{this.columns.map(col => {
- const colSelected = this.props.axes.length > 1 ? this.props.axes[0] == col || this.props.axes[1] == col : this.props.axes.length > 0 ? this.props.axes[0] == col : false;
+ const colSelected = this._props.axes.length > 1 ? this._props.axes[0] == col || this._props.axes[1] == col : this._props.axes.length > 0 ? this._props.axes[0] == col : false;
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>
+ <div className="tableBox-cell">{this._props.records[rowId][col]}</div>
</td>
);
})}
diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts
index 256acbf13..13ce5b7e2 100644
--- a/src/client/views/nodes/MapBox/AnimationUtility.ts
+++ b/src/client/views/nodes/MapBox/AnimationUtility.ts
@@ -1,13 +1,13 @@
-import mapboxgl from "mapbox-gl";
-import { MercatorCoordinate } from "mapbox-gl";
-import { MapRef } from "react-map-gl";
+import mapboxgl from 'mapbox-gl';
+import { MercatorCoordinate } from 'mapbox-gl';
+import { MapRef } from 'react-map-gl';
import * as React from 'react';
-import * as d3 from "d3";
+import * as d3 from 'd3';
import * as turf from '@turf/turf';
-import { Position } from "@turf/turf";
-import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from "geojson";
-import { observer } from "mobx-react";
-import { action, computed, observable } from "mobx";
+import { Position } from '@turf/turf';
+import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson';
+import { observer } from 'mobx-react';
+import { action, computed, observable } from 'mobx';
export enum AnimationStatus {
START = 'start',
@@ -21,37 +21,35 @@ export enum AnimationSpeed {
FAST = '3x',
}
-@observer
export class AnimationUtility {
- private SMOOTH_FACTOR = 0.95
- private ROUTE_COORDINATES: Position[] = [];
+ private SMOOTH_FACTOR = 0.95;
+ private ROUTE_COORDINATES: Position[] = [];
private PATH: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>;
private FLY_IN_START_PITCH = 40;
- private FIRST_LNG_LAT: {lng: number, lat: number};
+ private FIRST_LNG_LAT: { lng: number; lat: number };
private START_ALTITUDE = 3_000_000;
@observable private isStreetViewAnimation: boolean;
@observable private animationSpeed: AnimationSpeed;
-
- private previousLngLat: {lng: number, lat: number};
+ private previousLngLat: { lng: number; lat: number };
private currentStreetViewBearing: number = 0;
@computed get flyInEndBearing() {
- return this.isStreetViewAnimation ?
- this.calculateBearing(
- {
- lng: this.ROUTE_COORDINATES[0][0],
- lat: this.ROUTE_COORDINATES[0][1]
- },
- {
- lng: this.ROUTE_COORDINATES[1][0],
- lat: this.ROUTE_COORDINATES[1][1]
- }
- )
+ return this.isStreetViewAnimation
+ ? this.calculateBearing(
+ {
+ lng: this.ROUTE_COORDINATES[0][0],
+ lat: this.ROUTE_COORDINATES[0][1],
+ },
+ {
+ lng: this.ROUTE_COORDINATES[1][0],
+ lat: this.ROUTE_COORDINATES[1][1],
+ }
+ )
: -20;
- }
+ }
@computed get flyInStartBearing() {
return Math.max(0, Math.min(this.flyInEndBearing + 20, 360)); // between 0 and 360
@@ -80,8 +78,8 @@ export class AnimationUtility {
@computed get animationDuration(): number {
return 20_000;
- // compute path distance for a standard speed
- // get animation speed
+ // compute path distance for a standard speed
+ // get animation speed
// get isStreetViewAnimation (should be slower if so)
}
@@ -90,19 +88,13 @@ export class AnimationUtility {
// calculate new flyToDuration and animationDuration
this.animationSpeed = speed;
}
-
+
@action
public updateIsStreetViewAnimation(isStreetViewAnimation: boolean) {
this.isStreetViewAnimation = isStreetViewAnimation;
}
-
- constructor(
- firstLngLat: {lng: number, lat: number},
- routeCoordinates: Position[],
- isStreetViewAnimation: boolean,
- animationSpeed: AnimationSpeed
- ) {
+ constructor(firstLngLat: { lng: number; lat: number }, routeCoordinates: Position[], isStreetViewAnimation: boolean, animationSpeed: AnimationSpeed) {
this.FIRST_LNG_LAT = firstLngLat;
this.previousLngLat = firstLngLat;
this.ROUTE_COORDINATES = routeCoordinates;
@@ -111,11 +103,11 @@ export class AnimationUtility {
const bearing = this.calculateBearing(
{
lng: routeCoordinates[0][0],
- lat: routeCoordinates[0][1]
+ lat: routeCoordinates[0][1],
},
{
lng: routeCoordinates[1][0],
- lat: routeCoordinates[1][1]
+ lat: routeCoordinates[1][1],
}
);
this.currentStreetViewBearing = bearing;
@@ -137,293 +129,245 @@ export class AnimationUtility {
currentAnimationPhase,
updateAnimationPhase,
updateFrameId,
- }: {
- map: MapRef,
+ }: {
+ map: MapRef;
// path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>,
// startBearing: number,
// startAltitude: number,
// pitch: number,
- currentAnimationPhase: number,
- updateAnimationPhase: (
- newAnimationPhase: number,
- ) => void,
+ currentAnimationPhase: number;
+ updateAnimationPhase: (newAnimationPhase: number) => void;
updateFrameId: (newFrameId: number) => void;
+ }) => {
+ return new Promise<void>(async resolve => {
+ const pathDistance = turf.lineDistance(this.PATH);
+ console.log('PATH DISTANCE: ', pathDistance);
+ let startTime: number | null = null;
+
+ const frame = async (currentTime: number) => {
+ if (!startTime) startTime = currentTime;
+ const elapsedSinceLastFrame = currentTime - startTime;
+ const phaseIncrement = elapsedSinceLastFrame / this.animationDuration;
+ const animationPhase = currentAnimationPhase + phaseIncrement;
+
+ // when the duration is complete, resolve the promise and stop iterating
+ if (animationPhase > 1) {
+ resolve();
+ return;
+ }
- }) => {
- return new Promise<void>(async (resolve) => {
- const pathDistance = turf.lineDistance(this.PATH);
- console.log("PATH DISTANCE: ", pathDistance);
- let startTime: number | null = null;
-
- const frame = async (currentTime: number) => {
- if (!startTime) startTime = currentTime;
- const elapsedSinceLastFrame = currentTime - startTime;
- const phaseIncrement = elapsedSinceLastFrame / this.animationDuration;
- const animationPhase = currentAnimationPhase + phaseIncrement;
-
- // when the duration is complete, resolve the promise and stop iterating
- if (animationPhase > 1) {
- resolve();
- return;
- }
+ // calculate the distance along the path based on the animationPhase
+ const alongPath = turf.along(this.PATH, pathDistance * animationPhase).geometry.coordinates;
+
+ const lngLat = {
+ lng: alongPath[0],
+ lat: alongPath[1],
+ };
+
+ let bearing: number;
+ if (this.isStreetViewAnimation) {
+ bearing = this.lerp(
+ this.currentStreetViewBearing,
+ this.calculateBearing(this.previousLngLat, lngLat),
+ 0.028 // Adjust the transition speed as needed
+ );
+ this.currentStreetViewBearing = bearing;
+ // bearing = this.calculateBearing(this.previousLngLat, lngLat); // TODO: Calculate actual bearing
+ } else {
+ // slowly rotate the map at a constant rate
+ bearing = this.flyInEndBearing - animationPhase * 200.0;
+ // bearing = startBearing - animationPhase * 200.0;
+ }
+
+ this.previousLngLat = lngLat;
+
+ updateAnimationPhase(animationPhase);
+
+ // compute corrected camera ground position, so that he leading edge of the path is in view
+ var correctedPosition = this.computeCameraPosition(
+ this.isStreetViewAnimation,
+ this.flyInEndPitch,
+ bearing,
+ lngLat,
+ this.flyInEndAltitude,
+ true // smooth
+ );
+
+ // set the pitch and bearing of the camera
+ const camera = map.getFreeCameraOptions();
+ camera.setPitchBearing(this.flyInEndPitch, bearing);
+
+ console.log('Corrected pos: ', correctedPosition);
+ console.log('Start altitude: ', this.flyInEndAltitude);
+ // set the position and altitude of the camera
+ camera.position = MercatorCoordinate.fromLngLat(correctedPosition, this.flyInEndAltitude);
+
+ // apply the new camera options
+ map.setFreeCameraOptions(camera);
-
- // calculate the distance along the path based on the animationPhase
- const alongPath = turf.along(this.PATH, pathDistance * animationPhase).geometry
- .coordinates;
-
- const lngLat = {
- lng: alongPath[0],
- lat: alongPath[1],
+ // repeat!
+ const innerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(innerFrameId);
};
-
- let bearing: number;
- if (this.isStreetViewAnimation){
- bearing = this.lerp(
- this.currentStreetViewBearing,
- this.calculateBearing(this.previousLngLat, lngLat),
- 0.028 // Adjust the transition speed as needed
- );
- this.currentStreetViewBearing = bearing;
- // bearing = this.calculateBearing(this.previousLngLat, lngLat); // TODO: Calculate actual bearing
- } else {
- // slowly rotate the map at a constant rate
- bearing = this.flyInEndBearing - animationPhase * 200.0;
- // bearing = startBearing - animationPhase * 200.0;
- }
- this.previousLngLat = lngLat;
-
- updateAnimationPhase(animationPhase);
-
- // compute corrected camera ground position, so that he leading edge of the path is in view
- var correctedPosition = this.computeCameraPosition(
- this.isStreetViewAnimation,
- this.flyInEndPitch,
- bearing,
- lngLat,
- this.flyInEndAltitude,
- true // smooth
- );
-
- // set the pitch and bearing of the camera
- const camera = map.getFreeCameraOptions();
- camera.setPitchBearing(this.flyInEndPitch, bearing);
-
- console.log("Corrected pos: ", correctedPosition);
- console.log("Start altitude: ", this.flyInEndAltitude);
- // set the position and altitude of the camera
- camera.position = MercatorCoordinate.fromLngLat(
- correctedPosition,
- this.flyInEndAltitude
- );
-
-
- // apply the new camera options
- map.setFreeCameraOptions(camera);
-
- // repeat!
- const innerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(innerFrameId);
- };
-
- const outerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(outerFrameId);
+ const outerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(outerFrameId);
});
- };
+ };
- public flyInAndRotate = async ({
- map,
- updateFrameId
- }:
- {
- map: MapRef,
- updateFrameId: (newFrameId: number) => void
- }
- ) => {
- return new Promise<{bearing: number, altitude: number}>(async (resolve) => {
- let start: number | null;
-
- var currentAltitude;
- var currentBearing;
- var currentPitch;
-
- // the animation frame will run as many times as necessary until the duration has been reached
- const frame = async (time: number) => {
- if (!start) {
- start = time;
- }
-
- // otherwise, use the current time to determine how far along in the duration we are
- let animationPhase = (time - start) / this.flyToDuration;
-
- // because the phase calculation is imprecise, the final zoom can vary
- // if it ended up greater than 1, set it to 1 so that we get the exact endAltitude that was requested
- if (animationPhase > 1) {
- animationPhase = 1;
- }
-
- currentAltitude = this.START_ALTITUDE + (this.flyInEndAltitude - this.START_ALTITUDE) * d3.easeCubicOut(animationPhase)
- // rotate the camera between startBearing and endBearing
- currentBearing = this.flyInStartBearing + (this.flyInEndBearing - this.flyInStartBearing) * d3.easeCubicOut(animationPhase)
-
- currentPitch = this.FLY_IN_START_PITCH + (this.flyInEndPitch - this.FLY_IN_START_PITCH) * d3.easeCubicOut(animationPhase)
-
- // compute corrected camera ground position, so the start of the path is always in view
- var correctedPosition = this.computeCameraPosition(
- false,
- currentPitch,
- currentBearing,
- this.FIRST_LNG_LAT,
- currentAltitude
- );
-
- // set the pitch and bearing of the camera
- const camera = map.getFreeCameraOptions();
- camera.setPitchBearing(currentPitch, currentBearing);
-
- // set the position and altitude of the camera
- camera.position = MercatorCoordinate.fromLngLat(
- correctedPosition,
- currentAltitude
- );
-
- // apply the new camera options
- map.setFreeCameraOptions(camera);
-
- // when the animationPhase is done, resolve the promise so the parent function can move on to the next step in the sequence
- if (animationPhase === 1) {
- resolve({
- bearing: currentBearing,
- altitude: currentAltitude,
- });
-
- // return so there are no further iterations of this frame
- return;
- }
-
- const innerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(innerFrameId);
-
- };
-
- const outerFrameId = await window.requestAnimationFrame(frame);
- updateFrameId(outerFrameId);
+ public flyInAndRotate = async ({ map, updateFrameId }: { map: MapRef; updateFrameId: (newFrameId: number) => void }) => {
+ return new Promise<{ bearing: number; altitude: number }>(async resolve => {
+ let start: number | null;
+
+ var currentAltitude;
+ var currentBearing;
+ var currentPitch;
+
+ // the animation frame will run as many times as necessary until the duration has been reached
+ const frame = async (time: number) => {
+ if (!start) {
+ start = time;
+ }
+
+ // otherwise, use the current time to determine how far along in the duration we are
+ let animationPhase = (time - start) / this.flyToDuration;
+
+ // because the phase calculation is imprecise, the final zoom can vary
+ // if it ended up greater than 1, set it to 1 so that we get the exact endAltitude that was requested
+ if (animationPhase > 1) {
+ animationPhase = 1;
+ }
+
+ currentAltitude = this.START_ALTITUDE + (this.flyInEndAltitude - this.START_ALTITUDE) * d3.easeCubicOut(animationPhase);
+ // rotate the camera between startBearing and endBearing
+ currentBearing = this.flyInStartBearing + (this.flyInEndBearing - this.flyInStartBearing) * d3.easeCubicOut(animationPhase);
+
+ currentPitch = this.FLY_IN_START_PITCH + (this.flyInEndPitch - this.FLY_IN_START_PITCH) * d3.easeCubicOut(animationPhase);
+
+ // compute corrected camera ground position, so the start of the path is always in view
+ var correctedPosition = this.computeCameraPosition(false, currentPitch, currentBearing, this.FIRST_LNG_LAT, currentAltitude);
+
+ // set the pitch and bearing of the camera
+ const camera = map.getFreeCameraOptions();
+ camera.setPitchBearing(currentPitch, currentBearing);
+
+ // set the position and altitude of the camera
+ camera.position = MercatorCoordinate.fromLngLat(correctedPosition, currentAltitude);
+
+ // apply the new camera options
+ map.setFreeCameraOptions(camera);
+
+ // when the animationPhase is done, resolve the promise so the parent function can move on to the next step in the sequence
+ if (animationPhase === 1) {
+ resolve({
+ bearing: currentBearing,
+ altitude: currentAltitude,
+ });
+
+ // return so there are no further iterations of this frame
+ return;
+ }
+
+ const innerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(innerFrameId);
+ };
+
+ const outerFrameId = await window.requestAnimationFrame(frame);
+ updateFrameId(outerFrameId);
});
- };
+ };
- previousCameraPosition: {lng: number, lat: number} | null = null;
+ previousCameraPosition: { lng: number; lat: number } | null = null;
- lerp = (start: number, end: number, amt: number) => {
+ lerp = (start: number, end: number, amt: number) => {
return (1 - amt) * start + amt * end;
- }
-
- computeCameraPosition = (
- isStreetViewAnimation: boolean,
- pitch: number,
- bearing: number,
- targetPosition: {lng: number, lat: number},
- altitude: number,
- smooth = false
- ) => {
+ };
+
+ computeCameraPosition = (isStreetViewAnimation: boolean, pitch: number, bearing: number, targetPosition: { lng: number; lat: number }, altitude: number, smooth = false) => {
const bearingInRadian = (bearing * Math.PI) / 180;
- const pitchInRadian = ((90 - pitch)* Math.PI) / 180;
+ const pitchInRadian = ((90 - pitch) * Math.PI) / 180;
let correctedLng = targetPosition.lng;
let correctedLat = targetPosition.lat;
if (!isStreetViewAnimation) {
- const lngDiff =
- ((altitude / Math.tan(pitchInRadian)) *
- Math.sin(-bearingInRadian)) /
- 70000; // ~70km/degree longitude
- const latDiff =
- ((altitude / Math.tan(pitchInRadian)) *
- Math.cos(-bearingInRadian)) /
- 110000 // 110km/degree latitude
+ const lngDiff = ((altitude / Math.tan(pitchInRadian)) * Math.sin(-bearingInRadian)) / 70000; // ~70km/degree longitude
+ const latDiff = ((altitude / Math.tan(pitchInRadian)) * Math.cos(-bearingInRadian)) / 110000; // 110km/degree latitude
correctedLng = targetPosition.lng + lngDiff;
correctedLat = targetPosition.lat - latDiff;
-
}
-
+
const newCameraPosition = {
- lng: correctedLng,
- lat: correctedLat
+ lng: correctedLng,
+ lat: correctedLat,
};
-
+
if (smooth) {
- if (this.previousCameraPosition) {
- newCameraPosition.lng = this.lerp(newCameraPosition.lng, this.previousCameraPosition.lng, this.SMOOTH_FACTOR);
- newCameraPosition.lat = this.lerp(newCameraPosition.lat, this.previousCameraPosition.lat, this.SMOOTH_FACTOR);
- }
+ if (this.previousCameraPosition) {
+ newCameraPosition.lng = this.lerp(newCameraPosition.lng, this.previousCameraPosition.lng, this.SMOOTH_FACTOR);
+ newCameraPosition.lat = this.lerp(newCameraPosition.lat, this.previousCameraPosition.lat, this.SMOOTH_FACTOR);
+ }
}
-
- this.previousCameraPosition = newCameraPosition
-
+
+ this.previousCameraPosition = newCameraPosition;
+
return newCameraPosition;
- };
-
- public static createGeoJSONCircle = (center: number[], radiusInKm: number, points = 64): Feature<Geometry, GeoJsonProperties>=> {
+ };
+
+ public static createGeoJSONCircle = (center: number[], radiusInKm: number, points = 64): Feature<Geometry, GeoJsonProperties> => {
const coords = {
- latitude: center[1],
- longitude: center[0],
+ latitude: center[1],
+ longitude: center[0],
};
const km = radiusInKm;
const ret = [];
- const distanceX = km / (111.320 * Math.cos((coords.latitude * Math.PI) / 180));
+ const distanceX = km / (111.32 * Math.cos((coords.latitude * Math.PI) / 180));
const distanceY = km / 110.574;
let theta;
let x;
let y;
for (let i = 0; i < points; i += 1) {
- theta = (i / points) * (2 * Math.PI);
- x = distanceX * Math.cos(theta);
- y = distanceY * Math.sin(theta);
- ret.push([coords.longitude + x, coords.latitude + y]);
+ theta = (i / points) * (2 * Math.PI);
+ x = distanceX * Math.cos(theta);
+ y = distanceY * Math.sin(theta);
+ ret.push([coords.longitude + x, coords.latitude + y]);
}
ret.push(ret[0]);
return {
- type: 'Feature',
- geometry: {
- type: 'Polygon',
- coordinates: [ret],
- },
- properties: {}
+ type: 'Feature',
+ geometry: {
+ type: 'Polygon',
+ coordinates: [ret],
+ },
+ properties: {},
};
- }
+ };
- private calculateBearing(
- from: { lng: number; lat: number },
- to: { lng: number; lat: number }
- ): number {
+ private calculateBearing(from: { lng: number; lat: number }, to: { lng: number; lat: number }): number {
const lon1 = from.lng;
const lat1 = from.lat;
const lon2 = to.lng;
const lat2 = to.lat;
-
+
const lon1Rad = (lon1 * Math.PI) / 180;
const lon2Rad = (lon2 * Math.PI) / 180;
const lat1Rad = (lat1 * Math.PI) / 180;
const lat2Rad = (lat2 * Math.PI) / 180;
-
+
const y = Math.sin(lon2Rad - lon1Rad) * Math.cos(lat2Rad);
- const x =
- Math.cos(lat1Rad) * Math.sin(lat2Rad) -
- Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(lon2Rad - lon1Rad);
-
- let bearing = Math.atan2(y,x);
-
+ const x = Math.cos(lat1Rad) * Math.sin(lat2Rad) - Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(lon2Rad - lon1Rad);
+
+ let bearing = Math.atan2(y, x);
+
// Convert bearing from radians to degrees
bearing = (bearing * 180) / Math.PI;
-
+
// Ensure the bearing is within [0, 360)
if (bearing < 0) {
- bearing += 360;
+ bearing += 360;
}
-
- return bearing;
- }
-
-} \ No newline at end of file
+ return bearing;
+ }
+}
diff --git a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
index bf4028f01..f9607becf 100644
--- a/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
+++ b/src/client/views/nodes/MapBox/DirectionsAnchorMenu.tsx
@@ -1,15 +1,15 @@
-import React = require('react');
-import { observer } from "mobx-react";
-import { AntimodeMenu, AntimodeMenuProps } from "../../AntimodeMenu";
-import { IReactionDisposer, ObservableMap, reaction } from "mobx";
-import { Doc, Opt } from "../../../../fields/Doc";
-import { returnFalse, unimplementedFunction } from "../../../../Utils";
-import { NumCast, StrCast } from "../../../../fields/Types";
-import { SelectionManager } from "../../../util/SelectionManager";
-import { IconButton } from "browndash-components";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
-import { SettingsManager } from "../../../util/SettingsManager";
-import { IconLookup, faAdd, faCalendarDays, faRoute } from "@fortawesome/free-solid-svg-icons";
+import * as React from 'react';
+import { observer } from 'mobx-react';
+import { AntimodeMenu, AntimodeMenuProps } from '../../AntimodeMenu';
+import { IReactionDisposer, ObservableMap, reaction } from 'mobx';
+import { Doc, Opt } from '../../../../fields/Doc';
+import { returnFalse, unimplementedFunction } from '../../../../Utils';
+import { NumCast, StrCast } from '../../../../fields/Types';
+import { SelectionManager } from '../../../util/SelectionManager';
+import { IconButton } from 'browndash-components';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { SettingsManager } from '../../../util/SettingsManager';
+import { IconLookup, faAdd, faCalendarDays, faRoute } from '@fortawesome/free-solid-svg-icons';
@observer
export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
@@ -32,9 +32,9 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
private title: string | undefined = undefined;
- public setPinDoc(pinDoc: Doc){
- this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`) ;
- console.log("Title: ", this.title)
+ public setPinDoc(pinDoc: Doc) {
+ this.title = StrCast(pinDoc.title ? pinDoc.title : `${NumCast(pinDoc.longitude)}, ${NumCast(pinDoc.latitude)}`);
+ console.log('Title: ', this.title);
}
public get Active() {
@@ -54,7 +54,7 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
componentDidMount() {
this._disposer = reaction(
- () => SelectionManager.Views().slice(),
+ () => SelectionManager.Views.slice(),
sel => DirectionsAnchorMenu.Instance.fadeOut(true)
);
}
@@ -95,43 +95,28 @@ export class DirectionsAnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
render() {
const buttons = (
- <div className='directions-menu-buttons' style={{display: 'flex'}}>
+ <div className="directions-menu-buttons" style={{ display: 'flex' }}>
<IconButton
tooltip="Add route" //
onPointerDown={this.Delete}
icon={<FontAwesomeIcon icon={faAdd as IconLookup} />}
color={SettingsManager.userColor}
/>
-
-
- <IconButton
- tooltip='Animate route'
- onPointerDown={this.Delete} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faRoute as IconLookup}/>}
- color={SettingsManager.userColor}
- />
- <IconButton
- tooltip='Add to calendar'
- onPointerDown={this.Delete} /**TODO: fix */
- icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup}/>}
- color={SettingsManager.userColor}
- />
+
+ <IconButton tooltip="Animate route" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faRoute as IconLookup} />} color={SettingsManager.userColor} />
+ <IconButton tooltip="Add to calendar" onPointerDown={this.Delete} /**TODO: fix */ icon={<FontAwesomeIcon icon={faCalendarDays as IconLookup} />} color={SettingsManager.userColor} />
</div>
- )
+ );
return this.getElement(
- <div ref={DirectionsAnchorMenu.top} style={{ height: 'max-content' , width: '100%', display: 'flex', flexDirection: 'column' }}>
+ <div ref={DirectionsAnchorMenu.top} style={{ height: 'max-content', width: '100%', display: 'flex', flexDirection: 'column' }}>
<div>{this.title}</div>
- <div className='direction-inputs' style={{display: 'flex', flexDirection: 'column'}}>
- <input
- placeholder="Origin"
- />
- <input
- placeholder="Destination"
- />
+ <div className="direction-inputs" style={{ display: 'flex', flexDirection: 'column' }}>
+ <input placeholder="Origin" />
+ <input placeholder="Destination" />
</div>
{buttons}
</div>
- )
+ );
}
-} \ No newline at end of file
+}
diff --git a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
index a6182991d..6a14427c0 100644
--- a/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
+++ b/src/client/views/nodes/MapboxMapBox/MapboxContainer.tsx
@@ -43,7 +43,7 @@ import { MapProvider, Map as MapboxMap } from 'react-map-gl';
* A map marker is considered a document that contains a collection with stacking view of documents, it has a lat, lng location, which is passed to Maps API's custom marker (red pin) to be rendered on the google maps
*/
-const mapboxApiKey = "pk.eyJ1IjoiemF1bHRhdmFuZ2FyIiwiYSI6ImNsbnc2eHJpbTA1ZTUyam85aGx4Z2FhbGwifQ.2Kqw9mk-9wAAg9kmHmKzcg";
+const mapboxApiKey = 'pk.eyJ1IjoiemF1bHRhdmFuZ2FyIiwiYSI6ImNsbnc2eHJpbTA1ZTUyam85aGx4Z2FhbGwifQ.2Kqw9mk-9wAAg9kmHmKzcg';
const bingApiKey = process.env.BING_MAPS; // if you're running local, get a Bing Maps api key here: https://www.bingmapsportal.com/ and then add it to the .env file in the Dash-Web root directory as: _CLIENT_BING_MAPS=<your apikey>
/**
@@ -198,7 +198,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
title="Toggle Sidebar"
style={{
display: !this.props.isContentActive() ? 'none' : undefined,
- top: StrCast(this.rootDoc._layout_showTitle) === 'title' ? 20 : 5,
+ top: StrCast(this.Document._layout_showTitle) === 'title' ? 20 : 5,
backgroundColor: this.SidebarShown ? Colors.MEDIUM_BLUE : Colors.BLACK,
}}
onPointerDown={this.sidebarBtnDown}>
@@ -230,8 +230,8 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
});
const targetCreator = (annotationOn: Doc | undefined) => {
- const target = DocUtils.GetNewTextDoc('Note linked to ' + this.rootDoc.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
- FormattedTextBox.SelectOnLoad = target[Id];
+ const target = DocUtils.GetNewTextDoc('Note linked to ' + this.Document.title, 0, 0, 100, 100, undefined, annotationOn, undefined, 'yellow');
+ FormattedTextBox.SetSelectOnLoad(target);
return target;
};
const docView = this.props.DocumentView?.();
@@ -324,7 +324,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
};
@observable
- bingSearchBarContents: any = this.rootDoc.map; // For Bing Maps: The contents of the Bing search bar (string)
+ bingSearchBarContents: any = this.Document.map; // For Bing Maps: The contents of the Bing search bar (string)
geoDataRequestOptions = {
entityType: 'PopulatedPlace',
@@ -357,9 +357,9 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
deselectPin = () => {
if (this.selectedPin) {
// Removes filter
- Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
- Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
- Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
+ Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'remove');
+ Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'remove');
+ Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
const temp = this.selectedPin;
if (!this._unmounting) {
@@ -372,7 +372,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
}
this.map_docToPinMap.set(temp, newpin);
this.selectedPin = undefined;
- this.bingSearchBarContents = this.rootDoc.map;
+ this.bingSearchBarContents = this.Document.map;
}
};
@@ -389,9 +389,9 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
this.selectedPin = pinDoc;
this.bingSearchBarContents = pinDoc.map;
- // Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'match');
- // Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'match');
- Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
+ // Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'match');
+ // Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'match');
+ Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(this.selectedPin)}`, 'check');
this.recolorPin(this.selectedPin, 'green');
@@ -460,8 +460,8 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
getAnchor = (addAsAnnotation: boolean, pinProps?: PinProps, existingPin?: Doc) => {
/// this should use SELECTED pushpin for lat/long if there is a selection, otherwise CENTER
const anchor = Docs.Create.ConfigDocument({
- title: 'MapAnchor:' + this.rootDoc.title,
- text: StrCast(this.selectedPin?.map) || StrCast(this.rootDoc.map) || 'map location',
+ title: 'MapAnchor:' + this.Document.title,
+ text: StrCast(this.selectedPin?.map) || StrCast(this.Document.map) || 'map location',
config_latitude: NumCast((existingPin ?? this.selectedPin)?.latitude ?? this.dataDoc.latitude),
config_longitude: NumCast((existingPin ?? this.selectedPin)?.longitude ?? this.dataDoc.longitude),
config_map_zoom: NumCast(this.dataDoc.map_zoom),
@@ -469,15 +469,15 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
config_map: StrCast((existingPin ?? this.selectedPin)?.map) || StrCast(this.dataDoc.map),
layout_unrendered: true,
mapPin: existingPin ?? this.selectedPin,
- annotationOn: this.rootDoc,
+ annotationOn: this.Document,
});
if (anchor) {
if (!addAsAnnotation) anchor.backgroundColor = 'transparent';
addAsAnnotation && this.addDocument(anchor);
- PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.rootDoc);
+ PresBox.pinDocView(anchor, { pinDocLayout: pinProps?.pinDocLayout, pinData: { ...(pinProps?.pinData ?? {}), map: true } }, this.Document);
return anchor;
}
- return this.rootDoc;
+ return this.Document;
};
map_docToPinMap = new Map<Doc, any>();
@@ -524,9 +524,9 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
deleteSelectedPin = undoable(() => {
if (this.selectedPin) {
// Removes filter
- Doc.setDocFilter(this.rootDoc, 'latitude', this.selectedPin.latitude, 'remove');
- Doc.setDocFilter(this.rootDoc, 'longitude', this.selectedPin.longitude, 'remove');
- Doc.setDocFilter(this.rootDoc, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
+ Doc.setDocFilter(this.Document, 'latitude', this.selectedPin.latitude, 'remove');
+ Doc.setDocFilter(this.Document, 'longitude', this.selectedPin.longitude, 'remove');
+ Doc.setDocFilter(this.Document, LinkedTo, `mapPin=${Field.toScriptString(DocCast(this.selectedPin))}`, 'remove');
this.removePushpin(this.selectedPin);
}
@@ -614,7 +614,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
this.MicrosoftMaps.Events.addHandler(this._bingMap.current, 'maptypechanged', undoable(this.updateMapType, 'Map ViewType Change'));
this._disposers.mapLocation = reaction(
- () => this.rootDoc.map,
+ () => this.Document.map,
mapLoc => (this.bingSearchBarContents = mapLoc),
{ fireImmediately: true }
);
@@ -639,7 +639,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
);
this._disposers.location = reaction(
- () => ({ lat: this.rootDoc.latitude, lng: this.rootDoc.longitude, zoom: this.rootDoc.map_zoom, mapType: this.rootDoc.map_type }),
+ () => ({ lat: this.Document.latitude, lng: this.Document.longitude, zoom: this.Document.map_zoom, mapType: this.Document.map_type }),
locationObject => {
// if (this._bingMap.current)
try {
@@ -690,7 +690,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
}
},
e => {
- const createPin = () => this.createPushpin(this.rootDoc.latitude, this.rootDoc.longitude, this.rootDoc.map);
+ const createPin = () => this.createPushpin(this.Document.latitude, this.Document.longitude, this.Document.map);
if (this.bingSearchBarContents) {
this.bingSearch().then(createPin);
} else createPin();
@@ -706,7 +706,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
render() {
// bcz: no idea what's going on here, but bings maps have some kind of bug
// such that we need to delay rendering a second map on startup until the first map is rendered.
- this.rootDoc[DocCss];
+ this.Document[DocCss];
if (MapBoxContainer._rerenderDelay) {
// prettier-ignore
this._rerenderTimeout = this._rerenderTimeout ??
@@ -715,7 +715,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
MapBoxContainer._rerenderDelay = 0;
}
this._rerenderTimeout = undefined;
- this.rootDoc[DocCss] = this.rootDoc[DocCss] + 1;
+ this.Document[DocCss] = this.Document[DocCss] + 1;
}), MapBoxContainer._rerenderDelay);
return null;
}
@@ -732,7 +732,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
style={{ width: `calc(100% - ${this.sidebarWidthPercent})`, pointerEvents: this.pointerEvents() }}>
<div style={{ mixBlendMode: 'multiply' }}>{renderAnnotations(this.transparentFilter)}</div>
{renderAnnotations(this.opaqueFilter)}
- {SnappingManager.GetIsDragging() ? null : renderAnnotations()}
+ {SnappingManager.IsDragging ? null : renderAnnotations()}
<div className="mapBox-searchbar">
<EditableText
@@ -758,15 +758,10 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
</div>
</div>
<MapProvider>
- <MapboxMap
- id="mabox-map"
- mapStyle={`mapbox://styles/mapbox/streets-v9`}
- mapboxAccessToken={mapboxApiKey}
- />
+ <MapboxMap id="mabox-map" mapStyle={`mapbox://styles/mapbox/streets-v9`} mapboxAccessToken={mapboxApiKey} />
</MapProvider>
-
-
-{/*
+
+ {/*
<BingMapsReact
onMapReady={this.bingMapReady} //
bingMapsKey={bingApiKey}
@@ -786,7 +781,6 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
{...this.props}
renderDepth={this.props.renderDepth + 1}
Document={pushpin}
- DataDoc={undefined}
PanelWidth={returnOne}
PanelHeight={returnOne}
NativeWidth={returnOne}
@@ -824,7 +818,7 @@ export class MapBoxContainer extends ViewBoxAnnotatableComponent<ViewBoxAnnotata
ref={this._sidebarRef}
{...this.props}
fieldKey={this.fieldKey}
- rootDoc={this.rootDoc}
+ Document={this.Document}
layoutDoc={this.layoutDoc}
dataDoc={this.dataDoc}
usePanelWidth={true}