Skip to content

Commit

Permalink
added area and scatter chart styles
Browse files Browse the repository at this point in the history
  • Loading branch information
thefinaljob committed Jun 21, 2021
1 parent 7a6667c commit 5422129
Show file tree
Hide file tree
Showing 4 changed files with 240 additions and 21 deletions.
1 change: 1 addition & 0 deletions packages/react-components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"chart.js": "^2.8.0",
"lodash": "^4.17.15",
"react-chartjs-2": "^2.7.6",
"react-dropdown": "^1.9.2",
"react-grid-layout": "^1.1.0",
"react-modal": "^3.8.1",
"react-tooltip": "4.2.14",
Expand Down
244 changes: 223 additions & 21 deletions packages/react-components/src/components/xy-output-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AbstractOutputProps, AbstractOutputState } from './abstract-output-comp
import { AbstractTreeOutputComponent } from './abstract-tree-output-component';
import * as React from 'react';
import { Line } from 'react-chartjs-2';
import {Scatter} from 'react-chartjs-2';
import { QueryHelper } from 'tsp-typescript-client/lib/models/query/query-helper';
import { Entry } from 'tsp-typescript-client/lib/models/entry';
import { ResponseStatus } from 'tsp-typescript-client/lib/models/response/responses';
Expand All @@ -12,23 +13,35 @@ import { EntryTree } from './utils/filtrer-tree/entry-tree';
import { getAllExpandedNodeIds } from './utils/filtrer-tree/utils';
import { TreeNode } from './utils/filtrer-tree/tree-node';
import ColumnHeader from './utils/filtrer-tree/column-header';
import { ChangeEvent } from 'react';

type XYOuputState = AbstractOutputState & {
selectedSeriesId: number[];
xyTree: Entry[];
checkedSeries: number[];
collapsedNodes: number[];
orderedNodes: number[];
chartStyle: any;
// FIXME Type this properly
xyData: any;
columns: ColumnHeader[];
};

class xyPair {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}

export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutputProps, XYOuputState> {
private currentColorIndex = 0;
private colorMap: Map<string, number> = new Map();

private lineChartRef: any;
protected desiredChartStyle: any;
private chartRef: any;
private mouseIsDown = false;
private posPixelSelect = 0;
private plugin = {
Expand All @@ -40,7 +53,7 @@ export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutpu
const scale = this.props.viewRange.getEnd() - this.props.viewRange.getstart();
this.props.unitController.selectionRange = {
start: xStartPos,
end: xStartPos + ((event.screenX - this.posPixelSelect) / this.lineChartRef.current.chartInstance.width) * scale
end: xStartPos + ((event.screenX - this.posPixelSelect) / this.chartRef.current.chartInstance.width) * scale
};
}
};
Expand All @@ -54,6 +67,7 @@ export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutpu
constructor(props: AbstractOutputProps) {
super(props);
this.state = {
chartStyle: 'scatter',
outputStatus: ResponseStatus.RUNNING,
selectedSeriesId: [],
xyTree: [],
Expand All @@ -65,7 +79,7 @@ export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutpu
};

this.afterChartDraw = this.afterChartDraw.bind(this);
this.lineChartRef = React.createRef();
this.chartRef = React.createRef();
}

