refactor(B05): 노선 추천 창 칸 · 후보 표를 부품 입력 · 표로 (PLAN 553-2)

- 제약 · 비중 · 목표 기울기 칸 = 부품 입력 단 32(옛 작은 칸 30 + 페이지 덮음 둥글기 4 · 글 16 지움) · 지형 고르기 = 단 32
- 후보 표 = 부품 표 데이터표(줄 28 · 수 칸 오른쪽) · 페이지 표 CSS 지움
- 표 부품 CSS 를 design_page 가 실음 · 시험(test_63_4) 머리 칸 글 고침
- 그림 색 전후 다름 0(밝음 · 어두움)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-26 23:20:19 +09:00
co-authored by Claude Opus 5.5
parent 9f4761a6b0
commit b309460488
5 changed files with 34 additions and 55 deletions
+2 -1
View File
@@ -49,9 +49,10 @@ import {
peekSectionDetail,
} from "../../B06_Section/B06_Section_Section_Store";
import "./design_page.css";
// 클래스로 붙이는 부품(체크 · 빈 상태 · 보조 글)의 CSS — B05 · B06 화면 파일이 시험 때문에 import 없이 클래스만 씀(553-2)
// 클래스로 붙이는 부품(체크 · 빈 상태 · 보조 글 · 표)의 CSS — B05 · B06 화면 파일이 시험 때문에 import 없이 클래스만 씀(553-2)
import "@ui/ui_template_checkbox";
import "@ui/ui_template_empty";
import "@ui/ui_template_table";
import "@ui/ui_template_text";
export type { DesignPageShared } from "./design_shared";
@@ -229,7 +229,7 @@ export function openRecommendModal(params: RecommendParams): void {
</div>
<label class="b05-routerec__terrain" data-terrain-row hidden
title="추천 · 초기 종단을 셀 지형 — [적용]하면 이 지형이 설계 지형(평면 · 종단 · 횡단)">지형
<select class="ui-select ui-select--sm" data-act="terrain">
<select class="ui-select ui-select--sm" data-size="32" data-act="terrain">
<option value="las">라이다</option>
<option value="sheet">수치지형도</option>
</select></label>
@@ -287,7 +287,7 @@ export function openRecommendModal(params: RecommendParams): void {
for (const key of WEIGHT_KEYS) {
const bar = document.createElement("div");
bar.className = "b05-routerec__bar";
const numField = createInputField({ type: "number", min: 0, max: 100, compact: true });
const numField = createInputField({ type: "number", min: 0, max: 100, size: 32 }); // 부품 입력 단 32(553-2)
numField.input.step = "1";
numField.input.inputMode = "numeric";
numField.input.classList.add("b05-routerec__num");
@@ -321,7 +321,7 @@ export function openRecommendModal(params: RecommendParams): void {
row.title = field.title;
const name = document.createElement("span");
name.textContent = field.label;
const input = createInputField({ type: "number", min: 0, compact: true });
const input = createInputField({ type: "number", min: 0, size: 32 }); // 부품 입력 단 32(553-2)
input.input.inputMode = "decimal";
input.input.step = "0.1";
input.input.dataset.criteria = key;
@@ -332,7 +332,7 @@ export function openRecommendModal(params: RecommendParams): void {
criteriaBox.append(row);
}
const targetField = createInputField({ type: "number", min: 0, max: 100, compact: true });
const targetField = createInputField({ type: "number", min: 0, max: 100, size: 32 }); // 부품 입력 단 32(553-2)
targetField.input.inputMode = "decimal";
targetField.input.step = "0.1";
targetField.input.dataset.criteria = TARGET_GRADE_KEY;
@@ -10,8 +10,6 @@
* (`initial_profile` · 438 직선화 · 종단곡선 포함)을 펴서 같은 그림에 선 하나 · 한 줄로 더한다.
* ========================================================================== */
import { renderDataTableHtml, type DataColumn } from "@ui/ui_template_data_table";
/** 후보 하나의 균형 종단 — 서버 `metrics.profile`. */
export interface BalanceProfile {
stationsM: number[];
@@ -193,25 +191,26 @@ export function profileLegend(hasPlan: boolean, hasInitial: boolean): string {
].join("")}</div>`;
}
const CANDIDATE_COLUMNS: DataColumn[] = [
{ key: "name", label: "후보" },
{ key: "cut", label: "절토" },
{ key: "fill", label: "성토" },
{ key: "mass", label: "누가토량" },
{ key: "grade", label: "최대 종단경사" },
];
const CANDIDATE_COLUMNS = ["후보", "절토", "성토", "누가토량", "최대 종단경사"];
/** 이 모달에서 셈한 후보들 — 갈래 이름 · 절 · 성 · 누가토량(±) · 최대 종단경사 표. 비면 빈 글. */
const escapeHtml = (text: string): string =>
text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
/** 이 모달에서 셈한 후보들 — 갈래 이름 · 절 · 성 · 누가토량(±) · 최대 종단경사 표. 비면 빈 글.
* 꼴 = 부품 「표」 데이터표(`ui-table--data` · 553-2) — 이름 칸 왼쪽 · 수 칸 오른쪽. */
export function candidatesTable(rows: Array<[string, BalanceProfile]>): string {
if (!rows.length) return "";
return renderDataTableHtml({
columns: CANDIDATE_COLUMNS,
rows: rows.map(([name, b]) => ({
name,
cut: m3(b.cutM3),
fill: m3(b.fillM3),
mass: signedM3(b.massEndM3),
grade: pct(b.maxGradePct),
})),
const end = (i: number): string => (i ? ' class="is-end"' : "");
const head = CANDIDATE_COLUMNS.map((label, i) => `<th scope="col"${end(i)}>${label}</th>`);
const body = rows.map(([name, b]) => {
const cells = [
escapeHtml(name),
m3(b.cutM3),
m3(b.fillM3),
signedM3(b.massEndM3),
pct(b.maxGradePct),
];
return `<tr>${cells.map((cell, i) => `<td${end(i)}>${cell}</td>`).join("")}</tr>`;
});
return `<div class="ui-table-wrap"><table class="ui-table ui-table--data"><thead><tr>${head.join("")}</tr></thead><tbody>${body.join("")}</tbody></table></div>`;
}
@@ -326,16 +326,11 @@
accent-color: var(--color-primary);
}
/* 값 칸 — 슬라이더 옆에서 숫자를 직접 넣는다. 합 100 을 못 맞추면 빨간 테두리. */
.b05-routerec__num {
/* 값 칸 = 부품 입력 단 32(553-2) — 슬라이더 옆에서 숫자를 직접 넣는다. 합 100 을 못 맞추면 빨간 테두리. */
.b05-routerec__num.ui-input {
width: 100%;
min-width: 3.2em;
padding: 2px 4px;
border: 1px solid var(--color-border);
border-radius: var(--radius-4, 4px);
background: var(--color-surface);
color: var(--color-text-body);
font-size: var(--text-body);
padding-inline: 4px;
font-weight: 600;
text-align: center;
appearance: textfield;
@@ -401,15 +396,10 @@
color: var(--color-text-body);
}
.b05-routerec__criteria input,
.b05-routerec__target input {
/* 제약 칸 = 부품 입력 단 32(553-2) — 자리 · 오른쪽 맞춤만 */
.b05-routerec__criteria input.ui-input,
.b05-routerec__target input.ui-input {
width: 100%;
padding: 4px 8px;
border: 1px solid var(--color-border);
border-radius: var(--radius-4, 4px);
background: var(--color-surface);
color: var(--color-text-body);
font-size: var(--text-body);
text-align: right;
}
@@ -546,22 +536,11 @@
}
/* 이 모달에서 셈한 후보 표 — 절 · 성 · 반입 · 최대 종단경사. */
.b05-routerec__cands table {
width: 100%;
border-collapse: collapse;
font-size: var(--text-caption);
/* 후보 표 = 부품 표 데이터표(553-2) — 수 칸 자리 맞춤 · 한 줄 */
.b05-routerec__cands .ui-table {
font-variant-numeric: tabular-nums;
}
.b05-routerec__cands th,
.b05-routerec__cands td {
padding: 2px var(--spacing-8);
border-bottom: 1px solid var(--color-border);
text-align: right;
.b05-routerec__cands .ui-table td {
white-space: nowrap;
}
.b05-routerec__cands th[scope="row"],
.b05-routerec__cands thead th:first-child {
text-align: left;
}
+1 -1
View File
@@ -122,6 +122,6 @@ def test_균형_종단은_캐시에_안_남는다() -> None:
assert result["svg"].count("<path") == 2
assert "<td>토공 최소</td>" in result["table"]
assert (
'<th scope="col" rowspan="1" title="누가토량">누가토량</th>' in result["table"]
'<th scope="col" class="is-end">누가토량</th>' in result["table"] # 부품 표(553-2)
and "반입" not in result["table"]
)