refactor(dev-only): table deferred loading and aria improvements
This commit is contained in:
@@ -43,6 +43,7 @@ export type Table<Data> = {
|
|||||||
handleRowDoubleClick: (id: string) => MouseEventHandler<HTMLTableRowElement>
|
handleRowDoubleClick: (id: string) => MouseEventHandler<HTMLTableRowElement>
|
||||||
handleRowContextMenu: (id: string) => MouseEventHandler<HTMLTableRowElement>
|
handleRowContextMenu: (id: string) => MouseEventHandler<HTMLTableRowElement>
|
||||||
canSelectRows: boolean
|
canSelectRows: boolean
|
||||||
|
canSelectMultipleRows: boolean
|
||||||
selectedRows: string[]
|
selectedRows: string[]
|
||||||
selectionActions: ReactNode | undefined
|
selectionActions: ReactNode | undefined
|
||||||
showSelectionActions: boolean
|
showSelectionActions: boolean
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { classNames } from '@standardnotes/snjs'
|
import { classNames } from '@standardnotes/snjs'
|
||||||
import { useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import Icon from '../Icon/Icon'
|
import Icon from '../Icon/Icon'
|
||||||
import { Table, TableRow } from './CommonTypes'
|
import { Table, TableRow } from './CommonTypes'
|
||||||
|
|
||||||
@@ -26,6 +26,7 @@ function TableRow<Data>({
|
|||||||
<div
|
<div
|
||||||
role="row"
|
role="row"
|
||||||
aria-rowindex={rowIndex + 2}
|
aria-rowindex={rowIndex + 2}
|
||||||
|
{...(canSelectRows ? { 'aria-selected': row.isSelected } : {})}
|
||||||
className="group relative contents"
|
className="group relative contents"
|
||||||
onMouseEnter={() => {
|
onMouseEnter={() => {
|
||||||
setIsHovered(true)
|
setIsHovered(true)
|
||||||
@@ -75,7 +76,27 @@ function TableRow<Data>({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const MinTableRowHeight = 41
|
||||||
|
const MinRowsToDisplay = 20
|
||||||
|
const PageSize = Math.ceil(document.documentElement.clientHeight / MinTableRowHeight) || MinRowsToDisplay
|
||||||
|
const PageScrollThreshold = 200
|
||||||
|
|
||||||
function Table<Data>({ table }: { table: Table<Data> }) {
|
function Table<Data>({ table }: { table: Table<Data> }) {
|
||||||
|
const [rowsToDisplay, setRowsToDisplay] = useState<number>(PageSize)
|
||||||
|
const paginate = useCallback(() => {
|
||||||
|
setRowsToDisplay((cellsToDisplay) => cellsToDisplay + PageSize)
|
||||||
|
}, [])
|
||||||
|
const onScroll = useCallback(
|
||||||
|
(event: React.UIEvent<HTMLDivElement, UIEvent>) => {
|
||||||
|
const offset = PageScrollThreshold
|
||||||
|
const element = event.target as HTMLElement
|
||||||
|
if (element.scrollTop + element.offsetHeight >= element.scrollHeight - offset) {
|
||||||
|
paginate()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[paginate],
|
||||||
|
)
|
||||||
|
|
||||||
const {
|
const {
|
||||||
headers,
|
headers,
|
||||||
rows,
|
rows,
|
||||||
@@ -87,11 +108,12 @@ function Table<Data>({ table }: { table: Table<Data> }) {
|
|||||||
selectedRows,
|
selectedRows,
|
||||||
selectionActions,
|
selectionActions,
|
||||||
canSelectRows,
|
canSelectRows,
|
||||||
|
canSelectMultipleRows,
|
||||||
showSelectionActions,
|
showSelectionActions,
|
||||||
} = table
|
} = table
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="block min-h-0 overflow-auto">
|
<div className="block min-h-0 overflow-auto" onScroll={onScroll}>
|
||||||
{showSelectionActions && selectedRows.length >= 2 && (
|
{showSelectionActions && selectedRows.length >= 2 && (
|
||||||
<div className="flex items-center justify-between border-b border-border px-3 py-2">
|
<div className="flex items-center justify-between border-b border-border px-3 py-2">
|
||||||
<span className="text-info-0 text-sm font-medium">{selectedRows.length} selected</span>
|
<span className="text-info-0 text-sm font-medium">{selectedRows.length} selected</span>
|
||||||
@@ -103,6 +125,7 @@ function Table<Data>({ table }: { table: Table<Data> }) {
|
|||||||
role="grid"
|
role="grid"
|
||||||
aria-colcount={colCount}
|
aria-colcount={colCount}
|
||||||
aria-rowcount={rowCount}
|
aria-rowcount={rowCount}
|
||||||
|
aria-multiselectable={canSelectMultipleRows}
|
||||||
>
|
>
|
||||||
<div role="row" aria-rowindex={1} className="contents">
|
<div role="row" aria-rowindex={1} className="contents">
|
||||||
{headers
|
{headers
|
||||||
@@ -139,7 +162,7 @@ function Table<Data>({ table }: { table: Table<Data> }) {
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<div className="contents whitespace-nowrap">
|
<div className="contents whitespace-nowrap">
|
||||||
{rows.map((row, index) => (
|
{rows.slice(0, rowsToDisplay).map((row, index) => (
|
||||||
<TableRow
|
<TableRow
|
||||||
row={row}
|
row={row}
|
||||||
key={row.id}
|
key={row.id}
|
||||||
|
|||||||
@@ -195,11 +195,13 @@ export function useTable<Data>({
|
|||||||
handleRowContextMenu,
|
handleRowContextMenu,
|
||||||
selectedRows,
|
selectedRows,
|
||||||
canSelectRows: enableRowSelection || false,
|
canSelectRows: enableRowSelection || false,
|
||||||
|
canSelectMultipleRows: enableMultipleRowSelection || false,
|
||||||
selectionActions: selectionActions ? selectionActions(selectedRows) : undefined,
|
selectionActions: selectionActions ? selectionActions(selectedRows) : undefined,
|
||||||
showSelectionActions: showSelectionActions || false,
|
showSelectionActions: showSelectionActions || false,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
colCount,
|
colCount,
|
||||||
|
enableMultipleRowSelection,
|
||||||
enableRowSelection,
|
enableRowSelection,
|
||||||
handleRowClick,
|
handleRowClick,
|
||||||
handleRowContextMenu,
|
handleRowContextMenu,
|
||||||
|
|||||||
Reference in New Issue
Block a user