11// src/Table/CSVTable.jsx
2- import React , { useState , useEffect , useMemo , useCallback } from 'react' ;
2+ import React , { useState , useEffect , useMemo , useCallback , useRef } from 'react' ;
33import Papa from 'papaparse' ;
44import { calculateAverage , getGlobalAverage } from './Averaging' ;
55import { useTable } from "./SortTable" ;
66import { getModelInfo , getVariantGroup } from './modelLinks' ;
77import { useSearchParams } from 'react-router-dom' ;
88import Select from 'react-select' ;
9+ import { buildCurrentViewData , buildFullData , downloadCSV , downloadJSON , downloadExcel } from './downloadTable' ;
910
1011
1112const CSVTable = ( { dateStr} ) => {
@@ -24,7 +25,25 @@ const CSVTable = ({dateStr}) => {
2425 const [ showVariants , setShowVariants ] = useState ( false ) ;
2526 const [ showHighUnseenBias , setShowHighUnseenBias ] = useState ( true ) ;
2627
27- const updateURL = ( checkedCategories , newFilter , newSortField = null , newSortOrder = null , newShowProvider = null , newShowApiName = null , newShowReasoners = null , newShowOpenWeights = null , newShowVariants = null , newShowHighUnseenBias = null , newSearchQuery = null ) => {
28+ const [ showDownloadMenu , setShowDownloadMenu ] = useState ( false ) ;
29+ const [ downloadScope , setDownloadScope ] = useState ( 'current' ) ;
30+ const [ downloadFormats , setDownloadFormats ] = useState ( { csv : false , json : false , xlsx : false } ) ;
31+ const [ panelPos , setPanelPos ] = useState ( { top : 0 , left : 0 } ) ;
32+ const downloadMenuRef = useRef ( null ) ;
33+ const downloadBtnRef = useRef ( null ) ;
34+
35+ useEffect ( ( ) => {
36+ if ( ! showDownloadMenu ) return ;
37+ const handleClickOutside = ( e ) => {
38+ if ( downloadMenuRef . current && ! downloadMenuRef . current . contains ( e . target ) ) {
39+ setShowDownloadMenu ( false ) ;
40+ }
41+ } ;
42+ document . addEventListener ( 'mousedown' , handleClickOutside ) ;
43+ return ( ) => document . removeEventListener ( 'mousedown' , handleClickOutside ) ;
44+ } , [ showDownloadMenu ] ) ;
45+
46+ const updateURL = ( checkedCategories , newFilter , newSortField = null , newSortOrder = null , newShowProvider = null , newShowApiName = null , newShowReasoners = null , newShowOpenWeights = null , newShowVariants = null , newShowHighUnseenBias = null , newSearchQuery = null ) => {
2847 const params = new URLSearchParams ( ) ;
2948
3049 let allAverages = true ;
@@ -426,6 +445,18 @@ const CSVTable = ({dateStr}) => {
426445 updateURL ( defaultCategories , { } , 'ga' , 'desc' , true , false , true , false , false , false , '' ) ;
427446 }
428447
448+ const handleDownload = ( ) => {
449+ const rows = downloadScope === 'current'
450+ ? buildCurrentViewData ( displayedData , checkedCategories , categories , { showProvider, showApiName, displayNameCounts } )
451+ : buildFullData ( data , categories ) ;
452+ const filename = `livebench_${ downloadScope === 'current' ? 'current_view' : 'full_data' } ` ;
453+ if ( downloadFormats . csv ) downloadCSV ( rows , `${ filename } .csv` ) ;
454+ if ( downloadFormats . json ) downloadJSON ( rows , `${ filename } .json` ) ;
455+ if ( downloadFormats . xlsx ) downloadExcel ( rows , `${ filename } .xlsx` ) ;
456+ setDownloadFormats ( { csv : false , json : false , xlsx : false } ) ;
457+ setShowDownloadMenu ( false ) ;
458+ } ;
459+
429460 // Utility to compute class for sorting
430461 const getSortClass = ( accessor ) => {
431462 return sortField === accessor ? ( sortOrder === "asc" ? "up" : "down" ) : "default" ;
@@ -559,7 +590,58 @@ const CSVTable = ({dateStr}) => {
559590 < input type = "checkbox" checked = { showHighUnseenBias } onChange = { ( ) => setShowHighUnseenBias ( ! showHighUnseenBias ) } id = "showHighUnseenBias" />
560591 < span style = { { marginLeft : '0.5rem' } } > Show High Unseen Question Bias Models</ span >
561592 </ label >
593+ </ div >
594+ < div className = "action-buttons" >
562595 < button onClick = { handleResetFilters } className = "clear-filters-button" > Clear Filters</ button >
596+ < div className = "download-dropdown" ref = { downloadMenuRef } >
597+ < button ref = { downloadBtnRef } onClick = { ( ) => {
598+ if ( ! showDownloadMenu && downloadBtnRef . current ) {
599+ const rect = downloadBtnRef . current . getBoundingClientRect ( ) ;
600+ setPanelPos ( { top : rect . bottom + 4 , left : rect . left } ) ;
601+ }
602+ setShowDownloadMenu ( ! showDownloadMenu ) ;
603+ } } className = "download-button" >
604+ < i className = "fa fa-download" style = { { marginRight : '0.4rem' } } > </ i > Download
605+ </ button >
606+ { showDownloadMenu && (
607+ < div className = "download-panel" style = { { top : panelPos . top , left : panelPos . left } } >
608+ < div className = "download-panel-body" >
609+ < div className = "download-panel-scope" >
610+ < div className = "download-panel-heading" > Data</ div >
611+ < label >
612+ < input type = "radio" name = "downloadScope" checked = { downloadScope === 'current' } onChange = { ( ) => setDownloadScope ( 'current' ) } />
613+ < span > Current View</ span >
614+ </ label >
615+ < label >
616+ < input type = "radio" name = "downloadScope" checked = { downloadScope === 'full' } onChange = { ( ) => setDownloadScope ( 'full' ) } />
617+ < span > Full Data</ span >
618+ </ label >
619+ </ div >
620+ < div className = "download-panel-formats" >
621+ < div className = "download-panel-heading" > Format</ div >
622+ < label >
623+ < input type = "checkbox" checked = { downloadFormats . csv } onChange = { ( ) => setDownloadFormats ( f => ( { ...f , csv : ! f . csv } ) ) } />
624+ < span > CSV</ span >
625+ </ label >
626+ < label >
627+ < input type = "checkbox" checked = { downloadFormats . json } onChange = { ( ) => setDownloadFormats ( f => ( { ...f , json : ! f . json } ) ) } />
628+ < span > JSON</ span >
629+ </ label >
630+ < label >
631+ < input type = "checkbox" checked = { downloadFormats . xlsx } onChange = { ( ) => setDownloadFormats ( f => ( { ...f , xlsx : ! f . xlsx } ) ) } />
632+ < span > Excel (.xlsx)</ span >
633+ </ label >
634+ </ div >
635+ </ div >
636+ < button
637+ className = "download-panel-action"
638+ disabled = { ! downloadFormats . csv && ! downloadFormats . json && ! downloadFormats . xlsx }
639+ onClick = { handleDownload } >
640+ Download
641+ </ button >
642+ </ div >
643+ ) }
644+ </ div >
563645 </ div >
564646 < div className = "search-bar" >
565647 < input
0 commit comments