Files
Aislo/B08_Quantity/B08_Quantity_UI_EarthworkGrid_Style.ts
T
eomsangdonandClaude Opus 5 f952ac7ffd fix(git): 병합이 떨군 파일 22개와 되돌아간 파일 35개를 되살림
무슨 일이 있었나
랩탑 줄의 병합 `20ba886c`(Merge origin/main_desktop_1·main_laptop_1·sub_desktop_1 into
sub_laptop_1)가 우리 파일 22개를 떨구고 35개 파일의 내용을 옛것으로 되돌림. 손으로 지운
커밋은 없고 **병합 자체가 떨군 것**임. 그것이 `origin/dev`·`main_laptop_1`·`sub_laptop_1`·
`CODEX` 까지 퍼졌고(데스크탑 둘만 무사), 이 창의 병합 `d92c1f2b` 로 들어옴.

잃었던 것
- 공용 — `common_util_provenance.py` · `ui_template_provenance.ts`
- B08 — 근거 사전 · 좌측 패널 상자 모듈 · 토량환산계수 칸
- B09 — 근거 사전 셋
- B05 — 계획노선 편집 모듈 아홉 · 지형 라우터 · B04 지도 모듈
- 시험 셋과, 35개 파일 안의 최근 작업(환산계수 고르기 · 근거 호버 배선 등)

어떻게 되살렸나
`611a2b40`(병합 직전, 전부 온전)에서 `git show <커밋>:<경로>` 로 내용만 꺼내 되돌림.
이력은 안 건드림. ⚠ HEAD 에만 있던 「추가 816줄」은 랩탑의 새 작업이 아니라 **되살아난
옛 코드**였음(B05 편집은 모듈로 쪼개기 전 덩어리 · B08 라우터는 환산계수 고르기 전 옛
상수판). 되돌릴 시점 이후의 **진짜 새 커밋은 둘뿐**이라 그 둘만 패치로 다시 얹음 —
`9f827bf6`(리로드 빌드 고리 끊기, 데스크탑 보조) · `b9bca6b3`(B06 조정창 1px, 랩탑).
위키 여덟은 코덱스 몫이라 손대지 않음.

자체검증 — 양쪽 작업이 다 살아 있음을 짚어 확인: `main.py` 의 「개발 서버는 살려 둔다」 ·
`B05_Profile_Engine_Grade.py` 의 `plan_curve_length_limit_m` · `B08_..._EarthworkGrid.ts` 의
`attachProvenance`. `tsc --noEmit` 통과 · `pytest -q` **1317 passed, 28 skipped**
(되살리기 전에는 시험 둘이 수집 단계에서 깨져 있었음).

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

231 lines
8.4 KiB
TypeScript

