From 18b361cf809251dd633e31d3dd6d877b31d8db7f Mon Sep 17 00:00:00 2001 From: Phil Hughes Date: Wed, 27 Apr 2016 11:07:46 +0100 Subject: [PATCH] Group the contributing calendar by day This aligns the boxes correctly with the day on the left side of the calendar --- app/assets/javascripts/application.js.coffee | 1 - app/assets/javascripts/calendar.js.coffee | 147 +++++++++++++----- app/assets/stylesheets/application.scss | 1 - .../stylesheets/framework/calendar.scss | 69 ++------ app/assets/stylesheets/pages/profile.scss | 6 - app/controllers/users_controller.rb | 2 - app/views/users/calendar.html.haml | 17 +- app/views/users/show.html.haml | 7 +- lib/gitlab/contributions_calendar.rb | 2 +- 9 files changed, 132 insertions(+), 120 deletions(-) diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index bffce5a0c0f..ff69e27bbcf 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -19,7 +19,6 @@ #= require jquery.scrollTo #= require jquery.turbolinks #= require d3 -#= require cal-heatmap #= require turbolinks #= require autosave #= require bootstrap/affix diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee index ec153f61c2a..9c4d529f7e9 100644 --- a/app/assets/javascripts/calendar.js.coffee +++ b/app/assets/javascripts/calendar.js.coffee @@ -1,52 +1,83 @@ class @Calendar - constructor: (timestamps, starting_year, starting_month, calendar_activities_path) -> + constructor: (timestamps, calendar_activities_path) -> # Get the highest value from the timestampes highestValue = 0 _.each timestamps, (count) -> if count > highestValue highestValue = count - timestamps = _.chain(timestamps) - .map (stamp, key) -> - { - count: stamp - date: key - } - .groupBy (stamp, i) -> - Math.floor i / 7 - .toArray() - .value() + # Loop through the timestamps to create a group of objects + # The group of objects will be grouped based on the day of the week they are + timestampsTmp = [] + i = 0 + group = 0 + _.each timestamps, (count, date) -> + newDate = new Date parseInt(date) * 1000 + day = newDate.getDay() + + # Create a new group array if this is the first day of the week + # or if is first object + if (day is 0 and i isnt 0) or i is 0 + timestampsTmp.push [] + group++ + + innerArray = timestampsTmp[group-1] + + # Push to the inner array the values that will be used to render map + innerArray.push + count: count + date: newDate + day: day + + i++ + + # Color function for chart + color = d3 + .scale + .linear() + .range(['#acd5f2', '#254e77']) + .domain([0, highestValue]) + + # Color function for key + colorKey = d3 + .scale + .linear() + .range(['#acd5f2', '#254e77']) + .domain([0, 3]) + keyColors = ['#ededed', colorKey(0), colorKey(1), colorKey(2), colorKey(3)] monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] months = [] - svg = d3.select '#cal-heatmap' + svg = d3.select '.js-contrib-calendar' .append 'svg' - .attr 'width', 53 * 17 - .attr 'height', 140 + .attr 'width', 54 * 17 + .attr 'height', 167 + .attr 'class', 'contrib-calendar' # Setup each day box svg.selectAll 'g' - .data timestamps + .data timestampsTmp .enter() .append 'g' .attr 'transform', (group, i) -> - _.each group, (stamp) -> - month = new Date(parseInt(stamp.date) * 1000).getMonth() - x = 17 * i + 1 - lastMonth = _.last(months) + _.each group, (stamp, a) -> + if a is 0 and stamp.day is 0 + month = stamp.date.getMonth() + x = (17 * i + 1) + 17 + lastMonth = _.last(months) + if lastMonth? + lastMonthX = lastMonth.x - # If undefined, push - if !lastMonth? - months.push - month: month - x: x - else if lastMonth.x is x - lastMonth.month = month - else if lastMonth.month isnt month + if !lastMonth? months.push month: month x: x - "translate(#{17 * i + 1}, 18)" + else if month isnt lastMonth.month and x - 17 isnt lastMonthX + months.push + month: month + x: x + + "translate(#{(17 * i + 1) + 17}, 18)" .selectAll 'rect' .data (stamp) -> stamp @@ -54,22 +85,20 @@ class @Calendar .append 'rect' .attr 'x', '0' .attr 'y', (stamp, i) -> - 17 * i + (17 * stamp.day) .attr 'width', 15 .attr 'height', 15 .attr 'title', (stamp) -> - "#{stamp.count} contributions
#{gl.utils.formatDate parseInt(stamp.date) * 1000}" - .attr 'class', (stamp) -> - extraClass = '' + "#{stamp.count} contributions
#{gl.utils.formatDate stamp.date}" + .attr 'class', 'user-contrib-cell js-tooltip' + .attr 'fill', (stamp) -> if stamp.count isnt 0 - diff = stamp.count / highestValue - classNumber = Math.floor (diff / 0.25) + 1 - extraClass += "user-contrib-cell-#{classNumber}" - - "user-contrib-cell #{extraClass} js-tooltip" + color(stamp.count) + else + '#ededed' .attr 'data-container', 'body' .on 'click', (stamp) -> - date = new Date(parseInt(stamp.date) * 1000) + date = stamp.date formated_date = date.getFullYear() + "-" + (date.getMonth()+1) + "-" + date.getDate() $.ajax url: calendar_activities_path @@ -80,6 +109,7 @@ class @Calendar success: (data) -> $(".user-calendar-activities").html data + # Month titles svg.append 'g' .selectAll 'text' .data months @@ -92,5 +122,44 @@ class @Calendar .text (date) -> monthNames[date.month] - $('#cal-heatmap .js-tooltip').tooltip + # Day titles + days = [{ + text: 'M' + y: 29 + (17 * 1) + }, { + text: 'W' + y: 29 + (17 * 3) + }, { + text: 'F' + y: 29 + (17 * 5) + }] + svg.append 'g' + .selectAll 'text' + .data days + .enter() + .append 'text' + .attr 'text-anchor', 'middle' + .attr 'x', 8 + .attr 'y', (day) -> + day.y + .text (day) -> + day.text + .attr 'class', 'user-contrib-text' + + # Key with color boxes + svg.append 'g' + .attr 'transform', "translate(18, #{17 * 8 + 16})" + .selectAll 'rect' + .data keyColors + .enter() + .append 'rect' + .attr 'width', 15 + .attr 'height', 15 + .attr 'x', (color, i) -> + 17 * i + .attr 'y', 0 + .attr 'fill', (color) -> + color + + $('.js-contrib-calendar .js-tooltip').tooltip html: true diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 69b3b6586de..efdb2b06a8c 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -8,7 +8,6 @@ *= require select2 *= require_self *= require dropzone/basic - *= require cal-heatmap *= require cropper.css *= require animate */ diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index da03454d807..5b48801485b 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -26,75 +26,28 @@ } } -/** -* This overwrites the default values of the cal-heatmap gem -*/ -.calendar { - .qi { - fill: #fff; - } +.user-calendar { + text-align: center; - .q1 { - fill: #ededed !important; - } - - .q2 { - fill: #acd5f2 !important; - } - - .q3 { - fill: #7fa8d1 !important; - } - - .q4 { - fill: #49729b !important; - } - - .q5 { - fill: #254e77 !important; - } - - .future { - visibility: hidden; - } - - .domain-background { - fill: none; - shape-rendering: crispedges; - } - - .ch-tooltip { - padding: 3px; - font-weight: 550; + .calendar { + display: inline-block; } } .user-contrib-cell { - fill: #ededed; - cursor: pointer; - &:hover { + cursor: pointer; stroke: #000; } } -.user-contrib-cell-1 { - fill: #acd5f2; -} - -.user-contrib-cell-3 { - fill: #7fa8d1; -} - -.user-contrib-cell-4 { - fill: #49729b; -} - -.user-contrib-cell-5 { - fill: #254e77; -} - .user-contrib-text { font-size: 12px; fill: #959494; } + +.calendar-hint { + margin-top: -23px; + float: right; + font-size: 12px; +} diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 843379a3f54..ef375e1386c 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -66,12 +66,6 @@ } } -.calendar-hint { - margin-top: -12px; - float: right; - font-size: 12px; -} - .profile-link-holder { display: inline; diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 799421c185b..a99632454d9 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -74,8 +74,6 @@ class UsersController < ApplicationController def calendar calendar = contributions_calendar @timestamps = calendar.timestamps - @starting_year = calendar.starting_year - @starting_month = calendar.starting_month render 'calendar', layout: false end diff --git a/app/views/users/calendar.html.haml b/app/views/users/calendar.html.haml index 2abee8be2a6..77f2ddefb1e 100644 --- a/app/views/users/calendar.html.haml +++ b/app/views/users/calendar.html.haml @@ -1,8 +1,9 @@ -#cal-heatmap.calendar - :javascript - new Calendar( - #{@timestamps.to_json}, - #{@starting_year}, - #{@starting_month}, - '#{user_calendar_activities_path}' - ); +.clearfix.calendar + .js-contrib-calendar + .calendar-hint + Summary of issues, merge requests, and push events +:javascript + new Calendar( + #{@timestamps.to_json}, + '#{user_calendar_activities_path}' + ); diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index 9017fd54fcc..0c513308308 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -89,10 +89,9 @@ .tab-content #activity.tab-pane .row-content-block.calender-block.white.second-block.hidden-xs - %div{ class: container_class } - .user-calendar{data: {href: user_calendar_path}} - %h4.center.light - %i.fa.fa-spinner.fa-spin + .user-calendar{data: {href: user_calendar_path}} + %h4.center.light + %i.fa.fa-spinner.fa-spin .user-calendar-activities .content_list{ data: {href: user_path} } diff --git a/lib/gitlab/contributions_calendar.rb b/lib/gitlab/contributions_calendar.rb index 85583dce9ee..9dc2602867e 100644 --- a/lib/gitlab/contributions_calendar.rb +++ b/lib/gitlab/contributions_calendar.rb @@ -19,7 +19,7 @@ module Gitlab select('date(created_at) as date, count(id) as total_amount'). map(&:attributes) - dates = (1.year.ago.to_date..(Date.today + 1.day)).to_a + dates = (1.year.ago.to_date..Date.today).to_a dates.each do |date| date_id = date.to_time.to_i.to_s