Skip to content

Instantly share code, notes, and snippets.

@bayasdev
Last active November 7, 2023 13:17
Show Gist options
  • Save bayasdev/30841f14e03246ba6eee12e5e4d9eeb7 to your computer and use it in GitHub Desktop.
Save bayasdev/30841f14e03246ba6eee12e5e4d9eeb7 to your computer and use it in GitHub Desktop.
Shadcn data table with row selection
import {
ChevronLeftIcon,
ChevronRightIcon,
ChevronFirstIcon,
ChevronLastIcon,
} from 'lucide-react';
import { Table } from '@tanstack/react-table';
import { Button } from '@/components/ui/button';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
interface DataTablePaginationProps<TData> {
table: Table<TData>;
}
export function DataTablePagination<TData>({
table,
}: DataTablePaginationProps<TData>) {
return (
<div className="flex items-center justify-end px-2 py-4">
<div className="flex items-center space-x-6 overflow-x-auto lg:space-x-8 ">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium">Resultados por página</p>
<Select
value={`${table.getState().pagination.pageSize}`}
onValueChange={(value) => {
table.setPageSize(Number(value));
}}
>
<SelectTrigger className="h-8 w-[70px]">
<SelectValue placeholder={table.getState().pagination.pageSize} />
</SelectTrigger>
<SelectContent side="top">
{[10, 20, 30, 40, 50].map((pageSize) => (
<SelectItem key={pageSize} value={`${pageSize}`}>
{pageSize}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex w-[100px] items-center justify-center text-sm font-medium">
Página {table.getState().pagination.pageIndex + 1} de{' '}
{table.getPageCount()}
</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">Go to first page</span>
<ChevronFirstIcon 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">Go to previous page</span>
<ChevronLeftIcon 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">Go to next page</span>
<ChevronRightIcon 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">Go to last page</span>
<ChevronLastIcon className="h-4 w-4" />
</Button>
</div>
</div>
</div>
);
}
'use client';
import { DropdownMenuTrigger } from '@radix-ui/react-dropdown-menu';
import { Settings2Icon } from 'lucide-react';
import { Table } from '@tanstack/react-table';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuCheckboxItem,
DropdownMenuContent,
DropdownMenuLabel,
DropdownMenuSeparator,
} from '@/components/ui/dropdown-menu';
interface DataTableViewOptionsProps<TData> {
table: Table<TData>;
}
export function DataTableViewOptions<TData>({
table,
}: DataTableViewOptionsProps<TData>) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
variant="outline"
size="sm"
className="ml-auto hidden h-8 lg:flex"
>
<Settings2Icon className="mr-2 h-4 w-4" />
Vista
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[150px]">
<DropdownMenuLabel>Mostrar columnas</DropdownMenuLabel>
<DropdownMenuSeparator />
{table
.getAllColumns()
.filter(
(column) =>
typeof column.accessorFn !== 'undefined' && column.getCanHide(),
)
.map((column) => {
return (
<DropdownMenuCheckboxItem
key={column.id}
className="capitalize"
checked={column.getIsVisible()}
onCheckedChange={(value) => column.toggleVisibility(!!value)}
>
{column.id}
</DropdownMenuCheckboxItem>
);
})}
</DropdownMenuContent>
</DropdownMenu>
);
}
'use client';
import * as React from 'react';
import {
ColumnDef,
ColumnFiltersState,
RowSelectionState,
SortingState,
Updater,
VisibilityState,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from '@tanstack/react-table';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { Input } from '@/components/ui/input';
import { DataTablePagination } from '@/components/ui/data-table-pagination';
import { DataTableViewOptions } from '@/components/ui/data-table-view-options';
interface DataTableProps<TData, TValue> {
columns: ColumnDef<TData, TValue>[];
data: TData[] | null;
searchKey?: string;
showViewOptions?: boolean;
rowSelection?: {};
// eslint-disable-next-line no-unused-vars
setRowSelection?: (updater: Updater<RowSelectionState>) => void;
// eslint-disable-next-line no-unused-vars
onSelectedRowsChange?: (data: TData[]) => void;
resetRowSelection?: () => void;
showRowSelection?: boolean;
selectActions?: React.ReactNode;
}
export function DataTable<TData, TValue>({
columns,
data,
searchKey = 'name',
showViewOptions = true,
rowSelection = {},
setRowSelection = () => {},
onSelectedRowsChange = () => {},
showRowSelection = false,
selectActions,
}: DataTableProps<TData, TValue>) {
const [sorting, setSorting] = React.useState<SortingState>([]);
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
[],
);
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>({});
// const [rowSelection, setRowSelection] = React.useState({});
const table = useReactTable({
data: data || [],
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
onSortingChange: setSorting,
getSortedRowModel: getSortedRowModel(),
onColumnFiltersChange: setColumnFilters,
getFilteredRowModel: getFilteredRowModel(),
onColumnVisibilityChange: setColumnVisibility,
onRowSelectionChange: setRowSelection,
state: {
sorting,
columnFilters,
columnVisibility,
rowSelection,
},
});
React.useEffect(() => {
onSelectedRowsChange(
table.getSelectedRowModel().flatRows.map((row) => row.original),
);
}, [rowSelection, table, onSelectedRowsChange]);
return (
<div>
<div className="flex items-center py-4">
<Input
placeholder="Buscar"
value={(table.getColumn(searchKey)?.getFilterValue() as string) ?? ''}
onChange={(event) =>
table.getColumn(searchKey)?.setFilterValue(event.target.value)
}
className="max-w-sm"
/>
{showViewOptions && <DataTableViewOptions table={table} />}
</div>
<div className="rounded-md border">
<Table>
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => {
return (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
);
})}
</TableRow>
))}
</TableHeader>
<TableBody>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
No se encontraron resultados
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
{selectActions && (
<div className="flex items-center justify-end py-4">
{selectActions}
</div>
)}
{showRowSelection && (
<div className="flex-1 py-4 text-sm text-muted-foreground">
{table.getFilteredSelectedRowModel().rows.length} de{' '}
{table.getFilteredRowModel().rows.length} fila(s) seleccionada(s)
</div>
)}
<DataTablePagination table={table} />
</div>
);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment