From 325bddb5282627df6d325ba1c1b3196c918ad6c7 Mon Sep 17 00:00:00 2001 From: ssbg04 Date: Sat, 12 Sep 2026 10:42:16 +0800 Subject: [PATCH] feat(github): make contributions matrix responsive full width with mobile horizontal scroll --- src/components/GithubContributions.tsx | 124 +++++++++++++++---------- 1 file changed, 75 insertions(+), 49 deletions(-) diff --git a/src/components/GithubContributions.tsx b/src/components/GithubContributions.tsx index 24b3cb2..452304c 100644 --- a/src/components/GithubContributions.tsx +++ b/src/components/GithubContributions.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useState, useMemo } from 'react'; +import React, { useEffect, useState, useMemo, useRef } from 'react'; import haptic from '../lib/haptics'; interface ContributionDay { @@ -25,6 +25,7 @@ export default function GithubContributions({ username = 'ssbg04', className = ' const [loading, setLoading] = useState(true); const [error, setError] = useState(false); const [hoveredDay, setHoveredDay] = useState(null); + const scrollContainerRef = useRef(null); useEffect(() => { let isMounted = true; @@ -63,6 +64,19 @@ export default function GithubContributions({ username = 'ssbg04', className = ' return result; }, [data]); + // On mobile / horizontal scroll view, auto-scroll to the latest contributions (rightmost) + useEffect(() => { + if (scrollContainerRef.current && weeks.length > 0) { + const container = scrollContainerRef.current; + const timer = setTimeout(() => { + if (container) { + container.scrollLeft = container.scrollWidth; + } + }, 60); + return () => clearTimeout(timer); + } + }, [weeks]); + // Generate month headers const monthLabels = useMemo(() => { if (weeks.length === 0) return []; @@ -73,7 +87,7 @@ export default function GithubContributions({ username = 'ssbg04', className = ' if (week.length > 0) { const d = new Date(week[0].date); const m = d.getMonth(); - if (m !== lastMonth && index - (labels[labels.length - 1]?.index ?? -10) >= 3) { + if (m !== lastMonth && index - (labels[labels.length - 1]?.index ?? -10) >= 4) { labels.push({ name: d.toLocaleDateString('en-US', { month: 'short' }), index @@ -172,53 +186,64 @@ export default function GithubContributions({ username = 'ssbg04', className = ' /> ) : ( -
- {/* Month Labels along the top */} -
-
- {weeks.map((week, wIdx) => { - const label = monthLabels.find((l) => l.index === wIdx); - return ( -
- {label && ( - - {label.name} - - )} +
+
+ {/* Month Labels along the top */} +
+ {/* Spacer matching weekday labels */} +
+ + {/* Month columns */} +
+ {weeks.map((week, wIdx) => { + const label = monthLabels.find((l) => l.index === wIdx); + const isNearEnd = wIdx >= weeks.length - 2; + return ( +
+ {label && ( + + {label.name} + + )} +
+ ); + })} +
+
+ + {/* Heatmap with Row Labels on Left */} +
+ {/* Weekday indicators matching 7 grid rows exactly */} +
+ Sun + Mon + Tue + Wed + Thu + Fri + Sat +
+ + {/* Grid Columns (Weeks) */} +
+ {weeks.map((week, wIdx) => ( +
+ {week.map((day, dIdx) => ( +
setHoveredDay(day)} + onMouseLeave={() => setHoveredDay(null)} + onClick={() => setHoveredDay(day)} + className={`w-full aspect-square rounded-[1.5px] sm:rounded-[3px] transition-all cursor-pointer hover:scale-125 hover:z-10 relative ${getLevelColor(day.level)}`} + title={`${day.count} contributions on ${formatDate(day.date)}`} + /> + ))}
- ); - })} -
-
- - {/* Heatmap with Row Labels on Left */} -
- {/* Weekday indicators */} -
- Sun - Mon - Tue - Wed - Thu - Fri - Sat -
- - {/* Grid Columns (Weeks) */} -
- {weeks.map((week, wIdx) => ( -
- {week.map((day, dIdx) => ( -
setHoveredDay(day)} - onMouseLeave={() => setHoveredDay(null)} - className={`w-[11px] h-[11px] rounded-[2.5px] transition-all cursor-pointer ${getLevelColor(day.level)}`} - title={`${day.count} contributions on ${formatDate(day.date)}`} - /> - ))} -
- ))} + ))} +
@@ -234,7 +259,8 @@ export default function GithubContributions({ username = 'ssbg04', className = ' ) : ( - Hover over squares to see daily contribution activity + Hover over squares to see daily contribution activity + Swipe matrix horizontally • Tap squares to inspect )}