@@ -46,95 +46,93 @@ import {
4646
4747// Use React.memo for performance optimization
4848// Use React.memo for performance optimization
49- const TableHeader = React . memo (
50- ( {
51- table,
52- headerClassName,
53- enableSorting = false ,
54- enableStickyHeader = false ,
55- enableColumnResizing = false ,
56- renderSortIcon,
57- headerStyle,
58- } : {
59- table : TanStackTable < any > ;
60- headerClassName : string ;
61- enableSorting ?: boolean ;
62- enableStickyHeader ?: boolean ;
63- enableColumnResizing ?: boolean ;
64- renderSortIcon ?: ( direction : "asc" | "desc" | false ) => React . ReactNode ;
65- headerStyle ?: React . CSSProperties ;
66- } ) => {
67- return (
68- < thead
69- className = { `${ headerClassName } ${
70- enableStickyHeader ? "sticky top-0 z-10" : ""
71- } `}
72- style = { headerStyle }
73- >
74- { table . getHeaderGroups ( ) . map ( ( headerGroup ) => (
75- < tr key = { headerGroup . id } >
76- { headerGroup . headers . map ( ( header ) => (
77- < th
78- key = { header . id }
79- colSpan = { header . colSpan }
80- className = { header . column . columnDef . meta ?. headerClassName || "" }
81- style = { {
82- width : header . getSize ( ) ,
83- position : "relative" ,
84- ...( header . column . columnDef . meta ?. headerStyle || { } ) ,
85- } }
86- >
87- { header . isPlaceholder ? null : (
88- < div
89- className = {
90- enableSorting && header . column . getCanSort ( )
91- ? "cursor-pointer select-none"
92- : ""
93- }
94- onClick = { header . column . getToggleSortingHandler ( ) }
95- role = {
96- enableSorting && header . column . getCanSort ( )
97- ? "button"
98- : undefined
99- }
100- aria-label = {
101- enableSorting && header . column . getCanSort ( )
102- ? `Sort by ${ header . column . id } `
103- : undefined
104- }
105- >
106- { flexRender (
107- header . column . columnDef . header ,
108- header . getContext ( )
109- ) }
110- { renderSortIcon
111- ? renderSortIcon (
112- header . column . getIsSorted ( ) as "asc" | "desc" | false
113- )
114- : { asc : " 🔼" , desc : " 🔽" } [
115- header . column . getIsSorted ( ) as string
116- ] ?? null }
117- </ div >
118- ) }
119- { /* Resizer */ }
120- { enableColumnResizing && header . column . getCanResize ( ) && (
121- < div
122- onMouseDown = { header . getResizeHandler ( ) }
123- onTouchStart = { header . getResizeHandler ( ) }
124- className = { `absolute right-0 top-0 h-full w-1 bg-gray-300 cursor-col-resize touch-none select-none ${
125- header . column . getIsResizing ( ) ? "bg-blue-500" : ""
126- } `}
127- aria-label = { `Resize ${ header . column . id } column` }
128- />
129- ) }
130- </ th >
131- ) ) }
132- </ tr >
133- ) ) }
134- </ thead >
135- ) ;
136- }
137- ) ;
49+ function TableHeader < TData > ( {
50+ table,
51+ headerClassName,
52+ enableSorting = false ,
53+ enableStickyHeader = false ,
54+ enableColumnResizing = false ,
55+ renderSortIcon,
56+ headerStyle,
57+ } : {
58+ table : TanStackTable < TData > ;
59+ headerClassName : string ;
60+ enableSorting ?: boolean ;
61+ enableStickyHeader ?: boolean ;
62+ enableColumnResizing ?: boolean ;
63+ renderSortIcon ?: ( direction : "asc" | "desc" | false ) => React . ReactNode ;
64+ headerStyle ?: React . CSSProperties ;
65+ } ) {
66+ return (
67+ < thead
68+ className = { `${ headerClassName } ${
69+ enableStickyHeader ? "sticky top-0 z-10" : ""
70+ } `}
71+ style = { headerStyle }
72+ >
73+ { table . getHeaderGroups ( ) . map ( ( headerGroup ) => (
74+ < tr key = { headerGroup . id } >
75+ { headerGroup . headers . map ( ( header ) => (
76+ < th
77+ key = { header . id }
78+ colSpan = { header . colSpan }
79+ className = { header . column . columnDef . meta ?. headerClassName || "" }
80+ style = { {
81+ width : header . getSize ( ) ,
82+ position : "relative" ,
83+ ...( header . column . columnDef . meta ?. headerStyle || { } ) ,
84+ } }
85+ >
86+ { header . isPlaceholder ? null : (
87+ < div
88+ className = {
89+ enableSorting && header . column . getCanSort ( )
90+ ? "cursor-pointer select-none"
91+ : ""
92+ }
93+ onClick = { header . column . getToggleSortingHandler ( ) }
94+ role = {
95+ enableSorting && header . column . getCanSort ( )
96+ ? "button"
97+ : undefined
98+ }
99+ aria-label = {
100+ enableSorting && header . column . getCanSort ( )
101+ ? `Sort by ${ header . column . id } `
102+ : undefined
103+ }
104+ >
105+ { flexRender (
106+ header . column . columnDef . header ,
107+ header . getContext ( )
108+ ) }
109+ { renderSortIcon
110+ ? renderSortIcon (
111+ header . column . getIsSorted ( ) as "asc" | "desc" | false
112+ )
113+ : { asc : " 🔼" , desc : " 🔽" } [
114+ header . column . getIsSorted ( ) as string
115+ ] ?? null }
116+ </ div >
117+ ) }
118+ { /* Resizer */ }
119+ { enableColumnResizing && header . column . getCanResize ( ) && (
120+ < div
121+ onMouseDown = { header . getResizeHandler ( ) }
122+ onTouchStart = { header . getResizeHandler ( ) }
123+ className = { `absolute right-0 top-0 h-full w-1 bg-gray-300 cursor-col-resize touch-none select-none ${
124+ header . column . getIsResizing ( ) ? "bg-blue-500" : ""
125+ } `}
126+ aria-label = { `Resize ${ header . column . id } column` }
127+ />
128+ ) }
129+ </ th >
130+ ) ) }
131+ </ tr >
132+ ) ) }
133+ </ thead >
134+ ) ;
135+ }
138136
139137// Row component with memo for performance
140138const TableRow = React . memo (
@@ -784,16 +782,24 @@ export function TableAdapter<TData extends object, TValue = unknown>(
784782 { loading . paginationLoadingComponent }
785783 </ td >
786784 </ tr >
787- { table . getRowModel ( ) . rows . map ( ( row ) => (
788- < TableRow
789- key = { row . id }
790- row = { row }
791- rowClassName = { `${ mergedClassNames . tbodyRow } opacity-50` }
792- cellClassName = { mergedClassNames . tbodyCell }
793- rowStyle = { styling . rowStyle }
794- cellStyle = { styling . cellStyle }
795- />
796- ) ) }
785+ { table . getRowModel ( ) . rows . map ( ( row ) => {
786+ // Use a key that includes selection state for loading rows
787+ const isSelected = row . getIsSelected ( ) ;
788+ const rowKey = `${ row . id } -${
789+ isSelected ? "selected" : "unselected"
790+ } -loading`;
791+
792+ return (
793+ < TableRow
794+ key = { rowKey }
795+ row = { row }
796+ rowClassName = { `${ mergedClassNames . tbodyRow } opacity-50` }
797+ cellClassName = { mergedClassNames . tbodyCell }
798+ rowStyle = { styling . rowStyle }
799+ cellStyle = { styling . cellStyle }
800+ />
801+ ) ;
802+ } ) }
797803 </ >
798804 ) : table . getRowModel ( ) . rows . length === 0 ? (
799805 renderNoResults ? (
@@ -813,29 +819,37 @@ export function TableAdapter<TData extends object, TValue = unknown>(
813819 )
814820 ) : (
815821 // Regular data rows
816- table . getRowModel ( ) . rows . map ( ( row ) => (
817- < React . Fragment key = { row . id } >
818- < TableRow
819- row = { row }
820- rowClassName = { mergedClassNames . tbodyRow }
821- cellClassName = { mergedClassNames . tbodyCell }
822- onRowClick = { onRowClick }
823- onCellClick = { onCellClick }
824- rowStyle = { styling . rowStyle }
825- cellStyle = { styling . cellStyle }
826- />
827- { /* Expanded Row Content */ }
828- { features . expanding &&
829- row . getIsExpanded ( ) &&
830- renderRowSubComponent && (
831- < ExpandedRow
832- row = { row }
833- colSpan = { row . getVisibleCells ( ) . length }
834- renderRowSubComponent = { renderRowSubComponent }
835- />
836- ) }
837- </ React . Fragment >
838- ) )
822+ table . getRowModel ( ) . rows . map ( ( row ) => {
823+ // Generate a key that includes the selection state
824+ const isSelected = row . getIsSelected ( ) ;
825+ const rowKey = `${ row . id } -${
826+ isSelected ? "selected" : "unselected"
827+ } `;
828+
829+ return (
830+ < React . Fragment key = { rowKey } >
831+ < TableRow
832+ row = { row }
833+ rowClassName = { mergedClassNames . tbodyRow }
834+ cellClassName = { mergedClassNames . tbodyCell }
835+ onRowClick = { onRowClick }
836+ onCellClick = { onCellClick }
837+ rowStyle = { styling . rowStyle }
838+ cellStyle = { styling . cellStyle }
839+ />
840+ { /* Expanded Row Content */ }
841+ { features . expanding &&
842+ row . getIsExpanded ( ) &&
843+ renderRowSubComponent && (
844+ < ExpandedRow
845+ row = { row }
846+ colSpan = { row . getVisibleCells ( ) . length }
847+ renderRowSubComponent = { renderRowSubComponent }
848+ />
849+ ) }
850+ </ React . Fragment >
851+ ) ;
852+ } )
839853 ) }
840854 </ tbody >
841855 </ table >
0 commit comments