Skip to content

Commit 8b09557

Browse files
authored
Merge pull request #235 from HDI-Project/timeIndicators
Added tooltip time indicators
2 parents adb7b0c + 097df5d commit 8b09557

File tree

6 files changed

+170
-3
lines changed

6 files changed

+170
-3
lines changed

mtv-client/src/components/Timeseries/FocusChart/FocusChart.scss

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,31 @@
100100
}
101101
}
102102

103+
.tooltip-data {
104+
width: 170px;
105+
font-size: 12px;
106+
background: rgba(103, 106, 130, 0.7);
107+
padding: 10px;
108+
border-radius: 4px;
109+
position: fixed;
110+
z-index: 999;
111+
112+
text{
113+
color: #fff;
114+
}
115+
116+
&.active{
117+
display: block;
118+
}
119+
120+
li{
121+
display: flex;
122+
justify-content: space-between;
123+
span{
124+
flex-flow: column;
125+
}
126+
}
127+
}
128+
103129

104130
}

mtv-client/src/components/Timeseries/FocusChart/FocusChart.tsx

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
import { getWrapperSize, getScale } from './FocusChartUtils';
3333
import ShowErrors from './ShowErrors';
3434
import './FocusChart.scss';
35-
import { fromMonthToIndex, maxDaysInMonth } from '../../../model/utils/Utils';
35+
import { fromMonthToIndex, maxDaysInMonth, formatDate } from '../../../model/utils/Utils';
3636

3737
import { RootState } from '../../../model/types';
3838

@@ -49,6 +49,8 @@ type State = {
4949
zoomValue?: any;
5050
brushInstance?: any;
5151
brushContext?: any;
52+
isTooltipVisible?: boolean;
53+
tooltipData?: any;
5254
};
5355

