Files
Aislo/B05_Profile/B05_Profile_UI_Style_RouteEdit.css
T
eomsangdonandClaude Opus 5 abe956affc feat(b05): 계획노선 편집 손질 여섯 — 교각점 고정·횡단 벡터화·라벨 맨 위
- 교각점 고정 단추: 켜면 그 꺾임점이 노드·손잡이 어느 쪽으로도 안 끌림(`apexLock`)
- 계획 횡단선을 절·성토 교차점에서 끊음 — 그 밖은 원지반이라 지반선과 겹쳐 그려지던 것.
  교차점은 B06 이 쓰는 `toeOffsets` 를 열어 함께 씀
- 횡단 판을 캔버스에서 SVG 로 바꾸고 휠 줌·가운데 끌기 팬·더블클릭 원복을 붙임
- 줌·팬 본문을 `A00_Common/b_svg_zoom_pan.ts` 공용 조각으로 빼고 B06 은 감싸개만 남김
- 「이전 횡단」 판은 계획 평면선이 바뀔 때만 채움(같은 측점 두 번 누르기로는 안 채움)
- 「거리 재기」 단추의 눌린 꼴 추가 · 등고선 높이 라벨을 맨 위로 올리고 더 촘촘히

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DJffo4VwgTumVUM3kdbJzd
2026-09-12 20:37:13 +09:00

435 lines
11 KiB
CSS

