fix(B06): 그래프 이름표 오버레이화, 횡단 제목행 제거, 표 단위·정렬 정리
- 종단면도 이름표를 플롯 안쪽 상단으로 겹치고 LONG_PAD.top 30 → 12로 줄여 위 여백을 그래프 몫으로 돌린다. 구조물 라벨은 그만큼 내려 겹치지 않게 했다. - 유토곡선에 같은 양식의 이름표를 하단에 넣는다. 곡선이 지나가는 자리라 불투명 배경판을 깔고 아래 여백을 16 → 8로 줄였다. - 상단 패널과 횡단 카드 사이의 횡단면도 제목·개수 행을 없앤다. 카드마다 측점이 적혀 있어 무엇을 보는지 이미 분명하고, 공간만 먹었다. 관련 CSS와 로케일 키도 정리. - 종단 기준 곡선을 기본 비활성화한다(계획고 기반 개략값이라 참고용). 세션 저장 키를 v3로 올려 옛 값이 이 곡선을 되켜지 못하게 했다. - 면적표 단위를 값 칸에서 좌상단 교차 셀로 옮기고, 열 제목을 가운데 맞춤으로 바꿨다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -264,8 +264,9 @@ export function buildAreaReadout(
|
||||
const table = document.createElement("table");
|
||||
table.className = "b06-cross-card__areas";
|
||||
const head = document.createElement("tr");
|
||||
// 좌상단(행제목 × 열제목 교차) 칸은 단위 자리다 — 값 칸마다 단위를 붙이지 않는다.
|
||||
for (const [text, tip] of [
|
||||
["", ""],
|
||||
[L("B06_Design_Area_Unit"), ""],
|
||||
[L("B06_Design_Area_EA"), L("B06_Design_Ground_Soil")],
|
||||
[L("B06_Design_Area_RR"), L("B06_Design_Ground_Ripping")],
|
||||
[L("B06_Design_Area_BR"), L("B06_Design_Ground_Blasting")],
|
||||
|
||||
@@ -294,11 +294,12 @@ export function createLongitudinalProfile(
|
||||
}),
|
||||
);
|
||||
// 구조물(비정규) 측점: 구조물 이름을 측점선 상단에 표기해 위치를 식별할 수 있게 한다.
|
||||
// 그래프 이름표가 플롯 안 상단으로 들어왔으므로 그 아래로 내려 겹치지 않게 한다.
|
||||
if (station.structure) {
|
||||
marker.append(
|
||||
svgText(station.structure, {
|
||||
x: stationX,
|
||||
y: LONG_PAD.top + 12,
|
||||
y: LONG_PAD.top + 26,
|
||||
"text-anchor": "middle",
|
||||
class: "b06-chart__structure-label",
|
||||
}),
|
||||
@@ -340,11 +341,11 @@ export function createLongitudinalProfile(
|
||||
y2: heightPx - LONG_PAD.bottom,
|
||||
class: "b06-chart__axis",
|
||||
}),
|
||||
// X축 제목은 그래프 **상단**에 둔다(2026-08-02 사용자 지시). 아래쪽 여백은 측점 라벨 몫이며,
|
||||
// B06에서는 종단면도 바로 밑에 유토곡선이 붙어 두 그래프가 X축을 공유하는 것처럼 보여야 한다.
|
||||
// 그래프 이름표는 플롯 **안쪽 상단에 겹쳐** 놓는다(2026-08-02 사용자 지시) — 위쪽 여백을
|
||||
// 이름표 전용으로 비워 두지 않고 그래프 몫으로 돌리기 위해서다.
|
||||
svgText(L("B06_Profile_View_LongitudinalXAxis"), {
|
||||
x: widthPx / 2,
|
||||
y: 14,
|
||||
y: LONG_PAD.top + 12,
|
||||
"text-anchor": "middle",
|
||||
class: "b06-chart__axis-label",
|
||||
}),
|
||||
|
||||
@@ -100,8 +100,11 @@ const GROUND_TYPES: GroundType[] = ["soil", "ripping_rock", "blasting_rock"];
|
||||
/** 곡선을 그릴 산출 기준. 배열 순서가 곧 범례 순서다. */
|
||||
const SERIES_BASES: MassHaulBasis[] = ["cross", "longitudinal"];
|
||||
|
||||
/** 기본으로 켜 두는 곡선. */
|
||||
export const MASS_HAUL_DEFAULT_VISIBLE: MassHaulBasis[] = ["cross", "longitudinal"];
|
||||
/**
|
||||
* 기본으로 켜 두는 곡선 — 횡단 기준만. 종단 기준은 계획고 기반 개략값이라 참고용이므로
|
||||
* 사용자가 필요할 때 범례에서 켠다(2026-08-02 사용자 지시).
|
||||
*/
|
||||
export const MASS_HAUL_DEFAULT_VISIBLE: MassHaulBasis[] = ["cross"];
|
||||
|
||||
/** 환산계수가 비어 있거나 값이 이상하면 1.0으로 떨어뜨려 계산이 멈추지 않게 한다. */
|
||||
function factorFor(conversion: EarthworkConversion, ground: GroundType): number {
|
||||
|
||||
@@ -43,7 +43,7 @@ export const MASS_HAUL_HEIGHT = 190;
|
||||
* 아래쪽은 X축 제목을 종단면도 상단으로 합치면서 비었다.
|
||||
*/
|
||||
const MASS_PAD_TOP = 10;
|
||||
const MASS_PAD_BOTTOM = 16;
|
||||
const MASS_PAD_BOTTOM = 8;
|
||||
|
||||
const BASIS_LABEL: Record<MassHaulBasis, LocaleKey> = {
|
||||
cross: "B06_MassHaul_Basis_Cross",
|
||||
@@ -406,7 +406,7 @@ export function createMassHaulChart(
|
||||
y2: heightPx - MASS_PAD_BOTTOM,
|
||||
class: "b06-chart__axis",
|
||||
}),
|
||||
// X축 제목은 종단면도 상단 하나로 합쳤다 — 여기서 또 적으면 두 그래프가 별개 축으로 읽힌다.
|
||||
// X축 제목은 종단면도 이름표 하나로 합쳤다 — 여기서 또 적으면 두 그래프가 별개 축으로 읽힌다.
|
||||
svgText(L("B06_MassHaul_YAxis"), {
|
||||
x: 15,
|
||||
y: heightPx / 2,
|
||||
@@ -415,6 +415,29 @@ export function createMassHaulChart(
|
||||
class: "b06-chart__axis-label",
|
||||
}),
|
||||
);
|
||||
|
||||
// 그래프 이름표 — 종단면도와 같은 양식이되 **하단**에 놓고, 곡선이 지나가는 자리라
|
||||
// 불투명 배경을 깐다(2026-08-02 사용자 지시).
|
||||
const nameText = L("B06_MassHaul_Title");
|
||||
const nameWidth = textWidth(nameText) + 14;
|
||||
const nameY = heightPx - MASS_PAD_BOTTOM - 6;
|
||||
svg.append(
|
||||
svgElement("rect", {
|
||||
x: widthPx / 2 - nameWidth / 2,
|
||||
y: nameY - 12,
|
||||
width: nameWidth,
|
||||
height: 17,
|
||||
rx: 4,
|
||||
ry: 4,
|
||||
class: "b06-masshaul__name-plate",
|
||||
}),
|
||||
svgText(nameText, {
|
||||
x: widthPx / 2,
|
||||
y: nameY,
|
||||
"text-anchor": "middle",
|
||||
class: "b06-chart__axis-label",
|
||||
}),
|
||||
);
|
||||
return svg;
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,12 @@ export const CROSS_GRID_GAP = 16;
|
||||
// bottom은 측점 라벨 한 줄 몫이다. X축 제목을 그래프 상단으로 올린 뒤 남던 여백을 걷어내,
|
||||
// 종단면도 아래 라벨이 그 밑 유토곡선과의 틈 **가운데**에 오도록 좁혔다(2026-08-02 사용자 지시).
|
||||
// left/right는 유토곡선과 X축을 맞추는 값이라 함부로 바꾸면 두 그래프 측점선이 어긋난다.
|
||||
export const LONG_PAD = { left: 62, right: 24, top: 30, bottom: 26 };
|
||||
// top은 그래프 이름표가 겹쳐 놓이는 자리다. 이름표를 플롯 안 오버레이로 돌린 뒤 남던 위쪽
|
||||
// 여백을 걷어 그래프 몫으로 돌렸다(2026-08-02 사용자 지시).
|
||||
// bottom은 측점 라벨 한 줄 몫이다. X축 제목을 이름표로 합치며 남던 여백을 걷어내,
|
||||
// 종단면도 아래 라벨이 그 밑 유토곡선과의 틈 **가운데**에 오도록 좁혔다.
|
||||
// left/right는 유토곡선과 X축을 맞추는 값이라 함부로 바꾸면 두 그래프 측점선이 어긋난다.
|
||||
export const LONG_PAD = { left: 62, right: 24, top: 12, bottom: 26 };
|
||||
export const CROSS_PAD = { left: 58, right: 20, top: 10, bottom: 52 };
|
||||
|
||||
export interface YScaleOptions {
|
||||
|
||||
@@ -71,9 +71,9 @@ const MIN_PANEL_HEIGHT = MIN_LONG_HEIGHT + MASS_HAUL_MIN_HEIGHT + PANEL_CHROME_P
|
||||
const MAX_PANEL_HEIGHT_RATIO = 0.8;
|
||||
const PANEL_HEIGHT_KEY = "b06:profile-panel-height";
|
||||
const PANEL_COLLAPSED_KEY = "b06:profile-panel-collapsed";
|
||||
// 곡선 키가 `${기준}_${환산}`에서 기준 하나로 바뀌었다 — 옛 세션값을 읽으면 전부 꺼진 것처럼
|
||||
// 보이므로 저장 키의 판을 올려 갈아탄다.
|
||||
const MASS_HAUL_VISIBLE_KEY = "b06:masshaul-visible-v2";
|
||||
// 곡선 키·기본값이 바뀔 때마다 저장 키의 판을 올려 갈아탄다(옛 세션값을 읽으면 의도와 다른
|
||||
// 곡선이 켜진다). v2: 키가 `${기준}_${환산}` → `${기준}`. v3: 기본 표시가 횡단 기준 하나로.
|
||||
const MASS_HAUL_VISIBLE_KEY = "b06:masshaul-visible-v3";
|
||||
|
||||
/** 켜 둔 곡선 목록은 세션에만 남긴다(패널 높이·접힘과 같은 규칙). */
|
||||
function readVisibleSeries(): Set<string> {
|
||||
@@ -377,13 +377,8 @@ export function createSectionView(
|
||||
currentStationInterval ?? inferStationInterval(detail.longitudinal.stations);
|
||||
drawPanel();
|
||||
|
||||
const crossHeading = document.createElement("div");
|
||||
crossHeading.className = "b06-section__heading";
|
||||
const crossTitle = document.createElement("h3");
|
||||
crossTitle.textContent = L("B06_Profile_View_Cross");
|
||||
const crossCount = document.createElement("span");
|
||||
crossCount.textContent = `${detail.cross_sections.length}${L("B06_Profile_View_CrossCountSuffix")}`;
|
||||
crossHeading.append(crossTitle, crossCount);
|
||||
// 횡단면도 제목·개수 표기는 두지 않는다(2026-08-02 사용자 지시) — 상단 패널과 카드 사이
|
||||
// 공간만 먹고, 카드마다 측점이 적혀 있어 무엇을 보는지는 이미 분명하다.
|
||||
const grid = document.createElement("div");
|
||||
grid.className = "b06-section__grid";
|
||||
const columnCount = Math.max(
|
||||
@@ -424,7 +419,7 @@ export function createSectionView(
|
||||
}
|
||||
// panel은 이미 root의 자식이라 replaceChildren이 떼었다 붙이면서 스크롤을 잃는다.
|
||||
const keepScrollLeft = chartWrap.scrollLeft;
|
||||
root.replaceChildren(panel, crossHeading, grid);
|
||||
root.replaceChildren(panel, grid);
|
||||
chartWrap.scrollLeft = keepScrollLeft;
|
||||
};
|
||||
|
||||
|
||||
@@ -100,10 +100,6 @@
|
||||
content: "▾";
|
||||
}
|
||||
|
||||
.b06-section__heading h3 {
|
||||
font-size: var(--text-body);
|
||||
}
|
||||
|
||||
/* 종단면도와 유토곡선이 이 컨테이너 하나를 공유한다 — 가로 스크롤을 함께 써야
|
||||
같은 측점이 두 그래프에서 같은 자리에 선다. wrap을 나누면 정렬이 깨진다. */
|
||||
.b06-section__chart-wrap {
|
||||
@@ -154,15 +150,6 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.b06-section__heading {
|
||||
display: flex;
|
||||
align-items: end;
|
||||
justify-content: space-between;
|
||||
gap: var(--spacing-16);
|
||||
color: var(--color-text-secondary);
|
||||
font-size: var(--text-caption);
|
||||
}
|
||||
|
||||
.b06-section__grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(min(480px, 100%), 1fr));
|
||||
@@ -547,7 +534,9 @@
|
||||
font-weight: var(--font-weight-regular);
|
||||
}
|
||||
|
||||
/* 열 제목은 가운데 맞춤 — 값은 우측 정렬이라 자릿수가 흔들려도 머리글은 열 중앙에 선다. */
|
||||
.b06-cross-card__areas thead th {
|
||||
text-align: center;
|
||||
color: var(--color-text-muted);
|
||||
}
|
||||
|
||||
|
||||
@@ -99,6 +99,12 @@
|
||||
font-weight: var(--font-weight-medium);
|
||||
}
|
||||
|
||||
/* 그래프 이름표 배경 — 이름이 곡선 위에 놓이므로 불투명하게 깐다. */
|
||||
.b06-masshaul__name-plate {
|
||||
fill: var(--color-surface);
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.b06-masshaul__summary {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
@@ -186,7 +186,6 @@ export const ui_locales_b2 = {
|
||||
B06_Profile_View_Longitudinal: ["종단면도", "Longitudinal profile"],
|
||||
B06_Profile_View_Cross: ["횡단면도", "Cross sections"],
|
||||
B06_Profile_View_StationCount: ["횡단 측점", "Cross stations"],
|
||||
B06_Profile_View_CrossCountSuffix: ["개 도면", " drawings"],
|
||||
B06_Profile_View_LongitudinalXAxis: [
|
||||
"BP 기준 누적거리 (횡단 측점)",
|
||||
"Chainage from BP (cross stations)",
|
||||
@@ -259,7 +258,9 @@ export const ui_locales_b2 = {
|
||||
B06_Design_Area_EA: ["EA", "EA"],
|
||||
B06_Design_Area_RR: ["RR", "RR"],
|
||||
B06_Design_Area_BR: ["BR", "BR"],
|
||||
B06_Design_Area_Total: ["계 (㎡)", "Total (㎡)"],
|
||||
B06_Design_Area_Total: ["계", "Total"],
|
||||
/* 단위는 값 칸마다 붙이지 않고 표 좌상단(행제목 × 열제목 교차) 칸에 한 번만 적는다. */
|
||||
B06_Design_Area_Unit: ["㎡", "㎡"],
|
||||
B06_Design_Area_Highlight: ["누르면 해당 면적을 강조합니다", "Click to highlight this area"],
|
||||
B06_Design_Fill_Area: ["성토", "Fill"],
|
||||
B06_Design_Unset: ["미지정", "Not set"],
|
||||
|
||||
Reference in New Issue
Block a user