- 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
77 lines
2.6 KiB
TypeScript
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);
|
|
}
|