From b8e8328b1ffffb80bf4ebb776a4a24e5680fc5bc Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Fri, 28 Mar 2025 00:42:08 +0000
Subject: 테이블 칼럼 리사이즈 및 핀 충돌 해결
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/data-table/data-table-pin-right.tsx | 173 ++++++++++++++++++++-----
 1 file changed, 144 insertions(+), 29 deletions(-)

(limited to 'components/data-table/data-table-pin-right.tsx')

diff --git a/components/data-table/data-table-pin-right.tsx b/components/data-table/data-table-pin-right.tsx
index 051dd985..3ed42402 100644
--- a/components/data-table/data-table-pin-right.tsx
+++ b/components/data-table/data-table-pin-right.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, MoveRight } 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,12 +22,99 @@ import {
 } from "@/components/ui/popover"
 
 /**
- * “Pin Right” Popover. Similar to PinLeftButton, but pins columns to "right".
+ * Helper function to check if a column is a parent column (has subcolumns)
+ */
+function isParentColumn<TData>(column: Column<TData>): boolean {
+  return column.columns && column.columns.length > 0
+}
+
+/**
+ * Helper function to pin all subcolumns of a parent column
+ */
+function pinSubColumns<TData>(
+  column: Column<TData>,
+  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<TData>(
+  column: Column<TData>,
+  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
+  }
+}
+
+/**
+ * "Pin Right" Popover. Supports pinning both individual columns and header groups.
  */
 export function PinRightButton<TData>({ table }: { table: Table<TData> }) {
   const [open, setOpen] = React.useState(false)
   const triggerRef = React.useRef<HTMLButtonElement>(null)
 
+  // Get all columns that can be pinned, including parent columns
+  const pinnableColumns = React.useMemo(() => {
+    return table.getAllColumns().filter((column) => {
+      // 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])
+
+  // Handle column pinning
+  const handleColumnPin = React.useCallback((column: Column<TData>) => {
+    // For parent columns, pin/unpin all subcolumns
+    if (isParentColumn(column)) {
+      const allPinned = areAllSubColumnsPinned(column, "right")
+      pinSubColumns(column, allPinned ? false : "right")
+    } else {
+      // For leaf columns, toggle pin state
+      const isPinned = column.getIsPinned?.() === "right"
+      column.pin?.(isPinned ? false : "right")
+    }
+  }, [])
+
+  // Check if a column or its subcolumns are pinned right
+  const isColumnPinned = React.useCallback((column: Column<TData>): boolean => {
+    if (isParentColumn(column)) {
+      return areAllSubColumnsPinned(column, "right")
+    } else {
+      return column.getIsPinned?.() === "right"
+    }
+  }, [])
+
   return (
     <Popover open={open} onOpenChange={setOpen}>
       <PopoverTrigger asChild>
@@ -37,17 +125,17 @@ export function PinRightButton<TData>({ table }: { table: Table<TData> }) {
           className="h-8 gap-2"
         >
           <MoveRight className="size-4" />
-
+          
           <span className="hidden sm:inline">
             Right
           </span>
           <ChevronsUpDown className="ml-1 size-4 opacity-50 hidden sm:inline" />
         </Button>
       </PopoverTrigger>
-
+      
       <PopoverContent
         align="end"
-        className="w-44 p-0"
+        className="w-56 p-0"
         onCloseAutoFocus={() => triggerRef.current?.focus()}
       >
         <Command>
@@ -55,30 +143,57 @@ export function PinRightButton<TData>({ table }: { table: Table<TData> }) {
           <CommandList>
             <CommandEmpty>No columns found.</CommandEmpty>
             <CommandGroup>
-              {table
-                .getAllLeafColumns()
-                .filter((col) => col.getCanPin?.())
-                .map((column) => {
-                  const pinned = column.getIsPinned?.()
-                  return (
-                    <CommandItem
-                      key={column.id}
-                      onSelect={() => {
-                        column.pin?.(pinned === "right" ? false : "right")
-                      }}
-                    >
-                      <span className="truncate">
-                        {toSentenceCase(column.id)}
-                      </span>
-                      <Check
-                        className={cn(
-                          "ml-auto size-4 shrink-0",
-                          pinned === "right" ? "opacity-100" : "opacity-0"
-                        )}
-                      />
-                    </CommandItem>
-                  )
-                })}
+              {/* Header Columns (Parent Columns) */}
+              {pinnableColumns
+                .filter(isParentColumn)
+                .map((column) => (
+                  <CommandItem
+                    key={column.id}
+                    onSelect={() => {
+                      handleColumnPin(column)
+                    }}
+                    className="font-medium"
+                  >
+                    <span className="truncate">
+                      {column.id === "Basic Info" || column.id === "Metadata" 
+                        ? column.id // Use column ID directly for common groups
+                        : toSentenceCase(column.id)}
+                    </span>
+                    <Check
+                      className={cn(
+                        "ml-auto size-4 shrink-0",
+                        isColumnPinned(column) ? "opacity-100" : "opacity-0"
+                      )}
+                    />
+                  </CommandItem>
+                ))}
+              
+              {pinnableColumns.some(isParentColumn) && 
+               pinnableColumns.some(col => !isParentColumn(col)) && (
+                <CommandSeparator />
+              )}
+              
+              {/* Leaf Columns (individual columns) */}
+              {pinnableColumns
+                .filter(col => !isParentColumn(col))
+                .map((column) => (
+                  <CommandItem
+                    key={column.id}
+                    onSelect={() => {
+                      handleColumnPin(column)
+                    }}
+                  >
+                    <span className="truncate">
+                      {toSentenceCase(column.id)}
+                    </span>
+                    <Check
+                      className={cn(
+                        "ml-auto size-4 shrink-0",
+                        isColumnPinned(column) ? "opacity-100" : "opacity-0"
+                      )}
+                    />
+                  </CommandItem>
+                ))}
             </CommandGroup>
           </CommandList>
         </Command>
-- 
cgit v1.2.3

