Last active
November 7, 2023 13:17
-
-
Save bayasdev/30841f14e03246ba6eee12e5e4d9eeb7 to your computer and use it in GitHub Desktop.
Shadcn data table with row selection
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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> | |
); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'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