diff --git a/frontend/components/common/EditableSpreadsheet.tsx b/frontend/components/common/EditableSpreadsheet.tsx index 17c8ce94..67e7f80b 100644 --- a/frontend/components/common/EditableSpreadsheet.tsx +++ b/frontend/components/common/EditableSpreadsheet.tsx @@ -822,7 +822,43 @@ export const EditableSpreadsheet: React.FC = ({ } }, [isDraggingFill, selection]); + // 열의 숫자 패턴 간격 계산 (예: 201, 202 → 간격 1) + const calculateColumnIncrement = (colIndex: number, startRow: number, endRow: number): number | null => { + if (startRow === endRow) return 1; // 단일 행이면 기본 증가 1 + + const colName = columns[colIndex]; + const increments: number[] = []; + + for (let row = startRow; row < endRow; row++) { + const currentValue = String(data[row]?.[colName] ?? ""); + const nextValue = String(data[row + 1]?.[colName] ?? ""); + + const currentPattern = extractNumberPattern(currentValue); + const nextPattern = extractNumberPattern(nextValue); + + if (currentPattern && nextPattern) { + // 접두사와 접미사가 같은지 확인 + if (currentPattern.prefix === nextPattern.prefix && currentPattern.suffix === nextPattern.suffix) { + increments.push(nextPattern.number - currentPattern.number); + } else { + return null; // 패턴이 다르면 복사 모드 + } + } else { + return null; // 숫자 패턴이 없으면 복사 모드 + } + } + + // 모든 간격이 같은지 확인 + if (increments.length > 0 && increments.every(inc => inc === increments[0])) { + return increments[0]; + } + + return null; + }; + // 자동 채우기 드래그 종료 (다중 셀 지원) + // - 숫자 패턴이 있으면: 패턴 간격을 인식하여 증가 (201, 202 → 203, 204) + // - 숫자 패턴이 없으면: 선택된 패턴 그대로 반복 (복사) const handleFillDragEnd = useCallback(() => { if (!isDraggingFill || !selection || fillPreviewEnd === null) { setIsDraggingFill(false); @@ -836,15 +872,16 @@ export const EditableSpreadsheet: React.FC = ({ if (endRow !== norm.endRow && norm.startRow >= 0) { const newData = [...data]; + + // 각 열별로 증가 패턴 계산 + const columnIncrements: Map = new Map(); + for (let col = norm.startCol; col <= norm.endCol; col++) { + columnIncrements.set(col, calculateColumnIncrement(col, norm.startRow, norm.endRow)); + } if (endRow > norm.endRow) { // 아래로 채우기 for (let targetRow = norm.endRow + 1; targetRow <= endRow; targetRow++) { - // 선택 범위 내 행 순환 - const sourceRowOffset = (targetRow - norm.startRow) % selectionHeight; - const sourceRow = norm.startRow + sourceRowOffset; - const stepMultiplier = Math.floor((targetRow - norm.startRow) / selectionHeight); - if (!newData[targetRow]) { newData[targetRow] = {}; columns.forEach((c) => { @@ -855,20 +892,32 @@ export const EditableSpreadsheet: React.FC = ({ // 선택된 모든 열에 대해 채우기 for (let col = norm.startCol; col <= norm.endCol; col++) { const colName = columns[col]; - const sourceValue = String(data[sourceRow]?.[colName] ?? ""); - const step = targetRow - sourceRow; - newData[targetRow] = { - ...newData[targetRow], - [colName]: generateNextValue(sourceValue, step), - }; + const increment = columnIncrements.get(col); + + if (increment !== null) { + // 숫자 패턴 증가 모드 + // 마지막 선택 행의 값을 기준으로 증가 + const lastValue = String(data[norm.endRow]?.[colName] ?? ""); + const step = (targetRow - norm.endRow) * increment; + newData[targetRow] = { + ...newData[targetRow], + [colName]: generateNextValue(lastValue, step), + }; + } else { + // 복사 모드 (패턴 반복) + const sourceRowOffset = (targetRow - norm.endRow - 1) % selectionHeight; + const sourceRow = norm.startRow + sourceRowOffset; + const sourceValue = String(data[sourceRow]?.[colName] ?? ""); + newData[targetRow] = { + ...newData[targetRow], + [colName]: sourceValue, + }; + } } } } else if (endRow < norm.startRow) { // 위로 채우기 for (let targetRow = norm.startRow - 1; targetRow >= endRow; targetRow--) { - const sourceRowOffset = (norm.startRow - targetRow - 1) % selectionHeight; - const sourceRow = norm.endRow - sourceRowOffset; - if (!newData[targetRow]) { newData[targetRow] = {}; columns.forEach((c) => { @@ -878,12 +927,26 @@ export const EditableSpreadsheet: React.FC = ({ for (let col = norm.startCol; col <= norm.endCol; col++) { const colName = columns[col]; - const sourceValue = String(data[sourceRow]?.[colName] ?? ""); - const step = targetRow - sourceRow; - newData[targetRow] = { - ...newData[targetRow], - [colName]: generateNextValue(sourceValue, step), - }; + const increment = columnIncrements.get(col); + + if (increment !== null) { + // 숫자 패턴 감소 모드 + const firstValue = String(data[norm.startRow]?.[colName] ?? ""); + const step = (targetRow - norm.startRow) * increment; + newData[targetRow] = { + ...newData[targetRow], + [colName]: generateNextValue(firstValue, step), + }; + } else { + // 복사 모드 (패턴 반복) + const sourceRowOffset = (norm.startRow - targetRow - 1) % selectionHeight; + const sourceRow = norm.endRow - sourceRowOffset; + const sourceValue = String(data[sourceRow]?.[colName] ?? ""); + newData[targetRow] = { + ...newData[targetRow], + [colName]: sourceValue, + }; + } } } }