From 161b0a9f782388cf9fd6d8a611879f455623ba39 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 20 Oct 2026 19:51:25 +0900 Subject: [PATCH] =?UTF-8?q?fix(495-1=20CAD):=20=EC=86=90=EA=B0=80=EB=9D=BD?= =?UTF-8?q?=20=EB=8B=A8=EC=B6=94=20=EC=A7=80=EC=9B=80=20=C2=B7=20=EB=A6=AC?= =?UTF-8?q?=EB=B3=B8=20=EB=A7=88=EC=9A=B0=EC=8A=A4=20=EB=88=84=EB=A6=84=20?= =?UTF-8?q?=EC=B4=88=EC=A0=90=20=EB=86=93=EC=9D=8C=20=C2=B7=20=EC=B6=95?= =?UTF-8?q?=EC=B2=99=20=EC=B9=B8=20=EC=95=8C=EB=A6=BC=20=ED=95=9C=20?= =?UTF-8?q?=EC=A4=84=20=C2=B7=20=ED=83=AD=20=EA=B5=AC=EB=B6=84=EC=84=A0=20?= =?UTF-8?q?=EC=A7=84=ED=95=98=EA=B2=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 오른쪽 손가락 단추(Esc · 엔터 · 삭제 · 뒤로 · 다시 · 스냅) 지움 — 끌기 · 집기 동작 그대로 - 리본 nav 마우스 누름 한 곳에서 단추 blur(키보드 초점 남김) · 단추별 blur 셋 걷음 - B07 축척 칸 = 칸 밑 오류 글 숨김 + 줄 아래 알림 한 줄(X · Y 높이 같음) - CAD 탭 줄 밑 선 --color-smoke · 도면 탭 겹판 26 → 25 - 시험: test_cad_touch · test_280_5_b07_settings 고쳐 씀 Co-Authored-By: Claude Sonnet 5.5 --- .../B07_DesignDetail_UI_Settings_Scale.ts | 18 ++++++++---- .../B07_DesignDetail_UI_Style.css | 12 +++++++- B07_DesignDetail/openwebcad/src/App.css | 2 +- .../src/components/QuickAccessBar.tsx | 5 +--- .../src/inputController/input-touch.css | 29 ------------------- .../src/inputController/input-touch.ts | 28 ------------------ .../openwebcad/src/ribbon/Ribbon.tsx | 20 ++++++++----- resources/tester/test_280_5_b07_settings.py | 2 +- resources/tester/test_cad_touch.py | 25 ++++------------ 9 files changed, 45 insertions(+), 96 deletions(-) delete mode 100644 B07_DesignDetail/openwebcad/src/inputController/input-touch.css diff --git a/B07_DesignDetail/B07_DesignDetail_UI_Settings_Scale.ts b/B07_DesignDetail/B07_DesignDetail_UI_Settings_Scale.ts index 28352bd08..c868e41bb 100644 --- a/B07_DesignDetail/B07_DesignDetail_UI_Settings_Scale.ts +++ b/B07_DesignDetail/B07_DesignDetail_UI_Settings_Scale.ts @@ -116,14 +116,22 @@ function buildScaleFields( void save({ x: scaleToSend(x, fields[0].fallback), y: scaleToSend(y, fields[1].fallback) }); }, }); + // 칸 밑 오류 글은 숨기고(틀린 칸만 길어짐) 줄 아래 한 줄로 알림 + const alertLine = document.createElement("p"); + alertLine.className = "b07-scale__error"; + alertLine.setAttribute("role", "alert"); + alertLine.hidden = true; const check = (): void => { - let bad = false; + const errors = new Map(); for (const f of fields) { const result = f.read(); - f.field.setError(typeof result === "string" ? result : undefined); - bad ||= typeof result === "string"; + const message = typeof result === "string" ? result : undefined; + f.field.setError(message); + if (message) errors.set(message, [...(errors.get(message) ?? []), f.axis.toUpperCase()]); } - saveButton.disabled = bad; + alertLine.textContent = [...errors].map(([m, axes]) => `${axes.join(" · ")}: ${m}`).join(" / "); + alertLine.hidden = errors.size === 0; + saveButton.disabled = errors.size > 0; }; for (const f of fields) { f.field.input.addEventListener("input", check); @@ -153,5 +161,5 @@ function buildScaleFields( const buttons = document.createElement("div"); buttons.className = "b07-scale__actions"; buttons.append(reset, saveButton); - section.append(title, row, buttons); + section.append(title, row, alertLine, buttons); } diff --git a/B07_DesignDetail/B07_DesignDetail_UI_Style.css b/B07_DesignDetail/B07_DesignDetail_UI_Style.css index 7ac6e74ca..08a7af3f9 100644 --- a/B07_DesignDetail/B07_DesignDetail_UI_Style.css +++ b/B07_DesignDetail/B07_DesignDetail_UI_Style.css @@ -480,7 +480,7 @@ html.is-bm-route .b07-cad-stack > .cad-tabs { top: 0; right: var(--cad-tab-slot-right, 0px); left: var(--cad-tab-slot-left, 0px); - height: 26px; + height: 25px; padding: 2px 4px 0; border-bottom: none; background: transparent; @@ -522,6 +522,16 @@ html.is-bm-route .b07-cad-stack > .cad-tabs { flex: 1; } +.b07-scale .ui-field__error { + display: none; +} + +.b07-scale__error { + margin: 0; + color: var(--color-danger); + font-size: 12px; +} + .b07-scale__actions { display: flex; justify-content: flex-end; diff --git a/B07_DesignDetail/openwebcad/src/App.css b/B07_DesignDetail/openwebcad/src/App.css index 1554c60cf..c4b983701 100644 --- a/B07_DesignDetail/openwebcad/src/App.css +++ b/B07_DesignDetail/openwebcad/src/App.css @@ -105,7 +105,7 @@ body > canvas[data-id="canvas"] { gap: 2px; height: 26px; padding: 0 8px; - border-bottom: 1px solid var(--cad-line); + border-bottom: 1px solid var(--color-smoke); } .cad-ribbon-tabs__slot { flex: 1; diff --git a/B07_DesignDetail/openwebcad/src/components/QuickAccessBar.tsx b/B07_DesignDetail/openwebcad/src/components/QuickAccessBar.tsx index 046c48930..c99d8e61c 100644 --- a/B07_DesignDetail/openwebcad/src/components/QuickAccessBar.tsx +++ b/B07_DesignDetail/openwebcad/src/components/QuickAccessBar.tsx @@ -19,10 +19,7 @@ export const QuickAccessBar: FC = () => { key={command.id} hidden={QUICK_ACCESS_HIDDEN.includes(id)} title={`${command.label} (${command.id})`} - onClick={(event) => { - event.currentTarget.blur(); - runCommand(command); - }} + onClick={() => runCommand(command)} > {command.glyph} diff --git a/B07_DesignDetail/openwebcad/src/inputController/input-touch.css b/B07_DesignDetail/openwebcad/src/inputController/input-touch.css deleted file mode 100644 index e60591156..000000000 --- a/B07_DesignDetail/openwebcad/src/inputController/input-touch.css +++ /dev/null @@ -1,29 +0,0 @@ -/* 손가락 기기 명령 단추(기기대응 4-1 · `input-touch.ts`) — 자판 없이 Esc · Enter · 지움 · 되돌리기 · 다시 · 스냅. - 자판 없는 기기(손가락이 주 입력 · 마우스 스침 없음)만 보임 — 터치 노트북엔 안 보임(요구 280-6). 그림 면 오른쪽 가운데에 세로로. */ -.cad-touch-bar { - display: none; -} - -@media (hover: none) and (pointer: coarse) { - .cad-touch-bar { - position: fixed; - top: 50%; - right: 8px; - z-index: 50; - display: flex; - flex-direction: column; - gap: 6px; - transform: translateY(-50%); - } - - .cad-touch-bar > button { - width: 44px; - height: 44px; - border: 1px solid var(--color-border); - border-radius: 8px; - background: var(--color-surface-raised); - color: var(--color-text); - font-size: 18px; - box-shadow: var(--shadow-sm); - } -} diff --git a/B07_DesignDetail/openwebcad/src/inputController/input-touch.ts b/B07_DesignDetail/openwebcad/src/inputController/input-touch.ts index 432cf5b54..75cc9752e 100644 --- a/B07_DesignDetail/openwebcad/src/inputController/input-touch.ts +++ b/B07_DesignDetail/openwebcad/src/inputController/input-touch.ts @@ -2,7 +2,6 @@ import { Point } from '@flatten-js/core'; import { setTouchPickPx } from '../helpers/pick-radius.ts'; import { getCanvas, getScreenCanvasDrawController } from '../state.ts'; import type { InputController } from './input-controller.ts'; -import './input-touch.css'; /** * 손가락 CAD(기기대응 4-1) — 마우스 코드는 그대로 두고 손가락만 여기서 번역한다. @@ -11,7 +10,6 @@ import './input-touch.css'; * · 한 손가락 끌기 = 화면 옮기기 · 두 손가락 = 벌려 확대 + 같이 끌어 옮기기 * · 길게 누름(450ms) = 정밀 고르기 — 손가락 위 70px 에 십자선 · 끌어 맞추고 떼면 그 자리 클릭 * · 고름 반경 = 접촉 크기(최소 24px) · 마우스로 누르면 원래 반경 - * · 자판 없이 쓰는 명령 단추(Esc · Enter · 지움 · 되돌리기 · 다시 · 스냅) — 손가락 기기에서만 보임(App.css) */ const LONG_MS = 450; const SLOP_PX = 10; @@ -115,30 +113,4 @@ export function attachTouch(ctl: InputController): void { }; canvas.addEventListener('pointerup', (e) => lift(e, false)); canvas.addEventListener('pointercancel', (e) => lift(e, true)); - - mountTouchBar(ctl); -} - -/** 자판 없는 명령 단추 — 단축키와 같은 길(`handleKeyStroke`)로 보낸다 */ -function mountTouchBar(ctl: InputController): void { - const bar = document.createElement('div'); - bar.className = 'cad-touch-bar'; - const keys: [string, string, KeyboardEventInit][] = [ - ['Esc', '취소(Esc)', { key: 'Escape' }], - ['↵', '확정(Enter)', { key: 'Enter' }], - ['🗑', '지움(Delete)', { key: 'Delete' }], - ['↶', '되돌리기(Ctrl+Z)', { key: 'z', code: 'KeyZ', ctrlKey: true }], - ['↷', '다시(Ctrl+Y)', { key: 'y', code: 'KeyY', ctrlKey: true }], - ['⌖', '스냅 켬/끔(F3)', { key: 'F3' }], - ]; - for (const [label, title, init] of keys) { - const b = document.createElement('button'); - b.type = 'button'; - b.textContent = label; - b.title = title; - b.setAttribute('aria-label', title); - b.addEventListener('click', () => ctl.handleKeyStroke(new KeyboardEvent('keydown', init))); - bar.append(b); - } - document.body.append(bar); } diff --git a/B07_DesignDetail/openwebcad/src/ribbon/Ribbon.tsx b/B07_DesignDetail/openwebcad/src/ribbon/Ribbon.tsx index 954404559..d3ec81f83 100644 --- a/B07_DesignDetail/openwebcad/src/ribbon/Ribbon.tsx +++ b/B07_DesignDetail/openwebcad/src/ribbon/Ribbon.tsx @@ -35,11 +35,7 @@ const CommandButton: FC = ({ command, size, activeTool, onRu title={`${command.label}${command.aliases?.length ? ` (${command.aliases[0]})` : ''}${ command.hint ? ` — ${command.hint}` : '' }`} - onClick={(event) => { - // 초점 테두리가 남아 「선택」과 둘이 켜진 듯 보이지 않게(487) - event.currentTarget.blur(); - onRun(command); - }} + onClick={() => onRun(command)} > {command.glyph} {command.label} @@ -139,8 +135,7 @@ const Panel: FC<{ type="button" key={command.id} title={command.hint} - onClick={(event) => { - event.currentTarget.blur(); + onClick={() => { setOverflowAt(null); onRun(command); }} @@ -205,7 +200,16 @@ export const Ribbon: FC = ({ activeTool, widgets, onCommandRun }) = }; return ( -