From de250ab7598ccf1c0fb226ebffca6d23d1c022ad Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 26 Oct 2026 22:35:43 +0900 Subject: [PATCH] =?UTF-8?q?refactor(cad):=20CAD=20=EC=95=B1=20=EA=B2=89=20?= =?UTF-8?q?=EC=9E=90=EC=B2=B4=20=EB=B3=80=EC=A2=85=EC=9D=84=20=EB=B6=80?= =?UTF-8?q?=ED=92=88=20=EC=83=81=EC=9E=90=20=EB=B6=80=ED=92=88=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=E2=80=94=20=EC=83=81=ED=83=9C=EB=A7=89=EB=8C=80=20?= =?UTF-8?q?=EC=BC=9C=EA=B8=B0=20=EC=B9=A9=20=C2=B7=20=ED=8C=A8=EB=84=90=20?= =?UTF-8?q?=ED=83=AD=20=C2=B7=20=EB=96=A0=20=EC=9E=88=EB=8A=94=20=ED=8C=90?= =?UTF-8?q?=20=C2=B7=20=EB=A9=94=EB=89=B4=20=C2=B7=20=EC=95=84=EC=9D=B4?= =?UTF-8?q?=EC=BD=98=20=EB=8B=A8=EC=B6=94=20=C2=B7=20=ED=99=95=EC=9D=B8?= =?UTF-8?q?=EC=B0=BD=20(PLAN=20553-6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 부품 상자 CSS · 단추 기본 꼴을 CAD iframe 에 실음(integration/ui-kit.ts) · 자리 · 폭은 cad-* 그대로 - 상태막대 = ui-toggle--chip 22 · 특성/레이어 = ui-tabs 36 · ↶↷ · 리본 접기 · 탐색 막대 = ui-icon-btn 20 · 32 - 빠른 특성 · 블록 라이브러리 · 자리표 = ui-float-panel · 리본 넘침 · 스냅 · 명령 자동완성 = ui-ctx-menu - window.confirm · window.prompt → askConfirm · askText · 빈 안내 = ui-empty 작은 꼴 - 높이 단: 리본 칸 19→18 · 명령 칸 26→28 · 레이어 줄 40→42 · 문서 탭 25→24 · 도각 편집 단추 24 - 마우스 바탕 = --color-hover · 켜짐 = --color-accent-soft · 도면 그림 색(--draw-*) 안 건드림 - 안 쓰는 DropdownButton 지움(쓰는 곳 0 · grep) Co-Authored-By: Claude Opus 5.5 --- B07_DesignDetail/openwebcad/src/App.css | 84 ++----- B07_DesignDetail/openwebcad/src/App.types.ts | 1 - .../openwebcad/src/App_Layers.css | 215 +++--------------- .../src/commands/commands.insert.ts | 11 +- .../src/components/BlockLibraryPanel.tsx | 123 ++++++---- .../openwebcad/src/components/Button.tsx | 4 +- .../openwebcad/src/components/CommandLine.tsx | 8 +- .../src/components/DropdownButton.tsx | 69 ------ .../src/components/FramePlaceholderPanel.tsx | 125 +++++----- .../src/components/InspectorPanel.tsx | 13 +- .../src/components/PropertiesEditor.tsx | 9 +- .../src/components/QuickAccessBar.tsx | 5 +- .../src/components/QuickProperties.tsx | 21 +- .../openwebcad/src/components/StatusBar.tsx | 26 ++- .../src/components/ViewControls.tsx | 45 +++- .../src/inputController/snap-menu.css | 26 +-- .../src/inputController/snap-menu.ts | 3 +- .../openwebcad/src/integration/ui-kit.ts | 18 ++ B07_DesignDetail/openwebcad/src/main.tsx | 2 + .../openwebcad/src/ribbon/Ribbon.tsx | 10 +- B07_DesignDetail/openwebcad/tsconfig.app.json | 11 +- ui_template/cad_host/cad_host.css | 9 +- ui_template/cad_host/cad_host_frame_edit.ts | 4 + ui_template/cad_host/cad_host_tabs.css | 18 +- 24 files changed, 364 insertions(+), 496 deletions(-) delete mode 100644 B07_DesignDetail/openwebcad/src/components/DropdownButton.tsx create mode 100644 B07_DesignDetail/openwebcad/src/integration/ui-kit.ts diff --git a/B07_DesignDetail/openwebcad/src/App.css b/B07_DesignDetail/openwebcad/src/App.css index 9c4c7197a..994e1d59c 100644 --- a/B07_DesignDetail/openwebcad/src/App.css +++ b/B07_DesignDetail/openwebcad/src/App.css @@ -27,7 +27,8 @@ --cad-text-dim: var(--color-text-secondary); --cad-text-muted: var(--color-text-muted); --cad-accent: var(--color-accent); - --cad-hover: var(--color-mist-violet); + /* 마우스 바탕 = 부품 상자와 같은 값 · 켜짐 바탕은 --color-accent-soft */ + --cad-hover: var(--color-hover); --cad-canvas: var(--draw-canvas); } @@ -111,21 +112,12 @@ body > canvas[data-id="canvas"] { flex: 1; min-width: 0; } +/* 접기 단추 = 아이콘 단추 부품(단 20) · 여기는 자리만 */ .cad-ribbon-tabs__collapse { - width: 24px; - height: 20px; align-self: center; - padding: 0; - border: 1px solid transparent; - border-radius: 4px; - background: transparent; - color: var(--cad-text-dim); - font-size: 11px; } -.cad-ribbon-tabs__collapse:hover { - background: var(--cad-hover); -} -.cad-ribbon-tabs button { +/* 리본 탭 = CAD 고유 꼴 그대로(토큰만) */ +.cad-ribbon-tabs > button:not(.ui-icon-btn) { padding: 0 14px; border: 1px solid transparent; border-bottom: 0; @@ -134,10 +126,10 @@ body > canvas[data-id="canvas"] { color: var(--cad-text-dim); font-size: 12px; } -.cad-ribbon-tabs button:hover { +.cad-ribbon-tabs > button:not(.ui-icon-btn):hover { background: var(--cad-hover); } -.cad-ribbon-tabs button[data-active="true"] { +.cad-ribbon-tabs > button[data-active="true"] { background: var(--cad-chrome); border-color: var(--cad-line); color: var(--color-text); @@ -185,53 +177,25 @@ body > canvas[data-id="canvas"] { .cad-ribbon-group__label[data-expandable="true"]:hover { color: var(--cad-accent); } +/* 꼴 = 메뉴 부품(`ui-ctx-menu`) · 여기는 자리만 */ .cad-ribbon-overflow { /* 리본 패널 줄이 가로 스크롤 상자라 그 안에서는 잘린다 — 화면 좌표로 띄운다 */ position: fixed; z-index: 20; - display: flex; - flex-direction: column; min-width: 170px; max-height: 72vh; - padding: 4px; - overflow-y: auto; - background: var(--cad-chrome-raised); - border: 1px solid var(--cad-line); - border-radius: 4px; - box-shadow: var(--shadow-lg); } -.cad-ribbon-overflow button { +.cad-ribbon-overflow > .ui-ctx-menu__item { display: flex; gap: 8px; align-items: center; - padding: 5px 8px; - border: 0; - border-radius: 3px; - background: transparent; - font-size: 11px; - text-align: left; -} -.cad-ribbon-overflow button:hover { - background: var(--cad-hover); } +/* ↶ ↷ = 아이콘 단추 부품(단 20) · 여기는 자리만 */ .cad-quick-access { display: flex; gap: 2px; align-self: center; } -.cad-ribbon-tabs .cad-quick-access button { - width: 24px; - height: 20px; - padding: 0; - border: 1px solid transparent; - border-radius: 4px; - line-height: 1; - font-size: 13px; -} -.cad-quick-access button:hover { - border-color: var(--cad-line); - background: var(--cad-hover); -} .cad-ribbon-props { display: flex; flex-direction: column; @@ -252,15 +216,16 @@ body > canvas[data-id="canvas"] { width: 52px; text-align: left; } +/* 리본 안 칸 = CAD 고유 꼴(토큰만) · 높이 = 크기 단 18 · 둥글기 4 */ .cad-prop select, .cad-prop input[type="number"] { - height: 19px; + height: 18px; min-width: 78px; padding: 0 3px; color: var(--cad-text); background: var(--cad-chrome-sunken); border: 1px solid var(--cad-line); - border-radius: 3px; + border-radius: 4px; font-size: 11px; } .cad-prop input[type="number"] { @@ -268,13 +233,13 @@ body > canvas[data-id="canvas"] { width: 52px; } .cad-prop input[type="color"] { - height: 19px; + height: 18px; width: 44px; padding: 1px; background: var(--cad-chrome-sunken); /* 흰색을 골라도 스와치 자리가 보이게 테두리를 진하게 준다 (2026-09-01) */ border: 1px solid var(--cad-text-muted); - border-radius: 3px; + border-radius: 4px; cursor: pointer; } /* 고른 것들 색이 다름(488) — 빈 견본(투명 · 점선 테두리) */ @@ -328,7 +293,7 @@ input[type="color"][data-mixed="true"]::-webkit-color-swatch { background: trans } .cad-tool[data-active="true"] { border-color: var(--cad-accent); - background: var(--cad-hover); + background: var(--color-accent-soft); box-shadow: inset 0 -2px var(--cad-accent); } .cad-tool:disabled { @@ -373,23 +338,14 @@ input[type="color"][data-mixed="true"]::-webkit-color-swatch { background: trans background: var(--cad-chrome-raised); color: var(--cad-text-dim); } +/* 꼴 = 탭 부품(`ui-tabs`) · 여기는 칸 둘이 폭을 나누는 배치만 */ .cad-inspector-tabs { display: grid; grid-template-columns: 1fr 1fr; - height: 38px; - border-bottom: 1px solid var(--cad-line); + gap: 0; } -.cad-inspector-tabs button { - border: 0; - border-bottom: 2px solid transparent; - background: var(--cad-chrome-sunken); - color: var(--cad-text-dim); - font-size: 12px; -} -.cad-inspector-tabs button[data-active="true"] { - border-bottom-color: var(--cad-accent); - background: var(--cad-chrome-raised); - color: var(--color-text); +.cad-inspector-tabs > .ui-tabs__tab { + justify-content: center; } .cad-properties { padding: 14px 12px; diff --git a/B07_DesignDetail/openwebcad/src/App.types.ts b/B07_DesignDetail/openwebcad/src/App.types.ts index db75fc6de..6a1d0346e 100644 --- a/B07_DesignDetail/openwebcad/src/App.types.ts +++ b/B07_DesignDetail/openwebcad/src/App.types.ts @@ -74,7 +74,6 @@ export interface Layer { export enum LOCAL_STORAGE_KEY { DRAWING = 'OPEN_WEB_CAD__DRAWING', - DROPDOWN = 'OPEN_WEB_CAD__DROPDOWN', } export interface StartAndEndpointEntity { diff --git a/B07_DesignDetail/openwebcad/src/App_Layers.css b/B07_DesignDetail/openwebcad/src/App_Layers.css index c46a6d7d1..040234e55 100644 --- a/B07_DesignDetail/openwebcad/src/App_Layers.css +++ b/B07_DesignDetail/openwebcad/src/App_Layers.css @@ -14,22 +14,22 @@ padding-right: 2px; min-width: 0; } -/* 도면층 버튼 — 색은 토큰으로만, 상태는 Button이 붙이는 data-* 속성으로 가른다. */ +/* 도면층 버튼 — 목록 줄 부품(`ui-sidebar-row`) 꼴 · 상태는 Button이 붙이는 data-* 속성으로 가른다. */ .cad-layer-manager button[data-type="regular"] { - background: var(--cad-chrome-sunken); - color: var(--cad-accent); + background: transparent; + color: var(--color-text-body); } .cad-layer-manager button[data-type="transparent"] { background: transparent; - color: var(--cad-accent); + color: var(--color-text-secondary); } .cad-layer-manager button:hover { - background: var(--cad-hover); + background: var(--color-hover); color: var(--color-text); } .cad-layer-manager button[data-active="true"] { - background: var(--color-primary); - color: var(--color-primary-text); + background: var(--color-accent-soft); + color: var(--color-accent); } .cad-view-controls { @@ -41,7 +41,7 @@ align-items: center; overflow: hidden; border: 1px solid var(--cad-line); - border-radius: 4px; + border-radius: var(--radius-buttons); background: var(--cad-chrome-raised); box-shadow: var(--shadow-lg); } @@ -87,21 +87,7 @@ body.cad-file-dragging::after { pointer-events: none; } -.cad-view-controls button { - width: 34px; - height: 32px; - border: 0; - border-right: 1px solid var(--cad-line); - background: transparent; - font-size: 17px; -} -.cad-view-controls button:hover { - background: var(--cad-hover); -} -.cad-view-controls__fit { - color: var(--cad-accent); - font-size: 15px; -} +/* 단추 = 아이콘 단추 부품(단 32) · 여기는 배율 글만 */ .cad-view-controls span { min-width: 48px; color: var(--cad-text-dim); @@ -133,30 +119,16 @@ body.cad-file-dragging::after { color: var(--cad-text-muted); font: 11px Consolas, monospace; } +/* 꼴 = 메뉴 부품(`ui-ctx-menu`) · 여기는 자리와 명령 글꼴만 */ .cad-command-suggestions { position: absolute; z-index: 6; bottom: 100%; left: 10px; - display: flex; - flex-direction: column; min-width: 220px; - padding: 4px; - background: var(--cad-chrome-raised); - border: 1px solid var(--cad-line); - border-radius: 4px; - box-shadow: var(--shadow-lg); } -.cad-command-suggestions button { - padding: 4px 8px; - border: 0; - border-radius: 3px; - background: transparent; - font: 11px Consolas, monospace; - text-align: left; -} -.cad-command-suggestions button:hover { - background: var(--cad-hover); +.cad-command-suggestions > .ui-ctx-menu__item { + font: 12px Consolas, monospace; } .cad-prop--wide select { min-width: 132px; @@ -180,12 +152,13 @@ body.cad-file-dragging::after { font-size: 11px; font-weight: 600; } +/* 명령 칸 = CAD 고유 꼴(토큰만) · 높이 = 크기 단 28 */ .cad-command-area input { flex: 1; - height: 26px; + height: 28px; padding: 0 8px; border: 1px solid var(--cad-line); - border-radius: 2px; + border-radius: var(--radius-buttons); outline: none; background: var(--cad-chrome-sunken); color: var(--cad-text); @@ -210,23 +183,7 @@ body.cad-file-dragging::after { background: var(--cad-chrome); border-top: 1px solid var(--cad-line); } -.cad-statusbar button { - height: 21px; - padding: 0 8px; - border: 1px solid transparent; - border-radius: 2px; - background: transparent; - color: var(--cad-text-dim); - font-size: 10px; -} -.cad-statusbar button:hover { - background: var(--cad-hover); -} -.cad-statusbar button[data-active="true"] { - border-color: var(--cad-accent); - background: var(--color-primary); - color: var(--color-primary-text); -} +/* 켜기 = 켜기 칩 부품(`ui-toggle--chip` · 단 22) */ .cad-statusbar__hint { margin-left: auto; color: var(--cad-text-muted); @@ -261,13 +218,14 @@ body.cad-file-dragging::after { font-size: 11px; color: var(--cad-text-dim); } +/* 칸 = 크기 단 24(둥글기 6) · 토큰만 */ .cad-properties-editor select, .cad-properties-editor input { height: 24px; min-width: 0; padding: 0 4px; border: 1px solid var(--cad-line); - border-radius: 3px; + border-radius: 6px; background: var(--cad-chrome-sunken); color: var(--cad-text); font-size: 11px; @@ -292,59 +250,19 @@ body.cad-file-dragging::after { color: var(--cad-text); text-overflow: ellipsis; } -.cad-properties__empty { - margin: 0; - color: var(--cad-text-muted); - font-size: 11px; -} -/* 빠른 특성 — 화면 위에 뜨는 작은 상자 */ -.cad-quick-properties { - position: fixed; - z-index: 5; - top: calc(var(--cad-title-height) + var(--cad-ribbon-height) + 12px); - right: 14px; - width: 240px; - padding: 8px 10px 10px; - background: var(--cad-chrome-raised); - border: 1px solid var(--cad-line); - border-radius: 5px; - box-shadow: var(--shadow-lg); -} -.cad-quick-properties__header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 6px; - color: var(--cad-text-dim); - font-size: 11px; -} -.cad-quick-properties__header button { - border: 0; - background: transparent; - color: var(--cad-text-muted); - font-size: 12px; -} -/* 블록 라이브러리 팔레트 (INSERT/BLOCK) */ +/* 빠른 특성 · 블록 라이브러리 — 꼴 = 떠 있는 판 부품(`ui-float-panel`) · 여기는 자리 · 폭만 */ +.cad-quick-properties, .cad-block-library { position: fixed; z-index: 5; top: calc(var(--cad-title-height) + var(--cad-ribbon-height) + 12px); right: 14px; width: 240px; - padding: 8px 10px 10px; - background: var(--cad-chrome-raised); - border: 1px solid var(--cad-line); - border-radius: 5px; - box-shadow: var(--shadow-lg); -} -.cad-block-library__empty { - margin: 0; - color: var(--cad-text-muted); - font-size: 11px; - line-height: 1.5; } + +/* 블록 라이브러리 팔레트 (INSERT/BLOCK) */ .cad-block-library__list { max-height: 220px; margin: 0; @@ -357,40 +275,26 @@ body.cad-file-dragging::after { align-items: center; gap: 4px; } -.cad-block-library__list li button { - border: 0; - background: transparent; - color: var(--cad-text); - font-size: 12px; -} +/* 블록 줄 = 목록 줄 부품(`ui-sidebar-row`) · 지우기 = 아이콘 단추 부품(단 20) · 여기는 줄 폭만 */ .cad-block-library__name { flex: 1; + min-width: 0; overflow: hidden; - padding: 3px 4px; - text-align: left; text-overflow: ellipsis; white-space: nowrap; } -.cad-block-library__name:hover { - background: var(--cad-chrome-sunken); -} +/* 단추 = 단추 부품(보조 · 단 24) · 여기는 둘이 폭을 나눔만 */ .cad-block-library__actions { display: flex; gap: 6px; margin-top: 8px; } -.cad-block-library__actions button { +.cad-block-library__actions > .ui-btn { flex: 1; - padding: 4px 0; - border: 1px solid var(--cad-line); - border-radius: 3px; - background: transparent; - color: var(--cad-text-dim); - font-size: 11px; } /* 도각 자리표 패널 — 도각 편집으로 도면을 실었을 때만 뜬다 (2026-09-06 사용자 지시로 - 부모 사이드바에서 캐드 안으로 옮김). 도면 오른쪽 위, 리본 아래에 붙는다. */ + 부모 사이드바에서 캐드 안으로 옮김). 꼴 = 떠 있는 판 부품(`ui-float-panel`) · 여기는 자리 · 폭 · 줄 배치만. */ .cad-frame-tokens { position: fixed; /* 오른쪽 위는 진행단계 패널이 쓴다 — 아래쪽(상태막대·명령행 위)에 붙인다. */ @@ -398,36 +302,13 @@ body.cad-file-dragging::after { bottom: calc(var(--cad-status-height) + var(--cad-command-height) + 8px); z-index: 3; max-height: 46vh; - overflow-y: auto; + width: 236px; +} + +.cad-frame-tokens__body { display: flex; flex-direction: column; gap: 6px; - width: 236px; - padding: 8px; - border: 1px solid var(--cad-line); - border-radius: 6px; - background: var(--cad-chrome-raised); - box-shadow: var(--shadow-lg); - color: var(--cad-text); -} - -.cad-frame-tokens__header { - display: flex; - justify-content: space-between; - align-items: flex-start; - gap: 6px; - color: var(--cad-text-dim); - font-size: 11px; - line-height: 1.4; -} - -.cad-frame-tokens__fold { - flex: none; - border: 1px solid var(--cad-line); - border-radius: 4px; - background: transparent; - color: var(--cad-text); - cursor: pointer; } .cad-frame-tokens__group { @@ -439,39 +320,9 @@ body.cad-file-dragging::after { .cad-frame-tokens__title { width: 100%; - color: var(--cad-text-dim); - font-size: 11px; } -.cad-frame-tokens__button { - padding: 3px 7px; - border: 1px solid var(--cad-line); - border-radius: 4px; - background: var(--cad-chrome); - color: var(--cad-text); - font-size: 11px; - cursor: pointer; -} - -.cad-frame-tokens__button:hover { - background: var(--cad-accent-soft, var(--cad-chrome-raised)); -} - -/* 자리표 칸 크기 입력 (2026-09-06) */ -.cad-frame-tokens__size { - display: flex; - align-items: center; - gap: 4px; - color: var(--cad-text-dim); - font-size: 11px; -} - -.cad-frame-tokens__size input { +/* 자리표 칸 크기 입력 (2026-09-06) — 칸 = 입력 부품(단 24) */ +.cad-frame-tokens__size > .ui-input { width: 64px; - padding: 2px 4px; - border: 1px solid var(--cad-line); - border-radius: 4px; - background: var(--cad-chrome); - color: var(--cad-text); - font-size: 11px; } diff --git a/B07_DesignDetail/openwebcad/src/commands/commands.insert.ts b/B07_DesignDetail/openwebcad/src/commands/commands.insert.ts index 6cb2b8d73..d5d377d45 100644 --- a/B07_DesignDetail/openwebcad/src/commands/commands.insert.ts +++ b/B07_DesignDetail/openwebcad/src/commands/commands.insert.ts @@ -5,6 +5,7 @@ import { setBlockLibraryVisible } from '../components/ui-state'; import { importImageFromFile } from '../helpers/import-export-handlers/import-image-from-file'; import { pickFile } from '../helpers/pick-file'; import { importDrawingFile } from '../integration/dxf-clip-paste'; +import { askText } from '../integration/ui-kit'; import { getActiveToolActor, getSelectedEntities } from '../state'; import { Tool } from '../tools'; import { imageImportToolStateMachine } from '../tools/image-import-tool'; @@ -39,13 +40,15 @@ export const INSERT_COMMANDS: CadCommand[] = [ needsSelection: true, run: () => { const entities = getSelectedEntities(); - const name = window.prompt('블록 이름', '')?.trim(); - if (!name) return '블록 작성 취소'; - void saveBlockFromEntities(name, entities).then(() => { + // 글 묻기 = 확인창 부품(window.prompt 대신 · 553-6) + void askText({ message: '블록 이름', okLabel: '저장' }).then(async (text) => { + const name = text?.trim(); + if (!name) return; + await saveBlockFromEntities(name, entities); toast.success(`블록 '${name}' 저장 (객체 ${entities.length}개)`); setBlockLibraryVisible(true); }); - return `블록 '${name}' 저장`; + return '블록 이름 입력'; }, }, { diff --git a/B07_DesignDetail/openwebcad/src/components/BlockLibraryPanel.tsx b/B07_DesignDetail/openwebcad/src/components/BlockLibraryPanel.tsx index 3fc99300a..f53c3ffa7 100644 --- a/B07_DesignDetail/openwebcad/src/components/BlockLibraryPanel.tsx +++ b/B07_DesignDetail/openwebcad/src/components/BlockLibraryPanel.tsx @@ -1,4 +1,4 @@ -/** 블록 라이브러리 팔레트 — 저장한 블록을 고르면 삽입 도구가 켜진다 (INSERT/BLOCK) */ +/** 블록 라이브러리 팔레트 — 저장한 블록을 고르면 삽입 도구가 켜진다 (INSERT/BLOCK) · 떠 있는 판 부품 */ import type { FC } from 'react'; import { deleteBlock, @@ -7,6 +7,7 @@ import { importBlocksFromFile, } from '../blocks/block-library'; import { pickFile } from '../helpers/pick-file'; +import { askConfirm } from '../integration/ui-kit'; import { startBlockInsert } from '../tools/insert-block-tool'; import { isBlockLibraryVisible, setBlockLibraryVisible } from './ui-state'; @@ -16,57 +17,83 @@ export const BlockLibraryPanel: FC = () => { const blocks = getBlocks(); return ( -
-
- 블록 라이브러리 -
+
+ {blocks.length === 0 ? ( +
+

객체를 선택하고 BLOCK 명령으로 저장하십시오.

+
+ ) : ( +
    + {blocks.map((block) => ( +
  • + + +
  • + ))} +
+ )} - {blocks.length === 0 ? ( -

객체를 선택하고 BLOCK 명령으로 저장하십시오.

- ) : ( -
    - {blocks.map((block) => ( -
  • - - -
  • - ))} -
- )} - -
- - +
+ + +
); diff --git a/B07_DesignDetail/openwebcad/src/components/Button.tsx b/B07_DesignDetail/openwebcad/src/components/Button.tsx index f95ddc2c4..4f499c0de 100644 --- a/B07_DesignDetail/openwebcad/src/components/Button.tsx +++ b/B07_DesignDetail/openwebcad/src/components/Button.tsx @@ -37,9 +37,9 @@ export const Button: FC = ({ left = null, right = null, }) => { - // 색은 App.css의 테마 토큰이 data-type/data-active로 준다 — 여기서는 배치만. + // 색은 App.css의 테마 토큰이 data-type/data-active로 준다 — 여기서는 배치만 · 높이 = 크기 단 42(글 14 · 둥글기 8) const classParts = [ - 'font-semibold py-4 h-10 flex flex-row justify-start w-full items-center', + 'font-semibold h-[42px] text-sm rounded-lg flex flex-row justify-start w-full items-center', size === 'regular' ? 'pl-2 pr-2 gap-2' : '', size === 'small' ? 'pl-2 pr-2 gap-0' : '', className || '', diff --git a/B07_DesignDetail/openwebcad/src/components/CommandLine.tsx b/B07_DesignDetail/openwebcad/src/components/CommandLine.tsx index 5d878f70d..c3721829a 100644 --- a/B07_DesignDetail/openwebcad/src/components/CommandLine.tsx +++ b/B07_DesignDetail/openwebcad/src/components/CommandLine.tsx @@ -16,7 +16,10 @@ export const CommandLine: FC = () => { const history = getCommandHistory(); const instruction = currentPrompt().line || '명령을 입력하거나 도구를 선택하십시오.'; - const suggestions = useMemo(() => matchCommandPrefixes(pick?.base ?? text).slice(0, 8), [text, pick]); + const suggestions = useMemo( + () => matchCommandPrefixes(pick?.base ?? text).slice(0, 8), + [text, pick] + ); const submit = (event: FormEvent) => { event.preventDefault(); @@ -102,10 +105,11 @@ export const CommandLine: FC = () => { {instruction} {text.trim() && suggestions.length > 0 && ( -
+
{suggestions.map((command) => (
- ); -}; diff --git a/B07_DesignDetail/openwebcad/src/components/FramePlaceholderPanel.tsx b/B07_DesignDetail/openwebcad/src/components/FramePlaceholderPanel.tsx index 706adf570..732331f58 100644 --- a/B07_DesignDetail/openwebcad/src/components/FramePlaceholderPanel.tsx +++ b/B07_DesignDetail/openwebcad/src/components/FramePlaceholderPanel.tsx @@ -139,15 +139,15 @@ export const FramePlaceholderPanel: FC = () => { if (!visible) return null; return ( -
-
- - {folded ? '자리표 놓기' : '자리표 놓기 — 누르면 화면 가운데에 서고, 끌어서 자리를 잡습니다'} - +
+
+ 자리표 놓기
{!folded && ( - <> -
- 글자 - {TEXT_TOKENS.map((token) => ( - - ))} -
-
- 그림 (칸에 비율 그대로 들어감) - {IMAGE_TOKENS.map((token) => ( - - ))} -
- {picked && ( -
- 고른 자리표 칸 크기 (mm) - - +
+

+ 누르면 화면 가운데에 서고, 끌어서 자리를 잡습니다 +

+
+ 글자 + {TEXT_TOKENS.map((token) => ( + + ))} +
+
+ + 그림 (칸에 비율 그대로 들어감) + + {IMAGE_TOKENS.map((token) => ( + + ))} +
+ {picked && ( +
+ + 고른 자리표 칸 크기 (mm) + + + +
+ )}
)} - - )}
); }; diff --git a/B07_DesignDetail/openwebcad/src/components/InspectorPanel.tsx b/B07_DesignDetail/openwebcad/src/components/InspectorPanel.tsx index 3807a356a..b94064002 100644 --- a/B07_DesignDetail/openwebcad/src/components/InspectorPanel.tsx +++ b/B07_DesignDetail/openwebcad/src/components/InspectorPanel.tsx @@ -27,17 +27,24 @@ export const InspectorPanel: FC = ({ collapsed, onToggleCol {!collapsed && ( <> -
+ {/* 탭 부품(밑줄 탭 · 단 36) — 칸 둘이 폭을 나눠 가짐 */} +
- +
+ +
); }; diff --git a/B07_DesignDetail/openwebcad/src/components/StatusBar.tsx b/B07_DesignDetail/openwebcad/src/components/StatusBar.tsx index b34eef6e3..cf8188167 100644 --- a/B07_DesignDetail/openwebcad/src/components/StatusBar.tsx +++ b/B07_DesignDetail/openwebcad/src/components/StatusBar.tsx @@ -1,4 +1,4 @@ -/** 상태막대 — AutoCAD 하단 제도 보조 토글 */ +/** 상태막대 — AutoCAD 하단 제도 보조 토글 · 켜기 칩 부품(`ui-toggle--chip` · 단 22) */ import type { FC } from 'react'; import { getAngleStep, @@ -27,7 +27,9 @@ export const StatusBar: FC = ({ commandLineVisible, onToggleComm
- )} - {Math.round(zoom * 100)}% -
diff --git a/B07_DesignDetail/openwebcad/src/inputController/snap-menu.css b/B07_DesignDetail/openwebcad/src/inputController/snap-menu.css index 8d313cc24..7cb29eeff 100644 --- a/B07_DesignDetail/openwebcad/src/inputController/snap-menu.css +++ b/B07_DesignDetail/openwebcad/src/inputController/snap-menu.css @@ -1,29 +1,5 @@ -/* Shift+우클릭 한 번 스냅 메뉴(`snap-menu.ts`) — 커서 자리에 세로 목록 */ +/* Shift+우클릭 한 번 스냅 메뉴(`snap-menu.ts`) — 꼴 = 메뉴 부품(`ui-ctx-menu`) · 여기는 커서 자리 · 위 겹침만 */ .cad-snap-menu { position: fixed; z-index: 60; - display: flex; - flex-direction: column; - min-width: 160px; - padding: 4px; - border: 1px solid var(--color-border); - border-radius: 6px; - background: var(--color-surface-raised); - box-shadow: var(--shadow-sm); -} - -.cad-snap-menu > button { - padding: 4px 10px; - border: 0; - border-radius: 4px; - background: transparent; - color: var(--color-text); - font-size: 13px; - text-align: left; - cursor: pointer; -} - -.cad-snap-menu > button:hover, -.cad-snap-menu > button:focus-visible { - background: var(--color-surface-hover, rgba(127, 127, 127, 0.15)); } diff --git a/B07_DesignDetail/openwebcad/src/inputController/snap-menu.ts b/B07_DesignDetail/openwebcad/src/inputController/snap-menu.ts index bfb33b789..81533c097 100644 --- a/B07_DesignDetail/openwebcad/src/inputController/snap-menu.ts +++ b/B07_DesignDetail/openwebcad/src/inputController/snap-menu.ts @@ -55,11 +55,12 @@ export function openSnapMenu(x: number, y: number, onPick: (name: string) => voi current = { onPick, element: null }; if (typeof document === 'undefined') return; const menu = document.createElement('div'); - menu.className = 'cad-snap-menu controls'; + menu.className = 'ui-ctx-menu cad-snap-menu controls'; // 꼴 = 메뉴 부품 menu.setAttribute('role', 'menu'); for (const [name, label] of SNAP_MENU) { const item = document.createElement('button'); item.type = 'button'; + item.className = 'ui-ctx-menu__item'; item.setAttribute('role', 'menuitem'); item.textContent = `${label} (${name})`; item.addEventListener('click', () => pickSnapMenu(name)); diff --git a/B07_DesignDetail/openwebcad/src/integration/ui-kit.ts b/B07_DesignDetail/openwebcad/src/integration/ui-kit.ts new file mode 100644 index 000000000..1cdc55074 --- /dev/null +++ b/B07_DesignDetail/openwebcad/src/integration/ui-kit.ts @@ -0,0 +1,18 @@ +/** + * 부품 상자(`ui_template/`) CAD 쪽 한 벌(553-6) — CAD 겉도 앱 화면과 같은 부품 꼴을 씀. + * 부품 CSS 를 싣고 단추 · 확인창 기본 꼴(`injectBaseStyles`)을 한 번 넣음 · 확인창 · 글 묻기를 다시 냄. + * React 화면은 부품 클래스(`ui-btn` · `ui-icon-btn` · `ui-toggle--chip` · `ui-tabs` · `ui-float-panel` · `ui-ctx-menu` · `ui-empty` · `ui-text` · `ui-input`)와 + * `data-size` 단을 직접 붙임 · 자리 · 폭은 App.css 의 `cad-*` 가 맡음 · 도면 그림 색(`--draw-*`)은 안 건드림. + */ +import '../../../../ui_template/ui_template_tabs.css'; +import '../../../../ui_template/ui_template_toggle.css'; +import '../../../../ui_template/ui_template_float_panel.css'; +import '../../../../ui_template/ui_template_context_menu.css'; +import '../../../../ui_template/ui_template_empty.css'; +import '../../../../ui_template/ui_template_input.css'; +import '../../../../ui_template/ui_template_text.css'; +import { injectBaseStyles } from '../../../../ui_template/ui_template_elements_styles.ts'; + +export { askConfirm, askText } from '../../../../ui_template/ui_template_modal.ts'; + +injectBaseStyles(); diff --git a/B07_DesignDetail/openwebcad/src/main.tsx b/B07_DesignDetail/openwebcad/src/main.tsx index 38280e159..d5c4c0e7f 100644 --- a/B07_DesignDetail/openwebcad/src/main.tsx +++ b/B07_DesignDetail/openwebcad/src/main.tsx @@ -3,6 +3,8 @@ import { Point } from '@flatten-js/core'; import React from 'react'; import ReactDOM from 'react-dom/client'; import { Actor, type MachineSnapshot } from 'xstate'; +// 부품 상자 CSS 를 앱 CSS 보다 먼저 실음 — 자리 · 폭은 뒤에 오는 App.css 의 `cad-*` 가 덮음 +import './integration/ui-kit.ts'; import { SNAP_POINT_DISTANCE } from './App.consts'; import App from './App.tsx'; import { TOOL_STATE_MACHINES } from './commands/registry'; diff --git a/B07_DesignDetail/openwebcad/src/ribbon/Ribbon.tsx b/B07_DesignDetail/openwebcad/src/ribbon/Ribbon.tsx index d3ec81f83..10a17d975 100644 --- a/B07_DesignDetail/openwebcad/src/ribbon/Ribbon.tsx +++ b/B07_DesignDetail/openwebcad/src/ribbon/Ribbon.tsx @@ -129,10 +129,14 @@ const Panel: FC<{ {overflowCommands.length ? ' ▾' : ''} {overflowAt && overflowCommands.length > 0 && ( -
+
{overflowCommands.map((command) => (