import { useMemo, useState } from 'react'; import { PosBadge } from './pos-helpers'; import { fullReading } from '../../lib/reading-utils'; import type { VocabularyEntry } from '../../types/stats'; interface FrequencyRankTableProps { words: VocabularyEntry[]; knownWords: Set; onSelectWord?: (word: VocabularyEntry) => void; } const PAGE_SIZE = 25; export function FrequencyRankTable({ words, knownWords, onSelectWord }: FrequencyRankTableProps) { const [page, setPage] = useState(0); const [hideKnown, setHideKnown] = useState(true); const [collapsed, setCollapsed] = useState(false); const hasKnownData = knownWords.size > 0; const isWordKnown = (w: VocabularyEntry): boolean => { return knownWords.has(w.headword) || knownWords.has(w.word); }; const ranked = useMemo(() => { let filtered = words.filter((w) => w.frequencyRank != null && w.frequencyRank > 0); if (hideKnown && hasKnownData) { filtered = filtered.filter((w) => !isWordKnown(w)); } const byHeadword = new Map(); for (const w of filtered) { const existing = byHeadword.get(w.headword); if (!existing) { byHeadword.set(w.headword, { ...w }); } else { existing.frequency += w.frequency; existing.animeCount = Math.max(existing.animeCount, w.animeCount); if (w.frequencyRank! < existing.frequencyRank!) { existing.frequencyRank = w.frequencyRank; } if (!existing.reading && w.reading) { existing.reading = w.reading; } if (!existing.partOfSpeech && w.partOfSpeech) { existing.partOfSpeech = w.partOfSpeech; } } } return [...byHeadword.values()].sort((a, b) => a.frequencyRank! - b.frequencyRank!); }, [words, knownWords, hideKnown, hasKnownData]); if (words.every((w) => w.frequencyRank == null)) { return (

Most Common Words Seen

No frequency rank data available. Run the frequency backfill script or install a frequency dictionary.
); } const totalPages = Math.ceil(ranked.length / PAGE_SIZE); const paged = ranked.slice(page * PAGE_SIZE, (page + 1) * PAGE_SIZE); return (
{hasKnownData && ( )} {ranked.length} words
{collapsed ? null : ranked.length === 0 ? (
{hideKnown ? 'All ranked words are already in Anki!' : 'No words with frequency data.'}
) : ( <>
{paged.map((w) => ( onSelectWord?.(w)} className="border-b border-ctp-surface1 last:border-0 cursor-pointer hover:bg-ctp-surface1/50 transition-colors" > ))}
Rank Word Reading POS Seen
#{w.frequencyRank!.toLocaleString()} {w.headword} {fullReading(w.headword, w.reading) || w.headword} {w.partOfSpeech && } {w.frequency}x
{totalPages > 1 && (
{page + 1} / {totalPages}
)} )}
); }