github-contributions-ui 1.0.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 +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +383 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +356 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +25 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
type Theme = "light" | "dark" | "blue" | "purple";
|
|
5
|
+
interface Props {
|
|
6
|
+
username: string;
|
|
7
|
+
theme?: Theme;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
declare function GithubActivity({ username, theme, className }: Props): react_jsx_runtime.JSX.Element;
|
|
11
|
+
declare const _default: react.MemoExoticComponent<typeof GithubActivity>;
|
|
12
|
+
|
|
13
|
+
export { _default as GithubActivity };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as react from 'react';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
|
|
4
|
+
type Theme = "light" | "dark" | "blue" | "purple";
|
|
5
|
+
interface Props {
|
|
6
|
+
username: string;
|
|
7
|
+
theme?: Theme;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
declare function GithubActivity({ username, theme, className }: Props): react_jsx_runtime.JSX.Element;
|
|
11
|
+
declare const _default: react.MemoExoticComponent<typeof GithubActivity>;
|
|
12
|
+
|
|
13
|
+
export { _default as GithubActivity };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
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
|
+
GithubActivity: () => GithubActivity_default
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/GithubActivity.tsx
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
30
|
+
var MONTHS = [
|
|
31
|
+
"Jan",
|
|
32
|
+
"Feb",
|
|
33
|
+
"Mar",
|
|
34
|
+
"Apr",
|
|
35
|
+
"May",
|
|
36
|
+
"Jun",
|
|
37
|
+
"Jul",
|
|
38
|
+
"Aug",
|
|
39
|
+
"Sep",
|
|
40
|
+
"Oct",
|
|
41
|
+
"Nov",
|
|
42
|
+
"Dec"
|
|
43
|
+
];
|
|
44
|
+
var DAY_LABELS = { 1: "Mon", 3: "Wed", 5: "Fri" };
|
|
45
|
+
var CELL = 13;
|
|
46
|
+
var GAP = 3;
|
|
47
|
+
var STEP = CELL + GAP;
|
|
48
|
+
var DAY_COL_W = 30;
|
|
49
|
+
var LIGHT = ["#ebedf0", "#9be9a8", "#40c463", "#30a14e", "#216e39"];
|
|
50
|
+
var DARK = ["#21262d", "#0e4429", "#006d32", "#26a641", "#39d353"];
|
|
51
|
+
var BLUE = ["#21262d", "#a3c9ff", "#5fa3ff", "#2f7bff", "#0b5cff"];
|
|
52
|
+
var PURPLE = ["#21262d", "#d8b4ff", "#c084fc", "#a855f7", "#7e22ce"];
|
|
53
|
+
var COLOR_MAP = {
|
|
54
|
+
light: LIGHT,
|
|
55
|
+
dark: DARK,
|
|
56
|
+
blue: BLUE,
|
|
57
|
+
purple: PURPLE
|
|
58
|
+
};
|
|
59
|
+
function toLevel(count) {
|
|
60
|
+
if (count === 0) return 0;
|
|
61
|
+
if (count <= 3) return 1;
|
|
62
|
+
if (count <= 6) return 2;
|
|
63
|
+
if (count <= 9) return 3;
|
|
64
|
+
return 4;
|
|
65
|
+
}
|
|
66
|
+
function buildGrid(dayMap, year) {
|
|
67
|
+
let start;
|
|
68
|
+
let end;
|
|
69
|
+
if (year === "last") {
|
|
70
|
+
end = /* @__PURE__ */ new Date();
|
|
71
|
+
start = new Date(end);
|
|
72
|
+
start.setFullYear(start.getFullYear() - 1);
|
|
73
|
+
start.setDate(start.getDate() + 1);
|
|
74
|
+
} else {
|
|
75
|
+
start = new Date(year, 0, 1);
|
|
76
|
+
end = year === (/* @__PURE__ */ new Date()).getFullYear() ? /* @__PURE__ */ new Date() : new Date(year, 11, 31);
|
|
77
|
+
}
|
|
78
|
+
start.setDate(start.getDate() - start.getDay());
|
|
79
|
+
const weeks = [];
|
|
80
|
+
const cur = new Date(start);
|
|
81
|
+
while (cur <= end) {
|
|
82
|
+
const week = [];
|
|
83
|
+
for (let d = 0; d < 7; d++) {
|
|
84
|
+
const iso = cur.toISOString().slice(0, 10);
|
|
85
|
+
if (cur > end) {
|
|
86
|
+
week.push(null);
|
|
87
|
+
} else {
|
|
88
|
+
week.push(
|
|
89
|
+
dayMap.get(iso) ?? {
|
|
90
|
+
date: iso,
|
|
91
|
+
count: 0,
|
|
92
|
+
level: 0
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
cur.setDate(cur.getDate() + 1);
|
|
97
|
+
}
|
|
98
|
+
weeks.push(week);
|
|
99
|
+
}
|
|
100
|
+
return weeks;
|
|
101
|
+
}
|
|
102
|
+
function monthLabels(grid) {
|
|
103
|
+
const out = [];
|
|
104
|
+
let last = -1;
|
|
105
|
+
grid.forEach((week, i) => {
|
|
106
|
+
const first = week.find(Boolean);
|
|
107
|
+
if (!first) return;
|
|
108
|
+
const m = new Date(first.date).getMonth();
|
|
109
|
+
if (m !== last) {
|
|
110
|
+
out.push({ label: MONTHS[m], col: i });
|
|
111
|
+
last = m;
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
function Tooltip({ day, x, y }) {
|
|
117
|
+
const label = new Date(day.date).toLocaleDateString("en-US", {
|
|
118
|
+
weekday: "long",
|
|
119
|
+
year: "numeric",
|
|
120
|
+
month: "long",
|
|
121
|
+
day: "numeric"
|
|
122
|
+
});
|
|
123
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
124
|
+
"div",
|
|
125
|
+
{
|
|
126
|
+
className: "fixed z-50 pointer-events-none -translate-x-1/2 -translate-y-full px-2.5 py-1.5 rounded-md text-xs font-mono whitespace-nowrap shadow-xl bg-neutral-900 text-white dark:bg-white dark:text-neutral-900",
|
|
127
|
+
style: { left: x, top: y - 8 },
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("b", { children: [
|
|
130
|
+
day.count,
|
|
131
|
+
" contribution",
|
|
132
|
+
day.count !== 1 ? "s" : ""
|
|
133
|
+
] }),
|
|
134
|
+
" ",
|
|
135
|
+
"\xB7 ",
|
|
136
|
+
label
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function Skeleton() {
|
|
142
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex gap-[3px] animate-pulse opacity-40", children: Array.from({ length: 53 }).map((_, w) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col gap-[3px]", children: Array.from({ length: 7 }).map((_2, d) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
143
|
+
"div",
|
|
144
|
+
{
|
|
145
|
+
className: "w-[13px] h-[13px] rounded-[3px] bg-neutral-300 dark:bg-neutral-700"
|
|
146
|
+
},
|
|
147
|
+
d
|
|
148
|
+
)) }, w)) });
|
|
149
|
+
}
|
|
150
|
+
function YearPill({
|
|
151
|
+
label,
|
|
152
|
+
active,
|
|
153
|
+
onClick
|
|
154
|
+
}) {
|
|
155
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
156
|
+
"button",
|
|
157
|
+
{
|
|
158
|
+
onClick,
|
|
159
|
+
className: [
|
|
160
|
+
"px-2.5 py-0.5 rounded-full text-[11px] font-medium transition-colors duration-150",
|
|
161
|
+
active ? "bg-neutral-900 text-white dark:bg-white dark:text-neutral-900" : "bg-neutral-100 text-neutral-600 hover:bg-neutral-200 dark:bg-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-700"
|
|
162
|
+
].join(" "),
|
|
163
|
+
children: label
|
|
164
|
+
}
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
function GithubActivity({ username, theme = "dark", className }) {
|
|
168
|
+
const [allDays, setAllDays] = (0, import_react.useState)(/* @__PURE__ */ new Map());
|
|
169
|
+
const [years, setYears] = (0, import_react.useState)([]);
|
|
170
|
+
const [selectedYear, setSelectedYear] = (0, import_react.useState)("last");
|
|
171
|
+
const [total, setTotal] = (0, import_react.useState)(null);
|
|
172
|
+
const [bestDay, setBestDay] = (0, import_react.useState)(null);
|
|
173
|
+
const [loading, setLoading] = (0, import_react.useState)(true);
|
|
174
|
+
const [error, setError] = (0, import_react.useState)(false);
|
|
175
|
+
const [tip, setTip] = (0, import_react.useState)(
|
|
176
|
+
null
|
|
177
|
+
);
|
|
178
|
+
const colors = COLOR_MAP[theme] ?? COLOR_MAP.dark;
|
|
179
|
+
(0, import_react.useEffect)(() => {
|
|
180
|
+
if (!username) return;
|
|
181
|
+
let mounted = true;
|
|
182
|
+
setLoading(true);
|
|
183
|
+
setError(false);
|
|
184
|
+
(async () => {
|
|
185
|
+
try {
|
|
186
|
+
const res = await fetch(
|
|
187
|
+
`https://github-contributions-api.jogruber.de/v4/${username}?y=all`
|
|
188
|
+
);
|
|
189
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
190
|
+
const json = await res.json();
|
|
191
|
+
const raw = json.contributions ?? [];
|
|
192
|
+
if (!raw.length) throw new Error("empty data");
|
|
193
|
+
const map = /* @__PURE__ */ new Map();
|
|
194
|
+
const yearSet = /* @__PURE__ */ new Set();
|
|
195
|
+
raw.forEach((c) => {
|
|
196
|
+
map.set(c.date, {
|
|
197
|
+
date: c.date,
|
|
198
|
+
count: c.count,
|
|
199
|
+
level: toLevel(c.count)
|
|
200
|
+
});
|
|
201
|
+
yearSet.add(new Date(c.date).getFullYear());
|
|
202
|
+
});
|
|
203
|
+
if (mounted) {
|
|
204
|
+
setAllDays(map);
|
|
205
|
+
setYears(Array.from(yearSet).sort((a, b) => b - a));
|
|
206
|
+
setLoading(false);
|
|
207
|
+
}
|
|
208
|
+
} catch (err) {
|
|
209
|
+
console.warn("GithubActivity fetch error:", err);
|
|
210
|
+
if (mounted) {
|
|
211
|
+
setError(true);
|
|
212
|
+
setLoading(false);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
})();
|
|
216
|
+
return () => {
|
|
217
|
+
mounted = false;
|
|
218
|
+
};
|
|
219
|
+
}, [username]);
|
|
220
|
+
(0, import_react.useEffect)(() => {
|
|
221
|
+
if (!allDays.size) return;
|
|
222
|
+
const entries = Array.from(allDays.values()).filter((d) => {
|
|
223
|
+
if (selectedYear === "last") {
|
|
224
|
+
const oneYearAgo = /* @__PURE__ */ new Date();
|
|
225
|
+
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
|
|
226
|
+
return new Date(d.date) >= oneYearAgo;
|
|
227
|
+
}
|
|
228
|
+
return new Date(d.date).getFullYear() === selectedYear;
|
|
229
|
+
});
|
|
230
|
+
const sum = entries.reduce((s, d) => s + d.count, 0);
|
|
231
|
+
const best = entries.reduce(
|
|
232
|
+
(b, d) => !b || d.count > b.count ? d : b,
|
|
233
|
+
null
|
|
234
|
+
);
|
|
235
|
+
setTotal(sum);
|
|
236
|
+
setBestDay(best && best.count > 0 ? best : null);
|
|
237
|
+
}, [allDays, selectedYear]);
|
|
238
|
+
const grid = (0, import_react.useMemo)(
|
|
239
|
+
() => allDays.size ? buildGrid(allDays, selectedYear) : [],
|
|
240
|
+
[allDays, selectedYear]
|
|
241
|
+
);
|
|
242
|
+
const labels = (0, import_react.useMemo)(() => monthLabels(grid), [grid]);
|
|
243
|
+
const outlineColor = theme === "light" ? "#333" : "#f0f0f0";
|
|
244
|
+
const bestDateLabel = bestDay ? new Date(bestDay.date).toLocaleDateString("en-US", {
|
|
245
|
+
month: "short",
|
|
246
|
+
day: "numeric",
|
|
247
|
+
year: "numeric"
|
|
248
|
+
}) : null;
|
|
249
|
+
const periodLabel = selectedYear === "last" ? " in the last year" : ` in ${selectedYear}`;
|
|
250
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("section", { className: ["w-full font-mono", className].join(" ").trim(), children: [
|
|
251
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-1 mb-4 text-xs text-neutral-500 dark:text-neutral-400", children: [
|
|
252
|
+
loading ? "Loading\u2026" : error ? "Unavailable" : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
253
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "font-semibold text-neutral-700 dark:text-neutral-200", children: total?.toLocaleString() }),
|
|
254
|
+
" contributions",
|
|
255
|
+
periodLabel
|
|
256
|
+
] }),
|
|
257
|
+
bestDay && !loading && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "flex items-center gap-1.5", children: [
|
|
258
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
259
|
+
"span",
|
|
260
|
+
{
|
|
261
|
+
className: "inline-block w-[10px] h-[10px] rounded-[2px]",
|
|
262
|
+
style: { backgroundColor: colors[4] }
|
|
263
|
+
}
|
|
264
|
+
),
|
|
265
|
+
"Best day:",
|
|
266
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { className: "font-semibold text-neutral-700 dark:text-neutral-200", children: [
|
|
267
|
+
bestDay.count,
|
|
268
|
+
" contributions"
|
|
269
|
+
] }),
|
|
270
|
+
"on ",
|
|
271
|
+
bestDateLabel
|
|
272
|
+
] })
|
|
273
|
+
] }),
|
|
274
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "border border-neutral-200 dark:border-neutral-800 rounded-xl p-4 bg-white dark:bg-neutral-950", children: error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "text-sm italic text-neutral-400", children: "GitHub activity unavailable." }) : loading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Skeleton, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
275
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "overflow-x-auto overflow-y-visible pb-1", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
276
|
+
"div",
|
|
277
|
+
{
|
|
278
|
+
className: "relative",
|
|
279
|
+
style: {
|
|
280
|
+
minWidth: grid.length * STEP + DAY_COL_W + 8
|
|
281
|
+
},
|
|
282
|
+
children: [
|
|
283
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
284
|
+
"div",
|
|
285
|
+
{
|
|
286
|
+
className: "relative h-[18px] mb-1",
|
|
287
|
+
style: { marginLeft: DAY_COL_W },
|
|
288
|
+
children: labels.map(({ label, col }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
289
|
+
"span",
|
|
290
|
+
{
|
|
291
|
+
className: "absolute text-[11px] select-none text-neutral-500 dark:text-neutral-400",
|
|
292
|
+
style: { left: col * STEP },
|
|
293
|
+
children: label
|
|
294
|
+
},
|
|
295
|
+
`${label}-${col}`
|
|
296
|
+
))
|
|
297
|
+
}
|
|
298
|
+
),
|
|
299
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-[3px]", children: [
|
|
300
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
301
|
+
"div",
|
|
302
|
+
{
|
|
303
|
+
className: "flex flex-col gap-[3px] pt-px shrink-0",
|
|
304
|
+
style: { width: DAY_COL_W - GAP },
|
|
305
|
+
children: Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
306
|
+
"div",
|
|
307
|
+
{
|
|
308
|
+
className: "h-[13px] text-[10px] leading-[13px] text-right pr-1.5 select-none text-neutral-500 dark:text-neutral-400",
|
|
309
|
+
children: DAY_LABELS[i] ?? ""
|
|
310
|
+
},
|
|
311
|
+
i
|
|
312
|
+
))
|
|
313
|
+
}
|
|
314
|
+
),
|
|
315
|
+
grid.map((week, wi) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col gap-[3px]", children: week.map((day, di) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
316
|
+
"div",
|
|
317
|
+
{
|
|
318
|
+
className: "w-[13px] h-[13px] rounded-[3px] transition-transform duration-100 hover:scale-125",
|
|
319
|
+
style: {
|
|
320
|
+
backgroundColor: day ? colors[day.level] : "transparent",
|
|
321
|
+
cursor: day ? "pointer" : "default",
|
|
322
|
+
outline: bestDay && day?.date === bestDay.date ? `2px solid ${outlineColor}` : "none",
|
|
323
|
+
outlineOffset: "1px"
|
|
324
|
+
},
|
|
325
|
+
onMouseEnter: (e) => {
|
|
326
|
+
if (!day) return;
|
|
327
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
328
|
+
setTip({
|
|
329
|
+
day,
|
|
330
|
+
x: r.left + r.width / 2,
|
|
331
|
+
y: r.top
|
|
332
|
+
});
|
|
333
|
+
},
|
|
334
|
+
onMouseLeave: () => setTip(null)
|
|
335
|
+
},
|
|
336
|
+
di
|
|
337
|
+
)) }, wi))
|
|
338
|
+
] })
|
|
339
|
+
]
|
|
340
|
+
}
|
|
341
|
+
) }),
|
|
342
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-start gap-1 mt-3", children: [
|
|
343
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[11px] text-neutral-500 dark:text-neutral-400 mr-1", children: "Less" }),
|
|
344
|
+
colors.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
345
|
+
"div",
|
|
346
|
+
{
|
|
347
|
+
className: "w-[13px] h-[13px] rounded-[3px]",
|
|
348
|
+
style: { backgroundColor: c }
|
|
349
|
+
},
|
|
350
|
+
i
|
|
351
|
+
)),
|
|
352
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[11px] text-neutral-500 dark:text-neutral-400 ml-1", children: "More" })
|
|
353
|
+
] }),
|
|
354
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-wrap items-center gap-1.5 mt-4 pt-3 border-t border-neutral-100 dark:border-neutral-800", children: [
|
|
355
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "text-[11px] text-neutral-400 mr-1", children: "Year:" }),
|
|
356
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
357
|
+
YearPill,
|
|
358
|
+
{
|
|
359
|
+
label: "Last year",
|
|
360
|
+
active: selectedYear === "last",
|
|
361
|
+
onClick: () => setSelectedYear("last")
|
|
362
|
+
}
|
|
363
|
+
),
|
|
364
|
+
years.map((y) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
365
|
+
YearPill,
|
|
366
|
+
{
|
|
367
|
+
label: String(y),
|
|
368
|
+
active: selectedYear === y,
|
|
369
|
+
onClick: () => setSelectedYear(y)
|
|
370
|
+
},
|
|
371
|
+
y
|
|
372
|
+
))
|
|
373
|
+
] })
|
|
374
|
+
] }) }),
|
|
375
|
+
tip && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Tooltip, { day: tip.day, x: tip.x, y: tip.y })
|
|
376
|
+
] });
|
|
377
|
+
}
|
|
378
|
+
var GithubActivity_default = (0, import_react.memo)(GithubActivity);
|
|
379
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
380
|
+
0 && (module.exports = {
|
|
381
|
+
GithubActivity
|
|
382
|
+
});
|
|
383
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/GithubActivity.tsx"],"sourcesContent":["export { default as GithubActivity } from \"./GithubActivity\";","import { memo, useEffect, useState, useMemo } from \"react\";\r\n\r\ntype Theme = \"light\" | \"dark\" | \"blue\" | \"purple\";\r\n\r\ninterface Day {\r\n date: string;\r\n count: number;\r\n level: 0 | 1 | 2 | 3 | 4;\r\n}\r\n\r\ninterface Props {\r\n username: string;\r\n theme?: Theme;\r\n className?: string;\r\n}\r\n\r\n/* ---------- Constants ---------- */\r\n\r\nconst MONTHS = [\r\n \"Jan\",\r\n \"Feb\",\r\n \"Mar\",\r\n \"Apr\",\r\n \"May\",\r\n \"Jun\",\r\n \"Jul\",\r\n \"Aug\",\r\n \"Sep\",\r\n \"Oct\",\r\n \"Nov\",\r\n \"Dec\",\r\n];\r\nconst DAY_LABELS: Record<number, string> = { 1: \"Mon\", 3: \"Wed\", 5: \"Fri\" };\r\n\r\nconst CELL = 13;\r\nconst GAP = 3;\r\nconst STEP = CELL + GAP;\r\nconst DAY_COL_W = 30;\r\n\r\n/* ---------- Color Themes ---------- */\r\n\r\nconst LIGHT = [\"#ebedf0\", \"#9be9a8\", \"#40c463\", \"#30a14e\", \"#216e39\"];\r\nconst DARK = [\"#21262d\", \"#0e4429\", \"#006d32\", \"#26a641\", \"#39d353\"];\r\nconst BLUE = [\"#21262d\", \"#a3c9ff\", \"#5fa3ff\", \"#2f7bff\", \"#0b5cff\"];\r\nconst PURPLE = [\"#21262d\", \"#d8b4ff\", \"#c084fc\", \"#a855f7\", \"#7e22ce\"];\r\n\r\nconst COLOR_MAP: Record<Theme, string[]> = {\r\n light: LIGHT,\r\n dark: DARK,\r\n blue: BLUE,\r\n purple: PURPLE,\r\n};\r\n\r\n/* ---------- Utilities ---------- */\r\n\r\nfunction toLevel(count: number): 0 | 1 | 2 | 3 | 4 {\r\n if (count === 0) return 0;\r\n if (count <= 3) return 1;\r\n if (count <= 6) return 2;\r\n if (count <= 9) return 3;\r\n return 4;\r\n}\r\n\r\nfunction buildGrid(\r\n dayMap: Map<string, Day>,\r\n year: number | \"last\",\r\n): (Day | null)[][] {\r\n let start: Date;\r\n let end: Date;\r\n\r\n if (year === \"last\") {\r\n end = new Date();\r\n start = new Date(end);\r\n start.setFullYear(start.getFullYear() - 1);\r\n start.setDate(start.getDate() + 1);\r\n } else {\r\n start = new Date(year, 0, 1);\r\n end =\r\n year === new Date().getFullYear() ? new Date() : new Date(year, 11, 31);\r\n }\r\n\r\n start.setDate(start.getDate() - start.getDay());\r\n\r\n const weeks: (Day | null)[][] = [];\r\n const cur = new Date(start);\r\n\r\n while (cur <= end) {\r\n const week: (Day | null)[] = [];\r\n\r\n for (let d = 0; d < 7; d++) {\r\n const iso = cur.toISOString().slice(0, 10);\r\n\r\n if (cur > end) {\r\n week.push(null);\r\n } else {\r\n week.push(\r\n dayMap.get(iso) ?? {\r\n date: iso,\r\n count: 0,\r\n level: 0,\r\n },\r\n );\r\n }\r\n\r\n cur.setDate(cur.getDate() + 1);\r\n }\r\n\r\n weeks.push(week);\r\n }\r\n\r\n return weeks;\r\n}\r\n\r\nfunction monthLabels(grid: (Day | null)[][]) {\r\n const out: { label: string; col: number }[] = [];\r\n let last = -1;\r\n\r\n grid.forEach((week, i) => {\r\n const first = week.find(Boolean);\r\n if (!first) return;\r\n\r\n const m = new Date(first.date).getMonth();\r\n\r\n if (m !== last) {\r\n out.push({ label: MONTHS[m], col: i });\r\n last = m;\r\n }\r\n });\r\n\r\n return out;\r\n}\r\n\r\n/* ---------- UI Components ---------- */\r\n\r\nfunction Tooltip({ day, x, y }: { day: Day; x: number; y: number }) {\r\n const label = new Date(day.date).toLocaleDateString(\"en-US\", {\r\n weekday: \"long\",\r\n year: \"numeric\",\r\n month: \"long\",\r\n day: \"numeric\",\r\n });\r\n\r\n return (\r\n <div\r\n className=\"fixed z-50 pointer-events-none -translate-x-1/2 -translate-y-full px-2.5 py-1.5 rounded-md text-xs font-mono whitespace-nowrap shadow-xl bg-neutral-900 text-white dark:bg-white dark:text-neutral-900\"\r\n style={{ left: x, top: y - 8 }}\r\n >\r\n <b>\r\n {day.count} contribution{day.count !== 1 ? \"s\" : \"\"}\r\n </b>{\" \"}\r\n · {label}\r\n </div>\r\n );\r\n}\r\n\r\nfunction Skeleton() {\r\n return (\r\n <div className=\"flex gap-[3px] animate-pulse opacity-40\">\r\n {Array.from({ length: 53 }).map((_, w) => (\r\n <div key={w} className=\"flex flex-col gap-[3px]\">\r\n {Array.from({ length: 7 }).map((_, d) => (\r\n <div\r\n key={d}\r\n className=\"w-[13px] h-[13px] rounded-[3px] bg-neutral-300 dark:bg-neutral-700\"\r\n />\r\n ))}\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n}\r\n\r\nfunction YearPill({\r\n label,\r\n active,\r\n onClick,\r\n}: {\r\n label: string;\r\n active: boolean;\r\n onClick: () => void;\r\n}) {\r\n return (\r\n <button\r\n onClick={onClick}\r\n className={[\r\n \"px-2.5 py-0.5 rounded-full text-[11px] font-medium transition-colors duration-150\",\r\n active\r\n ? \"bg-neutral-900 text-white dark:bg-white dark:text-neutral-900\"\r\n : \"bg-neutral-100 text-neutral-600 hover:bg-neutral-200 dark:bg-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-700\",\r\n ].join(\" \")}\r\n >\r\n {label}\r\n </button>\r\n );\r\n}\r\n\r\n/* ---------- Main Component ---------- */\r\n\r\nfunction GithubActivity({ username, theme = \"dark\", className }: Props) {\r\n const [allDays, setAllDays] = useState<Map<string, Day>>(new Map());\r\n const [years, setYears] = useState<number[]>([]);\r\n const [selectedYear, setSelectedYear] = useState<number | \"last\">(\"last\");\r\n const [total, setTotal] = useState<number | null>(null);\r\n const [bestDay, setBestDay] = useState<Day | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState(false);\r\n const [tip, setTip] = useState<{ day: Day; x: number; y: number } | null>(\r\n null,\r\n );\r\n\r\n const colors = COLOR_MAP[theme] ?? COLOR_MAP.dark;\r\n\r\n /* Fetch Contributions */\r\n\r\n useEffect(() => {\r\n if (!username) return;\r\n\r\n let mounted = true;\r\n setLoading(true);\r\n setError(false);\r\n\r\n (async () => {\r\n try {\r\n const res = await fetch(\r\n `https://github-contributions-api.jogruber.de/v4/${username}?y=all`,\r\n );\r\n\r\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\r\n\r\n const json = await res.json();\r\n const raw: { date: string; count: number }[] = json.contributions ?? [];\r\n\r\n if (!raw.length) throw new Error(\"empty data\");\r\n\r\n const map = new Map<string, Day>();\r\n const yearSet = new Set<number>();\r\n\r\n raw.forEach((c) => {\r\n map.set(c.date, {\r\n date: c.date,\r\n count: c.count,\r\n level: toLevel(c.count),\r\n });\r\n\r\n yearSet.add(new Date(c.date).getFullYear());\r\n });\r\n\r\n if (mounted) {\r\n setAllDays(map);\r\n setYears(Array.from(yearSet).sort((a, b) => b - a));\r\n setLoading(false);\r\n }\r\n } catch (err) {\r\n console.warn(\"GithubActivity fetch error:\", err);\r\n\r\n if (mounted) {\r\n setError(true);\r\n setLoading(false);\r\n }\r\n }\r\n })();\r\n\r\n return () => {\r\n mounted = false;\r\n };\r\n }, [username]);\r\n\r\n /* Stats Calculation */\r\n\r\n useEffect(() => {\r\n if (!allDays.size) return;\r\n\r\n const entries = Array.from(allDays.values()).filter((d) => {\r\n if (selectedYear === \"last\") {\r\n const oneYearAgo = new Date();\r\n oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);\r\n return new Date(d.date) >= oneYearAgo;\r\n }\r\n\r\n return new Date(d.date).getFullYear() === selectedYear;\r\n });\r\n\r\n const sum = entries.reduce((s, d) => s + d.count, 0);\r\n\r\n const best = entries.reduce<Day | null>(\r\n (b, d) => (!b || d.count > b.count ? d : b),\r\n null,\r\n );\r\n\r\n setTotal(sum);\r\n setBestDay(best && best.count > 0 ? best : null);\r\n }, [allDays, selectedYear]);\r\n\r\n /* Rendering Helpers */\r\n\r\n const grid = useMemo(\r\n () => (allDays.size ? buildGrid(allDays, selectedYear) : []),\r\n [allDays, selectedYear],\r\n );\r\n\r\n const labels = useMemo(() => monthLabels(grid), [grid]);\r\n\r\n const outlineColor = theme === \"light\" ? \"#333\" : \"#f0f0f0\";\r\n\r\n const bestDateLabel = bestDay\r\n ? new Date(bestDay.date).toLocaleDateString(\"en-US\", {\r\n month: \"short\",\r\n day: \"numeric\",\r\n year: \"numeric\",\r\n })\r\n : null;\r\n\r\n const periodLabel =\r\n selectedYear === \"last\" ? \" in the last year\" : ` in ${selectedYear}`;\r\n\r\n return (\r\n <section className={[\"w-full font-mono\", className].join(\" \").trim()}>\r\n <div className=\"flex flex-wrap items-center gap-x-6 gap-y-1 mb-4 text-xs text-neutral-500 dark:text-neutral-400\">\r\n {loading ? (\r\n \"Loading…\"\r\n ) : error ? (\r\n \"Unavailable\"\r\n ) : (\r\n <>\r\n <span className=\"font-semibold text-neutral-700 dark:text-neutral-200\">\r\n {total?.toLocaleString()}\r\n </span>\r\n {\" contributions\"}\r\n {periodLabel}\r\n </>\r\n )}\r\n\r\n {bestDay && !loading && (\r\n <span className=\"flex items-center gap-1.5\">\r\n <span\r\n className=\"inline-block w-[10px] h-[10px] rounded-[2px]\"\r\n style={{ backgroundColor: colors[4] }}\r\n />\r\n Best day:\r\n <span className=\"font-semibold text-neutral-700 dark:text-neutral-200\">\r\n {bestDay.count} contributions\r\n </span>\r\n on {bestDateLabel}\r\n </span>\r\n )}\r\n </div>\r\n\r\n <div className=\"border border-neutral-200 dark:border-neutral-800 rounded-xl p-4 bg-white dark:bg-neutral-950\">\r\n {error ? (\r\n <p className=\"text-sm italic text-neutral-400\">\r\n GitHub activity unavailable.\r\n </p>\r\n ) : loading ? (\r\n <Skeleton />\r\n ) : (\r\n <>\r\n <div className=\"overflow-x-auto overflow-y-visible pb-1\">\r\n <div\r\n className=\"relative\"\r\n style={{\r\n minWidth: grid.length * STEP + DAY_COL_W + 8,\r\n }}\r\n >\r\n <div\r\n className=\"relative h-[18px] mb-1\"\r\n style={{ marginLeft: DAY_COL_W }}\r\n >\r\n {labels.map(({ label, col }) => (\r\n <span\r\n key={`${label}-${col}`}\r\n className=\"absolute text-[11px] select-none text-neutral-500 dark:text-neutral-400\"\r\n style={{ left: col * STEP }}\r\n >\r\n {label}\r\n </span>\r\n ))}\r\n </div>\r\n\r\n <div className=\"flex gap-[3px]\">\r\n <div\r\n className=\"flex flex-col gap-[3px] pt-px shrink-0\"\r\n style={{ width: DAY_COL_W - GAP }}\r\n >\r\n {Array.from({ length: 7 }).map((_, i) => (\r\n <div\r\n key={i}\r\n className=\"h-[13px] text-[10px] leading-[13px] text-right pr-1.5 select-none text-neutral-500 dark:text-neutral-400\"\r\n >\r\n {DAY_LABELS[i] ?? \"\"}\r\n </div>\r\n ))}\r\n </div>\r\n\r\n {grid.map((week, wi) => (\r\n <div key={wi} className=\"flex flex-col gap-[3px]\">\r\n {week.map((day, di) => (\r\n <div\r\n key={di}\r\n className=\"w-[13px] h-[13px] rounded-[3px] transition-transform duration-100 hover:scale-125\"\r\n style={{\r\n backgroundColor: day\r\n ? colors[day.level]\r\n : \"transparent\",\r\n cursor: day ? \"pointer\" : \"default\",\r\n outline:\r\n bestDay && day?.date === bestDay.date\r\n ? `2px solid ${outlineColor}`\r\n : \"none\",\r\n outlineOffset: \"1px\",\r\n }}\r\n onMouseEnter={(e) => {\r\n if (!day) return;\r\n\r\n const r = e.currentTarget.getBoundingClientRect();\r\n\r\n setTip({\r\n day,\r\n x: r.left + r.width / 2,\r\n y: r.top,\r\n });\r\n }}\r\n onMouseLeave={() => setTip(null)}\r\n />\r\n ))}\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-center justify-start gap-1 mt-3\">\r\n <span className=\"text-[11px] text-neutral-500 dark:text-neutral-400 mr-1\">\r\n Less\r\n </span>\r\n\r\n {colors.map((c, i) => (\r\n <div\r\n key={i}\r\n className=\"w-[13px] h-[13px] rounded-[3px]\"\r\n style={{ backgroundColor: c }}\r\n />\r\n ))}\r\n\r\n <span className=\"text-[11px] text-neutral-500 dark:text-neutral-400 ml-1\">\r\n More\r\n </span>\r\n </div>\r\n\r\n <div className=\"flex flex-wrap items-center gap-1.5 mt-4 pt-3 border-t border-neutral-100 dark:border-neutral-800\">\r\n <span className=\"text-[11px] text-neutral-400 mr-1\">Year:</span>\r\n\r\n <YearPill\r\n label=\"Last year\"\r\n active={selectedYear === \"last\"}\r\n onClick={() => setSelectedYear(\"last\")}\r\n />\r\n\r\n {years.map((y) => (\r\n <YearPill\r\n key={y}\r\n label={String(y)}\r\n active={selectedYear === y}\r\n onClick={() => setSelectedYear(y)}\r\n />\r\n ))}\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n\r\n {tip && <Tooltip day={tip.day} x={tip.x} y={tip.y} />}\r\n </section>\r\n );\r\n}\r\n\r\nexport default memo(GithubActivity);\r\n"],"mappings":"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","names":["_"]}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
// src/GithubActivity.tsx
|
|
2
|
+
import { memo, useEffect, useState, useMemo } from "react";
|
|
3
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
4
|
+
var MONTHS = [
|
|
5
|
+
"Jan",
|
|
6
|
+
"Feb",
|
|
7
|
+
"Mar",
|
|
8
|
+
"Apr",
|
|
9
|
+
"May",
|
|
10
|
+
"Jun",
|
|
11
|
+
"Jul",
|
|
12
|
+
"Aug",
|
|
13
|
+
"Sep",
|
|
14
|
+
"Oct",
|
|
15
|
+
"Nov",
|
|
16
|
+
"Dec"
|
|
17
|
+
];
|
|
18
|
+
var DAY_LABELS = { 1: "Mon", 3: "Wed", 5: "Fri" };
|
|
19
|
+
var CELL = 13;
|
|
20
|
+
var GAP = 3;
|
|
21
|
+
var STEP = CELL + GAP;
|
|
22
|
+
var DAY_COL_W = 30;
|
|
23
|
+
var LIGHT = ["#ebedf0", "#9be9a8", "#40c463", "#30a14e", "#216e39"];
|
|
24
|
+
var DARK = ["#21262d", "#0e4429", "#006d32", "#26a641", "#39d353"];
|
|
25
|
+
var BLUE = ["#21262d", "#a3c9ff", "#5fa3ff", "#2f7bff", "#0b5cff"];
|
|
26
|
+
var PURPLE = ["#21262d", "#d8b4ff", "#c084fc", "#a855f7", "#7e22ce"];
|
|
27
|
+
var COLOR_MAP = {
|
|
28
|
+
light: LIGHT,
|
|
29
|
+
dark: DARK,
|
|
30
|
+
blue: BLUE,
|
|
31
|
+
purple: PURPLE
|
|
32
|
+
};
|
|
33
|
+
function toLevel(count) {
|
|
34
|
+
if (count === 0) return 0;
|
|
35
|
+
if (count <= 3) return 1;
|
|
36
|
+
if (count <= 6) return 2;
|
|
37
|
+
if (count <= 9) return 3;
|
|
38
|
+
return 4;
|
|
39
|
+
}
|
|
40
|
+
function buildGrid(dayMap, year) {
|
|
41
|
+
let start;
|
|
42
|
+
let end;
|
|
43
|
+
if (year === "last") {
|
|
44
|
+
end = /* @__PURE__ */ new Date();
|
|
45
|
+
start = new Date(end);
|
|
46
|
+
start.setFullYear(start.getFullYear() - 1);
|
|
47
|
+
start.setDate(start.getDate() + 1);
|
|
48
|
+
} else {
|
|
49
|
+
start = new Date(year, 0, 1);
|
|
50
|
+
end = year === (/* @__PURE__ */ new Date()).getFullYear() ? /* @__PURE__ */ new Date() : new Date(year, 11, 31);
|
|
51
|
+
}
|
|
52
|
+
start.setDate(start.getDate() - start.getDay());
|
|
53
|
+
const weeks = [];
|
|
54
|
+
const cur = new Date(start);
|
|
55
|
+
while (cur <= end) {
|
|
56
|
+
const week = [];
|
|
57
|
+
for (let d = 0; d < 7; d++) {
|
|
58
|
+
const iso = cur.toISOString().slice(0, 10);
|
|
59
|
+
if (cur > end) {
|
|
60
|
+
week.push(null);
|
|
61
|
+
} else {
|
|
62
|
+
week.push(
|
|
63
|
+
dayMap.get(iso) ?? {
|
|
64
|
+
date: iso,
|
|
65
|
+
count: 0,
|
|
66
|
+
level: 0
|
|
67
|
+
}
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
cur.setDate(cur.getDate() + 1);
|
|
71
|
+
}
|
|
72
|
+
weeks.push(week);
|
|
73
|
+
}
|
|
74
|
+
return weeks;
|
|
75
|
+
}
|
|
76
|
+
function monthLabels(grid) {
|
|
77
|
+
const out = [];
|
|
78
|
+
let last = -1;
|
|
79
|
+
grid.forEach((week, i) => {
|
|
80
|
+
const first = week.find(Boolean);
|
|
81
|
+
if (!first) return;
|
|
82
|
+
const m = new Date(first.date).getMonth();
|
|
83
|
+
if (m !== last) {
|
|
84
|
+
out.push({ label: MONTHS[m], col: i });
|
|
85
|
+
last = m;
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return out;
|
|
89
|
+
}
|
|
90
|
+
function Tooltip({ day, x, y }) {
|
|
91
|
+
const label = new Date(day.date).toLocaleDateString("en-US", {
|
|
92
|
+
weekday: "long",
|
|
93
|
+
year: "numeric",
|
|
94
|
+
month: "long",
|
|
95
|
+
day: "numeric"
|
|
96
|
+
});
|
|
97
|
+
return /* @__PURE__ */ jsxs(
|
|
98
|
+
"div",
|
|
99
|
+
{
|
|
100
|
+
className: "fixed z-50 pointer-events-none -translate-x-1/2 -translate-y-full px-2.5 py-1.5 rounded-md text-xs font-mono whitespace-nowrap shadow-xl bg-neutral-900 text-white dark:bg-white dark:text-neutral-900",
|
|
101
|
+
style: { left: x, top: y - 8 },
|
|
102
|
+
children: [
|
|
103
|
+
/* @__PURE__ */ jsxs("b", { children: [
|
|
104
|
+
day.count,
|
|
105
|
+
" contribution",
|
|
106
|
+
day.count !== 1 ? "s" : ""
|
|
107
|
+
] }),
|
|
108
|
+
" ",
|
|
109
|
+
"\xB7 ",
|
|
110
|
+
label
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
function Skeleton() {
|
|
116
|
+
return /* @__PURE__ */ jsx("div", { className: "flex gap-[3px] animate-pulse opacity-40", children: Array.from({ length: 53 }).map((_, w) => /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-[3px]", children: Array.from({ length: 7 }).map((_2, d) => /* @__PURE__ */ jsx(
|
|
117
|
+
"div",
|
|
118
|
+
{
|
|
119
|
+
className: "w-[13px] h-[13px] rounded-[3px] bg-neutral-300 dark:bg-neutral-700"
|
|
120
|
+
},
|
|
121
|
+
d
|
|
122
|
+
)) }, w)) });
|
|
123
|
+
}
|
|
124
|
+
function YearPill({
|
|
125
|
+
label,
|
|
126
|
+
active,
|
|
127
|
+
onClick
|
|
128
|
+
}) {
|
|
129
|
+
return /* @__PURE__ */ jsx(
|
|
130
|
+
"button",
|
|
131
|
+
{
|
|
132
|
+
onClick,
|
|
133
|
+
className: [
|
|
134
|
+
"px-2.5 py-0.5 rounded-full text-[11px] font-medium transition-colors duration-150",
|
|
135
|
+
active ? "bg-neutral-900 text-white dark:bg-white dark:text-neutral-900" : "bg-neutral-100 text-neutral-600 hover:bg-neutral-200 dark:bg-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-700"
|
|
136
|
+
].join(" "),
|
|
137
|
+
children: label
|
|
138
|
+
}
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function GithubActivity({ username, theme = "dark", className }) {
|
|
142
|
+
const [allDays, setAllDays] = useState(/* @__PURE__ */ new Map());
|
|
143
|
+
const [years, setYears] = useState([]);
|
|
144
|
+
const [selectedYear, setSelectedYear] = useState("last");
|
|
145
|
+
const [total, setTotal] = useState(null);
|
|
146
|
+
const [bestDay, setBestDay] = useState(null);
|
|
147
|
+
const [loading, setLoading] = useState(true);
|
|
148
|
+
const [error, setError] = useState(false);
|
|
149
|
+
const [tip, setTip] = useState(
|
|
150
|
+
null
|
|
151
|
+
);
|
|
152
|
+
const colors = COLOR_MAP[theme] ?? COLOR_MAP.dark;
|
|
153
|
+
useEffect(() => {
|
|
154
|
+
if (!username) return;
|
|
155
|
+
let mounted = true;
|
|
156
|
+
setLoading(true);
|
|
157
|
+
setError(false);
|
|
158
|
+
(async () => {
|
|
159
|
+
try {
|
|
160
|
+
const res = await fetch(
|
|
161
|
+
`https://github-contributions-api.jogruber.de/v4/${username}?y=all`
|
|
162
|
+
);
|
|
163
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
164
|
+
const json = await res.json();
|
|
165
|
+
const raw = json.contributions ?? [];
|
|
166
|
+
if (!raw.length) throw new Error("empty data");
|
|
167
|
+
const map = /* @__PURE__ */ new Map();
|
|
168
|
+
const yearSet = /* @__PURE__ */ new Set();
|
|
169
|
+
raw.forEach((c) => {
|
|
170
|
+
map.set(c.date, {
|
|
171
|
+
date: c.date,
|
|
172
|
+
count: c.count,
|
|
173
|
+
level: toLevel(c.count)
|
|
174
|
+
});
|
|
175
|
+
yearSet.add(new Date(c.date).getFullYear());
|
|
176
|
+
});
|
|
177
|
+
if (mounted) {
|
|
178
|
+
setAllDays(map);
|
|
179
|
+
setYears(Array.from(yearSet).sort((a, b) => b - a));
|
|
180
|
+
setLoading(false);
|
|
181
|
+
}
|
|
182
|
+
} catch (err) {
|
|
183
|
+
console.warn("GithubActivity fetch error:", err);
|
|
184
|
+
if (mounted) {
|
|
185
|
+
setError(true);
|
|
186
|
+
setLoading(false);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
})();
|
|
190
|
+
return () => {
|
|
191
|
+
mounted = false;
|
|
192
|
+
};
|
|
193
|
+
}, [username]);
|
|
194
|
+
useEffect(() => {
|
|
195
|
+
if (!allDays.size) return;
|
|
196
|
+
const entries = Array.from(allDays.values()).filter((d) => {
|
|
197
|
+
if (selectedYear === "last") {
|
|
198
|
+
const oneYearAgo = /* @__PURE__ */ new Date();
|
|
199
|
+
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
|
|
200
|
+
return new Date(d.date) >= oneYearAgo;
|
|
201
|
+
}
|
|
202
|
+
return new Date(d.date).getFullYear() === selectedYear;
|
|
203
|
+
});
|
|
204
|
+
const sum = entries.reduce((s, d) => s + d.count, 0);
|
|
205
|
+
const best = entries.reduce(
|
|
206
|
+
(b, d) => !b || d.count > b.count ? d : b,
|
|
207
|
+
null
|
|
208
|
+
);
|
|
209
|
+
setTotal(sum);
|
|
210
|
+
setBestDay(best && best.count > 0 ? best : null);
|
|
211
|
+
}, [allDays, selectedYear]);
|
|
212
|
+
const grid = useMemo(
|
|
213
|
+
() => allDays.size ? buildGrid(allDays, selectedYear) : [],
|
|
214
|
+
[allDays, selectedYear]
|
|
215
|
+
);
|
|
216
|
+
const labels = useMemo(() => monthLabels(grid), [grid]);
|
|
217
|
+
const outlineColor = theme === "light" ? "#333" : "#f0f0f0";
|
|
218
|
+
const bestDateLabel = bestDay ? new Date(bestDay.date).toLocaleDateString("en-US", {
|
|
219
|
+
month: "short",
|
|
220
|
+
day: "numeric",
|
|
221
|
+
year: "numeric"
|
|
222
|
+
}) : null;
|
|
223
|
+
const periodLabel = selectedYear === "last" ? " in the last year" : ` in ${selectedYear}`;
|
|
224
|
+
return /* @__PURE__ */ jsxs("section", { className: ["w-full font-mono", className].join(" ").trim(), children: [
|
|
225
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-1 mb-4 text-xs text-neutral-500 dark:text-neutral-400", children: [
|
|
226
|
+
loading ? "Loading\u2026" : error ? "Unavailable" : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
227
|
+
/* @__PURE__ */ jsx("span", { className: "font-semibold text-neutral-700 dark:text-neutral-200", children: total?.toLocaleString() }),
|
|
228
|
+
" contributions",
|
|
229
|
+
periodLabel
|
|
230
|
+
] }),
|
|
231
|
+
bestDay && !loading && /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
|
|
232
|
+
/* @__PURE__ */ jsx(
|
|
233
|
+
"span",
|
|
234
|
+
{
|
|
235
|
+
className: "inline-block w-[10px] h-[10px] rounded-[2px]",
|
|
236
|
+
style: { backgroundColor: colors[4] }
|
|
237
|
+
}
|
|
238
|
+
),
|
|
239
|
+
"Best day:",
|
|
240
|
+
/* @__PURE__ */ jsxs("span", { className: "font-semibold text-neutral-700 dark:text-neutral-200", children: [
|
|
241
|
+
bestDay.count,
|
|
242
|
+
" contributions"
|
|
243
|
+
] }),
|
|
244
|
+
"on ",
|
|
245
|
+
bestDateLabel
|
|
246
|
+
] })
|
|
247
|
+
] }),
|
|
248
|
+
/* @__PURE__ */ jsx("div", { className: "border border-neutral-200 dark:border-neutral-800 rounded-xl p-4 bg-white dark:bg-neutral-950", children: error ? /* @__PURE__ */ jsx("p", { className: "text-sm italic text-neutral-400", children: "GitHub activity unavailable." }) : loading ? /* @__PURE__ */ jsx(Skeleton, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
249
|
+
/* @__PURE__ */ jsx("div", { className: "overflow-x-auto overflow-y-visible pb-1", children: /* @__PURE__ */ jsxs(
|
|
250
|
+
"div",
|
|
251
|
+
{
|
|
252
|
+
className: "relative",
|
|
253
|
+
style: {
|
|
254
|
+
minWidth: grid.length * STEP + DAY_COL_W + 8
|
|
255
|
+
},
|
|
256
|
+
children: [
|
|
257
|
+
/* @__PURE__ */ jsx(
|
|
258
|
+
"div",
|
|
259
|
+
{
|
|
260
|
+
className: "relative h-[18px] mb-1",
|
|
261
|
+
style: { marginLeft: DAY_COL_W },
|
|
262
|
+
children: labels.map(({ label, col }) => /* @__PURE__ */ jsx(
|
|
263
|
+
"span",
|
|
264
|
+
{
|
|
265
|
+
className: "absolute text-[11px] select-none text-neutral-500 dark:text-neutral-400",
|
|
266
|
+
style: { left: col * STEP },
|
|
267
|
+
children: label
|
|
268
|
+
},
|
|
269
|
+
`${label}-${col}`
|
|
270
|
+
))
|
|
271
|
+
}
|
|
272
|
+
),
|
|
273
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-[3px]", children: [
|
|
274
|
+
/* @__PURE__ */ jsx(
|
|
275
|
+
"div",
|
|
276
|
+
{
|
|
277
|
+
className: "flex flex-col gap-[3px] pt-px shrink-0",
|
|
278
|
+
style: { width: DAY_COL_W - GAP },
|
|
279
|
+
children: Array.from({ length: 7 }).map((_, i) => /* @__PURE__ */ jsx(
|
|
280
|
+
"div",
|
|
281
|
+
{
|
|
282
|
+
className: "h-[13px] text-[10px] leading-[13px] text-right pr-1.5 select-none text-neutral-500 dark:text-neutral-400",
|
|
283
|
+
children: DAY_LABELS[i] ?? ""
|
|
284
|
+
},
|
|
285
|
+
i
|
|
286
|
+
))
|
|
287
|
+
}
|
|
288
|
+
),
|
|
289
|
+
grid.map((week, wi) => /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-[3px]", children: week.map((day, di) => /* @__PURE__ */ jsx(
|
|
290
|
+
"div",
|
|
291
|
+
{
|
|
292
|
+
className: "w-[13px] h-[13px] rounded-[3px] transition-transform duration-100 hover:scale-125",
|
|
293
|
+
style: {
|
|
294
|
+
backgroundColor: day ? colors[day.level] : "transparent",
|
|
295
|
+
cursor: day ? "pointer" : "default",
|
|
296
|
+
outline: bestDay && day?.date === bestDay.date ? `2px solid ${outlineColor}` : "none",
|
|
297
|
+
outlineOffset: "1px"
|
|
298
|
+
},
|
|
299
|
+
onMouseEnter: (e) => {
|
|
300
|
+
if (!day) return;
|
|
301
|
+
const r = e.currentTarget.getBoundingClientRect();
|
|
302
|
+
setTip({
|
|
303
|
+
day,
|
|
304
|
+
x: r.left + r.width / 2,
|
|
305
|
+
y: r.top
|
|
306
|
+
});
|
|
307
|
+
},
|
|
308
|
+
onMouseLeave: () => setTip(null)
|
|
309
|
+
},
|
|
310
|
+
di
|
|
311
|
+
)) }, wi))
|
|
312
|
+
] })
|
|
313
|
+
]
|
|
314
|
+
}
|
|
315
|
+
) }),
|
|
316
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-start gap-1 mt-3", children: [
|
|
317
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-neutral-500 dark:text-neutral-400 mr-1", children: "Less" }),
|
|
318
|
+
colors.map((c, i) => /* @__PURE__ */ jsx(
|
|
319
|
+
"div",
|
|
320
|
+
{
|
|
321
|
+
className: "w-[13px] h-[13px] rounded-[3px]",
|
|
322
|
+
style: { backgroundColor: c }
|
|
323
|
+
},
|
|
324
|
+
i
|
|
325
|
+
)),
|
|
326
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-neutral-500 dark:text-neutral-400 ml-1", children: "More" })
|
|
327
|
+
] }),
|
|
328
|
+
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5 mt-4 pt-3 border-t border-neutral-100 dark:border-neutral-800", children: [
|
|
329
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-neutral-400 mr-1", children: "Year:" }),
|
|
330
|
+
/* @__PURE__ */ jsx(
|
|
331
|
+
YearPill,
|
|
332
|
+
{
|
|
333
|
+
label: "Last year",
|
|
334
|
+
active: selectedYear === "last",
|
|
335
|
+
onClick: () => setSelectedYear("last")
|
|
336
|
+
}
|
|
337
|
+
),
|
|
338
|
+
years.map((y) => /* @__PURE__ */ jsx(
|
|
339
|
+
YearPill,
|
|
340
|
+
{
|
|
341
|
+
label: String(y),
|
|
342
|
+
active: selectedYear === y,
|
|
343
|
+
onClick: () => setSelectedYear(y)
|
|
344
|
+
},
|
|
345
|
+
y
|
|
346
|
+
))
|
|
347
|
+
] })
|
|
348
|
+
] }) }),
|
|
349
|
+
tip && /* @__PURE__ */ jsx(Tooltip, { day: tip.day, x: tip.x, y: tip.y })
|
|
350
|
+
] });
|
|
351
|
+
}
|
|
352
|
+
var GithubActivity_default = memo(GithubActivity);
|
|
353
|
+
export {
|
|
354
|
+
GithubActivity_default as GithubActivity
|
|
355
|
+
};
|
|
356
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/GithubActivity.tsx"],"sourcesContent":["import { memo, useEffect, useState, useMemo } from \"react\";\r\n\r\ntype Theme = \"light\" | \"dark\" | \"blue\" | \"purple\";\r\n\r\ninterface Day {\r\n date: string;\r\n count: number;\r\n level: 0 | 1 | 2 | 3 | 4;\r\n}\r\n\r\ninterface Props {\r\n username: string;\r\n theme?: Theme;\r\n className?: string;\r\n}\r\n\r\n/* ---------- Constants ---------- */\r\n\r\nconst MONTHS = [\r\n \"Jan\",\r\n \"Feb\",\r\n \"Mar\",\r\n \"Apr\",\r\n \"May\",\r\n \"Jun\",\r\n \"Jul\",\r\n \"Aug\",\r\n \"Sep\",\r\n \"Oct\",\r\n \"Nov\",\r\n \"Dec\",\r\n];\r\nconst DAY_LABELS: Record<number, string> = { 1: \"Mon\", 3: \"Wed\", 5: \"Fri\" };\r\n\r\nconst CELL = 13;\r\nconst GAP = 3;\r\nconst STEP = CELL + GAP;\r\nconst DAY_COL_W = 30;\r\n\r\n/* ---------- Color Themes ---------- */\r\n\r\nconst LIGHT = [\"#ebedf0\", \"#9be9a8\", \"#40c463\", \"#30a14e\", \"#216e39\"];\r\nconst DARK = [\"#21262d\", \"#0e4429\", \"#006d32\", \"#26a641\", \"#39d353\"];\r\nconst BLUE = [\"#21262d\", \"#a3c9ff\", \"#5fa3ff\", \"#2f7bff\", \"#0b5cff\"];\r\nconst PURPLE = [\"#21262d\", \"#d8b4ff\", \"#c084fc\", \"#a855f7\", \"#7e22ce\"];\r\n\r\nconst COLOR_MAP: Record<Theme, string[]> = {\r\n light: LIGHT,\r\n dark: DARK,\r\n blue: BLUE,\r\n purple: PURPLE,\r\n};\r\n\r\n/* ---------- Utilities ---------- */\r\n\r\nfunction toLevel(count: number): 0 | 1 | 2 | 3 | 4 {\r\n if (count === 0) return 0;\r\n if (count <= 3) return 1;\r\n if (count <= 6) return 2;\r\n if (count <= 9) return 3;\r\n return 4;\r\n}\r\n\r\nfunction buildGrid(\r\n dayMap: Map<string, Day>,\r\n year: number | \"last\",\r\n): (Day | null)[][] {\r\n let start: Date;\r\n let end: Date;\r\n\r\n if (year === \"last\") {\r\n end = new Date();\r\n start = new Date(end);\r\n start.setFullYear(start.getFullYear() - 1);\r\n start.setDate(start.getDate() + 1);\r\n } else {\r\n start = new Date(year, 0, 1);\r\n end =\r\n year === new Date().getFullYear() ? new Date() : new Date(year, 11, 31);\r\n }\r\n\r\n start.setDate(start.getDate() - start.getDay());\r\n\r\n const weeks: (Day | null)[][] = [];\r\n const cur = new Date(start);\r\n\r\n while (cur <= end) {\r\n const week: (Day | null)[] = [];\r\n\r\n for (let d = 0; d < 7; d++) {\r\n const iso = cur.toISOString().slice(0, 10);\r\n\r\n if (cur > end) {\r\n week.push(null);\r\n } else {\r\n week.push(\r\n dayMap.get(iso) ?? {\r\n date: iso,\r\n count: 0,\r\n level: 0,\r\n },\r\n );\r\n }\r\n\r\n cur.setDate(cur.getDate() + 1);\r\n }\r\n\r\n weeks.push(week);\r\n }\r\n\r\n return weeks;\r\n}\r\n\r\nfunction monthLabels(grid: (Day | null)[][]) {\r\n const out: { label: string; col: number }[] = [];\r\n let last = -1;\r\n\r\n grid.forEach((week, i) => {\r\n const first = week.find(Boolean);\r\n if (!first) return;\r\n\r\n const m = new Date(first.date).getMonth();\r\n\r\n if (m !== last) {\r\n out.push({ label: MONTHS[m], col: i });\r\n last = m;\r\n }\r\n });\r\n\r\n return out;\r\n}\r\n\r\n/* ---------- UI Components ---------- */\r\n\r\nfunction Tooltip({ day, x, y }: { day: Day; x: number; y: number }) {\r\n const label = new Date(day.date).toLocaleDateString(\"en-US\", {\r\n weekday: \"long\",\r\n year: \"numeric\",\r\n month: \"long\",\r\n day: \"numeric\",\r\n });\r\n\r\n return (\r\n <div\r\n className=\"fixed z-50 pointer-events-none -translate-x-1/2 -translate-y-full px-2.5 py-1.5 rounded-md text-xs font-mono whitespace-nowrap shadow-xl bg-neutral-900 text-white dark:bg-white dark:text-neutral-900\"\r\n style={{ left: x, top: y - 8 }}\r\n >\r\n <b>\r\n {day.count} contribution{day.count !== 1 ? \"s\" : \"\"}\r\n </b>{\" \"}\r\n · {label}\r\n </div>\r\n );\r\n}\r\n\r\nfunction Skeleton() {\r\n return (\r\n <div className=\"flex gap-[3px] animate-pulse opacity-40\">\r\n {Array.from({ length: 53 }).map((_, w) => (\r\n <div key={w} className=\"flex flex-col gap-[3px]\">\r\n {Array.from({ length: 7 }).map((_, d) => (\r\n <div\r\n key={d}\r\n className=\"w-[13px] h-[13px] rounded-[3px] bg-neutral-300 dark:bg-neutral-700\"\r\n />\r\n ))}\r\n </div>\r\n ))}\r\n </div>\r\n );\r\n}\r\n\r\nfunction YearPill({\r\n label,\r\n active,\r\n onClick,\r\n}: {\r\n label: string;\r\n active: boolean;\r\n onClick: () => void;\r\n}) {\r\n return (\r\n <button\r\n onClick={onClick}\r\n className={[\r\n \"px-2.5 py-0.5 rounded-full text-[11px] font-medium transition-colors duration-150\",\r\n active\r\n ? \"bg-neutral-900 text-white dark:bg-white dark:text-neutral-900\"\r\n : \"bg-neutral-100 text-neutral-600 hover:bg-neutral-200 dark:bg-neutral-800 dark:text-neutral-400 dark:hover:bg-neutral-700\",\r\n ].join(\" \")}\r\n >\r\n {label}\r\n </button>\r\n );\r\n}\r\n\r\n/* ---------- Main Component ---------- */\r\n\r\nfunction GithubActivity({ username, theme = \"dark\", className }: Props) {\r\n const [allDays, setAllDays] = useState<Map<string, Day>>(new Map());\r\n const [years, setYears] = useState<number[]>([]);\r\n const [selectedYear, setSelectedYear] = useState<number | \"last\">(\"last\");\r\n const [total, setTotal] = useState<number | null>(null);\r\n const [bestDay, setBestDay] = useState<Day | null>(null);\r\n const [loading, setLoading] = useState(true);\r\n const [error, setError] = useState(false);\r\n const [tip, setTip] = useState<{ day: Day; x: number; y: number } | null>(\r\n null,\r\n );\r\n\r\n const colors = COLOR_MAP[theme] ?? COLOR_MAP.dark;\r\n\r\n /* Fetch Contributions */\r\n\r\n useEffect(() => {\r\n if (!username) return;\r\n\r\n let mounted = true;\r\n setLoading(true);\r\n setError(false);\r\n\r\n (async () => {\r\n try {\r\n const res = await fetch(\r\n `https://github-contributions-api.jogruber.de/v4/${username}?y=all`,\r\n );\r\n\r\n if (!res.ok) throw new Error(`HTTP ${res.status}`);\r\n\r\n const json = await res.json();\r\n const raw: { date: string; count: number }[] = json.contributions ?? [];\r\n\r\n if (!raw.length) throw new Error(\"empty data\");\r\n\r\n const map = new Map<string, Day>();\r\n const yearSet = new Set<number>();\r\n\r\n raw.forEach((c) => {\r\n map.set(c.date, {\r\n date: c.date,\r\n count: c.count,\r\n level: toLevel(c.count),\r\n });\r\n\r\n yearSet.add(new Date(c.date).getFullYear());\r\n });\r\n\r\n if (mounted) {\r\n setAllDays(map);\r\n setYears(Array.from(yearSet).sort((a, b) => b - a));\r\n setLoading(false);\r\n }\r\n } catch (err) {\r\n console.warn(\"GithubActivity fetch error:\", err);\r\n\r\n if (mounted) {\r\n setError(true);\r\n setLoading(false);\r\n }\r\n }\r\n })();\r\n\r\n return () => {\r\n mounted = false;\r\n };\r\n }, [username]);\r\n\r\n /* Stats Calculation */\r\n\r\n useEffect(() => {\r\n if (!allDays.size) return;\r\n\r\n const entries = Array.from(allDays.values()).filter((d) => {\r\n if (selectedYear === \"last\") {\r\n const oneYearAgo = new Date();\r\n oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);\r\n return new Date(d.date) >= oneYearAgo;\r\n }\r\n\r\n return new Date(d.date).getFullYear() === selectedYear;\r\n });\r\n\r\n const sum = entries.reduce((s, d) => s + d.count, 0);\r\n\r\n const best = entries.reduce<Day | null>(\r\n (b, d) => (!b || d.count > b.count ? d : b),\r\n null,\r\n );\r\n\r\n setTotal(sum);\r\n setBestDay(best && best.count > 0 ? best : null);\r\n }, [allDays, selectedYear]);\r\n\r\n /* Rendering Helpers */\r\n\r\n const grid = useMemo(\r\n () => (allDays.size ? buildGrid(allDays, selectedYear) : []),\r\n [allDays, selectedYear],\r\n );\r\n\r\n const labels = useMemo(() => monthLabels(grid), [grid]);\r\n\r\n const outlineColor = theme === \"light\" ? \"#333\" : \"#f0f0f0\";\r\n\r\n const bestDateLabel = bestDay\r\n ? new Date(bestDay.date).toLocaleDateString(\"en-US\", {\r\n month: \"short\",\r\n day: \"numeric\",\r\n year: \"numeric\",\r\n })\r\n : null;\r\n\r\n const periodLabel =\r\n selectedYear === \"last\" ? \" in the last year\" : ` in ${selectedYear}`;\r\n\r\n return (\r\n <section className={[\"w-full font-mono\", className].join(\" \").trim()}>\r\n <div className=\"flex flex-wrap items-center gap-x-6 gap-y-1 mb-4 text-xs text-neutral-500 dark:text-neutral-400\">\r\n {loading ? (\r\n \"Loading…\"\r\n ) : error ? (\r\n \"Unavailable\"\r\n ) : (\r\n <>\r\n <span className=\"font-semibold text-neutral-700 dark:text-neutral-200\">\r\n {total?.toLocaleString()}\r\n </span>\r\n {\" contributions\"}\r\n {periodLabel}\r\n </>\r\n )}\r\n\r\n {bestDay && !loading && (\r\n <span className=\"flex items-center gap-1.5\">\r\n <span\r\n className=\"inline-block w-[10px] h-[10px] rounded-[2px]\"\r\n style={{ backgroundColor: colors[4] }}\r\n />\r\n Best day:\r\n <span className=\"font-semibold text-neutral-700 dark:text-neutral-200\">\r\n {bestDay.count} contributions\r\n </span>\r\n on {bestDateLabel}\r\n </span>\r\n )}\r\n </div>\r\n\r\n <div className=\"border border-neutral-200 dark:border-neutral-800 rounded-xl p-4 bg-white dark:bg-neutral-950\">\r\n {error ? (\r\n <p className=\"text-sm italic text-neutral-400\">\r\n GitHub activity unavailable.\r\n </p>\r\n ) : loading ? (\r\n <Skeleton />\r\n ) : (\r\n <>\r\n <div className=\"overflow-x-auto overflow-y-visible pb-1\">\r\n <div\r\n className=\"relative\"\r\n style={{\r\n minWidth: grid.length * STEP + DAY_COL_W + 8,\r\n }}\r\n >\r\n <div\r\n className=\"relative h-[18px] mb-1\"\r\n style={{ marginLeft: DAY_COL_W }}\r\n >\r\n {labels.map(({ label, col }) => (\r\n <span\r\n key={`${label}-${col}`}\r\n className=\"absolute text-[11px] select-none text-neutral-500 dark:text-neutral-400\"\r\n style={{ left: col * STEP }}\r\n >\r\n {label}\r\n </span>\r\n ))}\r\n </div>\r\n\r\n <div className=\"flex gap-[3px]\">\r\n <div\r\n className=\"flex flex-col gap-[3px] pt-px shrink-0\"\r\n style={{ width: DAY_COL_W - GAP }}\r\n >\r\n {Array.from({ length: 7 }).map((_, i) => (\r\n <div\r\n key={i}\r\n className=\"h-[13px] text-[10px] leading-[13px] text-right pr-1.5 select-none text-neutral-500 dark:text-neutral-400\"\r\n >\r\n {DAY_LABELS[i] ?? \"\"}\r\n </div>\r\n ))}\r\n </div>\r\n\r\n {grid.map((week, wi) => (\r\n <div key={wi} className=\"flex flex-col gap-[3px]\">\r\n {week.map((day, di) => (\r\n <div\r\n key={di}\r\n className=\"w-[13px] h-[13px] rounded-[3px] transition-transform duration-100 hover:scale-125\"\r\n style={{\r\n backgroundColor: day\r\n ? colors[day.level]\r\n : \"transparent\",\r\n cursor: day ? \"pointer\" : \"default\",\r\n outline:\r\n bestDay && day?.date === bestDay.date\r\n ? `2px solid ${outlineColor}`\r\n : \"none\",\r\n outlineOffset: \"1px\",\r\n }}\r\n onMouseEnter={(e) => {\r\n if (!day) return;\r\n\r\n const r = e.currentTarget.getBoundingClientRect();\r\n\r\n setTip({\r\n day,\r\n x: r.left + r.width / 2,\r\n y: r.top,\r\n });\r\n }}\r\n onMouseLeave={() => setTip(null)}\r\n />\r\n ))}\r\n </div>\r\n ))}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <div className=\"flex items-center justify-start gap-1 mt-3\">\r\n <span className=\"text-[11px] text-neutral-500 dark:text-neutral-400 mr-1\">\r\n Less\r\n </span>\r\n\r\n {colors.map((c, i) => (\r\n <div\r\n key={i}\r\n className=\"w-[13px] h-[13px] rounded-[3px]\"\r\n style={{ backgroundColor: c }}\r\n />\r\n ))}\r\n\r\n <span className=\"text-[11px] text-neutral-500 dark:text-neutral-400 ml-1\">\r\n More\r\n </span>\r\n </div>\r\n\r\n <div className=\"flex flex-wrap items-center gap-1.5 mt-4 pt-3 border-t border-neutral-100 dark:border-neutral-800\">\r\n <span className=\"text-[11px] text-neutral-400 mr-1\">Year:</span>\r\n\r\n <YearPill\r\n label=\"Last year\"\r\n active={selectedYear === \"last\"}\r\n onClick={() => setSelectedYear(\"last\")}\r\n />\r\n\r\n {years.map((y) => (\r\n <YearPill\r\n key={y}\r\n label={String(y)}\r\n active={selectedYear === y}\r\n onClick={() => setSelectedYear(y)}\r\n />\r\n ))}\r\n </div>\r\n </>\r\n )}\r\n </div>\r\n\r\n {tip && <Tooltip day={tip.day} x={tip.x} y={tip.y} />}\r\n </section>\r\n );\r\n}\r\n\r\nexport default memo(GithubActivity);\r\n"],"mappings":"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","names":["_"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "github-contributions-ui",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "GitHub contributions graph React component",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": ["dist"],
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsup",
|
|
11
|
+
"publish-package": "npm run build && npm publish --access public"
|
|
12
|
+
},
|
|
13
|
+
"keywords": ["github", "react", "contributions", "graph", "ui"],
|
|
14
|
+
"author": "",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"react": "^18.2.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/react": "^18.0.0",
|
|
21
|
+
"tsup": "^8.5.1",
|
|
22
|
+
"typescript": "^5.9.3"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|