import { useState } from 'react'; import { useTrends, type TimeRange, type GroupBy } from '../../hooks/useTrends'; import { DateRangeSelector } from './DateRangeSelector'; import { TrendChart } from './TrendChart'; import { StackedTrendChart } from './StackedTrendChart'; import { buildAnimeVisibilityOptions, filterHiddenAnimeData, pruneHiddenAnime, } from './anime-visibility'; function SectionHeader({ children }: { children: React.ReactNode }) { return (

{children}

); } interface AnimeVisibilityFilterProps { animeTitles: string[]; hiddenAnime: ReadonlySet; onShowAll: () => void; onHideAll: () => void; onToggleAnime: (title: string) => void; } function AnimeVisibilityFilter({ animeTitles, hiddenAnime, onShowAll, onHideAll, onToggleAnime, }: AnimeVisibilityFilterProps) { if (animeTitles.length === 0) { return null; } return (

Anime Visibility

Shared across all anime trend charts. Default: show everything.

{animeTitles.map((title) => { const isVisible = !hiddenAnime.has(title); return ( ); })}
); } export function TrendsTab() { const [range, setRange] = useState('30d'); const [groupBy, setGroupBy] = useState('day'); const [hiddenAnime, setHiddenAnime] = useState>(() => new Set()); const { data, loading, error } = useTrends(range, groupBy); const cardsMinedColor = 'var(--color-ctp-cards-mined)'; const cardsMinedStackedColors = [ cardsMinedColor, '#8aadf4', '#c6a0f6', '#f5a97f', '#f5bde6', '#91d7e3', '#ee99a0', '#f4dbd6', ]; if (loading) return
Loading...
; if (error) return
Error: {error}
; if (!data) return null; const animeTitles = buildAnimeVisibilityOptions([ data.animePerDay.episodes, data.animePerDay.watchTime, data.animePerDay.cards, data.animePerDay.words, data.animePerDay.lookups, data.animeCumulative.episodes, data.animeCumulative.cards, data.animeCumulative.words, data.animeCumulative.watchTime, ]); const activeHiddenAnime = pruneHiddenAnime(hiddenAnime, animeTitles); const filteredEpisodesPerAnime = filterHiddenAnimeData( data.animePerDay.episodes, activeHiddenAnime, ); const filteredWatchTimePerAnime = filterHiddenAnimeData( data.animePerDay.watchTime, activeHiddenAnime, ); const filteredCardsPerAnime = filterHiddenAnimeData(data.animePerDay.cards, activeHiddenAnime); const filteredWordsPerAnime = filterHiddenAnimeData(data.animePerDay.words, activeHiddenAnime); const filteredLookupsPerAnime = filterHiddenAnimeData( data.animePerDay.lookups, activeHiddenAnime, ); const filteredLookupsPerHundredPerAnime = filterHiddenAnimeData( data.animePerDay.lookupsPerHundred, activeHiddenAnime, ); const filteredAnimeProgress = filterHiddenAnimeData( data.animeCumulative.episodes, activeHiddenAnime, ); const filteredCardsProgress = filterHiddenAnimeData( data.animeCumulative.cards, activeHiddenAnime, ); const filteredWordsProgress = filterHiddenAnimeData( data.animeCumulative.words, activeHiddenAnime, ); const filteredWatchTimeProgress = filterHiddenAnimeData( data.animeCumulative.watchTime, activeHiddenAnime, ); return (
Activity Period Trends Anime — Per Day setHiddenAnime(new Set())} onHideAll={() => setHiddenAnime(new Set(animeTitles))} onToggleAnime={(title) => setHiddenAnime((current) => { const next = new Set(current); if (next.has(title)) { next.delete(title); } else { next.add(title); } return next; }) } /> Anime — Cumulative Patterns
); }