From 5b6cf0103ed97f9828ba39d046d9c865a8c9c590 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 25 Sep 2026 17:43:43 +0900 Subject: [PATCH] =?UTF-8?q?feat(M02):=20=EB=8F=84=EB=A9=B4=20=EC=B9=B8=20?= =?UTF-8?q?=EC=9C=84=EC=AA=BD=EC=9D=84=20=EC=A4=84=EC=9E=84=20=E2=80=94=20?= =?UTF-8?q?=EC=9E=90=EB=A6=AC=ED=91=9C=20=EA=B8=80=20=EC=A4=84=20=EB=BA=8C?= =?UTF-8?q?=20=C2=B7=20CAD=20=EC=A0=9C=EB=AA=A9=20=EC=A4=84=20=EC=88=A8?= =?UTF-8?q?=EA=B9=80=20=C2=B7=20=EC=9E=91=EB=8F=84=20=EC=98=81=EC=97=AD=20?= =?UTF-8?q?=ED=95=9C=20=EC=A4=84=20=C2=B7=20headerSlot=20=EC=98=B5?= =?UTF-8?q?=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CAD 그림 칸 위쪽 448px 에서 285px 로, 그림 높이 598 에서 760 으로. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01PxvYb5ufV1kWdBvZbpDfu6 --- B07_DesignDetail/openwebcad/src/App.css | 3 ++ .../src/components/QuickAccessBar.tsx | 4 +- B07_DesignDetail/openwebcad/src/state.ts | 2 + .../M02_MasterTemplete_Drawing.css | 37 ++++++------- .../M02_MasterTemplete_Drawing.ts | 54 ++++--------------- 5 files changed, 38 insertions(+), 62 deletions(-) diff --git a/B07_DesignDetail/openwebcad/src/App.css b/B07_DesignDetail/openwebcad/src/App.css index f9979ba7..fadc2fdc 100644 --- a/B07_DesignDetail/openwebcad/src/App.css +++ b/B07_DesignDetail/openwebcad/src/App.css @@ -55,6 +55,9 @@ button { z-index: 2; pointer-events: none; } +.cad-host-save { + --cad-title-height: 0px; +} .controls { pointer-events: auto; } diff --git a/B07_DesignDetail/openwebcad/src/components/QuickAccessBar.tsx b/B07_DesignDetail/openwebcad/src/components/QuickAccessBar.tsx index 46476910..b51e26dc 100644 --- a/B07_DesignDetail/openwebcad/src/components/QuickAccessBar.tsx +++ b/B07_DesignDetail/openwebcad/src/components/QuickAccessBar.tsx @@ -4,7 +4,7 @@ import { HtmlEvent } from '../App.types'; import { getCommandById } from '../commands/registry'; import { runCommand } from '../commands/run-command'; import { QUICK_ACCESS_COMMANDS } from '../ribbon/ribbon.config'; -import { getHostTitle } from '../state'; +import { getHostTitle, isHostSave } from '../state'; export const QuickAccessBar: FC = () => { const [hostTitle, setTitle] = useState(getHostTitle()); @@ -13,6 +13,8 @@ export const QuickAccessBar: FC = () => { window.addEventListener(HtmlEvent.UPDATE_STATE, refresh); return () => window.removeEventListener(HtmlEvent.UPDATE_STATE, refresh); }, []); + // 부모 페이지가 저장·이름을 맡는 M02 에선 제목 줄을 숨겨 그림 칸을 넓힌다. + if (isHostSave()) return null; return (
diff --git a/B07_DesignDetail/openwebcad/src/state.ts b/B07_DesignDetail/openwebcad/src/state.ts index 244e96a9..3710d7b4 100644 --- a/B07_DesignDetail/openwebcad/src/state.ts +++ b/B07_DesignDetail/openwebcad/src/state.ts @@ -503,6 +503,8 @@ export const setDesignMeta = (newMeta: DesignMeta | null) => { export const isHostSave = (): boolean => hostSave; export const setHostSave = (enabled: boolean) => { hostSave = enabled; + // 제목 줄이 없어지면 그 높이만큼 위로 당긴다(App.css `.cad-host-save`). + document.documentElement.classList.toggle('cad-host-save', enabled); }; export const getHostTitle = (): string => hostTitle; export const setHostTitle = (title: string) => { diff --git a/M02_MasterTemplete/M02_MasterTemplete_Drawing.css b/M02_MasterTemplete/M02_MasterTemplete_Drawing.css index 9ef1deaf..67ac1940 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Drawing.css +++ b/M02_MasterTemplete/M02_MasterTemplete_Drawing.css @@ -1,4 +1,4 @@ -/* M02 도면 양식 편집 부품 — 위 도구 줄(작도 영역 · 자리표 · 단추) + 아래 웹캐드. */ +/* M02 도면 양식 편집 부품 — 위 도구 줄 한 줄(작도 영역 · 단추) + 아래 웹캐드. */ /* 도면 칸이 메인을 꽉 채우고 창 크기를 따른다 — 메인 칸(`ui-workflow-layout__main`)은 높이가 정해지지 않아(min-height 만) 100% 가 안 먹으므로 화면 높이로 잡는다. @@ -32,7 +32,7 @@ .m02-drawing__toolbar { display: flex; flex-wrap: wrap; - align-items: flex-end; + align-items: center; gap: var(--spacing-8) var(--spacing-16, 16px); } @@ -49,30 +49,31 @@ gap: var(--spacing-8); } -.m02-drawing__area .ui-field { - width: 5.5rem; -} - -.m02-drawing__fields { - display: flex; - flex: 1 1 20rem; +.m02-drawing__area { flex-wrap: wrap; align-items: center; +} + +/* 칸 이름과 입력을 한 줄로 — 세로로 쌓지 않는다. */ +.m02-drawing__area .ui-field { + flex-direction: row; + align-items: center; gap: var(--spacing-4, 4px); + width: auto; } -.m02-drawing__field { - padding: 2px 8px; - border: 1px solid var(--color-border); - border-radius: 1440px; - background-color: var(--color-surface); - color: var(--color-text); +.m02-drawing__area .ui-field input { + width: 4.5rem; + padding-block: 2px; +} + +.m02-drawing__area .ui-field label { font-size: 0.72rem; - cursor: pointer; + white-space: nowrap; } -.m02-drawing__field[data-source="도면"] { - color: var(--color-text-muted); +.m02-drawing__toolbar--slot { + flex: 0 1 auto; } .m02-drawing__actions { diff --git a/M02_MasterTemplete/M02_MasterTemplete_Drawing.ts b/M02_MasterTemplete/M02_MasterTemplete_Drawing.ts index 12d51b77..76560d42 100644 --- a/M02_MasterTemplete/M02_MasterTemplete_Drawing.ts +++ b/M02_MasterTemplete/M02_MasterTemplete_Drawing.ts @@ -36,6 +36,8 @@ export interface DrawingTemplateOptions { projectId?: string | null; /** 저장 안 한 고침이 생기면 true — CAD 편집 · 작도 영역 값 바꿈. */ onChanged?: (dirty: boolean) => void; + /** 페이지 제목 줄 안의 빈 칸 — 있으면 작도 영역 칸 · 파일 불러오기를 거기 한 줄로 놓는다. */ + headerSlot?: HTMLElement; } const RECOVERY_PREFIX = "OPEN_WEB_CAD__RECOVERY__"; @@ -73,19 +75,6 @@ export interface DrawingTemplateHandle { const DEFAULT_AREA: [number, number, number, number] = [42, 47, 812, 567]; const AREA_LABELS = ["왼쪽 x", "아래 y", "오른쪽 x", "위 y"]; -interface DrawingField { - key: string; - source: string; - label: string; -} - -async function fetchDrawingFields(): Promise { - const response = await fetch(`${API_BASE_URL}/m02/drawing-fields`, { credentials: "include" }); - const payload = (await response.json()) as { fields?: DrawingField[]; message?: string }; - if (!response.ok) throw new Error(payload.message ?? `HTTP ${response.status}`); - return payload.fields ?? []; -} - async function importDrawingFile( file: File, ): Promise<{ drawing: DrawingTemplateDoc; entity_count: number }> { @@ -126,6 +115,7 @@ export function mountDrawingTemplate( const areaTitle = document.createElement("span"); areaTitle.className = "m02-drawing__title"; areaTitle.textContent = "작도 영역"; + areaTitle.title = "도면 내용이 놓이는 칸 — 양식 좌표 mm"; const start = doc.drawing_area ?? DEFAULT_AREA; const areaInputs = AREA_LABELS.map((label, index) => { const field = createInputField({ label, type: "number", value: String(start[index]) }); @@ -142,34 +132,6 @@ export function mountDrawingTemplate( return valid ? (values as [number, number, number, number]) : null; }; - // 자리표 키 — 누르면 `{{키}}` 를 복사한다. 도각 글자에 붙여 넣으면 그릴 때 값이 채워진다. - const fields = document.createElement("div"); - fields.className = "m02-drawing__fields"; - const fieldsTitle = document.createElement("span"); - fieldsTitle.className = "m02-drawing__title"; - fieldsTitle.textContent = "자리표"; - fields.append(fieldsTitle); - void fetchDrawingFields() - .then((list) => { - for (const field of list) { - const chip = document.createElement("button"); - chip.type = "button"; - chip.className = "m02-drawing__field"; - chip.dataset.source = field.source; - chip.textContent = `{{${field.key}}}`; - chip.title = `${field.label} (${field.source}) — 눌러 복사`; - chip.addEventListener("click", () => { - void navigator.clipboard - ?.writeText(`{{${field.key}}}`) - .then(() => showToast(`{{${field.key}}} 를 복사했습니다.`, "success")); - }); - fields.append(chip); - } - }) - .catch((error) => - showToast(error instanceof Error ? error.message : "자리표 목록을 받지 못했습니다.", "error"), - ); - // CAD 안 💾 · Ctrl+S — 양식 파일은 페이지 [저장] 만 쓴다. 저장 중 또 눌러도 한 번만 부른다. let saving = false; const hostSave = (): void => { @@ -242,8 +204,13 @@ export function mountDrawingTemplate( return base; }; - toolbar.append(area, fields, actions); - root.append(toolbar, cad.element); + // 한 줄 — 페이지가 머리 칸(headerSlot)을 주면 거기, 아니면 그림 칸 위에 작게. + toolbar.append(area, actions); + if (options.headerSlot) { + toolbar.classList.add("m02-drawing__toolbar--slot"); + options.headerSlot.append(toolbar); + root.append(cad.element); + } else root.append(toolbar, cad.element); container.append(root); load(doc); @@ -251,6 +218,7 @@ export function mountDrawingTemplate( getDoc, destroy: () => { cad.destroy(); + toolbar.remove(); root.remove(); }, };