214 lines
8.2 KiB
TypeScript
214 lines
8.2 KiB
TypeScript
"use client";
|
|
import {
|
|
ColumnDef,
|
|
flexRender,
|
|
getCoreRowModel,
|
|
useReactTable,
|
|
getPaginationRowModel,
|
|
ColumnFiltersState,
|
|
getFilteredRowModel,
|
|
Table as TableType,
|
|
} from "@tanstack/react-table"
|
|
import { ReactNode, useEffect, useRef, useState } from "react";
|
|
import Image from "next/image";
|
|
import { icons } from "#/assets/icons";
|
|
import clsx from "clsx";
|
|
|
|
interface DataTableProps<TData, TValue> {
|
|
columns: ColumnDef<TData, TValue>[]
|
|
data: TData[],
|
|
pageSize?: number,
|
|
header?: ReactNode | ((table: TableType<TData>) => ReactNode),
|
|
isDataLoading?: boolean
|
|
}
|
|
|
|
export default function Table<TData, TValue>({
|
|
columns,
|
|
data,
|
|
pageSize = 10,
|
|
header,
|
|
isDataLoading = true
|
|
}: DataTableProps<TData, TValue>) {
|
|
const [rowSelection, setRowSelection] = useState({})
|
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>(
|
|
[]
|
|
)
|
|
|
|
const table = useReactTable({
|
|
data,
|
|
columns,
|
|
getCoreRowModel: getCoreRowModel(),
|
|
getPaginationRowModel: getPaginationRowModel(),
|
|
|
|
state: {
|
|
rowSelection,
|
|
columnFilters,
|
|
},
|
|
initialState: {
|
|
pagination: {
|
|
pageSize: pageSize,
|
|
}
|
|
},
|
|
enableRowSelection: true,
|
|
getFilteredRowModel: getFilteredRowModel(),
|
|
onRowSelectionChange: setRowSelection,
|
|
onColumnFiltersChange: setColumnFilters,
|
|
})
|
|
|
|
const headerCheckboxRef = useRef<HTMLInputElement>(null);
|
|
|
|
useEffect(() => {
|
|
if (headerCheckboxRef.current) {
|
|
headerCheckboxRef.current.indeterminate =
|
|
table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected();
|
|
}
|
|
|
|
const selectedRows = table.getSelectedRowModel().rows;
|
|
const filteredSelectedRows = table
|
|
.getRowModel()
|
|
.rows.filter((row) => row.getIsSelected())
|
|
.map((row) => row.original);
|
|
|
|
console.log("SELECTED ALL = ", selectedRows);
|
|
console.log("SELECTED = ", filteredSelectedRows);
|
|
}, [
|
|
table
|
|
]);
|
|
|
|
const totalPages = table.getPageCount()
|
|
const currentPage = table.getState().pagination.pageIndex + 1
|
|
|
|
const getPageNumbers = () => {
|
|
const pages = []
|
|
for (let i = 1; i <= totalPages; i++) {
|
|
pages.push(i)
|
|
}
|
|
return pages
|
|
}
|
|
|
|
const render = () => {
|
|
if(!header) return null
|
|
|
|
return(
|
|
<div className="mb-4">
|
|
{typeof header === 'function'
|
|
? header(table)
|
|
: header}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return(
|
|
<div className="w-full">
|
|
{render()}
|
|
|
|
<div className="rounded-lg border border-gray-200 w-auto">
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full overflow-x-auto rounded-lg">
|
|
<thead className="h-10">
|
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
<tr key={headerGroup.id} className="rounded-lg">
|
|
{header
|
|
?
|
|
<th className="bg-[#E9F0FF] p-3 text-start first:rounded-tl-lg">
|
|
|
|
</th>
|
|
:
|
|
<th className="bg-[#E9F0FF] p-3 text-start first:rounded-tl-lg">
|
|
<input
|
|
ref={headerCheckboxRef}
|
|
checked={!!table.getIsAllPageRowsSelected()}
|
|
onChange={(e) => table.toggleAllPageRowsSelected(e.target.checked)}
|
|
type="checkbox" name="" id=""
|
|
/>
|
|
</th>
|
|
}
|
|
{headerGroup.headers.map((header) => {
|
|
return(
|
|
<th key={header.id} className="bg-[#E9F0FF] p-3 text-start last:rounded-tr-lg">
|
|
{flexRender(
|
|
header.column.columnDef.header,
|
|
header.getContext()
|
|
)}
|
|
</th>
|
|
)
|
|
})}
|
|
</tr>
|
|
))}
|
|
</thead>
|
|
<tbody>
|
|
{table.getRowModel().rows.length ? (
|
|
table.getRowModel().rows.map((row) => (
|
|
<tr key={row.id} className={clsx('hover:bg-gray-100 border-t border-gray-200', { 'bg-gray-300': row.getIsSelected()})}>
|
|
<td className="p-3 text-start">
|
|
<input
|
|
checked={row.getIsSelected()}
|
|
onChange={(e) => row.toggleSelected(e.target.checked)}
|
|
type="checkbox" name="" id=""
|
|
/>
|
|
</td>
|
|
{row.getVisibleCells().map((cell) => (
|
|
<td key={cell.id} className="p-3 text-start">
|
|
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
</td>
|
|
))}
|
|
</tr>
|
|
))
|
|
)
|
|
: isDataLoading ?
|
|
(
|
|
<tr>
|
|
<td colSpan={columns.length} className="h-20 text-center">
|
|
Chargement...
|
|
</td>
|
|
</tr>
|
|
)
|
|
: (
|
|
<tr>
|
|
<td colSpan={columns.length} className="h-20 text-center">
|
|
Aucun résultats
|
|
</td>
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center justify-end space-x-2 py-4">
|
|
<button
|
|
className="bg-gray-100 shadow-xs hover:bg-gray-300 px-3 py-1 rounded w-9 h-9"
|
|
onClick={() => table.previousPage()}
|
|
disabled={!table.getCanPreviousPage()}
|
|
>
|
|
<Image alt="" src={icons.arrowLeft} className="hover:text-blue-400"/>
|
|
</button>
|
|
|
|
<div className="flex space-x-1">
|
|
{getPageNumbers().map((pageNumber) => (
|
|
<button
|
|
key={pageNumber}
|
|
className={clsx(
|
|
"px-3 py-1 rounded w-9 h-9",
|
|
pageNumber === currentPage
|
|
? "bg-[#E9F0FF] text-blue-400"
|
|
: "bg-gray-100 hover:bg-gray-300"
|
|
)}
|
|
onClick={() => table.setPageIndex(pageNumber - 1)}
|
|
>
|
|
{pageNumber}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<button
|
|
className="w-9 h-9 bg-gray-100 shadow-xs hover:bg-gray-300 hover:text-black px-3 py-1 rounded"
|
|
onClick={() => table.nextPage()}
|
|
disabled={!table.getCanNextPage()}
|
|
>
|
|
<Image alt="" src={icons.arrowRight} />
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)
|
|
} |