DataTableViewOptions.tsx103 lines · main
1import { GripVertical, Settings2 } from 'lucide-react'
2import { useId, useMemo, useState } from 'react'
3import {
4 Checkbox,
5 Command,
6 CommandEmpty,
7 CommandGroup,
8 CommandInput,
9 CommandItem,
10 CommandList,
11 Popover,
12 PopoverContent,
13 PopoverTrigger,
14} from 'ui'
15
16import { ButtonTooltip } from '../ButtonTooltip'
17import { Sortable, SortableDragHandle, SortableItem } from './primitives/Sortable'
18import { useDataTable } from './providers/DataTableProvider'
19
20export function DataTableViewOptions() {
21 const { table, enableColumnOrdering } = useDataTable()
22 const [open, setOpen] = useState(false)
23 const [drag, setDrag] = useState(false)
24 const [search, setSearch] = useState('')
25 const listboxId = useId()
26
27 const columnOrder = table.getState().columnOrder
28
29 const sortedColumns = useMemo(
30 () =>
31 table.getAllColumns().sort((a, b) => {
32 return columnOrder.indexOf(a.id) - columnOrder.indexOf(b.id)
33 }),
34 [columnOrder]
35 )
36
37 return (
38 <Popover open={open} onOpenChange={setOpen}>
39 <PopoverTrigger asChild>
40 <ButtonTooltip
41 type="default"
42 size="tiny"
43 role="combobox"
44 aria-expanded={open}
45 aria-controls={listboxId}
46 className="w-[26px]"
47 icon={<Settings2 className="text-foreground" />}
48 tooltip={{ content: { side: 'bottom', text: 'Toggle column visibility' } }}
49 />
50 </PopoverTrigger>
51 <PopoverContent id={listboxId} side="bottom" align="end" className="w-[200px] p-0">
52 <Command>
53 <CommandInput
54 value={search}
55 onValueChange={setSearch}
56 placeholder="Search columns..."
57 className="text-xs"
58 />
59 <CommandList>
60 <CommandEmpty>No option found.</CommandEmpty>
61 <CommandGroup>
62 <Sortable
63 value={sortedColumns.map((c) => ({ id: c.id }))}
64 onValueChange={(items) => table.setColumnOrder(items.map((c) => c.id))}
65 overlay={<div className="h-8 w-full rounded-md bg-muted/60" />}
66 onDragStart={() => setDrag(true)}
67 onDragEnd={() => setDrag(false)}
68 onDragCancel={() => setDrag(false)}
69 >
70 {sortedColumns
71 .filter(
72 (column) => typeof column.accessorFn !== 'undefined' && column.getCanHide()
73 )
74 .map((column) => (
75 <SortableItem key={column.id} value={column.id} asChild>
76 <CommandItem
77 value={column.id}
78 onSelect={() => column.toggleVisibility(!column.getIsVisible())}
79 className="capitalize p-1"
80 disabled={drag}
81 >
82 <Checkbox checked={column.getIsVisible()} className="mr-2" />
83 <span>{(column.columnDef.meta as any)?.label || column.id}</span>
84 {enableColumnOrdering && !search ? (
85 <SortableDragHandle
86 type="text"
87 size="tiny"
88 className="ml-auto size-5 text-muted-foreground hover:text-foreground focus:bg-muted focus:text-foreground"
89 >
90 <GripVertical className="size-4" aria-hidden="true" />
91 </SortableDragHandle>
92 ) : null}
93 </CommandItem>
94 </SortableItem>
95 ))}
96 </Sortable>
97 </CommandGroup>
98 </CommandList>
99 </Command>
100 </PopoverContent>
101 </Popover>
102 )
103}