Skip to content
This repository was archived by the owner on Mar 4, 2025. It is now read-only.

Commit a8d5005

Browse files
author
vikasrohit
committed
Merge pull request #815 from smtryingcode/dev
Fixed issue #814 - [$100] Topcoder Member Profile: Redesign ratings graph
2 parents 98c7df5 + 00aea17 commit a8d5005

File tree

11 files changed

+315
-38
lines changed

11 files changed

+315
-38
lines changed

app/directives/distribution-graph/distribution-graph.directive.js

Lines changed: 82 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import angular from 'angular'
22
import d3 from 'd3'
3+
import React from 'react' // eslint-disable-line no-unused-vars
4+
import ReactDOM from 'react-dom'
5+
import Tooltip from 'appirio-tech-react-components/components/Tooltip/Tooltip.jsx'
36

47
(function() {
58
'use strict'
@@ -59,7 +62,7 @@ import d3 from 'd3'
5962
]
6063

6164
var measurements = {
62-
w: 600,
65+
w: 900,
6366
h: 400,
6467
padding: {
6568
top: 20,
@@ -176,7 +179,24 @@ import d3 from 'd3'
176179
.attr('fill', function(d) {
177180
return ratingToColor($scope.colors, d.start)
178181
})
179-
182+
183+
var mousemoveInterval = null
184+
185+
/* render react tooltip component */
186+
ReactDOM.unmountComponentAtNode(document.getElementById('chart-tooltip'))
187+
ReactDOM.render(<Tooltip popMethod='click'>
188+
<div className='tooltip-target'></div>
189+
<div className='tooltip-body'>
190+
<div className='tooltip-rating'></div>
191+
<div className='tooltip-challenge'>
192+
<div className='challenge-name'></div>
193+
<div className='challenge-date'></div>
194+
</div>
195+
</div>
196+
</Tooltip>
197+
, document.getElementById('chart-tooltip'))
198+
199+
$scope.isFocused = false
180200
svg.selectAll('rect.hover')
181201
.data(ranges)
182202
.enter()
@@ -187,24 +207,81 @@ import d3 from 'd3'
187207
return xScale(i)
188208
})
189209
.attr('y', function(d) {
190-
return padding.top
210+
return yScale(d.number)
191211
})
192212
.attr('width', xScale.rangeBand())
193213
.attr('height', function(d) {
194-
return totalH - padding.bottom - padding.top
214+
return totalH - padding.bottom - yScale(d.number)
195215
})
196216
.on('mouseover', function(d) {
217+
$scope.isFocused = true
197218
$scope.highlightedRating = d.start
198219
$scope.displayCoders = true
199220
$scope.numCoders = d.number
221+
222+
/* update tooltip location on mouseover, feature currently not inbuilt in react tooltip component */
223+
d3.select('#chart-tooltip')
224+
.style('left', (d3.event.pageX-4) + 'px')
225+
.style('top', (d3.event.pageY-4) + 'px')
226+
227+
$('#chart-tooltip').addClass('distribution')
228+
d3.select('#chart-tooltip .tooltip-container')
229+
.style('left', '20px !important')
230+
.style('top', '-20px !important')
231+
232+
d3.select('#chart-tooltip .tooltip-container .tooltip-pointer')
233+
.style('left', '-5.5px !important')
234+
.style('bottom', '25px !important')
235+
236+
d3.select('#chart-tooltip .challenge-name').text($scope.numCoders + ' Coders')
237+
d3.select('#chart-tooltip .challenge-date').text('Rating Range: '+ $scope.highlightedRating + '-'+($scope.highlightedRating+99))
238+
d3.select('#chart-tooltip .tooltip-rating').text($scope.numCoders)
239+
d3.select('#chart-tooltip .tooltip-rating').style('background', ratingToColor($scope.colors, $scope.highlightedRating))
200240
$scope.$digest()
201241
})
242+
.on('mousemove', function(d) {
243+
244+
/* update tooltip on mousemove, using interval of 50ms to improve performance */
245+
window.clearTimeout(mousemoveInterval)
246+
var left = (d3.event.pageX-4)
247+
var top = (d3.event.pageY-4)
248+
249+
mousemoveInterval = window.setTimeout(function(){
250+
d3.select('#chart-tooltip')
251+
.style('left', left + 'px')
252+
.style('top', top + 'px')
253+
254+
d3.select('#chart-tooltip .tooltip-container')
255+
.style('left', '20px !important')
256+
.style('top', '-20px !important')
257+
258+
d3.select('#chart-tooltip .tooltip-container .tooltip-pointer')
259+
.style('left', '-5.5px !important')
260+
.style('bottom', '25px !important')
261+
}, 50)
262+
263+
})
202264
.on('mouseout', function(d) {
203265
$scope.displayCoders = false
204266
$scope.highlightedRating = false
267+
$scope.isFocused = false
205268
$scope.$digest()
206269
})
207-
270+
271+
/* hide tooltip when clicked anywhere outside */
272+
d3.select('body').on('click', function(){
273+
if((d3.event.target.classList[0] != 'tooltip-target') && !$('#chart-tooltip .tooltip-container').hasClass('tooltip-hide') &&
274+
!isInArray(d3.event.target.classList[0], ['tooltip-content-container', 'tooltip-container', 'tooltip-body', 'Tooltip']) &&
275+
(d3.event.target.tagName.toLowerCase()!='circle') && !(d3.event.target.tagName.toLowerCase()=='rect' && d3.event.target.classList[0] == 'hover')) {
276+
$('#chart-tooltip .tooltip-target').trigger('click')
277+
$('#chart-tooltip').removeClass('distribution')
278+
}
279+
})
280+
281+
function isInArray(value, array) {
282+
return array.indexOf(value) > -1
283+
}
284+
208285
svg.selectAll('line.xaxis')
209286
.data(ranges)
210287
.enter()
Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,11 @@
11
.distribution-graph-directive(ng-show="graphState.show == 'distribution'")
22

3-
.graph-viewer
4-
.distribution-graph
3+
.graph-title
4+
.text Rating Distribution Graph
5+
.button-group
6+
button.tc-btn.tc-btn-s(ng-click="graphState.show = 'history'") View Rating History
7+
button.tc-btn.tc-btn-s.active(ng-click="graphState.show = 'distribution'") View Rating Distribution
58

6-
.info-port
7-
.coders(ng-if="displayCoders", style="background: {{highlightedRating || rating | ratingColor}}")
8-
.num {{numCoders}}
9-
.label CODERS
10-
.coders(ng-if="!displayCoders", style="background: {{rating | ratingColor}}")
11-
.num {{rating}}
12-
.label RATING
13-
button.tc-btn.tc-btn-s.compare(ng-click="graphState.show = 'history'")
14-
| View Rating History
9+
.graph-viewer
1510

11+
.distribution-graph

app/directives/history-graph/history-graph.directive.js

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import angular from 'angular'
22
import moment from 'moment'
33
import d3 from 'd3'
4+
import React from 'react' // eslint-disable-line no-unused-vars
5+
import ReactDOM from 'react-dom'
6+
import Tooltip from 'appirio-tech-react-components/components/Tooltip/Tooltip.jsx'
47

58
(function() {
69
'use strict'
@@ -16,11 +19,11 @@ import d3 from 'd3'
1619
rating: '=',
1720
graphState: '='
1821
},
19-
controller: ['$scope', HistoryGraphController]
22+
controller: ['$scope', '$state', '$filter', 'CONSTANTS', HistoryGraphController]
2023
}
2124
}
2225

