feat(B06): [상세] 공용 큰 창 · 밖 누르면 닫힘 · 그림 위 크게 → 글 → 표 · 스크롤 없음(150-2)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JywHxQuWPk6UYXTebyrBoU
This commit is contained in:
@@ -2,11 +2,11 @@
|
||||
* B06_Section_UI_Cross_Detail.ts
|
||||
* 횡단 카드 측구 줄 맨 오른쪽 [상세] → 모달(138-4).
|
||||
*
|
||||
* 맨 위 = 도면 표 위 글(`design.structure_note` · 세션 초안 → [저장]이 patch 로 내보냄 ·
|
||||
* 위 = 카드 그림 복제(어두운 바탕 · CAD 꼴 · 남은 높이를 채움) ·
|
||||
* 가운데 = 도면 표 위 글(`design.structure_note` · 세션 초안 → [저장]이 patch 로 내보냄 ·
|
||||
* B07 횡단도 수량표 바로 위에 줄마다 찍힘 — `_Engine_Cad_Table.cross_note_lines`) ·
|
||||
* 가운데 = 카드 그림 복제(어두운 바탕 · CAD 꼴) · 아래 = B07 횡단 수량표(값 · 칸 배치 모두
|
||||
* 서버 `cross-quantity-table` 한 길 · 캐시 설계로 · 열 때 부름).
|
||||
* 막은 투명(누름 통과) · 자리는 그 카드를 안 가리는 넓은 쪽(좌/우 → 위/아래).
|
||||
* 아래 = B07 횡단 수량표(값 · 칸 배치 모두 서버 `cross-quantity-table` 한 길 · 캐시 설계로 · 열 때 부름).
|
||||
* 공용 큰 창(`wide`) · 공용 막(밖 누르면 닫힘) · 스크롤 없음(150-2).
|
||||
* 모달이 열린 채 카드를 다시 그리면 그림 · 표를 새로 맞춤.
|
||||
* ========================================================================== */
|
||||
|
||||
@@ -64,51 +64,6 @@ function writeNote(section: CrossSection, text: string): void {
|
||||
writeState("structure-notes", all, host.projectId, routeId);
|
||||
}
|
||||
|
||||
/* ── 자리 ───────────────────────────────────────────────────────────── */
|
||||
|
||||
const GAP_PX = 12;
|
||||
const MIN_WIDTH_PX = 360;
|
||||
const MAX_WIDTH_PX = 720;
|
||||
|
||||
/** 카드를 안 가리는 넓은 쪽에 붙임 — 좌우가 좁으면 위아래. */
|
||||
function place(dialog: HTMLElement, card: HTMLElement | null): void {
|
||||
const vw = window.innerWidth;
|
||||
const vh = window.innerHeight;
|
||||
const rect = card?.getBoundingClientRect();
|
||||
const style = dialog.style;
|
||||
style.position = "fixed";
|
||||
style.margin = "0";
|
||||
if (!rect) {
|
||||
style.width = `${Math.min(MAX_WIDTH_PX, vw - GAP_PX * 2)}px`;
|
||||
style.left = `${(vw - Math.min(MAX_WIDTH_PX, vw - GAP_PX * 2)) / 2}px`;
|
||||
style.top = `${GAP_PX}px`;
|
||||
style.maxHeight = `${vh - GAP_PX * 2}px`;
|
||||
return;
|
||||
}
|
||||
const left = rect.left;
|
||||
const right = vw - rect.right;
|
||||
if (Math.max(left, right) >= MIN_WIDTH_PX + GAP_PX * 2) {
|
||||
const width = Math.min(MAX_WIDTH_PX, Math.max(left, right) - GAP_PX * 2);
|
||||
const top = Math.min(Math.max(rect.top, GAP_PX), vh - GAP_PX - 240);
|
||||
style.width = `${width}px`;
|
||||
style.left = `${right >= left ? rect.right + GAP_PX : rect.left - GAP_PX - width}px`;
|
||||
style.top = `${Math.max(top, GAP_PX)}px`;
|
||||
style.maxHeight = `${vh - Math.max(top, GAP_PX) - GAP_PX}px`;
|
||||
return;
|
||||
}
|
||||
const width = Math.min(MAX_WIDTH_PX, vw - GAP_PX * 2);
|
||||
style.width = `${width}px`;
|
||||
style.left = `${Math.min(Math.max(rect.left, GAP_PX), vw - GAP_PX - width)}px`;
|
||||
const below = vh - rect.bottom;
|
||||
if (below >= rect.top) {
|
||||
style.top = `${rect.bottom + GAP_PX}px`;
|
||||
style.maxHeight = `${Math.max(below - GAP_PX * 2, 160)}px`;
|
||||
} else {
|
||||
style.top = `${GAP_PX}px`;
|
||||
style.maxHeight = `${Math.max(rect.top - GAP_PX * 2, 160)}px`;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 표(B07 칸 배치 그대로) ─────────────────────────────────────────────── */
|
||||
|
||||
function cell(
|
||||
@@ -182,9 +137,8 @@ export function openCrossDetail(section: CrossSection): void {
|
||||
title: `${title} 상세`,
|
||||
closeLabel: "닫기",
|
||||
dialogClass: "b06-detail",
|
||||
backdropClass: "b06-detail__backdrop",
|
||||
wide: true,
|
||||
mount(body, close) {
|
||||
const dialog = body.parentElement as HTMLElement;
|
||||
const note = document.createElement("textarea");
|
||||
note.className = "ui-input b06-detail__note";
|
||||
note.rows = 3;
|
||||
@@ -195,7 +149,7 @@ export function openCrossDetail(section: CrossSection): void {
|
||||
cad.className = "b06-detail__cad";
|
||||
const tableBox = document.createElement("div");
|
||||
tableBox.className = "b06-detail__table";
|
||||
body.append(note, cad, tableBox);
|
||||
body.append(cad, note, tableBox);
|
||||
|
||||
// 표 칸 누름 = 그림 그 항목 강조 · 다시 누르면 끔(150-4)
|
||||
let picked: string | null = null;
|
||||
@@ -220,7 +174,6 @@ export function openCrossDetail(section: CrossSection): void {
|
||||
const card = cardOf(state.section);
|
||||
const svg = card?.querySelector("svg.b06-section__chart");
|
||||
cad.replaceChildren(svg ? svg.cloneNode(true) : document.createTextNode("그림 없음"));
|
||||
place(dialog, card);
|
||||
showPicked();
|
||||
},
|
||||
loadTable() {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* =============================================================================
|
||||
* B06_Section_UI_Style_Cross_Detail.css
|
||||
* 횡단 카드 [상세] 단추 · 모달(138-4) — 투명 막(누름 통과) · 글 칸 · CAD 꼴 그림 · B07 수량표.
|
||||
* 횡단 카드 [상세] 단추 · 모달(138-4 · 150-2) — 공용 큰 창 · 그림(남은 높이) → 글 칸 → B07 수량표.
|
||||
* ========================================================================== */
|
||||
|
||||
/* 측구 줄 맨 오른쪽 — ⋯ 가 보이면 그 옆, 숨으면 혼자 오른쪽 끝 */
|
||||
@@ -13,25 +13,23 @@
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
/* 막은 투명 · 누름은 아래 화면으로 — 모달 몸만 받음 */
|
||||
.ui-modal__backdrop.b06-detail__backdrop {
|
||||
background-color: transparent;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.b06-detail__backdrop > .ui-modal.b06-detail {
|
||||
pointer-events: auto;
|
||||
overflow: auto;
|
||||
box-shadow: var(--shadow-lg);
|
||||
/* 스크롤 없음 — 글 · 표는 제 높이 · 그림이 남은 높이를 채움 */
|
||||
.ui-modal.b06-detail > .ui-modal__body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.b06-detail__note {
|
||||
flex: none;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
resize: vertical;
|
||||
resize: none;
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.b06-detail__table {
|
||||
flex: none;
|
||||
}
|
||||
|
||||
/* 가벼운 CAD 꼴 — 어두운 바탕 · 카드 그림 복제에 다크 색 토큰을 덮어씀 */
|
||||
.b06-detail__cad {
|
||||
--color-bg: #111111;
|
||||
@@ -50,12 +48,15 @@
|
||||
background: #111111;
|
||||
color: #f2f2f2;
|
||||
overflow: hidden;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
/* viewBox 그대로 — 비율 유지하며 칸 가득 */
|
||||
.b06-detail__cad > svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* B07 횡단 수량 산출표 — 칸 배치 그대로 */
|
||||
|
||||
@@ -76,3 +76,17 @@ def test_상세_표_칸_키_누름_배선():
|
||||
assert "element.dataset.key = key" in src
|
||||
assert "highlightDetail(svg, state.section.design, picked)" in src
|
||||
assert 'closest<HTMLElement>("[data-key]")' in src
|
||||
|
||||
|
||||
def test_상세_큰_창_공용_막_그림_글_표_순서():
|
||||
"""150-2 — 투명 막 지움(밖 누르면 공용 닫기) · 큰 창 · 그림 → 글 → 표 · 스크롤 없음."""
|
||||
base = os.path.join(ROOT, "B06_Section")
|
||||
with open(os.path.join(base, "B06_Section_UI_Cross_Detail.ts"), encoding="utf-8") as f:
|
||||
src = f.read()
|
||||
with open(os.path.join(base, "B06_Section_UI_Style_Cross_Detail.css"), encoding="utf-8") as f:
|
||||
css = f.read().replace(chr(13), "")
|
||||
assert "wide: true" in src and "backdropClass" not in src
|
||||
assert "body.append(cad, note, tableBox)" in src
|
||||
assert "pointer-events: none" not in css and "b06-detail__backdrop" not in css
|
||||
assert ".ui-modal.b06-detail > .ui-modal__body {\n overflow: hidden;" in css
|
||||
assert "flex: 1;\n min-height: 0;" in css and "height: 100%;" in css
|
||||
|
||||
Reference in New Issue
Block a user