- 새 M02_MasterTemplete_DrawingCard — 고칠 권한 + 저장 길이면 CAD 를 고치기로(보기만 끔) · 제목줄 · 리본 · 패널 · 상태줄 숨김(같은 출처 스타일 · CAD 앱 안 고침) · 전체 편집기 [도면 수정]과 같은 단축키 - 카드 [저장] · Ctrl+S = 전체 편집기와 같은 저장 길(saveTemplate · 읽은 문서에 도면 칸만) - ItemSource write(M02 화면 자료 자리만 · 모달 복사본은 보기만) - 저장 안 한 카드 고침 — 다른 항목 열 때 · 페이지 떠날 때 물음(ItemCards · Structure dirty · UI_Main 두 줄) - 시험 test_m02_drawing_card(새) · structure_panel · item_picker_detail 모양 맞춤 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BU9gbNE3aWFUvE61pS2WGt
162 lines
6.4 KiB
TypeScript
162 lines
6.4 KiB
TypeScript
/* =============================================================================
|
|
* M02_MasterTemplete_DrawingCard.ts
|
|
* 항목 카드 「도면 정보」 미리보기 — 고칠 수 있으면 메뉴(제목줄 · 리본 · 패널 · 상태줄) 없이
|
|
* **단축키로 그리기 · 고치기**(전체 편집기 [도면 수정]과 같은 웹캐드 · 같은 단축키) · 카드 안 [저장]
|
|
* (전체 편집기와 같은 저장 길 = 그 항목 구조물 도면 파일 `도면` 칸만 갈아 끼움) · ? = 단축키 도움말
|
|
* (PLAN 31-4). 고칠 수 없으면(읽기 층 · 저장 길 없는 자료 자리) 예전처럼 보기만.
|
|
* CAD 앱은 안 고침 — 메뉴는 같은 출처 iframe 에 스타일을 얹어 숨김.
|
|
* ========================================================================== */
|
|
|
|
import { createButton, el, showToast } from "@ui/ui_template_elements";
|
|
import { StaleError, type StructureDoc } from "./M02_MasterTemplete_Api_Fetch";
|
|
import { mountDrawingTemplate, type DrawingTemplateDoc } from "./M02_MasterTemplete_Drawing";
|
|
import type { ItemSource } from "./M02_MasterTemplete_ItemSource";
|
|
|
|
const TEXT = {
|
|
save: "저장",
|
|
help: "?",
|
|
helpTitle: "단축키 — 글자를 치고 Enter",
|
|
saved: "도면을 저장함",
|
|
stale: "그 사이 도면이 바뀜 — 항목을 다시 골라 새로 읽음",
|
|
failed: "도면을 저장 못 함 — {value}",
|
|
};
|
|
|
|
/** 단축키 도움말 — 전체 편집기 명령 별칭(openwebcad `commands.*.ts`)과 같은 것만 */
|
|
const KEYS: [string, string][] = [
|
|
["L · PL", "선 · 폴리선"],
|
|
["C · A · REC", "원 · 호 · 사각형"],
|
|
["T · DT", "글(여러 줄 · 한 줄)"],
|
|
["DLI", "치수"],
|
|
["M · CO · RO", "옮기기 · 복사 · 회전"],
|
|
["SC · MI · O", "축척 · 대칭 · 간격 띄우기"],
|
|
["TR · EX · F", "자르기 · 연장 · 모깎기"],
|
|
["E · Delete", "지우기"],
|
|
["누름 · 끌기 · Ctrl+A", "고르기 · 전부"],
|
|
["Esc", "고름 풀기 · 명령 멈춤"],
|
|
["Ctrl+Z · Ctrl+Y", "되돌리기 · 다시 하기"],
|
|
["Ctrl+S", "저장(카드 [저장]과 같음)"],
|
|
["휠 · 가운데 끌기", "확대·축소 · 화면 옮기기"],
|
|
];
|
|
|
|
/** 보기만 = 제목줄 · 리본만 숨김(예전 그대로) · 고치기 = 패널 · 상태줄까지(명령 안내 줄은 남김) */
|
|
const HIDE_VIEW = ".cad-titlebar, .cad-ribbon { display: none !important; }";
|
|
const HIDE_EDIT =
|
|
".cad-titlebar, .cad-ribbon, .cad-inspector, .cad-statusbar, .cad-quick-access" +
|
|
" { display: none !important; }";
|
|
|
|
/** 같은 출처 iframe 안쪽에 스타일 한 벌 — 다시 뜨면(load) 또 얹음 */
|
|
function hideChrome(frame: HTMLIFrameElement, css: string): void {
|
|
const apply = (): void => {
|
|
const inner = frame.contentDocument;
|
|
if (!inner?.head || inner.getElementById("m02-struct-hide")) return;
|
|
const style = inner.createElement("style");
|
|
style.id = "m02-struct-hide";
|
|
style.textContent = css;
|
|
inner.head.append(style);
|
|
};
|
|
frame.addEventListener("load", apply);
|
|
apply();
|
|
}
|
|
|
|
export interface DrawingCardOptions {
|
|
/** 구조물 도면 문서 통째(`도번` · `도면` …) — 저장은 `도면` 칸만 갈아 끼움 */
|
|
doc: StructureDoc;
|
|
/** 읽은 판 — 저장 때 보냄(다르면 409) */
|
|
version: string | null;
|
|
source: ItemSource;
|
|
column: string;
|
|
/** 고칠 권한(층) — 자료 자리에 저장 길(`write`)도 있어야 고치기 */
|
|
editable: boolean;
|
|
/** 저장 안 한 고침이 생김 · 없어짐 */
|
|
onDirty?: (dirty: boolean) => void;
|
|
}
|
|
|
|
export interface DrawingCardHandle {
|
|
/** 카드 머리에 붙일 [저장] · ? (보기만이면 빈 칸) */
|
|
tools: HTMLElement;
|
|
dirty: () => boolean;
|
|
destroy: () => void;
|
|
}
|
|
|
|
export function mountDrawingCard(host: HTMLElement, opt: DrawingCardOptions): DrawingCardHandle {
|
|
const write = opt.source.write;
|
|
const edit = opt.editable && !!write;
|
|
let doc = opt.doc;
|
|
let version = opt.version ?? "";
|
|
let dirty = false;
|
|
let saving = false;
|
|
|
|
const save = createButton({ label: TEXT.save, variant: "filled", size: "sm" });
|
|
const help = createButton({ label: TEXT.help, variant: "ghost", size: "sm" });
|
|
help.title = help.ariaLabel = TEXT.helpTitle;
|
|
const helpBox = el("div", {
|
|
className: "m02-struct__keys",
|
|
attrs: { hidden: "" },
|
|
children: [
|
|
el("strong", { text: TEXT.helpTitle }),
|
|
el("dl", {
|
|
children: KEYS.flatMap(([key, what]) => [
|
|
el("dt", { text: key }),
|
|
el("dd", { text: what }),
|
|
]),
|
|
}),
|
|
],
|
|
});
|
|
help.addEventListener("click", () => (helpBox.hidden = !helpBox.hidden));
|
|
|
|
const setDirty = (next: boolean): void => {
|
|
dirty = next;
|
|
save.disabled = !next || saving;
|
|
opt.onDirty?.(next);
|
|
};
|
|
|
|
/** 전체 편집기 [저장]과 같은 길 — 읽은 문서에 `도면` 만 갈아 끼워 그 항목 파일에 */
|
|
async function doSave(drawing?: DrawingTemplateDoc): Promise<void> {
|
|
if (!write || saving) return;
|
|
saving = true;
|
|
save.disabled = true;
|
|
try {
|
|
const next = { ...doc, 도면: (drawing ?? (await handle.getDoc())) as StructureDoc["도면"] };
|
|
version = (await write("structure", opt.column, version, next)).판;
|
|
doc = next;
|
|
setDirty(false);
|
|
showToast(TEXT.saved, "success");
|
|
} catch (error) {
|
|
const why = error instanceof Error ? error.message : "";
|
|
showToast(
|
|
error instanceof StaleError ? TEXT.stale : TEXT.failed.replace("{value}", why),
|
|
"error",
|
|
);
|
|
} finally {
|
|
saving = false;
|
|
save.disabled = !dirty;
|
|
}
|
|
}
|
|
save.addEventListener("click", () => void doSave());
|
|
save.disabled = true;
|
|
|
|
const handle = mountDrawingTemplate(host, doc.도면 as unknown as DrawingTemplateDoc, {
|
|
readOnly: !edit,
|
|
name: `preview:${opt.column}`, // 자동백업 칸은 전체 편집 화면과 갈라 둠
|
|
layer: opt.source.layer,
|
|
projectId: opt.source.projectId,
|
|
panelsCollapsed: true,
|
|
preview: !edit, // 보기만 = 왼쪽 끌기 화면 옮기기 · 고치기 = 누름 고르기 · 단축키
|
|
originMarker: true, // 좌표 0,0 기준점 십자
|
|
noArea: true, // 구조물도는 도각이 없음
|
|
...(edit ? { onChanged: setDirty, onSave: (drawing) => doSave(drawing) } : {}),
|
|
});
|
|
const frame = host.querySelector("iframe");
|
|
if (frame) hideChrome(frame, edit ? HIDE_EDIT : HIDE_VIEW);
|
|
if (edit) host.append(helpBox);
|
|
|
|
return {
|
|
tools: el("span", { className: "m02-struct__tools", children: edit ? [help, save] : [] }),
|
|
dirty: () => dirty,
|
|
destroy: () => {
|
|
handle.destroy();
|
|
helpBox.remove();
|
|
},
|
|
};
|
|
}
|