Skip to content

Commit 412eabe

Browse files
Table Sorting/Filtering (#74)
* Table Sorting/Filtering * type fixes * add advanced filtering to tables --------- Co-authored-by: Nick A <[email protected]>
1 parent 4e512cc commit 412eabe

File tree

9 files changed

+1121
-101
lines changed

9 files changed

+1121
-101
lines changed

frontend/package-lock.json

Lines changed: 116 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"dependencies": {
1212
"@hookform/resolvers": "^5.2.2",
1313
"@radix-ui/react-checkbox": "^1.3.3",
14-
"@radix-ui/react-label": "^2.1.7",
14+
"@radix-ui/react-dropdown-menu": "^2.1.16",
15+
"@radix-ui/react-label": "^2.1.8",
1516
"@radix-ui/react-popover": "^1.1.15",
1617
"@radix-ui/react-select": "^2.2.6",
1718
"@radix-ui/react-separator": "^1.1.7",
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
"use client";
2+
3+
import { Button } from "@/components/ui/button";
4+
import {
5+
DropdownMenu,
6+
DropdownMenuContent,
7+
DropdownMenuItem,
8+
DropdownMenuSeparator,
9+
DropdownMenuTrigger,
10+
} from "@/components/ui/dropdown-menu";
11+
import { Column } from "@tanstack/react-table";
12+
import { ArrowDown, ArrowUp, ChevronDown, Filter, X } from "lucide-react";
13+
import { useState } from "react";
14+
15+
interface ColumnHeaderProps<T> {
16+
column: Column<T, unknown>;
17+
title: string;
18+
onFilterClick?: () => void;
19+
}
20+
21+
export function ColumnHeader<T>({
22+
column,
23+
title,
24+
onFilterClick,
25+
}: ColumnHeaderProps<T>) {
26+
const [open, setOpen] = useState(false);
27+
const isFiltered = column.getIsFiltered();
28+
const isSorted = column.getIsSorted();
29+
const canFilter = column.getCanFilter();
30+
31+
return (
32+
<div className="flex items-center space-x-2">
33+
<DropdownMenu open={open} onOpenChange={setOpen}>
34+
<DropdownMenuTrigger asChild>
35+
<Button
36+
variant="ghost"
37+
size="sm"
38+
className={`-ml-3 h-8 data-[state=open]:bg-accent ${
39+
isFiltered ? "bg-purple-50" : ""
40+
}`}
41+
>
42+
<span>{title}</span>
43+
{isSorted === "asc" && (
44+
<ArrowUp className="ml-2 h-4 w-4" />
45+
)}
46+
{isSorted === "desc" && (
47+
<ArrowDown className="ml-2 h-4 w-4" />
48+
)}
49+
{!isSorted && <ChevronDown className="ml-2 h-4 w-4" />}
50+
</Button>
51+
</DropdownMenuTrigger>
52+
<DropdownMenuContent align="start">
53+
<DropdownMenuItem
54+
onClick={() => {
55+
column.toggleSorting(false);
56+
setOpen(false);
57+
}}
58+
>
59+
<ArrowUp className="mr-2 h-4 w-4" />
60+
Sort Ascending
61+
{isSorted === "asc" && (
62+
<span className="ml-auto text-primary"></span>
63+
)}
64+
</DropdownMenuItem>
65+
<DropdownMenuItem
66+
onClick={() => {
67+
column.toggleSorting(true);
68+
setOpen(false);
69+
}}
70+
>
71+
<ArrowDown className="mr-2 h-4 w-4" />
72+
Sort Descending
73+
{isSorted === "desc" && (
74+
<span className="ml-auto text-primary"></span>
75+
)}
76+
</DropdownMenuItem>
77+
{canFilter && (
78+
<>
79+
<DropdownMenuSeparator />
80+
{isFiltered ? (
81+
<DropdownMenuItem
82+
onClick={() => {
83+
column.setFilterValue(undefined);
84+
setOpen(false);
85+
}}
86+
className="text-red-600"
87+
>
88+
<X className="mr-2 h-4 w-4" />
89+
Clear Filter
90+
</DropdownMenuItem>
91+
) : (
92+
<DropdownMenuItem
93+
onClick={() => {
94+
setOpen(false);
95+
onFilterClick?.();
96+
}}
97+
>
98+
<Filter className="mr-2 h-4 w-4" />
99+
Add Filter
100+
</DropdownMenuItem>
101+
)}
102+
</>
103+
)}
104+
</DropdownMenuContent>
105+
</DropdownMenu>
106+
</div>
107+
);
108+
}

0 commit comments

Comments
 (0)