refactor(B05): 배관 추가·삭제 우클릭 메뉴를 종단 그래프로 이동
구조물 조작이 테이블과 그래프로 나뉘어 있었다. 이동은 그래프 측점선, 추가·삭제는 테이블이라 어디서 무엇을 하는지 알기 어려웠다. 메뉴를 그래프 칸으로 옮겨 셋을 한 자리에 모은다. 테이블은 값만 보여 준다. - mountStructureMenu의 부착 대상을 테이블 → 그래프 칸(.b05-profile__chart)으로 교체 - ProfileTableOptions.structureLines 제거 - CSS 블록명 b05-profile-table__context-menu → b05-profile-chart__context-menu Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,7 @@ import { LONG_PAD } from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Sectio
|
||||
import { createWorkflowPanelHandle } from "@ui/ui_template_overlay";
|
||||
import { createPanelResizer } from "@ui/ui_template_resizer";
|
||||
import { createDrainagePanel } from "./B05_wf2_Route_UI_Drainage_Panel";
|
||||
import { mountStructureMenu } from "./B05_wf2_Route_UI_Profile_Structures";
|
||||
import {
|
||||
hasLegacyAlignment,
|
||||
normalizedLongitudinal,
|
||||
@@ -457,13 +458,6 @@ export function createRouteProfilePanel(
|
||||
// 값 열 계획고 직접 입력 → 규칙 측점과 동일한 station_offset 파이프라인.
|
||||
onAdjustStation: (chainage, delta) =>
|
||||
base && applyEdits(adjustStation(base, store.edits(), chainage, delta)),
|
||||
// 구조물 우클릭 메뉴 — 배관 추가 / 구조물 삭제. 이동은 그래프 측점선에서 한다.
|
||||
structureLines: {
|
||||
chainageAt: chainageInverter(longitudinal, width, layout.originOffset),
|
||||
maxChainageM: maxChainageOf(longitudinal),
|
||||
onRemove: (station) => callbacks?.onStructureRemove?.(station),
|
||||
onAddPipe: (chainage) => callbacks?.onPipeAdd?.(chainage),
|
||||
},
|
||||
})
|
||||
: null;
|
||||
|
||||
@@ -511,6 +505,15 @@ export function createRouteProfilePanel(
|
||||
},
|
||||
),
|
||||
);
|
||||
// 구조물 우클릭 메뉴 — 측점선 위면 삭제, 빈 자리면 배관 추가. 이동도 여기(측점선 끌기)서 한다.
|
||||
mountStructureMenu(chartWrap, {
|
||||
stations: irregularStations,
|
||||
x,
|
||||
chainageAt: chainageInverter(longitudinal, width, layout.originOffset),
|
||||
maxChainageM: maxChainageOf(longitudinal),
|
||||
onRemove: (station) => callbacks?.onStructureRemove?.(station),
|
||||
onAddPipe: (chainage) => callbacks?.onPipeAdd?.(chainage),
|
||||
});
|
||||
// 가로 스크롤에도 고정되는 sticky Y축 오버레이(SVG와 같은 눈금·불투명 배경으로 값 누출 차단).
|
||||
if (yAxis) chartWrap.append(buildStickyYAxis(yAxis, chartHeight));
|
||||
if (alignment) {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
/* =============================================================================
|
||||
* 종단 테이블 구조물 배치 라인 (B05)
|
||||
*
|
||||
* 테이블 위 우클릭 메뉴만 담당한다. 세로선은 **그리지 않는다** — 구조물 위치는 그래프의 측점선이
|
||||
* 이미 보여 주므로 테이블에까지 얹으면 같은 것을 두 번 그리는 셈이다(2026-08-01 사용자 지시).
|
||||
* 위치를 옮기는 것도 그래프 측점선에서 한다.
|
||||
* 종단 **그래프** 위 우클릭 메뉴. 구조물 조작은 전부 그래프에서 한다 — 측점선을 끌어 옮기고,
|
||||
* 우클릭으로 배관을 넣거나 지운다(2026-08-01 사용자 지시). 테이블은 값만 보여 준다.
|
||||
*
|
||||
* 가까운 구조물이 있으면 삭제, 없으면 그 자리에 배관 추가를 띄운다. 배관은 배수유역도의 관 지점이
|
||||
* 투영된 것이라(`origin: "pipe"`), 지우면 관 지점 정본(`pipe_points.json`)이 바뀌고 세부유역도
|
||||
@@ -32,23 +31,23 @@ export interface StructureLineOptions {
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 요소에 우클릭 메뉴를 붙인다. 테이블은 매 그리기마다 새로 만들어지므로 이 함수도
|
||||
* 그래프 칸에 우클릭 메뉴를 붙인다. 그래프는 매 그리기마다 새로 만들어지므로 이 함수도
|
||||
* 그때마다 다시 부른다 — 상태를 밖에 남기지 않는다.
|
||||
*/
|
||||
export function mountStructureMenu(table: HTMLElement, options: StructureLineOptions): void {
|
||||
const menu = createMapContextMenu("b05-profile-table");
|
||||
export function mountStructureMenu(host: HTMLElement, options: StructureLineOptions): void {
|
||||
const menu = createMapContextMenu("b05-profile-chart");
|
||||
|
||||
function clamp(chainageM: number): number {
|
||||
return Math.min(Math.max(chainageM, 0), options.maxChainageM);
|
||||
}
|
||||
|
||||
// 우클릭 — 가까운 구조물이 있으면 삭제, 없으면 그 자리에 배관을 넣는다.
|
||||
table.addEventListener("contextmenu", (event) => {
|
||||
host.addEventListener("contextmenu", (event) => {
|
||||
if (menu.contains(event.target)) {
|
||||
event.preventDefault();
|
||||
return;
|
||||
}
|
||||
const rect = table.getBoundingClientRect();
|
||||
const rect = host.getBoundingClientRect();
|
||||
const localX = event.clientX - rect.left;
|
||||
const chainage = clamp(options.chainageAt(localX));
|
||||
// 선을 그리지 않으므로 화면 거리로 가장 가까운 구조물을 찾는다.
|
||||
@@ -66,9 +65,9 @@ export function mountStructureMenu(table: HTMLElement, options: StructureLineOpt
|
||||
: ["배관 추가", () => options.onAddPipe(Number(chainage.toFixed(2)))],
|
||||
]);
|
||||
});
|
||||
table.addEventListener("pointerdown", (event) => {
|
||||
host.addEventListener("pointerdown", (event) => {
|
||||
if (!menu.contains(event.target)) menu.close();
|
||||
});
|
||||
|
||||
table.append(menu.element);
|
||||
host.append(menu.element);
|
||||
}
|
||||
|
||||
@@ -19,10 +19,6 @@ import type {
|
||||
} from "./B05_wf2_Route_UI_Profile_Alignment";
|
||||
import { stationLabel } from "../B06_wf3_ProfileCross/B06_wf3_ProfileCross_UI_Section_Common";
|
||||
import { irregularStationId, type IrregularStation } from "./B05_wf2_Route_UI_IrregularStations";
|
||||
import {
|
||||
mountStructureMenu,
|
||||
type StructureLineOptions,
|
||||
} from "./B05_wf2_Route_UI_Profile_Structures";
|
||||
|
||||
export interface ProfileTableOptions {
|
||||
alignment: ProfileAlignment;
|
||||
@@ -46,8 +42,6 @@ export interface ProfileTableOptions {
|
||||
onCurveRadiusChange: (curve: AlignmentCurve, radiusM: number | null) => void;
|
||||
/** 임의 chainage의 계획고를 delta만큼 조정(비정규 측점 값 열 직접 입력용). */
|
||||
onAdjustStation?: (chainageM: number, deltaM: number) => void;
|
||||
/** 구조물 우클릭 메뉴(배관 추가·구조물 삭제). 없으면 메뉴를 붙이지 않는다. */
|
||||
structureLines?: Omit<StructureLineOptions, "stations" | "x">;
|
||||
}
|
||||
|
||||
interface StationRowSpec {
|
||||
@@ -541,14 +535,6 @@ export function createProfileTable(options: ProfileTableOptions): HTMLElement {
|
||||
),
|
||||
);
|
||||
}
|
||||
// 구조물 우클릭 메뉴 — 세로선은 그래프가 그리므로 여기서는 메뉴만 붙인다.
|
||||
if (options.structureLines) {
|
||||
mountStructureMenu(table, {
|
||||
...options.structureLines,
|
||||
stations: options.irregularStations ?? [],
|
||||
x,
|
||||
});
|
||||
}
|
||||
return table;
|
||||
}
|
||||
|
||||
|
||||
@@ -685,9 +685,9 @@
|
||||
|
||||
/* ─── 선택된 비정규 측점 값 열 오버레이 (규칙 열과 같은 12행) ──────────────
|
||||
세로 점선·구조물 태그 없이, 선택 시에만 값 열을 테이블 위에 겹쳐 보여준다. */
|
||||
/* ── 구조물 우클릭 메뉴 (배관 추가 · 구조물 삭제) ────────────────────────── */
|
||||
/* ── 구조물 우클릭 메뉴 (그래프 위 — 배관 추가 · 구조물 삭제) ─────────────── */
|
||||
|
||||
.b05-profile-table__context-menu {
|
||||
.b05-profile-chart__context-menu {
|
||||
position: absolute;
|
||||
z-index: 7;
|
||||
display: flex;
|
||||
@@ -700,11 +700,11 @@
|
||||
box-shadow: 0 4px 16px rgb(0 0 0 / 25%);
|
||||
}
|
||||
|
||||
.b05-profile-table__context-menu[hidden] {
|
||||
.b05-profile-chart__context-menu[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.b05-profile-table__context-menu-item {
|
||||
.b05-profile-chart__context-menu-item {
|
||||
padding: var(--spacing-4) var(--spacing-8);
|
||||
border: 0;
|
||||
border-radius: var(--radius-cards);
|
||||
@@ -715,7 +715,7 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.b05-profile-table__context-menu-item:hover {
|
||||
.b05-profile-chart__context-menu-item:hover {
|
||||
background: var(--color-surface-sunken);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user