1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
|
"use client"
import * as React from "react"
import { type Table } from "@tanstack/react-table"
import {
MoveLeft,
MoveRight,
Slash,
ChevronsUpDown,
} from "lucide-react"
import { cn, toSentenceCase } from "@/lib/utils"
import { Button } from "@/components/ui/button"
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command"
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover"
/**
* Button that opens a popover with a list of columns.
* Each column can be pinned left, pinned right, or unpinned.
*/
export function DataTablePinList<TData>({ table }: { table: Table<TData> }) {
const [open, setOpen] = React.useState(false)
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
variant="outline"
size="sm"
className="h-8 gap-2"
>
Pin
<ChevronsUpDown className="size-4 opacity-50" aria-hidden="true" />
</Button>
</PopoverTrigger>
<PopoverContent align="end" className="w-48 p-0">
<Command>
<CommandInput placeholder="Search columns..." />
<CommandList>
<CommandEmpty>No columns found.</CommandEmpty>
<CommandGroup>
{table
.getAllLeafColumns()
.filter((col) => col.getCanPin?.()) // Only show columns that can be pinned
.map((column) => {
const pinned = column.getIsPinned?.() // 'left' | 'right' | false
return (
<PinColumnItem
key={column.id}
column={column}
pinned={pinned}
onPinnedChange={(newPin) => {
column.pin?.(newPin === "none" ? false : newPin)
}}
/>
)
})}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
)
}
/**
* Renders a single column row (CommandItem) with sub-options:
* Left, Right, or Unpin
*/
function PinColumnItem({
column,
pinned,
onPinnedChange,
}: {
column: any
pinned: "left" | "right" | false
onPinnedChange: (newPin: "left" | "right" | "none") => void
}) {
const [subOpen, setSubOpen] = React.useState(false)
const colId = column.id
const handleMainSelect = () => {
// Toggle subOpen to show sub-options
setSubOpen((prev) => !prev)
}
return (
<>
<CommandItem onSelect={handleMainSelect}>
<span className="truncate">{toSentenceCase(colId)}</span>
{pinned === "left" && (
<MoveLeft className="ml-auto size-4 text-primary" />
)}
{pinned === "right" && (
<MoveRight className="ml-auto size-4 text-primary" />
)}
{pinned === false && (
<Slash className="ml-auto size-4 opacity-50" />
)}
</CommandItem>
{subOpen && (
<div className="ml-4 flex flex-col gap-1 border-l pl-2 pt-1">
<CommandItem
onSelect={() => {
onPinnedChange("left")
setSubOpen(false)
}}
>
<MoveLeft className="mr-2 size-4" />
Pin Left
</CommandItem>
<CommandItem
onSelect={() => {
onPinnedChange("right")
setSubOpen(false)
}}
>
<MoveRight className="mr-2 size-4" />
Pin Right
</CommandItem>
<CommandItem
onSelect={() => {
onPinnedChange("none")
setSubOpen(false)
}}
>
<Slash className="mr-2 size-4" />
No Pin
</CommandItem>
</div>
)}
</>
)
}
|