renderContributionGraph: function()

in pontoon/contributors/static/js/profile.js [160:291]


      renderContributionGraph: function () {
        const graph = $('#contribution-graph');
        const contributions = graph.data('contributions');

        // Set start date to 365 days before now
        const startDate = new Date();
        startDate.setMonth(startDate.getMonth() - 12);
        startDate.setUTCDate(startDate.getUTCDate() + 1);
        startDate.setUTCHours(0, 0, 0, 0);
        const endDate = new Date();

        let graphHTML = '';
        const step = 13;

        const currentDate = startDate;
        let currentMonth = currentDate.getMonth();
        const monthPosition = [{ monthIndex: currentMonth, x: 0 }];

        let week = 0;
        let gx = week * step;
        let weekHTML = `<g transform="translate(${gx.toString()}, 0)">`;

        while (currentDate.getTime() <= endDate.getTime()) {
          if (currentDate.getDay() == 0) {
            weekHTML = `<g transform="translate(${gx.toString()}, 0)">`;
          }

          const monthOfTheDay = currentDate.getMonth();

          if (currentDate.getDay() == 0 && monthOfTheDay != currentMonth) {
            currentMonth = monthOfTheDay;
            monthPosition.push({ monthIndex: currentMonth, x: gx });
          }

          const count = contributions[currentDate.getTime()] || 0;

          // Pick color based on count range
          let color;
          switch (true) {
            case count === 0:
              color = style.getPropertyValue('--dark-grey-1');
              break;
            case count < 10:
              color = style.getPropertyValue('--forest-green-1');
              break;
            case count < 25:
              color = style.getPropertyValue('--green');
              break;
            case count < 50:
              color = style.getPropertyValue('--green-2');
              break;
            default:
              color = style.getPropertyValue('--status-translated');
          }

          const y = currentDate.getDay() * step;
          const date = currentDate.getTime();
          weekHTML += `<rect class="day" width="10" height="10" y="${y}" fill="${color}" data-count="${count}" data-date="${date}" rx="2" ry="2"/>`;

          if (currentDate.getDay() == 6) {
            weekHTML += '</g>';
            graphHTML += weekHTML;
            weekHTML = null;
            week++;
            gx = week * step;
          }

          currentDate.setUTCDate(currentDate.getUTCDate() + 1);
        }

        // Add week items
        if (weekHTML != null) {
          weekHTML += '</g>';
          graphHTML += weekHTML;
        }

        // Remove the first incomplete month label
        if (monthPosition[1].x - monthPosition[0].x < 40) {
          monthPosition.shift();
        }

        // Remove the last incomplete month label
        if (monthPosition.at(-1).x > 660) {
          monthPosition.pop();
        }

        // Add month labels
        for (let x = 0; x < monthPosition.length; x++) {
          const item = monthPosition[x];
          const monthName = monthNameFormat.format(
            new Date(2022, item.monthIndex),
          );
          graphHTML += `<text x="${item.x}" y="-7" class="month">${monthName}</text>`;
        }

        // Add day labels
        graphHTML += `
            <text text-anchor="middle" class="wday" dx="-10" dy="23">M<title>Monday</title></text>
            <text text-anchor="middle" class="wday" dx="-10" dy="49">W<title>Wednesday</title></text>
            <text text-anchor="middle" class="wday" dx="-10" dy="75">F<title>Friday</title></text>`;

        graph.html(`
            <svg width="690" height="110" viewBox="0 0 702 110" class="js-calendar-graph-svg">
              <g transform="translate(16, 20)">${graphHTML}</g>
            </svg>`);

        // Handle tooltip
        $(graph)
          .find('.day')
          .hover(
            function (e) {
              const count = $(e.target).attr('data-count');
              const date = shortDateFormat.format(
                $(e.target).attr('data-date'),
              );
              const action = count == 1 ? 'contribution' : 'contributions';
              const text = `${count} ${action} on ${date}`;

              const tooltip = $('.svg-tip').show();
              tooltip.html(text);

              const offset = $(e.target).offset();
              const width = Math.round(tooltip.width() / 2 + 5);
              const height = tooltip.height() * 2 + 10;
              tooltip.css({ top: offset.top - height - 5 });
              tooltip.css({ left: offset.left - width });
            },
            function () {
              $('.svg-tip').hide();
            },
          );
      },