/* ============================================================================= * spreadsheet_fill.ts (주인 B) * 채우기 핸들 — 원본 범위를 대상 범위로 늘림: 수 하나 = 복사 · 수 둘 이상 = 등차 연속 · * 글 끝 번호(`구-01` → `구-02`) · 식 = 상대 참조 옮김(A `moveFormula`) · 서식 같이. * 결과는 `{ 종류: "칸" }` 명령에 넣을 칸 뭉치. * ========================================================================== */ import type { Cell, CellRange, FillDirection, Workbook } from "./spreadsheet_types"; import { moveFormula } from "./spreadsheet_refshift"; import { toA1 } from "./spreadsheet_address"; function detectDirection(source: CellRange, target: CellRange): FillDirection { if (target.r1 > source.r1 && target.c0 === source.c0 && target.c1 === source.c1) return "down"; if (target.r0 < source.r0 && target.c0 === source.c0 && target.c1 === source.c1) return "up"; if (target.c1 > source.c1 && target.r0 === source.r0 && target.r1 === source.r1) return "right"; return "left"; } /** 글 끝 숫자를 옮김(`구-01`,step 1 → `구-02`) · 끝 숫자가 없으면 그대로 */ function incrementTrailingNumber(text: string, step: number): string { const m = /^(.*?)(\d+)$/.exec(text); if (!m) return text; const [, prefix, digits] = m; const next = BigInt(digits) + BigInt(step); const clamped = next < 0n ? 0n : next; return prefix + clamped.toString().padStart(digits.length, "0"); } /** `target` 은 `source` 를 한 방향으로 늘린 범위(방향은 둘을 견줘 앎) · A1 → 칸(null = 비움) */ export function fillCells( book: Workbook, sheet: string, source: CellRange, target: CellRange, ): Record { const sheetObj = book.시트.find((s) => s.id === sheet); const out: Record = {}; if (!sheetObj) return out; const direction = detectDirection(source, target); const vertical = direction === "down" || direction === "up"; const lineCount = vertical ? source.c1 - source.c0 + 1 : source.r1 - source.r0 + 1; for (let li = 0; li < lineCount; li++) { const fixed = vertical ? source.c0 + li : source.r0 + li; const sourceStart = vertical ? source.r0 : source.c0; const sourceEnd = vertical ? source.r1 : source.c1; const targetStart = vertical ? target.r0 : target.c0; const targetEnd = vertical ? target.r1 : target.c1; const lineLen = sourceEnd - sourceStart + 1; const key = (pos: number): string => (vertical ? toA1(pos, fixed) : toA1(fixed, pos)); const sourceCells: (Cell | undefined)[] = []; for (let p = sourceStart; p <= sourceEnd; p++) sourceCells.push(sheetObj.칸[key(p)]); const allNumeric = sourceCells.every( (c) => c !== undefined && c.식 === undefined && typeof c.값 === "number", ); const nums = allNumeric ? sourceCells.map((c) => c!.값 as number) : []; const step = nums.length > 1 ? (nums[nums.length - 1] - nums[0]) / (nums.length - 1) : 0; for (let p = targetStart; p <= targetEnd; p++) { if (p >= sourceStart && p <= sourceEnd) continue; const rel = p - sourceStart; const slot = ((rel % lineLen) + lineLen) % lineLen; const period = (rel - slot) / lineLen; const template = sourceCells[slot]; const outKey = key(p); if (template === undefined) { out[outKey] = null; continue; } const cell: Cell = {}; if (template.서식 !== undefined) cell.서식 = template.서식; if (allNumeric) { cell.값 = nums[0] + step * rel; } else if (template.식 !== undefined) { const dr = vertical ? period * lineLen : 0; const dc = vertical ? 0 : period * lineLen; cell.식 = moveFormula(template.식, dr, dc); } else if (typeof template.값 === "string" && lineLen === 1) { cell.값 = incrementTrailingNumber(template.값, rel); } else if (template.값 !== undefined) { cell.값 = template.값; } out[outKey] = cell; } } return out; }