fix(M01): 47-3 로직 상세 오른쪽 = 공용 접는 패널 둘(흐름도 · 원문) — 위아래 접기 카드 걷어냄
- 흐름도 · 원문 = 따로 패널 · 접힌 패널 = 세로 글 띠 · 하나만 열림 · 모두 접힘 됨 · 폭 공유(경계 끌기 · 화살표 키) - 열린 패널 · 폭 = 취향 키 m01-logic-side · 흐름도 패널 자체 접기 단추 · 끌기 경계는 숨김 - 옛 두 칸 비율 막대(buildSplit) 제거 · 부품 칸 숨김([hidden]) 규칙 덧붙임 - 화면 시나리오 ui_checks/47-3_로직상세_오른쪽패널.js Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NPJGtfXo6GMbcpiboQC4uK
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
/* 47-3 M01 단가산출 로직 상세 오른쪽 패널 — `orca eval --expression "$(cat 이 파일)"` 한 번에 돎 · JSON 돌려줌.
|
||||
*
|
||||
* 시작 화면: M01 마스터 데이터(#/m01-master-data · 시스템 관리자 로그인) · 아무 화면이나.
|
||||
* 로직 GC000277(건설품셈 토목 1-1-2 유도선 설치 및 해체)을 엶 · 고치지 않음 · 끝에 열린 패널 · 폭을 처음 값으로 되돌림.
|
||||
* 기대 수치:
|
||||
* panels = ["흐름도","원문"] · 패널 몸 = 공용 패널(.ui-side-panels) · 왼쪽 칸 오른쪽에 붙음(sideRight)
|
||||
* flow = {open:["flow"], strips:["원문"]} · 흐름도 판 보임 · 흐름도 패널 자체 접기 단추 · 끌기 경계 안 보임(inner 0)
|
||||
* source = {open:["source"], strips:["흐름도"]} · 폭 = flow 폭(공유) · srcHead 에 「1-1-2 유도선」 · srcTable true
|
||||
* none = {open:[], strips:["흐름도","원문"]} · 띠 너비 < 40
|
||||
* back = {open:["flow"]}(흐름도 띠로 다시 폄)
|
||||
* drag = 왼쪽 경계를 100px 왼쪽으로 끌면 폭 +100 · 원문으로 바꿔도 같은 폭 · key = 화살표 ← 한 번 +20
|
||||
* noLink = 「원문 연결 없음」 + 「출처 · …」(서버가 빈 목록을 줄 때 · fetch 를 잠시 바꿔 봄)
|
||||
* restored = 처음 취향 값과 같음
|
||||
*/
|
||||
(async () => {
|
||||
const W = (ms) => new Promise((r) => setTimeout(r, ms));
|
||||
const $ = (s, root = document) => root.querySelector(s);
|
||||
const $$ = (s, root = document) => [...root.querySelectorAll(s)];
|
||||
const until = async (f, ms = 15000) => {
|
||||
for (let t = 0; t < ms; t += 200) {
|
||||
const v = f();
|
||||
if (v) return v;
|
||||
await W(200);
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const PREF = "aislo:pref:m01-logic-side";
|
||||
const first = localStorage.getItem(PREF);
|
||||
const out = {};
|
||||
const openLogic = async (key) => {
|
||||
sessionStorage.setItem("m01.logic.pending", key);
|
||||
const tab = $$(".ui-collapsible__title").find((h) => h.innerText.trim() === "단가산출 로직");
|
||||
if (!tab) return false;
|
||||
if (!tab.closest(".ui-collapsible").classList.contains("is-collapsed")) tab.click();
|
||||
tab.click();
|
||||
return until(() => {
|
||||
const t = $(".m02-dfp__title")?.innerText ?? "";
|
||||
return !$(".m01lab-side")?.hidden && $(".m01lab-side .m02-dfp") && t;
|
||||
});
|
||||
};
|
||||
if (!(await openLogic("GC000277"))) return JSON.stringify({ error: "로직 못 엶" });
|
||||
await W(800);
|
||||
const shown = (n) => !!n && n.getBoundingClientRect().height > 0;
|
||||
const panel = (id) => $(`.m01lab-side .ui-side-panels__panel[data-panel="${id}"]`);
|
||||
const strip = (title) =>
|
||||
$$(".m01lab-side .ui-side-panels__strip").find((s) => s.innerText.replace(/\s/g, "") === title);
|
||||
const wOf = (n) => Math.round(n.getBoundingClientRect().width);
|
||||
const state = () => ({
|
||||
open: ["flow", "source"].filter((id) => shown(panel(id))),
|
||||
strips: $$(".m01lab-side .ui-side-panels__strip")
|
||||
.filter(shown)
|
||||
.map((s) => s.innerText.replace(/\s/g, "")),
|
||||
width: ["flow", "source"].filter((id) => shown(panel(id))).map((id) => wOf(panel(id)))[0] ?? 0,
|
||||
});
|
||||
const pref = () => JSON.parse(localStorage.getItem(PREF) ?? "{}");
|
||||
out.panels = $$(".m01lab-side .ui-side-panels__title").map((t) => t.innerText.trim());
|
||||
const editor = $(".m01lab-side > .m01-logic__editor").getBoundingClientRect();
|
||||
const side = $(".m01lab-side .ui-side-panels").getBoundingClientRect();
|
||||
out.sideRight = Math.round(side.left - editor.right);
|
||||
// 처음 = 흐름도 열림으로 맞춤
|
||||
if (!shown(panel("flow"))) {
|
||||
strip("흐름도").click();
|
||||
await W(400);
|
||||
}
|
||||
out.flow = state();
|
||||
out.flowDiagram = shown($(".m02-dfp__open", panel("flow")));
|
||||
out.inner = $$(".m01lab-side .m02-dfp__toggle, .m01lab-side .m02-dfp__grip").filter(shown).length;
|
||||
// 끌기 — 왼쪽 경계를 100px 왼쪽으로
|
||||
const grip = $(".ui-side-panels__grip", panel("flow"));
|
||||
const g = grip.getBoundingClientRect();
|
||||
const x0 = g.left + g.width / 2;
|
||||
const y0 = g.top + 50;
|
||||
const w0 = state().width;
|
||||
grip.dispatchEvent(
|
||||
new PointerEvent("pointerdown", { bubbles: true, button: 0, clientX: x0, clientY: y0 }),
|
||||
);
|
||||
window.dispatchEvent(new PointerEvent("pointermove", { clientX: x0 - 50, clientY: y0 }));
|
||||
window.dispatchEvent(new PointerEvent("pointerup", { clientX: x0 - 100, clientY: y0 }));
|
||||
await W(200);
|
||||
out.drag = { before: w0, after: state().width, pref: pref().width };
|
||||
// 원문 — 폭 공유
|
||||
strip("원문").click();
|
||||
await until(() => $(".m01lab-src__head"));
|
||||
out.source = state();
|
||||
out.srcHead = $(".m01lab-src__head")?.innerText;
|
||||
out.srcTable = $$(".m01lab-src__table td").some((td) => td.innerText === "특별인부");
|
||||
// 키보드 — ← 한 번 = +20 · → 한 번 = 되돌림
|
||||
const sg = $(".ui-side-panels__grip", panel("source"));
|
||||
sg.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowLeft", bubbles: true }));
|
||||
const k1 = state().width;
|
||||
sg.dispatchEvent(new KeyboardEvent("keydown", { key: "ArrowRight", bubbles: true }));
|
||||
out.key = { left: k1 - out.source.width, back: state().width - out.source.width };
|
||||
// 모두 접기 — 원문 머리 ▸
|
||||
$(".ui-side-panels__fold", panel("source")).click();
|
||||
await W(300);
|
||||
out.none = { ...state(), stripW: wOf(strip("흐름도")), pref: pref().open };
|
||||
strip("흐름도").click();
|
||||
await W(600);
|
||||
out.back = { ...state(), pref: pref().open };
|
||||
// 원문 연결 없음 — 서버가 빈 목록을 줄 때
|
||||
strip("원문").click();
|
||||
const real = window.fetch;
|
||||
window.fetch = (u, o) =>
|
||||
String(u).includes("/m01/logic/source")
|
||||
? Promise.resolve(new Response(JSON.stringify({ found: [] }), { status: 200 }))
|
||||
: real(u, o);
|
||||
try {
|
||||
await openLogic("GC000338");
|
||||
await until(() => $(".m01lab-src__none"));
|
||||
out.noLink = [
|
||||
$(".m01lab-src__none")?.innerText,
|
||||
$(".m01lab-src__body .m01-logic__muted")?.innerText,
|
||||
];
|
||||
} finally {
|
||||
window.fetch = real;
|
||||
}
|
||||
// 되돌림 — 폭(끈 만큼 ←→ 화살표로) · 열린 패널
|
||||
const was = JSON.parse(first ?? "{}");
|
||||
const want = was.open === undefined ? "flow" : was.open;
|
||||
const now = () => ["flow", "source"].find((id) => shown(panel(id))) ?? null;
|
||||
if (now() !== want) {
|
||||
if (want === null) $(".ui-side-panels__fold", panel(now())).click();
|
||||
else strip(want === "flow" ? "흐름도" : "원문").click();
|
||||
await W(300);
|
||||
}
|
||||
const target = was.width ?? w0;
|
||||
const anyGrip = $$(".m01lab-side .ui-side-panels__grip")[0];
|
||||
for (let i = 0; i < 40 && pref().width !== target; i++) {
|
||||
const d = target - pref().width;
|
||||
anyGrip.dispatchEvent(
|
||||
new KeyboardEvent("keydown", {
|
||||
key: d > 0 ? "ArrowLeft" : "ArrowRight",
|
||||
shiftKey: Math.abs(d) >= 80,
|
||||
bubbles: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
out.restored = first === null ? pref() : localStorage.getItem(PREF) === first;
|
||||
out.first = first;
|
||||
return JSON.stringify(out);
|
||||
})();
|
||||
Reference in New Issue
Block a user