@rsalianto/git-heatmap-react 0.1.0
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/dist/index.d.mts +33 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +283 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +262 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +40 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HeatmapData, LevelConfig, HeatmapTheme, HeatmapDay } from '@rsalianto/git-heatmap-core';
|
|
3
|
+
|
|
4
|
+
interface GitHeatmapProps {
|
|
5
|
+
data?: HeatmapData;
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
fetchData?: () => Promise<HeatmapData>;
|
|
8
|
+
levels?: LevelConfig[];
|
|
9
|
+
cellSize?: number;
|
|
10
|
+
cellGap?: number;
|
|
11
|
+
cellRadius?: number;
|
|
12
|
+
showTotal?: boolean;
|
|
13
|
+
showLegend?: boolean;
|
|
14
|
+
showMonthLabels?: boolean;
|
|
15
|
+
showDayLabels?: boolean;
|
|
16
|
+
theme?: Partial<HeatmapTheme>;
|
|
17
|
+
onDayClick?: (day: HeatmapDay) => void;
|
|
18
|
+
label?: string;
|
|
19
|
+
}
|
|
20
|
+
declare function GitHeatmap({ data: dataProp, apiUrl, fetchData, levels, cellSize, cellGap, cellRadius, showTotal, showLegend, showMonthLabels, showDayLabels, theme, onDayClick, label, }: GitHeatmapProps): react.JSX.Element | null;
|
|
21
|
+
|
|
22
|
+
type HeatmapStatus = "idle" | "loading" | "success" | "error";
|
|
23
|
+
declare function useHeatmapData(options: {
|
|
24
|
+
data?: HeatmapData;
|
|
25
|
+
apiUrl?: string;
|
|
26
|
+
fetchData?: () => Promise<HeatmapData>;
|
|
27
|
+
}): {
|
|
28
|
+
data: HeatmapData | null;
|
|
29
|
+
status: HeatmapStatus;
|
|
30
|
+
error: Error | null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { GitHeatmap, type GitHeatmapProps, type HeatmapStatus, useHeatmapData };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import { HeatmapData, LevelConfig, HeatmapTheme, HeatmapDay } from '@rsalianto/git-heatmap-core';
|
|
3
|
+
|
|
4
|
+
interface GitHeatmapProps {
|
|
5
|
+
data?: HeatmapData;
|
|
6
|
+
apiUrl?: string;
|
|
7
|
+
fetchData?: () => Promise<HeatmapData>;
|
|
8
|
+
levels?: LevelConfig[];
|
|
9
|
+
cellSize?: number;
|
|
10
|
+
cellGap?: number;
|
|
11
|
+
cellRadius?: number;
|
|
12
|
+
showTotal?: boolean;
|
|
13
|
+
showLegend?: boolean;
|
|
14
|
+
showMonthLabels?: boolean;
|
|
15
|
+
showDayLabels?: boolean;
|
|
16
|
+
theme?: Partial<HeatmapTheme>;
|
|
17
|
+
onDayClick?: (day: HeatmapDay) => void;
|
|
18
|
+
label?: string;
|
|
19
|
+
}
|
|
20
|
+
declare function GitHeatmap({ data: dataProp, apiUrl, fetchData, levels, cellSize, cellGap, cellRadius, showTotal, showLegend, showMonthLabels, showDayLabels, theme, onDayClick, label, }: GitHeatmapProps): react.JSX.Element | null;
|
|
21
|
+
|
|
22
|
+
type HeatmapStatus = "idle" | "loading" | "success" | "error";
|
|
23
|
+
declare function useHeatmapData(options: {
|
|
24
|
+
data?: HeatmapData;
|
|
25
|
+
apiUrl?: string;
|
|
26
|
+
fetchData?: () => Promise<HeatmapData>;
|
|
27
|
+
}): {
|
|
28
|
+
data: HeatmapData | null;
|
|
29
|
+
status: HeatmapStatus;
|
|
30
|
+
error: Error | null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export { GitHeatmap, type GitHeatmapProps, type HeatmapStatus, useHeatmapData };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
GitHeatmap: () => GitHeatmap,
|
|
24
|
+
useHeatmapData: () => useHeatmapData
|
|
25
|
+
});
|
|
26
|
+
module.exports = __toCommonJS(index_exports);
|
|
27
|
+
|
|
28
|
+
// src/GitHeatmap.tsx
|
|
29
|
+
var import_react2 = require("react");
|
|
30
|
+
var import_git_heatmap_core = require("@rsalianto/git-heatmap-core");
|
|
31
|
+
|
|
32
|
+
// src/useHeatmapData.ts
|
|
33
|
+
var import_react = require("react");
|
|
34
|
+
function useHeatmapData(options) {
|
|
35
|
+
const [data, setData] = (0, import_react.useState)(options.data ?? null);
|
|
36
|
+
const [status, setStatus] = (0, import_react.useState)(options.data ? "success" : "idle");
|
|
37
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
38
|
+
const apiUrl = options.apiUrl;
|
|
39
|
+
const fetchData = options.fetchData;
|
|
40
|
+
const staticData = options.data;
|
|
41
|
+
const didLoad = (0, import_react.useRef)(false);
|
|
42
|
+
(0, import_react.useEffect)(() => {
|
|
43
|
+
if (staticData) {
|
|
44
|
+
setData(staticData);
|
|
45
|
+
setStatus("success");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const resolver = fetchData ?? (apiUrl ? () => fetch(apiUrl).then((r) => r.json()) : null);
|
|
49
|
+
if (!resolver) return;
|
|
50
|
+
let cancelled = false;
|
|
51
|
+
didLoad.current = false;
|
|
52
|
+
setStatus("loading");
|
|
53
|
+
resolver().then((d) => {
|
|
54
|
+
if (!cancelled) {
|
|
55
|
+
setData(d);
|
|
56
|
+
setStatus("success");
|
|
57
|
+
}
|
|
58
|
+
}).catch((e) => {
|
|
59
|
+
if (!cancelled) {
|
|
60
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
61
|
+
setStatus("error");
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
return () => {
|
|
65
|
+
cancelled = true;
|
|
66
|
+
};
|
|
67
|
+
}, [staticData, apiUrl, fetchData]);
|
|
68
|
+
return { data, status, error };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/GitHeatmap.tsx
|
|
72
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
73
|
+
function themeToVars(theme) {
|
|
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
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
var BASE_STYLE = `
|
|
89
|
+
[data-git-heatmap] {
|
|
90
|
+
--ghm-color-l0: rgba(255,255,255,0.08);
|
|
91
|
+
--ghm-color-l1: #1c3d06;
|
|
92
|
+
--ghm-color-l2: #3a7510;
|
|
93
|
+
--ghm-color-l3: #6ab81e;
|
|
94
|
+
--ghm-color-l4: #aafd35;
|
|
95
|
+
--ghm-text: rgba(255,255,255,0.5);
|
|
96
|
+
--ghm-tooltip-bg: #1c2128;
|
|
97
|
+
--ghm-tooltip-border: rgba(255,255,255,0.1);
|
|
98
|
+
--ghm-tooltip-text: rgba(255,255,255,0.75);
|
|
99
|
+
--ghm-font: inherit;
|
|
100
|
+
--ghm-fs: 11px;
|
|
101
|
+
display: block;
|
|
102
|
+
position: relative;
|
|
103
|
+
width: 100%;
|
|
104
|
+
font-size: var(--ghm-fs);
|
|
105
|
+
font-family: var(--ghm-font);
|
|
106
|
+
color: var(--ghm-text);
|
|
107
|
+
user-select: none;
|
|
108
|
+
box-sizing: border-box;
|
|
109
|
+
}
|
|
110
|
+
[data-git-heatmap] * { box-sizing: border-box; }
|
|
111
|
+
`;
|
|
112
|
+
var styleInjected = false;
|
|
113
|
+
function ensureStyle() {
|
|
114
|
+
if (styleInjected || typeof document === "undefined") return;
|
|
115
|
+
const el = document.createElement("style");
|
|
116
|
+
el.textContent = BASE_STYLE;
|
|
117
|
+
document.head.appendChild(el);
|
|
118
|
+
styleInjected = true;
|
|
119
|
+
}
|
|
120
|
+
function levelColor(level) {
|
|
121
|
+
return `var(--ghm-color-l${level})`;
|
|
122
|
+
}
|
|
123
|
+
function GitHeatmap({
|
|
124
|
+
data: dataProp,
|
|
125
|
+
apiUrl,
|
|
126
|
+
fetchData,
|
|
127
|
+
levels = import_git_heatmap_core.DEFAULT_LEVELS,
|
|
128
|
+
cellSize = import_git_heatmap_core.CELL,
|
|
129
|
+
cellGap = import_git_heatmap_core.GAP,
|
|
130
|
+
cellRadius = 2,
|
|
131
|
+
showTotal = true,
|
|
132
|
+
showLegend = true,
|
|
133
|
+
showMonthLabels = true,
|
|
134
|
+
showDayLabels = true,
|
|
135
|
+
theme,
|
|
136
|
+
onDayClick,
|
|
137
|
+
label = "Contribution heatmap"
|
|
138
|
+
}) {
|
|
139
|
+
const step = cellSize + cellGap;
|
|
140
|
+
const { data, status, error } = useHeatmapData({ data: dataProp, apiUrl, fetchData });
|
|
141
|
+
const scrollRef = (0, import_react2.useRef)(null);
|
|
142
|
+
const wrapperRef = (0, import_react2.useRef)(null);
|
|
143
|
+
const [tappedDay, setTappedDay] = (0, import_react2.useState)(null);
|
|
144
|
+
const [tooltip, setTooltip] = (0, import_react2.useState)(null);
|
|
145
|
+
(0, import_react2.useEffect)(() => {
|
|
146
|
+
ensureStyle();
|
|
147
|
+
}, []);
|
|
148
|
+
(0, import_react2.useEffect)(() => {
|
|
149
|
+
if (data && scrollRef.current) {
|
|
150
|
+
scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;
|
|
151
|
+
}
|
|
152
|
+
}, [data]);
|
|
153
|
+
const mergedTheme = { ...import_git_heatmap_core.DEFAULT_THEME, ...theme };
|
|
154
|
+
const cssVars = themeToVars(mergedTheme);
|
|
155
|
+
const formatTooltip = (day) => day.count === 0 ? `No contributions on ${day.date}` : `${day.count} contribution${day.count > 1 ? "s" : ""} on ${day.date}`;
|
|
156
|
+
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
|
+
}
|
|
159
|
+
if (status === "loading" || status === "idle") {
|
|
160
|
+
const skeletonCols = Array.from({ length: 53 });
|
|
161
|
+
const skeletonRows = Array.from({ length: 7 });
|
|
162
|
+
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
|
+
] });
|
|
166
|
+
}
|
|
167
|
+
if (!data) return null;
|
|
168
|
+
const monthLabels = showMonthLabels ? (0, import_git_heatmap_core.buildMonthLabels)(data.weeks) : [];
|
|
169
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-git-heatmap": "", ref: wrapperRef, style: cssVars, "aria-label": label, children: [
|
|
170
|
+
showTotal && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: { marginBottom: 12, color: "var(--ghm-text)" }, children: [
|
|
171
|
+
data.totalContributions.toLocaleString(),
|
|
172
|
+
" contributions in the last year"
|
|
173
|
+
] }),
|
|
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",
|
|
188
|
+
{
|
|
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)
|
|
222
|
+
},
|
|
223
|
+
dow
|
|
224
|
+
);
|
|
225
|
+
}) }, wi)) })
|
|
226
|
+
] })
|
|
227
|
+
] }) }),
|
|
228
|
+
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
|
+
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
|
+
/* @__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
|
+
)),
|
|
239
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: "More" })
|
|
240
|
+
] }) }),
|
|
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
|
+
)
|
|
276
|
+
] });
|
|
277
|
+
}
|
|
278
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
279
|
+
0 && (module.exports = {
|
|
280
|
+
GitHeatmap,
|
|
281
|
+
useHeatmapData
|
|
282
|
+
});
|
|
283
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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","_"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
// src/GitHeatmap.tsx
|
|
2
|
+
import { useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
|
|
3
|
+
import {
|
|
4
|
+
buildMonthLabels,
|
|
5
|
+
CELL,
|
|
6
|
+
GAP,
|
|
7
|
+
DAY_LABELS,
|
|
8
|
+
DEFAULT_LEVELS,
|
|
9
|
+
DEFAULT_THEME
|
|
10
|
+
} from "@rsalianto/git-heatmap-core";
|
|
11
|
+
|
|
12
|
+
// src/useHeatmapData.ts
|
|
13
|
+
import { useEffect, useRef, useState } from "react";
|
|
14
|
+
function useHeatmapData(options) {
|
|
15
|
+
const [data, setData] = useState(options.data ?? null);
|
|
16
|
+
const [status, setStatus] = useState(options.data ? "success" : "idle");
|
|
17
|
+
const [error, setError] = useState(null);
|
|
18
|
+
const apiUrl = options.apiUrl;
|
|
19
|
+
const fetchData = options.fetchData;
|
|
20
|
+
const staticData = options.data;
|
|
21
|
+
const didLoad = useRef(false);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
if (staticData) {
|
|
24
|
+
setData(staticData);
|
|
25
|
+
setStatus("success");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const resolver = fetchData ?? (apiUrl ? () => fetch(apiUrl).then((r) => r.json()) : null);
|
|
29
|
+
if (!resolver) return;
|
|
30
|
+
let cancelled = false;
|
|
31
|
+
didLoad.current = false;
|
|
32
|
+
setStatus("loading");
|
|
33
|
+
resolver().then((d) => {
|
|
34
|
+
if (!cancelled) {
|
|
35
|
+
setData(d);
|
|
36
|
+
setStatus("success");
|
|
37
|
+
}
|
|
38
|
+
}).catch((e) => {
|
|
39
|
+
if (!cancelled) {
|
|
40
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
41
|
+
setStatus("error");
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return () => {
|
|
45
|
+
cancelled = true;
|
|
46
|
+
};
|
|
47
|
+
}, [staticData, apiUrl, fetchData]);
|
|
48
|
+
return { data, status, error };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/GitHeatmap.tsx
|
|
52
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
53
|
+
function themeToVars(theme) {
|
|
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
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
var BASE_STYLE = `
|
|
69
|
+
[data-git-heatmap] {
|
|
70
|
+
--ghm-color-l0: rgba(255,255,255,0.08);
|
|
71
|
+
--ghm-color-l1: #1c3d06;
|
|
72
|
+
--ghm-color-l2: #3a7510;
|
|
73
|
+
--ghm-color-l3: #6ab81e;
|
|
74
|
+
--ghm-color-l4: #aafd35;
|
|
75
|
+
--ghm-text: rgba(255,255,255,0.5);
|
|
76
|
+
--ghm-tooltip-bg: #1c2128;
|
|
77
|
+
--ghm-tooltip-border: rgba(255,255,255,0.1);
|
|
78
|
+
--ghm-tooltip-text: rgba(255,255,255,0.75);
|
|
79
|
+
--ghm-font: inherit;
|
|
80
|
+
--ghm-fs: 11px;
|
|
81
|
+
display: block;
|
|
82
|
+
position: relative;
|
|
83
|
+
width: 100%;
|
|
84
|
+
font-size: var(--ghm-fs);
|
|
85
|
+
font-family: var(--ghm-font);
|
|
86
|
+
color: var(--ghm-text);
|
|
87
|
+
user-select: none;
|
|
88
|
+
box-sizing: border-box;
|
|
89
|
+
}
|
|
90
|
+
[data-git-heatmap] * { box-sizing: border-box; }
|
|
91
|
+
`;
|
|
92
|
+
var styleInjected = false;
|
|
93
|
+
function ensureStyle() {
|
|
94
|
+
if (styleInjected || typeof document === "undefined") return;
|
|
95
|
+
const el = document.createElement("style");
|
|
96
|
+
el.textContent = BASE_STYLE;
|
|
97
|
+
document.head.appendChild(el);
|
|
98
|
+
styleInjected = true;
|
|
99
|
+
}
|
|
100
|
+
function levelColor(level) {
|
|
101
|
+
return `var(--ghm-color-l${level})`;
|
|
102
|
+
}
|
|
103
|
+
function GitHeatmap({
|
|
104
|
+
data: dataProp,
|
|
105
|
+
apiUrl,
|
|
106
|
+
fetchData,
|
|
107
|
+
levels = DEFAULT_LEVELS,
|
|
108
|
+
cellSize = CELL,
|
|
109
|
+
cellGap = GAP,
|
|
110
|
+
cellRadius = 2,
|
|
111
|
+
showTotal = true,
|
|
112
|
+
showLegend = true,
|
|
113
|
+
showMonthLabels = true,
|
|
114
|
+
showDayLabels = true,
|
|
115
|
+
theme,
|
|
116
|
+
onDayClick,
|
|
117
|
+
label = "Contribution heatmap"
|
|
118
|
+
}) {
|
|
119
|
+
const step = cellSize + cellGap;
|
|
120
|
+
const { data, status, error } = useHeatmapData({ data: dataProp, apiUrl, fetchData });
|
|
121
|
+
const scrollRef = useRef2(null);
|
|
122
|
+
const wrapperRef = useRef2(null);
|
|
123
|
+
const [tappedDay, setTappedDay] = useState2(null);
|
|
124
|
+
const [tooltip, setTooltip] = useState2(null);
|
|
125
|
+
useEffect2(() => {
|
|
126
|
+
ensureStyle();
|
|
127
|
+
}, []);
|
|
128
|
+
useEffect2(() => {
|
|
129
|
+
if (data && scrollRef.current) {
|
|
130
|
+
scrollRef.current.scrollLeft = scrollRef.current.scrollWidth;
|
|
131
|
+
}
|
|
132
|
+
}, [data]);
|
|
133
|
+
const mergedTheme = { ...DEFAULT_THEME, ...theme };
|
|
134
|
+
const cssVars = themeToVars(mergedTheme);
|
|
135
|
+
const formatTooltip = (day) => day.count === 0 ? `No contributions on ${day.date}` : `${day.count} contribution${day.count > 1 ? "s" : ""} on ${day.date}`;
|
|
136
|
+
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
|
+
}
|
|
139
|
+
if (status === "loading" || status === "idle") {
|
|
140
|
+
const skeletonCols = Array.from({ length: 53 });
|
|
141
|
+
const skeletonRows = Array.from({ length: 7 });
|
|
142
|
+
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
|
+
] });
|
|
146
|
+
}
|
|
147
|
+
if (!data) return null;
|
|
148
|
+
const monthLabels = showMonthLabels ? buildMonthLabels(data.weeks) : [];
|
|
149
|
+
return /* @__PURE__ */ jsxs("div", { "data-git-heatmap": "", ref: wrapperRef, style: cssVars, "aria-label": label, children: [
|
|
150
|
+
showTotal && /* @__PURE__ */ jsxs("p", { style: { marginBottom: 12, color: "var(--ghm-text)" }, children: [
|
|
151
|
+
data.totalContributions.toLocaleString(),
|
|
152
|
+
" contributions in the last year"
|
|
153
|
+
] }),
|
|
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",
|
|
168
|
+
{
|
|
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)
|
|
202
|
+
},
|
|
203
|
+
dow
|
|
204
|
+
);
|
|
205
|
+
}) }, wi)) })
|
|
206
|
+
] })
|
|
207
|
+
] }) }),
|
|
208
|
+
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
|
+
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
|
+
/* @__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
|
+
)),
|
|
219
|
+
/* @__PURE__ */ jsx("span", { children: "More" })
|
|
220
|
+
] }) }),
|
|
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
|
+
)
|
|
256
|
+
] });
|
|
257
|
+
}
|
|
258
|
+
export {
|
|
259
|
+
GitHeatmap,
|
|
260
|
+
useHeatmapData
|
|
261
|
+
};
|
|
262
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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","_"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rsalianto/git-heatmap-react",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React component for git contribution heatmap",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.mjs",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"require": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": ["dist", "README.md"],
|
|
17
|
+
"sideEffects": false,
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsup",
|
|
20
|
+
"dev": "tsup --watch",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"clean": "rm -rf dist"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@rsalianto/git-heatmap-core": "*"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"react": ">=18.0.0",
|
|
29
|
+
"react-dom": ">=18.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"tsup": "*",
|
|
33
|
+
"typescript": "*",
|
|
34
|
+
"vitest": "*",
|
|
35
|
+
"@types/react": "^18.0.0",
|
|
36
|
+
"@types/react-dom": "^18.0.0",
|
|
37
|
+
"react": "^18.0.0",
|
|
38
|
+
"react-dom": "^18.0.0"
|
|
39
|
+
}
|
|
40
|
+
}
|