File size: 4,013 Bytes
3857bd9 fdb7c58 d088c21 fdb7c58 cd201c7 494bbae cd201c7 fdb7c58 d088c21 929d75e cd201c7 fdb7c58 cd201c7 3857bd9 98fcc8e 3857bd9 98fcc8e 3857bd9 98fcc8e 3857bd9 cd201c7 fbae805 494bbae fbae805 494bbae fdb7c58 d088c21 929d75e d088c21 929d75e fdb7c58 cd201c7 3857bd9 fbae805 3857bd9 cd201c7 3857bd9 cd201c7 fdb7c58 3857bd9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
import React, { useEffect, useState } from "react";
import ActivityCalendar from "react-activity-calendar";
import { Tooltip, Avatar } from "@mui/material";
import Link from "next/link";
import { aggregateToWeeklyData } from "../utils/weeklyCalendar";
import WeeklyHeatmap from "./WeeklyHeatmap";
import { getHeatmapTheme, getHeatmapColorIntensity } from "../utils/heatmapColors";
type ViewMode = 'daily' | 'weekly';
type HeatmapProps = {
data: Array<{ date: string; count: number; level: number }>;
color: string;
providerName: string;
fullName: string;
avatarUrl: string;
authorId: string;
showHeader?: boolean;
viewMode: ViewMode;
};
const Heatmap: React.FC<HeatmapProps> = ({
data,
color,
providerName,
fullName,
avatarUrl,
authorId,
showHeader = true,
viewMode
}) => {
// Process data based on view mode
const processedData = viewMode === 'weekly' ? aggregateToWeeklyData(data) : data;
// Track theme state for proper ActivityCalendar theming
const [isDarkMode, setIsDarkMode] = useState(false);
useEffect(() => {
// Check initial theme
const checkTheme = () => {
setIsDarkMode(document.documentElement.classList.contains('dark'));
};
checkTheme();
// Watch for theme changes
const observer = new MutationObserver(checkTheme);
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
});
return () => observer.disconnect();
}, []);
// Use theme-aware colors
const emptyColor = isDarkMode ? "#374151" : "#d1d5db";
// Create separate intensity levels for light and dark modes
const lightIntensityColors = [
emptyColor, // level 0
getHeatmapColorIntensity(1, color, false), // level 1 (bright for light mode)
getHeatmapColorIntensity(2, color, false), // level 2
getHeatmapColorIntensity(3, color, false), // level 3
getHeatmapColorIntensity(4, color, false) // level 4 (darkest for light mode)
].filter((color): color is string => color !== null);
const darkIntensityColors = [
emptyColor, // level 0
getHeatmapColorIntensity(1, color, true), // level 1 (darker for dark mode)
getHeatmapColorIntensity(2, color, true), // level 2
getHeatmapColorIntensity(3, color, true), // level 3
getHeatmapColorIntensity(4, color, true) // level 4 (brightest for dark mode)
].filter((color): color is string => color !== null);
return (
<div className="flex flex-col items-center w-full mx-auto">
{showHeader && (
<div className="flex flex-col sm:flex-row items-center mb-4 w-full justify-center">
{avatarUrl && (
<Avatar src={avatarUrl} alt={fullName} className="mb-2 sm:mb-0 sm:mr-4" sx={{ width: 48, height: 48 }} />
)}
<div className="text-center sm:text-left">
<h2 className="text-lg font-semibold">
<Link
href={`https://huggingface.co/${authorId}`}
target="_blank"
rel="noopener noreferrer"
className="hover:text-blue-500 hover:underline"
>
{fullName}
</Link>
</h2>
</div>
</div>
)}
<div className="w-full overflow-x-auto flex justify-center">
{viewMode === 'weekly' ? (
<WeeklyHeatmap data={processedData} color={color} />
) : (
<ActivityCalendar
data={processedData}
theme={{
light: lightIntensityColors,
dark: darkIntensityColors
}}
blockSize={11}
blockMargin={2}
hideTotalCount
renderBlock={(block, activity) => (
<Tooltip
title={`${activity.count} new repos on ${activity.date}`}
arrow
>
{block}
</Tooltip>
)}
/>
)}
</div>
</div>
);
};
export default Heatmap;
|