import { Button , CssBaseline , InputLabel , MenuItem , TextField } from '@material-ui/core' ; import React , { useCallback } from 'react' ; import { CellProps , FilterProps , FilterValue , IdType , Row , TableInstance } from 'react-table' ; import { Page } from './Page' ; import { Table } from './Table' ; import { PersonData , makeData } from './utils' ; function roundedMedian ( values ) { let min = values [ 0 ] || '' ; let max = values [ 0 ] || '' ; values . forEach ( ( value ) => { min = Math . min ( min , value ) ; max = Math . max ( max , value ) ; } ) ; return Math . round ( ( min + max ) / 2 ) ; } function filterGreaterThan ( rows , id , filterValue ) { return rows . filter ( ( row ) => { const rowValue = row . values [ id [ 0 ] ] ; return rowValue >= filterValue ; } ) ; } filterGreaterThan . autoRemove = ( val ) => typeof val !== 'number' ; function SelectColumnFilter ( { column : { filterValue , render , setFilter , preFilteredRows , id } , } ) { const options = React . useMemo ( ( ) => { const options = new Set < any > ( ) ; preFilteredRows . forEach ( ( row ) => { options . add ( row . values [ id ] ) ; } ) ; return [ ... Array . from ( options . values ( ) ) ] ; } , [ id , preFilteredRows ] ) ; return ( < TextField select label = { render ( 'Header' ) } value = { filterValue || '' } onChange = { ( e ) => { setFilter ( e . target . value || undefined ) ; } } > < MenuItem value = { '' } > All < /MenuItem> { options . map ( ( option , i ) => ( < MenuItem key = { i } value = { option } > { option } < /MenuItem>))} < /TextField>); } const getMinMax = ( rows , id ) => { let min = rows . length ? rows [ 0 ] . values [ id ] : 0 ; let max = rows . length ? rows [ 0 ] . values [ id ] : 0 ; rows . forEach ( ( row ) => { min = Math . min ( row . values [ id ] , min ) ; max = Math . max ( row . values [ id ] , max ) ; } ) ; return [ min , max ] ; } ; function SliderColumnFilter ( { column : { render , filterValue , setFilter , preFilteredRows , id } , } ) { const [ min , max ] = React . useMemo ( ( ) => getMinMax ( preFilteredRows , id ) , [ id , preFilteredRows ] ) ; return ( < div style = { { display : 'flex' , justifyContent : 'space-between' , alignItems : 'baseline' , } } > < TextField name = { id } label = { render ( 'Header' ) } type = 'range' inputProps = { { min , max , } } value = { filterValue || min } onChange = { ( e ) => { setFilter ( parseInt ( e . target . value , 10 ) ) ; } } /> < Button variant = 'outlined' style = { { width : 60 , height : 36 } } onClick = { ( ) => setFilter ( undefined ) } > Off < /Button> < /div>); } const useActiveElement = ( ) => { const [ active , setActive ] = React . useState ( document . activeElement ) ; const handleFocusIn = ( ) => { setActive ( document . activeElement ) ; } ; React . useEffect ( ( ) => { document . addEventListener ( 'focusin' , handleFocusIn ) ; return ( ) => { document . removeEventListener ( 'focusin' , handleFocusIn ) ; } ; } , [ ] ) ; return active ; } ; function NumberRangeColumnFilter ( { column : { filterValue = [ ] , render , preFilteredRows , setFilter , id } , } ) { const [ min , max ] = React . useMemo ( ( ) => getMinMax ( preFilteredRows , id ) , [ id , preFilteredRows ] ) ; const focusedElement = useActiveElement ( ) ; const hasFocus = focusedElement && ( focusedElement . id === `${ id }_1` || focusedElement . id === `${ id }_2` ) ; return ( < > < InputLabel htmlFor = { id } shrink focused = { ! ! hasFocus } > { render ( 'Header' ) } < /InputLabel> < div style = { { display : 'flex' , justifyContent : 'space-between' , alignItems : 'baseline' , paddingTop : 5 , } } > < TextField id = { `${ id }_1` } value = { filterValue [ 0 ] || '' } type = 'number' onChange = { ( e ) => { const val = e . target . value ; setFilter ( ( old = [ ] ) => [ val ? parseInt ( val , 10 ) : undefined , old [ 1 ] ] ) ; } } placeholder = { `Min (${ min })` } style = { { width : '70px' , marginRight : '0.5rem' , } } /> to < TextField id = { `${ id }_2` } value = { filterValue [ 1 ] || '' } type = 'number' onChange = { ( e ) => { const val = e . target . value ; setFilter ( ( old = [ ] ) => [ old [ 0 ] , val ? parseInt ( val , 10 ) : undefined ] ) ; } } placeholder = { `Max (${ max })` } style = { { width : '70px' , marginLeft : '0.5rem' , } } /> < /div> < />); } const columns = [ { Header : 'Name' , columns : [ { Header : 'First Name' , accessor : 'firstName' , aggregate : 'count' , Aggregated : ( { cell : { value } } ) => `${ value } Names` , } , { Header : 'Last Name' , accessor : 'lastName' , aggregate : 'uniqueCount' , filter : 'fuzzyText' , Aggregated : ( { cell : { value } } ) => `${ value } Unique Names` , } , ] , } , { Header : 'Info' , columns : [ { Header : 'Age' , accessor : 'age' , width : 50 , minWidth : 50 , align : 'right' , Filter : SliderColumnFilter , filter : 'equals' , aggregate : 'average' , disableGroupBy : true , defaultCanSort : false , disableSortBy : false , Aggregated : ( { cell : { value } } ) => `${ value } (avg)` , } , { Header : 'Visits' , accessor : 'visits' , width : 50 , minWidth : 50 , align : 'right' , Filter : NumberRangeColumnFilter , filter : 'between' , aggregate : 'sum' , Aggregated : ( { cell : { value } } ) => `${ value } (total)` , } , { Header : 'Status' , accessor : 'status' , Filter : SelectColumnFilter , filter : 'includes' , } , { Header : 'Profile Progress' , accessor : 'progress' , Filter : SliderColumnFilter , filter : filterGreaterThan , aggregate : roundedMedian , Aggregated : ( { cell : { value } } ) => `${ value } (med)` , } , ] , } , ] ; const App = ( ) => { const [ data ] = React . useState < PersonData [ ] > ( ( ) => makeData ( 100 ) ) ; const dummy = useCallback ( ( instance ) => ( ) => { console . log ( 'Selected' , instance . selectedFlatRows . map ( ( v ) => `'${ v . original . firstName } ${ v . original . lastName }'` ) . join ( ', ' ) ) ; } , [ ] ) ; return ( < Page > < CssBaseline /> < Table < PersonData > name = { 'testTable' } columns = { columns } data = { data } onAdd = { dummy } onEdit = { dummy } onDelete = { dummy } / > < /Page>); } ; export default App ;