File size: 4,165 Bytes
3857bd9
fdb7c58
d088c21
fdb7c58
cd201c7
 
494bbae
cd201c7
 
fdb7c58
 
 
 
 
d088c21
 
929d75e
 
cd201c7
fdb7c58
 
cd201c7
 
 
 
 
 
 
 
 
 
3857bd9
 
98fcc8e
3857bd9
 
98fcc8e
3857bd9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98fcc8e
3857bd9
 
cd201c7
4c668a3
 
 
 
 
 
 
 
 
 
 
 
fbae805
4c668a3
494bbae
fdb7c58
d088c21
929d75e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d088c21
929d75e
fdb7c58
cd201c7
 
 
 
4c668a3
cd201c7
3857bd9
4c668a3
 
3857bd9
cd201c7
 
 
66f319d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
127
128
129
130
131
132
133
134
135
136
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 the correct color progression based on current theme
  const getThemeAwareColors = () => {
    const colors = [emptyColor]; // level 0
    
    for (let level = 1; level <= 4; level++) {
      const intensity = getHeatmapColorIntensity(level, color, isDarkMode);
      if (intensity) {
        colors.push(intensity);
      }
    }
    return colors;
  };
  
  const themeColors = getThemeAwareColors();
  
  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
            key={`${isDarkMode}-${color}`} // Force re-render on theme change
            data={processedData}
            theme={{
              light: themeColors,
              dark: themeColors
            }}
            blockSize={11}
            blockMargin={2}
            hideTotalCount
            renderBlock={(block, activity) => {
              const activityData = activity as any; // Type assertion since react-activity-calendar may not have our extended type
              const itemsText = activityData.items && activityData.items.length > 0 
                ? activityData.items.join(', ') 
                : 'No releases';
              
              const tooltipTitle = activity.count > 0 
                ? `${activity.count} new repos on ${activity.date}: ${itemsText}`
                : `No repos on ${activity.date}`;
                
              return (
                <Tooltip
                  title={tooltipTitle}
                  arrow
                >
                  {block}
                </Tooltip>
              );
            }}
          />
        )}
      </div>
    </div>
  );
};

export default Heatmap;