refactor(794-1b): M01 흐름도 패널 걷음 — 로직 상세 오른쪽 = 원문만 · [신규 로직] = 빈 시트 줄 · 흐름 서버 길 · DataFlow 화면 지움
요구 651 · 653 — 계산 공식 참고용 흐름도(① M01 로직 상세 오른쪽 접는 패널)를 걷음. ② M02 일위대가 다이어그램은 그대로.
지움(64 파일 · 18,666 줄 · 되돌릴 길 = git 이력)
- M01 6 파일 1,064 줄 — Store_Flow.py · UI_LogicLab_Flow.ts/.css · FlowRows.ts · FlowView.ts/.css
- M02_MasterTemplete/DataFlow 43 파일 12,799 줄 — 흐름도 화면 · 셈(Compute · Convert · Lookup · Model · Eval …)
- 시험 15 파일 4,803 줄 — test_m01_logic_flow{view,_engine,_project,_save} · test_m02_df_*(값 찾기 values_old 뺌) — 지운 흐름 코드만 지키던 것
남김(바닥) — DataFlow M02_DF_UnitPick(일위대가 셋) · M02_DF_Values(M02_Item_Prices) · M02_DF_Round · _standard 표 ·
M02_DF_Router = GET /api/m02/df/standards 하나 · M02_DF_Api = cfStandards 하나 · CfRound 꼴은 Round 안으로
고침
- M01 Router — /save = Store_Make.save(개정 확인 · 자체 파일 새 줄 GX · added 그대로) · /calc /text /logic/auto = 옛 셈만 · /logic/flow 둘 지움
- master_formula — 흐름 훅 지움 · 줄에 「흐름」 이 남으면 「흐름도 로직은 걷음」 까닭 오류(저장된 흐름 로직 0 — 마스터 · storage 다시 확인)
- Sangeun_Calc._defaults = 옛 자동값 · M02_Logic_Lib_Read /logic/flow 지움 · main.py 주석
- 화면 — LogicLab(startFlow · scheduleFlow · 예시값 잇기 지움 · [신규 로직] 줄 = 입력 · 중간 · 호표 빈 목록 + 끝수 null) ·
Side(원문 패널 하나) · Source(머리 칸 이름 m01lab-src__top/__title · DF_Panel.css 안 씀) · Detail · View(nowInputs 지움) · Sheet(흐름 갈래 지움) · 글
- 시험 — 지운 파일을 가리키던 목록 넷(spreadsheet_size · 208_1 · 588 · input_prefs) · 717_1 흐름 길 두 줄 · values_old 흐름 셈 부분 지움
- test_713_3_list 기대 — 로직 1,461 → 1,515 · 묶음 307 → 335 · 작업량 21 → 65 · 부르는 506 → 512(782-1 · 2 · 3 건설 기초 로직 54 더함 ·
옛 키만 세면 307 · 21 · 506 그대로)
- 새 지킴 test_794_1b_flow_gone — 흐름 길 404 · 표준 표 남음 · 신규 로직 자체 GX · 흐름 남은 줄 까닭 오류
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,145 +0,0 @@
|
||||
/* =============================================================================
|
||||
* M02_DF_FuncExtra.ts
|
||||
* 함수 판 보조 — 식 보기 그리기 · 칸 연결 표 손잡이 · 우클릭 「조건 만들기」 에 주는 것
|
||||
* 판 본체(M02_DF_FuncPanel)가 700줄 안에 들도록 뗌 · 상태는 모르고 손잡이(ExtraContext)만 받음
|
||||
* ========================================================================== */
|
||||
|
||||
import { currentLanguageIndex } from "@ui/ui_template_locale";
|
||||
import { showToast } from "@ui/ui_template_elements";
|
||||
import type { ConditionOptions } from "../../A00_Common/spreadsheet/spreadsheet_condition";
|
||||
import { renderCellTable } from "./M02_DF_CellTable";
|
||||
import type { DetailOptions } from "./M02_DF_Detail";
|
||||
import { cellValuesOf, formulaLines, renderFormulaView } from "./M02_DF_FormulaView";
|
||||
import { TEXT } from "./M02_DF_FuncText";
|
||||
import { valueChoices, type CfFunc, type CfLogic, type CfVariable } from "./M02_DF_Model";
|
||||
|
||||
export interface ExtraContext {
|
||||
lg: CfLogic;
|
||||
boxId: string;
|
||||
detail: DetailOptions;
|
||||
/** 선 규칙을 지나 들어온 값 */
|
||||
arrived: Record<string, unknown>;
|
||||
readOnly: boolean;
|
||||
place(port: string, cell: string): void;
|
||||
setOutput(cell: string): void;
|
||||
patch(make: (f: CfFunc) => CfFunc): void;
|
||||
used(f: CfFunc): Map<string, string>;
|
||||
}
|
||||
|
||||
const busy = (): string => TEXT.Busy[currentLanguageIndex === 1 ? 1 : 0];
|
||||
const boxOf = (x: ExtraContext) => x.lg.boxes.find((b) => b.id === x.boxId);
|
||||
|
||||
/** 식 보기 — 칸 식을 고치면 부른 쪽이 다시 부름 */
|
||||
export function drawFormula(host: HTMLElement, x: ExtraContext): void {
|
||||
const box = boxOf(x);
|
||||
if (!box?.func) return;
|
||||
renderFormulaView(host, formulaLines(box, cellValuesOf(box, x.arrived)));
|
||||
}
|
||||
|
||||
/** 칸 연결 표 — 표에서 칸 바꾸기 · 풀기(입력/변수/결과 모두 같은 규칙: 이미 쓰는 칸 막음 · 결과 칸 하나) */
|
||||
export function drawCells(host: HTMLElement, x: ExtraContext): void {
|
||||
const box = boxOf(x);
|
||||
if (!box?.func) return;
|
||||
renderCellTable(host, x.lg, box, x.readOnly, {
|
||||
move: (kind, key, cell) => {
|
||||
if (kind === "in") return x.place(key, cell);
|
||||
if (kind === "out") return x.setOutput(cell);
|
||||
if (box.func && x.used(box.func).has(cell)) return void showToast(busy(), "error");
|
||||
x.patch((f) => ({
|
||||
...f,
|
||||
variables: f.variables.map((v) => (v.name === key ? { ...v, cell } : v)),
|
||||
}));
|
||||
},
|
||||
release: (kind, key) =>
|
||||
x.patch((f) =>
|
||||
kind === "in"
|
||||
? { ...f, inputs: f.inputs.map((i) => (i.port === key ? { ...i, cell: "" } : i)) }
|
||||
: kind === "var"
|
||||
? { ...f, variables: f.variables.filter((v) => v.name !== key) }
|
||||
: { ...f, output: { ...f.output, cell: "" } },
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
/** 우클릭 「조건 만들기」 에 주는 것 — 칸에 올 값 목록(알약이면 valueChoices · 변수면 고르기 목록) · 이름 붙은 칸
|
||||
* get 으로 부를 때마다 지금 상태를 다시 읽음(나중에 놓은 알약 · 만든 변수도 목록에 뜸) */
|
||||
export function conditionsOf(get: () => ExtraContext): ConditionOptions {
|
||||
return {
|
||||
valuesFor: (a1) => {
|
||||
const x = get();
|
||||
const f = boxOf(x)?.func;
|
||||
const at = a1.toUpperCase();
|
||||
const v = f?.variables.find((y) => y.cell?.toUpperCase() === at);
|
||||
if (v) return v.type === "choice" && v.choices?.length ? [...v.choices] : null;
|
||||
const port = f?.inputs.find((i) => i.cell.toUpperCase() === at)?.port;
|
||||
const link = port
|
||||
? x.lg.links.find((l) => l.to.box === x.boxId && l.to.name === port)
|
||||
: undefined;
|
||||
return link ? valueChoices(x.lg, link.from, { values: x.detail.values }) : null;
|
||||
},
|
||||
pills: () => {
|
||||
const x = get();
|
||||
const f = boxOf(x)?.func;
|
||||
return [
|
||||
...(f?.inputs ?? []).filter((i) => i.cell).map((i) => ({ name: i.port, cell: i.cell })),
|
||||
...(f?.variables ?? []).filter((v) => v.cell).map((v) => ({ name: v.name, cell: v.cell! })),
|
||||
];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** 변수 만들기 폼 → 변수 · 틀리면 까닭(글 열쇠) — 같은 이름 · 이미 쓰는 칸 · 숫자 아님 · 범위 거꾸로 */
|
||||
export interface VarFormValues {
|
||||
name: string;
|
||||
type: CfVariable["type"];
|
||||
unit: string;
|
||||
min: string;
|
||||
max: string;
|
||||
choices: string;
|
||||
example: string;
|
||||
}
|
||||
|
||||
export function variableFrom(
|
||||
form: VarFormValues,
|
||||
f: CfFunc,
|
||||
cell: string,
|
||||
used: (f: CfFunc) => Map<string, string>,
|
||||
):
|
||||
| { v: CfVariable; cell: string }
|
||||
| { why: "NeedName" | "SameName" | "Busy" | "NotNumber" | "BadRange" } {
|
||||
const name = form.name.trim();
|
||||
if (!name) return { why: "NeedName" };
|
||||
const mine = f.variables.find((v) => v.name === name);
|
||||
if (!mine && [...f.inputs.map((i) => i.port), ...f.variables.map((v) => v.name)].includes(name))
|
||||
return { why: "SameName" };
|
||||
if (used({ ...f, variables: f.variables.filter((v) => v.name !== name) }).has(cell))
|
||||
return { why: "Busy" };
|
||||
const v: CfVariable = {
|
||||
name,
|
||||
type: form.type,
|
||||
unit: form.unit.trim() || "-",
|
||||
example: null,
|
||||
cell,
|
||||
};
|
||||
let example: number | string | null = form.example.trim() === "" ? null : form.example.trim();
|
||||
if (form.type === "number") {
|
||||
if (example !== null) {
|
||||
const n = Number(String(example).replace(/,/g, ""));
|
||||
if (!Number.isFinite(n)) return { why: "NotNumber" };
|
||||
example = n;
|
||||
}
|
||||
const lo = form.min.trim() === "" ? null : Number(form.min);
|
||||
const hi = form.max.trim() === "" ? null : Number(form.max);
|
||||
if (lo !== null && hi !== null) {
|
||||
if (!(lo < hi)) return { why: "BadRange" };
|
||||
v.range = [lo, hi];
|
||||
}
|
||||
} else if (form.type === "choice") {
|
||||
v.choices = form.choices
|
||||
.split(",")
|
||||
.map((x) => x.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
v.example = example;
|
||||
return { v, cell };
|
||||
}
|
||||
Reference in New Issue
Block a user