"use client" import * as React from "react" import { type Table } from "@tanstack/react-table" import { ArrowUpDown, Check, ChevronsUpDown, GripVertical, Settings2, } 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" // Sortable import { Sortable, SortableItem, SortableDragHandle, } from "@/components/ui/sortable" /** * ViewOptionsProps: * - table: TanStack Table instance */ interface DataTableViewOptionsProps { table: Table } declare module "@tanstack/react-table" { interface ColumnMeta { excelHeader?: string group?: string type?: string // or whatever other fields you actually use } } /** * DataTableViewOptions: * - Renders a Popover with hideable columns * - Lets user reorder columns (drag & drop) + toggle visibility */ export function ClientDataTableViewOptions({ table, }: DataTableViewOptionsProps) { const triggerRef = React.useRef(null) // 1) Identify columns that can be hidden const hideableCols = React.useMemo(() => { return table .getAllLeafColumns() .filter((col) => col.getCanHide() && col.accessorFn !== undefined) }, [table]) // 2) local state for "columnOrder" (just the ID of hideable columns) // We'll reorder these with drag & drop const [columnOrder, setColumnOrder] = React.useState(() => hideableCols.map((c) => c.id) ) // 3) onMove: when user finishes drag // - update local `columnOrder` only (no table.setColumnOrder yet) const handleMove = React.useCallback( ({ activeIndex, overIndex }: { activeIndex: number; overIndex: number }) => { setColumnOrder((prev) => { const newOrder = [...prev] const [removed] = newOrder.splice(activeIndex, 1) newOrder.splice(overIndex, 0, removed) return newOrder }) }, [] ) // 4) After local state changes, reflect in tanstack table // - We do this in useEffect to avoid "update a different component" error React.useEffect(() => { // Also consider "non-hideable" columns, if any, to keep them in original positions const nonHideable = table .getAllColumns() .filter((col) => col.getCanHide() && col.accessorFn !== undefined) .filter((col) => !hideableCols.some((hc) => hc.id === col.id)) .map((c) => c.id) // e.g. place nonHideable at the front, then our local hideable order const finalOrder = ["select",...nonHideable, ...columnOrder] // Now we set the table's official column order table.setColumnOrder(finalOrder) }, [columnOrder, hideableCols, table]) return ( triggerRef.current?.focus()} > No columns found. {/** * 5) Sortable: we pass an array of { id: string } from `columnOrder`, * so we can reorder them with drag & drop */} ({ id }))} onMove={handleMove} > {columnOrder.map((colId) => { // find column instance const column = hideableCols.find((c) => c.id === colId) if (!column) return null return ( column.toggleVisibility(!column.getIsVisible()) } > {/* Drag handle on the left */} {/* label */} {column?.columnDef?.meta?.excelHeader} {/* check if visible */} ) })} ) }