mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
init
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
TableOptions,
|
||||
useReactTable,
|
||||
ColumnDef,
|
||||
} from '@tanstack/react-table';
|
||||
import { rankItem } from '@tanstack/match-sorter-utils';
|
||||
import { Icon } from '@iconify/react';
|
||||
|
||||
import Pagination from '@/components/Pagination';
|
||||
import { cn } from '@/lib/helper';
|
||||
|
||||
interface TableClassNames {
|
||||
containerClassName?: string;
|
||||
tableWrapperClassName?: string;
|
||||
tableClassName?: string;
|
||||
tableHeaderClassName?: string;
|
||||
headerRowClassName?: string;
|
||||
headerColumnClassName?: string;
|
||||
tableBodyClassName?: string;
|
||||
bodyRowClassName?: string;
|
||||
bodyColumnClassName?: string;
|
||||
paginationClassName?: string;
|
||||
}
|
||||
|
||||
// Type for the Table component props
|
||||
interface TableProps<TData extends object> {
|
||||
data: TData[];
|
||||
columns: ColumnDef<TData, any>[];
|
||||
pageSize?: number;
|
||||
isLoading?: boolean;
|
||||
fuzzySearchValue?: string | null;
|
||||
onFuzzySearchValueChange?: (value: string) => void;
|
||||
className?: TableClassNames;
|
||||
}
|
||||
|
||||
const DUMMY_SKELETON_DATA = [{}, {}, {}, {}, {}];
|
||||
|
||||
const fuzzyFilter = (
|
||||
row: any,
|
||||
columnId: string,
|
||||
value: string,
|
||||
addMeta: (meta: any) => void
|
||||
) => {
|
||||
const itemRank = rankItem(row.getValue(columnId), value);
|
||||
addMeta({ itemRank });
|
||||
return itemRank.passed;
|
||||
};
|
||||
|
||||
const Table = <TData extends object>({
|
||||
data = [],
|
||||
columns = [],
|
||||
pageSize = 10,
|
||||
isLoading = false,
|
||||
fuzzySearchValue = null,
|
||||
onFuzzySearchValueChange = () => {},
|
||||
className = {
|
||||
containerClassName: '',
|
||||
tableWrapperClassName: '',
|
||||
tableClassName: '',
|
||||
tableHeaderClassName: '',
|
||||
headerRowClassName: '',
|
||||
headerColumnClassName: '',
|
||||
tableBodyClassName: '',
|
||||
bodyRowClassName: '',
|
||||
bodyColumnClassName: '',
|
||||
paginationClassName: '',
|
||||
},
|
||||
}: TableProps<TData>) => {
|
||||
const [pagination, setPagination] = useState({
|
||||
pageIndex: 0,
|
||||
pageSize: pageSize,
|
||||
});
|
||||
|
||||
const tableOptions: TableOptions<TData> = {
|
||||
columns,
|
||||
data: isLoading ? (DUMMY_SKELETON_DATA as TData[]) : data, // Type assertion
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onPaginationChange: setPagination,
|
||||
state: {
|
||||
pagination,
|
||||
globalFilter: fuzzySearchValue,
|
||||
},
|
||||
filterFns: {
|
||||
fuzzy: fuzzyFilter,
|
||||
},
|
||||
globalFilterFn: fuzzyFilter,
|
||||
};
|
||||
|
||||
if (fuzzySearchValue !== null) {
|
||||
tableOptions.onGlobalFilterChange = onFuzzySearchValueChange;
|
||||
tableOptions.getFilteredRowModel = getFilteredRowModel();
|
||||
}
|
||||
|
||||
const table = useReactTable(tableOptions);
|
||||
|
||||
return (
|
||||
<div className={className.containerClassName}>
|
||||
<div className={className.tableWrapperClassName}>
|
||||
<table className={className.tableClassName}>
|
||||
<thead className={className.tableHeaderClassName}>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<tr key={headerGroup.id} className={className.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'
|
||||
: '',
|
||||
className.headerColumnClassName
|
||||
)}
|
||||
>
|
||||
<div className='flex items-center gap-1'>
|
||||
{flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext()
|
||||
)}
|
||||
|
||||
{header.column.getCanSort() && (
|
||||
<div className='flex items-center'>
|
||||
<Icon
|
||||
icon='lucide:arrow-up'
|
||||
width={12}
|
||||
height={12}
|
||||
className={cn(
|
||||
'transition-all ease-in-out duration-200',
|
||||
header.column.getIsSorted() === 'asc'
|
||||
? 'text-black'
|
||||
: 'text-black/30'
|
||||
)}
|
||||
/>
|
||||
<Icon
|
||||
icon='lucide:arrow-down'
|
||||
width={12}
|
||||
height={12}
|
||||
className={cn(
|
||||
'transition-all ease-in-out duration-200',
|
||||
header.column.getIsSorted() === 'desc'
|
||||
? 'text-black'
|
||||
: 'text-black/30'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
|
||||
<tbody className={className.tableBodyClassName}>
|
||||
{table.getRowModel().rows.map((row) => (
|
||||
<tr key={row.id} className={className.bodyRowClassName}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<td key={cell.id} className={className.bodyColumnClassName}>
|
||||
{!isLoading &&
|
||||
flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
|
||||
{isLoading && <div className='skeleton w-full h-4' />}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{data.length > 0 && !isLoading && (
|
||||
<div className={cn('mt-5', className.paginationClassName)}>
|
||||
<Pagination
|
||||
totalItems={table.getRowCount()}
|
||||
itemsPerPage={table.getState().pagination.pageSize}
|
||||
currentPage={table.getState().pagination.pageIndex + 1}
|
||||
onPrevPage={() => table.previousPage()}
|
||||
onNextPage={() => table.nextPage()}
|
||||
onPageChange={(pageNumber) =>
|
||||
table.setPageIndex(pageNumber ? pageNumber - 1 : 0)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Table;
|
||||
Reference in New Issue
Block a user