feat(B06): 횡단 보기 반폭을 별도 컨테이너로 분리 + 사이드 양식 B05 정렬

- 「횡단 보기 설정」 컨테이너 신설: 반폭 입력 + [적용] 버튼 한 줄. 표준 횡단면
  설정의 [전체 측점 반영]에 물려 있던 반폭 적용을 떼어냈다 — 보유 샘플(20m) 안이면
  전 측점 설계 재계산 없이 즉시 반영된다.
- 표준 횡단면 설정 컨테이너는 기본 접힘.
- 사이드 컨테이너 양식(여백·글자 크기·색)을 B05 .b05-route__panel-section 값으로
  맞춤. 접힘 토글·외곽선은 이미 공용(ui-collapsible / ui-sidebar-section)이었다.
  표준단면 「변수 위치 안내」 모식도 스타일은 현행 유지.
- 표준패널의 미사용 extraControl 인자 제거.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-23 18:47:36 +09:00
co-authored by Claude Opus 5
parent bab1f6b2c2
commit dff1e3aff9
4 changed files with 68 additions and 34 deletions
+37 -22
View File
@@ -56,22 +56,35 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise<void> {
let stationInterval: number | undefined;
// 표준 횡단면 설정 패널 자리. context 로드 후 config 기본값으로 채운다.
const standardGroup = buildGroup(L("B06_Std_Title"));
// 표준 횡단면 설정은 기본 접힘(2026-08-23 사용자 지시) — 자주 여는 칸이 아니다.
const standardGroup = buildGroup(L("B06_Std_Title"), true);
const standardPanelSlot = document.createElement("div");
standardGroup.append(standardPanelSlot);
let standardPanel: StandardPanelController | null = null;
// 높이 배율 옵션은 폐지 — 항상 1배율(2026-08-06 사용자 지시). 표시 옵션 그룹도 함께
// 사라지고, 횡단 반폭은 표준 횡단면 설정 컨테이너의 [전체 측점 반영] 위로 옮겼다.
// 반폭 적용도 그 버튼이 담당한다(재계산 버튼 폐지) — 요청 반폭이 보유 샘플 폭(기본
// 20m, config SECTION_CROSS_HALF_WIDTH_M) 이하면 표시만 바꾸고, 넘으면 B05부터
// 재생성해 영구저장 후 다시 로드한다.
// 높이 배율 옵션은 폐지 — 항상 1배율(2026-08-06 사용자 지시).
// 횡단 반폭은 **자체 컨테이너 + 자체 [보기 반폭 적용] 버튼**이다(2026-08-23 사용자 지시)
// — 표준 횡단면 설정의 [전체 측점 반영]에 물려 있어 반폭만 바꿔도 전 측점 설계
// 재계산을 기다려야 했다. 요청 반폭이 보유 샘플 폭(기본 20m, config
// SECTION_CROSS_HALF_WIDTH_M) 이하면 표시만 바꾸므로 즉시 끝나고, 넘을 때만 재생성한다.
const crossHalfWidthField = createInputField({
label: L("B05_Route_Field_CrossHalfWidth"),
type: "number",
});
crossHalfWidthField.input.min = "0.1";
crossHalfWidthField.input.step = "0.1";
const applyWidthButton = createButton({
label: L("B06_View_Apply"),
variant: "filled",
onClick: () => void applyCrossHalfWidth(),
});
applyWidthButton.title = L("B06_View_Apply_Tip");
const viewGroup = buildGroup(L("B06_View_Title"));
// 반폭 입력 + [적용]은 한 줄(2026-08-23 사용자 지시).
const viewRow = document.createElement("div");
viewRow.className = "b06-profile__field-row";
viewRow.append(crossHalfWidthField.root, applyWidthButton);
viewGroup.append(viewRow);
/** 횡단면도 **표시** 반폭 기본값(m) — 2026-08-19 사용자 지시로 20 → 12. 계산·보유
* 샘플 폭은 백엔드 기본(SECTION_CROSS_HALF_WIDTH_M = 20m) 그대로라 12는 표시
* 범위만 좁힌다(12 ≤ 20이므로 재계산 없음). 계산 범위와 혼동 금지. */
@@ -105,7 +118,7 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise<void> {
const leftForm = document.createElement("div");
leftForm.className = "b06-profile__form";
leftForm.append(standardGroup, actionRow);
leftForm.append(viewGroup, standardGroup, actionRow);
// 그룹 제목 행 클릭 시 접기/펼치기(N-4-1). 액션 버튼 행은 collapsible 아님.
attachCollapsible(leftForm);
@@ -265,23 +278,30 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise<void> {
},
});
/** 패널 [전체 반영](N-2-1): design 보유 전 측점을 패널 최신값으로 순차 재계산한다.
* handleDesignChange가 standardPanel.getValues()를 실어 보내므로 표준단면 수치만
* 갱신되고 측점별 버튼 선택값은 보존된다. 순차 await로 동시 API 호출 수를 제한한다. */
async function applyPanelToAll(): Promise<void> {
if (!sectionDetail || !projectId || currentRouteId === null) return;
/**
* [보기 반폭 적용] — 표시 반폭만 반영한다(2026-08-23 사용자 지시로 분리).
* 보유 샘플(기본 20m) 안이면 재계산 없이 즉시, 넘을 때만 재생성이 돈다.
* 측점 개별 반폭은 전역값으로 초기화한다 — 남아 있으면 전역값이 무시된다.
*/
async function applyCrossHalfWidth(): Promise<void> {
if (!sectionDetail) return;
const requested = crossHalfWidth();
if (requested !== undefined && !(await ensureSampledWidth(requested))) return;
// 전체 반영 = 개별 반폭도 전역값으로 초기화(2026-08-06). 저장된 개별값
// (design.display_half_width_m)이 남아 있으면 전역 반폭이 무시되므로, 세션에
// 전역값을 명시해 저장값보다 우선하게 한다(확정 시 저장값도 전역으로 덮인다).
if (!sectionDetail) return;
stationControls.applyGlobalWidth(
requested,
sectionDetail.cross_sections.map((section) => section.chainage_m),
);
persistDisplayHalfWidth();
renderSectionDetail();
updateActionState();
showToast(L("B06_View_Apply_Success"), "success");
}
/** 패널 [전체 반영](N-2-1): design 보유 전 측점을 패널 최신값으로 순차 재계산한다.
* handleDesignChange가 standardPanel.getValues()를 실어 보내므로 표준단면 수치만
* 갱신되고 측점별 버튼 선택값은 보존된다. 순차 await로 동시 API 호출 수를 제한한다. */
async function applyPanelToAll(): Promise<void> {
if (!sectionDetail || !projectId || currentRouteId === null) return;
const targets = sectionDetail.cross_sections.filter((section) => section.design);
if (!targets.length) return;
showLoadingOverlay();
@@ -607,12 +627,7 @@ export async function renderB06ProfileCross(root: HTMLElement): Promise<void> {
// 표준 횡단면 설정 패널 장착(세션값 우선, 없으면 config 기본값).
// 횡단 반폭 입력은 [전체 측점 반영] 버튼 위로 들어간다(2026-08-06 사용자 지시).
standardPanel = createStandardPanel(
projectId,
context.standard_cross_section,
applyPanelToAll,
crossHalfWidthField.root,
);
standardPanel = createStandardPanel(projectId, context.standard_cross_section, applyPanelToAll);
standardPanelSlot.append(standardPanel.root);
if (context.route_id === null) {
+2 -5
View File
@@ -159,8 +159,6 @@ export function createStandardPanel(
projectId: string,
defaults: StandardCrossSection,
onApplyAll?: () => void | Promise<void>,
/** [전체 측점 반영] 버튼 **위**에 끼울 추가 컨트롤(횡단 반폭 입력, 2026-08-06 사용자 지시). */
extraControl?: HTMLElement,
): StandardPanelController {
// 세션값 우선, 없으면 config 기본값. defaults는 복원 기준으로 보존한다.
const sessionValue = readSession(projectId);
@@ -264,9 +262,8 @@ export function createStandardPanel(
}
actions.append(resetButton);
// 추가 컨트롤(횡단 반폭)액션 행 바로 위 — [전체 측점 반영]이 반폭 적용까지 담당한다.
if (extraControl) root.append(body, loader, extraControl, actions);
else root.append(body, loader, actions);
// 횡단 반폭은 사이드의 **별도 컨테이너**로 뺐다(2026-08-23) — 여기는 표준단면 설정만.
root.append(body, loader, actions);
return {
root,
+28 -6
View File
@@ -31,23 +31,45 @@
gap: var(--spacing-16);
}
/* 사이드 컨테이너 양식은 B05 경로 패널(.b05-route__panel-section)과 같게 맞춘다
— 두 화면의 좌측 패널이 같은 폼으로 보여야 한다(2026-08-23 사용자 지시).
여백·글자 크기·색을 B05 값으로 옮겼고, 표준단면 「변수 위치 안내」 모식도만
기존 스타일(.b06-std__diagram*)을 그대로 둔다. */
.b06-profile__group {
display: flex;
flex-direction: column;
gap: var(--spacing-16);
gap: var(--spacing-8);
margin: 0;
padding: var(--spacing-16);
padding: calc(var(--spacing-8) + var(--spacing-4));
/* 테두리는 공통 ui-sidebar-section이 전담 — 로컬 border 선언이 공통 색을 덮던
문제 제거(2026-08-06 사용자 보고). */
border-radius: var(--radius-cards);
background-color: var(--color-surface-raised);
color: var(--color-text-body);
font-size: var(--text-caption);
}
.b06-profile__group-legend {
padding: 0 var(--spacing-8);
font-size: var(--text-caption);
font-weight: var(--font-weight-medium);
color: var(--color-text-secondary);
margin: 0;
padding: 0;
font-size: var(--text-body-sm);
color: var(--color-text);
}
/* 입력 한 칸 + 버튼을 한 줄로(횡단 보기 설정). 버튼은 입력 아래 선에 맞춘다. */
.b06-profile__field-row {
display: flex;
align-items: flex-end;
gap: var(--spacing-8);
}
.b06-profile__field-row > *:first-child {
flex: 1 1 auto;
min-width: 0;
}
.b06-profile__field-row > .ui-btn {
flex: 0 0 auto;
}
/* --- 읽기 전용 경로 컨텍스트 --- */
+1 -1
View File
@@ -466,7 +466,7 @@ export const ui_locales_b2 = {
/* --- B06 횡단 보기 설정(반폭) --- */
B06_View_Title: ["횡단 보기 설정", "Cross-section view"],
B06_View_Apply: ["보기 반폭 적용", "Apply view width"],
B06_View_Apply: ["적용", "Apply"],
B06_View_Apply_Tip: [
"표시 반폭만 바로 반영합니다(측점 설계 재계산 없음). 계산 반폭(20m)을 넘는 값만 재생성이 필요해 시간이 걸립니다.",
"Applies the display half-width only (no per-station redesign). Only values beyond the sampled 20 m need regeneration.",