Setting the file. One moment. Data Table · Shadcn UI · google-labs-code/stitch-skills · Skills Docsexamples/data-table.tsx
examples/data-table.tsx
TypeScript·313 lines·9 KB
from
"@tanstack/react-table"
17import { ArrowUpDown, ChevronDown, MoreHorizontal } from "lucide-react"
18import * as React from "react"
19
20import { Button } from "@/components/ui/button"
21import {
22 DropdownMenu,
23 DropdownMenuCheckboxItem,
24 DropdownMenuContent,
25 DropdownMenuItem,
26 DropdownMenuLabel,
27 DropdownMenuSeparator,
28 DropdownMenuTrigger,
29} from "@/components/ui/dropdown-menu"
30import { Input } from "@/components/ui/input"
31import {
32 Table,
33 TableBody,
34 TableCell,
35 TableHead,
36 TableHeader,
37 TableRow,
38} from "@/components/ui/table"
39
40// Define data type
41export type User = {
42 id: string
43 name: string
44 email: string
45 role: "admin" | "user" | "viewer"
46 status: "active" | "inactive"
47}
48
49// Sample data
50const data: User[] = [
51 {
52 id: "1",
53 name: "Alice Johnson",
54 email: "alice@example.com",
55 role: "admin",
56 status: "active",
57 },
58 {
59 id: "2",
60 name: "Bob Smith",
61 email: "bob@example.com",
62 role: "user",
63 status: "active",
64 },
65 {
66 id: "3",
67 name: "Carol White",
68 email: "carol@example.com",
69 role: "viewer",
70 status: "inactive",
71 },
72]
73
74// Define columns
75export const columns: ColumnDef<User>[] = [
76 {
77 accessorKey: "name",
78 header: ({ column }) => {
79 return (
80 <Button
81 variant="ghost"
82 onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
83 >
84 Name
85 <ArrowUpDown className="ml-2 h-4 w-4" />
86 </Button>
87 )
88 },
89 cell: ({ row }) => <div className="capitalize">{row.getValue("name")}</div>,
90 },
91 {
92 accessorKey: "email",
93 header: ({ column }) => {
94 return (
95 <Button
96 variant="ghost"
97 onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
98 >
99 Email
100 <ArrowUpDown className="ml-2 h-4 w-4" />
101 </Button>
102 )
103 },
104 cell: ({ row }) => <div className="lowercase">{row.getValue("email")}</div>,
105 },
106 {
107 accessorKey: "role",
108 header: "Role",
109 cell: ({ row }) => (
110 <div className="capitalize">{row.getValue("role")}</div>
111 ),
112 },
113 {
114 accessorKey: "status",
115 header: "Status",
116 cell: ({ row }) => (
117 <div className="capitalize">{row.getValue("status")}</div>
118 ),
119 },
120 {
121 id: "actions",
122 enableHiding: false,
123 cell: ({ row }) => {
124 const user = row.original
125
126 return (
127 <DropdownMenu>
128 <DropdownMenuTrigger asChild>
129 <Button variant="ghost" className="h-8 w-8 p-0">
130 <span className="sr-only">Open menu</span>
131 <MoreHorizontal className="h-4 w-4" />
132 </Button>
133 </DropdownMenuTrigger>
134 <DropdownMenuContent align="end">
135 <DropdownMenuLabel>Actions</DropdownMenuLabel>
136 <DropdownMenuItem
137 onClick={() => navigator.clipboard.writeText(user.id)}
138 >
139 Copy user ID
140 </DropdownMenuItem>
141 <DropdownMenuSeparator />
142 <DropdownMenuItem>View user</DropdownMenuItem>
143 <DropdownMenuItem>Edit user</DropdownMenuItem>
144 </DropdownMenuContent>
145 </DropdownMenu>
146 )
147 },
148 },
149]
150
151export function DataTableExample() {
152 const [sorting, setSorting] = React.useState<SortingState>([])
153 const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([])
154 const [columnVisibility, setColumnVisibility] = React.useState({})
155 const [rowSelection, setRowSelection] = React.useState({})
156
157 const table = useReactTable({
158 data,
159 columns,
160 onSortingChange: setSorting,
161 onColumnFiltersChange: setColumnFilters,
162 getCoreRowModel: getCoreRowModel(),
163 getPaginationRowModel: getPaginationRowModel(),
164 getSortedRowModel: getSortedRowModel(),
165 getFilteredRowModel: getFilteredRowModel(),
166 onColumnVisibilityChange: setColumnVisibility,
167 onRowSelectionChange: setRowSelection,
168 state: {
169 sorting,
170 columnFilters,
171 columnVisibility,
172 rowSelection,
173 },
174 })
175
176 return (
177 <div className="w-full">
178 <div className="flex items-center py-4">
179 <Input
180 placeholder="Filter names..."
181 value={(table.getColumn("name")?.getFilterValue() as string) ?? ""}
182 onChange={(event) =>
183 table.getColumn("name")?.setFilterValue(event.target.value)
184 }
185 className="max-w-sm"
186 />
187 <DropdownMenu>
188 <DropdownMenuTrigger asChild>
189 <Button variant="outline" className="ml-auto">
190 Columns <ChevronDown className="ml-2 h-4 w-4" />
191 </Button>
192 </DropdownMenuTrigger>
193 <DropdownMenuContent align="end">
194 {table
195 .getAllColumns()
196 .filter((column) => column.getCanHide())
197 .map((column) => {
198 return (
199 <DropdownMenuCheckboxItem
200 key={column.id}
201 className="capitalize"
202 checked={column.getIsVisible()}
203 onCheckedChange={(value) =>
204 column.toggleVisibility(!!value)
205 }
206 >
207 {column.id}
208 </DropdownMenuCheckboxItem>
209 )
210 })}
211 </DropdownMenuContent>
212 </DropdownMenu>
213 </div>
214 <div className="rounded-md border">
215 <Table>
216 <TableHeader>
217 {table.getHeaderGroups().map((headerGroup) => (
218 <TableRow key={headerGroup.id}>
219 {headerGroup.headers.map((header) => {
220 return (
221 <TableHead key={header.id}>
222 {header.isPlaceholder
223 ? null
224 : flexRender(
225 header.column.columnDef.header,
226 header.getContext()
227 )}
228 </TableHead>
229 )
230 })}
231 </TableRow>
232 ))}
233 </TableHeader>
234 <TableBody>
235 {table.getRowModel().rows?.length ? (
236 table.getRowModel().rows.map((row) => (
237 <TableRow
238 key={row.id}
239 data-state={row.getIsSelected() && "selected"}
240 >
241 {row.getVisibleCells().map((cell) => (
242 <TableCell key={cell.id}>
243 {flexRender(
244 cell.column.columnDef.cell,
245 cell.getContext()
246 )}
247 </TableCell>
248 ))}
249 </TableRow>
250 ))
251 ) : (
252 <TableRow>
253 <TableCell
254 colSpan={columns.length}
255 className="h-24 text-center"
256 >
257 No results.
258 </TableCell>
259 </TableRow>
260 )}
261 </TableBody>
262 </Table>
263 </div>
264 <div className="flex items-center justify-end space-x-2 py-4">
265 <div className="flex-1 text-sm text-muted-foreground">
266 {table.getFilteredSelectedRowModel().rows.length} of{" "}
267 {table.getFilteredRowModel().rows.length} row(s) selected.
268 </div>
269 <div className="space-x-2">
270 <Button
271 variant="outline"
272 size="sm"
273 onClick={() => table.previousPage()}
274 disabled={!table.getCanPreviousPage()}
275 >
276 Previous
277 </Button>
278 <Button
279 variant="outline"
280 size="sm"
281 onClick={() => table.nextPage()}
282 disabled={!table.getCanNextPage()}
283 >
284 Next
285 </Button>
286 </div>
287 </div>
288 </div>
289 )
290}
291
292/**
293 * Key Patterns Demonstrated:
294 *
295 * 1. TanStack Table Integration: Using @tanstack/react-table with shadcn/ui
296 * 2. Sorting: Click headers to sort ascending/descending
297 * 3. Filtering: Text input to filter table data
298 * 4. Column Visibility: Toggle columns via dropdown menu
299 * 5. Pagination: Built-in pagination controls
300 * 6. Row Actions: Dropdown menu per row for context actions
301 * 7. Responsive Design: Table adapts to different screen sizes
302 *
303 * Required Dependencies:
304 * - @tanstack/react-table
305 * - lucide-react
306 *
307 * Installation:
308 * npx shadcn@latest add table
309 * npx shadcn@latest add button
310 * npx shadcn@latest add input
311 * npx shadcn@latest add dropdown-menu
312 * npm install @tanstack/react-table lucide-react
313 */