Skip to content

Commit adbb75b

Browse files
committed
Fixed issues. Improved stories.
1 parent 0215457 commit adbb75b

12 files changed

+3195
-239
lines changed

.storybook/preview.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,23 @@ const preview: Preview = {
99
date: /Date$/i,
1010
},
1111
},
12+
options: {
13+
storySort: {
14+
order: [
15+
"TableAdapter",
16+
[
17+
"Basic",
18+
"Pagination",
19+
"Sorting",
20+
"Selection",
21+
"Loading States",
22+
"Server Side",
23+
"Advanced",
24+
"Filtering",
25+
],
26+
],
27+
},
28+
},
1229
},
1330
};
1431

src/TableAdapter.tsx

Lines changed: 136 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -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
140138
const 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

Comments
 (0)