/
HeatMap.jsx
125 lines (115 loc) · 3.29 KB
/
HeatMap.jsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import d3 from 'react-d3';
import CalHeatMap from 'cal-heatmap';
import { createSelector } from 'reselect';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import differenceInCalendarMonths from 'date-fns/difference_in_calendar_months';
import { FullWidthRow } from '../../../helperComponents';
import { userByNameSelector } from '../../../redux';
function ensureD3() {
// CalHeatMap requires d3 to be available on window
if (typeof window !== 'undefined') {
if ('d3' in window) {
return;
} else {
window.d3 = d3;
}
return;
}
return;
}
const mapStateToProps = createSelector(
userByNameSelector,
({ calendar, streak }) => ({ calendar, streak })
);
const propTypes = {
calendar: PropTypes.object,
streak: PropTypes.shape({
current: PropTypes.number,
longest: PropTypes.number
})
};
class HeatMap extends Component {
constructor(props) {
super(props);
this.renderMap = this.renderMap.bind(this);
}
componentDidMount() {
ensureD3();
this.renderMap();
}
renderMap() {
const { calendar = {} } = this.props;
if (Object.keys(calendar).length === 0) {
return null;
}
const today = new Date();
const cal = new CalHeatMap();
const rectSelector = '#cal-heatmap > svg > svg.graph-legend > g > rect.r';
const calLegendTitles = ['0 items', '1 item', '2 items', '3 or more items'];
const firstTS = Object.keys(calendar)[0];
const sevenMonths = 1000 * 60 * 60 * 24 * 210;
// start should not be earlier than 7 months before now:
let start = (firstTS * 1000 + sevenMonths < Date.now())
? new Date(Date.now() - sevenMonths)
: new Date(firstTS * 1000);
const monthsSinceFirstActive = differenceInCalendarMonths(
today,
start
);
cal.init({
itemSelector: '#cal-heatmap',
domain: 'month',
subDomain: 'day',
domainDynamicDimension: true,
domainGutter: 5,
data: calendar,
cellSize: 15,
cellRadius: 3,
cellPadding: 2,
tooltip: true,
range: monthsSinceFirstActive < 12 ? monthsSinceFirstActive + 1 : 12,
start,
legendColors: ['#cccccc', '#006400'],
legend: [1, 2, 3],
label: {
position: 'top'
}
});
calLegendTitles.forEach(function(title, i) {
document
.querySelector(rectSelector + (i + 1).toString() + '> title')
.innerHTML = title;
});
return null;
}
render() {
const { streak = {} } = this.props;
return (
<div id='cal-heatmap-container'>
<Helmet>
<link href='/css/cal-heatmap.css' rel='stylesheet' />
</Helmet>
<FullWidthRow>
<div id='cal-heatmap' />
</FullWidthRow>
<FullWidthRow>
<div className='streak-container'>
<span className='streak'>
<strong>Longest Streak:</strong> { streak.longest || 1 }
</span>
<span className='streak'>
<strong>Current Streak:</strong> { streak.current || 1 }
</span>
</div>
</FullWidthRow>
<hr />
</div>
);
}
}
HeatMap.displayName = 'HeatMap';
HeatMap.propTypes = propTypes;
export default connect(mapStateToProps)(HeatMap);