]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/heatmap.js
Fix month label index desync causing missing labels
[rails.git] / app / assets / javascripts / heatmap.js
1 $(document).on("turbo:frame-load", function () {
2   const heatmap = $(".heatmap").removeClass("d-none").addClass("d-grid");
3   const weekInfo = getWeekInfo();
4   const maxPerDay = heatmap.data("max-per-day");
5   const weekdayLabels = heatmap.find("[data-weekday]");
6   const monthLabelStartIndex = Math.min(...heatmap.find("[data-month]").get().map(l => l.dataset.month));
7   let weekColumn = 1;
8   let previousMonth = null;
9
10   for (const day of weekdayLabels) {
11     const $day = $(day);
12     const weekday = $day.data("weekday");
13     if (weekday < weekInfo.firstDay % 7) {
14       $day.insertAfter(weekdayLabels.last());
15     }
16     const weekdayRow = getWeekdayRow(weekday);
17     if (weekdayRow % 2 === 0) $day.remove();
18     $day.css("grid-area", weekdayRow + " / 1");
19   }
20
21   for (const day of heatmap.find("[data-date]")) {
22     const $day = $(day);
23     const date = new Date($day.data("date"));
24     if (date.getUTCDay() === weekInfo.firstDay % 7) {
25       weekColumn++;
26       const currentMonth = getMonthOfThisWeek(date);
27       if (previousMonth === null) {
28         previousMonth = currentMonth + (Math.round((monthLabelStartIndex - currentMonth) / 12) * 12);
29         heatmap.find(`[data-month]:has( ~ [data-month="${previousMonth}"])`).remove();
30         heatmap.find("[data-month]").first().css("grid-column-start", 2);
31       }
32       if (previousMonth % 12 !== currentMonth % 12) {
33         heatmap.find(`[data-month="${previousMonth}"]`).css("grid-column-end", weekColumn);
34         previousMonth++;
35         heatmap.find(`[data-month="${previousMonth}"]`).css("grid-column-start", weekColumn);
36       }
37     }
38     if (weekColumn === 1) {
39       $day.remove();
40       continue;
41     }
42     const count = $day.data("count") ?? 0;
43     const tooltipText = getTooltipText($day.data("date"), count);
44     $day
45       .css("grid-area", getWeekdayRow(date.getUTCDay()) + " / " + weekColumn)
46       .attr("aria-label", tooltipText)
47       .tooltip({
48         title: tooltipText,
49         customClass: "wide",
50         delay: { show: 0, hide: 0 }
51       })
52       .find("span")
53       .css("opacity", Math.sqrt(count / maxPerDay));
54   }
55   heatmap.find(`[data-month="${previousMonth}"] ~ [data-month]`).remove();
56   heatmap.find("[data-month]").last().css("grid-column-end", weekColumn + 1);
57
58   function getMonthOfThisWeek(date) {
59     const nextDate = new Date(date);
60     nextDate.setUTCDate(date.getUTCDate() + weekInfo.minimalDays - 1);
61     return nextDate.getUTCMonth() + 1;
62   }
63
64   function getWeekdayRow(weekday) {
65     return ((weekday - weekInfo.firstDay + 7) % 7) + 2;
66   }
67
68   function getTooltipText(date, value) {
69     const localizedDate = OSM.i18n.l("date.formats.heatmap", date);
70
71     if (value > 0) {
72       return OSM.i18n.t("javascripts.heatmap.tooltip.contributions", { count: value, date: localizedDate });
73     }
74
75     return OSM.i18n.t("javascripts.heatmap.tooltip.no_contributions", { date: localizedDate });
76   }
77
78   function getWeekInfo() {
79     const weekInfo = { firstDay: 1, minimalDays: 4 }; // ISO 8601
80     const locale = new Intl.Locale(OSM.i18n.locale);
81     return { ...weekInfo, ...locale.weekInfo, ...locale.getWeekInfo?.() };
82   }
83 });