diff options
| author | joonhoekim <26rote@gmail.com> | 2025-10-28 14:26:44 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-10-28 14:26:44 +0900 |
| commit | 06cf51e5dd14e118fa8dbb8c666d78ace61cbf9b (patch) | |
| tree | 13867df07db4c6509fcbafe05ea6b6e4ef3a2bb7 | |
| parent | c9251054ccea7e53d144a4a1a405c9dfad969a8b (diff) | |
(김준회) fix: 공통컴포넌트: 데이터 그리드: 중첩헤더 있는 경우의 width 계산 및 스타일 적용문제 해결
| -rw-r--r-- | components/data-table/data-table-detail.tsx | 28 | ||||
| -rw-r--r-- | components/data-table/data-table.tsx | 17 | ||||
| -rw-r--r-- | components/data-table/expandable-data-table.tsx | 15 | ||||
| -rw-r--r-- | components/data-table/infinite-data-table.tsx | 15 |
4 files changed, 62 insertions, 13 deletions
diff --git a/components/data-table/data-table-detail.tsx b/components/data-table/data-table-detail.tsx index f3f09fe2..016b9f42 100644 --- a/components/data-table/data-table-detail.tsx +++ b/components/data-table/data-table-detail.tsx @@ -60,6 +60,19 @@ export function DataTableDetail<TData>({ useAutoSizeColumns(table, autoSizeColumns) + // nested header 감지: columns 속성을 가진 헤더가 있는지 확인 + const hasNestedHeader = React.useMemo(() => { + return table.getHeaderGroups().some(headerGroup => + headerGroup.headers.some(header => 'columns' in header.column.columnDef) + ) + }, [table]) + + // 🎯 테이블 총 너비 계산 (nested header가 있을 때만 사용) + const getTableWidth = React.useCallback(() => { + const totalSize = table.getCenterTotalSize() + table.getLeftTotalSize() + table.getRightTotalSize() + return Math.max(totalSize, 800) // 최소 800px 보장 + }, [table]) + // ✅ compactStyles를 useMemo로 메모이제이션 const compactStyles = React.useMemo(() => compact ? COMPACT_STYLES : NORMAL_STYLES, @@ -70,7 +83,12 @@ export function DataTableDetail<TData>({ <div className={cn("w-full space-y-2.5 overflow-auto", className)} {...props}> {children} <div className="max-w-[100vw] overflow-auto" style={{ maxHeight: maxHeight || '35rem' }} > - <Table className="[&>thead]:sticky [&>thead]:top-0 [&>thead]:z-10 table-fixed"> + <Table + className={cn( + "[&>thead]:sticky [&>thead]:top-0 [&>thead]:z-10", + !hasNestedHeader && "table-fixed" + )} + style={{ minWidth: hasNestedHeader ? getTableWidth() : undefined }}> {/* 테이블 헤더 */} <TableHeader> {table.getHeaderGroups().map((headerGroup) => ( @@ -93,7 +111,11 @@ export function DataTableDetail<TData>({ }), // 부모 그룹 헤더는 colSpan으로 너비가 결정되므로 width 설정하지 않음 // 자식 헤더만 개별 width 설정 - ...(!('columns' in header.column.columnDef) && { width: header.getSize() }), + ...(!('columns' in header.column.columnDef) && { + width: header.getSize(), + minWidth: header.getSize(), + maxWidth: header.column.columnDef.maxSize, + }), }} > <div style={{ position: "relative" }}> @@ -190,6 +212,8 @@ export function DataTableDetail<TData>({ style={{ ...getCommonPinningStylesWithBorder({ column: cell.column }), width: cell.column.getSize(), + minWidth: cell.column.getSize(), + maxWidth: cell.column.columnDef.maxSize, }} > {flexRender( diff --git a/components/data-table/data-table.tsx b/components/data-table/data-table.tsx index 44cbd47b..f6d3af73 100644 --- a/components/data-table/data-table.tsx +++ b/components/data-table/data-table.tsx @@ -78,6 +78,12 @@ export function DataTable<TData>({ return children; }, [children]); + // 🎯 테이블 총 너비 계산 (nested header가 있을 때만 사용) + const getTableWidth = React.useCallback(() => { + const totalSize = table.getCenterTotalSize() + table.getLeftTotalSize() + table.getRightTotalSize() + return Math.max(totalSize, 800) // 최소 800px 보장 + }, [table]) + return ( <div className={cn("w-full space-y-2.5 overflow-auto", className)} {...props}> {stableChildren} @@ -86,8 +92,9 @@ export function DataTable<TData>({ className={cn( "[&>thead]:sticky [&>thead]:top-0 [&>thead]:z-10", !hasNestedHeader && "table-fixed" // nested header가 없으면 table-fixed 적용 - )}> - {/* nested header가 있으면 table-fixed 제거, 없으면 적용 */} + )} + style={{ minWidth: hasNestedHeader ? getTableWidth() : undefined }}> + {/* nested header가 있으면 table-fixed 제거하고 minWidth로 너비 강제, 없으면 table-fixed 적용 */} {/* 테이블 헤더 */} <TableHeader> {table.getHeaderGroups().map((headerGroup) => ( @@ -116,7 +123,9 @@ export function DataTable<TData>({ // 부모 그룹 헤더는 colSpan으로 너비가 결정되므로 width 설정하지 않음 // 자식 헤더만 개별 width 설정 ...(!('columns' in header.column.columnDef) && { - width: header.getSize() + width: header.getSize(), + minWidth: header.getSize(), + maxWidth: header.column.columnDef.maxSize, }), }} > @@ -214,6 +223,8 @@ export function DataTable<TData>({ style={{ ...getCommonPinningStylesWithBorder({ column: cell.column }), width: cell.column.getSize(), + minWidth: cell.column.getSize(), + maxWidth: cell.column.columnDef.maxSize, }} > {flexRender( diff --git a/components/data-table/expandable-data-table.tsx b/components/data-table/expandable-data-table.tsx index 165ebd34..bc0833a6 100644 --- a/components/data-table/expandable-data-table.tsx +++ b/components/data-table/expandable-data-table.tsx @@ -510,8 +510,9 @@ export function ExpandableDataTable<TData>({ className={cn( "[&>thead]:sticky [&>thead]:top-0 [&>thead]:z-10", !hasNestedHeader && "table-fixed" // nested header가 없으면 table-fixed 적용 - )}> - {/* nested header가 있으면 table-fixed 제거, 없으면 적용 */} + )} + style={{ minWidth: hasNestedHeader ? getTableWidth() : undefined }}> + {/* nested header가 있으면 table-fixed 제거하고 minWidth로 너비 강제, 없으면 table-fixed 적용 */} <TableHeader className="bg-background"> {table.getHeaderGroups().map((headerGroup) => ( <TableRow key={headerGroup.id} className={compactStyles.headerRow}> @@ -540,7 +541,11 @@ export function ExpandableDataTable<TData>({ ...getPinnedStyle(header.column, true), // 부모 그룹 헤더는 colSpan으로 너비가 결정되므로 width 설정하지 않음 // 자식 헤더만 개별 width 설정 - ...(!('columns' in header.column.columnDef) && { width: header.getSize() }), + ...(!('columns' in header.column.columnDef) && { + width: header.getSize(), + minWidth: header.getSize(), + maxWidth: header.column.columnDef.maxSize, + }), }} > <div style={{ position: "relative" }}> @@ -655,7 +660,9 @@ export function ExpandableDataTable<TData>({ )} style={{ ...getPinnedStyle(cell.column, false), - width: cell.column.getSize() + width: cell.column.getSize(), + minWidth: cell.column.getSize(), + maxWidth: cell.column.columnDef.maxSize, }} // ✅ 클릭 이벤트 추가 onClick={isClickable ? (e) => handleCellClick(row.id, cell.column.id, e) : undefined} diff --git a/components/data-table/infinite-data-table.tsx b/components/data-table/infinite-data-table.tsx index 0242e052..a17a9b55 100644 --- a/components/data-table/infinite-data-table.tsx +++ b/components/data-table/infinite-data-table.tsx @@ -154,8 +154,9 @@ export function InfiniteDataTable<TData>({ className={cn( "[&>thead]:sticky [&>thead]:top-0 [&>thead]:z-10", !hasNestedHeader && "table-fixed" // nested header가 없으면 table-fixed 적용 - )}> - {/* nested header가 있으면 table-fixed 제거, 없으면 적용 */} + )} + style={{ minWidth: hasNestedHeader ? getTableWidth() : undefined }}> + {/* nested header가 있으면 table-fixed 제거하고 minWidth로 너비 강제, 없으면 table-fixed 적용 */} {/* 테이블 헤더 */} <TableHeader> {table.getHeaderGroups().map((headerGroup) => ( @@ -175,7 +176,11 @@ export function InfiniteDataTable<TData>({ ...getPinnedStyle(header.column, true), // 🎯 헤더임을 명시 // 부모 그룹 헤더는 colSpan으로 너비가 결정되므로 width 설정하지 않음 // 자식 헤더만 개별 width 설정 - ...(!('columns' in header.column.columnDef) && { width: header.getSize() }), + ...(!('columns' in header.column.columnDef) && { + width: header.getSize(), + minWidth: header.getSize(), + maxWidth: header.column.columnDef.maxSize, + }), }} > <div style={{ position: "relative" }}> @@ -272,7 +277,9 @@ export function InfiniteDataTable<TData>({ className={compactStyles.cell} style={{ ...getPinnedStyle(cell.column, false), // 🎯 바디 셀임을 명시 - width: cell.column.getSize(), // 🎯 width 별도 설정 + width: cell.column.getSize(), + minWidth: cell.column.getSize(), + maxWidth: cell.column.columnDef.maxSize, }} > {flexRender( |
