fix: 컬럼 초기 너비 이하로 줄어들지 않는 문제 해결

 해결 방법:
- 백분율 defaultWidth 제거, 초기값은 undefined로 설정
- ref callback에서 첫 렌더링 시 실제 offsetWidth 측정
- 측정한 실제 너비를 columnWidths state에 저장
- 이후 드래그로 80px까지 줄일 수 있음

 적용 파일:
- TableListComponent.tsx (실제 화면)
- InteractiveDataTable.tsx (디자인 모드)

 기술적 개선:
- table-layout: fixed + 동적 초기 너비 측정
- DOM 직접 조작으로 부드러운 리사이즈
- 최소 80px 보장 (Math.max)
This commit is contained in:
kjs 2025-11-03 12:13:56 +09:00
parent 3ada095e43
commit 511884f323
2 changed files with 45 additions and 10 deletions

View File

@ -1921,7 +1921,7 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
{visibleColumns.length > 0 ? (
<>
<div className="overflow-hidden rounded-lg border border-gray-200/60 bg-white shadow-sm">
<Table>
<Table style={{ tableLayout: 'fixed' }}>
<TableHeader>
<TableRow>
{/* 체크박스 컬럼 (삭제 기능이 활성화된 경우) */}
@ -1935,15 +1935,25 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
)}
{visibleColumns.map((column: DataTableColumn, columnIndex) => {
const columnWidth = columnWidths[column.id];
const defaultWidth = `${((column.gridColumns || 2) / totalGridColumns) * 100}%`;
return (
<TableHead
key={column.id}
ref={(el) => {
// 첫 렌더링 시 실제 너비를 측정해서 상태에 저장
if (el && !columnWidth) {
const measuredWidth = el.offsetWidth;
if (measuredWidth > 0) {
setColumnWidths(prev => ({
...prev,
[column.id]: measuredWidth
}));
}
}
}}
className="relative bg-gradient-to-r from-gray-50 to-slate-50 px-4 font-semibold text-gray-700 select-none"
style={{
width: columnWidth ? `${columnWidth}px` : defaultWidth,
minWidth: '80px',
width: columnWidth ? `${columnWidth}px` : undefined,
userSelect: 'none'
}}
>
@ -1956,10 +1966,13 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
onClick={(e) => e.stopPropagation()}
onMouseDown={(e) => {
e.preventDefault();
e.stopPropagation(); // 상위 컴포넌트 드래그 이벤트 방지
e.stopPropagation();
const thElement = e.currentTarget.parentElement as HTMLTableCellElement;
if (!thElement) return;
const startX = e.clientX;
const startWidth = columnWidth || (e.currentTarget.parentElement?.offsetWidth || 100);
const startWidth = columnWidth || thElement.offsetWidth;
// 드래그 중 텍스트 선택 방지
document.body.style.userSelect = 'none';
@ -1967,12 +1980,23 @@ export const InteractiveDataTable: React.FC<InteractiveDataTableProps> = ({
const handleMouseMove = (moveEvent: MouseEvent) => {
moveEvent.preventDefault();
const diff = moveEvent.clientX - startX;
const newWidth = Math.max(80, startWidth + diff);
setColumnWidths(prev => ({ ...prev, [column.id]: newWidth }));
// 직접 DOM 스타일 변경 (리렌더링 없음)
if (thElement) {
thElement.style.width = `${newWidth}px`;
}
};
const handleMouseUp = () => {
// 최종 너비를 state에 저장
if (thElement) {
const finalWidth = Math.max(80, thElement.offsetWidth);
setColumnWidths(prev => ({ ...prev, [column.id]: finalWidth }));
}
// 텍스트 선택 복원
document.body.style.userSelect = '';
document.body.style.cursor = '';

View File

@ -1023,19 +1023,30 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
<tr className="h-10 border-b border-border sm:h-12">
{visibleColumns.map((column, columnIndex) => {
const columnWidth = columnWidths[column.columnName];
const defaultWidth = `${100 / visibleColumns.length}%`;
return (
<th
key={column.columnName}
ref={(el) => (columnRefs.current[column.columnName] = el)}
ref={(el) => {
columnRefs.current[column.columnName] = el;
// 첫 렌더링 시 실제 너비를 측정해서 상태에 저장
if (el && !columnWidth) {
const measuredWidth = el.offsetWidth;
if (measuredWidth > 0) {
setColumnWidths(prev => ({
...prev,
[column.columnName]: measuredWidth
}));
}
}
}}
className={cn(
"relative h-10 px-2 py-2 text-xs font-semibold text-foreground overflow-hidden text-ellipsis bg-background select-none sm:h-12 sm:px-6 sm:py-3 sm:text-sm sm:whitespace-nowrap",
column.sortable && "cursor-pointer"
)}
style={{
textAlign: column.align || "left",
width: columnWidth ? `${columnWidth}px` : defaultWidth,
width: columnWidth ? `${columnWidth}px` : undefined,
userSelect: 'none'
}}
onClick={() => column.sortable && handleSort(column.columnName)}