Skip to content

Commit cfb71f3

Browse files
committed
Enhance TableGrid with Column Pinning and Header Grouping Features
1 parent f80b8bf commit cfb71f3

File tree

10 files changed

+1122
-188
lines changed

10 files changed

+1122
-188
lines changed

bun.lockb

416 Bytes
Binary file not shown.

src/app/page.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,12 @@ import HeaderGroupsTable from "@/components/containers/header-groups";
33
import BasicSearch from "@/components/containers/basic-search";
44
import FuzzySearchFilter from "@/components/containers/fuzzy-search-filter";
55
import ColumnVisibilityTable from "@/components/containers/column-visibility-table";
6+
import ColumnPinningTable from "@/components/containers/toggle-column-pinning-table";
67

78
export default function Home() {
89
return (
910
<div>
11+
<ColumnPinningTable />
1012
<ColumnVisibilityTable />
1113
<BasicTable />
1214
<HeaderGroupsTable />
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"use client";
2+
import TableGrid from "@/components/ui/table-grid/table-grid";
3+
import dummyData from "@/data/dummy.json";
4+
import type { Column } from "@/components/ui/table-grid/table-grid";
5+
import { useTableGrid } from "@/hooks/use-table-grid";
6+
7+
interface DataItem extends Record<string, unknown> {
8+
id: number;
9+
name: string;
10+
age: number;
11+
email: string;
12+
department: string;
13+
role: string;
14+
salary: number;
15+
status: string;
16+
location: string;
17+
joinDate: string;
18+
phone: string;
19+
}
20+
21+
const columns: Column<DataItem>[] = [
22+
{
23+
id: "id",
24+
header: "ID",
25+
accessorKey: "id",
26+
sortable: true,
27+
pinned: "left",
28+
},
29+
{
30+
id: "name",
31+
header: "Name",
32+
accessorKey: "name",
33+
sortable: true,
34+
},
35+
{
36+
id: "department",
37+
header: "Department",
38+
accessorKey: "department",
39+
sortable: true,
40+
},
41+
{
42+
id: "role",
43+
header: "Role",
44+
accessorKey: "role",
45+
sortable: true,
46+
},
47+
{
48+
id: "salary",
49+
header: "Salary",
50+
accessorKey: "salary",
51+
sortable: true,
52+
cell: ({ value }) => `$${(value as number).toLocaleString()}`,
53+
},
54+
{
55+
id: "status",
56+
header: "Status",
57+
accessorKey: "status",
58+
sortable: true,
59+
},
60+
{
61+
id: "location",
62+
header: "Location",
63+
accessorKey: "location",
64+
sortable: true,
65+
},
66+
{
67+
id: "age",
68+
header: "Age",
69+
accessorKey: "age",
70+
sortable: true,
71+
},
72+
{
73+
id: "email",
74+
header: "Email",
75+
accessorKey: "email",
76+
sortable: true,
77+
},
78+
{
79+
id: "phone",
80+
header: "Phone",
81+
accessorKey: "phone",
82+
sortable: true,
83+
},
84+
{
85+
id: "joinDate",
86+
header: "Join Date",
87+
accessorKey: "joinDate",
88+
sortable: true,
89+
},
90+
];
91+
92+
const ColumnPinningTable = () => {
93+
const { filteredData, handleSort, sortColumn, sortDirection } =
94+
useTableGrid<DataItem>({
95+
data: dummyData,
96+
columns,
97+
initialState: {
98+
sortColumn: "name",
99+
sortDirection: "asc",
100+
},
101+
});
102+
103+
return (
104+
<div className="p-4">
105+
<div className="flex flex-col gap-4 mb-4">
106+
<h2 className="text-2xl font-bold">Column Pinning</h2>
107+
</div>
108+
<TableGrid<DataItem>
109+
columns={columns}
110+
data={filteredData}
111+
gridTemplateColumns="1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr"
112+
maxHeight="400px"
113+
variant="classic"
114+
onSort={handleSort}
115+
sortColumn={sortColumn}
116+
sortDirection={sortDirection}
117+
/>
118+
</div>
119+
);
120+
};
121+
122+
export default ColumnPinningTable;

src/components/containers/header-groups.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ const HeaderGroupsTable = () => {
7171
onSort={handleSort}
7272
sortColumn={sortColumn}
7373
sortDirection={sortDirection}
74+
headerGroups={true}
7475
/>
7576
</div>
7677
);
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
"use client"
2+
import TableGrid from "@/components/ui/table-grid/table-grid";
3+
import dummyData from "@/data/dummy.json";
4+
import type { Column } from "@/components/ui/table-grid/table-grid";
5+
import { useTableGrid } from "@/hooks/use-table-grid";
6+
import { PiPushPinSimpleFill } from "react-icons/pi";
7+
import { useMemo } from "react";
8+
9+
interface DataItem extends Record<string, unknown> {
10+
id: number;
11+
name: string;
12+
age: number;
13+
email: string;
14+
department: string;
15+
role: string;
16+
salary: number;
17+
status: string;
18+
location: string;
19+
joinDate: string;
20+
phone: string;
21+
}
22+
23+
const columns: Column<DataItem>[] = [
24+
{
25+
id: "id",
26+
header: "ID",
27+
accessorKey: "id",
28+
sortable: true,
29+
width: "80px"
30+
},
31+
{
32+
id: "name",
33+
header: "Name",
34+
accessorKey: "name",
35+
sortable: true,
36+
},
37+
{
38+
id: "department",
39+
header: "Department",
40+
accessorKey: "department",
41+
sortable: true
42+
},
43+
{
44+
id: "role",
45+
header: "Role",
46+
accessorKey: "role",
47+
sortable: true
48+
},
49+
{
50+
id: "salary",
51+
header: "Salary",
52+
accessorKey: "salary",
53+
sortable: true,
54+
cell: ({ value }) => `$${(value as number).toLocaleString()}`
55+
},
56+
{
57+
id: "status",
58+
header: "Status",
59+
accessorKey: "status",
60+
sortable: true
61+
},
62+
{
63+
id: "location",
64+
header: "Location",
65+
accessorKey: "location",
66+
sortable: true
67+
},
68+
{
69+
id: "age",
70+
header: "Age",
71+
accessorKey: "age",
72+
sortable: true
73+
},
74+
{
75+
id: "email",
76+
header: "Email",
77+
accessorKey: "email",
78+
sortable: true,
79+
},
80+
{
81+
id: "phone",
82+
header: "Phone",
83+
accessorKey: "phone",
84+
sortable: true,
85+
},
86+
{
87+
id: "joinDate",
88+
header: "Join Date",
89+
accessorKey: "joinDate",
90+
sortable: true
91+
},
92+
];
93+
94+
const ToggleColumnPinningTable = () => {
95+
const {
96+
filteredData,
97+
handleSort,
98+
sortColumn,
99+
sortDirection,
100+
pinnedColumns,
101+
toggleColumnPin,
102+
} = useTableGrid<DataItem>({
103+
data: dummyData,
104+
columns,
105+
initialState: {
106+
sortColumn: "name",
107+
sortDirection: "asc",
108+
pinnedColumns: {
109+
left: ["id"],
110+
right: ["phone"]
111+
},
112+
},
113+
});
114+
115+
const syncedColumns = useMemo(() =>
116+
columns.map(column => ({
117+
...column,
118+
pinned: pinnedColumns.left.includes(column.id)
119+
? 'left' as const
120+
: pinnedColumns.right.includes(column.id)
121+
? 'right' as const
122+
: false as const
123+
})), [columns, pinnedColumns]
124+
);
125+
126+
const renderPinningControls = (columnId: string) => {
127+
const isPinnedLeft = pinnedColumns.left.includes(columnId);
128+
const isPinnedRight = pinnedColumns.right.includes(columnId);
129+
130+
return (
131+
<div className="flex gap-2">
132+
<button
133+
className={`flex items-center gap-2 ${isPinnedLeft ? "text-blue-500" : ""}`}
134+
onClick={() => toggleColumnPin(columnId, isPinnedLeft ? false : 'left')}
135+
disabled={isPinnedRight}
136+
>
137+
<PiPushPinSimpleFill className={isPinnedLeft ? "rotate-45" : ""} />
138+
<span className="ml-2">Pin Left</span>
139+
</button>
140+
<button
141+
className={`flex items-center gap-2 ${isPinnedRight ? "text-blue-500" : ""}`}
142+
onClick={() => toggleColumnPin(columnId, isPinnedRight ? false : 'right')}
143+
disabled={isPinnedLeft}
144+
>
145+
<PiPushPinSimpleFill className={isPinnedRight ? "rotate-45" : ""} />
146+
<span className="ml-2">Pin Right</span>
147+
</button>
148+
</div>
149+
);
150+
};
151+
152+
return (
153+
<div className="p-4">
154+
<div className="flex flex-col gap-4 mb-4">
155+
<h2 className="text-2xl font-bold">Column Pinning</h2>
156+
<div className="flex flex-wrap gap-4">
157+
{columns.map((column) => (
158+
<div key={column.id} className="flex items-center gap-2">
159+
<span>{column.header as string}:</span>
160+
{renderPinningControls(column.id)}
161+
</div>
162+
))}
163+
</div>
164+
</div>
165+
<TableGrid<DataItem>
166+
columns={syncedColumns}
167+
data={filteredData}
168+
gridTemplateColumns="1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr"
169+
maxHeight="400px"
170+
variant="classic"
171+
onSort={handleSort}
172+
sortColumn={sortColumn}
173+
sortDirection={sortDirection}
174+
/>
175+
</div>
176+
);
177+
};
178+
179+
export default ToggleColumnPinningTable;

src/components/ui/table-grid/styles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const tableStyles = tv({
2020
sortButton: "ms-2 inline-flex items-center gap-1",
2121
searchContainer: "mb-4",
2222
searchInput: "w-full px-4 py-2 border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500",
23+
headerGroup: "px-4 py-2 border-b bg-gray-50 dark:bg-gray-800 text-gray-700 dark:text-gray-200",
2324
},
2425
variants: {
2526
variant: {

0 commit comments

Comments
 (0)