feat(B05): 토량분배 시 유토곡선 전체 맞춤 + 상단 여백·테이블 높이 정리
- 토량분배를 켜면 유토곡선이 종단과의 X 맞춤·세로 창을 버리고 스크롤러 안쪽 폭·전 구간 Y 로 그려 곡선 전체가 한 화면에 들어옴(평형선·운반 블록을 노선 전체로 읽는 그림이라). 끄면 예전대로 종단과 맞물린 가로 스크롤로 복귀. - 요약 막대가 상단줄로 올라간 뒤 남던 상단 여백 축소 — 범례 top 34px → 4px, 차트 padTop 40 → 30(범례 높이 24 + 여유). - 종단 테이블 오버레이 높이 상한 신설 — 6행 × 행 30px + 24px(=204px). 12행에서 6행으로 줄인 뒤 같은 높이를 나눠 가지며 행이 부풀던 것을 막음. 레이아웃 제어(리사이저·패널 비례 연동)는 그대로 두고 상한만 걸음. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -422,17 +422,26 @@ export function createRouteMassHaulPanel(
|
||||
(overlay.clientHeight || OVERLAY_DEFAULT_HEIGHT) - bar.offsetHeight - 12;
|
||||
const chartHeight = Math.max(scrollHeight, MASS_HAUL_MIN_HEIGHT);
|
||||
|
||||
// 토량 분배를 켜면 **곡선 전체가 한 화면에** 들어와야 한다(2026-09-06 사용자 지시).
|
||||
// 평형선·운반 블록은 노선 전체를 놓고 읽는 그림이라, 종단 그래프와의 X 맞춤(가로
|
||||
// 스크롤)과 보이는 구간만 보는 Y 창을 둘 다 버리고 스크롤러 안쪽 폭·전 구간 Y로 그린다.
|
||||
const fitAll = haulPlan !== null;
|
||||
const widthPx = fitAll ? scroll.clientWidth || params.widthPx : params.widthPx;
|
||||
const chartAxis = fitAll
|
||||
? { ...params.axis, viewRange: undefined, window: undefined }
|
||||
: { ...params.axis, window: windowState };
|
||||
|
||||
let massAxis: { padLeft: number; ticks: Array<{ y: number; label: string }> } | null = null;
|
||||
const chart = createMassHaulChart(
|
||||
series,
|
||||
visible,
|
||||
params.stationSource,
|
||||
{ ...params.axis, window: windowState },
|
||||
chartAxis,
|
||||
params.selectedStationId,
|
||||
params.stationInterval,
|
||||
params.widthPx,
|
||||
widthPx,
|
||||
chartHeight,
|
||||
params.widthPx,
|
||||
widthPx,
|
||||
params.onSelectStation,
|
||||
haulPlan,
|
||||
(axis) => {
|
||||
@@ -442,14 +451,16 @@ export function createRouteMassHaulPanel(
|
||||
// 종단 캔버스와 같은 폭의 래퍼 — sticky Y축 앵커의 기준(position: relative)이 된다.
|
||||
const wrap = document.createElement("div");
|
||||
wrap.className = "b05-profile__chart b05-profile__masshaul-chart";
|
||||
wrap.style.width = `${params.widthPx}px`;
|
||||
wrap.style.width = `${widthPx}px`;
|
||||
wrap.style.height = `${chartHeight}px`;
|
||||
wrap.append(chart);
|
||||
// 앵커는 첫 자식 — SVG 뒤면 흐름 위치가 밀려 스크롤 시 축이 안 보인다(B06과 같은 규칙).
|
||||
if (massAxis) wrap.prepend(buildStickyYAxis(massAxis, chartHeight));
|
||||
scroll.replaceChildren(wrap);
|
||||
// 종단 스크롤러가 이미 옆으로 가 있으면 새로 그린 곡선도 같은 자리에서 시작한다.
|
||||
if (syncedMain) scroll.scrollLeft = syncedMain.scrollLeft;
|
||||
// 전체 맞춤일 때는 스크롤할 것이 없으므로 왼쪽 끝에 둔다.
|
||||
if (fitAll) scroll.scrollLeft = 0;
|
||||
else if (syncedMain) scroll.scrollLeft = syncedMain.scrollLeft;
|
||||
|
||||
legendLayer.append(
|
||||
createMassHaulLegend(series, visible, toggleSeries, () => {
|
||||
|
||||
@@ -432,9 +432,9 @@ export function renderProfile(ctx: ProfileRenderContext): void {
|
||||
padRight: LONG_PAD.right + originOffset,
|
||||
// 축 선·눈금은 위 종단 그래프의 축과 같은 자리에 — 축이 두 개로 보이지 않게.
|
||||
axisX: LONG_PAD.left,
|
||||
// 범례·기준 버튼 오버레이(top 34px)가 곡선 위에 떠서 그만큼 상단 여유를 준다
|
||||
// (2026-08-05 사용자 보고: 버튼과 커브 겹침).
|
||||
padTop: 40,
|
||||
// 범례·기준 버튼 오버레이(top 4px)가 곡선 위에 떠서 그 높이만큼만 여유를 준다
|
||||
// (2026-08-05: 버튼과 커브 겹침 / 2026-09-06: 요약 막대가 빠져 상단이 너무 비었음).
|
||||
padTop: 30,
|
||||
// 유토곡선 Y 도 종단과 같은 창을 본다 — 전 구간 최대 토량으로 고정하면 확대해도
|
||||
// 곡선이 납작하게 눌린다(2026-09-04 사용자 지시).
|
||||
viewRange: { fromM: viewFromM, toM: viewToM },
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import { readStateRaw, stateKey, writeStateRaw } from "../A00_Common/b_page_state";
|
||||
import { createWorkflowPanelHandle } from "@ui/ui_template_overlay";
|
||||
import { createPanelResizer } from "@ui/ui_template_resizer";
|
||||
import { TABLE_ROW_COUNT } from "./B05_Profile_UI_Profile_Layout";
|
||||
import { attachWheelHorizontalScroll } from "./B05_Profile_UI_Profile_Wheel";
|
||||
|
||||
/* 펼침·높이는 화면 취향(①) — 키는 등록표(`b_page_state`)가 만든다. */
|
||||
@@ -21,8 +22,16 @@ const OVERLAY_MIN_HEIGHT = 140;
|
||||
/** 테이블 오버레이 최소 높이 — 메인 패널 하한 계산(Profile_Panel)이 함께 쓴다. */
|
||||
export const TABLE_OVERLAY_MIN_HEIGHT = OVERLAY_MIN_HEIGHT;
|
||||
const OVERLAY_MAX_RATIO = 0.75;
|
||||
/** CSS 기본 높이 — `var(--b05-table-height, 300px)`(_Style_MassHaul.css)와 같아야 한다. */
|
||||
const OVERLAY_DEFAULT_HEIGHT = 300;
|
||||
/** 행 하나의 높이 상한(px). 행이 이보다 커지면 글자만 커지고 읽을 내용은 그대로다 —
|
||||
* 12행에서 6행으로 줄인 뒤 같은 높이를 나눠 가지며 행이 두 배로 부푼 것을 막는다
|
||||
* (2026-09-06 사용자 지시: 「최대값 제한, 레이아웃 제어는 현재 유지」). */
|
||||
const MAX_ROW_HEIGHT = 30;
|
||||
/** 오버레이 높이 상한 = 6행 × 행 상한 + 가로 스크롤바·경계 몫. */
|
||||
const OVERLAY_MAX_HEIGHT = TABLE_ROW_COUNT * MAX_ROW_HEIGHT + 24;
|
||||
/** CSS 기본 높이 — `var(--b05-table-height, 204px)`(_Style_MassHaul.css)와 같아야 한다. */
|
||||
const OVERLAY_DEFAULT_HEIGHT = OVERLAY_MAX_HEIGHT;
|
||||
/** 어느 경로로 들어온 높이든 상한을 넘지 않게 한다(리사이저·패널 비례 연동 공용). */
|
||||
const clampHeight = (px: number): number => Math.min(px, OVERLAY_MAX_HEIGHT);
|
||||
/** 리사이저가 높이를 담는 CSS 변수. */
|
||||
const HEIGHT_VAR = "--b05-table-height";
|
||||
|
||||
@@ -71,7 +80,8 @@ export function createProfileTableOverlay(onChanged: () => void): RouteProfileTa
|
||||
cssVar: HEIGHT_VAR,
|
||||
direction: -1,
|
||||
min: OVERLAY_MIN_HEIGHT,
|
||||
max: () => (overlay.parentElement?.clientHeight ?? window.innerHeight) * OVERLAY_MAX_RATIO,
|
||||
max: () =>
|
||||
clampHeight((overlay.parentElement?.clientHeight ?? window.innerHeight) * OVERLAY_MAX_RATIO),
|
||||
storageKey: HEIGHT_KEY,
|
||||
onResize: () => {
|
||||
syncHandlePosition();
|
||||
@@ -132,10 +142,10 @@ export function createProfileTableOverlay(onChanged: () => void): RouteProfileTa
|
||||
isOpen: () => open,
|
||||
desiredHeight: () => {
|
||||
const raw = parseFloat(overlay.style.getPropertyValue(HEIGHT_VAR));
|
||||
return Number.isFinite(raw) && raw > 0 ? raw : OVERLAY_DEFAULT_HEIGHT;
|
||||
return clampHeight(Number.isFinite(raw) && raw > 0 ? raw : OVERLAY_DEFAULT_HEIGHT);
|
||||
},
|
||||
commitHeight: (px) => {
|
||||
const next = Math.round(px);
|
||||
const next = Math.round(clampHeight(px));
|
||||
overlay.style.setProperty(HEIGHT_VAR, `${next}px`);
|
||||
writeStateRaw("table-height", String(next));
|
||||
syncHandlePosition();
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
공용 범례(.b06-masshaul__legend)가 이미 absolute + right 배치라 top만 이 래퍼가 정한다. */
|
||||
.b05-profile__masshaul-legend {
|
||||
position: absolute;
|
||||
top: 34px;
|
||||
top: 4px;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 3;
|
||||
@@ -120,10 +120,10 @@
|
||||
|
||||
/* 재계산 대기 표시 — 곡선을 지우지 않고(2026-09-03 깜빡임 제거) 곡선 영역 우측 상단에
|
||||
**떠 있는** 알림으로 둔다. 요약 막대에 붙이면 막대가 줄바꿈되며 곡선을 밀어냈다
|
||||
(2026-09-04 사용자 보고). 범례(top: 34px) 아래 줄에 앉혀 서로 가리지 않는다. */
|
||||
(2026-09-04 사용자 보고). 범례(top: 4px) 아래 줄에 앉혀 서로 가리지 않는다. */
|
||||
.b05-profile__masshaul-pending {
|
||||
position: absolute;
|
||||
top: 62px;
|
||||
top: 32px;
|
||||
right: var(--spacing-8);
|
||||
z-index: 4;
|
||||
padding: 2px var(--spacing-8);
|
||||
@@ -173,7 +173,7 @@
|
||||
z-index: 5;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: var(--b05-table-height, 300px);
|
||||
height: var(--b05-table-height, 204px);
|
||||
border-top: 1px solid var(--color-border);
|
||||
background: var(--color-surface-raised);
|
||||
box-shadow: 0 -4px 12px rgb(0 0 0 / 18%);
|
||||
|
||||
Reference in New Issue
Block a user