Files
Aislo/B06_Section/B06_Section_UI_Style_Cross.css
T
eomsangdonandClaude Opus 5 6a3c991b5a feat(B06): 기슭막이 선택·X 자리 조절 화살표
- 기슭막이 폴리곤을 눌러 고르면 강조되고 카드 하단에 ◀/▶/↺가 뜬다. 0.1m씩
  화면 좌우로 민다(좌우 벽에서 뜻이 뒤집히지 않게 outward 부호는 내부 환산).
- 구조물 선택이 절·성토 면적 강조보다 우선한다. 벽 클릭은 벽에서 멈춰 밴드·카드
  선택으로 번지지 않고, 한쪽을 고르면 다른 쪽 강조는 꺼진다.
- computeCulvertLayout에 revetShift 인자 추가. 손으로 민 자리는 고정되어 관 길이
  정수 맞춤이 벽을 다시 옮기지 않고 폭으로만 흡수한다(폭 상한 0.45×2).
- 값은 세션 보관(b06:revetx), 이동 범위 ±3m.
- 700줄 정리: Page 751→690(_UI_Page_Station_Controls 분리),
  Cross_View 765→629(_UI_Cross_View_Zoom 분리).

검증: 이동 5케이스 접점 오차 0.00e+00, 계획고 스윕 24케이스 회귀 문제 0,
tsc 통과, pytest 148 pass. 화면 조작 확인은 다음 단계.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-21 16:14:17 +09:00

612 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;
}
/* 지반유형 세그먼트(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);
}
.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-08-06 사용자 지시). */
.b06-cross-card__widthctl {
display: flex;
flex: 0 0 auto;
gap: 2px;
margin-left: auto;
}
/* 줌 버튼 — 그래프 우측 상단 오버레이. 휠 대신 쓰는 조작구다(2026-08-02 사용자 확정). */
.b06-cross-card__zoom {
position: absolute;
top: var(--spacing-4);
right: var(--spacing-4);
z-index: 2;
display: flex;
overflow: hidden;
border: 1px solid var(--color-border);
border-radius: var(--radius-inputs);
background: color-mix(in srgb, var(--color-surface-raised) 88%, transparent);
}
.b06-cross-card__zoom-btn {
width: 22px;
height: 22px;
padding: 0;
border: none;
border-left: 1px solid var(--color-border);
background: none;
color: var(--color-text-secondary);
font-size: 0.8rem;
line-height: 1;
cursor: pointer;
}
.b06-cross-card__zoom-btn:first-child {
border-left: none;
}
.b06-cross-card__zoom-btn:hover {
color: var(--color-text);
background: var(--color-surface);
}
/* 기슭막이 선택 — 구조물 선택이 절·성토 면적 강조보다 우선한다(2026-08-21 사용자 ①).
색은 토큰만 쓰고 하드코딩하지 않는다. */
.b06-chart__culvert-revet.is-selectable {
cursor: pointer;
}
.b06-chart__culvert-revet.is-active {
stroke: var(--color-accent, currentColor);
stroke-width: 2;
filter: brightness(1.15);
}