diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts index 3da28021..f5bb0458 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Main.ts @@ -93,7 +93,12 @@ export function canEdit(layer: Layer, isAdmin: boolean): boolean { return layer === "project" || layer === "personal" || (layer === "system" && isAdmin); } -export function createMain(isAdmin: boolean, onSaved?: () => void): MainHandle { +/** `onOpened` — 무엇을 열든(좌측 · 카드 [도면 수정] · 다시 읽기) 연 바로 뒤 부름(좌측 고름 표시) */ +export function createMain( + isAdmin: boolean, + onSaved?: () => void, + onOpened?: () => void, +): MainHandle { const title = el("h2", { className: "m02-main__title", text: L("M02_PickTemplate") }); const reload = createButton({ label: L("M02_Reload"), variant: "ghost" }); const save = createButton({ label: L("M02_Save"), variant: "filled" }); @@ -363,6 +368,7 @@ export function createMain(isAdmin: boolean, onSaved?: () => void): MainHandle { drop(); showNotice([]); sel = next; + onOpened?.(); bar(); if (!next) { title.textContent = L("M02_PickTemplate"); diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts index 89350db2..8d20be4c 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Page.ts @@ -17,7 +17,11 @@ import "./M02_MasterTemplete_UI_Style.css"; export async function renderM02MasterTemplate(root: HTMLElement): Promise { const user = await fetchSessionUser().catch(() => null); const isAdmin = user?.role === "SYSTEM_ADMIN"; - const main = createMain(isAdmin, () => void side.refresh()); + const main = createMain( + isAdmin, + () => void side.refresh(), + () => side.paint(), + ); const side = buildSide({ isAdmin, onOpen: (sel) => void main.open(sel), diff --git a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts index 9eec9861..9eb2d162 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_UI_Side.ts @@ -80,8 +80,8 @@ export interface SideOptions { export interface SideHandle { root: HTMLElement; refresh: () => Promise; - /** 좌측 고름 표시만 옮김 — 화면은 열지 않음 */ - select: (kind: Kind, name: string) => void; + /** 고름 표시를 지금 열린 것 한 줄로 — 편집 칸이 무엇을 열든(카드 [도면 수정] 등) 부름 */ + paint: () => void; } /** 서버 이름 규칙과 같게 — 화면에서 먼저 막음 */ @@ -102,8 +102,6 @@ export function buildSide(opt: SideOptions): SideHandle { let numbers: Record = {}; let seq = 0; - let picked: { kind: Kind; name: string } | null = null; - const section = (title: string, body: HTMLElement, ...more: HTMLElement[]): HTMLElement => el("section", { className: "m02-side__group ui-collapsible ui-sidebar-section ui-sidebar-card is-collapsed", @@ -140,9 +138,8 @@ export function buildSide(opt: SideOptions): SideHandle { const root = el("div", { className: "m02-side ui-sidebar-panel", children: [listHost] }); - /** 고름 표시 — `picked`(방금 누름) 이 있으면 그것 · 없으면 지금 열린 것과 비교 */ + /** 고름 표시 — 편집 칸에 지금 열린 것 하나뿐 */ const isPicked = (kind: Kind, name: string): boolean => { - if (picked) return picked.kind === kind && picked.name === name; const cur = opt.getOpen(); return !!cur && cur.layer === layer && cur.kind === kind && cur.name === name; }; @@ -199,21 +196,23 @@ export function buildSide(opt: SideOptions): SideHandle { for (const [kind, b] of editButtons) b.copy.disabled = b.del.disabled = cur?.종류 !== kind; } + /** 고름 표시는 여기서만 — 목록 · 열 줄을 함께 그려야 두 줄이 동시에 안 남음 */ + function paint(): void { + paintList(); + paintColumns(); + paintButtons(); + } + + // 여는 것은 편집 칸(`onOpen`) — 편집 칸이 열 때마다 `paint` 를 부름(페이지가 이음) async function select(info: TemplateInfo): Promise { if (!isOpen(info) && !(await opt.confirmLeave())) return; - picked = null; opt.onOpen({ layer, projectId: null, kind: info.종류, name: info.이름 }); - paintList(); - paintButtons(); } async function selectColumn(col: StructureColumn, kind: ColumnKind): Promise { if (!isPicked(kind, col.id) && !(await opt.confirmLeave())) return; - picked = null; // 제목은 편집 화면이 문서 번호를 앞에 붙임(「SD010101 돌기슭막이 …」) opt.onOpen({ layer, projectId: null, kind, name: col.id, label: columnHead(col) }); - paintColumns(); - paintButtons(); } async function refreshStructure(): Promise { @@ -328,13 +327,5 @@ export function buildSide(opt: SideOptions): SideHandle { } void refresh(); - return { - root, - refresh, - select: (kind, name) => { - picked = { kind, name }; - paintList(); - paintColumns(); - }, - }; + return { root, refresh, paint }; } diff --git a/resources/tester/test_m02_structure_panel.py b/resources/tester/test_m02_structure_panel.py index ac370442..6e832ca0 100644 --- a/resources/tester/test_m02_structure_panel.py +++ b/resources/tester/test_m02_structure_panel.py @@ -148,6 +148,18 @@ def test_좌측_새로_본떠_지우기는_표_도면_컨테이너_안에_하나 assert "양식: name" in side.split("function copyTemplate")[1] # 복사본 머리 이름 = 새 이름 +def test_좌측_고름_표시는_열린_것_한_줄뿐(): + """카드 [도면 수정] 등 무엇으로 열든 편집 칸이 좌측 `paint` 를 부름 — 목록 · 열 줄을 함께 그림.""" + side = _text(SIDE) + assert "picked" not in side # 따로 든 고름 상태 없음 — 편집 칸의 지금 열린 것만 봄 + assert re.search(r"function paint\(\): void \{\s*paintList\(\);\s*paintColumns\(\);", side) + for fn in ("async function select(", "async function selectColumn("): + body = side.split(fn)[1].split("\n }\n")[0] + assert "paint" not in body, fn # 여기서 한쪽만 그리면 두 줄이 남음 + assert re.search(r"sel = next;\s*onOpened\?\.\(\);", _text(MAIN)) + assert "() => side.paint()" in _text(M02 / "M02_MasterTemplete_UI_Page.ts") + + def test_리본_접힘_모드에서_탭으로_편_것은_밖을_누르면_다시_접힘(): ribbon = _text(ROOT / "B07_DesignDetail/openwebcad/src/ribbon/Ribbon.tsx") assert "if (collapsed) setPeek(true);" in ribbon # 탭 = 잠깐 펼침(접힘 모드 유지)