Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 22 additions & 4 deletions web/default/src/components/data-table/core/data-table-row.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import * as React from 'react'
import { flexRender, type Cell, type Row } from '@tanstack/react-table'
import {
flexRender,
type Cell,
type Row,
type Table as TanstackTable,
} from '@tanstack/react-table'
import { cn } from '@/lib/utils'
import { TableCell, TableRow } from '@/components/ui/table'
import { TruncatedCell } from './truncated-cell'
Expand All @@ -27,6 +32,7 @@ type DataTableRowProps<TData> = {
row: Row<TData>
className?: string
getColumnClassName?: DataTableColumnClassName
cellRenderColumns?: TanstackTable<TData>['options']['columns']
} & Omit<React.ComponentProps<typeof TableRow>, 'children'>

type DataTableRowInnerProps<TData> = DataTableRowProps<TData> & {
Expand All @@ -38,8 +44,13 @@ function DataTableRowInner<TData>({
isSelected,
className,
getColumnClassName,
cellRenderColumns,
...rowProps
}: DataTableRowInnerProps<TData>) {
// Destructured only to keep it out of `rowProps` (it is not a valid DOM attr)
// and to feed the memo comparator below; it is intentionally unused here.
void cellRenderColumns

return (
<TableRow
data-state={isSelected ? 'selected' : undefined}
Expand All @@ -62,13 +73,20 @@ function DataTableRowInner<TData>({
}

const MemoizedDataTableRow = React.memo(DataTableRowInner, (prev, next) => {
// Do not read row.getIsSelected() here: TanStack row objects may keep a stable
// reference while their selection state changes.
// Do not read row.getIsSelected() inside the comparator: TanStack row objects
// keep a stable reference while their selection state mutates, so reading it
// here compares identical live values and misses selection changes. Selection
// is lifted to the `isSelected` prop, captured per render in DataTableRow.
//
// Column cell renderers (and getColumnClassName) can close over external
// state while the row stays stable, so column definitions and the class
// resolver are part of the render identity and must be compared too.
return (
prev.row === next.row &&
prev.className === next.className &&
prev.isSelected === next.isSelected &&
prev.getColumnClassName === next.getColumnClassName &&
prev.isSelected === next.isSelected
prev.cellRenderColumns === next.cellRenderColumns
)
}) as typeof DataTableRowInner

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ function SplitHeaderTableView<TData>({
<div
className={cn(
'min-h-0 flex-1 overflow-auto',
'[&_[data-slot=table-header]]:[--table-header-bg:color-mix(in_oklch,var(--muted)_30%,var(--background))]',
'[&_[data-slot=table-header]]:[background-color:var(--table-header-bg)]',
'**:data-[slot=table-header]:[--table-header-bg:color-mix(in_oklch,var(--muted)_30%,var(--background))]',
'**:data-[slot=table-header]:bg-(--table-header-bg)',
props.splitHeaderScrollClassName,
props.bodyContainerClassName
)}
Expand Down Expand Up @@ -320,6 +320,7 @@ function renderDefaultRow<TData>(
row={row}
className={cn(props.tableBodyRowClassName, props.getRowClassName?.(row))}
getColumnClassName={getColumnClassName}
cellRenderColumns={props.table.options.columns}
/>
)
}
Loading