Files
Aislo/B05_Profile/B05_Profile_UI_RouteEdit_Chrome.ts
T
eomsangdonandClaude Opus 5.5 207c2e40eb feat(B05): 계획노선 편집 창 — 측점 사이 기울기 라벨 · 1m 등고선 켬끔 · 보던 화면 기억
- 측점 사이 종단기울기(지반고 %) 라벨 — 측점 간격이 화면 90px 이상일 때만 · 측점 라벨 맞은편 · 회전해도 글 바로 섬 · 끄는 동안 안 물음
- 「1m 등고선」 단추(기본 꺼짐 · LAS 없으면 잠김) — 1m 선 얇고 옅게 · 켰을 때 처음 한 번만 준비 · 5m 선은 한 단 짙게(--map-contour-major)
- 회전 · 가운데 · 배율을 화면 취향(routeedit-view · 프로젝트마다 · 계정에도 올라감)에 적고 다시 열면 그대로
- 700줄 한도 — 화면 셈·기억은 _View, 재기 배선은 _Measure, 바탕 읽기는 _Contour, 노드 끼우기·빼기는 _Edits 로 나눔
- 시험 test_b05_routeedit_view.py (창 크기 달라도 같은 자리 · 저장소 왕복 · 노드 편집값 자리)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sCeZz8z8YnbovsKgn7HK7
2026-09-29 09:33:57 +09:00

115 lines
6.2 KiB
TypeScript