/* =============================================================================
* B08_Quantity_UI_EarthworkGrid_Style.ts
* 토적표 그리드 스타일. 한 번만 주입한다.
*
* 실무 산출서와 눈으로 대조되는 것이 이 표의 목적이라, 장식보다 **줄·칸이 또렷한 것**을
* 우선한다. 숫자는 등폭으로 두어 자릿수가 세로로 맞는다.
* ========================================================================== */
const STYLE_ID = "b08-earthwork-grid-style";
/* 색은 전부 프로젝트 테마 변수를 쓴다 — 어두운 테마에서 머리글이 묻히지 않아야 한다.
글자색을 배경과 함께 지정하는 까닭이 그것이다(배경만 주면 상속색이 배경에 잠긴다). */
const CSS = `
.b08-grid { display: flex; flex-direction: column; gap: 8px; min-width: 0; height: 100%; }
/* 표 안에서 고르는 칸 — 관급/사급처럼 **줄마다 갈리는 값**을 여기서 정한다. */
.b08-grid__select {
width: 100%;
min-width: 5.5rem;
padding: 0.15rem 0.25rem;
font: inherit;
color: var(--color-text);
background: var(--color-surface-raised);
border: 1px solid var(--color-border, rgba(128, 128, 128, 0.4));
border-radius: 3px;
}
.b08-grid__select:disabled {
opacity: 0.45; /* 사급 줄의 설치 주체 — 뜻이 없으므로 흐리게 둔다 */
}
/* 만진 줄은 표시가 남는다 — 무엇을 바꿨는지 보여야 한다. */
.b08-grid__table td.is-changed {
box-shadow: inset 2px 0 0 var(--color-accent, #6c8ebf);
}
/* 잠정값 안내 — 금액에 걸리는 값이 조용히 기본으로 돌지 않게. */
.b08-quantity__notice {
margin: 0.35rem 0 0;
padding: 0.4rem 0.5rem;
font-size: 0.85em;
line-height: 1.4;
color: var(--color-text);
background: var(--color-surface-raised);
border-left: 3px solid var(--color-warning, #c08a3e);
border-radius: 3px;
}
.b08-grid__caption {
margin: 0;
font-size: 12px;
color: var(--color-text-secondary);
}
/* 열이 많아 화면을 넘친다 — 표만 가로로 구르고 페이지 몸통은 안 구른다. */
.b08-grid__scroll { overflow: auto; flex: 1 1 auto; min-height: 0; }
.b08-grid__table {
border-collapse: collapse;
font-size: 12px;
font-variant-numeric: tabular-nums;
white-space: nowrap;
color: var(--color-text-body);
}
.b08-grid__table th,
.b08-grid__table td {
border: 1px solid var(--color-border);
padding: 2px 8px;
text-align: right;
}
.b08-grid__table thead th {
position: sticky;
top: 0;
background: var(--color-surface-raised);
color: var(--color-text);
font-weight: var(--font-weight-medium, 600);
text-align: center;
z-index: 1;
}
/* 측점 열은 왼쪽에 붙어 있어야 가로로 굴러도 어느 줄인지 보인다. */
.b08-grid__station {
position: sticky;
left: 0;
background: var(--color-surface);
color: var(--color-text);
text-align: left;
font-weight: var(--font-weight-medium, 500);
}
.b08-grid__table tfoot td {
background: var(--color-surface-raised);
color: var(--color-text);
font-weight: var(--font-weight-medium, 600);
}
/* 잘린 측점 안내 — 발생률이 21~26 % 로 늘 뜨는 것이라 붉은 오류가 아니라 **주의**로 둔다.
요란하면 곧 무시당한다. 목록은 접어 두고 필요할 때만 편다. */
.b08-grid__warning {
padding: 6px 10px;
font-size: 12px;
color: var(--color-text-body);
background: var(--color-surface-raised);
border-left: 3px solid var(--color-text-muted);
}
.b08-grid__warning-summary { cursor: pointer; }
.b08-grid__warning-list {
display: flex;
flex-wrap: wrap;
gap: 4px 6px;
padding-top: 6px;
}
.b08-grid__warning-station {
font-size: 11px;
padding: 0 6px;
border: 1px solid var(--color-border);
background: var(--color-surface);
color: var(--color-text);
cursor: pointer;
font-variant-numeric: tabular-nums;
}
/* 잘린 줄은 표에서도 알아보게 왼쪽에 표시를 남긴다 — 눈에 띄되 요란하지 않게. */
.b08-grid__table tbody tr.is-unclosed .b08-grid__station {
border-left: 3px solid var(--color-text-muted);
}
.b08-grid__table tbody tr.is-highlighted td {
background: var(--color-royal-amethyst, #d8ccff);
color: #1b2220;
}
.b08-quantity__tabs { display: flex; gap: 4px; border-bottom: 1px solid var(--color-border); }
.b08-quantity__tab {
padding: 4px 12px;
font-size: 13px;
border: 1px solid var(--color-border);
border-bottom: none;
background: var(--color-surface-raised);
color: var(--color-text-secondary);
cursor: pointer;
}
.b08-quantity__tab.is-active {
background: var(--color-surface);
color: var(--color-text);
font-weight: var(--font-weight-medium, 600);
}
.b08-quantity__body { display: flex; flex-direction: column; gap: 8px; padding: 8px; min-height: 0; flex: 1 1 auto; }
.b08-quantity__pane { display: flex; flex-direction: column; min-height: 0; flex: 1 1 auto; }
/* 집계·운반표는 열이 적어 왼쪽 정렬이 읽기 좋다 — 숫자 칸만 오른쪽으로 둔다. */
.b08-grid__table--summary th,
.b08-grid__table--summary td { text-align: left; }
.b08-grid__table--summary td:nth-child(5),
.b08-grid__table--summary td:nth-child(4) { text-align: right; }
.b08-grid__unit { text-align: center; }
.b08-grid__note { white-space: normal; max-width: 26rem; }
/* 「내역 제외」 같은 표시 — 규칙이 코드에만 있으면 잊힌다. 화면에 남긴다. */
.b08-grid__tag {
display: inline-block;
margin-right: 4px;
padding: 0 6px;
font-size: 11px;
border: 1px solid var(--color-border);
color: var(--color-text-secondary);
}
.b08-quantity__input {
width: 5rem;
font-size: 12px;
text-align: right;
font-variant-numeric: tabular-nums;
background: var(--color-surface);
color: var(--color-text);
border: 1px solid var(--color-border);
}
.b08-quantity__message { margin: 0; padding: 16px; font-size: 13px; color: var(--color-text-secondary); }
.b08-quantity__field { display: flex; justify-content: space-between; gap: 8px; font-size: 12px; padding: 2px 0; }
.b08-quantity__field-value { color: var(--color-text-secondary); font-variant-numeric: tabular-nums; }
/* 칸 밑 근거 한 줄 — 왜 그 값인지 화면에서 보이게 한다(2026-09-09 사용자 지시). */
.b08-quantity__hint { margin: 0 0 6px; font-size: 11px; line-height: 1.4; color: var(--color-text-secondary); }
/* 품셈 범위 밖을 고른 칸 — **막지 않고** 사유를 받는 자리라 경고 색만 준다. */
.b08-quantity__hint--warn { color: var(--color-warning, #b45309); }
/* 좌측 산출조건 패널 — B03~B07 과 같은 공통 틀을 쓴다(접히는 상자 ui-collapsible
+ 공통 외곽선 ui-sidebar-section + 바닥 고정 액션 줄 ui-sidebar-actions).
⚠ 테두리는 공용 .ui-sidebar-section(ui_template_overlay.css)이 전담한다 —
여기서 border 를 다시 선언하면 로드 순서상 공통 색을 덮어 흐려진다(B04 주석과 같은 까닭). */
.b08-quantity__panel { display: flex; flex-direction: column; gap: 8px; }
.b08-quantity__section {
display: flex;
flex-direction: column;
gap: 2px;
margin: 0;
padding: 10px 12px;
border-radius: var(--radius-cards);
background-color: var(--color-surface-raised);
}
.b08-quantity__section-title {
margin: 0 0 2px;
font-size: 12px;
font-weight: 600;
color: var(--color-text-secondary);
}
/* 개발 전용 줄 — ⚠ 단추 묶음에 공용 ui-sidebar-actions 를 쓰면 공용 코드가 **이 줄**을
패널 바닥 액션으로 잘못 집어(첫 번째 것을 찾는다) 스크롤 영역이 이 줄 안에 갇히고
아래 칸들이 통째로 잘린다(2026-09-12 사용자 보고). 생김새만 같게 두고 클래스는 따로 쓴다. */
.b08-quantity__dev { display: flex; flex-direction: column; gap: 4px; }
.b08-quantity__dev-actions { display: flex; gap: 8px; }
.b08-quantity__dev-actions > * { flex: 1 1 0; min-width: 0; }
.b08-quantity__note { margin: 0; font-size: 11px; line-height: 1.4; color: var(--color-text-secondary); }
/* 토량환산계수 구획 — 갈래마다 (값 · 안내 · 사유)가 한 덩어리로 붙는다. */
.b08-quantity__factors { margin: 4px 0 10px; }
.b08-quantity__factor { margin-bottom: 6px; }
`;
/** 스타일을 한 번만 넣는다 — 페이지를 다시 그려도 중복되지 않는다. */
export function injectEarthworkGridStyles(): void {
if (document.getElementById(STYLE_ID)) return;
const style = document.createElement("style");
style.id = STYLE_ID;
style.textContent = CSS;
document.head.append(style);
}