1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
|
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
import { Header } from "@tanstack/react-table"
interface DataTableResizerProps<TData, TValue>
extends React.HTMLAttributes<HTMLDivElement> {
header: Header<TData, TValue>
onResizeStart?: () => void
onResizeEnd?: () => void
}
export function DataTableResizer<TData, TValue>({
header,
onResizeStart,
onResizeEnd,
className,
...props
}: DataTableResizerProps<TData, TValue>) {
const contentRef = React.useRef<HTMLDivElement>(null)
// 더블클릭 시 너비 자동 조정 함수
const handleDoubleClick = React.useCallback((e: React.MouseEvent) => {
e.stopPropagation(); // 이벤트 버블링 중지
// 테이블 인스턴스 가져오기
const table = header.getContext().table
// 0. 몇 가지 기본 설정
const defaultMinWidth = 80 // 기본 최소 너비
const extraPadding = 24 // 여유 공간
// 헤더 타이틀 얻기 시도
const headerElement = contentRef.current?.closest('th')
const headerText = headerElement?.textContent || ""
// 1. 컬럼 ID 가져오기
const columnId = header.column.id
// 2. 테이블 바디에서 해당 ID를 가진 모든 셀 선택
const allCells = document.querySelectorAll(`tbody td[data-column-id="${columnId}"]`)
// 3. 최대 컨텐츠 너비 측정을 위한 임시 요소 생성
const measureElement = document.createElement('div')
measureElement.style.position = 'absolute'
measureElement.style.visibility = 'hidden'
measureElement.style.whiteSpace = 'nowrap' // 내용이 줄바꿈되지 않도록
measureElement.style.font = window.getComputedStyle(headerElement || document.body).font // 동일한 폰트 사용
document.body.appendChild(measureElement)
// 4. 헤더 너비 측정
measureElement.textContent = headerText
let maxWidth = measureElement.getBoundingClientRect().width
// 5. 모든 셀의 내용 너비 측정하고 최대값 찾기
Array.from(allCells).forEach(cell => {
const cellText = cell.textContent || ""
measureElement.textContent = cellText
const cellWidth = measureElement.getBoundingClientRect().width
maxWidth = Math.max(maxWidth, cellWidth)
})
// 6. 측정용 요소 제거
document.body.removeChild(measureElement)
// 7. 계산된 너비에 여유 공간 추가
let finalWidth = maxWidth + extraPadding
// 8. 최소 너비 적용
const minWidth = header.column.columnDef.minSize || defaultMinWidth
finalWidth = Math.max(finalWidth, minWidth)
// 9. 컬럼 사이즈 업데이트
const columnSizingInfo = table.getState().columnSizing
const updatedSizing = {
...columnSizingInfo,
[columnId]: finalWidth
}
// 사이즈 업데이트
table.setColumnSizing(updatedSizing)
// 콘솔 로그 추가 (디버깅용)
console.log(`컬럼 [${columnId}] 너비 자동 조정: ${finalWidth}px`);
}, [header])
// 마우스 다운 핸들러 (리사이징 시작)
const handleMouseDown = React.useCallback((e: React.MouseEvent | React.TouchEvent) => {
// 리사이즈 시작을 알림
if (onResizeStart) onResizeStart()
// 기존 리사이즈 핸들러 호출
if (header.getResizeHandler()) {
header.getResizeHandler()(e as any)
}
}, [header, onResizeStart])
return (
<>
{/* 헤더 콘텐츠 참조를 위한 요소 */}
<div ref={contentRef} className="absolute opacity-0 pointer-events-none" />
{/* 리사이저 */}
<div
{...props}
onMouseDown={handleMouseDown}
onTouchStart={handleMouseDown}
onDoubleClick={handleDoubleClick}
className={cn(
"absolute right-0 top-0 h-full w-1 cursor-col-resize bg-transparent hover:bg-gray-300 active:bg-gray-400",
header.column.getIsResizing() ? "bg-gray-400" : "",
className
)}
title="더블 클릭하여 내용에 맞게 크기 조정" // 힌트 추가
/>
</>
)
}
|