23-
function HistoryGraphController($scope) {
26+
function HistoryGraphController($scope, $state, $filter, CONSTANTS) {
2427
$scope.colors = [
2528
// grey
2629
{
@@ -59,7 +62,7 @@ import d3 from 'd3'
5962
}
6063
]
6164
var measurements = {
62-
w: 600,
65+
w: 900,
6366
h: 400,
6467
padding: {
6568
top: 20,
@@ -156,7 +159,6 @@ import d3 from 'd3'
156159
.attr('width', w + padding.left + padding.right)
157160
.attr('height', h + padding.top + padding.bottom)
158161

159-
160162
svg.append('rect')
161163
.attr('x', padding.left)
162164
.attr('y', padding.top)
@@ -243,7 +245,21 @@ import d3 from 'd3'
243245
return y
244246
}
245247
}
246-
248+
249+
/* render react tooltip component */
250+
ReactDOM.unmountComponentAtNode(document.getElementById('chart-tooltip'))
251+
ReactDOM.render(<Tooltip popMethod='click'>
252+
<div className='tooltip-target'></div>
253+
<div className='tooltip-body'>
254+
<div className='tooltip-rating'></div>
255+
<div className='tooltip-challenge'>
256+
<div className='challenge-name'></div>
257+
<div className='challenge-date'></div>
258+
</div>
259+
</div>
260+
</Tooltip>
261+
, document.getElementById('chart-tooltip'))
262+
247263
svg.selectAll('circle')
248264
.data(history)
249265
.enter()
@@ -261,20 +277,44 @@ import d3 from 'd3'
261277
$scope.historyRating = d.newRating
262278
$scope.historyDate = moment(d.ratingDate).format('YYYY-MM-DD')
263279
$scope.historyChallenge = d.challengeName
280+
$('#chart-tooltip .tooltip-container').on('click', function(){
281+
if($state.params && ($state.params.subTrack === 'SRM' || $state.params.subTrack === 'MARATHON_MATCH'))
282+
location.href = $filter('challengeLinks')({'rounds': [{id: d.challengeId, forumId: null}], 'track': $state.params.track, 'subTrack': $state.params.subTrack}, 'detail')
283+
else
284+
location.href = $filter('challengeLinks')({id: d.challengeId, 'track': $state.params.track, 'subTrack': $state.params.subTrack}, 'detail')
285+
})
286+
287+
/* update tooltip location on mouseover, feature currently not inbuilt in react tooltip component */
288+
d3.select('#chart-tooltip')
289+
.style('left', (d3.event.pageX-5) + 'px')
290+
.style('top', (d3.event.pageY-5) + 'px')
291+
d3.select('#chart-tooltip .tooltip-container')
292+
.style('left', '20px !important')
293+
.style('top', '-20px !important')
294+
d3.select('#chart-tooltip .tooltip-container .tooltip-pointer')
295+
.style('left', '-5.5px !important')
296+
.style('bottom', '25px !important')
297+
298+
d3.select('#chart-tooltip .challenge-name').text($scope.historyChallenge)
299+
d3.select('#chart-tooltip .challenge-date').text(moment(d.ratingDate).format('MMM DD, YYYY'))
300+
d3.select('#chart-tooltip .tooltip-rating').text($scope.historyRating)
301+
d3.select('#chart-tooltip .tooltip-rating').style('background', ratingToColor($scope.colors, $scope.historyRating))
302+
$('#chart-tooltip').removeClass('distribution')
264303
$scope.$digest()
265-
d3.select(this)
266-
.attr('r', 6.0)
267304
})
268305
.on('mouseout', function(d) {
269306
$scope.historyRating = undefined
270307
$scope.$digest()
271-
d3.select(this)
272-
.attr('r', 4.5)
273-
.attr('stroke', 'none')
274-
.attr('stroke-width', '0px')
275308
})
276-
.attr('r', 4.5)
277309

