'use client'; import { useState, useEffect, useMemo, useRef } from 'react'; import { Database, Filter, Image as ImageIcon, FileText, Loader2, ChevronDown, ChevronLeft, ChevronRight, Search, X, } from 'lucide-react'; import { clsx } from 'clsx'; import { ExampleCard } from './ExampleCard'; import { useDataset } from '@/lib/dataset/DatasetProvider'; import { TASK_LABELS, CATEGORY_LABELS } from '@/config/constants'; import type { DatasetExample, TaskType, Category } from '@/types'; interface ExamplesPanelProps { onSelectExample: (example: DatasetExample) => void; } type ModalityFilter = 'all' | 'multimodal' | 'text-only'; type Split = 'train' | 'validation' | 'test'; const ITEMS_PER_PAGE = 25; export function ExamplesPanel({ onSelectExample }: ExamplesPanelProps) { const { isLoading: isDatasetLoading, loadedSplits, splitCounts, filterExamples, loadSplit } = useDataset(); const [typeFilter, setTypeFilter] = useState('all'); const [categoryFilter, setCategoryFilter] = useState('all'); const [modalityFilter, setModalityFilter] = useState('all'); const [showFilters, setShowFilters] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [debouncedSearch, setDebouncedSearch] = useState(''); const [split, setSplit] = useState('test'); const [currentPage, setCurrentPage] = useState(0); const scrollContainerRef = useRef(null); const searchInputRef = useRef(null); // Load train split if selected (not loaded by default) useEffect(() => { if (split === 'train' && !loadedSplits.has('train')) { loadSplit('train'); } }, [split, loadedSplits, loadSplit]); // Debounce search useEffect(() => { const timer = setTimeout(() => { setDebouncedSearch(searchQuery); setCurrentPage(0); }, 300); return () => clearTimeout(timer); }, [searchQuery]); // Reset page when filters change useEffect(() => { setCurrentPage(0); }, [split, typeFilter, categoryFilter, modalityFilter, debouncedSearch]); // Filter locally loaded data const { examples, totalExamples } = useMemo(() => { if (!loadedSplits.has(split)) { return { examples: [], totalExamples: 0 }; } const filters: { type?: TaskType; category?: Category; hasImage?: boolean; search?: string; } = {}; if (typeFilter !== 'all') filters.type = typeFilter; if (categoryFilter !== 'all') filters.category = categoryFilter; if (modalityFilter === 'multimodal') filters.hasImage = true; else if (modalityFilter === 'text-only') filters.hasImage = false; if (debouncedSearch) filters.search = debouncedSearch; const result = filterExamples(split, filters, ITEMS_PER_PAGE, currentPage * ITEMS_PER_PAGE); return { examples: result.examples, totalExamples: result.total }; }, [loadedSplits, split, filterExamples, typeFilter, categoryFilter, modalityFilter, debouncedSearch, currentPage]); // Scroll to top on page change useEffect(() => { if (scrollContainerRef.current) { scrollContainerRef.current.scrollTop = 0; } }, [currentPage]); const totalPages = Math.ceil(totalExamples / ITEMS_PER_PAGE); const stats = useMemo(() => ({ total: totalExamples, displayed: examples.length, }), [examples, totalExamples]); const clearSearch = () => { setSearchQuery(''); searchInputRef.current?.focus(); }; const handleKeyDown = (e: React.KeyboardEvent) => { if (e.key === 'Escape') { clearSearch(); } }; const isLoading = isDatasetLoading || !loadedSplits.has(split); return (
{/* Header */}

Dataset Examples

{isLoading && ( )}
{/* Split Selector */}
{(['train', 'validation', 'test'] as Split[]).map((s) => ( ))}
{/* Search */}
setSearchQuery(e.target.value)} onKeyDown={handleKeyDown} placeholder="Search examples..." className="w-full bg-zinc-800/80 border border-zinc-700/50 rounded-lg pl-9 pr-8 py-2 text-sm text-zinc-300 placeholder:text-zinc-600 focus:outline-none focus:ring-1 focus:ring-teal-600/50 focus:border-teal-700/50" /> {searchQuery && ( )}
{/* Stats */}
{stats.total} examples {debouncedSearch && ( <> | "{debouncedSearch}" )}
{/* Filters Toggle */} {/* Filter Options */} {showFilters && (
{(['all', 'multimodal', 'text-only'] as ModalityFilter[]).map((mode) => ( ))}
{/* Clear Filters */} {(typeFilter !== 'all' || categoryFilter !== 'all' || modalityFilter !== 'all') && ( )}
)}
{/* Examples List */}
{isLoading ? (
Loading examples...
) : examples.length === 0 ? (

No examples match your filters

{debouncedSearch && ( )}
) : (

Showing {currentPage * ITEMS_PER_PAGE + 1}–{Math.min((currentPage + 1) * ITEMS_PER_PAGE, totalExamples)} of {totalExamples}

{examples.map((example) => ( ))}
)}
{/* Pagination */} {totalPages > 1 && !isLoading && (
{(() => { const maxVisible = 3; const pages: (number | 'ellipsis')[] = []; if (totalPages <= maxVisible + 2) { for (let i = 0; i < totalPages; i++) pages.push(i); } else { pages.push(0); if (currentPage > 2) { pages.push('ellipsis'); } const start = Math.max(1, currentPage - 1); const end = Math.min(totalPages - 2, currentPage + 1); for (let i = start; i <= end; i++) { if (!pages.includes(i)) pages.push(i); } if (currentPage < totalPages - 3) { pages.push('ellipsis'); } if (!pages.includes(totalPages - 1)) { pages.push(totalPages - 1); } } return pages.map((page, idx) => { if (page === 'ellipsis') { return ( ); } return ( ); }); })()}
)}
); }