heatmap.tsx (3424B)
1 "use client" 2 3 import { Calendar } from "lucide-react" 4 5 export interface HeatmapDay { 6 date: string 7 count: number 8 } 9 10 interface HeatmapProps { 11 data: HeatmapDay[] 12 title: string 13 colorIntensityLabel?: string 14 className?: string 15 } 16 17 export function Heatmap({ data, title, colorIntensityLabel = "Activity", className = "" }: HeatmapProps) { 18 // Get intensity color based on count 19 const getColor = (count: number) => { 20 if (count === 0) return "bg-gray-900" 21 if (count < 3) return "bg-purple-950" 22 if (count < 6) return "bg-purple-900" 23 if (count < 9) return "bg-purple-800" 24 return "bg-purple-600" 25 } 26 27 // Group data by week 28 const weeks: HeatmapDay[][] = [] 29 let currentWeek: HeatmapDay[] = [] 30 31 // Process data to create weeks (7 days per week) 32 data.forEach((day, index) => { 33 currentWeek.push(day) 34 if (currentWeek.length === 7 || index === data.length - 1) { 35 weeks.push([...currentWeek]) 36 currentWeek = [] 37 } 38 }) 39 40 return ( 41 <div className={`space-y-2 ${className}`}> 42 <div className="flex items-center justify-between"> 43 <div className="flex items-center gap-2"> 44 <Calendar className="h-4 w-4 text-gray-400" /> 45 <h3 className="text-sm font-medium">{title}</h3> 46 </div> 47 <div className="flex items-center gap-1 text-xs text-gray-400"> 48 <span>Less</span> 49 <div className="flex gap-1"> 50 <div className="h-3 w-3 rounded-sm bg-gray-900"></div> 51 <div className="h-3 w-3 rounded-sm bg-purple-950"></div> 52 <div className="h-3 w-3 rounded-sm bg-purple-900"></div> 53 <div className="h-3 w-3 rounded-sm bg-purple-800"></div> 54 <div className="h-3 w-3 rounded-sm bg-purple-600"></div> 55 </div> 56 <span>More</span> 57 </div> 58 </div> 59 60 <div className="flex gap-2"> 61 {/* Days of week labels */} 62 <div className="flex flex-col pr-2 text-xs text-gray-500"> 63 <div className="h-[18px] mb-[2px]">Mon</div> 64 <div className="h-[18px] mb-[2px]">Tue</div> 65 <div className="h-[18px] mb-[2px]">Wed</div> 66 <div className="h-[18px] mb-[2px]">Thu</div> 67 <div className="h-[18px] mb-[2px]">Fri</div> 68 <div className="h-[18px] mb-[2px]">Sat</div> 69 <div className="h-[18px]">Sun</div> 70 </div> 71 72 {/* Heatmap grid */} 73 <div className="overflow-x-auto"> 74 <div className="flex gap-1 min-w-[650px]"> 75 {weeks.map((week, weekIndex) => ( 76 <div key={weekIndex} className="flex flex-col gap-[2px]"> 77 {week.map((day, dayIndex) => ( 78 <div 79 key={`${weekIndex}-${dayIndex}`} 80 className={`h-[18px] w-[18px] rounded-sm ${getColor(day.count)} transition-opacity hover:opacity-80 cursor-pointer`} 81 title={`${day.date}: ${day.count} ${colorIntensityLabel}`} 82 ></div> 83 ))} 84 </div> 85 ))} 86 </div> 87 88 {/* Month labels */} 89 <div className="flex text-xs text-gray-500 mt-1 min-w-[650px]"> 90 {["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"].map((month, i) => ( 91 <div key={i} className="flex-1"> 92 {month} 93 </div> 94 ))} 95 </div> 96 </div> 97 </div> 98 </div> 99 ) 100 }