This commit is contained in:
2026-07-25 14:21:12 +09:00
parent 6bbed32f35
commit ac6adcac88
8 changed files with 70 additions and 14 deletions
+17 -1
View File
@@ -160,6 +160,8 @@ export interface EditOverlayOptions {
onStation: (chainageM: number, delta: number) => void;
onSegment: (segment: AlignmentSegment, delta: number) => void;
onResetStation: (chainageM: number) => void;
/** 구간 평행이동 편집을 자동 선형으로 원복(양 끝 오프셋 삭제). */
onResetSegment: (segment: AlignmentSegment) => void;
}
function overlayButton(
@@ -213,7 +215,8 @@ function planElevationAtSample(alignment: ProfileAlignment, chainageM: number):
/** 그래프 영역 위에 겹치는 편집 버튼 층을 만든다 (선 자체는 가리지 않는다). */
export function createEditOverlay(options: EditOverlayOptions): HTMLElement {
const { alignment, width, x, step, onStation, onSegment, onResetStation } = options;
const { alignment, width, x, step, onStation, onSegment, onResetStation, onResetSegment } =
options;
const irregular = options.irregularStations ?? [];
const layer = document.createElement("div");
layer.className = "b05-profile-edit";
@@ -305,6 +308,19 @@ export function createEditOverlay(options: EditOverlayOptions): HTMLElement {
);
down.style.left = `${center - BUTTON_HALF_PX}px`;
layer.append(up, down);
// 구간 양 끝 중 하나라도 편집됐으면 원복 ↺ 노출(측점 ↺와 동일 스타일·연산).
if (edited.has(chainageKey(segment.from_m)) || edited.has(chainageKey(segment.to_m))) {
const reset = overlayButton(
repeater,
"is-segment is-reset",
"↺",
`${label} — 자동 선형으로 원복`,
() => onResetSegment(segment),
);
reset.style.left = `${center - BUTTON_HALF_PX}px`;
layer.append(reset);
}
});
return layer;
@@ -499,6 +499,11 @@ export function createRouteProfilePanel(
onSegment: (segment, delta) =>
base && applyEdits(shiftSegment(base, store.edits(), segment, delta)),
onResetStation: (chainage) => store.resetStation(chainage),
// 구간 원복 = 양 끝 측점 오프셋 삭제(측점 원복 연산 ×2).
onResetSegment: (segment) => {
store.resetStation(segment.from_m);
store.resetStation(segment.to_m);
},
}),
);
}
@@ -408,6 +408,23 @@ export function appendCrossDesignOverlay(
polyline.setAttribute("points", points.join(" "));
polyline.setAttribute("class", "b06-chart__design-cross");
svg.append(polyline);
// 차도·노견 경계 짧은 수직 틱(N-4-2): 노면 단일 기울기라 육안 구분이 안 되는 경계를
// 표시한다. 노견 바깥 끝(road_edges)은 측구·사면 꺾임으로 이미 구분되므로 제외한다.
const edges = design.carriageway_edges;
if (edges) {
for (const edge of [edges.left, edges.right]) {
const cx = x(edge.offset_m);
const cy = toDisplayY(edge.elevation_m);
const tick = document.createElementNS(SVG_NS, "line");
tick.setAttribute("x1", String(cx));
tick.setAttribute("y1", String(cy - 6));
tick.setAttribute("x2", String(cx));
tick.setAttribute("y2", String(cy + 6));
tick.setAttribute("class", "b06-chart__carriageway-tick");
svg.append(tick);
}
}
}
/**
@@ -8,6 +8,7 @@ import {
showToast,
} from "@ui/ui_template_elements";
import { createWorkflowLayout } from "@ui/ui_template_workflow_layout";
import { attachCollapsible } from "@ui/ui_template_collapsible";
import { workflowSteps } from "../A00_Common/b_page_scaffold";
import {
fetchWorkflowState,
@@ -43,11 +44,12 @@ function L(key: keyof typeof ui_locales): string {
return ui_locales[key][currentLanguageIndex];
}
function buildGroup(legend: string): HTMLElement {
const group = document.createElement("fieldset");
group.className = "b06-profile__group";
const legendElement = document.createElement("legend");
legendElement.className = "b06-profile__group-legend";
// B05 사이드패널 접기 컨테이너 템플릿 재사용(N-4-1): 제목 행 클릭 토글, 우측 ▾/▸ 캐럿.
function buildGroup(legend: string, collapsed = false): HTMLElement {
const group = document.createElement("section");
group.className = `b06-profile__group ui-collapsible${collapsed ? " is-collapsed" : ""}`;
const legendElement = document.createElement("h3");
legendElement.className = "b06-profile__group-legend ui-collapsible__title";
legendElement.textContent = legend;
group.append(legendElement);
return group;
@@ -99,6 +101,8 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise<void> {
const leftForm = document.createElement("div");
leftForm.className = "b06-profile__form";
leftForm.append(standardGroup, displayGroup, actionRow);
// 그룹 제목 행 클릭 시 접기/펼치기(N-4-1). 액션 버튼 행은 collapsible 아님.
attachCollapsible(leftForm);
// 측점별 최신 요청 시퀀스 — 늦게 도착한 옛 응답을 폐기해 경합을 방지한다.
const designRequestSeq = new Map<number, number>();
@@ -179,9 +179,10 @@ export function createStandardPanel(
const buildGroup = (key: StandardCrossKey, legendKey: keyof typeof ui_locales): HTMLElement => {
const group = state[key];
const fieldset = document.createElement("fieldset");
// B05 "기준값 직접 지정"과 동일한 details/summary 패턴, 기본 접힘(N-4-1).
const fieldset = document.createElement("details");
fieldset.className = "b06-std__group";
const legend = document.createElement("legend");
const legend = document.createElement("summary");
legend.className = "b06-std__legend";
legend.textContent = L(legendKey);
fieldset.append(legend);
@@ -198,32 +198,39 @@
.b06-diag__label {
fill: var(--color-text-body);
font-size: 12px;
font-size: 14px;
}
.b06-diag__label-sm {
fill: var(--color-text-secondary);
font-size: 11px;
font-size: 12px;
}
/* 구간 그룹(details/summary) — 상단 "변수 위치 안내" 모식도와 동일한 균일 8px 패딩으로
맞춰 접힘 시 좌측·하단 여백을 줄인다(N-4 후속). */
.b06-std__group {
display: flex;
flex-direction: column;
gap: var(--spacing-8);
margin: 0;
padding: var(--spacing-8) var(--spacing-16) var(--spacing-16);
padding: var(--spacing-8);
border: 1px solid var(--color-border);
border-radius: var(--radius-inputs);
background-color: var(--color-surface);
}
.b06-std__legend {
padding: 0 var(--spacing-8);
font-size: var(--text-caption);
font-weight: var(--font-weight-medium);
color: var(--color-text-secondary);
}
/* 구간 그룹 접기(N-4-1) — details/summary. 기본 ▶ 마커로 접힘/펼침 표시. */
.b06-std__group > summary.b06-std__legend {
cursor: pointer;
user-select: none;
}
.b06-std__grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -483,6 +483,12 @@
stroke-linejoin: round;
}
/* 차도·노견 경계 짧은 수직 틱(N-4-2) — 설계선과 같은 계열, 얇게. */
.b06-chart__carriageway-tick {
stroke: var(--color-royal-amethyst);
stroke-width: 1.2;
}
/* 암 경계선(설계선 복사 + 오프셋): 리핑암·발파암 구간 점선 */
.b06-chart__rock-boundary {
fill: none;
+2 -2
View File
@@ -833,8 +833,8 @@ export const ui_locales = {
"Slope direction is adjustable only for both-cut and both-fill sections.",
],
B06_Design_More: ["더보기", "More"],
B06_Design_SlopeDir_Left: ["좌경사", "Slope left"],
B06_Design_SlopeDir_Right: ["우경사", "Slope right"],
B06_Design_SlopeDir_Left: ["좌", "Left"],
B06_Design_SlopeDir_Right: ["우", "Right"],
B06_Design_Disabled_RockCut: [
"암(리핑/발파) 지반의 절토 단면에서만 사용할 수 있습니다.",
"Available only for rock ground with a cut section.",