Files
Aislo/M01_MasterData/M01_MasterData_UI_LogicLab_Flow.ts
T
eomsangdonandClaude Opus 5.5 accf8ecc4f feat(M01): 단가산출 로직 단추 정리 · 로직 상세 두 칸
- 「식으로 새로 만들기」 단추 · 화면 길 지움(LogicLab_New 다섯 파일 — 쓰는 곳 없음)
- 「시험판 ▾」 메뉴 · 시험판 단추 둘(CostFlow · DataFlow 입구) · 가져오기 줄 지움
- 「본떠 만들기」 → 「로직 복사/생성」 · 단추 차례 = ← 로직 목록 · 로직 복사/생성 · 로직 지우기 · 고친 것 버리기 · 저장
- 로직 상세 두 칸(LogicLab_Flow) — 왼쪽 지금 내용 · 오른쪽 흐름도 자리 · 끌기 막대 · 너비 비율 기억(localStorage · 25~75%)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
2026-10-01 23:27:29 +09:00

96 lines
3.2 KiB
TypeScript

/* =============================================================================
* M01_MasterData_UI_LogicLab_Flow.ts
* 로직 상세 두 칸 — 왼쪽 지금 내용(보기) · 가운데 끌기 막대 · 오른쪽 흐름도 자리(패널은 LogicLab 이 붙임)
* 너비 비율은 브라우저에 기억(localStorage · 막히면 기본 반반). 정함 `docs/raw/M01_쉬운단가/흐름도_적용_정함.md` 5 · 10
* ========================================================================== */
import { currentLanguageIndex } from "@ui/ui_template_locale";
import { el } from "@ui/ui_template_elements";
import "./M01_MasterData_UI_LogicLab_Flow.css";
const TEXT = {
Resize: ["두 칸 너비 — 끌어서 조절", "Drag to resize the two columns"],
Waiting: ["흐름도 자리", "Flow diagram"],
} as const;
export const tf = (key: keyof typeof TEXT): string => TEXT[key][currentLanguageIndex === 1 ? 1 : 0];
const STORE = "m01.logic.flowRatio";
const MIN = 0.25;
const MAX = 0.75;
const clamp = (r: number): number => Math.min(MAX, Math.max(MIN, r));
function readRatio(): number {
try {
const r = Number(localStorage.getItem(STORE));
return r ? clamp(r) : 0.5;
} catch {
return 0.5;
}
}
export interface SplitHandle {
root: HTMLElement;
/** 오른쪽 칸 — 흐름도 패널을 붙이는 곳 */
right: HTMLElement;
}
/** 두 칸 만들기 — left 는 지금 화면 그대로 왼쪽에 둠 */
export function buildSplit(left: HTMLElement): SplitHandle {
const right = el("div", {
className: "m01lab-flow__right",
children: [el("p", { className: "m01lab-flow__wait", text: tf("Waiting") })],
});
const bar = el("div", {
className: "m01lab-flow__bar",
attrs: {
role: "separator",
"aria-orientation": "vertical",
"aria-label": tf("Resize"),
title: tf("Resize"),
tabindex: "0",
},
});
const leftCol = el("div", { className: "m01lab-flow__left", children: [left] });
const root = el("div", { className: "m01lab-flow", children: [leftCol, bar, right] });
let ratio = readRatio();
const apply = (): void => {
root.style.gridTemplateColumns = `minmax(0, ${ratio}fr) auto minmax(0, ${1 - ratio}fr)`;
};
const keep = (): void => {
try {
localStorage.setItem(STORE, String(ratio));
} catch {
/* 기억 못 해도 지금 화면은 그대로 */
}
};
bar.addEventListener("pointerdown", (ev) => {
if (ev.button !== 0) return;
ev.preventDefault();
const box = root.getBoundingClientRect();
const move = (m: PointerEvent): void => {
ratio = clamp((m.clientX - box.left) / box.width);
apply();
};
const up = (): void => {
window.removeEventListener("pointermove", move);
window.removeEventListener("pointerup", up);
keep();
};
window.addEventListener("pointermove", move);
window.addEventListener("pointerup", up);
});
// 키보드 — 왼쪽 · 오른쪽 화살표로 조금씩
bar.addEventListener("keydown", (ev) => {
const step = ev.key === "ArrowLeft" ? -0.05 : ev.key === "ArrowRight" ? 0.05 : 0;
if (!step) return;
ev.preventDefault();
ratio = clamp(ratio + step);
apply();
keep();
});
apply();
return { root, right };
}