diff options
Diffstat (limited to 'components/data-table/data-table-pin.tsx')
| -rw-r--r-- | components/data-table/data-table-pin.tsx | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/components/data-table/data-table-pin.tsx b/components/data-table/data-table-pin.tsx new file mode 100644 index 00000000..991152f6 --- /dev/null +++ b/components/data-table/data-table-pin.tsx @@ -0,0 +1,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> + )} + </> + ) +}
\ No newline at end of file |