5456
class FocusChart extends Component<Props, State> {
@@ -60,6 +62,8 @@ class FocusChart extends Component<Props, State> {
6062
this.state = {
6163
width: 0,
6264
height: 0,
65+
isTooltipVisible: false,
66+
tooltipData: {},
6367
};
6468

6569
this.zoomHandler = this.zoomHandler.bind(this);
@@ -286,6 +290,25 @@ class FocusChart extends Component<Props, State> {
286290
.attr('width', Math.max(xCoord(timeSeries[stopIndex][0]) - xCoord(timeSeries[startIndex][0])))
287291
.attr('y', 0)
288292
.attr('x', xCoord(timeSeries[startIndex][0]))
293+
.on('mouseover, mousemove', () => {
294+
const startDate = new Date(timeSeries[currentEvent[0]][0]);
295+
const stopDate = new Date(timeSeries[currentEvent[1]][0]);
296+
const tooltipOffset = 20;
297+
this.setState({
298+
isTooltipVisible: true,
299+
tooltipData: {
300+
xCoord: d3.event.x + tooltipOffset,
301+
yCoord: d3.event.y,
302+
eventStartTime: startDate,
303+
eventEndTime: stopDate,
304+
},
305+
});
306+
})
307+
.on('mouseout', () => {
308+
this.setState({
309+
isTooltipVisible: false,
310+
});
311+
})
289312
.on('click', () => {
290313
setCurrentEvent(currentEventID);
291314
});
@@ -617,7 +640,6 @@ class FocusChart extends Component<Props, State> {
617640
.attr('x', xCoord(currentEventDetails.start_time))
618641
.attr('width', Math.max(xCoord(stop_time) - xCoord(start_time)));
619642
d3.select(`g#_${currentEventDetails.id} path.evt-highlight`).attr('d', this.drawLine(lineData));
620-
621643
this.updateLineChart();
622644
}
623645

@@ -727,11 +749,40 @@ class FocusChart extends Component<Props, State> {
727749
}
728750
}
729751

752+
showEventTooltip() {
753+
const { tooltipData } = this.state;
754+
const startDate = formatDate(tooltipData.eventStartTime);
755+
const endDate = formatDate(tooltipData.eventEndTime);
756+
757+
return (
758+
<div className="tooltip-data" style={{ left: `${tooltipData.xCoord}px`, top: `${tooltipData.yCoord}px` }}>
759+
<ul>
760+
<li>
761+
starts:
762+
<span>
763+
{startDate.day}/{startDate.month}/{startDate.year}
764+
</span>
765+
<span>{startDate.time}</span>
766+
</li>
767+
<li>
768+
ends:
769+
<span>
770+
{endDate.day}/{endDate.month}/{endDate.year}
771+
</span>
772+
<span>{endDate.time}</span>
773+
</li>
774+
</ul>
775+
</div>
776+
);
777+
}
778+
730779
render() {
780+
const { isTooltipVisible } = this.state;
731781
return (
732782
<div className="focus-chart" id="focusChartWrapper">
733783
<ShowErrors isOpen={this.props.isPredictionVisible} />
734784
<EventDetails />
785+
{isTooltipVisible && this.showEventTooltip()}
735786
<svg id="focusChart" />
736787
<div className="zoomControlsHolder">
737788
<ZoomControls />

mtv-client/src/components/Timeseries/Overview/DrawChart.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { Component } from 'react';
2-
import { drawChart, updateBrushPeriod, updateHighlithedEvents } from './chartUtils';
2+
import { drawChart, updateBrushPeriod, updateHighlithedEvents, brushTooltip } from './chartUtils';
33
import { Props as DatarunProps } from './Datarun';
44

55
let props: DatarunProps;
@@ -25,6 +25,7 @@ class DrawChart extends Component<Props> {
2525
// @TODO - investigate how this can be set in immerJS as a compound object
2626
if (JSON.stringify(prevProps.selectedPeriod) !== JSON.stringify(selectedPeriod)) {
2727
updateBrushPeriod(selectedPeriod);
28+
brushTooltip(selectedPeriod, this.props.dataRun);
2829
}
2930
if (prevProps.dataRun.eventWindows !== this.props.dataRun.eventWindows) {
3031
updateHighlithedEvents(this.props.dataRun);

mtv-client/src/components/Timeseries/Overview/Overview.scss

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,3 +91,20 @@
9191
display: none;
9292
}
9393
}
94+
95+
.brush-tooltip{
96+
position: fixed;
97+
width: 170px;
98+
font-size: 12px;
99+
background: rgba(103, 106, 130, 0.7);
100+
padding: 10px;
101+
border-radius: 4px;
102+
z-index: 999;
103+
li{
104+
display: flex;
105+
justify-content: space-between;
106+
span{
107+
flex-flow: column;
108+
}
109+
}
110+
}

mtv-client/src/components/Timeseries/Overview/chartUtils.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import * as d3 from 'd3';
22
import { FocusChartConstants } from '../FocusChart/Constants';
3+
import { formatDate } from '../../../model/utils/Utils';
34

45
const { TRANSLATE_LEFT, CHART_MARGIN } = FocusChartConstants;
56

@@ -47,6 +48,60 @@ const setRatio = width => {
4748
ratio = chartWidth / focusChartWidth;
4849
};
4950

51+
const toggleBrushTooltip = (selectedRange = null, dataRun) => {
52+
const tootltipContext = d3.select('body');
53+
const { eventRange } = selectedRange;
54+
55+
if (selectedRange === null) {
56+
return;
57+
}
58+
59+
const { clientX, clientY } = d3.event;
60+
const { xCoord } = getScale(chartWidth, 36, dataRun);
61+
62+
const startDate = formatDate(new Date(xCoord.invert(eventRange[0]).getTime()));
63+
const endDate = formatDate(new Date(xCoord.invert(eventRange[1]).getTime()));
64+
const tooltipHTML = `<ul>
65+
<li><span>starts:</span> <span>${startDate.day}/${startDate.month}/${startDate.year}</span> <span>${startDate.time}</span> </li>
66+
<li><span>ends:</span> <span>${endDate.day}/${endDate.month}/${endDate.year}</span> <span>${endDate.time}</span></li>
67+
</ul>`;
68+
69+
const tooltipDiv = tootltipContext
70+
.append('div')
71+
.attr('class', 'brush-tooltip')
72+
.attr('style', `left: ${clientX + 10}px; top: ${clientY + 10}px`);
73+
74+
tooltipDiv.html(tooltipHTML);
75+
};
76+
77+
const removeTooltip = () => {
78+
d3.selectAll('.brush-tooltip').remove();
79+
};
80+
81+
const updateTooltipCoords = () => {
82+
const { clientX, clientY } = d3.event;
83+
const tooltipDiv = d3.select('.brush-tooltip');
84+
tooltipDiv.attr('style', `left: ${clientX + 10}px; top: ${clientY + 10}px`);
85+
};
86+
87+
export const brushTooltip = (selectedRange, dataRun) => {
88+
const brushes = d3.selectAll('.brush .selection');
89+
90+
// eslint-disable-next-line no-underscore-dangle
91+
brushes._groups[0].forEach(currentBrush => {
92+
d3.select(currentBrush)
93+
.on('mouseover', function() {
94+
toggleBrushTooltip(selectedRange, dataRun);
95+
})
96+
.on('mousemove', function() {
97+
updateTooltipCoords();
98+
})
99+
.on('mouseout', function() {
100+
removeTooltip();
101+
});
102+
});
103+
};
104+
50105
export function drawBrush(element, width, onPeriodTimeChange, onSelectDatarun, dataRun) {
51106
const brushHeight = 43;
52107

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
11
export const months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
22
export const fromMonthToIndex = month => months.indexOf(month) + 1;
33
export const maxDaysInMonth = (currentYear, currentMonth) => new Date(currentYear, currentMonth, 0).getDate();
4+
5+
export function formatDate(date) {
6+
let hours = date.getHours();
7+
let minutes = date.getMinutes();
8+
hours %= 12;
9+
hours = hours || 12;
10+
minutes = minutes < 10 ? `0${minutes}` : minutes;
11+
const ampm = hours >= 12 ? 'pm' : 'am';
12+
let strTime = `${hours}:${minutes}`;
13+
const formattedTime = {
14+
year: date.getFullYear(),
15+
month: date.getMonth() + 1,
16+
day: date.getDate(),
17+
time: `${strTime}:${ampm}`,
18+
};
19+
return formattedTime;
20+
}

0 commit comments

Comments
 (0)