310+
/* hide tooltip when clicked anywhere outside */
311+
d3.select('body').on('click', function(){
312+
if((d3.event.target.classList[0] != 'tooltip-target') && !$('#chart-tooltip .tooltip-container').hasClass('tooltip-hide') &&
313+
(d3.event.target.tagName.toLowerCase()!='circle') && !(d3.event.target.tagName.toLowerCase()=='rect' && d3.event.target.classList[0] == 'hover')) {
314+
$('#chart-tooltip .tooltip-target').trigger('click')
315+
$('#chart-tooltip .tooltip-container').off('click')
316+
}
317+
})
278318

279319
}
280320

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
.history-graph-directive(ng-show="graphState.show == 'history'")
2-
.history-graph-container
32

4-
.history-graph
3+
.graph-title
4+
.text Rating History Graph
5+
.button-group
6+
button.tc-btn.tc-btn-s.active(ng-click="graphState.show = 'history'") View Rating History
7+
button.tc-btn.tc-btn-s(ng-click="graphState.show = 'distribution'") View Rating Distribution
8+
9+
.history-graph-container
510

6-
.info-port
7-
.rating(style="background: {{historyRating || rating | ratingColor}}")
8-
.num {{historyRating || rating}}
9-
.label RATING
10-
.history-info
11-
.challenge(ng-if="historyRating") {{historyChallenge}}
12-
.date(ng-if="historyRating") {{historyDate | date}}
13-
button.tc-btn.tc-btn-s.compare(ng-click="graphState.show = 'distribution'") View Rating Distribution
11+
.history-graph

