diff --git a/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx b/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx index 410fd9a6..56e9a321 100644 --- a/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx +++ b/frontend/lib/registry/components/modal-repeater-table/RepeaterTable.tsx @@ -36,6 +36,71 @@ export function RepeaterTable({ // 동적 데이터 소스 Popover 열림 상태 const [openPopover, setOpenPopover] = useState(null); + + // 컬럼 너비 상태 관리 + const [columnWidths, setColumnWidths] = useState>(() => { + const widths: Record = {}; + columns.forEach((col) => { + widths[col.field] = col.width ? parseInt(col.width) : 120; + }); + return widths; + }); + + // 기본 너비 저장 (리셋용) + const defaultWidths = React.useMemo(() => { + const widths: Record = {}; + columns.forEach((col) => { + widths[col.field] = col.width ? parseInt(col.width) : 120; + }); + return widths; + }, [columns]); + + // 리사이즈 상태 + const [resizing, setResizing] = useState<{ field: string; startX: number; startWidth: number } | null>(null); + + // 리사이즈 핸들러 + const handleMouseDown = (e: React.MouseEvent, field: string) => { + e.preventDefault(); + setResizing({ + field, + startX: e.clientX, + startWidth: columnWidths[field] || 120, + }); + }; + + // 더블클릭으로 기본 너비로 리셋 + const handleDoubleClick = (field: string) => { + setColumnWidths((prev) => ({ + ...prev, + [field]: defaultWidths[field] || 120, + })); + }; + + useEffect(() => { + if (!resizing) return; + + const handleMouseMove = (e: MouseEvent) => { + if (!resizing) return; + const diff = e.clientX - resizing.startX; + const newWidth = Math.max(60, resizing.startWidth + diff); + setColumnWidths((prev) => ({ + ...prev, + [resizing.field]: newWidth, + })); + }; + + const handleMouseUp = () => { + setResizing(null); + }; + + document.addEventListener("mousemove", handleMouseMove); + document.addEventListener("mouseup", handleMouseUp); + + return () => { + document.removeEventListener("mousemove", handleMouseMove); + document.removeEventListener("mouseup", handleMouseUp); + }; + }, [resizing, columns, data]); // 데이터 변경 감지 (필요시 활성화) // useEffect(() => { @@ -79,7 +144,7 @@ export function RepeaterTable({ onChange={(e) => handleCellEdit(rowIndex, column.field, parseFloat(e.target.value) || 0) } - className="h-7 text-xs" + className="h-8 text-xs border-gray-200 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 rounded-none" /> ); @@ -107,7 +172,7 @@ export function RepeaterTable({ type="date" value={formatDateValue(value)} onChange={(e) => handleCellEdit(rowIndex, column.field, e.target.value)} - className="h-7 text-xs" + className="h-8 text-xs border-gray-200 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 rounded-none" /> ); @@ -119,7 +184,7 @@ export function RepeaterTable({ handleCellEdit(rowIndex, column.field, newValue) } > - + @@ -138,19 +203,19 @@ export function RepeaterTable({ type="text" value={value || ""} onChange={(e) => handleCellEdit(rowIndex, column.field, e.target.value)} - className="h-7 text-xs" + className="h-8 text-xs border-gray-200 focus:border-blue-500 focus:ring-1 focus:ring-blue-500 rounded-none" /> ); } }; return ( -
-
- - +
+
+
+ - {columns.map((col) => { @@ -163,101 +228,113 @@ export function RepeaterTable({ return ( + "inline-flex items-center gap-1 hover:text-blue-600 transition-colors", + "focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 rounded px-1 -mx-1" + )} + > + {col.label} + + + + +
+ 데이터 소스 선택 +
+ {col.dynamicDataSource!.options.map((option) => ( + + ))} +
+ + ) : ( + <> + {col.label} + {col.required && *} + + )} + + {/* 리사이즈 핸들 */} +
handleMouseDown(e, col.field)} + title="드래그하여 너비 조정" + /> +
+ ); })} -
- + {data.length === 0 ? ( ) : ( data.map((row, rowIndex) => ( - - + {columns.map((col) => ( - ))} -
+ # handleDoubleClick(col.field)} + title="더블클릭하여 기본 너비로 되돌리기" > - {hasDynamicSource ? ( - setOpenPopover(open ? col.field : null)} - > - - - - -
- 데이터 소스 선택 -
- {col.dynamicDataSource!.options.map((option) => ( - - ))} -
-
- ) : ( - <> - {col.label} - {col.required && *} - - )} -
+ 삭제
추가된 항목이 없습니다
+
{rowIndex + 1} + {renderCell(row, col, rowIndex)} +