/** * 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 = 'cad-snap-menu controls'; menu.setAttribute('role', 'menu'); for (const [name, label] of SNAP_MENU) { const item = document.createElement('button'); item.type = 'button'; 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); }