app/filters/challengeLinks.filter.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,15 @@ import angular from 'angular'
2323
case 'detail':
2424
return String.supplant('https://community.{domain}/longcontest/stats/?module=ViewOverview&rd={roundId}', data)
2525
}
26+
} else if (challenge.subTrack === 'SRM') {
27+
data = {
28+
domain: CONSTANTS.domain,
29+
roundId: challenge.rounds[0].id
30+
}
31+
switch (type) {
32+
case 'detail':
33+
return String.supplant('https://community.{domain}/stat?c=round_overview&rd={roundId}', data)
34+
}
2635
} else {
2736
data = {
2837
domain: CONSTANTS.domain,

app/index.jade

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ html
3636
.fold-pusher
3737

3838
div(ui-view="footer")
39+
40+
#chart-tooltip

app/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ require('xml2js')
2525

2626
require('appirio-tech-ng-ui-components')
2727
require('appirio-tech-ng-iso-constants')
28+
require('appirio-tech-react-components')
2829

2930
// Vendor styles
3031
require('../node_modules/angucomplete-alt/angucomplete-alt.css')

assets/css/directives/distribution-graph.scss

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,51 @@
1212

1313
}
1414

15+
.graph-title {
16+
width: 960px;
17+
padding-left: 45px;
18+
text-align: left;
19+
margin: auto;
20+
font-size: 28px;
21+
line-height: 35px;
22+
color: #47474F;
23+
24+
.text {
25+
float: left;
26+
}
27+
28+
.button-group {
29+
float: right;
30+
display: block;
31+
button.tc-btn.tc-btn-s {
32+
background: #fff;
33+
color: #47474f;
34+
border: 1px solid #c3c3c8;
35+
height: 30px;
36+
box-sizing: border-box;
37+
&.active {
38+
background: #dcdce0;
39+
-webkit-box-shadow: inset 0px 1px 3px 0px rgba(0,0,0,1);
40+
-moz-box-shadow: inset 0px 1px 3px 0px rgba(0,0,0,1);
41+
box-shadow: inset 0px 1px 3px 0px rgba(0,0,0,1);
42+
border: none;
43+
}
44+
&:first-child {
45+
border-top-left-radius: 2px;
46+
border-bottom-left-radius: 2px;
47+
border-top-right-radius: 0;
48+
border-bottom-right-radius: 0;
49+
}
50+
&:last-child {
51+
border-top-left-radius: 0;
52+
border-bottom-left-radius: 0;
53+
border-top-right-radius: 2px;
54+
border-bottom-right-radius: 2px;
55+
}
56+
}
57+
}
58+
}
59+
1560
.graph-viewer {
1661
margin-top: 18px;
1762
display: flex;

0 commit comments

Comments
 (0)