@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 +95 -0
- package/dist/index.js +138 -119
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +138 -119
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -2
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(
|
|
73
|
+
function themeToVars(t) {
|
|
74
74
|
return {
|
|
75
|
-
"--ghm-color-l0":
|
|
76
|
-
"--ghm-color-l1":
|
|
77
|
-
"--ghm-color-l2":
|
|
78
|
-
"--ghm-color-l3":
|
|
79
|
-
"--ghm-color-l4":
|
|
80
|
-
"--ghm-text":
|
|
81
|
-
"--ghm-tooltip-bg":
|
|
82
|
-
"--ghm-tooltip-border":
|
|
83
|
-
"--ghm-tooltip-text":
|
|
84
|
-
"--ghm-font":
|
|
85
|
-
"--ghm-fs":
|
|
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
|
-
|
|
121
|
-
|
|
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: {
|
|
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
|
|
161
|
-
const
|
|
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:
|
|
164
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("
|
|
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)(
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
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
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
224
|
-
)
|
|
225
|
-
|
|
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
|
-
"
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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(
|
|
53
|
+
function themeToVars(t) {
|
|
54
54
|
return {
|
|
55
|
-
"--ghm-color-l0":
|
|
56
|
-
"--ghm-color-l1":
|
|
57
|
-
"--ghm-color-l2":
|
|
58
|
-
"--ghm-color-l3":
|
|
59
|
-
"--ghm-color-l4":
|
|
60
|
-
"--ghm-text":
|
|
61
|
-
"--ghm-tooltip-bg":
|
|
62
|
-
"--ghm-tooltip-border":
|
|
63
|
-
"--ghm-tooltip-text":
|
|
64
|
-
"--ghm-font":
|
|
65
|
-
"--ghm-fs":
|
|
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
|
-
|
|
101
|
-
|
|
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: {
|
|
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
|
|
141
|
-
const
|
|
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:
|
|
144
|
-
/* @__PURE__ */ jsx("
|
|
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(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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
|
-
|
|
204
|
-
)
|
|
205
|
-
|
|
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
|
-
"
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
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 {
|
package/dist/index.mjs.map
CHANGED
|
@@ -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.
|
|
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": [
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
17
20
|
"sideEffects": false,
|
|
18
21
|
"scripts": {
|
|
19
22
|
"build": "tsup",
|