feat(view): drop the 3D title band; pier name in 교각 목록; view menu under 부재 표시, origin under 색 기준
On wide screens the pier name moves to the 교각 목록 (name · type), the band is gone and the 3D view takes the full height. A left stack holds 부재 표시 and then 시점; a right stack holds 색 기준 and then the ⓘ origin note. The active pier is scrolled into view when it changes. Below 600 px the title is kept above the model and the stacks sit below it. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
e66c259d1e
commit
86a4b2fd80
3 files changed
+50
-39
No files matched your search
+21
-15
@@ -74,27 +74,33 @@
|
||||
<section class="panel viewer" id="viewer-block" aria-labelledby="viewer-title">
|
||||
<div class="viewer-head">
|
||||
<h2 id="viewer-title">교각 3D 뷰</h2>
|
||||
</div>
|
||||
<div id="canvas" class="canvas" aria-label="교각 3D 뷰"></div>
|
||||
<div class="viewer-stack viewer-stack-left">
|
||||
<details class="info-panel parts-info" id="parts-info">
|
||||
<summary>부재 표시 <span id="parts-count" class="panel-state"></span></summary>
|
||||
<div id="parts"></div>
|
||||
</details>
|
||||
<div class="views-menu" id="views-menu">
|
||||
<button id="views-toggle" class="btn views-toggle" type="button" aria-haspopup="true" aria-expanded="false" aria-controls="views">시점</button>
|
||||
<div id="views" class="views views-list" role="group" aria-label="시점" hidden></div>
|
||||
</div>
|
||||
<button id="origin-toggle" class="info-btn" type="button" aria-label="원점 표기" aria-expanded="false" aria-controls="origin">ⓘ</button>
|
||||
<span id="origin" class="origin-pop muted" hidden></span>
|
||||
</div>
|
||||
<div id="canvas" class="canvas" aria-label="교각 3D 뷰"></div>
|
||||
<details class="info-panel parts-info" id="parts-info">
|
||||
<summary>부재 표시 <span id="parts-count" class="panel-state"></span></summary>
|
||||
<div id="parts"></div>
|
||||
</details>
|
||||
<details class="info-panel color-info" id="color-info" hidden>
|
||||
<summary>색 기준 <span id="color-state" class="panel-state"></span></summary>
|
||||
<div class="rebar-color" id="rebar-color" role="group" aria-label="철근 색 기준">
|
||||
<button type="button" data-color="role" aria-pressed="true">역할</button>
|
||||
<button type="button" data-color="diameter" aria-pressed="false">직경</button>
|
||||
<button type="button" data-color="member" aria-pressed="false">부재</button>
|
||||
<ul id="rebar-legend" class="rebar-legend"></ul>
|
||||
<div class="viewer-stack viewer-stack-right">
|
||||
<details class="info-panel color-info" id="color-info" hidden>
|
||||
<summary>색 기준 <span id="color-state" class="panel-state"></span></summary>
|
||||
<div class="rebar-color" id="rebar-color" role="group" aria-label="철근 색 기준">
|
||||
<button type="button" data-color="role" aria-pressed="true">역할</button>
|
||||
<button type="button" data-color="diameter" aria-pressed="false">직경</button>
|
||||
<button type="button" data-color="member" aria-pressed="false">부재</button>
|
||||
<ul id="rebar-legend" class="rebar-legend"></ul>
|
||||
</div>
|
||||
</details>
|
||||
<div class="origin-wrap">
|
||||
<button id="origin-toggle" class="info-btn" type="button" aria-label="원점 표기" aria-expanded="false" aria-controls="origin">ⓘ</button>
|
||||
<span id="origin" class="origin-pop muted" hidden></span>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<aside class="panel results" aria-labelledby="result-title">
|
||||
|
||||
+10
-1
@@ -314,7 +314,8 @@ function renderPierRows(list: HTMLUListElement, mode: Stage): void {
|
||||
pier.checked = check.checked;
|
||||
renderAll();
|
||||
});
|
||||
const name = el('button', pier.input.name, 'pier-name');
|
||||
// The pier name and type are shown here, since the 3D title band is gone on wide screens (승인 2026-10-23).
|
||||
const name = el('button', `${pier.input.name} · ${pier.input.type}`, 'pier-name');
|
||||
name.type = 'button';
|
||||
name.setAttribute('aria-pressed', String(pier.input.name === state.active));
|
||||
name.addEventListener('click', () => {
|
||||
@@ -372,6 +373,7 @@ const pt = (p: { e: number; n: number }) => `E ${p.e.toFixed(3)}, N ${p.n.toFixe
|
||||
function renderPanels(): void {
|
||||
const pier = activePier();
|
||||
const m = pier?.model;
|
||||
// The pier name is in 교각 목록 on wide screens; the title keeps it on narrow ones (승인 2026-10-23).
|
||||
$('viewer-title').textContent = pier ? `${pier.input.name} · ${pier.input.type} 교각 형상` : '교각 3D 뷰';
|
||||
$('origin').textContent = m ? `원점 = 교각 중심 (${pt(m.pivot)})` : '';
|
||||
|
||||
@@ -447,8 +449,15 @@ function renderDefaults(): void {
|
||||
for (const text of items) box.appendChild(el('li', text));
|
||||
}
|
||||
|
||||
/** Pier last brought into view in 교각 목록, so that a manual scroll is not undone by the next render. */
|
||||
let lastScrolledPier: string | null = null;
|
||||
|
||||
function renderAll(): void {
|
||||
updatePartsCount();
|
||||
if (state.active !== lastScrolledPier) {
|
||||
lastScrolledPier = state.active;
|
||||
document.querySelector<HTMLButtonElement>('.pier-items [aria-pressed="true"]')?.scrollIntoView({ block: 'nearest' });
|
||||
}
|
||||
$('status').textContent = state.piers.length ? '' : '엑셀을 불러오면 교각 형상을 생성합니다.';
|
||||
renderHeader();
|
||||
renderPierList();
|
||||
|
||||
+19
-23
@@ -761,36 +761,32 @@ body { background: #eef1f4; font-family: 'Noto Sans KR', system-ui, sans-serif;
|
||||
.rebar-table thead th { position: sticky; top: 0; background: #ffffff; z-index: 1; }
|
||||
}
|
||||
|
||||
/* 3D 영역 (승인 2026-10-23): 상단 띠와 좌·우 정보창을 캔버스 위에 얇게 띄움. 조작 요소 44px. */
|
||||
/* 3D 영역 (승인 2026-10-23): 제목은 교각 목록으로. 좌측 스택(부재 표시 · 시점), 우측 스택(색 기준 · 원점)을 캔버스 위에 둠. */
|
||||
#viewer-block { position: relative; }
|
||||
#viewer-block .viewer-head { position: absolute; top: 8px; left: 8px; right: 8px; z-index: 5; margin: 0; height: 44px; padding: 0 10px; flex-wrap: nowrap; background: rgba(255, 255, 255, 0.92); border-radius: 6px; }
|
||||
#viewer-block .viewer-head h2 { font-size: 14px; white-space: nowrap; }
|
||||
#viewer-block .viewer-head #origin { margin-left: auto; font-size: 12px; white-space: nowrap; }
|
||||
#viewer-block .info-panel { position: absolute; z-index: 5; top: 60px; max-height: calc(100% - 120px); overflow: auto; background: rgba(255, 255, 255, 0.92); border: 1px solid #d5dde6; border-radius: 6px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12); padding: 0 8px 8px; font-size: 13px; }
|
||||
#viewer-block .viewer-head { display: none; }
|
||||
.viewer-stack { display: flex; flex-direction: column; gap: 8px; width: 168px; z-index: 5; pointer-events: none; }
|
||||
.viewer-stack > * { pointer-events: auto; }
|
||||
#viewer-block .viewer-stack-left { position: absolute; top: 8px; left: 8px; }
|
||||
#viewer-block .viewer-stack-right { position: absolute; top: 8px; right: 8px; width: 180px; align-items: stretch; }
|
||||
#viewer-block .info-panel { background: rgba(255, 255, 255, 0.92); border: 1px solid #d5dde6; border-radius: 6px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12); padding: 0 8px 8px; font-size: 13px; }
|
||||
#viewer-block .info-panel > summary { min-height: 44px; display: flex; align-items: center; cursor: pointer; font-weight: 700; color: #18212b; list-style-position: outside; }
|
||||
#viewer-block .parts-info { left: 8px; width: 168px; }
|
||||
#viewer-block .color-info { right: 8px; width: 180px; }
|
||||
#viewer-block .parts-info #parts label { display: flex; align-items: center; gap: 8px; min-height: 44px; }
|
||||
#viewer-block .parts-info .parts-row { display: flex; flex-direction: column; align-items: stretch; gap: 0; }
|
||||
#viewer-block .color-info .rebar-color { display: flex; flex-direction: column; align-items: stretch; gap: 0; margin: 0; }
|
||||
#viewer-block .color-info .rebar-color button { width: 100%; }
|
||||
#viewer-block .color-info .rebar-legend { flex-direction: column; gap: 4px; padding: 6px 0 0; }
|
||||
@media (max-width: 600px) {
|
||||
#viewer-block .viewer-head { position: static; height: auto; flex-wrap: wrap; }
|
||||
#viewer-block .info-panel { position: static; width: auto; max-height: none; margin: 8px 0 0; }
|
||||
}
|
||||
#viewer-block .parts-info .parts-row { display: flex; flex-direction: column; align-items: stretch; gap: 0; }
|
||||
|
||||
/* 좁은 창 (601–1100px, 승인 2026-10-23): 상단 띠에서 시점 버튼과 원점 표기를 오른쪽으로 */
|
||||
@media (min-width: 601px) and (max-width: 1100px) {
|
||||
#viewer-block .viewer-head #views-menu { margin-left: auto; }
|
||||
}
|
||||
|
||||
/* 3D band (승인 2026-10-23): view menu, origin popover, collapsed panel summaries */
|
||||
#viewer-block .views-menu { position: relative; margin-left: auto; }
|
||||
#viewer-block .views-toggle { min-height: 44px; font-weight: 700; background: #eef3f8; border: 1px solid #c3cbd4; }
|
||||
#viewer-block #views.views-list { position: absolute; top: 100%; right: 0; z-index: 10; display: flex; flex-direction: column; gap: 0; min-width: 120px; padding: 4px; margin-top: 4px; background: #ffffff; border: 1px solid #c3cbd4; border-radius: 6px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15); }
|
||||
#viewer-block .views-menu { position: relative; }
|
||||
#viewer-block .views-toggle { width: 100%; min-height: 44px; font-weight: 700; background: #eef3f8; border: 1px solid #c3cbd4; border-radius: 6px; }
|
||||
#viewer-block #views.views-list { position: absolute; top: 100%; left: 0; right: 0; z-index: 10; display: flex; flex-direction: column; gap: 0; padding: 4px; margin-top: 4px; background: #ffffff; border: 1px solid #c3cbd4; border-radius: 6px; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.15); }
|
||||
#viewer-block #views.views-list .btn { min-height: 44px; justify-content: flex-start; text-align: left; border: 0; background: transparent; }
|
||||
#viewer-block #views.views-list .btn[aria-pressed='true'] { background: #1f5fa8; color: #ffffff; }
|
||||
#viewer-block .origin-wrap { position: relative; align-self: flex-end; }
|
||||
#viewer-block .info-btn { min-width: 44px; min-height: 44px; border: 1px solid #c3cbd4; border-radius: 6px; background: #ffffff; font-size: 16px; cursor: pointer; }
|
||||
#viewer-block .origin-pop { position: absolute; top: 100%; right: 8px; z-index: 10; margin-top: 4px; padding: 6px 10px; background: #ffffff; border: 1px solid #c3cbd4; border-radius: 6px; font-size: 12px; white-space: nowrap; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12); }
|
||||
#viewer-block .origin-pop { position: absolute; top: 100%; right: 0; z-index: 10; margin-top: 4px; padding: 6px 10px; background: #ffffff; border: 1px solid #c3cbd4; border-radius: 6px; font-size: 12px; white-space: nowrap; box-shadow: 0 4px 14px rgba(0, 0, 0, 0.12); }
|
||||
#viewer-block .panel-state { font-weight: 400; color: #55606e; margin-left: 6px; font-size: 12px; }
|
||||
@media (max-width: 600px) {
|
||||
#viewer-block .viewer-head { display: flex; height: auto; margin: 0 0 8px; }
|
||||
#viewer-block .viewer-stack { position: static; width: auto; margin-top: 8px; pointer-events: auto; }
|
||||
#viewer-block .viewer-stack-left, #viewer-block .viewer-stack-right { position: static; width: auto; }
|
||||
#viewer-block .origin-wrap { align-self: flex-start; }
|
||||
}
|
||||
Reference in new issue
Block a user