feat(m01-sl): 표 상자 열 차례를 서버 표 블록에 맞춤 — 값 칸 범위도 아래 · 위 두 칸 · 숫자 하나는 같은 수
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019ACSGaHdLgnkEkoA4LDtMU
This commit is contained in:
@@ -165,7 +165,11 @@ async function loadInfo(key: string): Promise<TblInfo> {
|
||||
/* ── 역할 · 알약 ── */
|
||||
const rolesOf = (node: SlNode): TblRoles => ({
|
||||
cond: node.pillsIn.map((p) => p.name),
|
||||
out: node.pillsOut.filter((p) => !p.outside).map((p) => p.name),
|
||||
out: [
|
||||
...new Set(
|
||||
node.pillsOut.filter((p) => !p.outside).map((p) => p.name.replace(/·(아래|위)$/, "")),
|
||||
),
|
||||
],
|
||||
});
|
||||
|
||||
/** 내 표 읽기 — 시트 블록에서 · 조건 칸은 입력 알약 이름 */
|
||||
|
||||
@@ -77,17 +77,20 @@ const at = (r: number, c: number): string => `${colName(c)}${r}`;
|
||||
const MARGIN = 20;
|
||||
const quote = (s: string): string => `"${s.replace(/"/g, '""')}"`;
|
||||
|
||||
/** 블록 열 차례 — 조건 칸 · 값 칸 차례 그대로 · 범위 칸은 둘로 */
|
||||
/** 범위 칸인가 — 조건 칸이 「범위」 이거나 값 칸에 범위(배열) 줄이 하나라도 있으면(서버 표 블록과 같은 규칙) */
|
||||
export const isSplit = (info: TblInfo, c: string): boolean =>
|
||||
info.conds[c] === "범위" || (c in info.vals && info.rows.some((r) => Array.isArray(r[c])));
|
||||
|
||||
/** 블록 열 차례 — 조건 칸 → 값 칸 · 범위 칸은 「이름·아래」 「이름·위」 둘로(서버 `_table` 과 같은 차례) */
|
||||
export function layoutOf(info: TblInfo): TblCol[] {
|
||||
const out: TblCol[] = [];
|
||||
for (const c of Object.keys(info.conds))
|
||||
if (info.conds[c] === "범위")
|
||||
for (const c of [...Object.keys(info.conds), ...Object.keys(info.vals)])
|
||||
if (isSplit(info, c))
|
||||
out.push(
|
||||
{ header: `${c}·아래`, of: c, part: "아래" },
|
||||
{ header: `${c}·위`, of: c, part: "위" },
|
||||
);
|
||||
else out.push({ header: c, of: c, part: "값" });
|
||||
for (const c of Object.keys(info.vals)) out.push({ header: c, of: c, part: "값" });
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -106,13 +109,8 @@ export function blockCells(info: TblInfo): Record<string, Cell> {
|
||||
info.rows.forEach((row, i) =>
|
||||
cols.forEach((col, c) => {
|
||||
const v = row[col.of];
|
||||
const x = Array.isArray(v)
|
||||
? col.part === "아래"
|
||||
? v[0]
|
||||
: v[1]
|
||||
: col.part === "값"
|
||||
? v
|
||||
: null;
|
||||
// 범위 칸에 숫자 하나면 아래 · 위에 같은 수
|
||||
const x = Array.isArray(v) ? (col.part === "아래" ? v[0] : v[1]) : v;
|
||||
const cell = cellOf(x);
|
||||
if (cell) cells[at(i + 2, c)] = cell;
|
||||
}),
|
||||
@@ -258,10 +256,11 @@ export function planLookup(info: TblInfo, roles: TblRoles): TblPlan {
|
||||
);
|
||||
const outCells: Record<string, string> = {};
|
||||
for (const c of roles.out.filter((o) => o in info.vals)) {
|
||||
outCells[c] = idxRow(
|
||||
c,
|
||||
`IF(${statusCell}=${quote("찾음")},INDEX(${rng(letter(c))},${idx}),NA())`,
|
||||
);
|
||||
for (const h of isSplit(info, c) ? [`${c}·아래`, `${c}·위`] : [c])
|
||||
outCells[h] = idxRow(
|
||||
h,
|
||||
`IF(${statusCell}=${quote("찾음")},INDEX(${rng(letter(h))},${idx}),NA())`,
|
||||
);
|
||||
}
|
||||
return {
|
||||
cols,
|
||||
@@ -286,7 +285,7 @@ export function pillsOf(
|
||||
const pillsOut = Object.entries(plan.outCells).map(([name, cell]): SlPill => ({
|
||||
name,
|
||||
cell,
|
||||
unit: info.vals[name] || undefined,
|
||||
unit: info.vals[name.replace(/·(아래|위)$/, "")] || undefined,
|
||||
}));
|
||||
if (outside) pillsOut.push({ name: "표", cell: "A1", outside: true });
|
||||
return { pillsIn, pillsOut };
|
||||
@@ -416,8 +415,10 @@ export function infoFromSheet(sheet: Sheet, prev: Pick<TblInfo, "conds" | "name"
|
||||
names.push(m ? { of: m[1], part: m[2] as "아래" | "위", c } : { of: h, part: "값", c });
|
||||
});
|
||||
for (const nm of names) {
|
||||
if (nm.part !== "값") conds[nm.of] = "범위";
|
||||
else if (prev.conds[nm.of]) conds[nm.of] = prev.conds[nm.of];
|
||||
if (nm.part !== "값") {
|
||||
if (prev.conds[nm.of]) conds[nm.of] = "범위";
|
||||
else vals[nm.of] = "";
|
||||
} else if (prev.conds[nm.of]) conds[nm.of] = prev.conds[nm.of];
|
||||
else vals[nm.of] = "";
|
||||
}
|
||||
const rows: Rec[] = [];
|
||||
|
||||
Reference in New Issue
Block a user