Files
Aislo/B06_Section/B06_Section_UI_Style_Cross.css
T
eomsangdonandClaude Opus 5 6b9c115baa feat(B06): 절토 경사를 사용자가 넣게 — 카드에 암 절토각(°), 전체는 표준 설정
사용자 지시(2026-09-07): 「대신 경고부분은 삭제해주고 대신 각도를 사용자가 넣을수 있게 반영.
전체 공통으로 변경하는 경우에는 기본값 지정으로 하면 되지만 횡단도 하나만 변경하는 폼은
가져야함 / 개별 횡단도에는 암 절토 각도의 개별 수정 가능해야함」

**화면** — 암 측점 카드 아래에 「암 절토 68.2° ↺」 칸이 섬. 값을 넣으면 그 측점만 사면이
새 경사로 다시 그려지고 절토량도 따라 바뀜. ↺ 는 표준값으로 되돌림. 전체를 바꾸는 자리는
종전대로 좌측 [표준 횡단면 설정]임.

**계산에 넣는 자리는 한 곳씩** — 파이썬 `compute_cross_design(cut_slope_ratio=…)` ·
TS `computeCrossDesign({cutSlopeRatio})` 의 **그룹을 만든 바로 뒤**에서 경사비만 갈아 끼움.
부르는 쪽 9곳에서 표준값을 측점마다 복제하는 방식은 안 씀(한 곳만 빠져도 값이 조용히
사라지는 실패군). 기하·소단 코드는 한 줄도 안 건드림 — 25 확인대로 소단이 그 값을 읽어
서므로 **위치·개수가 새 경사를 저절로 따라옴**.

⚠ **경사는 나르는 값이 아니라 기하 입력임** — 계산 뒤에 키만 베껴 붙이면 설계선은 옛 경사로
그려지고 숫자만 새것이 됨(소단에서 겪은 자리). 그래서 재계산 세 경로(포장 강제·세월교 하강·
선형 재계산)와 브라우저 재계산·서버 프리뷰 **모두 계산 인자로** 넘김.

**되돌리기는 0 을 남김** — 세션에서 지우기만 하면 정본에 남은 옛 사용자 값이 되살아나
표준으로 못 돌아감. 0 = 「표준값을 씀」.

값의 길 — 세션 `cutslope`(등록표 한 줄) → 카드 입력 → [저장]·[확정]에서 `cross_patches`
(`design.cut_slope_ratio_user`) → 정본. `USER_TOUCHED_KEYS` 양쪽에 넣어 **표준을 바꿔도
개별로 고친 측점은 그대로** 둠(사용자 원문 끝줄).

시험 — 새 7건(넣은 경사가 실제로 그려짐 · 무릎 위 토사 경사는 그대로 · 0 은 되돌림 ·
재계산에도 남음 · **계산 전에 넘어감** · 각도↔경사비 · 칸은 암 측점에만),
거울 시험에 「암 절토 경사를 측점에서 바꿈」 한 갈래 추가, 7-3 회귀 한 줄 추가.
전체 **483 passed · 17 skipped**.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-07 17:52:06 +09:00

