feat: enhance DataTable component with row numbering and selection features
This commit is contained in:
parent
c0ea08f1e9
commit
1613cdfc09
@ -19,6 +19,7 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Button } from "./ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
@ -34,7 +35,7 @@ import {
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import React from "react";
|
||||
import { Input } from "./ui/input";
|
||||
import { Settings2, ChevronDown, ListFilter, Trash2 } from "lucide-react";
|
||||
import { Settings2, ChevronDown, ListFilter, Trash2, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from "lucide-react";
|
||||
|
||||
interface DataTableFilterOption {
|
||||
columnId: string;
|
||||
@ -55,6 +56,8 @@ interface DataTableProps<TData, TValue> {
|
||||
searchKey?: string;
|
||||
filters?: DataTableFilterOption[];
|
||||
bulkActions?: DataTableBulkAction<TData>[];
|
||||
showNumbering?: boolean;
|
||||
showSelection?: boolean;
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
@ -62,6 +65,8 @@ export function DataTable<TData, TValue>({
|
||||
data,
|
||||
filters,
|
||||
bulkActions,
|
||||
showNumbering = true,
|
||||
showSelection = true,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
|
||||
@ -69,9 +74,55 @@ export function DataTable<TData, TValue>({
|
||||
const [rowSelection, setRowSelection] = React.useState({});
|
||||
const [globalFilter, setGlobalFilter] = React.useState("");
|
||||
|
||||
const finalColumns = React.useMemo(() => {
|
||||
const computedColumns = [...columns];
|
||||
|
||||
if (showNumbering) {
|
||||
const numberingColumn: ColumnDef<TData, TValue> = {
|
||||
id: "numbering",
|
||||
header: "No.",
|
||||
cell: ({ row, table }) => {
|
||||
const pageIndex = table.getState().pagination.pageIndex;
|
||||
const pageSize = table.getState().pagination.pageSize;
|
||||
return pageIndex * pageSize + row.index + 1;
|
||||
},
|
||||
enableHiding: false,
|
||||
};
|
||||
computedColumns.unshift(numberingColumn);
|
||||
}
|
||||
|
||||
if (showSelection) {
|
||||
const selectColumn: ColumnDef<TData, TValue> = {
|
||||
id: "select",
|
||||
header: ({ table }) => (
|
||||
<Checkbox
|
||||
checked={
|
||||
table.getIsAllPageRowsSelected() ||
|
||||
(table.getIsSomePageRowsSelected() && "indeterminate")
|
||||
}
|
||||
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
|
||||
aria-label="Select all"
|
||||
/>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
<Checkbox
|
||||
checked={row.getIsSelected()}
|
||||
onCheckedChange={(value) => row.toggleSelected(!!value)}
|
||||
aria-label="Select row"
|
||||
/>
|
||||
),
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
};
|
||||
computedColumns.unshift(selectColumn);
|
||||
}
|
||||
|
||||
return computedColumns;
|
||||
}, [columns, showNumbering, showSelection]);
|
||||
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
columns: finalColumns,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
onSortingChange: setSorting,
|
||||
@ -214,7 +265,14 @@ export function DataTable<TData, TValue>({
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
return (
|
||||
<TableHead key={header.id}>
|
||||
<TableHead
|
||||
key={header.id}
|
||||
className={
|
||||
header.column.id === "select" || header.column.id === "numbering"
|
||||
? "w-10 text-center"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(header.column.columnDef.header, header.getContext())}
|
||||
@ -229,7 +287,14 @@ export function DataTable<TData, TValue>({
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id} data-state={row.getIsSelected() && "selected"}>
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
className={
|
||||
cell.column.id === "select" || cell.column.id === "numbering"
|
||||
? "w-10 text-center"
|
||||
: ""
|
||||
}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
@ -237,7 +302,7 @@ export function DataTable<TData, TValue>({
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
<TableCell colSpan={finalColumns.length} className="h-24 text-center">
|
||||
Tidak ada data yang ditemukan.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@ -247,27 +312,60 @@ export function DataTable<TData, TValue>({
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between py-4">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
<div className="flex-1 text-sm text-muted-foreground">
|
||||
{table.getFilteredSelectedRowModel().rows.length} dari{" "}
|
||||
{table.getFilteredRowModel().rows.length} baris dipilih.
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
Previous
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Next
|
||||
</Button>
|
||||
<div className="flex items-center space-x-6 lg:space-x-8">
|
||||
<div className="flex items-center justify-center text-sm font-medium">
|
||||
{table.getFilteredRowModel().rows.length > 0 ? (
|
||||
<>
|
||||
Menampilkan {table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1} sampai{" "}
|
||||
{Math.min((table.getState().pagination.pageIndex + 1) * table.getState().pagination.pageSize, table.getFilteredRowModel().rows.length)}{" "}
|
||||
dari {table.getFilteredRowModel().rows.length} data
|
||||
</>
|
||||
) : (
|
||||
"Tidak ada data"
|
||||
)}
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() => table.setPageIndex(0)}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Halaman pertama</span>
|
||||
<ChevronsLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<span className="sr-only">Halaman sebelumnya</span>
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-8 w-8 p-0"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Halaman selanjutnya</span>
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hidden h-8 w-8 p-0 lg:flex"
|
||||
onClick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
<span className="sr-only">Halaman terakhir</span>
|
||||
<ChevronsRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user