diff options
Diffstat (limited to 'components/client-table/client-table-column-header.tsx')
| -rw-r--r-- | components/client-table/client-table-column-header.tsx | 70 |
1 files changed, 63 insertions, 7 deletions
diff --git a/components/client-table/client-table-column-header.tsx b/components/client-table/client-table-column-header.tsx index 12dc57ac..2d8e5bce 100644 --- a/components/client-table/client-table-column-header.tsx +++ b/components/client-table/client-table-column-header.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { Header } from "@tanstack/react-table" +import { Header, Column } from "@tanstack/react-table" import { useSortable } from "@dnd-kit/sortable" import { CSS } from "@dnd-kit/utilities" import { flexRender } from "@tanstack/react-table" @@ -20,19 +20,29 @@ import { PinOff, MoveLeft, MoveRight, + Group, + Ungroup, } from "lucide-react" import { cn } from "@/lib/utils" -import { ClientTableFilter } from "./client-table-filter" +import { ClientTableFilter } from "../client-table/client-table-filter" interface ClientTableColumnHeaderProps<TData, TValue> extends React.HTMLAttributes<HTMLTableHeaderCellElement> { header: Header<TData, TValue> enableReordering?: boolean + renderHeaderVisualFeedback?: (props: { + column: Column<TData, TValue> + isPinned: boolean | string + isSorted: boolean | string + isFiltered: boolean + isGrouped: boolean + }) => React.ReactNode } export function ClientTableColumnHeader<TData, TValue>({ header, enableReordering = true, + renderHeaderVisualFeedback, className, ...props }: ClientTableColumnHeaderProps<TData, TValue>) { @@ -46,7 +56,7 @@ export function ClientTableColumnHeader<TData, TValue>({ isDragging, } = useSortable({ id: header.id, - disabled: !enableReordering, + disabled: !enableReordering || column.getIsResizing(), }) // -- Styles -- @@ -62,14 +72,18 @@ export function ClientTableColumnHeader<TData, TValue>({ // Pinning Styles const isPinned = column.getIsPinned() + const isSorted = column.getIsSorted() + const isFiltered = column.getFilterValue() !== undefined + const isGrouped = column.getIsGrouped() + if (isPinned === "left") { style.left = `${column.getStart("left")}px` style.position = "sticky" - style.zIndex = 20 + style.zIndex = 30 // Pinned columns needs to be higher than normal headers } else if (isPinned === "right") { style.right = `${column.getAfter("right")}px` style.position = "sticky" - style.zIndex = 20 + style.zIndex = 30 // Pinned columns needs to be higher than normal headers } // -- Handlers -- @@ -77,6 +91,7 @@ export function ClientTableColumnHeader<TData, TValue>({ const handlePinLeft = () => column.pin("left") const handlePinRight = () => column.pin("right") const handleUnpin = () => column.pin(false) + const handleToggleGrouping = () => column.toggleGrouping() // -- Content -- const content = ( @@ -100,12 +115,14 @@ export function ClientTableColumnHeader<TData, TValue>({ )} </span> )} + {isGrouped && <Group className="h-4 w-4 text-blue-500" />} </div> {/* Resize Handle */} <div onMouseDown={header.getResizeHandler()} onTouchStart={header.getResizeHandler()} + onPointerDown={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()} // Prevent sort trigger className={cn( "absolute right-0 top-0 h-full w-2 cursor-col-resize select-none touch-none z-10", @@ -117,6 +134,25 @@ export function ClientTableColumnHeader<TData, TValue>({ {/* Filter */} {column.getCanFilter() && <ClientTableFilter column={column} />} + + {/* Visual Feedback Indicators */} + {renderHeaderVisualFeedback ? ( + renderHeaderVisualFeedback({ + column, + isPinned, + isSorted, + isFiltered, + isGrouped, + }) + ) : ( + (isPinned || isFiltered || isGrouped) && ( + <div className="absolute top-0.5 right-1 flex gap-1 z-10 pointer-events-none"> + {isPinned && <div className="h-1.5 w-1.5 rounded-full bg-blue-500" />} + {isFiltered && <div className="h-1.5 w-1.5 rounded-full bg-yellow-500" />} + {isGrouped && <div className="h-1.5 w-1.5 rounded-full bg-green-500" />} + </div> + ) + )} </> ) @@ -141,9 +177,9 @@ export function ClientTableColumnHeader<TData, TValue>({ colSpan={header.colSpan} style={style} className={cn( - "border-b px-4 py-2 text-left text-sm font-medium bg-muted group", + "border-b px-4 py-2 text-left text-sm font-medium bg-muted group transition-colors", isDragging ? "opacity-50 bg-accent" : "", - isPinned ? "bg-muted shadow-[0_0_10px_rgba(0,0,0,0.1)]" : "", + isPinned ? "shadow-[0_0_10px_rgba(0,0,0,0.1)]" : "", className )} {...attributes} @@ -158,6 +194,26 @@ export function ClientTableColumnHeader<TData, TValue>({ <EyeOff className="mr-2 h-4 w-4" /> Hide Column </ContextMenuItem> + + {column.getCanGroup() && ( + <> + <ContextMenuSeparator /> + <ContextMenuItem onClick={handleToggleGrouping}> + {isGrouped ? ( + <> + <Ungroup className="mr-2 h-4 w-4" /> + Ungroup + </> + ) : ( + <> + <Group className="mr-2 h-4 w-4" /> + Group by {column.id} + </> + )} + </ContextMenuItem> + </> + )} + <ContextMenuSeparator /> <ContextMenuItem onClick={handlePinLeft}> <MoveLeft className="mr-2 h-4 w-4" /> |