/* =============================================================================
* B05_Profile_UI_RouteEdit_Chrome.ts
* 계획노선 편집 모달의 **뼈대** — 창·단추·오버레이 판을 만들고 자주 쓰는 요소를 집어 준다.
*
* `B05_Profile_UI_RouteEdit.ts` 가 700줄을 넘겨 떼어낸 조각이다(2026-09-12). 생김새만 있고
* 동작은 없다 — 배선은 본체와 각 조각(`_Apply`·`_Rotate`·`_History`…)이 한다.
*
* **배치**(2026-09-12 사용자 지시 ⑩~⑬·⑱) — 아래 정보행을 없애고 지도 위 오버레이로 옮겼다.
* 단추는 제목행 오른쪽, 조작 설명은 지도 왼쪽 위 2열, 상태·범례는 왼쪽 아래, 잰 값은 오른쪽
* 아래. 메인 창은 왼쪽으로 밀고 오른쪽 세로 칸에 횡단 두 판이 앉는다.
* ========================================================================== */
/** 회전 단추 아이콘 — **반만 도는 화살표**(2026-09-12 사용자 지시 ㉙). 한 바퀴를 다 그린
* 기호(`↺`·`↻`)는 「한 바퀴 돈다」로 읽혀 한 칸씩 도는 동작과 안 맞았다. */
const HALF_TURN_ICON = {
ccw: `<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" fill="none"
stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<path d="M13 8a5 5 0 0 0-10 0" /><path d="M3 8 1.2 5.6" /><path d="M3 8 5.4 6.6" /></svg>`,
cw: `<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" fill="none"
stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round">
<path d="M3 8a5 5 0 0 1 10 0" /><path d="M13 8 14.8 5.6" /><path d="M13 8 10.6 6.6" /></svg>`,
};
export interface RouteEditChrome {
overlay: HTMLElement;
canvas: HTMLCanvasElement;
status: HTMLElement;
busy: HTMLElement;
measureBox: HTMLElement;
measureText: HTMLElement;
measureButton: HTMLButtonElement;
minorContourButton: HTMLButtonElement;
}
/** 모달을 만들어 `document.body` 에 붙이고, 자주 쓰는 요소를 집어 돌려준다. */
export function createRouteEditChrome(): RouteEditChrome {
const overlay = document.createElement("div");
overlay.className = "b05-routeedit";
overlay.innerHTML = `
<div class="b05-routeedit__box" role="dialog" aria-label="계획노선 편집">
<div class="b05-routeedit__head">
<strong>계획노선 편집</strong>
<span class="b05-routeedit__actions">
<button type="button" class="ui-btn ui-btn--ghost" data-act="contour-minor"
aria-pressed="false" disabled
title="1m 간격 등고선을 얇고 옅게 겹칩니다 (확대하면 보임)">1m 등고선</button>
<button type="button" class="ui-btn ui-btn--ghost" data-act="measure"
title="노선 위 두 점을 눌러 거리·기울기를 잽니다 (Shift+클릭도 같음)">거리 재기</button>
<i class="b05-routeedit__divider" aria-hidden="true"></i>
<button type="button" class="ui-btn ui-btn--ghost" data-act="undo"
title="되돌리기 (Ctrl+Z)" disabled>↶ 되돌리기</button>
<button type="button" class="ui-btn ui-btn--ghost" data-act="redo"
title="다시하기 (Ctrl+Y)" disabled>↷ 다시하기</button>
<button type="button" class="ui-btn ui-btn--ghost" data-act="history-reset"
title="이 창을 연 상태로 되돌립니다 (재계산 없음)" disabled>초기화</button>
<button type="button" class="ui-btn ui-btn--ghost" data-act="reset">예상노선으로</button>
<button type="button" class="ui-btn ui-btn--ghost" data-act="cancel">취소</button>
<button type="button" class="ui-btn ui-btn--filled" data-act="apply">확인</button>
</span>
<button type="button" class="b05-routeedit__close" aria-label="닫기">✕</button>
</div>
<div class="b05-routeedit__canvas-wrap">
<canvas class="b05-routeedit__canvas"></canvas>
<div class="b05-routeedit__hint">
<span>노드 끌기 = 옮기기</span><span>노드 클릭 = R 라벨</span>
<span>선 두 번 클릭 = 노드 추가</span><span>노드 오른쪽 클릭 = 삭제</span>
<span>측점 눈금 클릭 = 횡단 미리보기</span><span>Shift+클릭 = 거리·기울기</span>
<span>가운데(휠) 버튼 끌기 = 지도 이동</span><span>휠 = 확대</span>
</div>
<div class="b05-routeedit__spin">
<button type="button" class="ui-btn ui-btn--glass" data-act="rotate-ccw"
title="반시계로 돌리기" aria-label="반시계로 돌리기">${HALF_TURN_ICON.ccw}</button>
<button type="button" class="ui-btn ui-btn--glass" data-act="rotate-cw"
title="시계로 돌리기" aria-label="시계로 돌리기">${HALF_TURN_ICON.cw}</button>
</div>
<div class="b05-routeedit__measure" hidden>
<span class="b05-routeedit__measure-text"></span>
<button type="button" class="b05-routeedit__measure-close" aria-label="닫기"
title="닫기">✕</button>
</div>
<div class="b05-routeedit__info">
<span class="b05-routeedit__status">노선을 읽는 중…</span>
<span class="b05-routeedit__legend">
<i class="is-expected"></i> 예상노선(원본)
<i class="is-planned"></i> 계획노선
</span>
</div>
</div>
<div class="b05-routeedit__busy" hidden><span></span></div>
</div>
<div class="b05-routeedit__side"></div>`;
document.body.append(overlay);
const canvas = overlay.querySelector<HTMLCanvasElement>(".b05-routeedit__canvas")!;
const status = overlay.querySelector<HTMLElement>(".b05-routeedit__status")!;
const measureBox = overlay.querySelector<HTMLElement>(".b05-routeedit__measure")!;
const measureText = overlay.querySelector<HTMLElement>(".b05-routeedit__measure-text")!;
const measureButton = overlay.querySelector<HTMLButtonElement>('[data-act="measure"]')!;
const busy = overlay.querySelector<HTMLElement>(".b05-routeedit__busy")!;
const minorContourButton = overlay.querySelector<HTMLButtonElement>(
'[data-act="contour-minor"]',
)!;
return {
overlay,
canvas,
status,
busy,
measureBox,
measureText,
measureButton,
minorContourButton,
};
}