/* ============================================================================= * B06_Section_UI_Section_View_Menu.ts * B06 종단 그래프 위 **우클릭 메뉴** — B05 와 같은 부품(`mountStructureMenu`)을 그대로 쓴다. * * 왜 여기서도 여나(2026-09-12 사용자) — B05·B06 은 한 페이지다. 같은 그래프를 보면서 * 한쪽에서만 넣고 뺄 수 있으면 사용자가 화면을 오가야 하고 직관도 깨진다. * * 정본에 바로 쓰지 않는다 — 추가·삭제·이동은 **세션에 예약**하고 [저장]·[확정]에서 * 관 정본(`pipe_points.json`)으로 나간다(CLAUDE.md 5장). 세부 배수유역 재분할은 그때 * 서버가 관 목록을 다시 받아 처리한다. * ========================================================================== */ import type { StructureInstance, StructureType } from "../B05_Profile/B05_Profile_Api_Structures"; import { structureAnchorM } from "../B05_Profile/B05_Profile_Api_Structures"; import type { IrregularStation } from "../B05_Profile/B05_Profile_UI_IrregularStations"; import { mountStructureMenu } from "../B05_Profile/B05_Profile_UI_Profile_Structures"; /** 종단 그래프에서 구조물을 넣고 빼는 길 — 페이지가 물려 준다. 없으면 메뉴를 안 붙인다. */ export interface SectionStructureEdit { /** 관(계곡 통과 시설)을 그 자리에 넣는다. */ addPipe: (chainageM: number) => void; /** 관을 뺀다. */ removePipe: (chainageM: number) => void; /** 레지스트리 종류를 골라 구조물을 넣는다(좌측 「구조물 배치」와 같은 2단 메뉴). */ addStructureType: (chainageM: number, typeId: string) => void; /** 구조물(관 아님)을 뺀다. */ removeStructure: (structureId: string) => void; } /** 알약·우클릭이 쓰는 「관인가」 판정 — 관은 정본이 달라 지우는 길도 다르다. */ export function isPipeMark(structure: StructureInstance): boolean { return String(structure.structure_id ?? "").startsWith("pipe-"); } /** * 종단 그래프 칸에 우클릭 메뉴를 붙인다. 그래프는 그릴 때마다 새로 만들어지므로 이 * 함수도 그때마다 다시 부른다 — 상태를 밖에 남기지 않는다(B05 와 같은 규칙). */ export function mountSectionStructureMenu( host: HTMLElement, input: { structures: ReadonlyArray; types: ReadonlyArray; x: (chainageM: number) => number; chainageAt: (px: number) => number; maxChainageM: number; edit: SectionStructureEdit; }, ): void { // 메뉴가 쓰는 최소 정보만 옮겨 담는다 — B06 에는 B05 의 비정규 측점 목록이 없다. const stations: IrregularStation[] = input.structures.map((structure) => { const chainage = structureAnchorM(structure); const pipe = isPipeMark(structure); return { id: String(structure.structure_id), station: 0, remainder: 0, chainage_m: chainage, structure: input.types.find((type) => type.type_id === structure.type_id)?.name ?? String(structure.type_id), origin: pipe ? "pipe" : "user", } as IrregularStation; }); mountStructureMenu(host, { stations, x: input.x, chainageAt: input.chainageAt, maxChainageM: input.maxChainageM, onRemove: (station) => { if (station.origin === "pipe") input.edit.removePipe(station.chainage_m); else input.edit.removeStructure(station.id); }, onAddPipe: (chainageM) => input.edit.addPipe(chainageM), structureTypes: input.types.map((type) => ({ type_id: type.type_id, group: type.group, name: type.name, })), onAddStructureType: (chainageM, typeId) => input.edit.addStructureType(chainageM, typeId), }); }