feat(FE-326): Support custom header rows and cell render hook

This commit is contained in:
rstubryan
2025-12-03 22:28:18 +07:00
parent 8fbe6aa148
commit 50559caf52
+71 -6
View File
@@ -14,6 +14,8 @@ import {
SortingState, SortingState,
OnChangeFn, OnChangeFn,
Row, Row,
HeaderGroup,
Column,
} from '@tanstack/react-table'; } from '@tanstack/react-table';
import { rankItem } from '@tanstack/match-sorter-utils'; import { rankItem } from '@tanstack/match-sorter-utils';
import { Icon } from '@iconify/react'; import { Icon } from '@iconify/react';
@@ -32,6 +34,20 @@ interface TableClassNames {
bodyRowClassName?: string; bodyRowClassName?: string;
bodyColumnClassName?: string; bodyColumnClassName?: string;
paginationClassName?: string; paginationClassName?: string;
customHeaderRowClassName?: string;
customHeaderCellClassName?: string;
}
export interface CustomHeaderRow {
id: string;
cells: Array<{
id: string;
content: ReactNode;
colSpan?: number;
rowSpan?: number;
className?: string;
}>;
className?: string;
} }
export interface TableProps<TData extends object> { export interface TableProps<TData extends object> {
@@ -52,6 +68,13 @@ export interface TableProps<TData extends object> {
rowSelection?: Record<string, boolean>; rowSelection?: Record<string, boolean>;
setRowSelection?: OnChangeFn<Record<string, boolean>>; setRowSelection?: OnChangeFn<Record<string, boolean>>;
enableRowSelection?: boolean | ((row: Row<TData>) => boolean); enableRowSelection?: boolean | ((row: Row<TData>) => boolean);
customHeaderRows?: CustomHeaderRow[];
renderCustomHeaders?: boolean;
onCustomHeaderCellRender?: <TData extends object>(
cell: ReactNode,
column: Column<TData, unknown>,
headerGroup: HeaderGroup<TData>
) => ReactNode;
} }
const DUMMY_SKELETON_DATA = [{}, {}, {}, {}, {}]; const DUMMY_SKELETON_DATA = [{}, {}, {}, {}, {}];
@@ -85,6 +108,8 @@ const Table = <TData extends object>({
bodyRowClassName: '', bodyRowClassName: '',
bodyColumnClassName: '', bodyColumnClassName: '',
paginationClassName: '', paginationClassName: '',
customHeaderRowClassName: '',
customHeaderCellClassName: '',
}, },
emptyContent = emptyContentDefaultValue, emptyContent = emptyContentDefaultValue,
sorting, sorting,
@@ -93,6 +118,9 @@ const Table = <TData extends object>({
rowSelection, rowSelection,
setRowSelection, setRowSelection,
enableRowSelection, enableRowSelection,
customHeaderRows = [],
renderCustomHeaders = false,
onCustomHeaderCellRender,
}: TableProps<TData>) => { }: TableProps<TData>) => {
const isServerSideTable = const isServerSideTable =
totalItems !== undefined && totalItems !== undefined &&
@@ -195,12 +223,51 @@ const Table = <TData extends object>({
<div className={className.tableWrapperClassName}> <div className={className.tableWrapperClassName}>
<table className={className.tableClassName}> <table className={className.tableClassName}>
<thead className={className.tableHeaderClassName}> <thead className={className.tableHeaderClassName}>
{renderCustomHeaders &&
customHeaderRows.length > 0 &&
customHeaderRows.map((headerRow) => (
<tr
key={headerRow.id}
className={
headerRow.className || className.customHeaderRowClassName
}
>
{headerRow.cells.map((cell) => (
<th
key={cell.id}
colSpan={cell.colSpan}
rowSpan={cell.rowSpan}
className={
cell.className || className.customHeaderCellClassName
}
>
{cell.content}
</th>
))}
</tr>
))}
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
<tr key={headerGroup.id} className={className.headerRowClassName}> <tr key={headerGroup.id} className={className.headerRowClassName}>
{headerGroup.headers.map((header) => ( {headerGroup.headers.map((header) => {
let cellContent = flexRender(
header.column.columnDef.header,
header.getContext()
);
if (onCustomHeaderCellRender) {
cellContent = onCustomHeaderCellRender(
cellContent,
header.column,
headerGroup
);
}
return (
<th <th
key={header.id} key={header.id}
colSpan={header.colSpan} colSpan={header.colSpan}
rowSpan={header.rowSpan}
onClick={header.column.getToggleSortingHandler()} onClick={header.column.getToggleSortingHandler()}
className={cn( className={cn(
header.column.getCanSort() header.column.getCanSort()
@@ -210,10 +277,7 @@ const Table = <TData extends object>({
)} )}
> >
<div className='flex items-center gap-1'> <div className='flex items-center gap-1'>
{flexRender( {cellContent}
header.column.columnDef.header,
header.getContext()
)}
{header.column.getCanSort() && ( {header.column.getCanSort() && (
<div className='flex items-center'> <div className='flex items-center'>
@@ -243,7 +307,8 @@ const Table = <TData extends object>({
)} )}
</div> </div>
</th> </th>
))} );
})}
</tr> </tr>
))} ))}
</thead> </thead>