From 65e751e4bbfdf50d0ce70e12fccd4c07b2dc8af1 Mon Sep 17 00:00:00 2001 From: ypshim Date: Fri, 23 Oct 2026 11:08:20 +0900 Subject: [PATCH] feat(view): fixed frame with its own scroll for the side panels, the table and the model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On wide screens the page is one fixed-height frame: the left list, the centre (model band on top, table below) and the right results each scroll on their own. The model band is 280–560 px (48vh) and the 3D view follows its container through a ResizeObserver. The table header stays on top. Below 1100 px the panels stack with a 60vh height limit. Co-Authored-By: Claude Haiku 5.5 --- src/main.ts | 2 ++ src/style.css | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/main.ts b/src/main.ts index 73da512..89d73af 100644 --- a/src/main.ts +++ b/src/main.ts @@ -168,6 +168,8 @@ function resize(): void { camera.updateProjectionMatrix(); } window.addEventListener('resize', resize); +// The 3D view follows its container, which changes when the layout frame or a panel size changes (승인 2026-10-23). +new ResizeObserver(() => resize()).observe(container); resize(); function animate(): void { diff --git a/src/style.css b/src/style.css index 17f5820..1624611 100644 --- a/src/style.css +++ b/src/style.css @@ -734,3 +734,29 @@ body { background: #eef1f4; font-family: 'Noto Sans KR', system-ui, sans-serif; .toast { display: flex; align-items: center; gap: 10px; background: #1b2733; color: #ffffff; padding: 10px 14px; border-radius: 6px; font-size: 13px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.2); } .toast.error { background: #8a1c1c; } .toast .btn { min-height: 44px; } + +/* --- 고정 프레임 (승인 2026-10-23): 좌측 창 · 중앙 상단(모델) · 중앙 하단 · 우측 창, 각각 독립 스크롤 --- */ +@media (min-width: 1101px) { + .app { height: 100vh; overflow: hidden; } + .top, .status-row { flex: none; } + .grid { flex: 1 1 auto; min-height: 0; height: auto; grid-template-rows: minmax(0, 1fr); padding-bottom: 12px; } + .grid > .panel, .grid > .center-col { min-height: 0; overflow: auto; } + .grid > #viewer-block { display: flex; flex-direction: column; } + .grid > #viewer-block .canvas { flex: 1 1 auto; height: auto; min-height: 240px; } + /* ②–④: the model keeps a fixed band at the top; the table scrolls below it. */ + .center-col { display: grid; grid-template-rows: clamp(280px, 48vh, 560px) minmax(0, 1fr); gap: 12px; } + .viewer-slot { min-height: 0; height: 100%; display: flex; flex-direction: column; } + .viewer-slot > .viewer { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; } + .viewer-slot .canvas { flex: 1 1 auto; height: auto; min-height: 180px; } + .center-col > .viewer { min-height: 0; overflow: auto; } + .rebar-table thead th, .quantity .rebar-table thead th { position: sticky; top: 0; background: #ffffff; z-index: 1; } +} + +@media (max-width: 1100px) { + .app { min-height: 100vh; } + .grid { height: auto; } + .grid > .panel, .grid > .center-col { max-height: 60vh; overflow: auto; } + .center-col { display: flex; flex-direction: column; gap: 16px; } + .viewer-slot .canvas { height: 60vh; min-height: 420px; flex: none; } + .rebar-table thead th { position: sticky; top: 0; background: #ffffff; z-index: 1; } +}