610 lines
18 KiB
CSS
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =============================================================================
* B06_Section_UI_Style_Cross.css
* 종·횡단 도면 뷰(섹션·카드·차트·설계 컨트롤·줌/팬·오버레이) 스타일.
* 700줄 제한 대응으로 _UI_Style.css에서 분리(패널/폼/모식도는 원본 유지).
* ========================================================================== */
/* --- 종·횡단 도면 --- */
.b06-profile__main,
.b06-section {
display: flex;
flex-direction: column;
gap: var(--spacing-24);
min-width: 0;
}
.b06-section {
box-sizing: border-box;
padding-inline: var(--spacing-24);
}
/* 상단 패널이 sticky라 카드로 스크롤하면 그 아래로 숨는다. 패널 높이를 JS가
`--b06-cross-scroll-margin`으로 실어 주고, 스크롤 정지 위치를 그만큼 아래로 민다
(패널을 키우거나 줄이면 이 여백도 따라 바뀐다). */
.b06-cross-card {
overflow: hidden;
border: 1px solid var(--color-border);
border-radius: var(--radius-cards);
background: var(--color-surface-raised);
scroll-margin-top: var(--b06-cross-scroll-margin, 240px);
/* 카드를 **자체 쌓임 맥락**으로 만든다. 이게 없으면 카드 안 오버레이(줌 버튼·면적 표)의
z-index가 최상위 축에서 겨루게 되고, 상단 패널(sticky, z-index 2)과 같은 층에서 DOM
순서로 이겨 **스크롤할 때 패널 위로 삐져나온다**(2026-08-02 사용자 지적). */
isolation: isolate;
}
/* 상단 슬라이드 패널(종단면도 + 유토곡선).
높이는 인라인이 아니라 CSS 변수로만 받는다 — 인라인 height는 접기 규칙을 이겨
패널이 접히지 않는다(ui_template_resizer.ts 주석). sticky는 그 자체로 위치 지정
요소라 리사이저 손잡이(absolute)의 기준이 되므로 position: relative가 따로 필요 없다. */
.b06-section__panel {
position: sticky;
z-index: 2;
top: 0;
display: flex;
flex-direction: column;
height: var(--b06-profile-height, auto);
border: 1px solid var(--color-border);
border-radius: var(--radius-cards);
background: var(--color-surface-raised);
/* `overflow: hidden`을 걸지 않는다 — 접기 손잡이가 패널 경계 **밖 아래**에 떠 있어
잘라내면 통째로 사라진다(B05 하단 패널도 같은 이유로 걸지 않는다). 안쪽 스크롤은
`.b06-section__panel-body`가 맡는다. */
}
/* 최소 높이는 **펼친 상태에만** 건다. 접힘에도 걸리면(인라인 min-height가 그랬다) 접어도
그 높이만큼 빈 배경이 남는다. 값은 JS가 `--b06-profile-min-height`로 실어 준다. */
.b06-section__panel:not(.is-collapsed) {
min-height: var(--b06-profile-min-height, 360px);
}
/* 접으면 높이를 0으로 접고 알맹이를 숨긴다 — 패널 밖 아래에 뜬 손잡이만 남아 다시 펼 수 있다.
(B05 하단 패널과 같은 방식: 전역 `ui-collapsible` 규칙에 기대지 않고 여기서 명시한다.) */
.b06-section__panel.is-collapsed {
height: 0;
min-height: 0;
}
.b06-section__panel.is-collapsed > header,
.b06-section__panel.is-collapsed > .b06-section__panel-body,
.b06-section__panel.is-collapsed > .ui-resizer {
display: none;
}
/* 공용 리사이저는 손잡이를 패널 위쪽 경계에 두지만(B05 하단 패널 기준),
이 패널은 화면 위에 붙어 있으므로 아래쪽 경계로 옮긴다(아래로 끌면 커짐). */
.b06-section__panel > .ui-resizer--vertical {
top: auto;
/* 경계에 그대로 둔다 — 손잡이가 패널 밖으로 나가 z축으로 덮으므로 자리를 비켜 줄 필요가 없다. */
bottom: 0;
height: 8px;
}
/* 상시 표시선은 히트 영역 가운데가 아니라 **패널 아래 경계**에 정확히 붙인다 —
이 리사이저는 히트 영역이 패널 안쪽(bottom 0~8px)에 있어 가운데 정렬이면 표시선이
경계에서 4px 위(요약 막대 위)로 넘어간다(2026-08-04 사용자 지적). */
.b06-section__panel > .ui-resizer--vertical::before {
top: auto;
bottom: 0;
transform: none;
}
/* position: relative — 유토곡선 범례가 이 상자를 기준으로 우측 상단에 겹친다. */
.b06-section__panel-body {
position: relative;
display: flex;
flex: 1;
flex-direction: column;
gap: var(--spacing-8);
min-height: 0;
padding-bottom: var(--spacing-8);
overflow: hidden;
}
.b06-section__panel > header,
.b06-cross-card > header,
.b06-cross-card > footer {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--spacing-8);
padding: var(--spacing-8) var(--spacing-16);
background: var(--color-surface);
color: var(--color-text-secondary);
font-size: var(--text-caption);
}
/* 상태 문구만 담는 행 — 비어 있을 때가 많아 높이를 최소로 둔다. */
.b06-section__panel > header {
justify-content: center;
padding-block: 2px;
border-bottom: 1px solid var(--color-border);
}
/* B05 하단 패널 손잡이와 **같은 구조**로, 상하만 뒤집는다: 공용 `--bottom` 손잡이(64×24)를
패널 경계 **바깥**으로 내보내 리사이저를 z축으로 덮는다. 패널 안 흐름에 두면 리사이저와
위아래로 나란히 서서, 손잡이를 누르러 가는 길에 리사이저 hover 색이 먼저 켜진다
(2026-08-02 사용자 지적). 이 때문에 패널에는 `overflow: hidden`을 걸지 않는다. */
.b06-section__panel-toggle {
z-index: 3;
top: auto;
bottom: calc(-1 * var(--spacing-24));
border-top: 0;
/* 아래 테두리도 좌우와 같은 강조색 — 색이 섞이면 외곽선이 틀어져 보인다(2026-08-04). */
border-bottom: 1px solid var(--panel-handle-border, var(--color-border));
border-radius: 0 0 var(--radius-buttons) var(--radius-buttons);
}
/* 종단면도와 유토곡선이 이 컨테이너 하나를 공유한다 — 가로 스크롤을 함께 써야
같은 측점이 두 그래프에서 같은 자리에 선다. wrap을 나누면 정렬이 깨진다. */
.b06-section__chart-wrap {
display: flex;
flex: 1;
flex-direction: column;
width: 100%;
min-height: 0;
overflow-x: auto;
/* 한 축만 auto로 두면 브라우저가 나머지 축도 auto로 올려 세로 스크롤이 생긴다.
높이는 `chartHeights()`가 정확히 맞추므로 잘릴 것도 없다. */
overflow-y: hidden;
}
.b06-section__chart {
display: block;
max-width: none;
}
/* 상단 패널의 두 그래프는 세로로 눌리면 안 된다. flex 기본값(shrink: 1)으로 두면 패널을
줄였을 때 렌더 높이가 height 속성보다 작아지고, SVG는 preserveAspectRatio 기본값이 meet라
**가로까지 같은 비율로 축소**된다(그래프 폭이 줄어 보이던 원인). */
.b06-section__chart-wrap > .b06-section__chart {
flex: none;
}
/* 횡단도 줌·팬 영역(E-5) — 휠 줌/드래그 팬. */
.b06-cross-card__chart-wrap {
position: relative;
}
/* 좌클릭은 선택, 팬은 가운데 버튼 드래그다 — 손 모양 커서는 실제로 미는 동안에만 띄운다. */
.b06-cross-card__chart-wrap .b06-section__chart {
touch-action: none;
}
.b06-cross-card__chart-wrap .b06-section__chart.is-panning,
.b06-section__chart-wrap.is-panning {
cursor: grabbing;
}
/* 확대해도 선 굵기 고정(E-5) — 줌 시 해상도 저하처럼 보이던 문제 해소. */
.b06-section__chart line,
.b06-section__chart polyline,
.b06-section__chart polygon,
.b06-section__chart rect {
vector-effect: non-scaling-stroke;
}
.b06-cross-card > .b06-section__chart,
.b06-cross-card__chart-wrap > .b06-section__chart {
width: 100%;
}
.b06-section__grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(min(480px, 100%), 1fr));
gap: var(--spacing-16);
}
.b06-cross-card {
cursor: pointer;
transition:
border-color var(--transition-fast),
box-shadow var(--transition-fast);
}
/* 마우스 hover 회색 강조는 선택 강조(빨강)보다 하위 — 미선택 카드에만 적용해
:not(...):hover 특정도가 selected 규칙을 넘어 빨강 테두리를 덮지 않게 한다. */
.b06-cross-card:not(.b06-cross-card--selected):hover {
border-color: var(--color-text-muted);
box-shadow: var(--shadow-sm);
}
.b06-cross-card--selected {
border-color: var(--color-danger);
box-shadow: 0 0 0 2px var(--color-danger);
}
/* 제목행 1행 구조(E-2/E-3): 측점 라벨 → 단면유형 pill → 지반유형 → 구조물·kind. */
.b06-cross-card > header {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: var(--spacing-8);
border-bottom: 1px solid var(--color-border);
overflow-x: auto;
}
.b06-cross-card__title,
.b06-cross-card > header > .b06-cross-card__meta {
display: flex;
align-items: baseline;
gap: var(--spacing-8);
flex: 0 0 auto;
}
/* 사면 미교차 경고 — 면적이 계산 반폭에서 잘린 측점(2026-09-03). */
.b06-cross-card__warning {
color: var(--color-warning);
white-space: nowrap;
}
/* 성토사면 경사길이 — 제목행 우측 끝(2026-09-03). meta 의 마지막 칸이라 우측 맞춤이다. */
.b06-cross-card__fillslope {
color: var(--color-text-muted);
white-space: nowrap;
}
/* 지반유형 세그먼트(D-6): 제목행 1행 내 인라인 배치. 좌측 구분선(3번). */
.b06-design__seg--header {
flex: 0 0 auto;
justify-content: flex-start;
}
.b06-cross-card__ground {
padding-left: var(--spacing-8);
border-left: 1px solid var(--color-border);
}
/* 단면유형 pill(E-3) — 구조물 pill과 동일 표기. */
.b06-cross-card__mode {
flex: 0 0 auto;
padding: 1px 6px;
border-radius: var(--radius-pills);
background: color-mix(in srgb, var(--color-info, rgb(37 99 235)) 14%, transparent);
color: var(--color-info, rgb(37 99 235));
font-size: var(--text-caption);
white-space: nowrap;
}
/* 단면유형 pill을 지반유형 우측·우측 맞춤으로 밀어 붙인다(1번). meta는 그 뒤에 따라온다. */
.b06-cross-card__mode--right {
margin-left: auto;
}
.b06-cross-card > header strong {
color: var(--color-text);
font-size: var(--text-body-sm);
}
/* 비정규 측점의 구조물 라벨 — kind(일반측점/BP/EP) 좌측에 자수정색 pill로 표기. */
.b06-cross-card__structure {
padding: 1px 6px;
border-radius: var(--radius-pills);
background: color-mix(in srgb, var(--color-royal-amethyst, rgb(109 40 217)) 16%, transparent);
color: var(--color-royal-amethyst, rgb(109 40 217));
font-size: var(--text-caption);
white-space: nowrap;
}
.b06-cross-card > footer {
border-top: 1px solid var(--color-border);
}
.b06-section__empty {
display: flex;
min-height: 180px;
align-items: center;
justify-content: center;
padding: var(--spacing-24);
border: 1px dashed var(--color-text-muted);
color: var(--color-text-muted);
background: var(--color-surface);
}
/* SVG 차트 색상은 테마 토큰만 사용한다. */
.b06-chart__bg {
fill: var(--color-surface-raised);
}
/* 세로 창을 변환으로 옮기는 겹(2026-09-04) — 세로로 눌리거나 늘어나도 선 굵기·점선 간격은
그대로여야 한다. 변환이 걸려 있을 때만 적용해 B06 종단·횡단은 예전 그대로 그려진다. */
.b06-chart__ywindow[transform] * {
vector-effect: non-scaling-stroke;
}
.b06-chart__grid {
stroke: var(--color-border);
stroke-width: 1;
}
.b06-chart__axis {
stroke: var(--color-text-secondary);
stroke-width: 1;
}
.b06-chart__tick,
.b06-chart__station-label,
.b06-chart__axis-label {
fill: var(--color-text-secondary);
font-family: var(--font-body);
}
.b06-chart__tick,
.b06-chart__station-label {
font-size: 10px;
}
/* 구조물(비정규) 측점 이름 — 종단면도 측점선 상단 표기. */
.b06-chart__structure-label {
font-size: 9px;
font-weight: var(--font-weight-medium);
fill: rgb(180 83 9);
}
.b06-chart__axis-label {
fill: var(--color-text-body);
font-size: var(--text-caption);
font-weight: var(--font-weight-medium);
}
.b06-chart__profile {
fill: none;
stroke: var(--color-chart-0);
stroke-width: 2.5;
}
.b06-chart__cross-profile {
fill: none;
stroke: var(--color-chart-1);
stroke-width: 2.4;
}
/* 종단 계획선(시공계획고): 지반선과 구분되도록 파선 + 강조색 */
.b06-chart__design-profile {
fill: none;
stroke: var(--color-royal-amethyst);
stroke-width: 2.2;
stroke-dasharray: 8 4;
}
/* 절토(계획고가 지반고보다 낮음) / 성토 구간 음영 */
.b06-chart__band {
stroke: none;
}
.b06-chart__band--cut {
fill: rgb(220 38 38 / 14%);
}
.b06-chart__band--fill {
fill: rgb(37 99 235 / 14%);
}
.b06-chart__balance-boundary {
stroke: var(--color-royal-amethyst);
stroke-width: 1.2;
stroke-dasharray: 3 3;
opacity: 0.55;
}
.b06-chart__station {
cursor: pointer;
}
/* 구조물 측점선은 끌어 옮길 수 있다 — 커서로 그 사실을 알린다(2026-08-01 사용자 지시). */
.b06-chart__station--draggable {
cursor: ew-resize;
touch-action: none;
}
.b06-chart__station-line {
stroke-width: 1.2;
}
.b06-chart__station-hit {
stroke: transparent;
stroke-width: 14;
pointer-events: stroke;
}
.b06-chart__station-line--bp,
.b06-chart__station-line--regular {
stroke: var(--color-warning);
}
.b06-chart__station-line--ep {
stroke: var(--color-accent);
stroke-dasharray: 4 3;
}
/* 비정규 측점(구조물)은 종단면도에서도 자수정색 파선으로 구분한다(B05 종단 규칙과 정합).
미선택 시 stroke 미정의로 세로선이 안 그려지던 문제 해소. */
.b06-chart__station-line--irregular {
stroke: var(--color-royal-amethyst, rgb(139 92 246));
stroke-width: 1.4;
stroke-dasharray: 5 3;
}
.b06-chart__station-line--selected,
.b06-chart__center-marker {
stroke: var(--color-danger);
stroke-width: 2.5;
}
/* 십자선이 계획 노선(계획고) 위치를 가리킬 때: 종단 계획선과 동일 색으로 대응시킨다 */
.b06-chart__center-marker--design {
stroke: var(--color-royal-amethyst);
}
.b06-chart__station--selected .b06-chart__station-label {
fill: var(--color-danger);
font-weight: var(--font-weight-bold);
}
/* 측점 표준횡단 설계 지정 컨트롤 (카드 헤더 아래) — 좌측 여유 추가(2번).
한 행 고정(nowrap): 넘치는 컨트롤은 다음 행으로 밀지 않고 "⋯" 드롭다운으로 이동(N-2-2). */
.b06-design {
display: flex;
flex-wrap: nowrap;
align-items: center;
gap: var(--spacing-8) var(--spacing-16);
padding: var(--spacing-8) 0 var(--spacing-8) var(--spacing-16);
border-bottom: 1px solid var(--color-border);
margin-bottom: var(--spacing-8);
}
/* 오버플로 드롭다운(N-2-2): 넘친 컨트롤을 담는 우측 맞춤 "⋯" 메뉴. 세로 배치. */
.b06-design__more {
position: relative;
flex: 0 0 auto;
margin-left: auto;
}
.b06-design__more-summary {
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 22px;
border: 1px solid var(--color-border);
border-radius: var(--radius-inputs);
background: var(--color-surface);
color: var(--color-text-secondary);
font-size: 0.9rem;
line-height: 1;
cursor: pointer;
list-style: none;
}
.b06-design__more-summary::-webkit-details-marker {
display: none;
}
.b06-design__more[open] .b06-design__more-summary {
color: var(--color-surface);
background: var(--color-royal-amethyst);
border-color: var(--color-royal-amethyst);
}
.b06-design__more-panel {
position: absolute;
right: 0;
top: calc(100% + 4px);
z-index: 3;
display: flex;
flex-direction: column;
gap: var(--spacing-8);
padding: var(--spacing-8);
border: 1px solid var(--color-border);
border-radius: var(--radius-inputs);
background: var(--color-surface-raised);
box-shadow: var(--shadow-sm);
}
.b06-design__seg {
display: flex;
align-items: center;
gap: var(--spacing-8);
}
.b06-design__seg-legend {
font-size: 0.72rem;
color: var(--color-text-secondary);
white-space: nowrap;
}
/* 선행 조건 미충족 옵션(E-6) — 상시 노출하되 흐리게·클릭 불가. */
.b06-design__seg--disabled {
opacity: 0.4;
}
.b06-design__seg--disabled .b06-design__btn {
cursor: not-allowed;
}
.b06-design__seg-buttons {
display: inline-flex;
border: 1px solid var(--color-border);
border-radius: var(--radius-inputs);
overflow: hidden;
}
.b06-design__btn {
padding: 3px 8px;
font-size: 0.72rem;
color: var(--color-text-secondary);
background: var(--color-surface);
border: none;
border-left: 1px solid var(--color-border);
cursor: pointer;
}
.b06-design__btn:first-child {
border-left: none;
}
.b06-design__btn--active {
color: var(--color-surface);
background: var(--color-royal-amethyst);
font-weight: var(--font-weight-medium);
}
.b06-design__areas {
display: inline-flex;
gap: var(--spacing-8);
margin-left: auto;
font-size: 0.72rem;
font-family: var(--font-mono);
}
/* 암 경계선 제어(E-7) — 하단 정보 행 **가운데 맞춤**(2026-08-06 사용자 지시).
방위각 삭제로 space-between 3분할 배치가 깨져 좌측에 붙었다 — 절대 배치로
행 중앙에 고정한다(중심고·반폭 버튼과 겹치지 않는 폭). */
.b06-cross-card > footer {
position: relative;
}
.b06-cross-card__rockb {
position: absolute;
left: 50%;
transform: translateX(-50%);
flex: 0 0 auto;
border-radius: var(--radius-inputs);
padding: 1px 4px;
}
/* 측점별 암 절토 경사(2026-09-07) — 암 경계선이 행 가운데를 절대 배치로 쓰므로
이 칸은 행의 **오른쪽 끝**에 둔다. 중심고(왼쪽)와 겹치지 않는다. */
.b06-cross-card__cutslope {
margin-left: auto;
flex: 0 0 auto;
border-radius: var(--radius-inputs);
padding: 1px 4px;
}
.b06-design__cutslope-input {
width: 44px;
padding: 0 2px;
font-size: 0.72rem;
font-family: var(--font-mono);
text-align: right;
color: var(--color-text);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: var(--radius-inputs);
}
/* 화살표(스피너)는 칸이 좁아 지운다 — 값은 직접 넣거나 ↺ 로 되돌린다. */
.b06-design__cutslope-input::-webkit-outer-spin-button,
.b06-design__cutslope-input::-webkit-inner-spin-button {
margin: 0;
appearance: none;
}
.b06-design__cutslope-unit {
padding: 0 2px 0 1px;
font-size: 0.72rem;
color: var(--color-text-secondary);
align-self: center;
}