perf(data-table): cache pinned column class resolution
- reuse the pinned column lookup while table props stay stable to reduce repeated per-render work. - share the resolved column class handler across unified and split-header table layouts. - localize page-number screen reader labels so pagination remains accessible in every locale.
This commit is contained in:
@@ -23,17 +23,19 @@ export function getResolvedColumnClassName(
|
||||
getColumnClassName?: DataTableColumnClassName,
|
||||
pinnedColumns?: DataTablePinnedColumn[]
|
||||
): DataTableColumnClassName {
|
||||
if (!pinnedColumns?.length) {
|
||||
return (columnId, kind) => getColumnClassName?.(columnId, kind)
|
||||
}
|
||||
|
||||
const pinnedColumnById = new Map(
|
||||
pinnedColumns.map((column) => [column.columnId, column])
|
||||
return getResolvedColumnClassNameFromMap(
|
||||
getColumnClassName,
|
||||
getPinnedColumnMap(pinnedColumns)
|
||||
)
|
||||
}
|
||||
|
||||
export function getResolvedColumnClassNameFromMap(
|
||||
getColumnClassName?: DataTableColumnClassName,
|
||||
pinnedColumnById?: Map<string, DataTablePinnedColumn>
|
||||
): DataTableColumnClassName {
|
||||
return (columnId, kind) => {
|
||||
const pinnedColumn = pinnedColumnById.get(columnId)
|
||||
const customClassName = getColumnClassName?.(columnId, kind)
|
||||
const pinnedColumn = pinnedColumnById?.get(columnId)
|
||||
|
||||
if (!pinnedColumn) return customClassName
|
||||
|
||||
@@ -41,6 +43,12 @@ export function getResolvedColumnClassName(
|
||||
}
|
||||
}
|
||||
|
||||
export function getPinnedColumnMap(pinnedColumns?: DataTablePinnedColumn[]) {
|
||||
if (!pinnedColumns?.length) return undefined
|
||||
|
||||
return new Map(pinnedColumns.map((column) => [column.columnId, column]))
|
||||
}
|
||||
|
||||
function getPinnedColumnClassName(
|
||||
pinnedColumn: DataTablePinnedColumn,
|
||||
kind: 'header' | 'cell'
|
||||
|
||||
+45
-12
@@ -20,14 +20,21 @@ import * as React from 'react'
|
||||
import { type Row } from '@tanstack/react-table'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { Table, TableBody, TableCell, TableRow } from '@/components/ui/table'
|
||||
import { getResolvedColumnClassName } from './column-pinning'
|
||||
import {
|
||||
getPinnedColumnMap,
|
||||
getResolvedColumnClassNameFromMap,
|
||||
} from './column-pinning'
|
||||
import { DataTableColgroup } from './data-table-colgroup'
|
||||
import { DataTableHeader } from './data-table-header'
|
||||
import { DataTableRow } from './data-table-row'
|
||||
import { TableEmpty } from './table-empty'
|
||||
import { getTableSizeStyle } from './table-sizing'
|
||||
import { TableSkeleton } from './table-skeleton'
|
||||
import type { DataTableColumnClassName, DataTableViewProps } from './types'
|
||||
import type {
|
||||
DataTableColumnClassName,
|
||||
DataTablePinnedColumn,
|
||||
DataTableViewProps,
|
||||
} from './types'
|
||||
|
||||
export type {
|
||||
DataTableColumnClassName,
|
||||
@@ -40,6 +47,10 @@ export { DataTableRow } from './data-table-row'
|
||||
export function DataTableView<TData>(props: DataTableViewProps<TData>) {
|
||||
const rows = props.rows ?? props.table.getRowModel().rows
|
||||
const colSpan = props.table.getVisibleLeafColumns().length
|
||||
const columnClassName = useResolvedColumnClassName(
|
||||
props.getColumnClassName,
|
||||
props.pinnedColumns
|
||||
)
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -50,9 +61,19 @@ export function DataTableView<TData>(props: DataTableViewProps<TData>) {
|
||||
{...props.containerProps}
|
||||
>
|
||||
{props.splitHeader ? (
|
||||
<SplitHeaderTableView props={props} rows={rows} colSpan={colSpan} />
|
||||
<SplitHeaderTableView
|
||||
props={props}
|
||||
rows={rows}
|
||||
colSpan={colSpan}
|
||||
getColumnClassName={columnClassName}
|
||||
/>
|
||||
) : (
|
||||
<UnifiedTableView props={props} rows={rows} colSpan={colSpan} />
|
||||
<UnifiedTableView
|
||||
props={props}
|
||||
rows={rows}
|
||||
colSpan={colSpan}
|
||||
getColumnClassName={columnClassName}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
@@ -62,15 +83,13 @@ function UnifiedTableView<TData>({
|
||||
props,
|
||||
rows,
|
||||
colSpan,
|
||||
getColumnClassName,
|
||||
}: {
|
||||
props: DataTableViewProps<TData>
|
||||
rows: Row<TData>[]
|
||||
colSpan: number
|
||||
getColumnClassName: DataTableColumnClassName
|
||||
}) {
|
||||
const getColumnClassName = getResolvedColumnClassName(
|
||||
props.getColumnClassName,
|
||||
props.pinnedColumns
|
||||
)
|
||||
const tableSizing = getTableSizing(props)
|
||||
|
||||
return (
|
||||
@@ -94,17 +113,15 @@ function SplitHeaderTableView<TData>({
|
||||
props,
|
||||
rows,
|
||||
colSpan,
|
||||
getColumnClassName,
|
||||
}: {
|
||||
props: DataTableViewProps<TData>
|
||||
rows: Row<TData>[]
|
||||
colSpan: number
|
||||
getColumnClassName: DataTableColumnClassName
|
||||
}) {
|
||||
const headerHostRef = React.useRef<HTMLDivElement>(null)
|
||||
const bodyHostRef = React.useRef<HTMLDivElement>(null)
|
||||
const getColumnClassName = getResolvedColumnClassName(
|
||||
props.getColumnClassName,
|
||||
props.pinnedColumns
|
||||
)
|
||||
const tableSizing = getTableSizing(props)
|
||||
|
||||
React.useEffect(() => {
|
||||
@@ -174,6 +191,22 @@ function SplitHeaderTableView<TData>({
|
||||
)
|
||||
}
|
||||
|
||||
function useResolvedColumnClassName(
|
||||
getColumnClassName?: DataTableColumnClassName,
|
||||
pinnedColumns?: DataTablePinnedColumn[]
|
||||
) {
|
||||
const pinnedColumnById = React.useMemo(
|
||||
() => getPinnedColumnMap(pinnedColumns),
|
||||
[pinnedColumns]
|
||||
)
|
||||
|
||||
return React.useMemo(
|
||||
() =>
|
||||
getResolvedColumnClassNameFromMap(getColumnClassName, pinnedColumnById),
|
||||
[getColumnClassName, pinnedColumnById]
|
||||
)
|
||||
}
|
||||
|
||||
function getTableSizing<TData>(props: DataTableViewProps<TData>): {
|
||||
colgroup?: React.ReactNode
|
||||
style?: React.CSSProperties
|
||||
|
||||
@@ -134,7 +134,9 @@ export function DataTablePagination<TData>({
|
||||
)}
|
||||
onClick={() => table.setPageIndex((pageNumber as number) - 1)}
|
||||
>
|
||||
<span className='sr-only'>Go to page {pageNumber}</span>
|
||||
<span className='sr-only'>
|
||||
{t('Go to page {{page}}', { page: pageNumber })}
|
||||
</span>
|
||||
{pageNumber}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
Vendored
+1
@@ -1856,6 +1856,7 @@
|
||||
"Go to io.net API Keys": "Go to io.net API Keys",
|
||||
"Go to last page": "Go to last page",
|
||||
"Go to next page": "Go to next page",
|
||||
"Go to page {{page}}": "Go to page {{page}}",
|
||||
"Go to previous page": "Go to previous page",
|
||||
"Go to settings": "Go to settings",
|
||||
"Go to Settings": "Go to Settings",
|
||||
|
||||
Vendored
+1
@@ -1856,6 +1856,7 @@
|
||||
"Go to io.net API Keys": "Accéder aux clés API io.net",
|
||||
"Go to last page": "Aller à la dernière page",
|
||||
"Go to next page": "Aller à la page suivante",
|
||||
"Go to page {{page}}": "Aller à la page {{page}}",
|
||||
"Go to previous page": "Aller à la page précédente",
|
||||
"Go to settings": "Aller aux paramètres",
|
||||
"Go to Settings": "Aller aux paramètres",
|
||||
|
||||
Vendored
+1
@@ -1856,6 +1856,7 @@
|
||||
"Go to io.net API Keys": "io.net API キーへ移動",
|
||||
"Go to last page": "最後のページへ移動",
|
||||
"Go to next page": "次のページへ移動",
|
||||
"Go to page {{page}}": "{{page}}ページ目へ移動",
|
||||
"Go to previous page": "前のページへ移動",
|
||||
"Go to settings": "設定へ",
|
||||
"Go to Settings": "設定へ移動",
|
||||
|
||||
Vendored
+1
@@ -1856,6 +1856,7 @@
|
||||
"Go to io.net API Keys": "Перейти к ключам API io.net",
|
||||
"Go to last page": "Перейти на последнюю страницу",
|
||||
"Go to next page": "Перейти на следующую страницу",
|
||||
"Go to page {{page}}": "Перейти на страницу {{page}}",
|
||||
"Go to previous page": "Перейти на предыдущую страницу",
|
||||
"Go to settings": "Перейти к настройкам",
|
||||
"Go to Settings": "Перейти к настройкам",
|
||||
|
||||
Vendored
+3
-2
@@ -1851,11 +1851,12 @@
|
||||
"Go Back": "Quay lại",
|
||||
"Go back and edit": "Quay lại và chỉnh sửa",
|
||||
"Go to Dashboard": "Truy cập Dashboard",
|
||||
"Go to first page": "Go to the first page",
|
||||
"Go to first page": "Đi đến trang đầu tiên",
|
||||
"Go to home": "Về trang chủ",
|
||||
"Go to io.net API Keys": "Đi đến Khóa API io.net",
|
||||
"Go to last page": "Go to the last page",
|
||||
"Go to last page": "Đi đến trang cuối cùng",
|
||||
"Go to next page": "Đi đến trang tiếp theo",
|
||||
"Go to page {{page}}": "Đi đến trang {{page}}",
|
||||
"Go to previous page": "Quay lại trang trước",
|
||||
"Go to settings": "Đi tới cài đặt",
|
||||
"Go to Settings": "Đi đến Cài đặt",
|
||||
|
||||
Vendored
+1
@@ -1856,6 +1856,7 @@
|
||||
"Go to io.net API Keys": "前往 io.net API 密钥",
|
||||
"Go to last page": "前往末页",
|
||||
"Go to next page": "前往下一页",
|
||||
"Go to page {{page}}": "前往第 {{page}} 页",
|
||||
"Go to previous page": "前往上一页",
|
||||
"Go to settings": "前往设置",
|
||||
"Go to Settings": "前往设置",
|
||||
|
||||
Reference in New Issue
Block a user