@rsalianto/git-heatmap-react 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # @rsalianto/git-heatmap-react
2
+
3
+ React component for displaying GitHub/GitLab contribution heatmaps.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @rsalianto/git-heatmap-react @rsalianto/git-heatmap-core
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Fetch from an API endpoint
14
+
15
+ ```tsx
16
+ import { GitHeatmap } from "@rsalianto/git-heatmap-react";
17
+
18
+ export default function Page() {
19
+ return <GitHeatmap apiUrl="/api/github-contributions" />;
20
+ }
21
+ ```
22
+
23
+ ### Pass data directly
24
+
25
+ ```tsx
26
+ import { GitHeatmap } from "@rsalianto/git-heatmap-react";
27
+ import { normalizeManual } from "@rsalianto/git-heatmap-core";
28
+
29
+ const data = normalizeManual([
30
+ { date: "2024-06-01", count: 4 },
31
+ { date: "2024-06-02", count: 0 },
32
+ ]);
33
+
34
+ export default function Page() {
35
+ return <GitHeatmap data={data} />;
36
+ }
37
+ ```
38
+
39
+ ### Custom fetch function
40
+
41
+ ```tsx
42
+ <GitHeatmap fetchData={() => fetch("/api/contributions").then(r => r.json())} />
43
+ ```
44
+
45
+ ## Props
46
+
47
+ | Prop | Type | Default | Description |
48
+ |---|---|---|---|
49
+ | `data` | `HeatmapData` | — | Static data (skips fetching) |
50
+ | `apiUrl` | `string` | — | Endpoint returning `HeatmapData` JSON |
51
+ | `fetchData` | `() => Promise<HeatmapData>` | — | Custom fetch function |
52
+ | `levels` | `LevelConfig[]` | `DEFAULT_LEVELS` | Color thresholds (5 levels) |
53
+ | `cellSize` | `number` | `10` | Cell size in px |
54
+ | `cellGap` | `number` | `3` | Gap between cells in px |
55
+ | `cellRadius` | `number` | `2` | Cell border radius in px |
56
+ | `showTotal` | `boolean` | `true` | Show total contributions text |
57
+ | `showLegend` | `boolean` | `true` | Show Less/More legend |
58
+ | `showMonthLabels` | `boolean` | `true` | Show month labels |
59
+ | `showDayLabels` | `boolean` | `true` | Show Mon/Wed/Fri labels |
60
+ | `theme` | `Partial<HeatmapTheme>` | — | Override CSS variable values |
61
+ | `onDayClick` | `(day: HeatmapDay) => void` | — | Click handler |
62
+ | `label` | `string` | `"Contribution heatmap"` | Accessible label |
63
+
64
+ ## Theming
65
+
66
+ Override via the `theme` prop or CSS variables:
67
+
68
+ ```tsx
69
+ <GitHeatmap
70
+ apiUrl="/api/contributions"
71
+ theme={{
72
+ colorL0: "#161b22",
73
+ colorL1: "#0e4429",
74
+ colorL2: "#006d32",
75
+ colorL3: "#26a641",
76
+ colorL4: "#39d353",
77
+ }}
78
+ />
79
+ ```
80
+
81
+ CSS variables: `--ghm-color-l0` through `--ghm-color-l4`, `--ghm-text`, `--ghm-tooltip-bg`, `--ghm-tooltip-border`, `--ghm-tooltip-text`, `--ghm-font`, `--ghm-fs`.
82
+
83
+ ## `useHeatmapData` hook
84
+
85
+ ```ts
86
+ import { useHeatmapData } from "@rsalianto/git-heatmap-react";
87
+
88
+ const { data, status, error } = useHeatmapData({ apiUrl: "/api/contributions" });
89
+ // status: "idle" | "loading" | "success" | "error"
90
+ ```
91
+
92
+ ## Related packages
93
+
94
+ - [`@rsalianto/git-heatmap-next`](https://www.npmjs.com/package/@rsalianto/git-heatmap-next) — Next.js App Router API route handlers
95
+ - [`@rsalianto/git-heatmap-core`](https://www.npmjs.com/package/@rsalianto/git-heatmap-core) — Core utilities
package/dist/index.js CHANGED
@@ -70,19 +70,19 @@ function useHeatmapData(options) {
70
70
 
71
71
  // src/GitHeatmap.tsx
72
72
  var import_jsx_runtime = require("react/jsx-runtime");
73
- function themeToVars(theme) {
73
+ function themeToVars(t) {
74
74
  return {
75
- "--ghm-color-l0": theme.colorL0,
76
- "--ghm-color-l1": theme.colorL1,
77
- "--ghm-color-l2": theme.colorL2,
78
- "--ghm-color-l3": theme.colorL3,
79
- "--ghm-color-l4": theme.colorL4,
80
- "--ghm-text": theme.textColor,
81
- "--ghm-tooltip-bg": theme.tooltipBg,
82
- "--ghm-tooltip-border": theme.tooltipBorderColor,
83
- "--ghm-tooltip-text": theme.tooltipTextColor,
84
- "--ghm-font": theme.fontFamily,
85
- "--ghm-fs": theme.fontSize
75
+ "--ghm-color-l0": t.colorL0,
76
+ "--ghm-color-l1": t.colorL1,
77
+ "--ghm-color-l2": t.colorL2,
78
+ "--ghm-color-l3": t.colorL3,
79
+ "--ghm-color-l4": t.colorL4,
80
+ "--ghm-text": t.textColor,
81
+ "--ghm-tooltip-bg": t.tooltipBg,
82
+ "--ghm-tooltip-border": t.tooltipBorderColor,
83
+ "--ghm-tooltip-text": t.tooltipTextColor,
84
+ "--ghm-font": t.fontFamily,
85
+ "--ghm-fs": t.fontSize
86
86
  };
87
87
  }
88
88
  var BASE_STYLE = `
@@ -108,6 +108,8 @@ var BASE_STYLE = `
108
108
  box-sizing: border-box;
109
109
  }
110
110
  [data-git-heatmap] * { box-sizing: border-box; }
111
+ [data-git-heatmap] rect { transition: opacity 0.15s; }
112
+ [data-git-heatmap] rect:hover { opacity: 0.7; }
111
113
  `;
112
114
  var styleInjected = false;
113
115
  function ensureStyle() {
@@ -117,9 +119,8 @@ function ensureStyle() {
117
119
  document.head.appendChild(el);
118
120
  styleInjected = true;
119
121
  }
120
- function levelColor(level) {
121
- return `var(--ghm-color-l${level})`;
122
- }
122
+ var DAY_LABEL_W = 32;
123
+ var MONTH_LABEL_H = 16;
123
124
  function GitHeatmap({
124
125
  data: dataProp,
125
126
  apiUrl,
@@ -138,141 +139,159 @@ function GitHeatmap({
138
139
  }) {
139
140
  const step = cellSize + cellGap;
140
141
  const { data, status, error } = useHeatmapData({ data: dataProp, apiUrl, fetchData });
141
- const scrollRef = (0, import_react2.useRef)(null);
142
142
  const wrapperRef = (0, import_react2.useRef)(null);
143
+ const scrollRef = (0, import_react2.useRef)(null);
143
144
  const [tappedDay, setTappedDay] = (0, import_react2.useState)(null);
144
145
  const [tooltip, setTooltip] = (0, import_react2.useState)(null);
145
146
  (0, import_react2.useEffect)(() => {
146
147
  ensureStyle();
147
148
  }, []);
148
149
  (0, import_react2.useEffect)(() => {
149
- if (data && scrollRef.current) {
150
- scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;
151
- }
150
+ if (data && scrollRef.current) scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;
152
151
  }, [data]);
153
152
  const mergedTheme = { ...import_git_heatmap_core.DEFAULT_THEME, ...theme };
154
153
  const cssVars = themeToVars(mergedTheme);
155
154
  const formatTooltip = (day) => day.count === 0 ? `No contributions on ${day.date}` : `${day.count} contribution${day.count > 1 ? "s" : ""} on ${day.date}`;
155
+ const gridOffsetX = showDayLabels ? DAY_LABEL_W : 0;
156
+ const gridOffsetY = showMonthLabels ? MONTH_LABEL_H + cellGap : 0;
156
157
  if (error) {
157
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-git-heatmap": "", style: cssVars, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { color: "var(--ghm-text)", opacity: 0.6, fontSize: "0.9em", padding: "1rem 0" }, children: "Could not load contribution data." }) });
158
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-git-heatmap": "", style: cssVars, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { opacity: 0.6, fontSize: "0.9em", padding: "1rem 0" }, children: "Could not load contribution data." }) });
158
159
  }
159
160
  if (status === "loading" || status === "idle") {
160
- const skeletonCols = Array.from({ length: 53 });
161
- const skeletonRows = Array.from({ length: 7 });
161
+ const cols = 53;
162
+ const svgW2 = cols * step + gridOffsetX;
163
+ const svgH2 = 7 * step - cellGap + gridOffsetY;
162
164
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-git-heatmap": "", style: { ...cssVars, opacity: 0.4 }, children: [
163
- showTotal && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { height: 16, width: 160, background: "var(--ghm-text)", borderRadius: 4, marginBottom: 12, opacity: 0.2 } }),
164
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", gap: cellGap }, children: skeletonCols.map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: cellGap }, children: skeletonRows.map((_2, j) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: cellSize, height: cellSize, borderRadius: cellRadius, background: "var(--ghm-color-l0)" } }, j)) }, i)) })
165
+ showTotal && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { height: 14, width: 160, background: "var(--ghm-text)", borderRadius: 4, marginBottom: 12, opacity: 0.2 } }),
166
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: svgW2, height: svgH2, style: { display: "block" }, children: Array.from({ length: cols }).map(
167
+ (_, wi) => Array.from({ length: 7 }).map((_2, dow) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
168
+ "rect",
169
+ {
170
+ x: gridOffsetX + wi * step,
171
+ y: gridOffsetY + dow * step,
172
+ width: cellSize,
173
+ height: cellSize,
174
+ rx: cellRadius,
175
+ fill: "var(--ghm-color-l0)"
176
+ },
177
+ `${wi}-${dow}`
178
+ ))
179
+ ) })
165
180
  ] });
166
181
  }
167
182
  if (!data) return null;
168
183
  const monthLabels = showMonthLabels ? (0, import_git_heatmap_core.buildMonthLabels)(data.weeks) : [];
184
+ const svgW = data.weeks.length * step + gridOffsetX;
185
+ const svgH = 7 * step - cellGap + gridOffsetY;
169
186
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-git-heatmap": "", ref: wrapperRef, style: cssVars, "aria-label": label, children: [
170
187
  showTotal && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: { marginBottom: 12, color: "var(--ghm-text)" }, children: [
171
188
  data.totalContributions.toLocaleString(),
172
189
  " contributions in the last year"
173
190
  ] }),
174
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: scrollRef, style: { overflowX: "auto", overflowY: "visible", paddingBottom: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "inline-flex", flexDirection: "column", minWidth: data.weeks.length * step }, children: [
175
- showMonthLabels && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", marginBottom: cellGap, marginLeft: showDayLabels ? 32 : 0 }, children: monthLabels.map(({ label: ml, col }, idx) => {
176
- const nextCol = monthLabels[idx + 1]?.col ?? data.weeks.length;
177
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: (nextCol - col) * step, whiteSpace: "nowrap", fontSize: "0.9em", color: "var(--ghm-text)" }, children: ml }, ml + col);
178
- }) }),
179
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex" }, children: [
180
- showDayLabels && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: cellGap, marginRight: 6 }, children: Array.from({ length: 7 }).map((_, dow) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { height: cellSize, lineHeight: `${cellSize}px`, width: 26, textAlign: "right", paddingRight: 4, fontSize: "0.9em", color: "var(--ghm-text)" }, children: import_git_heatmap_core.DAY_LABELS[dow] ?? "" }, dow)) }),
181
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", gap: cellGap }, children: data.weeks.map((week, wi) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: cellGap }, children: Array.from({ length: 7 }).map((_, dow) => {
182
- const day = week.days[dow];
183
- if (!day || !day.date) {
184
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { width: cellSize, height: cellSize } }, dow);
185
- }
186
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
187
- "div",
191
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { ref: scrollRef, style: { overflowX: "auto", overflowY: "visible", paddingBottom: 4 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
192
+ "svg",
193
+ {
194
+ width: svgW,
195
+ height: svgH,
196
+ style: { display: "block", overflow: "visible" },
197
+ role: "img",
198
+ "aria-label": label,
199
+ children: [
200
+ showMonthLabels && monthLabels.map(({ label: ml, col }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
201
+ "text",
188
202
  {
189
- style: {
190
- width: cellSize,
191
- height: cellSize,
192
- borderRadius: cellRadius,
193
- background: levelColor(day.level),
194
- cursor: "pointer",
195
- transition: "opacity 0.15s"
196
- },
197
- onMouseEnter: (e) => {
198
- const r = e.currentTarget.getBoundingClientRect();
199
- const wr = wrapperRef.current?.getBoundingClientRect();
200
- if (wr) setTooltip({ day, x: r.left - wr.left + cellSize / 2, y: r.top - wr.top });
201
- },
202
- onMouseLeave: () => setTooltip(null),
203
- onClick: () => {
204
- setTappedDay((prev) => prev?.date === day.date ? null : day);
205
- onDayClick?.(day);
206
- },
207
- onMouseOver: (e) => {
208
- e.currentTarget.style.opacity = "0.7";
209
- },
210
- onFocus: (e) => {
211
- e.currentTarget.style.opacity = "0.7";
212
- },
213
- onMouseOut: (e) => {
214
- e.currentTarget.style.opacity = "1";
215
- },
216
- onBlur: (e) => {
217
- e.currentTarget.style.opacity = "1";
218
- },
219
- role: "button",
220
- tabIndex: 0,
221
- "aria-label": formatTooltip(day)
203
+ x: gridOffsetX + col * step,
204
+ y: MONTH_LABEL_H - 4,
205
+ fill: "var(--ghm-text)",
206
+ fontSize: "10",
207
+ children: ml
222
208
  },
223
- dow
224
- );
225
- }) }, wi)) })
226
- ] })
227
- ] }) }),
209
+ ml + col
210
+ )),
211
+ showDayLabels && Array.from({ length: 7 }).map(
212
+ (_, dow) => import_git_heatmap_core.DAY_LABELS[dow] ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
213
+ "text",
214
+ {
215
+ x: DAY_LABEL_W - 6,
216
+ y: gridOffsetY + dow * step + cellSize,
217
+ fill: "var(--ghm-text)",
218
+ fontSize: "10",
219
+ textAnchor: "end",
220
+ children: import_git_heatmap_core.DAY_LABELS[dow]
221
+ },
222
+ dow
223
+ ) : null
224
+ ),
225
+ data.weeks.map(
226
+ (week, wi) => Array.from({ length: 7 }).map((_, dow) => {
227
+ const day = week.days[dow];
228
+ if (!day?.date) return null;
229
+ const x = gridOffsetX + wi * step;
230
+ const y = gridOffsetY + dow * step;
231
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
232
+ "rect",
233
+ {
234
+ x,
235
+ y,
236
+ width: cellSize,
237
+ height: cellSize,
238
+ rx: cellRadius,
239
+ fill: `var(--ghm-color-l${day.level})`,
240
+ style: { cursor: "pointer" },
241
+ "aria-label": formatTooltip(day),
242
+ role: "button",
243
+ onMouseEnter: (e) => {
244
+ const r = e.currentTarget.getBoundingClientRect();
245
+ const wr = wrapperRef.current?.getBoundingClientRect();
246
+ if (wr) setTooltip({ day, x: r.left - wr.left + cellSize / 2, y: r.top - wr.top });
247
+ },
248
+ onMouseLeave: () => setTooltip(null),
249
+ onClick: () => {
250
+ setTappedDay((prev) => prev?.date === day.date ? null : day);
251
+ onDayClick?.(day);
252
+ }
253
+ },
254
+ `${wi}-${dow}`
255
+ );
256
+ })
257
+ )
258
+ ]
259
+ }
260
+ ) }),
228
261
  tappedDay && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginTop: 8, fontSize: "0.9em", color: "var(--ghm-text)", background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 4, padding: "6px 12px" }, children: formatTooltip(tappedDay) }),
229
262
  showLegend && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { display: "flex", alignItems: "center", justifyContent: "flex-end", marginTop: 8 }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: "0.9em", color: "var(--ghm-text)" }, children: [
230
263
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "Less" }),
231
- levels.map((lvl, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
232
- "div",
233
- {
234
- style: { width: cellSize, height: cellSize, borderRadius: cellRadius, background: `var(--ghm-color-l${i})` },
235
- title: lvl.label
236
- },
237
- i
238
- )),
264
+ levels.map((lvl, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { width: cellSize, height: cellSize, style: { display: "block" }, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { width: cellSize, height: cellSize, rx: cellRadius, fill: `var(--ghm-color-l${i})`, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("title", { children: lvl.label }) }) }, i)),
239
265
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "More" })
240
266
  ] }) }),
241
- tooltip && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
242
- "div",
243
- {
244
- style: {
245
- pointerEvents: "none",
246
- position: "absolute",
247
- zIndex: 50,
248
- left: tooltip.x,
249
- top: tooltip.y - 6,
250
- transform: "translate(-50%, -100%)",
251
- marginBottom: 4,
252
- background: "var(--ghm-tooltip-bg)",
253
- border: "1px solid var(--ghm-tooltip-border)",
254
- color: "var(--ghm-tooltip-text)",
255
- fontSize: "0.9em",
256
- borderRadius: 4,
257
- padding: "3px 8px",
258
- whiteSpace: "nowrap"
259
- },
260
- children: [
261
- formatTooltip(tooltip.day),
262
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
263
- position: "absolute",
264
- left: "50%",
265
- top: "100%",
266
- transform: "translate(-50%, -50%) rotate(45deg)",
267
- width: 8,
268
- height: 8,
269
- background: "var(--ghm-tooltip-bg)",
270
- borderRight: "1px solid var(--ghm-tooltip-border)",
271
- borderBottom: "1px solid var(--ghm-tooltip-border)"
272
- } })
273
- ]
274
- }
275
- )
267
+ tooltip && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: {
268
+ pointerEvents: "none",
269
+ position: "absolute",
270
+ zIndex: 50,
271
+ left: tooltip.x,
272
+ top: tooltip.y - 6,
273
+ transform: "translate(-50%, -100%)",
274
+ background: "var(--ghm-tooltip-bg)",
275
+ border: "1px solid var(--ghm-tooltip-border)",
276
+ color: "var(--ghm-tooltip-text)",
277
+ fontSize: "0.9em",
278
+ borderRadius: 4,
279
+ padding: "3px 8px",
280
+ whiteSpace: "nowrap"
281
+ }, children: [
282
+ formatTooltip(tooltip.day),
283
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: {
284
+ position: "absolute",
285
+ left: "50%",
286
+ top: "100%",
287
+ transform: "translate(-50%, -50%) rotate(45deg)",
288
+ width: 8,
289
+ height: 8,
290
+ background: "var(--ghm-tooltip-bg)",
291
+ borderRight: "1px solid var(--ghm-tooltip-border)",
292
+ borderBottom: "1px solid var(--ghm-tooltip-border)"
293
+ } })
294
+ ] })
276
295
  ] });
277
296
  }
278
297
  // Annotate the CommonJS export names for ESM import in node:
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/GitHeatmap.tsx","../src/useHeatmapData.ts"],"sourcesContent":["export { GitHeatmap } from \"./GitHeatmap\";\nexport type { GitHeatmapProps } from \"./GitHeatmap\";\nexport { useHeatmapData } from \"./useHeatmapData\";\nexport type { HeatmapStatus } from \"./useHeatmapData\";\n","\"use client\";\nimport { useEffect, useRef, useState } from \"react\";\nimport {\n buildMonthLabels,\n CELL, GAP, STEP,\n DAY_LABELS,\n DEFAULT_LEVELS,\n DEFAULT_THEME,\n} from \"@rsalianto/git-heatmap-core\";\nimport type { HeatmapData, HeatmapDay, HeatmapTheme, LevelConfig } from \"@rsalianto/git-heatmap-core\";\nimport { useHeatmapData } from \"./useHeatmapData\";\n\nexport interface GitHeatmapProps {\n data?: HeatmapData;\n apiUrl?: string;\n fetchData?: () => Promise<HeatmapData>;\n levels?: LevelConfig[];\n cellSize?: number;\n cellGap?: number;\n cellRadius?: number;\n showTotal?: boolean;\n showLegend?: boolean;\n showMonthLabels?: boolean;\n showDayLabels?: boolean;\n theme?: Partial<HeatmapTheme>;\n onDayClick?: (day: HeatmapDay) => void;\n label?: string;\n}\n\nfunction themeToVars(theme: HeatmapTheme): Record<string, string> {\n return {\n \"--ghm-color-l0\": theme.colorL0,\n \"--ghm-color-l1\": theme.colorL1,\n \"--ghm-color-l2\": theme.colorL2,\n \"--ghm-color-l3\": theme.colorL3,\n \"--ghm-color-l4\": theme.colorL4,\n \"--ghm-text\": theme.textColor,\n \"--ghm-tooltip-bg\": theme.tooltipBg,\n \"--ghm-tooltip-border\": theme.tooltipBorderColor,\n \"--ghm-tooltip-text\": theme.tooltipTextColor,\n \"--ghm-font\": theme.fontFamily,\n \"--ghm-fs\": theme.fontSize,\n };\n}\n\nconst BASE_STYLE = `\n[data-git-heatmap] {\n --ghm-color-l0: rgba(255,255,255,0.08);\n --ghm-color-l1: #1c3d06;\n --ghm-color-l2: #3a7510;\n --ghm-color-l3: #6ab81e;\n --ghm-color-l4: #aafd35;\n --ghm-text: rgba(255,255,255,0.5);\n --ghm-tooltip-bg: #1c2128;\n --ghm-tooltip-border: rgba(255,255,255,0.1);\n --ghm-tooltip-text: rgba(255,255,255,0.75);\n --ghm-font: inherit;\n --ghm-fs: 11px;\n display: block;\n position: relative;\n width: 100%;\n font-size: var(--ghm-fs);\n font-family: var(--ghm-font);\n color: var(--ghm-text);\n user-select: none;\n box-sizing: border-box;\n}\n[data-git-heatmap] * { box-sizing: border-box; }\n`;\n\nlet styleInjected = false;\nfunction ensureStyle() {\n if (styleInjected || typeof document === \"undefined\") return;\n const el = document.createElement(\"style\");\n el.textContent = BASE_STYLE;\n document.head.appendChild(el);\n styleInjected = true;\n}\n\nfunction levelColor(level: 0 | 1 | 2 | 3 | 4): string {\n return `var(--ghm-color-l${level})`;\n}\n\nexport function GitHeatmap({\n data: dataProp,\n apiUrl,\n fetchData,\n levels = DEFAULT_LEVELS,\n cellSize = CELL,\n cellGap = GAP,\n cellRadius = 2,\n showTotal = true,\n showLegend = true,\n showMonthLabels = true,\n showDayLabels = true,\n theme,\n onDayClick,\n label = \"Contribution heatmap\",\n}: GitHeatmapProps) {\n const step = cellSize + cellGap;\n\n const { data, status, error } = useHeatmapData({ data: dataProp, apiUrl, fetchData });\n const scrollRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const [tappedDay, setTappedDay] = useState<HeatmapDay | null>(null);\n const [tooltip, setTooltip] = useState<{ day: HeatmapDay; x: number; y: number } | null>(null);\n\n useEffect(() => { ensureStyle(); }, []);\n\n useEffect(() => {\n if (data && scrollRef.current) {\n scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;\n }\n }, [data]);\n\n const mergedTheme: HeatmapTheme = { ...DEFAULT_THEME, ...theme };\n const cssVars = themeToVars(mergedTheme) as React.CSSProperties;\n\n const formatTooltip = (day: HeatmapDay) =>\n day.count === 0\n ? `No contributions on ${day.date}`\n : `${day.count} contribution${day.count > 1 ? \"s\" : \"\"} on ${day.date}`;\n\n if (error) {\n return (\n <div data-git-heatmap=\"\" style={cssVars}>\n <p style={{ color: \"var(--ghm-text)\", opacity: 0.6, fontSize: \"0.9em\", padding: \"1rem 0\" }}>\n Could not load contribution data.\n </p>\n </div>\n );\n }\n\n if (status === \"loading\" || status === \"idle\") {\n const skeletonCols = Array.from({ length: 53 });\n const skeletonRows = Array.from({ length: 7 });\n return (\n <div data-git-heatmap=\"\" style={{ ...cssVars, opacity: 0.4 }}>\n {showTotal && <div style={{ height: 16, width: 160, background: \"var(--ghm-text)\", borderRadius: 4, marginBottom: 12, opacity: 0.2 }} />}\n <div style={{ display: \"flex\", gap: cellGap }}>\n {skeletonCols.map((_, i) => (\n <div key={i} style={{ display: \"flex\", flexDirection: \"column\", gap: cellGap }}>\n {skeletonRows.map((_, j) => (\n <div key={j} style={{ width: cellSize, height: cellSize, borderRadius: cellRadius, background: \"var(--ghm-color-l0)\" }} />\n ))}\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n if (!data) return null;\n\n const monthLabels = showMonthLabels ? buildMonthLabels(data.weeks) : [];\n\n return (\n <div data-git-heatmap=\"\" ref={wrapperRef} style={cssVars} aria-label={label}>\n\n {showTotal && (\n <p style={{ marginBottom: 12, color: \"var(--ghm-text)\" }}>\n {data.totalContributions.toLocaleString()} contributions in the last year\n </p>\n )}\n\n <div ref={scrollRef} style={{ overflowX: \"auto\", overflowY: \"visible\", paddingBottom: 4 }}>\n <div style={{ display: \"inline-flex\", flexDirection: \"column\", minWidth: data.weeks.length * step }}>\n\n {showMonthLabels && (\n <div style={{ display: \"flex\", marginBottom: cellGap, marginLeft: showDayLabels ? 32 : 0 }}>\n {monthLabels.map(({ label: ml, col }, idx) => {\n const nextCol = monthLabels[idx + 1]?.col ?? data.weeks.length;\n return (\n <div key={ml + col} style={{ width: (nextCol - col) * step, whiteSpace: \"nowrap\", fontSize: \"0.9em\", color: \"var(--ghm-text)\" }}>\n {ml}\n </div>\n );\n })}\n </div>\n )}\n\n <div style={{ display: \"flex\" }}>\n {showDayLabels && (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: cellGap, marginRight: 6 }}>\n {Array.from({ length: 7 }).map((_, dow) => (\n <div key={dow} style={{ height: cellSize, lineHeight: `${cellSize}px`, width: 26, textAlign: \"right\", paddingRight: 4, fontSize: \"0.9em\", color: \"var(--ghm-text)\" }}>\n {DAY_LABELS[dow] ?? \"\"}\n </div>\n ))}\n </div>\n )}\n\n <div style={{ display: \"flex\", gap: cellGap }}>\n {data.weeks.map((week, wi) => (\n <div key={wi} style={{ display: \"flex\", flexDirection: \"column\", gap: cellGap }}>\n {Array.from({ length: 7 }).map((_, dow) => {\n const day = week.days[dow];\n if (!day || !day.date) {\n return <div key={dow} style={{ width: cellSize, height: cellSize }} />;\n }\n return (\n <div\n key={dow}\n style={{\n width: cellSize,\n height: cellSize,\n borderRadius: cellRadius,\n background: levelColor(day.level),\n cursor: \"pointer\",\n transition: \"opacity 0.15s\",\n }}\n onMouseEnter={(e) => {\n const r = (e.currentTarget as HTMLElement).getBoundingClientRect();\n const wr = wrapperRef.current?.getBoundingClientRect();\n if (wr) setTooltip({ day, x: r.left - wr.left + cellSize / 2, y: r.top - wr.top });\n }}\n onMouseLeave={() => setTooltip(null)}\n onClick={() => {\n setTappedDay((prev) => (prev?.date === day.date ? null : day));\n onDayClick?.(day);\n }}\n onMouseOver={(e) => { (e.currentTarget as HTMLElement).style.opacity = \"0.7\"; }}\n onFocus={(e) => { (e.currentTarget as HTMLElement).style.opacity = \"0.7\"; }}\n onMouseOut={(e) => { (e.currentTarget as HTMLElement).style.opacity = \"1\"; }}\n onBlur={(e) => { (e.currentTarget as HTMLElement).style.opacity = \"1\"; }}\n role=\"button\"\n tabIndex={0}\n aria-label={formatTooltip(day)}\n />\n );\n })}\n </div>\n ))}\n </div>\n </div>\n\n </div>\n </div>\n\n {tappedDay && (\n <div style={{ marginTop: 8, fontSize: \"0.9em\", color: \"var(--ghm-text)\", background: \"rgba(255,255,255,0.05)\", border: \"1px solid rgba(255,255,255,0.1)\", borderRadius: 4, padding: \"6px 12px\" }}>\n {formatTooltip(tappedDay)}\n </div>\n )}\n\n {showLegend && (\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"flex-end\", marginTop: 8 }}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, fontSize: \"0.9em\", color: \"var(--ghm-text)\" }}>\n <span>Less</span>\n {levels.map((lvl, i) => (\n <div\n key={i}\n style={{ width: cellSize, height: cellSize, borderRadius: cellRadius, background: `var(--ghm-color-l${i})` }}\n title={lvl.label}\n />\n ))}\n <span>More</span>\n </div>\n </div>\n )}\n\n {tooltip && (\n <div\n style={{\n pointerEvents: \"none\",\n position: \"absolute\",\n zIndex: 50,\n left: tooltip.x,\n top: tooltip.y - 6,\n transform: \"translate(-50%, -100%)\",\n marginBottom: 4,\n background: \"var(--ghm-tooltip-bg)\",\n border: \"1px solid var(--ghm-tooltip-border)\",\n color: \"var(--ghm-tooltip-text)\",\n fontSize: \"0.9em\",\n borderRadius: 4,\n padding: \"3px 8px\",\n whiteSpace: \"nowrap\",\n }}\n >\n {formatTooltip(tooltip.day)}\n <div style={{\n position: \"absolute\",\n left: \"50%\",\n top: \"100%\",\n transform: \"translate(-50%, -50%) rotate(45deg)\",\n width: 8,\n height: 8,\n background: \"var(--ghm-tooltip-bg)\",\n borderRight: \"1px solid var(--ghm-tooltip-border)\",\n borderBottom: \"1px solid var(--ghm-tooltip-border)\",\n }} />\n </div>\n )}\n </div>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport type { HeatmapData } from \"@rsalianto/git-heatmap-core\";\n\nexport type HeatmapStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nexport function useHeatmapData(options: {\n data?: HeatmapData;\n apiUrl?: string;\n fetchData?: () => Promise<HeatmapData>;\n}): { data: HeatmapData | null; status: HeatmapStatus; error: Error | null } {\n const [data, setData] = useState<HeatmapData | null>(options.data ?? null);\n const [status, setStatus] = useState<HeatmapStatus>(options.data ? \"success\" : \"idle\");\n const [error, setError] = useState<Error | null>(null);\n\n const apiUrl = options.apiUrl;\n const fetchData = options.fetchData;\n const staticData = options.data;\n\n const didLoad = useRef(false);\n\n useEffect(() => {\n if (staticData) {\n setData(staticData);\n setStatus(\"success\");\n return;\n }\n\n const resolver: (() => Promise<HeatmapData>) | null =\n fetchData ?? (apiUrl ? () => fetch(apiUrl).then((r) => r.json()) : null);\n\n if (!resolver) return;\n\n let cancelled = false;\n didLoad.current = false;\n setStatus(\"loading\");\n\n resolver()\n .then((d) => {\n if (!cancelled) { setData(d); setStatus(\"success\"); }\n })\n .catch((e: unknown) => {\n if (!cancelled) {\n setError(e instanceof Error ? e : new Error(String(e)));\n setStatus(\"error\");\n }\n });\n\n return () => { cancelled = true; };\n }, [staticData, apiUrl, fetchData]);\n\n return { data, status, error };\n}\n"],"mappings":"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","names":["import_react","_"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/GitHeatmap.tsx","../src/useHeatmapData.ts"],"sourcesContent":["export { GitHeatmap } from \"./GitHeatmap\";\nexport type { GitHeatmapProps } from \"./GitHeatmap\";\nexport { useHeatmapData } from \"./useHeatmapData\";\nexport type { HeatmapStatus } from \"./useHeatmapData\";\n","\"use client\";\nimport { useEffect, useRef, useState } from \"react\";\nimport {\n buildMonthLabels,\n CELL, GAP,\n DAY_LABELS,\n DEFAULT_LEVELS,\n DEFAULT_THEME,\n} from \"@rsalianto/git-heatmap-core\";\nimport type { HeatmapData, HeatmapDay, HeatmapTheme, LevelConfig } from \"@rsalianto/git-heatmap-core\";\nimport { useHeatmapData } from \"./useHeatmapData\";\n\nexport interface GitHeatmapProps {\n data?: HeatmapData;\n apiUrl?: string;\n fetchData?: () => Promise<HeatmapData>;\n levels?: LevelConfig[];\n cellSize?: number;\n cellGap?: number;\n cellRadius?: number;\n showTotal?: boolean;\n showLegend?: boolean;\n showMonthLabels?: boolean;\n showDayLabels?: boolean;\n theme?: Partial<HeatmapTheme>;\n onDayClick?: (day: HeatmapDay) => void;\n label?: string;\n}\n\nfunction themeToVars(t: HeatmapTheme): React.CSSProperties {\n return {\n \"--ghm-color-l0\": t.colorL0,\n \"--ghm-color-l1\": t.colorL1,\n \"--ghm-color-l2\": t.colorL2,\n \"--ghm-color-l3\": t.colorL3,\n \"--ghm-color-l4\": t.colorL4,\n \"--ghm-text\": t.textColor,\n \"--ghm-tooltip-bg\": t.tooltipBg,\n \"--ghm-tooltip-border\": t.tooltipBorderColor,\n \"--ghm-tooltip-text\": t.tooltipTextColor,\n \"--ghm-font\": t.fontFamily,\n \"--ghm-fs\": t.fontSize,\n } as React.CSSProperties;\n}\n\nconst BASE_STYLE = `\n[data-git-heatmap] {\n --ghm-color-l0: rgba(255,255,255,0.08);\n --ghm-color-l1: #1c3d06;\n --ghm-color-l2: #3a7510;\n --ghm-color-l3: #6ab81e;\n --ghm-color-l4: #aafd35;\n --ghm-text: rgba(255,255,255,0.5);\n --ghm-tooltip-bg: #1c2128;\n --ghm-tooltip-border: rgba(255,255,255,0.1);\n --ghm-tooltip-text: rgba(255,255,255,0.75);\n --ghm-font: inherit;\n --ghm-fs: 11px;\n display: block;\n position: relative;\n width: 100%;\n font-size: var(--ghm-fs);\n font-family: var(--ghm-font);\n color: var(--ghm-text);\n user-select: none;\n box-sizing: border-box;\n}\n[data-git-heatmap] * { box-sizing: border-box; }\n[data-git-heatmap] rect { transition: opacity 0.15s; }\n[data-git-heatmap] rect:hover { opacity: 0.7; }\n`;\n\nlet styleInjected = false;\nfunction ensureStyle() {\n if (styleInjected || typeof document === \"undefined\") return;\n const el = document.createElement(\"style\");\n el.textContent = BASE_STYLE;\n document.head.appendChild(el);\n styleInjected = true;\n}\n\nconst DAY_LABEL_W = 32;\nconst MONTH_LABEL_H = 16;\n\nexport function GitHeatmap({\n data: dataProp,\n apiUrl,\n fetchData,\n levels = DEFAULT_LEVELS,\n cellSize = CELL,\n cellGap = GAP,\n cellRadius = 2,\n showTotal = true,\n showLegend = true,\n showMonthLabels = true,\n showDayLabels = true,\n theme,\n onDayClick,\n label = \"Contribution heatmap\",\n}: GitHeatmapProps) {\n const step = cellSize + cellGap;\n const { data, status, error } = useHeatmapData({ data: dataProp, apiUrl, fetchData });\n const wrapperRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const [tappedDay, setTappedDay] = useState<HeatmapDay | null>(null);\n const [tooltip, setTooltip] = useState<{ day: HeatmapDay; x: number; y: number } | null>(null);\n\n useEffect(() => { ensureStyle(); }, []);\n useEffect(() => {\n if (data && scrollRef.current) scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;\n }, [data]);\n\n const mergedTheme: HeatmapTheme = { ...DEFAULT_THEME, ...theme };\n const cssVars = themeToVars(mergedTheme);\n\n const formatTooltip = (day: HeatmapDay) =>\n day.count === 0\n ? `No contributions on ${day.date}`\n : `${day.count} contribution${day.count > 1 ? \"s\" : \"\"} on ${day.date}`;\n\n const gridOffsetX = showDayLabels ? DAY_LABEL_W : 0;\n const gridOffsetY = showMonthLabels ? MONTH_LABEL_H + cellGap : 0;\n\n if (error) {\n return (\n <div data-git-heatmap=\"\" style={cssVars}>\n <p style={{ opacity: 0.6, fontSize: \"0.9em\", padding: \"1rem 0\" }}>Could not load contribution data.</p>\n </div>\n );\n }\n\n // ── Skeleton ──────────────────────────────────────────────────────────────\n if (status === \"loading\" || status === \"idle\") {\n const cols = 53;\n const svgW = cols * step + gridOffsetX;\n const svgH = 7 * step - cellGap + gridOffsetY;\n return (\n <div data-git-heatmap=\"\" style={{ ...cssVars, opacity: 0.4 }}>\n {showTotal && <div style={{ height: 14, width: 160, background: \"var(--ghm-text)\", borderRadius: 4, marginBottom: 12, opacity: 0.2 }} />}\n <svg width={svgW} height={svgH} style={{ display: \"block\" }}>\n {Array.from({ length: cols }).map((_, wi) =>\n Array.from({ length: 7 }).map((_, dow) => (\n <rect\n key={`${wi}-${dow}`}\n x={gridOffsetX + wi * step}\n y={gridOffsetY + dow * step}\n width={cellSize}\n height={cellSize}\n rx={cellRadius}\n fill=\"var(--ghm-color-l0)\"\n />\n ))\n )}\n </svg>\n </div>\n );\n }\n\n if (!data) return null;\n\n const monthLabels = showMonthLabels ? buildMonthLabels(data.weeks) : [];\n const svgW = data.weeks.length * step + gridOffsetX;\n const svgH = 7 * step - cellGap + gridOffsetY;\n\n return (\n <div data-git-heatmap=\"\" ref={wrapperRef} style={cssVars} aria-label={label}>\n\n {showTotal && (\n <p style={{ marginBottom: 12, color: \"var(--ghm-text)\" }}>\n {data.totalContributions.toLocaleString()} contributions in the last year\n </p>\n )}\n\n <div ref={scrollRef} style={{ overflowX: \"auto\", overflowY: \"visible\", paddingBottom: 4 }}>\n <svg\n width={svgW}\n height={svgH}\n style={{ display: \"block\", overflow: \"visible\" }}\n role=\"img\"\n aria-label={label}\n >\n {/* Month labels */}\n {showMonthLabels && monthLabels.map(({ label: ml, col }) => (\n <text\n key={ml + col}\n x={gridOffsetX + col * step}\n y={MONTH_LABEL_H - 4}\n fill=\"var(--ghm-text)\"\n fontSize=\"10\"\n >\n {ml}\n </text>\n ))}\n\n {/* Day labels */}\n {showDayLabels && Array.from({ length: 7 }).map((_, dow) =>\n DAY_LABELS[dow] ? (\n <text\n key={dow}\n x={DAY_LABEL_W - 6}\n y={gridOffsetY + dow * step + cellSize}\n fill=\"var(--ghm-text)\"\n fontSize=\"10\"\n textAnchor=\"end\"\n >\n {DAY_LABELS[dow]}\n </text>\n ) : null\n )}\n\n {/* Grid */}\n {data.weeks.map((week, wi) =>\n Array.from({ length: 7 }).map((_, dow) => {\n const day = week.days[dow];\n if (!day?.date) return null;\n const x = gridOffsetX + wi * step;\n const y = gridOffsetY + dow * step;\n return (\n <rect\n key={`${wi}-${dow}`}\n x={x}\n y={y}\n width={cellSize}\n height={cellSize}\n rx={cellRadius}\n fill={`var(--ghm-color-l${day.level})`}\n style={{ cursor: \"pointer\" }}\n aria-label={formatTooltip(day)}\n role=\"button\"\n onMouseEnter={(e) => {\n const r = (e.currentTarget as SVGRectElement).getBoundingClientRect();\n const wr = wrapperRef.current?.getBoundingClientRect();\n if (wr) setTooltip({ day, x: r.left - wr.left + cellSize / 2, y: r.top - wr.top });\n }}\n onMouseLeave={() => setTooltip(null)}\n onClick={() => {\n setTappedDay(prev => prev?.date === day.date ? null : day);\n onDayClick?.(day);\n }}\n />\n );\n })\n )}\n </svg>\n </div>\n\n {tappedDay && (\n <div style={{ marginTop: 8, fontSize: \"0.9em\", color: \"var(--ghm-text)\", background: \"rgba(255,255,255,0.05)\", border: \"1px solid rgba(255,255,255,0.1)\", borderRadius: 4, padding: \"6px 12px\" }}>\n {formatTooltip(tappedDay)}\n </div>\n )}\n\n {showLegend && (\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"flex-end\", marginTop: 8 }}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, fontSize: \"0.9em\", color: \"var(--ghm-text)\" }}>\n <span>Less</span>\n {levels.map((lvl, i) => (\n <svg key={i} width={cellSize} height={cellSize} style={{ display: \"block\" }}>\n <rect width={cellSize} height={cellSize} rx={cellRadius} fill={`var(--ghm-color-l${i})`}>\n <title>{lvl.label}</title>\n </rect>\n </svg>\n ))}\n <span>More</span>\n </div>\n </div>\n )}\n\n {tooltip && (\n <div style={{\n pointerEvents: \"none\",\n position: \"absolute\",\n zIndex: 50,\n left: tooltip.x,\n top: tooltip.y - 6,\n transform: \"translate(-50%, -100%)\",\n background: \"var(--ghm-tooltip-bg)\",\n border: \"1px solid var(--ghm-tooltip-border)\",\n color: \"var(--ghm-tooltip-text)\",\n fontSize: \"0.9em\",\n borderRadius: 4,\n padding: \"3px 8px\",\n whiteSpace: \"nowrap\",\n }}>\n {formatTooltip(tooltip.day)}\n <div style={{\n position: \"absolute\", left: \"50%\", top: \"100%\",\n transform: \"translate(-50%, -50%) rotate(45deg)\",\n width: 8, height: 8,\n background: \"var(--ghm-tooltip-bg)\",\n borderRight: \"1px solid var(--ghm-tooltip-border)\",\n borderBottom: \"1px solid var(--ghm-tooltip-border)\",\n }} />\n </div>\n )}\n </div>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport type { HeatmapData } from \"@rsalianto/git-heatmap-core\";\n\nexport type HeatmapStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nexport function useHeatmapData(options: {\n data?: HeatmapData;\n apiUrl?: string;\n fetchData?: () => Promise<HeatmapData>;\n}): { data: HeatmapData | null; status: HeatmapStatus; error: Error | null } {\n const [data, setData] = useState<HeatmapData | null>(options.data ?? null);\n const [status, setStatus] = useState<HeatmapStatus>(options.data ? \"success\" : \"idle\");\n const [error, setError] = useState<Error | null>(null);\n\n const apiUrl = options.apiUrl;\n const fetchData = options.fetchData;\n const staticData = options.data;\n\n const didLoad = useRef(false);\n\n useEffect(() => {\n if (staticData) {\n setData(staticData);\n setStatus(\"success\");\n return;\n }\n\n const resolver: (() => Promise<HeatmapData>) | null =\n fetchData ?? (apiUrl ? () => fetch(apiUrl).then((r) => r.json()) : null);\n\n if (!resolver) return;\n\n let cancelled = false;\n didLoad.current = false;\n setStatus(\"loading\");\n\n resolver()\n .then((d) => {\n if (!cancelled) { setData(d); setStatus(\"success\"); }\n })\n .catch((e: unknown) => {\n if (!cancelled) {\n setError(e instanceof Error ? e : new Error(String(e)));\n setStatus(\"error\");\n }\n });\n\n return () => { cancelled = true; };\n }, [staticData, apiUrl, fetchData]);\n\n return { data, status, error };\n}\n"],"mappings":"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","names":["import_react","svgW","svgH","_"]}
package/dist/index.mjs CHANGED
@@ -50,19 +50,19 @@ function useHeatmapData(options) {
50
50
 
51
51
  // src/GitHeatmap.tsx
52
52
  import { jsx, jsxs } from "react/jsx-runtime";
53
- function themeToVars(theme) {
53
+ function themeToVars(t) {
54
54
  return {
55
- "--ghm-color-l0": theme.colorL0,
56
- "--ghm-color-l1": theme.colorL1,
57
- "--ghm-color-l2": theme.colorL2,
58
- "--ghm-color-l3": theme.colorL3,
59
- "--ghm-color-l4": theme.colorL4,
60
- "--ghm-text": theme.textColor,
61
- "--ghm-tooltip-bg": theme.tooltipBg,
62
- "--ghm-tooltip-border": theme.tooltipBorderColor,
63
- "--ghm-tooltip-text": theme.tooltipTextColor,
64
- "--ghm-font": theme.fontFamily,
65
- "--ghm-fs": theme.fontSize
55
+ "--ghm-color-l0": t.colorL0,
56
+ "--ghm-color-l1": t.colorL1,
57
+ "--ghm-color-l2": t.colorL2,
58
+ "--ghm-color-l3": t.colorL3,
59
+ "--ghm-color-l4": t.colorL4,
60
+ "--ghm-text": t.textColor,
61
+ "--ghm-tooltip-bg": t.tooltipBg,
62
+ "--ghm-tooltip-border": t.tooltipBorderColor,
63
+ "--ghm-tooltip-text": t.tooltipTextColor,
64
+ "--ghm-font": t.fontFamily,
65
+ "--ghm-fs": t.fontSize
66
66
  };
67
67
  }
68
68
  var BASE_STYLE = `
@@ -88,6 +88,8 @@ var BASE_STYLE = `
88
88
  box-sizing: border-box;
89
89
  }
90
90
  [data-git-heatmap] * { box-sizing: border-box; }
91
+ [data-git-heatmap] rect { transition: opacity 0.15s; }
92
+ [data-git-heatmap] rect:hover { opacity: 0.7; }
91
93
  `;
92
94
  var styleInjected = false;
93
95
  function ensureStyle() {
@@ -97,9 +99,8 @@ function ensureStyle() {
97
99
  document.head.appendChild(el);
98
100
  styleInjected = true;
99
101
  }
100
- function levelColor(level) {
101
- return `var(--ghm-color-l${level})`;
102
- }
102
+ var DAY_LABEL_W = 32;
103
+ var MONTH_LABEL_H = 16;
103
104
  function GitHeatmap({
104
105
  data: dataProp,
105
106
  apiUrl,
@@ -118,141 +119,159 @@ function GitHeatmap({
118
119
  }) {
119
120
  const step = cellSize + cellGap;
120
121
  const { data, status, error } = useHeatmapData({ data: dataProp, apiUrl, fetchData });
121
- const scrollRef = useRef2(null);
122
122
  const wrapperRef = useRef2(null);
123
+ const scrollRef = useRef2(null);
123
124
  const [tappedDay, setTappedDay] = useState2(null);
124
125
  const [tooltip, setTooltip] = useState2(null);
125
126
  useEffect2(() => {
126
127
  ensureStyle();
127
128
  }, []);
128
129
  useEffect2(() => {
129
- if (data && scrollRef.current) {
130
- scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;
131
- }
130
+ if (data && scrollRef.current) scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;
132
131
  }, [data]);
133
132
  const mergedTheme = { ...DEFAULT_THEME, ...theme };
134
133
  const cssVars = themeToVars(mergedTheme);
135
134
  const formatTooltip = (day) => day.count === 0 ? `No contributions on ${day.date}` : `${day.count} contribution${day.count > 1 ? "s" : ""} on ${day.date}`;
135
+ const gridOffsetX = showDayLabels ? DAY_LABEL_W : 0;
136
+ const gridOffsetY = showMonthLabels ? MONTH_LABEL_H + cellGap : 0;
136
137
  if (error) {
137
- return /* @__PURE__ */ jsx("div", { "data-git-heatmap": "", style: cssVars, children: /* @__PURE__ */ jsx("p", { style: { color: "var(--ghm-text)", opacity: 0.6, fontSize: "0.9em", padding: "1rem 0" }, children: "Could not load contribution data." }) });
138
+ return /* @__PURE__ */ jsx("div", { "data-git-heatmap": "", style: cssVars, children: /* @__PURE__ */ jsx("p", { style: { opacity: 0.6, fontSize: "0.9em", padding: "1rem 0" }, children: "Could not load contribution data." }) });
138
139
  }
139
140
  if (status === "loading" || status === "idle") {
140
- const skeletonCols = Array.from({ length: 53 });
141
- const skeletonRows = Array.from({ length: 7 });
141
+ const cols = 53;
142
+ const svgW2 = cols * step + gridOffsetX;
143
+ const svgH2 = 7 * step - cellGap + gridOffsetY;
142
144
  return /* @__PURE__ */ jsxs("div", { "data-git-heatmap": "", style: { ...cssVars, opacity: 0.4 }, children: [
143
- showTotal && /* @__PURE__ */ jsx("div", { style: { height: 16, width: 160, background: "var(--ghm-text)", borderRadius: 4, marginBottom: 12, opacity: 0.2 } }),
144
- /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: cellGap }, children: skeletonCols.map((_, i) => /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: cellGap }, children: skeletonRows.map((_2, j) => /* @__PURE__ */ jsx("div", { style: { width: cellSize, height: cellSize, borderRadius: cellRadius, background: "var(--ghm-color-l0)" } }, j)) }, i)) })
145
+ showTotal && /* @__PURE__ */ jsx("div", { style: { height: 14, width: 160, background: "var(--ghm-text)", borderRadius: 4, marginBottom: 12, opacity: 0.2 } }),
146
+ /* @__PURE__ */ jsx("svg", { width: svgW2, height: svgH2, style: { display: "block" }, children: Array.from({ length: cols }).map(
147
+ (_, wi) => Array.from({ length: 7 }).map((_2, dow) => /* @__PURE__ */ jsx(
148
+ "rect",
149
+ {
150
+ x: gridOffsetX + wi * step,
151
+ y: gridOffsetY + dow * step,
152
+ width: cellSize,
153
+ height: cellSize,
154
+ rx: cellRadius,
155
+ fill: "var(--ghm-color-l0)"
156
+ },
157
+ `${wi}-${dow}`
158
+ ))
159
+ ) })
145
160
  ] });
146
161
  }
147
162
  if (!data) return null;
148
163
  const monthLabels = showMonthLabels ? buildMonthLabels(data.weeks) : [];
164
+ const svgW = data.weeks.length * step + gridOffsetX;
165
+ const svgH = 7 * step - cellGap + gridOffsetY;
149
166
  return /* @__PURE__ */ jsxs("div", { "data-git-heatmap": "", ref: wrapperRef, style: cssVars, "aria-label": label, children: [
150
167
  showTotal && /* @__PURE__ */ jsxs("p", { style: { marginBottom: 12, color: "var(--ghm-text)" }, children: [
151
168
  data.totalContributions.toLocaleString(),
152
169
  " contributions in the last year"
153
170
  ] }),
154
- /* @__PURE__ */ jsx("div", { ref: scrollRef, style: { overflowX: "auto", overflowY: "visible", paddingBottom: 4 }, children: /* @__PURE__ */ jsxs("div", { style: { display: "inline-flex", flexDirection: "column", minWidth: data.weeks.length * step }, children: [
155
- showMonthLabels && /* @__PURE__ */ jsx("div", { style: { display: "flex", marginBottom: cellGap, marginLeft: showDayLabels ? 32 : 0 }, children: monthLabels.map(({ label: ml, col }, idx) => {
156
- const nextCol = monthLabels[idx + 1]?.col ?? data.weeks.length;
157
- return /* @__PURE__ */ jsx("div", { style: { width: (nextCol - col) * step, whiteSpace: "nowrap", fontSize: "0.9em", color: "var(--ghm-text)" }, children: ml }, ml + col);
158
- }) }),
159
- /* @__PURE__ */ jsxs("div", { style: { display: "flex" }, children: [
160
- showDayLabels && /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: cellGap, marginRight: 6 }, children: Array.from({ length: 7 }).map((_, dow) => /* @__PURE__ */ jsx("div", { style: { height: cellSize, lineHeight: `${cellSize}px`, width: 26, textAlign: "right", paddingRight: 4, fontSize: "0.9em", color: "var(--ghm-text)" }, children: DAY_LABELS[dow] ?? "" }, dow)) }),
161
- /* @__PURE__ */ jsx("div", { style: { display: "flex", gap: cellGap }, children: data.weeks.map((week, wi) => /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: cellGap }, children: Array.from({ length: 7 }).map((_, dow) => {
162
- const day = week.days[dow];
163
- if (!day || !day.date) {
164
- return /* @__PURE__ */ jsx("div", { style: { width: cellSize, height: cellSize } }, dow);
165
- }
166
- return /* @__PURE__ */ jsx(
167
- "div",
171
+ /* @__PURE__ */ jsx("div", { ref: scrollRef, style: { overflowX: "auto", overflowY: "visible", paddingBottom: 4 }, children: /* @__PURE__ */ jsxs(
172
+ "svg",
173
+ {
174
+ width: svgW,
175
+ height: svgH,
176
+ style: { display: "block", overflow: "visible" },
177
+ role: "img",
178
+ "aria-label": label,
179
+ children: [
180
+ showMonthLabels && monthLabels.map(({ label: ml, col }) => /* @__PURE__ */ jsx(
181
+ "text",
168
182
  {
169
- style: {
170
- width: cellSize,
171
- height: cellSize,
172
- borderRadius: cellRadius,
173
- background: levelColor(day.level),
174
- cursor: "pointer",
175
- transition: "opacity 0.15s"
176
- },
177
- onMouseEnter: (e) => {
178
- const r = e.currentTarget.getBoundingClientRect();
179
- const wr = wrapperRef.current?.getBoundingClientRect();
180
- if (wr) setTooltip({ day, x: r.left - wr.left + cellSize / 2, y: r.top - wr.top });
181
- },
182
- onMouseLeave: () => setTooltip(null),
183
- onClick: () => {
184
- setTappedDay((prev) => prev?.date === day.date ? null : day);
185
- onDayClick?.(day);
186
- },
187
- onMouseOver: (e) => {
188
- e.currentTarget.style.opacity = "0.7";
189
- },
190
- onFocus: (e) => {
191
- e.currentTarget.style.opacity = "0.7";
192
- },
193
- onMouseOut: (e) => {
194
- e.currentTarget.style.opacity = "1";
195
- },
196
- onBlur: (e) => {
197
- e.currentTarget.style.opacity = "1";
198
- },
199
- role: "button",
200
- tabIndex: 0,
201
- "aria-label": formatTooltip(day)
183
+ x: gridOffsetX + col * step,
184
+ y: MONTH_LABEL_H - 4,
185
+ fill: "var(--ghm-text)",
186
+ fontSize: "10",
187
+ children: ml
202
188
  },
203
- dow
204
- );
205
- }) }, wi)) })
206
- ] })
207
- ] }) }),
189
+ ml + col
190
+ )),
191
+ showDayLabels && Array.from({ length: 7 }).map(
192
+ (_, dow) => DAY_LABELS[dow] ? /* @__PURE__ */ jsx(
193
+ "text",
194
+ {
195
+ x: DAY_LABEL_W - 6,
196
+ y: gridOffsetY + dow * step + cellSize,
197
+ fill: "var(--ghm-text)",
198
+ fontSize: "10",
199
+ textAnchor: "end",
200
+ children: DAY_LABELS[dow]
201
+ },
202
+ dow
203
+ ) : null
204
+ ),
205
+ data.weeks.map(
206
+ (week, wi) => Array.from({ length: 7 }).map((_, dow) => {
207
+ const day = week.days[dow];
208
+ if (!day?.date) return null;
209
+ const x = gridOffsetX + wi * step;
210
+ const y = gridOffsetY + dow * step;
211
+ return /* @__PURE__ */ jsx(
212
+ "rect",
213
+ {
214
+ x,
215
+ y,
216
+ width: cellSize,
217
+ height: cellSize,
218
+ rx: cellRadius,
219
+ fill: `var(--ghm-color-l${day.level})`,
220
+ style: { cursor: "pointer" },
221
+ "aria-label": formatTooltip(day),
222
+ role: "button",
223
+ onMouseEnter: (e) => {
224
+ const r = e.currentTarget.getBoundingClientRect();
225
+ const wr = wrapperRef.current?.getBoundingClientRect();
226
+ if (wr) setTooltip({ day, x: r.left - wr.left + cellSize / 2, y: r.top - wr.top });
227
+ },
228
+ onMouseLeave: () => setTooltip(null),
229
+ onClick: () => {
230
+ setTappedDay((prev) => prev?.date === day.date ? null : day);
231
+ onDayClick?.(day);
232
+ }
233
+ },
234
+ `${wi}-${dow}`
235
+ );
236
+ })
237
+ )
238
+ ]
239
+ }
240
+ ) }),
208
241
  tappedDay && /* @__PURE__ */ jsx("div", { style: { marginTop: 8, fontSize: "0.9em", color: "var(--ghm-text)", background: "rgba(255,255,255,0.05)", border: "1px solid rgba(255,255,255,0.1)", borderRadius: 4, padding: "6px 12px" }, children: formatTooltip(tappedDay) }),
209
242
  showLegend && /* @__PURE__ */ jsx("div", { style: { display: "flex", alignItems: "center", justifyContent: "flex-end", marginTop: 8 }, children: /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: "0.9em", color: "var(--ghm-text)" }, children: [
210
243
  /* @__PURE__ */ jsx("span", { children: "Less" }),
211
- levels.map((lvl, i) => /* @__PURE__ */ jsx(
212
- "div",
213
- {
214
- style: { width: cellSize, height: cellSize, borderRadius: cellRadius, background: `var(--ghm-color-l${i})` },
215
- title: lvl.label
216
- },
217
- i
218
- )),
244
+ levels.map((lvl, i) => /* @__PURE__ */ jsx("svg", { width: cellSize, height: cellSize, style: { display: "block" }, children: /* @__PURE__ */ jsx("rect", { width: cellSize, height: cellSize, rx: cellRadius, fill: `var(--ghm-color-l${i})`, children: /* @__PURE__ */ jsx("title", { children: lvl.label }) }) }, i)),
219
245
  /* @__PURE__ */ jsx("span", { children: "More" })
220
246
  ] }) }),
221
- tooltip && /* @__PURE__ */ jsxs(
222
- "div",
223
- {
224
- style: {
225
- pointerEvents: "none",
226
- position: "absolute",
227
- zIndex: 50,
228
- left: tooltip.x,
229
- top: tooltip.y - 6,
230
- transform: "translate(-50%, -100%)",
231
- marginBottom: 4,
232
- background: "var(--ghm-tooltip-bg)",
233
- border: "1px solid var(--ghm-tooltip-border)",
234
- color: "var(--ghm-tooltip-text)",
235
- fontSize: "0.9em",
236
- borderRadius: 4,
237
- padding: "3px 8px",
238
- whiteSpace: "nowrap"
239
- },
240
- children: [
241
- formatTooltip(tooltip.day),
242
- /* @__PURE__ */ jsx("div", { style: {
243
- position: "absolute",
244
- left: "50%",
245
- top: "100%",
246
- transform: "translate(-50%, -50%) rotate(45deg)",
247
- width: 8,
248
- height: 8,
249
- background: "var(--ghm-tooltip-bg)",
250
- borderRight: "1px solid var(--ghm-tooltip-border)",
251
- borderBottom: "1px solid var(--ghm-tooltip-border)"
252
- } })
253
- ]
254
- }
255
- )
247
+ tooltip && /* @__PURE__ */ jsxs("div", { style: {
248
+ pointerEvents: "none",
249
+ position: "absolute",
250
+ zIndex: 50,
251
+ left: tooltip.x,
252
+ top: tooltip.y - 6,
253
+ transform: "translate(-50%, -100%)",
254
+ background: "var(--ghm-tooltip-bg)",
255
+ border: "1px solid var(--ghm-tooltip-border)",
256
+ color: "var(--ghm-tooltip-text)",
257
+ fontSize: "0.9em",
258
+ borderRadius: 4,
259
+ padding: "3px 8px",
260
+ whiteSpace: "nowrap"
261
+ }, children: [
262
+ formatTooltip(tooltip.day),
263
+ /* @__PURE__ */ jsx("div", { style: {
264
+ position: "absolute",
265
+ left: "50%",
266
+ top: "100%",
267
+ transform: "translate(-50%, -50%) rotate(45deg)",
268
+ width: 8,
269
+ height: 8,
270
+ background: "var(--ghm-tooltip-bg)",
271
+ borderRight: "1px solid var(--ghm-tooltip-border)",
272
+ borderBottom: "1px solid var(--ghm-tooltip-border)"
273
+ } })
274
+ ] })
256
275
  ] });
257
276
  }
258
277
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/GitHeatmap.tsx","../src/useHeatmapData.ts"],"sourcesContent":["\"use client\";\nimport { useEffect, useRef, useState } from \"react\";\nimport {\n buildMonthLabels,\n CELL, GAP, STEP,\n DAY_LABELS,\n DEFAULT_LEVELS,\n DEFAULT_THEME,\n} from \"@rsalianto/git-heatmap-core\";\nimport type { HeatmapData, HeatmapDay, HeatmapTheme, LevelConfig } from \"@rsalianto/git-heatmap-core\";\nimport { useHeatmapData } from \"./useHeatmapData\";\n\nexport interface GitHeatmapProps {\n data?: HeatmapData;\n apiUrl?: string;\n fetchData?: () => Promise<HeatmapData>;\n levels?: LevelConfig[];\n cellSize?: number;\n cellGap?: number;\n cellRadius?: number;\n showTotal?: boolean;\n showLegend?: boolean;\n showMonthLabels?: boolean;\n showDayLabels?: boolean;\n theme?: Partial<HeatmapTheme>;\n onDayClick?: (day: HeatmapDay) => void;\n label?: string;\n}\n\nfunction themeToVars(theme: HeatmapTheme): Record<string, string> {\n return {\n \"--ghm-color-l0\": theme.colorL0,\n \"--ghm-color-l1\": theme.colorL1,\n \"--ghm-color-l2\": theme.colorL2,\n \"--ghm-color-l3\": theme.colorL3,\n \"--ghm-color-l4\": theme.colorL4,\n \"--ghm-text\": theme.textColor,\n \"--ghm-tooltip-bg\": theme.tooltipBg,\n \"--ghm-tooltip-border\": theme.tooltipBorderColor,\n \"--ghm-tooltip-text\": theme.tooltipTextColor,\n \"--ghm-font\": theme.fontFamily,\n \"--ghm-fs\": theme.fontSize,\n };\n}\n\nconst BASE_STYLE = `\n[data-git-heatmap] {\n --ghm-color-l0: rgba(255,255,255,0.08);\n --ghm-color-l1: #1c3d06;\n --ghm-color-l2: #3a7510;\n --ghm-color-l3: #6ab81e;\n --ghm-color-l4: #aafd35;\n --ghm-text: rgba(255,255,255,0.5);\n --ghm-tooltip-bg: #1c2128;\n --ghm-tooltip-border: rgba(255,255,255,0.1);\n --ghm-tooltip-text: rgba(255,255,255,0.75);\n --ghm-font: inherit;\n --ghm-fs: 11px;\n display: block;\n position: relative;\n width: 100%;\n font-size: var(--ghm-fs);\n font-family: var(--ghm-font);\n color: var(--ghm-text);\n user-select: none;\n box-sizing: border-box;\n}\n[data-git-heatmap] * { box-sizing: border-box; }\n`;\n\nlet styleInjected = false;\nfunction ensureStyle() {\n if (styleInjected || typeof document === \"undefined\") return;\n const el = document.createElement(\"style\");\n el.textContent = BASE_STYLE;\n document.head.appendChild(el);\n styleInjected = true;\n}\n\nfunction levelColor(level: 0 | 1 | 2 | 3 | 4): string {\n return `var(--ghm-color-l${level})`;\n}\n\nexport function GitHeatmap({\n data: dataProp,\n apiUrl,\n fetchData,\n levels = DEFAULT_LEVELS,\n cellSize = CELL,\n cellGap = GAP,\n cellRadius = 2,\n showTotal = true,\n showLegend = true,\n showMonthLabels = true,\n showDayLabels = true,\n theme,\n onDayClick,\n label = \"Contribution heatmap\",\n}: GitHeatmapProps) {\n const step = cellSize + cellGap;\n\n const { data, status, error } = useHeatmapData({ data: dataProp, apiUrl, fetchData });\n const scrollRef = useRef<HTMLDivElement>(null);\n const wrapperRef = useRef<HTMLDivElement>(null);\n const [tappedDay, setTappedDay] = useState<HeatmapDay | null>(null);\n const [tooltip, setTooltip] = useState<{ day: HeatmapDay; x: number; y: number } | null>(null);\n\n useEffect(() => { ensureStyle(); }, []);\n\n useEffect(() => {\n if (data && scrollRef.current) {\n scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;\n }\n }, [data]);\n\n const mergedTheme: HeatmapTheme = { ...DEFAULT_THEME, ...theme };\n const cssVars = themeToVars(mergedTheme) as React.CSSProperties;\n\n const formatTooltip = (day: HeatmapDay) =>\n day.count === 0\n ? `No contributions on ${day.date}`\n : `${day.count} contribution${day.count > 1 ? \"s\" : \"\"} on ${day.date}`;\n\n if (error) {\n return (\n <div data-git-heatmap=\"\" style={cssVars}>\n <p style={{ color: \"var(--ghm-text)\", opacity: 0.6, fontSize: \"0.9em\", padding: \"1rem 0\" }}>\n Could not load contribution data.\n </p>\n </div>\n );\n }\n\n if (status === \"loading\" || status === \"idle\") {\n const skeletonCols = Array.from({ length: 53 });\n const skeletonRows = Array.from({ length: 7 });\n return (\n <div data-git-heatmap=\"\" style={{ ...cssVars, opacity: 0.4 }}>\n {showTotal && <div style={{ height: 16, width: 160, background: \"var(--ghm-text)\", borderRadius: 4, marginBottom: 12, opacity: 0.2 }} />}\n <div style={{ display: \"flex\", gap: cellGap }}>\n {skeletonCols.map((_, i) => (\n <div key={i} style={{ display: \"flex\", flexDirection: \"column\", gap: cellGap }}>\n {skeletonRows.map((_, j) => (\n <div key={j} style={{ width: cellSize, height: cellSize, borderRadius: cellRadius, background: \"var(--ghm-color-l0)\" }} />\n ))}\n </div>\n ))}\n </div>\n </div>\n );\n }\n\n if (!data) return null;\n\n const monthLabels = showMonthLabels ? buildMonthLabels(data.weeks) : [];\n\n return (\n <div data-git-heatmap=\"\" ref={wrapperRef} style={cssVars} aria-label={label}>\n\n {showTotal && (\n <p style={{ marginBottom: 12, color: \"var(--ghm-text)\" }}>\n {data.totalContributions.toLocaleString()} contributions in the last year\n </p>\n )}\n\n <div ref={scrollRef} style={{ overflowX: \"auto\", overflowY: \"visible\", paddingBottom: 4 }}>\n <div style={{ display: \"inline-flex\", flexDirection: \"column\", minWidth: data.weeks.length * step }}>\n\n {showMonthLabels && (\n <div style={{ display: \"flex\", marginBottom: cellGap, marginLeft: showDayLabels ? 32 : 0 }}>\n {monthLabels.map(({ label: ml, col }, idx) => {\n const nextCol = monthLabels[idx + 1]?.col ?? data.weeks.length;\n return (\n <div key={ml + col} style={{ width: (nextCol - col) * step, whiteSpace: \"nowrap\", fontSize: \"0.9em\", color: \"var(--ghm-text)\" }}>\n {ml}\n </div>\n );\n })}\n </div>\n )}\n\n <div style={{ display: \"flex\" }}>\n {showDayLabels && (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: cellGap, marginRight: 6 }}>\n {Array.from({ length: 7 }).map((_, dow) => (\n <div key={dow} style={{ height: cellSize, lineHeight: `${cellSize}px`, width: 26, textAlign: \"right\", paddingRight: 4, fontSize: \"0.9em\", color: \"var(--ghm-text)\" }}>\n {DAY_LABELS[dow] ?? \"\"}\n </div>\n ))}\n </div>\n )}\n\n <div style={{ display: \"flex\", gap: cellGap }}>\n {data.weeks.map((week, wi) => (\n <div key={wi} style={{ display: \"flex\", flexDirection: \"column\", gap: cellGap }}>\n {Array.from({ length: 7 }).map((_, dow) => {\n const day = week.days[dow];\n if (!day || !day.date) {\n return <div key={dow} style={{ width: cellSize, height: cellSize }} />;\n }\n return (\n <div\n key={dow}\n style={{\n width: cellSize,\n height: cellSize,\n borderRadius: cellRadius,\n background: levelColor(day.level),\n cursor: \"pointer\",\n transition: \"opacity 0.15s\",\n }}\n onMouseEnter={(e) => {\n const r = (e.currentTarget as HTMLElement).getBoundingClientRect();\n const wr = wrapperRef.current?.getBoundingClientRect();\n if (wr) setTooltip({ day, x: r.left - wr.left + cellSize / 2, y: r.top - wr.top });\n }}\n onMouseLeave={() => setTooltip(null)}\n onClick={() => {\n setTappedDay((prev) => (prev?.date === day.date ? null : day));\n onDayClick?.(day);\n }}\n onMouseOver={(e) => { (e.currentTarget as HTMLElement).style.opacity = \"0.7\"; }}\n onFocus={(e) => { (e.currentTarget as HTMLElement).style.opacity = \"0.7\"; }}\n onMouseOut={(e) => { (e.currentTarget as HTMLElement).style.opacity = \"1\"; }}\n onBlur={(e) => { (e.currentTarget as HTMLElement).style.opacity = \"1\"; }}\n role=\"button\"\n tabIndex={0}\n aria-label={formatTooltip(day)}\n />\n );\n })}\n </div>\n ))}\n </div>\n </div>\n\n </div>\n </div>\n\n {tappedDay && (\n <div style={{ marginTop: 8, fontSize: \"0.9em\", color: \"var(--ghm-text)\", background: \"rgba(255,255,255,0.05)\", border: \"1px solid rgba(255,255,255,0.1)\", borderRadius: 4, padding: \"6px 12px\" }}>\n {formatTooltip(tappedDay)}\n </div>\n )}\n\n {showLegend && (\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"flex-end\", marginTop: 8 }}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, fontSize: \"0.9em\", color: \"var(--ghm-text)\" }}>\n <span>Less</span>\n {levels.map((lvl, i) => (\n <div\n key={i}\n style={{ width: cellSize, height: cellSize, borderRadius: cellRadius, background: `var(--ghm-color-l${i})` }}\n title={lvl.label}\n />\n ))}\n <span>More</span>\n </div>\n </div>\n )}\n\n {tooltip && (\n <div\n style={{\n pointerEvents: \"none\",\n position: \"absolute\",\n zIndex: 50,\n left: tooltip.x,\n top: tooltip.y - 6,\n transform: \"translate(-50%, -100%)\",\n marginBottom: 4,\n background: \"var(--ghm-tooltip-bg)\",\n border: \"1px solid var(--ghm-tooltip-border)\",\n color: \"var(--ghm-tooltip-text)\",\n fontSize: \"0.9em\",\n borderRadius: 4,\n padding: \"3px 8px\",\n whiteSpace: \"nowrap\",\n }}\n >\n {formatTooltip(tooltip.day)}\n <div style={{\n position: \"absolute\",\n left: \"50%\",\n top: \"100%\",\n transform: \"translate(-50%, -50%) rotate(45deg)\",\n width: 8,\n height: 8,\n background: \"var(--ghm-tooltip-bg)\",\n borderRight: \"1px solid var(--ghm-tooltip-border)\",\n borderBottom: \"1px solid var(--ghm-tooltip-border)\",\n }} />\n </div>\n )}\n </div>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport type { HeatmapData } from \"@rsalianto/git-heatmap-core\";\n\nexport type HeatmapStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nexport function useHeatmapData(options: {\n data?: HeatmapData;\n apiUrl?: string;\n fetchData?: () => Promise<HeatmapData>;\n}): { data: HeatmapData | null; status: HeatmapStatus; error: Error | null } {\n const [data, setData] = useState<HeatmapData | null>(options.data ?? null);\n const [status, setStatus] = useState<HeatmapStatus>(options.data ? \"success\" : \"idle\");\n const [error, setError] = useState<Error | null>(null);\n\n const apiUrl = options.apiUrl;\n const fetchData = options.fetchData;\n const staticData = options.data;\n\n const didLoad = useRef(false);\n\n useEffect(() => {\n if (staticData) {\n setData(staticData);\n setStatus(\"success\");\n return;\n }\n\n const resolver: (() => Promise<HeatmapData>) | null =\n fetchData ?? (apiUrl ? () => fetch(apiUrl).then((r) => r.json()) : null);\n\n if (!resolver) return;\n\n let cancelled = false;\n didLoad.current = false;\n setStatus(\"loading\");\n\n resolver()\n .then((d) => {\n if (!cancelled) { setData(d); setStatus(\"success\"); }\n })\n .catch((e: unknown) => {\n if (!cancelled) {\n setError(e instanceof Error ? e : new Error(String(e)));\n setStatus(\"error\");\n }\n });\n\n return () => { cancelled = true; };\n }, [staticData, apiUrl, fetchData]);\n\n return { data, status, error };\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","useRef","useState","useEffect","_"]}
1
+ {"version":3,"sources":["../src/GitHeatmap.tsx","../src/useHeatmapData.ts"],"sourcesContent":["\"use client\";\nimport { useEffect, useRef, useState } from \"react\";\nimport {\n buildMonthLabels,\n CELL, GAP,\n DAY_LABELS,\n DEFAULT_LEVELS,\n DEFAULT_THEME,\n} from \"@rsalianto/git-heatmap-core\";\nimport type { HeatmapData, HeatmapDay, HeatmapTheme, LevelConfig } from \"@rsalianto/git-heatmap-core\";\nimport { useHeatmapData } from \"./useHeatmapData\";\n\nexport interface GitHeatmapProps {\n data?: HeatmapData;\n apiUrl?: string;\n fetchData?: () => Promise<HeatmapData>;\n levels?: LevelConfig[];\n cellSize?: number;\n cellGap?: number;\n cellRadius?: number;\n showTotal?: boolean;\n showLegend?: boolean;\n showMonthLabels?: boolean;\n showDayLabels?: boolean;\n theme?: Partial<HeatmapTheme>;\n onDayClick?: (day: HeatmapDay) => void;\n label?: string;\n}\n\nfunction themeToVars(t: HeatmapTheme): React.CSSProperties {\n return {\n \"--ghm-color-l0\": t.colorL0,\n \"--ghm-color-l1\": t.colorL1,\n \"--ghm-color-l2\": t.colorL2,\n \"--ghm-color-l3\": t.colorL3,\n \"--ghm-color-l4\": t.colorL4,\n \"--ghm-text\": t.textColor,\n \"--ghm-tooltip-bg\": t.tooltipBg,\n \"--ghm-tooltip-border\": t.tooltipBorderColor,\n \"--ghm-tooltip-text\": t.tooltipTextColor,\n \"--ghm-font\": t.fontFamily,\n \"--ghm-fs\": t.fontSize,\n } as React.CSSProperties;\n}\n\nconst BASE_STYLE = `\n[data-git-heatmap] {\n --ghm-color-l0: rgba(255,255,255,0.08);\n --ghm-color-l1: #1c3d06;\n --ghm-color-l2: #3a7510;\n --ghm-color-l3: #6ab81e;\n --ghm-color-l4: #aafd35;\n --ghm-text: rgba(255,255,255,0.5);\n --ghm-tooltip-bg: #1c2128;\n --ghm-tooltip-border: rgba(255,255,255,0.1);\n --ghm-tooltip-text: rgba(255,255,255,0.75);\n --ghm-font: inherit;\n --ghm-fs: 11px;\n display: block;\n position: relative;\n width: 100%;\n font-size: var(--ghm-fs);\n font-family: var(--ghm-font);\n color: var(--ghm-text);\n user-select: none;\n box-sizing: border-box;\n}\n[data-git-heatmap] * { box-sizing: border-box; }\n[data-git-heatmap] rect { transition: opacity 0.15s; }\n[data-git-heatmap] rect:hover { opacity: 0.7; }\n`;\n\nlet styleInjected = false;\nfunction ensureStyle() {\n if (styleInjected || typeof document === \"undefined\") return;\n const el = document.createElement(\"style\");\n el.textContent = BASE_STYLE;\n document.head.appendChild(el);\n styleInjected = true;\n}\n\nconst DAY_LABEL_W = 32;\nconst MONTH_LABEL_H = 16;\n\nexport function GitHeatmap({\n data: dataProp,\n apiUrl,\n fetchData,\n levels = DEFAULT_LEVELS,\n cellSize = CELL,\n cellGap = GAP,\n cellRadius = 2,\n showTotal = true,\n showLegend = true,\n showMonthLabels = true,\n showDayLabels = true,\n theme,\n onDayClick,\n label = \"Contribution heatmap\",\n}: GitHeatmapProps) {\n const step = cellSize + cellGap;\n const { data, status, error } = useHeatmapData({ data: dataProp, apiUrl, fetchData });\n const wrapperRef = useRef<HTMLDivElement>(null);\n const scrollRef = useRef<HTMLDivElement>(null);\n const [tappedDay, setTappedDay] = useState<HeatmapDay | null>(null);\n const [tooltip, setTooltip] = useState<{ day: HeatmapDay; x: number; y: number } | null>(null);\n\n useEffect(() => { ensureStyle(); }, []);\n useEffect(() => {\n if (data && scrollRef.current) scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;\n }, [data]);\n\n const mergedTheme: HeatmapTheme = { ...DEFAULT_THEME, ...theme };\n const cssVars = themeToVars(mergedTheme);\n\n const formatTooltip = (day: HeatmapDay) =>\n day.count === 0\n ? `No contributions on ${day.date}`\n : `${day.count} contribution${day.count > 1 ? \"s\" : \"\"} on ${day.date}`;\n\n const gridOffsetX = showDayLabels ? DAY_LABEL_W : 0;\n const gridOffsetY = showMonthLabels ? MONTH_LABEL_H + cellGap : 0;\n\n if (error) {\n return (\n <div data-git-heatmap=\"\" style={cssVars}>\n <p style={{ opacity: 0.6, fontSize: \"0.9em\", padding: \"1rem 0\" }}>Could not load contribution data.</p>\n </div>\n );\n }\n\n // ── Skeleton ──────────────────────────────────────────────────────────────\n if (status === \"loading\" || status === \"idle\") {\n const cols = 53;\n const svgW = cols * step + gridOffsetX;\n const svgH = 7 * step - cellGap + gridOffsetY;\n return (\n <div data-git-heatmap=\"\" style={{ ...cssVars, opacity: 0.4 }}>\n {showTotal && <div style={{ height: 14, width: 160, background: \"var(--ghm-text)\", borderRadius: 4, marginBottom: 12, opacity: 0.2 }} />}\n <svg width={svgW} height={svgH} style={{ display: \"block\" }}>\n {Array.from({ length: cols }).map((_, wi) =>\n Array.from({ length: 7 }).map((_, dow) => (\n <rect\n key={`${wi}-${dow}`}\n x={gridOffsetX + wi * step}\n y={gridOffsetY + dow * step}\n width={cellSize}\n height={cellSize}\n rx={cellRadius}\n fill=\"var(--ghm-color-l0)\"\n />\n ))\n )}\n </svg>\n </div>\n );\n }\n\n if (!data) return null;\n\n const monthLabels = showMonthLabels ? buildMonthLabels(data.weeks) : [];\n const svgW = data.weeks.length * step + gridOffsetX;\n const svgH = 7 * step - cellGap + gridOffsetY;\n\n return (\n <div data-git-heatmap=\"\" ref={wrapperRef} style={cssVars} aria-label={label}>\n\n {showTotal && (\n <p style={{ marginBottom: 12, color: \"var(--ghm-text)\" }}>\n {data.totalContributions.toLocaleString()} contributions in the last year\n </p>\n )}\n\n <div ref={scrollRef} style={{ overflowX: \"auto\", overflowY: \"visible\", paddingBottom: 4 }}>\n <svg\n width={svgW}\n height={svgH}\n style={{ display: \"block\", overflow: \"visible\" }}\n role=\"img\"\n aria-label={label}\n >\n {/* Month labels */}\n {showMonthLabels && monthLabels.map(({ label: ml, col }) => (\n <text\n key={ml + col}\n x={gridOffsetX + col * step}\n y={MONTH_LABEL_H - 4}\n fill=\"var(--ghm-text)\"\n fontSize=\"10\"\n >\n {ml}\n </text>\n ))}\n\n {/* Day labels */}\n {showDayLabels && Array.from({ length: 7 }).map((_, dow) =>\n DAY_LABELS[dow] ? (\n <text\n key={dow}\n x={DAY_LABEL_W - 6}\n y={gridOffsetY + dow * step + cellSize}\n fill=\"var(--ghm-text)\"\n fontSize=\"10\"\n textAnchor=\"end\"\n >\n {DAY_LABELS[dow]}\n </text>\n ) : null\n )}\n\n {/* Grid */}\n {data.weeks.map((week, wi) =>\n Array.from({ length: 7 }).map((_, dow) => {\n const day = week.days[dow];\n if (!day?.date) return null;\n const x = gridOffsetX + wi * step;\n const y = gridOffsetY + dow * step;\n return (\n <rect\n key={`${wi}-${dow}`}\n x={x}\n y={y}\n width={cellSize}\n height={cellSize}\n rx={cellRadius}\n fill={`var(--ghm-color-l${day.level})`}\n style={{ cursor: \"pointer\" }}\n aria-label={formatTooltip(day)}\n role=\"button\"\n onMouseEnter={(e) => {\n const r = (e.currentTarget as SVGRectElement).getBoundingClientRect();\n const wr = wrapperRef.current?.getBoundingClientRect();\n if (wr) setTooltip({ day, x: r.left - wr.left + cellSize / 2, y: r.top - wr.top });\n }}\n onMouseLeave={() => setTooltip(null)}\n onClick={() => {\n setTappedDay(prev => prev?.date === day.date ? null : day);\n onDayClick?.(day);\n }}\n />\n );\n })\n )}\n </svg>\n </div>\n\n {tappedDay && (\n <div style={{ marginTop: 8, fontSize: \"0.9em\", color: \"var(--ghm-text)\", background: \"rgba(255,255,255,0.05)\", border: \"1px solid rgba(255,255,255,0.1)\", borderRadius: 4, padding: \"6px 12px\" }}>\n {formatTooltip(tappedDay)}\n </div>\n )}\n\n {showLegend && (\n <div style={{ display: \"flex\", alignItems: \"center\", justifyContent: \"flex-end\", marginTop: 8 }}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 6, fontSize: \"0.9em\", color: \"var(--ghm-text)\" }}>\n <span>Less</span>\n {levels.map((lvl, i) => (\n <svg key={i} width={cellSize} height={cellSize} style={{ display: \"block\" }}>\n <rect width={cellSize} height={cellSize} rx={cellRadius} fill={`var(--ghm-color-l${i})`}>\n <title>{lvl.label}</title>\n </rect>\n </svg>\n ))}\n <span>More</span>\n </div>\n </div>\n )}\n\n {tooltip && (\n <div style={{\n pointerEvents: \"none\",\n position: \"absolute\",\n zIndex: 50,\n left: tooltip.x,\n top: tooltip.y - 6,\n transform: \"translate(-50%, -100%)\",\n background: \"var(--ghm-tooltip-bg)\",\n border: \"1px solid var(--ghm-tooltip-border)\",\n color: \"var(--ghm-tooltip-text)\",\n fontSize: \"0.9em\",\n borderRadius: 4,\n padding: \"3px 8px\",\n whiteSpace: \"nowrap\",\n }}>\n {formatTooltip(tooltip.day)}\n <div style={{\n position: \"absolute\", left: \"50%\", top: \"100%\",\n transform: \"translate(-50%, -50%) rotate(45deg)\",\n width: 8, height: 8,\n background: \"var(--ghm-tooltip-bg)\",\n borderRight: \"1px solid var(--ghm-tooltip-border)\",\n borderBottom: \"1px solid var(--ghm-tooltip-border)\",\n }} />\n </div>\n )}\n </div>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\nimport type { HeatmapData } from \"@rsalianto/git-heatmap-core\";\n\nexport type HeatmapStatus = \"idle\" | \"loading\" | \"success\" | \"error\";\n\nexport function useHeatmapData(options: {\n data?: HeatmapData;\n apiUrl?: string;\n fetchData?: () => Promise<HeatmapData>;\n}): { data: HeatmapData | null; status: HeatmapStatus; error: Error | null } {\n const [data, setData] = useState<HeatmapData | null>(options.data ?? null);\n const [status, setStatus] = useState<HeatmapStatus>(options.data ? \"success\" : \"idle\");\n const [error, setError] = useState<Error | null>(null);\n\n const apiUrl = options.apiUrl;\n const fetchData = options.fetchData;\n const staticData = options.data;\n\n const didLoad = useRef(false);\n\n useEffect(() => {\n if (staticData) {\n setData(staticData);\n setStatus(\"success\");\n return;\n }\n\n const resolver: (() => Promise<HeatmapData>) | null =\n fetchData ?? (apiUrl ? () => fetch(apiUrl).then((r) => r.json()) : null);\n\n if (!resolver) return;\n\n let cancelled = false;\n didLoad.current = false;\n setStatus(\"loading\");\n\n resolver()\n .then((d) => {\n if (!cancelled) { setData(d); setStatus(\"success\"); }\n })\n .catch((e: unknown) => {\n if (!cancelled) {\n setError(e instanceof Error ? e : new Error(String(e)));\n setStatus(\"error\");\n }\n });\n\n return () => { cancelled = true; };\n }, [staticData, apiUrl, fetchData]);\n\n return { data, status, error };\n}\n"],"mappings":"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","names":["useEffect","useRef","useState","useRef","useState","useEffect","svgW","svgH","_"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsalianto/git-heatmap-react",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "React component for git contribution heatmap",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
@@ -13,7 +13,10 @@
13
13
  "require": "./dist/index.js"
14
14
  }
15
15
  },
16
- "files": ["dist", "README.md"],
16
+ "files": [
17
+ "dist",
18
+ "README.md"
19
+ ],
17
20
  "sideEffects": false,
18
21
  "scripts": {
19
22
  "build": "tsup",