Files
Aislo/B07_DesignDetail/openwebcad/src/inputController/snap-menu.ts
T
eomsangdonandClaude Opus 5.5 66279024d8 feat(B07 CAD): 337-2 마-2 ZOOM 덧물음 · Shift+우클릭 스냅 메뉴 · 고르기 ALL/L/P · 그립 종료(X) · ↶↷ 끊고 되돌림 · snapped 캐스트 지움
- ZOOM(Z): 「윈도우 구석 지정, 축척 비율 (nX 또는 nXP) 입력 또는 [전체(A)/범위(E)/이전(P)/윈도우(W)] <실시간>:」 덧물음 · E · A = 범위 · P = 앞 화면 · W · 누름 = 두 구석 윈도우 · 2X = 배율 곱 · 빈 Enter = 실시간 안내 · 중심 · 동적 · 축척 · 객체 = 없음 안내 · 'Z 투명 · ⛶ 단추 = 바로 범위
- 약속 더함: SubPrompt.onText(꼴이 정해지지 않은 글 · nX) — routeTyped 가 덧물음에 먼저 물음 · undefined = 사다리 그대로(브레인 알림)
- Shift+우클릭: 점 물음에서만 한 번 스냅 메뉴(시작점에서 · 두 점 사이의 중간 · 끝점 · 중간점 · 교차점 · 중심 · 사분점 · 수직 · 노드 · 근처점 · 없음) → 고른 이름을 친 것과 같은 길 · Esc · 바깥 누름 = 메뉴만 닫음(ic/snap-menu.ts)
- 고르기 숨은 키워드 ALL · L · P(선택 도구 첫 점 대기 · 쉼 · 명령 안 자식) — 앞 고름에 더함 · 「n개를 찾음, 총 m개」
- 그립 물음 새 꼴 「** 신축 ** 신축점 지정 또는 [종료(X)]:」 · X = 놓음 · 기준점(B) · 복사(C) · 명령 취소(U) 없음 안내
- ↶ ↷ 단추 = undoCommand · redoCommand(끊고 되돌림) · 우클릭 = 명령 중 Enter · 쉴 때 반복(가-1 길 · 시험)
- 시험: mouse-acad 마-2 더함(21)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf
2026-10-16 00:11:53 +09:00

77 lines
2.6 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 = '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);
}