260725_5
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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.",
|
||||
|
||||
Reference in New Issue
Block a user