componentDidMount(): void {
Expand Down Expand Up @@ -108,19 +122,41 @@ export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutpu
if (needToUpdate || prevState.outputStatus === ResponseStatus.RUNNING) {
this.updateXY();
}
if (this.lineChartRef.current) {
this.lineChartRef.current.chartInstance.render();
if (this.chartRef.current) {
this.chartRef.current.chartInstance.render();
}
}

synchronizeTreeScroll(): void { /* Nothing to do by default */ }

changeChartStyle(e: ChangeEvent<HTMLSelectElement>): void {
const value = e.target.value.toString();
this.desiredChartStyle = value;
this.updateXY()
.then(() => {
this.setState({chartStyle:value});
});
}

renderTree(): React.ReactNode | undefined {
this.onToggleCheck = this.onToggleCheck.bind(this);
this.onToggleCollapse = this.onToggleCollapse.bind(this);
this.onOrderChange = this.onOrderChange.bind(this);
return this.state.xyTree.length
? <EntryTree
? <>
<select
className="theia-select"
value={this.state.chartStyle}
onChange={
e => this.changeChartStyle(e)
}
id="xy-dropdown"
>
<option value="line">Line</option>
<option value="scatter">Scatter</option>
<option value="area">Stacked Area</option>
</select>
<EntryTree
entries={this.state.xyTree}
showCheckboxes={true}
collapsedNodes={this.state.collapsedNodes}
Expand All @@ -130,16 +166,20 @@ export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutpu
onOrderChange={this.onOrderChange}
headers={this.state.columns}
/>
</>
: undefined
;
}

renderChart(): React.ReactNode {
chooseChart(): React.ReactNode {

const lineOptions: Chart.ChartOptions = {
responsive: true,
elements: {
point: { radius: 0 },
line: { tension: 0 }
line: { tension: 0,
borderWidth: 2
}
},
maintainAspectRatio: false,
legend: { display: false },
Expand All @@ -153,22 +193,116 @@ export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutpu
},
scales: {
xAxes: [{ id: 'time-axis', display: false }],
yAxes: [{ display: false }]
yAxes: [{
display: false,
stacked: false
}]
},
animation: { duration: 0 },
events: [ 'mousedown' ],
};

const areaOptions: Chart.ChartOptions = {
responsive: true,
elements: {
point: { radius: 0 },
line: { tension: 0,
borderWidth: 0
}
},
maintainAspectRatio: false,
legend: { display: false },
layout: {
padding: {
left: 0,
right: 0,
top: 15,
bottom: 5
}
},
scales: {
xAxes: [{
id: 'time-axis',
display: false
}],
yAxes: [{
stacked: true,
display: false
}]
},
animation: { duration: 0 },
events: [ 'mousedown' ],
};

const scatterOptions: Chart.ChartOptions = {
responsive: true,
maintainAspectRatio: false,
legend: { display: false },
layout: {
padding: {
left: 0,
right: 0,
top: 15,
bottom: 5
}
},
scales: {
xAxes: [{
id: 'time-axis',
display: false,
ticks: {
min: this.props.viewRange?.getstart(),
max: this.props.viewRange?.getEnd()
}
}],
yAxes: [
{
display: false,
},
],
},
animation: { duration: 0 },
events: [ 'mousedown' ]
};

switch (this.state.chartStyle) {
case 'scatter':
return <Scatter
data={this.state.xyData}
height={parseInt(this.props.style.height.toString())}
options={scatterOptions}
ref={this.chartRef}
plugins={[this.plugin]}
>
</Scatter>;
case 'area':
return <Line
data={this.state.xyData}
height={parseInt(this.props.style.height.toString())}
options={areaOptions}
ref={this.chartRef}
plugins={[this.plugin]}
>
</Line>;
case 'line':
return <Line
data={this.state.xyData}
height={parseInt(this.props.style.height.toString())}
options={lineOptions}
ref={this.chartRef}
plugins={[this.plugin]}
>
</Line>;
}

}

renderChart(): React.ReactNode {
// width={this.props.style.chartWidth}
return <React.Fragment>
{this.state.outputStatus === ResponseStatus.COMPLETED ?
<div id='xy-main' onMouseDown={event => this.beginSelection(event)} style={{ height: this.props.style.height }} >
<Line
data={this.state.xyData}
height={parseInt(this.props.style.height.toString())}
options={lineOptions}
ref={this.lineChartRef}
plugins={[this.plugin]}>
</Line>
{this.chooseChart()}
</div> :
<div className='analysis-running'>
{(
Expand Down Expand Up @@ -279,7 +413,7 @@ export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutpu
const offset = this.props.viewRange.getOffset() ?? 0;
const scale = this.props.viewRange.getEnd() - this.props.viewRange.getstart();
const xPos = this.props.viewRange.getstart() - offset +
(event.nativeEvent.offsetX / this.lineChartRef.current.chartInstance.width) * scale;
(event.nativeEvent.offsetX / this.chartRef.current.chartInstance.width) * scale;
this.props.unitController.selectionRange = {
start: xPos,
end: xPos
Expand All @@ -288,7 +422,7 @@ export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutpu
document.addEventListener('mouseup', this.endSelection);
}

private async updateXY() {
private async updateXY(): Promise<void> {
let start = 1332170682440133097;
let end = 1332170682540133097;
const viewRange = this.props.viewRange;
Expand All @@ -303,11 +437,80 @@ export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutpu
const tspClientResponse = await this.props.tspClient.fetchXY(this.props.traceId, this.props.outputDescriptor.id, xyDataParameters);
const xyDataResponse = tspClientResponse.getModel();
if (tspClientResponse.isOk() && xyDataResponse) {
this.buildXYData(xyDataResponse.model.series);
switch (this.desiredChartStyle) {
case 'scatter':
this.buildScatterData(xyDataResponse.model.series);
break;
case 'area':
this.buildAreaData(xyDataResponse.model.series);
break;
case 'line':
this.buildLineData(xyDataResponse.model.series);
break;
default:
this.buildScatterData(xyDataResponse.model.series);
break;
}
}
}

private buildXYData(seriesObj: XYSeries[]) {
private buildScatterData(seriesObj: XYSeries[]) {
const dataSetArray = new Array<any>();
let xValues: number[] = [];
let yValues: number[] = [];
let pairs: xyPair[] = [];
seriesObj.forEach(series => {
const color = this.getSeriesColor(series.seriesName);
xValues = series.xValues;
yValues = series.yValues;

xValues.forEach((value, index) => {
pairs.push(new xyPair(value, yValues[index]));
});

dataSetArray.push({
label: series.seriesName,
data: pairs,
backgroundColor: color,
borderColor: color,
});
pairs = [];
});
const scatterData = {
labels: xValues,
datasets: dataSetArray
};

this.setState({
xyData: scatterData
});
}

private buildAreaData(seriesObj: XYSeries[]) {
const dataSetArray = new Array<any>();
let xValues: number[] = [];
seriesObj.forEach(series => {
const color = this.getSeriesColor(series.seriesName);
xValues = series.xValues;
dataSetArray.push({
label: series.seriesName,
fill: 'start',
backgroundColor: color,
data: series.yValues
});
});
dataSetArray.shift();
const areaData = {
labels: xValues,
datasets: dataSetArray
};

this.setState({
xyData: areaData
});
}

private buildLineData(seriesObj: XYSeries[]) {
const dataSetArray = new Array<any>();
let xValues: number[] = [];
seriesObj.forEach(series => {
Expand All @@ -317,7 +520,6 @@ export class XYOutputComponent extends AbstractTreeOutputComponent<AbstractOutpu
label: series.seriesName,
fill: false,
borderColor: color,
borderWidth: 2,
data: series.yValues
});
});
Expand Down
4 changes: 4 additions & 0 deletions packages/react-components/style/output-components-style.css
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@
display: flex;
}

#xy-dropdown {
margin-bottom: 5px;
}

#main {
border: 1px solid;
margin: 10px 0;
Expand Down
Loading

0 comments on commit 5422129

Please sign in to comment.