From bac0228d21b7195065e9cddcc327ae33659c7bcc Mon Sep 17 00:00:00 2001
From: dujinkim <dujin.kim@dtsolution.co.kr>
Date: Sun, 1 Jun 2025 13:52:21 +0000
Subject: (대표님) 20250601까지 작업사항
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 components/data-table/infinite-data-table.tsx | 69 +++++++++++++++++++++++----
 1 file changed, 60 insertions(+), 9 deletions(-)

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

diff --git a/components/data-table/infinite-data-table.tsx b/components/data-table/infinite-data-table.tsx
index fcac56ee..b8764d62 100644
--- a/components/data-table/infinite-data-table.tsx
+++ b/components/data-table/infinite-data-table.tsx
@@ -6,7 +6,7 @@ import { ChevronRight, ChevronUp, Loader2 } from "lucide-react"
 import { useIntersection } from "@mantine/hooks"
 
 import { cn } from "@/lib/utils"
-import { getCommonPinningStyles } from "@/lib/data-table"
+import { getCommonPinningStylesWithBorder } from "@/lib/data-table"
 import {
   Table,
   TableBody,
@@ -53,6 +53,9 @@ export function InfiniteDataTable<TData>({
 
   useAutoSizeColumns(table, autoSizeColumns)
 
+  // 🎯 스크롤 상태 감지 추가
+  const [isScrolled, setIsScrolled] = React.useState(false)
+
   // Intersection Observer for infinite scroll
   const { ref: loadMoreRef, entry } = useIntersection({
     threshold: 0.1,
@@ -65,21 +68,62 @@ export function InfiniteDataTable<TData>({
     }
   }, [entry?.isIntersecting, hasNextPage, isLoadingMore, onLoadMore])
 
+  // 🎯 스크롤 핸들러 추가
+  const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
+    const scrollLeft = e.currentTarget.scrollLeft
+    setIsScrolled(scrollLeft > 0)
+  }
+
+  // 🎯 동적 핀 스타일 함수 (width 중복 제거)
+  const getPinnedStyle = (column: any, isHeader: boolean = false) => {
+    const baseStyle = getCommonPinningStylesWithBorder({ column })
+    const pinnedSide = column.getIsPinned()
+    
+    // width를 제외한 나머지 스타일만 반환
+    const { width, ...restBaseStyle } = baseStyle
+    
+    return {
+      ...restBaseStyle,
+      // 헤더는 핀 여부와 관계없이 항상 배경 유지 (sticky로 고정되어 있기 때문)
+      ...(isHeader && {
+        background: "hsl(var(--background))",
+        transition: "none",
+      }),
+      // 바디 셀은 핀된 경우에만 스크롤 상태에 따라 동적 변경
+      ...(!isHeader && pinnedSide && {
+        background: isScrolled 
+          ? "hsl(var(--background))" 
+          : "transparent",
+        transition: "background-color 0.15s ease-out",
+      }),
+    }
+  }
+
+  // 🎯 테이블 총 너비 계산
+  const getTableWidth = React.useCallback(() => {
+    const totalSize = table.getCenterTotalSize() + table.getLeftTotalSize() + table.getRightTotalSize()
+    return Math.max(totalSize, 800) // 최소 800px 보장
+  }, [table])
+
   // 컴팩트 모드를 위한 클래스 정의
   const compactStyles = compact ? {
     row: "h-7",
     cell: "py-1 px-2 text-sm",
+    header: "py-1 px-2 text-sm", // 헤더 스타일 추가
+    headerRow: "h-8", // 헤더 행 높이 추가
     groupRow: "py-1 bg-muted/20 text-sm",
     emptyRow: "h-16",
   } : {
     row: "",
     cell: "",
+    header: "", // 헤더 스타일 추가
+    headerRow: "", // 헤더 행 높이 추가
     groupRow: "bg-muted/20",
     emptyRow: "h-24",
   }
 
   return (
-    <div className={cn("w-full space-y-2.5 overflow-auto", className)} {...props}>
+    <div className={cn("w-full space-y-2.5", className)} {...props}>
       {children}
       
       {/* 총 개수 표시 */}
@@ -97,12 +141,19 @@ export function InfiniteDataTable<TData>({
       <div 
         className="max-w-[100vw] overflow-auto" 
         style={{ maxHeight: maxHeight || '35rem' }}
+        onScroll={handleScroll} // 🎯 스크롤 이벤트 핸들러 추가
       >
-        <Table className="[&>thead]:sticky [&>thead]:top-0 [&>thead]:z-10 table-fixed">
+        <Table 
+          className="[&>thead]:sticky [&>thead]:top-0 [&>thead]:z-10"
+          style={{ 
+            width: getTableWidth(), // 🎯 동적 너비 계산
+            minWidth: '100%'
+          }}
+        >
           {/* 테이블 헤더 */}
           <TableHeader>
             {table.getHeaderGroups().map((headerGroup) => (
-              <TableRow key={headerGroup.id} className={compact ? "h-8" : ""}>
+              <TableRow key={headerGroup.id} className={compactStyles.headerRow}>
                 {headerGroup.headers.map((header) => {
                   if (header.column.getIsGrouped()) {
                     return null
@@ -113,10 +164,10 @@ export function InfiniteDataTable<TData>({
                       key={header.id}
                       colSpan={header.colSpan}
                       data-column-id={header.column.id}
-                      className={compact ? "py-1 px-2 text-sm" : ""}
+                      className={compactStyles.header}
                       style={{
-                        ...getCommonPinningStyles({ column: header.column }),
-                        width: header.getSize(),
+                        ...getPinnedStyle(header.column, true), // 🎯 헤더임을 명시
+                        width: header.getSize(), // 🎯 width 별도 설정
                       }}
                     >
                       <div style={{ position: "relative" }}>
@@ -211,8 +262,8 @@ export function InfiniteDataTable<TData>({
                             data-column-id={cell.column.id}
                             className={compactStyles.cell}
                             style={{
-                              ...getCommonPinningStyles({ column: cell.column }),
-                              width: cell.column.getSize(),
+                              ...getPinnedStyle(cell.column, false), // 🎯 바디 셀임을 명시
+                              width: cell.column.getSize(), // 🎯 width 별도 설정
                             }}
                           >
                             {flexRender(
-- 
cgit v1.2.3

