mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
feat(FE-284): Refactor table component support for nesting header
This commit is contained in:
+103
-88
@@ -14,6 +14,7 @@ import {
|
||||
SortingState,
|
||||
OnChangeFn,
|
||||
Row,
|
||||
HeaderContext,
|
||||
} from '@tanstack/react-table';
|
||||
import { rankItem } from '@tanstack/match-sorter-utils';
|
||||
import { Icon } from '@iconify/react';
|
||||
@@ -57,8 +58,6 @@ export interface TableProps<TData extends object> {
|
||||
setRowSelection?: OnChangeFn<Record<string, boolean>>;
|
||||
enableRowSelection?: boolean | ((row: Row<TData>) => boolean);
|
||||
renderFooter?: boolean;
|
||||
footerContent?: ReactNode;
|
||||
footerData?: TData[];
|
||||
withCheckbox?: boolean;
|
||||
rowOptions?: number[];
|
||||
}
|
||||
@@ -73,22 +72,22 @@ const emptyContentDefaultValue = (
|
||||
</div>
|
||||
);
|
||||
|
||||
const TABLE_DEFAULT_STYLING = {
|
||||
export const TABLE_DEFAULT_STYLING = {
|
||||
containerClassName: 'w-full mb-20',
|
||||
tableWrapperClassName:
|
||||
'overflow-x-auto border border-solid border-base-content/10 rounded-lg',
|
||||
tableClassName: 'font-inter w-full table-auto text-sm font-medium',
|
||||
tableHeaderClassName: '',
|
||||
headerRowClassName: '',
|
||||
headerColumnClassName: 'px-4 py-3 text-base-content/50',
|
||||
headerColumnClassName:
|
||||
'px-4 py-3 border-base-content/10 text-base-content/50',
|
||||
tableBodyClassName: '',
|
||||
bodyRowClassName: 'border-t border-t-base-content/10',
|
||||
bodyRowClassName: 'border-t border-base-content/10',
|
||||
bodyColumnClassName: 'px-4 py-3 text-base-content',
|
||||
paginationClassName: '',
|
||||
|
||||
tableFooterClassName: '',
|
||||
footerRowClassName: '',
|
||||
footerColumnClassName: '',
|
||||
tableFooterClassName: 'font-semibold border-base-content/10',
|
||||
footerRowClassName: 'bg-base-200 border-t-2 border-base-content/10',
|
||||
footerColumnClassName: 'p-4 text-base-content whitespace-nowrap',
|
||||
};
|
||||
|
||||
const Table = <TData extends object>({
|
||||
@@ -111,8 +110,6 @@ const Table = <TData extends object>({
|
||||
setRowSelection,
|
||||
enableRowSelection,
|
||||
renderFooter = false,
|
||||
footerContent,
|
||||
footerData = [],
|
||||
withCheckbox = false,
|
||||
rowOptions = [10, 20, 50, 100],
|
||||
}: TableProps<TData>) => {
|
||||
@@ -187,14 +184,6 @@ const Table = <TData extends object>({
|
||||
const table = useReactTable(tableOptions);
|
||||
const { setPageSize } = table;
|
||||
|
||||
const footerTableOptions: TableOptions<TData> = {
|
||||
columns,
|
||||
data: footerData,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
};
|
||||
|
||||
const footerTable = useReactTable(footerTableOptions);
|
||||
|
||||
const prevPageClickHandler = () => {
|
||||
table.previousPage();
|
||||
|
||||
@@ -235,58 +224,82 @@ const Table = <TData extends object>({
|
||||
key={headerGroup.id}
|
||||
className={tableClassNames.headerRowClassName}
|
||||
>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<th
|
||||
key={header.id}
|
||||
colSpan={header.colSpan}
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
className={cn(
|
||||
header.column.getCanSort()
|
||||
? 'cursor-pointer select-none'
|
||||
: '',
|
||||
{
|
||||
'first:w-9 first:pr-0': withCheckbox,
|
||||
},
|
||||
tableClassNames.headerColumnClassName
|
||||
)}
|
||||
>
|
||||
<div className='flex items-center gap-1'>
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
{headerGroup.headers.map((header) => {
|
||||
const columnRelativeDepth =
|
||||
header.depth - header.column.depth;
|
||||
if (
|
||||
!header.isPlaceholder &&
|
||||
columnRelativeDepth > 1 &&
|
||||
header.id === header.column.id
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
let rowSpan = 1;
|
||||
if (header.isPlaceholder) {
|
||||
const leafs = header.getLeafHeaders();
|
||||
rowSpan = leafs[leafs.length - 1].depth - header.depth;
|
||||
}
|
||||
return (
|
||||
<th
|
||||
key={header.id}
|
||||
colSpan={header.colSpan}
|
||||
rowSpan={rowSpan}
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
className={cn(
|
||||
header.column.getCanSort()
|
||||
? 'cursor-pointer select-none'
|
||||
: '',
|
||||
{
|
||||
'first:w-9 first:pr-0': withCheckbox,
|
||||
},
|
||||
{
|
||||
'border-b': header.colSpan > 1,
|
||||
},
|
||||
tableClassNames.headerColumnClassName
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn('flex items-center gap-1 min-h-full', {
|
||||
'justify-center': header.colSpan > 1,
|
||||
})}
|
||||
>
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
|
||||
{header.column.getCanSort() && (
|
||||
<div className='w-4 h-4 relative flex flex-col items-center'>
|
||||
<Icon
|
||||
icon='heroicons:chevron-up-16-solid'
|
||||
width={18}
|
||||
height={18}
|
||||
className={cn(
|
||||
'absolute -top-1',
|
||||
'transition-all ease-in-out duration-200',
|
||||
header.column.getIsSorted() === 'asc'
|
||||
? 'text-black'
|
||||
: 'text-black/30'
|
||||
)}
|
||||
/>
|
||||
<Icon
|
||||
icon='heroicons:chevron-down-16-solid'
|
||||
width={18}
|
||||
height={18}
|
||||
className={cn(
|
||||
'absolute -bottom-1.5',
|
||||
'transition-all ease-in-out duration-200',
|
||||
header.column.getIsSorted() === 'desc'
|
||||
? 'text-black'
|
||||
: 'text-black/30'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
{header.column.getCanSort() && (
|
||||
<div className='w-4 h-4 relative flex flex-col items-center'>
|
||||
<Icon
|
||||
icon='heroicons:chevron-up-16-solid'
|
||||
width={18}
|
||||
height={18}
|
||||
className={cn(
|
||||
'absolute -top-1',
|
||||
'transition-all ease-in-out duration-200',
|
||||
header.column.getIsSorted() === 'asc'
|
||||
? 'text-black'
|
||||
: 'text-black/30'
|
||||
)}
|
||||
/>
|
||||
<Icon
|
||||
icon='heroicons:chevron-down-16-solid'
|
||||
width={18}
|
||||
height={18}
|
||||
className={cn(
|
||||
'absolute -bottom-1.5',
|
||||
'transition-all ease-in-out duration-200',
|
||||
header.column.getIsSorted() === 'desc'
|
||||
? 'text-black'
|
||||
: 'text-black/30'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</th>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
@@ -311,25 +324,27 @@ const Table = <TData extends object>({
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
<tfoot className={cn(className.tableFooterClassName)}>
|
||||
{renderFooter &&
|
||||
(footerData && footerData.length > 0
|
||||
? footerTable.getRowModel().rows.map((row) => (
|
||||
<tr key={row.id} className={className.footerRowClassName}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<td
|
||||
key={cell.id}
|
||||
className={className.footerColumnClassName}
|
||||
>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext()
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))
|
||||
: footerContent)}
|
||||
<tfoot className={cn(tableClassNames.tableFooterClassName)}>
|
||||
{renderFooter && (
|
||||
<tr className={cn(tableClassNames.footerRowClassName)}>
|
||||
{table.getAllLeafColumns().map((column) => (
|
||||
<td
|
||||
key={column.id}
|
||||
className={cn(
|
||||
{ 'first:w-9 first:pr-0': withCheckbox },
|
||||
tableClassNames.footerColumnClassName
|
||||
)}
|
||||
>
|
||||
{column.columnDef.footer &&
|
||||
flexRender(column.columnDef.footer, {
|
||||
column,
|
||||
header: column.columnDef,
|
||||
table,
|
||||
} as HeaderContext<TData, unknown>)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
)}
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user