/* 계획노선 편집 모달 — 큰 모달 하나. 계산이 오래 걸리는 조작이라 화면을 통째로 덮는다
(2026-09-06 사용자 확정). 색은 전부 테마 토큰을 쓴다. */
.b05-routeedit {
position: fixed;
inset: 0;
z-index: var(--z-modal, 1000);
display: flex;
gap: var(--spacing-12);
align-items: center;
justify-content: center;
padding: var(--spacing-12);
background: rgb(0 0 0 / 55%);
}
/* 메인 창은 **왼쪽**, 횡단 두 판은 오른쪽 세로 칸(2026-09-12 사용자 지시 ⑱).
좁은 화면에서는 오른쪽 칸이 접히고 메인이 폭을 다 가진다. */
.b05-routeedit__box {
position: relative;
display: flex;
flex: 1 1 auto;
flex-direction: column;
max-width: 1200px;
height: min(820px, 92vh);
overflow: hidden;
border: 1px solid var(--color-border);
border-radius: var(--radius-16, 12px);
background: var(--color-surface-raised);
box-shadow: 0 12px 40px rgb(0 0 0 / 45%);
}
.b05-routeedit__head {
display: flex;
flex: none;
align-items: center;
gap: var(--spacing-12);
padding: var(--spacing-12) var(--spacing-16);
border-bottom: 1px solid var(--color-border);
}
/* 제목행 — 왼쪽에 이름, **오른쪽 끝에 단추 묶음**(2026-09-12 사용자 지시 ⑪).
단추는 공용 규격(`.ui-btn`)을 그대로 쓴다 — 모달이 따로 만든 크기를 걷어냈다. */
.b05-routeedit__actions {
display: flex;
flex: 1 1 auto;
flex-wrap: wrap;
align-items: center;
justify-content: flex-end;
gap: var(--spacing-8);
}
.b05-routeedit__close {
flex: none;
border: none;
background: none;
color: var(--color-text-secondary);
font-size: 16px;
cursor: pointer;
}
.b05-routeedit__canvas-wrap {
position: relative;
flex: 1 1 auto;
min-height: 0;
background: var(--color-surface);
}
.b05-routeedit__canvas {
display: block;
width: 100%;
height: 100%;
touch-action: none;
}
/* 지도 위에 얹는 판들 — 아래 정보행을 없애고 여기로 옮겼다(2026-09-12 사용자 지시 ⑩·⑫·⑬).
글판은 **클릭을 통과시킨다** — 지도 조작을 가리면 안 된다. */
.b05-routeedit__hint,
.b05-routeedit__info {
position: absolute;
z-index: 1;
padding: var(--spacing-8) var(--spacing-12);
border: 1px solid color-mix(in srgb, var(--color-border) 65%, transparent);
border-radius: var(--radius-8, 6px);
background: color-mix(in srgb, var(--color-surface-raised) 78%, transparent);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
color: var(--color-text-secondary);
font-size: var(--text-caption);
pointer-events: none;
}
/* 조작 설명 — **2열**로 묶는다(사용자 지시 ⑩). 한 줄로 늘어놓으면 창이 좁을 때 접힌다. */
.b05-routeedit__hint {
top: var(--spacing-12);
left: var(--spacing-12);
display: grid;
grid-template-columns: auto auto;
gap: 2px var(--spacing-16);
max-width: 52%;
}
/* 상태·범례 — 지도 왼쪽 아래(사용자 지시 ⑫). */
.b05-routeedit__info {
bottom: var(--spacing-12);
left: var(--spacing-12);
display: flex;
flex-direction: column;
gap: 4px;
max-width: 62%;
}
/* 회전 단추 — 지도 오른쪽 위(사용자 지시 ⑯). 여기만 클릭을 받는다. */
.b05-routeedit__spin {
position: absolute;
top: var(--spacing-12);
right: var(--spacing-12);
z-index: 1;
display: flex;
gap: var(--spacing-8);
}
.b05-routeedit__spin .ui-btn {
padding: var(--spacing-8) var(--spacing-12);
font-size: var(--text-body);
line-height: 1;
}
.b05-routeedit__legend {
display: inline-flex;
align-items: center;
gap: var(--spacing-8);
}
.b05-routeedit__legend i {
display: inline-block;
width: 22px;
height: 0;
margin-right: 2px;
vertical-align: middle;
}
.b05-routeedit__legend i.is-expected {
border-top: 2px dashed var(--color-text-secondary, #9ca3af);
}
.b05-routeedit__legend i.is-planned {
border-top: 2px solid var(--map-route, #f97316);
}
/* 오른쪽 세로 칸 — 위아래 반씩 나눠 **지금 횡단**과 **이전 횡단**이 앉는다. */
.b05-routeedit__side {
display: flex;
flex: none;
flex-direction: column;
gap: var(--spacing-12);
width: 452px;
height: min(820px, 92vh);
}
@media (width < 1500px) {
/* 자리가 모자라면 오른쪽 칸을 접는다 — 지도가 먼저다. */
.b05-routeedit__side {
display: none;
}
}
.b05-routeedit__cross {
display: flex;
flex: 1 1 0;
min-height: 0;
flex-direction: column;
gap: var(--spacing-8);
padding: var(--spacing-12);
overflow: hidden;
border: 1px solid var(--color-border);
border-radius: var(--radius-16, 12px);
background: var(--color-surface-raised);
box-shadow: 0 12px 40px rgb(0 0 0 / 45%);
}
.b05-routeedit__cross-head {
display: flex;
flex: none;
align-items: baseline;
gap: var(--spacing-8);
}
.b05-routeedit__cross-station {
color: var(--color-text-secondary);
font-size: var(--text-caption);
}
/* 횡단 판 — 캔버스 그림이 아니라 **SVG 선**이다(2026-09-12 사용자 지시 ③).
휠 = 확대·축소, 가운데 버튼 끌기 = 이동, 더블클릭 = 원복(공용 조각이 맡음). */
.b05-routeedit__cross-box {
flex: 1 1 auto;
min-height: 150px;
overflow: hidden;
width: 100%;
border: 1px solid var(--color-border);
border-radius: var(--radius-8, 6px);
background: var(--color-surface);
}
.b05-routeedit__cross-svg {
display: block;
width: 100%;
height: 100%;
cursor: default;
}
.b05-routeedit__cross-svg.is-panning {
cursor: grabbing;
}
.b05-routeedit__cross-axis {
stroke: rgb(148 163 184 / 70%);
stroke-width: 1;
stroke-dasharray: 4 4;
}
.b05-routeedit__cross-ground {
fill: none;
stroke: var(--color-text-secondary, #94a3b8);
stroke-width: 1.6;
vector-effect: non-scaling-stroke;
}
.b05-routeedit__cross-design {
fill: none;
stroke: var(--map-route, #f97316);
stroke-width: 2.2;
vector-effect: non-scaling-stroke;
}
.b05-routeedit__cross-key {
fill: var(--color-text-secondary, #94a3b8);
font-size: 11px;
}
.b05-routeedit__cross-key.is-design {
fill: var(--map-route, #f97316);
}
.b05-routeedit__cross-foot {
flex: none;
color: var(--color-text-secondary);
font-size: var(--text-caption);
line-height: 1.5;
}
/* ㉓ 거리 재기와 되돌리기 사이 구분선. */
.b05-routeedit__divider {
width: 1px;
height: 20px;
margin: 0 var(--spacing-4, 4px);
background: var(--color-border);
}
/* ㉔ 잰 값 — 지도 오른쪽 아래 작은 창. 닫으면 잰 것이 지워진다. */
.b05-routeedit__measure {
position: absolute;
right: var(--spacing-12);
bottom: var(--spacing-12);
z-index: 1;
display: flex;
align-items: flex-start;
gap: var(--spacing-8);
max-width: 52%;
padding: var(--spacing-8) var(--spacing-12);
border: 1px solid color-mix(in srgb, #22c55e 60%, transparent);
border-radius: var(--radius-8, 6px);
background: color-mix(in srgb, var(--color-surface-raised) 82%, transparent);
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
color: var(--color-text-body);
font-size: var(--text-caption);
}
/* 글이 길면 접힌다 — flex 자식은 기본으로 안 줄어들어 왼쪽으로 넘쳐 잘렸다(2026-09-12). */
.b05-routeedit__measure-text {
min-width: 0;
line-height: 1.5;
}
.b05-routeedit__measure-close {
flex: none;
border: none;
background: none;
color: var(--color-text-secondary);
cursor: pointer;
}
/* 재계산 중에는 화면 전체를 덮는다 — 결과를 기다릴 수밖에 없는 조작(CLAUDE.md 5장). */
.b05-routeedit__busy {
position: absolute;
inset: 0;
z-index: 2;
display: flex;
align-items: center;
justify-content: center;
padding: var(--spacing-16);
background: rgb(0 0 0 / 55%);
color: #fff;
text-align: center;
}
/* R 라벨 — 고른 꺾임점 **옆에 떠 있는** 작은 상자(2026-09-07 사용자 지시 ③).
예전에는 모달 맨 아랫줄이라 지금 고른 것이 지도 어디인지 눈으로 안 이어졌다.
자리는 `_Label.ts` 가 매 프레임 잡아 준다 — 여기서는 모양만 정한다. */
.b05-routeedit__label {
/* `document.body` 에 붙여 **화면 기준**으로 띄운다 — 모달이 `overflow: hidden` 이라
안에 두면 가장자리에서 잘린다(2026-09-07 사용자 지시 1). */
position: fixed;
z-index: calc(var(--z-modal, 1000) + 1);
display: flex;
flex-direction: column;
gap: 4px;
min-width: 12rem;
padding: var(--spacing-8);
border: 1px solid var(--color-border);
border-radius: var(--radius-8, 6px);
/* 모달 본체와 **같은 토큰**을 쓴다 — `--color-surface-2` 는 이 테마에 없어 밝은
기본값으로 떨어지면서 다크 화면에서 글자가 안 읽혔다(2026-09-07 실화면). */
background: var(--color-surface-raised);
color: var(--color-text-body);
box-shadow: 0 4px 14px rgb(0 0 0 / 45%);
font-size: var(--text-caption);
}
/* 머리는 **잡아 옮기는 자리**다 — 패널이 곡선을 가리면 손으로 치울 수 있어야 한다. */
.b05-routeedit__label-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--spacing-8, 8px);
cursor: move;
touch-action: none;
}
.b05-routeedit__label-close {
border: none;
background: none;
color: var(--color-text-secondary);
font-size: 13px;
line-height: 1;
cursor: pointer;
}
/* 고정 단추 — 켜지면 색이 찬다. 켠 값은 노드를 옮겨도 안 바뀐다. */
.b05-routeedit__lock {
padding: 1px 6px;
border: 1px solid var(--color-border);
border-radius: var(--radius-4, 4px);
background: var(--color-surface);
color: var(--color-text-secondary);
font: inherit;
cursor: pointer;
}
.b05-routeedit__lock.is-on {
border-color: transparent;
background: var(--color-primary, #7c3aed);
color: #fff;
}
.b05-routeedit__lock:disabled {
color: var(--color-text-secondary);
cursor: default;
}
/* 「칸을 비우면 자동」 — 상태 설명과 줄을 나눈다(2026-09-07 사용자 지시 4). */
.b05-routeedit__curve-note {
color: var(--color-text-secondary);
}
.b05-routeedit__label-toggle {
padding: 1px 6px;
border: 1px solid var(--color-border);
border-radius: var(--radius-4, 4px);
background: var(--color-surface);
color: var(--color-text-body);
font: inherit;
cursor: pointer;
}
.b05-routeedit__label-toggle:disabled {
color: var(--color-text-secondary);
cursor: default;
}
.b05-routeedit__curve-label {
font-weight: 600;
white-space: nowrap;
}
.b05-routeedit__curve-field {
display: flex;
align-items: center;
justify-content: space-between;
gap: 6px;
white-space: nowrap;
}
.b05-routeedit__curve-radius,
.b05-routeedit__curve-arc {
width: 5rem;
padding: 2px 6px;
border: 1px solid var(--color-border);
border-radius: var(--radius-4, 4px);
background: var(--color-surface);
color: var(--color-text-body);
font: inherit;
text-align: right;
}
.b05-routeedit__curve-radius:disabled,
.b05-routeedit__curve-arc:disabled {
color: var(--color-text-secondary);
}
.b05-routeedit__curve-info {
max-width: 15rem;
color: var(--color-text-secondary);
line-height: 1.35;
}
/* 「거리 재기」가 켜진 동안 — 단추가 눌린 꼴로 남는다(2026-09-12 사용자 지시 ⑥).
`is-active` 는 예전에도 붙고 있었으나 꼴이 없어 겉모습이 그대로였다. */
.b05-routeedit__actions .ui-btn.is-active {
border-color: transparent;
background: var(--color-primary, #7c3aed);
color: #fff;
}