feat(view): show the 3D model on the 형상계획 and 철근배근 screens

The 3D viewer (views, canvas, part toggles) moves into a slot at the top of ②
and ③ while those screens are shown, and back to ① otherwise. Moving the
element keeps the WebGL context; the viewer is measured after its screen is
visible.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
ypshimandClaude Haiku 5.5 committed 2026-10-22 12:28:02 +09:00
1 parent e7f57f0a8a
commit 8c96ab76f5
3 files changed
+30 -1

No files matched your search

+3 -1
View File
@@ -67,7 +67,7 @@
</section>
</aside>
<section class="panel viewer" aria-labelledby="viewer-title">
<section class="panel viewer" id="viewer-block" aria-labelledby="viewer-title">
<div class="viewer-head">
<h2 id="viewer-title">교각 3D 뷰</h2>
<div id="views" class="views" role="group" aria-label="시점"></div>
@@ -107,6 +107,7 @@
</main>
<main class="grid plan" id="view-plan" hidden>
<div class="viewer-slot" id="slot-plan"></div>
<aside class="panel piers" aria-labelledby="plan-pier-title">
<p id="plan-pier-title" class="lbl">교각 목록</p>
<div class="phead" aria-hidden="true"><span></span><span>교각</span><span>변경</span></div>
@@ -135,6 +136,7 @@
</main>
<main class="grid rebar" id="view-rebar" hidden>
<div class="viewer-slot" id="slot-rebar"></div>
<aside class="panel piers" aria-labelledby="rebar-pier-title">
<p id="rebar-pier-title" class="lbl">교각 목록</p>
<div class="phead" aria-hidden="true"><span></span><span>교각</span><span>배근</span></div>
+23
View File
@@ -678,11 +678,33 @@ function renderShapeOptions(member: MemberId): void {
}
// --- ② 형상계획 (plan.md C1) -------------------------------------------------------------
/**
* The 3D viewer block (views, canvas, part toggles) is shared: it sits in ① and moves into the top slot of
* ② or ③ while that screen is shown. Moving the element keeps the WebGL context.
*/
const viewerHome: { parent: Node | null; next: Node | null } = { parent: null, next: null };
function captureViewerHome(): void {
const block = $<HTMLElement>('viewer-block');
viewerHome.parent = block.parentElement;
viewerHome.next = block.nextElementSibling;
}
function placeViewer(stage: 'shape' | 'plan' | 'rebar'): void {
const block = $<HTMLElement>('viewer-block');
const slot = stage === 'plan' ? $<HTMLElement>('slot-plan') : stage === 'rebar' ? $<HTMLElement>('slot-rebar') : null;
if (slot) slot.appendChild(block);
else if (viewerHome.parent) viewerHome.parent.insertBefore(block, viewerHome.next);
resize();
}
function setStage(stage: 'shape' | 'plan' | 'rebar'): void {
state.stage = stage;
$<HTMLElement>('view-shape').hidden = stage !== 'shape';
$<HTMLElement>('view-plan').hidden = stage !== 'plan';
$<HTMLElement>('view-rebar').hidden = stage !== 'rebar';
// Move the viewer after its new parent is visible, so that resize() measures the real size.
placeViewer(stage);
// The highlight is the 'current' class (style.css); aria-current is set alongside it for assistive tech.
const nav: [string, 'shape' | 'plan' | 'rebar'][] = [['nav-shape', 'shape'], ['nav-plan', 'plan'], ['nav-rebar', 'rebar']];
for (const [id, key] of nav) {
@@ -1378,6 +1400,7 @@ $<HTMLButtonElement>('discard-backup').addEventListener('click', () => {
});
// --- start ------------------------------------------------------------------------------
captureViewerHome();
buildPartToggles();
buildViewButtons();
renderDefaults();
+4
View File
@@ -687,3 +687,7 @@ body { background: #eef1f4; font-family: 'Noto Sans KR', system-ui, sans-serif;
.rebar-table th:nth-child(9),
.rebar-table th:nth-child(10) { text-align: right; }
.rebar-table td:nth-child(12) { white-space: normal; min-width: 240px; }
/* 3D viewer shared by ①, ②, ③: the slot takes the full width above the other columns */
.viewer-slot { flex: 1 1 100%; min-width: 0; }
.viewer-slot > .viewer { width: 100%; }