Files
Aislo/B05_Profile/B05_Profile_UI_Panel_View.ts
T
eomsangdonandClaude Opus 5.5 baea638043 refactor(B05): 날 단추 · 작은 도구 단추를 부품 단추 · 아이콘 단추 · 켜기 칩 · 목록 줄로 (PLAN 553-2)
- 종단 도구 · [창 분할] = 단추 유리 · 보조 단 18 · ▲▼ = 아이콘 단추 선 18 · 배율 + − ⤢ = 아이콘 단추 22
- 배수 머리 단추 = 단추 22 · 층 단추 = 켜기 칩 20(색 점 = 층 색) · 유역 줄 = 목록 줄 32
- [추가] · [삭제] · [리셋] · [해제] · 확폭 = 단추 주 · 위험 고스트 · 보조 · [-][+] = 아이콘 단추 24
- 지도 도구줄 · 3D 보기 줄 = 단 32(34 에서) · 접기 ▾ = 아이콘 단추 18 · 잰 값 닫기 = 아이콘 단추 24
- 페이지 CSS 의 단추 꼴(테두리 · 바탕 · 글 · 둥글기) 지움 · 날 색 대체값(#d98a00 · #5b8def · #3a3f4a) 지움
- 시험 선택자 · 가짜 DOM(setAttribute) 같이 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 22:51:58 +09:00

124 lines
5.7 KiB
TypeScript

/* =============================================================================
* B05_Profile_UI_Panel_View.ts
* 3D 화면 위에 얹는 **보기 조작 줄**(ISO·TOP·… · 표시 켬끔 · 뷰 초기화 · [3D 업데이트]).
* 좌측 패널 칸이 아니라 뷰어에 붙는다 — `B05_Profile_UI_Panel.ts` 에서 떼어냈다(계획서 24-2b).
* ========================================================================== */
import { readStateRaw, writeStateRaw } from "../A00_Common/b_page_state";
import { calcOnServerNow } from "../A00_Common/design/design_calc_sites";
import { L, button, toggleButton } from "./B05_Profile_UI_Panel_Fields";
/** 뷰 조작 줄이 부르는 것 — 좌측 패널 콜백의 일부다. */
export interface ViewControlCallbacks {
onSurfaceVisible: (visible: boolean) => void;
onCorridorVisible: (visible: boolean) => void;
onContoursVisible: (visible: boolean) => void;
onAxesVisible: (visible: boolean) => void;
onStationLinesVisible: (visible: boolean) => void;
onStationLabelsVisible: (visible: boolean) => void;
onSurfaceGrayscale: (grayscale: boolean) => void;
onView: (view: "iso" | "top" | "front" | "side") => void;
onProjection: (kind: "perspective" | "ortho") => void;
onResetView: () => void;
onCorridorRefresh: () => void | Promise<void>;
}
export function createViewControls(callbacks: ViewControlCallbacks): {
viewControls: HTMLElement;
/** [3D 업데이트] 대기 표시 — 편집이 3D에 아직 안 들어갔음을 버튼에 알린다. */
setCorridorPending: (pending: boolean) => void;
} {
const viewControls = document.createElement("div");
viewControls.className = "b05-route__view-controls";
const viewButtons = document.createElement("div");
viewButtons.className = "b05-route__view-group";
(["iso", "top", "front", "side"] as const).forEach((preset) =>
viewButtons.append(button(preset.toUpperCase(), () => callbacks.onView(preset), "glass")),
);
// 원근·직교 두 단추 — 지금 켜진 쪽을 강조한다. 기본은 원근 · 고른 값은 화면 설정에 기억.
const initialProjection = readStateRaw("b05-3d-projection") === "ortho" ? "ortho" : "perspective";
const projectionButtons = (
[
["perspective", "원근"],
["ortho", "직교"],
] as const
).map(([kind, label]) => {
const element = button(
label,
() => {
projectionButtons.forEach(({ kind: other, element: el }) => {
el.classList.toggle("is-active", other === kind);
el.setAttribute("aria-pressed", String(other === kind));
});
writeStateRaw("b05-3d-projection", kind);
callbacks.onProjection(kind);
},
"glass",
);
element.classList.toggle("is-active", kind === initialProjection);
element.setAttribute("aria-pressed", String(kind === initialProjection));
return { kind, element };
});
viewButtons.append(...projectionButtons.map(({ element }) => element));
if (initialProjection === "ortho") callbacks.onProjection("ortho");
const visibilityButtons = document.createElement("div");
visibilityButtons.className = "b05-route__view-group";
visibilityButtons.append(
toggleButton("지표면", true, callbacks.onSurfaceVisible),
// 예상형상(2026-08-23): ON=공사 후 형상(기본), OFF=원지반 완전체.
toggleButton("예상형상", true, callbacks.onCorridorVisible),
// 등고선 · 흑백 지형 기본 꺼짐(176-2) — 뷰어 처음 값과 맞춘다.
toggleButton("등고선", false, callbacks.onContoursVisible),
toggleButton("축 표시", false, callbacks.onAxesVisible),
toggleButton(L("B05_Route_Field_StationLines"), true, callbacks.onStationLinesVisible),
toggleButton(L("B05_Route_Field_StationLabels"), true, callbacks.onStationLabelsVisible),
// 무지개 고도색이 헷갈릴 때 명도만 남기는 흑백 표시(2026-08-05 사용자 요청).
toggleButton("흑백 지형", false, callbacks.onSurfaceGrayscale),
);
// [3D 업데이트](2026-09-01 사용자 지시) — 계획선을 고칠 때마다 3D가 따라오면 조작이
// 무거워, 갱신을 이 버튼으로 모았다. 편집이 쌓이면 is-pending으로 알리고, 누르는 동안은
// 이 버튼만 잠근다(화면 전체 잠금 금지 — CLAUDE.md 5장).
const corridorRefresh = button(
"3D 업데이트",
() => {
corridorRefresh.disabled = true;
// D3 = 서버 = 서버가 짓는 동안 옛 모형 그대로(508-2)
corridorRefresh.textContent = calcOnServerNow("corridor_mesh")
? "서버에서 짓는 중…"
: "갱신 중…";
void Promise.resolve(callbacks.onCorridorRefresh()).finally(() => {
corridorRefresh.disabled = false;
corridorRefresh.textContent = "3D 업데이트";
corridorRefresh.classList.remove("is-pending");
});
},
"glass",
);
corridorRefresh.title =
"계획선 편집을 3D 예상형상·측점선에 반영합니다.\n편집이 쌓이면 버튼에 표시가 붙습니다.";
const separator1 = document.createElement("span");
separator1.className = "b05-route__view-separator";
const separator2 = separator1.cloneNode() as HTMLSpanElement;
const separator3 = separator1.cloneNode() as HTMLSpanElement;
viewControls.append(
viewButtons,
separator1,
visibilityButtons,
separator2,
button("뷰 초기화", callbacks.onResetView, "glass"),
separator3,
corridorRefresh,
);
// 보기 줄 단추 = 부품 단추 단 32(553-2)
viewControls
.querySelectorAll<HTMLElement>(".ui-btn")
.forEach((b) => b.setAttribute("data-size", "32"));
return {
viewControls,
setCorridorPending(pending: boolean) {
corridorRefresh.classList.toggle("is-pending", pending);
},
};
}