Files
Aislo/B05_Profile/B05_Profile_UI_Structure_Pick_Session.ts
T
eomsangdonandClaude Fable 5 738e09e7b1 feat(B05/B06): 구조물 선택 화면 전체 활성화 + 세션 상시 공유
- 바깥 선택이 폼에 실릴 때 접힌 좌측 패널 자동 펼침(onReveal 창구)
- 계곡 통과 시설 목록 행도 보이는 자리로 스크롤(rAF)
- 종단 테이블 구조물 구간 판정을 허용오차 비교로 교체 + 병합 측점 포함(강조 0건→6건)
- 구조물 선택 세션을 소비형에서 유지형으로 — B06 카드·목록·부재 선택도 같은 칸에 기록,
  양쪽 진입 시 복원(목록 지연 로드 대비 이중 복원)
- 조정창 닫기가 세션 선택까지 지우던 결함 수정(부재 해제와 측점 해제 분리)
- B05 페이지 700줄 유지 위해 상단측 세션 보관을 Page_Helpers로 이동(동작 불변)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-04 16:27:35 +09:00

123 lines
5.4 KiB
TypeScript

/* =============================================================================
* B05_Profile_UI_Structure_Pick_Session.ts
* 3D에서 고른 구조물을 **좌측 「구조물 배치」 패널로 넘기고**, 그 선택을 세션 한 칸에
* 남겨 **B06 진입 때 같은 측점 카드가 열리게** 한다(2026-09-04 사용자 확정).
*
* B05·B06은 라우터가 따로 띄우는 화면이라 실시간 양방향이 아니다 — 조작 상태는 캐시
* 몫이라는 데이터 3층 규칙대로 sessionStorage 한 칸에 남긴다. 값은 **지우지 않고 남긴다**
* (2026-09-04 사용자: 두 화면이 한 페이지처럼 움직여야 함) — B06에서 고른 것도 같은 칸에
* 적혀, 어느 쪽으로 오가든 마지막 선택이 그대로 살아 있다.
* ========================================================================== */
import type { StructurePick, StructurePickControls } from "./B05_Profile_UI_Viewer_Structure_Pick";
import type { StructuresSection } from "./B05_Profile_UI_Structures_Panel_Types";
import type { StructureInstance } from "./B05_Profile_Api_Structures";
/** B06으로 넘기는 선택 — B06이 쓰는 `{ 측점, 부재키 }` 한 쌍과 같은 모양이다. */
export interface StructurePickHandoff {
at: number;
key?: string;
}
const sessionKey = (projectId: string): string => `aislo:structure-pick:${projectId}`;
/** 세션에 남긴다(선택 해제면 지운다). */
export function rememberStructurePick(projectId: string | null, pick: StructurePick | null): void {
if (!projectId) return;
try {
if (!pick) {
window.sessionStorage.removeItem(sessionKey(projectId));
return;
}
const handoff: StructurePickHandoff = { at: pick.chainageM, key: pick.key };
window.sessionStorage.setItem(sessionKey(projectId), JSON.stringify(handoff));
} catch {
/* 세션 저장소가 막힌 환경 — 화면 선택만 살고 넘김은 포기한다. */
}
}
/** 화면에서 고른 것을 세션에 적는다 — B06 쪽 창구(측점만 고르면 부재키는 비운다). */
export function writeStructurePick(
projectId: string | null,
at: number | null,
key?: string,
): void {
if (!projectId) return;
try {
if (at === null) window.sessionStorage.removeItem(sessionKey(projectId));
else window.sessionStorage.setItem(sessionKey(projectId), JSON.stringify({ at, key }));
} catch {
/* 세션 저장소가 막힌 환경 — 화면 선택만 살고 넘김은 포기한다. */
}
}
/** 세션에 남은 선택을 읽는다(지우지 않는다). 없으면 null. */
export function readStructurePick(projectId: string | null): StructurePickHandoff | null {
if (!projectId) return null;
try {
const raw = window.sessionStorage.getItem(sessionKey(projectId));
if (!raw) return null;
const value = JSON.parse(raw) as Partial<StructurePickHandoff>;
if (typeof value.at !== "number" || !Number.isFinite(value.at)) return null;
return { at: value.at, key: typeof value.key === "string" ? value.key : undefined };
} catch {
return null;
}
}
/** 구간형 구조물이 그 누가거리를 덮는가(기준점형은 ±0.51m 안). */
function covers(entry: StructureInstance, chainageM: number): boolean {
const start = entry.start_m ?? entry.chainage_m;
const end = entry.end_m ?? entry.chainage_m;
if (start == null || end == null) return false;
return chainageM >= Math.min(start, end) - 0.51 && chainageM <= Math.max(start, end) + 0.51;
}
/**
* 3D 클릭 결과를 화면 전체에 적용한다.
*
* 배수관 세트(기슭막이·집수정·배관)는 `selectStation`(=기존 선택 동기화)이 좌측 폼·종단
* 그래프·3D 마커·유역도를 한 줄로 맞춘다. 그 줄에서 아무것도 안 잡히면 옛 정본 구조물로
* 보고 구조물 id로 연다.
*/
export function wireStructurePick(
controls: StructurePickControls,
projectId: string | null,
structures: StructuresSection,
/** 기존 선택 동기화(`selectStationOfPipe`) — 누가거리 하나로 전 화면을 맞춘다. */
selectStation: (chainageM: number | null) => void,
): void {
controls.onPick = (pick: StructurePick | null): void => {
rememberStructurePick(projectId, pick);
if (!pick) {
selectStation(null);
return;
}
selectStation(pick.chainageM);
if (structures.hasSelection()) return;
const hit = structures.getStructures().find((entry) => covers(entry, pick.chainageM));
structures.selectById(hit?.structure_id ?? null);
};
}
/**
* 화면에 들어올 때 세션에 남은 선택을 되살린다 — B06에서 고른 것도 그대로 이어 받는다
* (2026-09-04 사용자: 두 화면이 한 페이지처럼). 3D 강조는 코리도가 뜬 뒤 자동으로 다시
* 칠해지므로 여기서는 선택만 세워 둔다.
*
* **되살렸으면 true.** 관 목록이 아직 안 실렸으면 폼이 그 시설을 못 찾으므로 false를
* 돌려준다 — 부르는 쪽은 목록이 실릴 때마다 다시 부르면 된다.
*/
export function restoreStructurePick(
controls: StructurePickControls,
projectId: string | null,
structures: StructuresSection,
selectStation: (chainageM: number | null) => void,
): boolean {
const handoff = readStructurePick(projectId);
if (!handoff) return true; // 되살릴 것이 없으면 끝난 것으로 본다.
controls.select({ chainageM: handoff.at, kind: "", key: handoff.key });
selectStation(handoff.at);
return structures.hasSelection();
}