From ef4c533ebacc2cdc97e518f30e9a9350004fcdfb Mon Sep 17 00:00:00 2001 From: dujinkim Date: Mon, 28 Apr 2025 02:13:30 +0000 Subject: ~20250428 작업사항 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/data-table/data-table-pin-left.tsx | 243 +++++++++++++++++++++++--- 1 file changed, 217 insertions(+), 26 deletions(-) (limited to 'components/data-table/data-table-pin-left.tsx') diff --git a/components/data-table/data-table-pin-left.tsx b/components/data-table/data-table-pin-left.tsx index 81e83564..e79e01eb 100644 --- a/components/data-table/data-table-pin-left.tsx +++ b/components/data-table/data-table-pin-left.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { type Table } from "@tanstack/react-table" +import { type Column, type Table } from "@tanstack/react-table" import { Check, ChevronsUpDown, MoveLeft } from "lucide-react" import { cn, toSentenceCase } from "@/lib/utils" @@ -13,6 +13,7 @@ import { CommandInput, CommandItem, CommandList, + CommandSeparator, } from "@/components/ui/command" import { Popover, @@ -21,14 +22,152 @@ import { } from "@/components/ui/popover" /** - * “Pin Left” Popover. Lists columns that can be pinned. - * If pinned===‘left’ → checked, if pinned!==‘left’ → unchecked. - * Toggling check => pin(‘left’) or pin(false). + * Helper function to check if a column is a parent column (has subcolumns) + */ +function isParentColumn(column: Column): boolean { + return column.columns && column.columns.length > 0 +} + +/** + * Helper function to pin all subcolumns of a parent column + */ +function pinSubColumns( + column: Column, + pinType: false | "left" | "right" +): void { + // If this is a parent column, pin all its subcolumns + if (isParentColumn(column)) { + column.columns.forEach((subColumn) => { + // Recursively handle nested columns + pinSubColumns(subColumn, pinType) + }) + } else { + // For leaf columns, apply the pin if possible + if (column.getCanPin?.()) { + column.pin?.(pinType) + } + } +} + +/** + * Checks if all subcolumns of a parent column are pinned to the specified side + */ +function areAllSubColumnsPinned( + column: Column, + pinType: "left" | "right" +): boolean { + if (isParentColumn(column)) { + // Check if all subcolumns are pinned + return column.columns.every((subColumn) => + areAllSubColumnsPinned(subColumn, pinType) + ) + } else { + // For leaf columns, check if it's pinned to the specified side + return column.getIsPinned?.() === pinType + } +} + +/** + * Helper function to get the display name of a column + */ +function getColumnDisplayName(column: Column): string { + // First try to use excelHeader from meta if available + const excelHeader = column.columnDef.meta?.excelHeader + if (excelHeader) { + return excelHeader + } + + // Fall back to converting the column ID to sentence case + return toSentenceCase(column.id) +} + +/** + * Array of column IDs that should be auto-pinned to the left when available + */ +const AUTO_PIN_LEFT_COLUMNS = ['select'] + +/** + * "Pin Left" Popover. Supports pinning both individual columns and header groups. */ export function PinLeftButton({ table }: { table: Table }) { const [open, setOpen] = React.useState(false) const triggerRef = React.useRef(null) + // Try to auto-pin select and action columns if they exist + React.useEffect(() => { + AUTO_PIN_LEFT_COLUMNS.forEach((columnId) => { + const column = table.getColumn(columnId) + if (column?.getCanPin?.()) { + column.pin?.("left") + } + }) + }, [table]) + + // Get all columns that can be pinned (excluding auto-pinned columns) + const pinnableColumns = React.useMemo(() => { + return table.getAllColumns().filter((column) => { + // Skip auto-pinned columns + if (AUTO_PIN_LEFT_COLUMNS.includes(column.id)) { + return false + } + + // If it's a leaf column, check if it can be pinned + if (!isParentColumn(column)) { + return column.getCanPin?.() + } + + // If it's a parent column, check if at least one subcolumn can be pinned + return column.columns.some((subCol) => { + if (isParentColumn(subCol)) { + // Recursively check nested columns + return subCol.columns.some(c => c.getCanPin?.()) + } + return subCol.getCanPin?.() + }) + }) + }, [table]) + + // Get flat list of all leaf columns for display + const allPinnableLeafColumns = React.useMemo(() => { + const leafColumns: Column[] = [] + + // Function to recursively collect leaf columns + const collectLeafColumns = (column: Column) => { + if (isParentColumn(column)) { + column.columns.forEach(collectLeafColumns) + } else if (column.getCanPin?.() && !AUTO_PIN_LEFT_COLUMNS.includes(column.id)) { + leafColumns.push(column) + } + } + + // Process all columns + table.getAllColumns().forEach(collectLeafColumns) + + return leafColumns + }, [table]) + + // Handle column pinning + const handleColumnPin = React.useCallback((column: Column) => { + // For parent columns, pin/unpin all subcolumns + if (isParentColumn(column)) { + const allPinned = areAllSubColumnsPinned(column, "left") + pinSubColumns(column, allPinned ? false : "left") + } else { + // For leaf columns, toggle pin state + const isPinned = column.getIsPinned?.() === "left" + column.pin?.(isPinned ? false : "left") + } + }, []) + + // Check if a column or its subcolumns are pinned left + const isColumnPinned = React.useCallback((column: Column): boolean => { + if (isParentColumn(column)) { + return areAllSubColumnsPinned(column, "left") + } else { + return column.getIsPinned?.() === "left" + } + }, []) + return ( @@ -39,53 +178,105 @@ export function PinLeftButton({ table }: { table: Table }) { className="h-8 gap-2" > - + - Left + 왼쪽 고정 - - + triggerRef.current?.focus()} > - + No columns found. - {table - .getAllLeafColumns() - .filter((col) => col.getCanPin?.()) - .map((column) => { - const pinned = column.getIsPinned?.() // 'left'|'right'|false - // => pinned === 'left' => checked - return ( + {/* Parent Columns with subcolumns */} + {pinnableColumns + .filter(isParentColumn) + .map((parentColumn) => ( + + {/* Parent column header - can pin/unpin all children at once */} { - // if currently pinned===left => unpin - // else => pin left - column.pin?.(pinned === "left" ? false : "left") + handleColumnPin(parentColumn) }} + className="font-medium bg-muted/50" > - {toSentenceCase(column.id)} + {getColumnDisplayName(parentColumn)} - {/* Check if pinned===‘left’ */} - ) - })} + + {/* Individual subcolumns */} + {parentColumn.columns + .filter(col => !isParentColumn(col) && col.getCanPin?.()) + .map(subColumn => ( + { + handleColumnPin(subColumn) + }} + className="pl-6 text-sm" + > + + {getColumnDisplayName(subColumn)} + + + + ))} + + ))} + + + {/* Separator if we have both parent columns and standalone leaf columns */} + {pinnableColumns.some(isParentColumn) && + allPinnableLeafColumns.some(col => !pinnableColumns.find(parent => + isParentColumn(parent) && parent.columns.includes(col)) + ) && ( + + )} + + {/* Standalone leaf columns (not part of any parent column group) */} + + {allPinnableLeafColumns + .filter(col => !pinnableColumns.find(parent => + isParentColumn(parent) && parent.columns.includes(col) + )) + .map((column) => ( + { + handleColumnPin(column) + }} + > + + {getColumnDisplayName(column)} + + + + ))} -- cgit v1.2.3