Merge remote-tracking branch 'origin/dev' into main_desktop_1

# Conflicts:
#	M01_MasterData/M01_MasterData_UI_LogicLab.ts
#	M01_MasterData/M01_MasterData_UI_LogicLab_Style.css
#	M01_MasterData/M01_MasterData_UI_Logic_Api.ts
#	resources/master_data/ref/_검증_별칭_복사.md
This commit is contained in:
2026-09-21 21:37:11 +09:00
18 changed files with 1300 additions and 99 deletions
+47 -5
View File
@@ -33,9 +33,11 @@ import { buildCalc } from "./M01_MasterData_UI_Logic_Calc";
import { buildList, logicId, type ListItem, type ListMark } from "./M01_MasterData_UI_Logic_List";
import type { SideHandle } from "./M01_MasterData_UI_Side";
import { tx } from "./M01_MasterData_UI_Logic_Text";
import { buildLabDetail } from "./M01_MasterData_UI_LogicLab_Detail";
import { buildLabDetail, isOwnLogic } from "./M01_MasterData_UI_LogicLab_Detail";
import { openCopyScreen } from "./M01_MasterData_UI_LogicLab_Copy";
import { tl } from "./M01_MasterData_UI_LogicLab_Text";
import { buildNewLogic } from "./M01_MasterData_UI_LogicLab_New";
import { tn } from "./M01_MasterData_UI_LogicLab_New_Text";
import "./M01_MasterData_UI_Logic_Style.css";
/** 저장 안 한 로직 하나 — origKey null = 새 로직 · row null = 지움 */
@@ -56,10 +58,12 @@ interface Opened extends Draft {
reasons: string[];
lines: CalcLine[] | null;
text: TextAnswer | null;
ownHost: HTMLElement;
values: Record<string, string>;
}
const cacheKey = "m01_logic_drafts_lab";
const ownHost = (): HTMLElement => el("div", { className: "m01lab__own" });
const clone = <T>(v: T): T => JSON.parse(JSON.stringify(v)) as T;
function loadDrafts(): Record<string, Draft> {
@@ -82,10 +86,13 @@ export async function mountM01LogicLab(
): Promise<LogicHandle> {
let drafts = loadDrafts();
let opened: Opened | null = null;
let creating = false; // 「식으로 새로 만들기」 화면이 열려 있음
let calcInputs = "";
let recalc: number | undefined;
const editor = el("div", { className: "m01-logic__editor" });
const errors = el("div", { className: "m01-logic__reasons", attrs: { hidden: "" } });
const calc = el("div", { className: "m01lab__calc" });
const newHost = el("div", { className: "m01lab__new", attrs: { hidden: "" } });
const filterHost = el("div");
const waiting = el("p", { className: "m01-logic__muted", text: tx("Loading") });
let loaded = false; // 첫 목록이 오기 전에는 「0 줄」 빈 표를 보이지 않음
@@ -132,6 +139,11 @@ export async function mountM01LogicLab(
persist();
const inputs = JSON.stringify(opened.row. ?? []);
if (inputs !== calcInputs) drawCalc();
if (isOwnLogic(opened.row)) {
// 자체 로직 — 고친 뒤 잠시 쉬면 다시 계산해 호표 소계와 텍스트 수식을 새로 그림
window.clearTimeout(recalc);
recalc = window.setTimeout(() => calc.querySelector("button")?.click(), 700);
}
};
const pick = (o: Opened, row: LogicRow | null): Draft => ({
@@ -167,10 +179,12 @@ export async function mountM01LogicLab(
const drawEditor = (): void => {
// 고른 로직이 없으면 목록 · 있으면 호표 표 + 시험 계산
const listing = !opened;
const listing = !opened && !creating;
list.root.hidden = !listing || !loaded;
waiting.hidden = loaded;
editor.hidden = back.hidden = listing;
editor.hidden = !opened;
back.hidden = listing;
newHost.hidden = !creating;
if (!opened) return;
if (!opened.row) {
editor.replaceChildren(
@@ -187,6 +201,7 @@ export async function mountM01LogicLab(
lines: current.lines,
text: current.text,
values: current.values,
ownHost: current.ownHost,
onChange: touch,
calcHost: calc,
});
@@ -194,6 +209,7 @@ export async function mountM01LogicLab(
const show = (next: Opened | null): void => {
opened = next;
creating = false;
errors.hidden = true;
drawEditor();
drawCalc();
@@ -210,6 +226,7 @@ export async function mountM01LogicLab(
reasons: [],
lines: null,
text: null,
ownHost: ownHost(),
values: {},
});
return;
@@ -228,6 +245,7 @@ export async function mountM01LogicLab(
reasons: one.reasons,
lines: null,
text: null,
ownHost: ownHost(),
values: {},
});
} catch (error) {
@@ -253,7 +271,7 @@ export async function mountM01LogicLab(
restore: true,
onPick: (sub, detail) => {
list.setPick(sub, detail);
if (opened) show(null);
if (opened || creating) show(null);
},
}),
);
@@ -337,6 +355,29 @@ export async function mountM01LogicLab(
}
};
/** 식으로 새로 만들기 — 저장하면 목록을 다시 받아 그 로직의 상세 화면을 엶 */
const startNew = (): void => {
opened = null;
creating = true;
newHost.replaceChildren(
buildNewLogic({
onSaved: (key) => {
void (async () => {
try {
await reload();
await openKeyOn(key);
} catch (error) {
showToast(error instanceof Error ? error.message : tx("Load_Failed"), "error");
}
})();
},
onClose: () => show(null),
}),
);
drawEditor();
drawCalc();
};
const saveButton = createButton({ label: tx("Bar_Save"), onClick: () => void onSave() });
const discardButton = createButton({
label: tx("Bar_Discard"),
@@ -349,6 +390,7 @@ export async function mountM01LogicLab(
back,
el("h2", { text: tx("Title") }),
pending,
createButton({ label: tn("Open"), variant: "filled", onClick: startNew }),
createButton({ label: tx("Bar_Delete"), variant: "danger", onClick: () => void onDelete() }),
discardButton,
saveButton,
@@ -374,7 +416,7 @@ export async function mountM01LogicLab(
children: [
el("main", {
className: "m01-logic__main",
children: [bar, errors, waiting, list.root, editor],
children: [bar, errors, waiting, list.root, editor, newHost],
}),
],
}),