import { AreaChart, Area, CartesianGrid, XAxis, YAxis, Tooltip, ResponsiveContainer, } from 'recharts'; import { CHART_DEFAULTS, CHART_THEME, TOOLTIP_CONTENT_STYLE } from '../../lib/chart-theme'; import { epochDayToDate } from '../../lib/formatters'; export interface PerAnimeDataPoint { epochDay: number; animeTitle: string; value: number; } interface StackedTrendChartProps { title: string; data: PerAnimeDataPoint[]; colorPalette?: string[]; } const DEFAULT_LINE_COLORS = [ '#8aadf4', '#c6a0f6', '#a6da95', '#f5a97f', '#f5bde6', '#91d7e3', '#ee99a0', '#f4dbd6', ]; function buildLineData(raw: PerAnimeDataPoint[]) { const totalByAnime = new Map(); for (const entry of raw) { totalByAnime.set(entry.animeTitle, (totalByAnime.get(entry.animeTitle) ?? 0) + entry.value); } const sorted = [...totalByAnime.entries()].sort((a, b) => b[1] - a[1]); const topTitles = sorted.slice(0, 7).map(([title]) => title); const topSet = new Set(topTitles); const byDay = new Map>(); for (const entry of raw) { if (!topSet.has(entry.animeTitle)) continue; const row = byDay.get(entry.epochDay) ?? {}; row[entry.animeTitle] = (row[entry.animeTitle] ?? 0) + Math.round(entry.value * 10) / 10; byDay.set(entry.epochDay, row); } const points = [...byDay.entries()] .sort(([a], [b]) => a - b) .map(([epochDay, values]) => { const row: Record = { label: epochDayToDate(epochDay).toLocaleDateString(undefined, { month: 'short', day: 'numeric', }), }; for (const title of topTitles) { row[title] = values[title] ?? 0; } return row; }); return { points, seriesKeys: topTitles }; } export function StackedTrendChart({ title, data, colorPalette }: StackedTrendChartProps) { const { points, seriesKeys } = buildLineData(data); const colors = colorPalette ?? DEFAULT_LINE_COLORS; if (points.length === 0) { return (

{title}

No data
); } return (

{title}

{seriesKeys.map((key, i) => ( ))}
{seriesKeys.map((key, i) => ( {key} ))}
); }