]> git.openstreetmap.org Git - rails.git/blob - app/assets/javascripts/heatmap.js
Merge remote-tracking branch 'upstream/pull/5402'
[rails.git] / app / assets / javascripts / heatmap.js
1 //= require d3/dist/d3
2 //= require cal-heatmap/dist/cal-heatmap
3 //= require popper
4 //= require cal-heatmap/dist/plugins/Tooltip
5
6 /* global CalHeatmap, Tooltip */
7 document.addEventListener("DOMContentLoaded", () => {
8   const heatmapElement = document.querySelector("#cal-heatmap");
9
10   if (!heatmapElement) {
11     return;
12   }
13
14   const heatmapData = heatmapElement.dataset.heatmap ? JSON.parse(heatmapElement.dataset.heatmap) : [];
15   const colorScheme = document.documentElement.getAttribute("data-bs-theme") ?? "auto";
16   const rangeColors = ["#14432a", "#166b34", "#37a446", "#4dd05a"];
17   const startDate = new Date(Date.now() - (365 * 24 * 60 * 60 * 1000));
18   const monthNames = I18n.t("date.abbr_month_names");
19
20   const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
21
22   let cal = new CalHeatmap();
23   let currentTheme = getTheme();
24
25   function renderHeatmap() {
26     cal.destroy();
27     cal = new CalHeatmap();
28
29     cal.paint({
30       itemSelector: "#cal-heatmap",
31       theme: currentTheme,
32       domain: {
33         type: "month",
34         gutter: 4,
35         label: {
36           text: (timestamp) => monthNames[new Date(timestamp).getMonth() + 1],
37           position: "top",
38           textAlign: "middle"
39         },
40         dynamicDimension: true
41       },
42       subDomain: {
43         type: "ghDay",
44         radius: 2,
45         width: 11,
46         height: 11,
47         gutter: 4
48       },
49       date: {
50         start: startDate
51       },
52       range: 13,
53       data: {
54         source: heatmapData,
55         type: "json",
56         x: "date",
57         y: "total_changes"
58       },
59       scale: {
60         color: {
61           type: "threshold",
62           range: currentTheme === "dark" ? rangeColors : Array.from(rangeColors).reverse(),
63           domain: [10, 20, 30, 40]
64         }
65       }
66     }, [
67       [Tooltip, {
68         text: (date, value) => getTooltipText(date, value)
69       }]
70     ]);
71   }
72
73   function getTooltipText(date, value) {
74     const localizedDate = I18n.l("date.formats.long", date);
75
76     if (value > 0) {
77       return I18n.t("javascripts.heatmap.tooltip.contributions", { count: value, date: localizedDate });
78     }
79
80     return I18n.t("javascripts.heatmap.tooltip.no_contributions", { date: localizedDate });
81   }
82
83   function getTheme() {
84     if (colorScheme === "auto") {
85       return mediaQuery.matches ? "dark" : "light";
86     }
87
88     return colorScheme;
89   }
90
91   if (colorScheme === "auto") {
92     mediaQuery.addEventListener("change", (e) => {
93       currentTheme = e.matches ? "dark" : "light";
94       renderHeatmap();
95     });
96   }
97
98   renderHeatmap();
99 });
100
101