feat(종단보기): 346-2 덧 2 B05 종단 보기 · 3D 측점선도 갈아끼운 상세로 — 보일 때만 · 숨었으면 다시 보일 때 · 횡단 보기 없이도

- design_view_profile_sections: 노선마다 상세 갈아끼움을 듣고 종단도 · 표 · 3D 측점선 다시(보일 때만 · 숨으면 flushSwap 이 다시 보일 때) · 가게 stations 도 들어 횡단 보기를 안 열어도 갈아끼움(먼저 부른 쪽만 · 다음은 「같은 목록」)
- design_view_profile: 받는 줄만(보임 · 떠날 때 끊음 · 다시 보일 때 flushSwap)
- 갈아끼움 알림판 B06_Section_Swap_Bus(가져오기 없음 · 돌고 도는 가져오기 막음) — 조정창 값 · 암 경계 옮김을 어느 보기가 갈아끼웠든 한 번 · 횡단 보기는 알림으로 다시 그림
- 시험: 종단 보기 보임 7 → 8 · 숨음 = 안 그림 · 다시 보임 = 6 · 3D 측점선 같은 차례 · 옛 시험 둘(342-1 화면 선 = listedBase · 323-9 열쇠 모듈 진짜로) 맞춤

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QfHKQs7J3hQELKMWQMUJEy
This commit is contained in:
2026-10-16 12:28:11 +09:00
co-authored by Claude Opus 5.5
parent 69d84e5ca9
commit da3dfc1ca8
13 changed files with 168 additions and 39 deletions
+3
View File
@@ -259,7 +259,9 @@ export async function mountProfileView(
panel: () => panel,
profilePanel: () => profilePanel,
bridge: () => bridge,
visible: () => host.visible(), // 숨은 동안 갈아끼운 상세(346-2)는 다시 보일 때 그림
});
host.onDispose(sections.stopSwap);
const { restorePanel, renderLatest, restoreSections } = sections;
const view3d = createProfile3d({
projectId: activeProjectId,
@@ -390,6 +392,7 @@ export async function mountProfileView(
ready: () => !state.restoring,
// 다시 보이면 횡단 보기가 한 벌(폼 · 종단도)에 실어 둔 값을 종단 보기 값으로 되돌린다.
reload: async () => {
sections.flushSwap(); // 숨은 동안 측점 목록이 바뀌었으면 종단도 · 3D 측점선 다시(346-2)
await bridge.load();
if (lastPipes) applyPipes(lastPipes);
},
@@ -13,7 +13,12 @@ import type { createRouteProfilePanel } from "../../B05_Profile/B05_Profile_UI_P
import type { createStructuresBridge } from "../../B05_Profile/B05_Profile_UI_Page_Structures";
import type { RouteViewer } from "../../B05_Profile/B05_Profile_UI_Viewer";
import type { SectionDetailResponse } from "../../B06_Section/B06_Section_Api_Fetch";
import { loadSectionDetail } from "../../B06_Section/B06_Section_Section_Store";
import {
loadSectionDetail,
subscribeSectionDetail,
swapSectionStations,
} from "../../B06_Section/B06_Section_Section_Store";
import { useDesignStore } from "./design_store";
import { migrateLegacyStations } from "../../B05_Profile/B05_Profile_Api_Structures";
import { loadCorridorIfFresh } from "../../B05_Profile/B05_Profile_UI_Corridor";
import { stationLinesOf } from "../../B05_Profile/B05_Profile_UI_Page_Stations";
@@ -42,6 +47,8 @@ export interface ProfileSectionsDeps {
panel: () => RoutePanel;
profilePanel: () => ReturnType<typeof createRouteProfilePanel>;
bridge: () => ReturnType<typeof createStructuresBridge>;
/** 종단 보기가 보이나 — 숨은 동안 갈아끼운 상세는 다시 보일 때 그림(346-2 · 없으면 늘 보임). */
visible?: () => boolean;
}
/** 구 구조물 측점 이관을 이미 시도한 노선 — 탭 수명 동안 유지한다. */
@@ -114,6 +121,7 @@ export function createProfileSections(deps: ProfileSectionsDeps) {
return;
}
profilePanel.setLoading(null);
followSwap(routeId);
try {
renderSections(detail, routeId);
// 구 확정분에 남은 비정규 측점(자유 텍스트)을 구조물 정본으로 1회 이관한다 — 멱등이지만
@@ -154,6 +162,38 @@ export function createProfileSections(deps: ProfileSectionsDeps) {
}
}
/* 측점 수가 바뀜(346-2 · 묶음 측점 → 횡단 쪽이 상세를 갈아끼움) — 종단도 · 표 · 3D 측점선을 새 목록으로
* 다시(보일 때만 · 숨었으면 다시 보일 때 `flushSwap`). 노선마다 한 번 듣고, 노선이 바뀌면 옛 것을 끊음. */
let swap: { routeId: number; stop: () => void } | null = null;
let swapPending = false;
function followSwap(routeId: number): void {
if (swap?.routeId === routeId) return;
swap?.stop();
const stopListen = subscribeSectionDetail(projectId, routeId, (next) => {
state.detail = next;
swapPending = !(deps.visible?.() ?? true);
if (!swapPending) renderSections(next, routeId);
});
// 묶음 측점(가게)이 바뀌면 갈아끼움 — 횡단 보기를 안 열었어도(그쪽도 부르면 둘째는 「같은 목록」)
const stopStore = useDesignStore(projectId, null).subscribe("stations", (stations) => {
if (stations?.length) void swapSectionStations(projectId, routeId, stations);
});
swap = {
routeId,
stop: () => {
stopListen();
stopStore();
},
};
}
/** 숨은 동안 받은 새 상세를 그림 — 종단 보기가 다시 보일 때. */
function flushSwap(): void {
if (!swapPending || !state.detail || !swap) return;
swapPending = false;
renderSections(state.detail, swap.routeId);
}
/** 「3D 늦음」 — [3D 업데이트] 표시(setCorridorPending 그대로)와 상태 한 칸을 같이 맞춘다. */
function setCorridorPending(pending: boolean): void {
state.corridorPending = pending;
@@ -184,5 +224,7 @@ export function createProfileSections(deps: ProfileSectionsDeps) {
restoreSections,
renderLatest,
setCorridorPending,
flushSwap,
stopSwap: () => swap?.stop(),
};
}