@ -134,52 +134,56 @@ export function DataTable(props: {
}
return (
< table className = "wf" >
< thead >
< tr >
{ props . columns . map ( ( c ) = > {
const sortable = canSort ( c )
const arrow = sort ? . key === c . key ? ( sort . dir === 'asc' ? ' ▲' : ' ▼' ) : ''
// Horizontal-scroll boundary: on narrow screens a wide table scrolls inside this box
// instead of pushing the page body wider than the viewport (see .wf-table-wrap CSS).
< div className = "wf-table-wrap" >
< table className = "wf" >
< thead >
< tr >
{ props . columns . map ( ( c ) = > {
const sortable = canSort ( c )
const arrow = sort ? . key === c . key ? ( sort . dir === 'asc' ? ' ▲' : ' ▼' ) : ''
return (
< th key = { c . key } className = { c . numeric === true ? 'num' : undefined }
aria - sort = { sort ? . key === c . key ? ( sort . dir === 'asc' ? 'ascending' : 'descending' ) : undefined }
onClick = { sortable ? ( ) = > toggle ( c . key ) : undefined }
style = { sortable ? { cursor : 'pointer' , userSelect : 'none' , whiteSpace : 'nowrap' } : undefined }
title = { sortable ? 'Sort' : undefined } >
{ c . label } < span style = { { opacity : 0.7 } } > { arrow } < / span >
< / th >
)
} ) }
< / tr >
< / thead >
< tbody >
{ ordered . map ( ( { row , i } ) = > {
const tone = props . rowTone ? . ( i )
const clickable = props . onRowClick !== undefined
return (
< th key = { c . key } className = { c . numeric === true ? 'num' : undefined }
aria - sort = { sort ? . key === c . key ? ( sort . dir === 'asc' ? 'ascending' : 'descending' ) : undefined }
onClick = { sortable ? ( ) = > toggle ( c . key ) : undefined }
style = { sortable ? { cursor : 'pointer' , userSelect : 'none' , whiteSpace : 'nowrap' } : undefined }
title = { sortable ? 'Sort' : undefined } >
{ c . label } < span style = { { opacity : 0.7 } } > { arrow } < / span >
< / th >
< tr
key = { i }
className = { tone !== undefined ? ` tone- ${ tone } ` : undefined }
// Row click is keyboard-operable (WCAG 2.1.1): role=button + tabindex
// make it a focusable control, and Enter/Space fire the same handler.
onClick = { clickable ? ( ) = > props . onRowClick ? . ( row , i ) : undefined }
onKeyDown = { clickable
? ( e ) = > {
if ( e . key === 'Enter' || e . key === ' ' ) { e . preventDefault ( ) ; props . onRowClick ? . ( row , i ) }
}
: undefined }
role = { clickable ? 'button' : undefined }
tabIndex = { clickable ? 0 : undefined }
style = { clickable ? { cursor : 'pointer' } : undefined }
>
{ props . columns . map ( ( c ) = > (
< td key = { c . key } className = { cellClass ( c ) } > { row [ c . key ] } < / td >
) ) }
< / tr >
)
} ) }
< / tr >
< / thead >
< tbody >
{ ordered . map ( ( { row , i } ) = > {
const tone = props . rowTone ? . ( i )
const clickable = props . onRowClick !== undefined
return (
< tr
key = { i }
className = { tone !== undefined ? ` tone- ${ tone } ` : undefined }
// Row click is keyboard-operable (WCAG 2.1.1): role=button + tabindex
// make it a focusable control, and Enter/Space fire the same handler.
onClick = { clickable ? ( ) = > props . onRowClick ? . ( row , i ) : undefined }
onKeyDown = { clickable
? ( e ) = > {
if ( e . key === 'Enter' || e . key === ' ' ) { e . preventDefault ( ) ; props . onRowClick ? . ( row , i ) }
}
: undefined }
role = { clickable ? 'button' : undefined }
tabIndex = { clickable ? 0 : undefined }
style = { clickable ? { cursor : 'pointer' } : undefined }
>
{ props . columns . map ( ( c ) = > (
< td key = { c . key } className = { cellClass ( c ) } > { row [ c . key ] } < / td >
) ) }
< / tr >
)
} ) }
< / tbody >
< / table >
< / tbody >
< / table >
< / div >
)
}