Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
open_llm_leaderboard
/
frontend
/src
/pages
/LeaderboardPage
/components
/Leaderboard
/Leaderboard.js
import React, { useMemo, useEffect, useCallback } from "react"; | |
import { Box, Typography } from "@mui/material"; | |
import { useSearchParams } from "react-router-dom"; | |
import { TABLE_DEFAULTS } from "./constants/defaults"; | |
import { useLeaderboard } from "./context/LeaderboardContext"; | |
import { useLeaderboardProcessing } from "./hooks/useLeaderboardData"; | |
import { useLeaderboardData } from "./hooks/useLeaderboardData"; | |
import LeaderboardFilters from "./components/Filters/Filters"; | |
import LeaderboardTable from "./components/Table/Table"; | |
import SearchBar, { SearchBarSkeleton } from "./components/Filters/SearchBar"; | |
import PerformanceMonitor from "./components/PerformanceMonitor"; | |
import QuickFilters, { | |
QuickFiltersSkeleton, | |
} from "./components/Filters/QuickFilters"; | |
const FilterAccordion = ({ expanded, quickFilters, advancedFilters }) => { | |
const advancedFiltersRef = React.useRef(null); | |
const quickFiltersRef = React.useRef(null); | |
const [height, setHeight] = React.useState("auto"); | |
const resizeTimeoutRef = React.useRef(null); | |
const updateHeight = React.useCallback(() => { | |
if (expanded && advancedFiltersRef.current) { | |
setHeight(`${advancedFiltersRef.current.scrollHeight}px`); | |
} else if (!expanded && quickFiltersRef.current) { | |
setHeight(`${quickFiltersRef.current.scrollHeight}px`); | |
} | |
}, [expanded]); | |
React.useEffect(() => { | |
// Initial height calculation | |
const timer = setTimeout(updateHeight, 100); | |
// Resize handler with debounce | |
const handleResize = () => { | |
if (resizeTimeoutRef.current) { | |
clearTimeout(resizeTimeoutRef.current); | |
} | |
resizeTimeoutRef.current = setTimeout(updateHeight, 150); | |
}; | |
window.addEventListener("resize", handleResize); | |
return () => { | |
clearTimeout(timer); | |
window.removeEventListener("resize", handleResize); | |
if (resizeTimeoutRef.current) { | |
clearTimeout(resizeTimeoutRef.current); | |
} | |
}; | |
}, [updateHeight]); | |
// Update height when expanded state changes | |
React.useEffect(() => { | |
updateHeight(); | |
}, [expanded, updateHeight]); | |
return ( | |
<Box | |
sx={{ | |
position: "relative", | |
width: "100%", | |
height, | |
transition: "height 0.3s ease", | |
mb: 0.5, | |
overflow: "hidden", | |
}} | |
> | |
<Box | |
ref={quickFiltersRef} | |
sx={{ | |
position: expanded ? "absolute" : "relative", | |
top: 0, | |
left: 0, | |
right: 0, | |
opacity: expanded ? 0 : 1, | |
visibility: expanded ? "hidden" : "visible", | |
transition: "opacity 0.3s ease", | |
mb: 0, | |
}} | |
> | |
{quickFilters} | |
</Box> | |
<Box | |
ref={advancedFiltersRef} | |
sx={{ | |
position: !expanded ? "absolute" : "relative", | |
top: 0, | |
left: 0, | |
right: 0, | |
opacity: expanded ? 1 : 0, | |
visibility: !expanded ? "hidden" : "visible", | |
transition: "opacity 0.3s ease", | |
mt: 0, | |
}} | |
> | |
{advancedFilters} | |
</Box> | |
</Box> | |
); | |
}; | |
const Leaderboard = () => { | |
const { state, actions } = useLeaderboard(); | |
const [searchParams, setSearchParams] = useSearchParams(); | |
const { | |
data, | |
isLoading: dataLoading, | |
error: dataError, | |
} = useLeaderboardData(); | |
const { | |
table, | |
filteredData, | |
error: processingError, | |
} = useLeaderboardProcessing(); | |
// Memoize filtered data | |
const memoizedFilteredData = useMemo(() => filteredData, [filteredData]); | |
const memoizedTable = useMemo(() => table, [table]); | |
// Memoize table options | |
const hasTableOptionsChanges = useMemo(() => { | |
return ( | |
state.display.rowSize !== TABLE_DEFAULTS.ROW_SIZE || | |
JSON.stringify(state.display.scoreDisplay) !== | |
JSON.stringify(TABLE_DEFAULTS.SCORE_DISPLAY) || | |
state.display.averageMode !== TABLE_DEFAULTS.AVERAGE_MODE || | |
state.display.rankingMode !== TABLE_DEFAULTS.RANKING_MODE | |
); | |
}, [state.display]); | |
const hasColumnFilterChanges = useMemo(() => { | |
return ( | |
JSON.stringify([...state.display.visibleColumns].sort()) !== | |
JSON.stringify([...TABLE_DEFAULTS.COLUMNS.DEFAULT_VISIBLE].sort()) | |
); | |
}, [state.display.visibleColumns]); | |
// Memoize callbacks | |
const onToggleFilters = useCallback(() => { | |
actions.toggleFiltersExpanded(); | |
}, [actions]); | |
const onColumnVisibilityChange = useCallback( | |
(newVisibility) => { | |
actions.setDisplayOption( | |
"visibleColumns", | |
Object.keys(newVisibility).filter((key) => newVisibility[key]) | |
); | |
}, | |
[actions] | |
); | |
const onRowSizeChange = useCallback( | |
(size) => { | |
actions.setDisplayOption("rowSize", size); | |
}, | |
[actions] | |
); | |
const onScoreDisplayChange = useCallback( | |
(display) => { | |
actions.setDisplayOption("scoreDisplay", display); | |
}, | |
[actions] | |
); | |
const onAverageModeChange = useCallback( | |
(mode) => { | |
actions.setDisplayOption("averageMode", mode); | |
}, | |
[actions] | |
); | |
const onRankingModeChange = useCallback( | |
(mode) => { | |
actions.setDisplayOption("rankingMode", mode); | |
}, | |
[actions] | |
); | |
const onPrecisionsChange = useCallback( | |
(precisions) => { | |
actions.setFilter("precisions", precisions); | |
}, | |
[actions] | |
); | |
const onTypesChange = useCallback( | |
(types) => { | |
actions.setFilter("types", types); | |
}, | |
[actions] | |
); | |
const onParamsRangeChange = useCallback( | |
(range) => { | |
actions.setFilter("paramsRange", range); | |
}, | |
[actions] | |
); | |
const onBooleanFiltersChange = useCallback( | |
(filters) => { | |
actions.setFilter("booleanFilters", filters); | |
}, | |
[actions] | |
); | |
const onReset = useCallback(() => { | |
actions.resetFilters(); | |
}, [actions]); | |
// Memoize loading states | |
const loadingStates = useMemo(() => { | |
const isInitialLoading = dataLoading || !data; | |
const isProcessingData = !memoizedTable || !memoizedFilteredData; | |
const isApplyingFilters = state.models.length > 0 && !memoizedFilteredData; | |
const hasValidFilterCounts = | |
state.countsReady && | |
state.filterCounts && | |
state.filterCounts.normal && | |
state.filterCounts.officialOnly; | |
return { | |
isInitialLoading, | |
isProcessingData, | |
isApplyingFilters, | |
showSearchSkeleton: isInitialLoading || !hasValidFilterCounts, | |
showFiltersSkeleton: isInitialLoading || !hasValidFilterCounts, | |
showTableSkeleton: | |
isInitialLoading || | |
isProcessingData || | |
isApplyingFilters || | |
!hasValidFilterCounts, | |
}; | |
}, [ | |
dataLoading, | |
data, | |
memoizedTable, | |
memoizedFilteredData, | |
state.models.length, | |
state.filterCounts, | |
state.countsReady, | |
]); | |
// Memoize child components | |
const memoizedSearchBar = useMemo( | |
() => ( | |
<SearchBar | |
onToggleFilters={onToggleFilters} | |
filtersOpen={state.filtersExpanded} | |
loading={loadingStates.showTableSkeleton} | |
data={memoizedFilteredData} | |
table={table} | |
/> | |
), | |
[ | |
onToggleFilters, | |
state.filtersExpanded, | |
loadingStates.showTableSkeleton, | |
memoizedFilteredData, | |
table, | |
] | |
); | |
const memoizedQuickFilters = useMemo( | |
() => ( | |
<QuickFilters | |
totalCount={state.models.length} | |
filteredCount={memoizedFilteredData?.length || 0} | |
data={memoizedFilteredData} | |
table={memoizedTable} | |
/> | |
), | |
[state.models.length, memoizedFilteredData, memoizedTable] | |
); | |
const memoizedLeaderboardFilters = useMemo( | |
() => ( | |
<LeaderboardFilters | |
data={memoizedFilteredData} | |
loading={loadingStates.showFiltersSkeleton} | |
selectedPrecisions={state.filters.precisions} | |
onPrecisionsChange={onPrecisionsChange} | |
selectedTypes={state.filters.types} | |
onTypesChange={onTypesChange} | |
paramsRange={state.filters.paramsRange} | |
onParamsRangeChange={onParamsRangeChange} | |
selectedBooleanFilters={state.filters.booleanFilters} | |
onBooleanFiltersChange={onBooleanFiltersChange} | |
onReset={onReset} | |
/> | |
), | |
[ | |
memoizedFilteredData, | |
loadingStates.showFiltersSkeleton, | |
state.filters.precisions, | |
state.filters.types, | |
state.filters.paramsRange, | |
state.filters.booleanFilters, | |
onPrecisionsChange, | |
onTypesChange, | |
onParamsRangeChange, | |
onBooleanFiltersChange, | |
onReset, | |
] | |
); | |
// No need to memoize LeaderboardTable as it handles its own sorting state | |
const tableComponent = ( | |
<LeaderboardTable | |
table={table} | |
loading={loadingStates.showTableSkeleton} | |
onColumnVisibilityChange={onColumnVisibilityChange} | |
hasTableOptionsChanges={hasTableOptionsChanges} | |
hasColumnFilterChanges={hasColumnFilterChanges} | |
rowSize={state.display.rowSize} | |
onRowSizeChange={onRowSizeChange} | |
scoreDisplay={state.display.scoreDisplay} | |
onScoreDisplayChange={onScoreDisplayChange} | |
averageMode={state.display.averageMode} | |
onAverageModeChange={onAverageModeChange} | |
rankingMode={state.display.rankingMode} | |
onRankingModeChange={onRankingModeChange} | |
searchParams={searchParams} | |
setSearchParams={setSearchParams} | |
pinnedModels={state.pinnedModels} | |
/> | |
); | |
// Update context with loaded data | |
useEffect(() => { | |
if (data) { | |
actions.setModels(data); | |
} | |
}, [data, actions]); | |
// Log to understand loading state | |
useEffect(() => { | |
if (process.env.NODE_ENV === "development") { | |
console.log("Loading state:", { | |
dataLoading, | |
hasData: !!data, | |
hasTable: !!table, | |
hasFilteredData: !!filteredData, | |
filteredDataLength: filteredData?.length, | |
stateModelsLength: state.models.length, | |
hasFilters: Object.keys(state.filters).some((key) => { | |
if (Array.isArray(state.filters[key])) { | |
return state.filters[key].length > 0; | |
} | |
return !!state.filters[key]; | |
}), | |
}); | |
} | |
}, [ | |
dataLoading, | |
data, | |
table, | |
filteredData?.length, | |
state.models.length, | |
filteredData, | |
state.filters, | |
]); | |
// If an error occurred, display it | |
if (dataError || processingError) { | |
return ( | |
<Box sx={{ p: 3, textAlign: "center" }}> | |
<Typography color="error"> | |
{(dataError || processingError)?.message || | |
"An error occurred while loading the data"} | |
</Typography> | |
</Box> | |
); | |
} | |
return ( | |
<Box | |
sx={{ | |
width: "100%", | |
display: "flex", | |
flexDirection: "column", | |
}} | |
> | |
<PerformanceMonitor /> | |
<Box | |
sx={{ | |
display: "flex", | |
flexDirection: "column", | |
gap: 0, | |
alignItems: "center", | |
}} | |
> | |
<Box | |
sx={{ | |
width: { | |
xs: "100%", | |
sm: "100%", | |
md: "80%", | |
}, | |
maxWidth: "1200px", | |
}} | |
> | |
{loadingStates.showSearchSkeleton ? ( | |
<SearchBarSkeleton /> | |
) : ( | |
memoizedSearchBar | |
)} | |
<Box sx={{ mt: 1 }}> | |
{loadingStates.showFiltersSkeleton ? ( | |
<QuickFiltersSkeleton /> | |
) : ( | |
<FilterAccordion | |
expanded={state.filtersExpanded} | |
quickFilters={memoizedQuickFilters} | |
advancedFilters={memoizedLeaderboardFilters} | |
/> | |
)} | |
</Box> | |
</Box> | |
<Box | |
sx={{ | |
display: "flex", | |
alignItems: "flex-start", | |
justifyContent: "center", | |
width: "100%", | |
overflow: "hidden", | |
}} | |
> | |
<Box | |
sx={{ | |
width: "100%", | |
px: 1, | |
}} | |
> | |
{tableComponent} | |
</Box> | |
</Box> | |
</Box> | |
</Box> | |
); | |
}; | |
export default Leaderboard; | |