Files
Aislo/B07_DesignDetail/openwebcad/src/inputController/snap-menu.ts
T
eomsangdonandClaude Opus 5.5 de250ab759 refactor(cad): CAD 앱 겉 자체 변종을 부품 상자 부품으로 — 상태막대 켜기 칩 · 패널 탭 · 떠 있는 판 · 메뉴 · 아이콘 단추 · 확인창 (PLAN 553-6)
- 부품 상자 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 <noreply@anthropic.com>
2026-10-26 22:35:43 +09:00

78 lines
2.7 KiB
TypeScript

/**
* Shift+우클릭 한 번 스냅 메뉴(337 마 · 오토캐드) — 점 물음일 때만 · 고르면 그 이름을 친 것과 같은 길(`submitText`).
* 스냅 이름 · FROM · M2P 의 뜻은 친 글 사다리(`prompt-input.ts`) 몫 · 메뉴는 고르기만.
*/
import './snap-menu.css';
/** [친 글, 이름] — 오토캐드 객체 스냅 메뉴 차례 */
export const SNAP_MENU: readonly [string, string][] = [
['FROM', '시작점에서'],
['M2P', '두 점 사이의 중간'],
['END', '끝점'],
['MID', '중간점'],
['INT', '교차점'],
['CEN', '중심'],
['QUA', '사분점'],
['PER', '수직'],
['NOD', '노드'],
['NEA', '근처점'],
['NON', '없음'],
];
let current: { onPick: (name: string) => void; element: HTMLElement | null } | null = null;
const onKey = (evt: KeyboardEvent) => {
if (evt.key !== 'Escape') return;
evt.preventDefault();
evt.stopImmediatePropagation(); // Esc = 메뉴만 닫음(명령은 그대로)
closeSnapMenu();
};
const onOutside = (evt: MouseEvent) => {
if (!current?.element?.contains(evt.target as Node)) closeSnapMenu();
};
export const isSnapMenuOpen = (): boolean => current !== null;
export function closeSnapMenu(): void {
if (!current) return;
current.element?.remove();
current = null;
if (typeof window === 'undefined') return;
window.removeEventListener('keydown', onKey, true);
window.removeEventListener('mousedown', onOutside, true);
}
/** 메뉴에서 하나 고름 — 닫고 그 이름을 넘김 */
export function pickSnapMenu(name: string): void {
const picked = current;
closeSnapMenu();
picked?.onPick(name);
}
/** 화면 (x, y) 에 메뉴를 띄움 — DOM 없는 시험에선 상태만 */
export function openSnapMenu(x: number, y: number, onPick: (name: string) => void): void {
closeSnapMenu();
current = { onPick, element: null };
if (typeof document === 'undefined') return;
const menu = document.createElement('div');
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));
menu.append(item);
}
document.body.append(menu);
// 화면 밖으로 안 나가게
const box = menu.getBoundingClientRect();
menu.style.left = `${Math.min(x, window.innerWidth - box.width - 4)}px`;
menu.style.top = `${Math.min(y, window.innerHeight - box.height - 4)}px`;
current.element = menu;
window.addEventListener('keydown', onKey, true);
window.addEventListener('mousedown', onOutside, true);
}