feat(sw): viewer header logo menu and file list; simplify SW Storybook

SW Viewer Header (approved optional props, public export names unchanged at 170):
- SWViewerHeader: logoMenu, documents, activeDocumentId, onDocumentSelect,
  onDocumentOpen, documentOpenLabel; measured responsive toggles (full ->
  icons -> More) with the SWHeader More / file-list styling.
- SW AI guides, ExampleH, viewer/text-weight/theme-inheritance tests and the
  logo-document-menu tests with their QA record.

SW Storybook cleanup (Story IDs and test fixtures kept):
- Sidebar: Overview -> "SW Header" (first), 전체 Footer -> "SW Footer",
  Footer/Viewer responsive pages -> "Responsive More", Viewer content-variants
  -> "Settings Popup" (merges the settings popup page).
- Hidden by the verification tag (direct URL kept): Header Dropdown,
  Document Open, Interactive States, Document Dropdown, Hidden Sub Controls,
  Home Menu standalone; Footer tool groups / custom content / parts;
  SW/Theme; Viewer menu groups / item states / variable size / settings
  panel / Header + Dropdown.
- Removed demo UI: Overview size and callback output (demo data and
  selection only with &verification), Home Menu Figma node table and width
  demo, Header Responsive More reduced to 1440 / 768 / 390, Popup reopen
  button, property-panel comparison captions, Viewer context-menu demo
  (&verification only).
- Tests: storybook-inventory records the hidden and renamed Stories;
  overview / range-cross-browser / viewer open the demo controls with
  &verification; integrated-popup reloads the Story instead of the reopen
  button; dropdown-consolidation asserts Dropdown is hidden from the sidebar
  and still opens by its direct URL.
This commit is contained in:
shkim4 committed 2026-10-21 14:52:46 +09:00
1 parent 46119eacb5
commit 23d522c136
57 files changed
+2623 -333

No files matched your search

@@ -0,0 +1,109 @@
# SW Viewer Header — 로고 파일 열기 메뉴 · 파일명 샘플 목록 · 반응형 (2026-10-21)
기준: `main` 46119ea (미커밋 작업). 승인 범위: `SWViewerHeader` optional Props `logoMenu`, `documents`, `activeDocumentId`, `onDocumentSelect` 추가.
## 1. 동작
| 클릭 대상 | 표시 | 재사용 |
|---|---|---|
| Viewer 로고 | 로고 아래 파일 열기 메뉴(Open) | `SWHeaderMore`(SWHeader 반응형 `...` 메뉴) — 같은 면·행·Hover·Escape·바깥 클릭·ArrowUp/Down |
| 파일명 | 파일명 아래 샘플 문서 목록 | `SWHeaderDocumentTitle`(SWHeader 파일명 목록) `showNew={false}` `showOpen={false}` |
| 메뉴의 Open | 기존 파일 열기 명령(`onSelect` → `onCommand({ id: "open", itemId: "open" })`) | — |
| 샘플 항목 | `onDocumentSelect` → 프로그램이 불러오고 `documentTitle`·`activeDocumentId` 갱신 | — |
- 로고 메뉴와 파일명 목록은 하나의 상태(`openMenu`)로 제어해 동시에 열리지 않습니다(포인터·키보드 모두).
- 로고 메뉴: `title`과 보이는 항목이 모두 있을 때만 타이틀 + 구분선, `title`이 없으면 항목만, 보이는 항목이 없으면 메뉴·버튼을 만들지 않고 기존 로고(`onHomeClick`)로 둡니다. `logoMenu`는 `onHomeClick`보다 우선합니다.
- 로고 버튼: `aria-haspopup="menu"`, `aria-expanded`, `aria-controls`, ArrowDown = 첫 항목 Focus, 선택·Escape 후 로고로 Focus 복귀.
- `SWHeaderMore`(내부, 미공개)에 `renderTrigger`·`title`·`label`·`open`/`onOpenChange`·`align`·`className`을 추가했습니다. 기본값은 기존 `...` 버튼 동작 그대로이며, ArrowDown 열기·선택 후 Focus 복귀는 `renderTrigger`를 쓸 때만 적용합니다.
## 2. 반응형 (최종: 실제 가용 폭 기준 3단계, 2026-10-21)
- 고정 Breakpoint 대신 `SWViewerHeader`가 Header 행의 남는 폭을 측정합니다(SWHeader의 measured More와 같은 방식: `data-measuring` 상태에서 각 부분을 자연 폭으로 두고 한 번 측정, 그리지 않음). 파일명은 글자 폭까지만 계산해 짧은 파일명이면 메뉴가 더 오래 유지됩니다.
- ① 공간 충분: 아이콘+텍스트(1440·1100에서 전체 보기·레이어·설정·속성 모두 표시) → ② 부족: `icon`+`iconOnlyBelow` 항목(전체 보기·레이어·설정·속성)을 한 번에 아이콘만(라벨은 Tooltip·접근성 이름) → ③ 더 부족: `collapseBelow` 항목을 하나씩 More로(값이 큰 것, 같으면 오른쪽 먼저: 어두운 배경 → 2.5D → 전체 보기 → 도면 공간). 넓어지면 역순 복귀. 각 단계가 확보한 폭을 기록해 그 폭이 다시 생길 때만 되돌려 경계에서 깜빡이지 않습니다.
- Storybook 기본 구성 실측 경계(파일명 `C0060202-001-평면및종단면도.dwg`): 1080px 이상 기본, 1070px 아이콘만, 810px 이하 More. 380px까지 가로 넘침 없음. 줄일 때와 늘릴 때 같은 폭의 상태가 같습니다. 짧은 파일명(`simple.dxf`)에서는 경계가 더 좁은 폭으로 이동합니다.
- `collapseBelow`·`iconOnlyBelow` Props 형식은 그대로이고 숫자는 순서만 정합니다. `types.ts`의 두 Props 설명("Header width (px) below which…")은 공개 선언 파일 변경을 피하려고 이번에 고치지 않았습니다(`SWViewerHeader`·VIEWER.md에 실제 동작 기재). API 기준 갱신 작업 때 함께 고치는 것을 권합니다.
- 1180px 미만 간격 2px, 560px 미만 상태 글자 숨김은 메뉴 이동이 아닌 CSS container 규칙으로 그대로 둡니다.
## 2a. 레이어 · 위치 · 스타일 (2026-10-21)
- 파일명 목록이 로고를 가리던 원인: 패널 위 표시를 위해 파일명 요소를 z-index 60으로 올려 로고 면(z-index 2) 위로 파일명 칸이 그려졌습니다. SWHeader는 로고(2) > 파일명(1) 순서를 유지합니다. 수정: 내부 순서를 SWHeader와 같게 되돌리고, 메뉴가 열린 동안만 Header 전체를 기존 뷰어 Dropdown 층(SWViewerMenu, 60)으로 올립니다(닫히면 층 없음).
- 로고 메뉴: Header 컨테이너 기준(`top: calc(100% + 1px)`, `left: 0`)으로 Header 하단 경계에 바로 붙고 로고 왼쪽에 맞춤. 크기·간격·글자·구분선은 `SWViewerMenu`(상단 메뉴 + Dropdown)와 같게, 색(면 #0e3d2f·Border·글자·Hover)은 유지.
- 파일명 목록: SWHeader 파일명 목록과 같은 모양 그대로(스타일 변경 없음).
## 3. 공개 API
- Export 170개 유지, 기존 Props 변경·삭제 없음.
- `public-api.check.mjs`: 6/7 PASS. FAIL 1건은 `components/sw-viewer/SWViewerHeader.d.ts` 해시 변경으로, 승인된 optional Props 추가에 따른 예상 차이입니다. 새로 노출된 내부 선언 파일은 없습니다. 승인 JSON·기준 Commit은 수정하지 않았습니다(별도 API 기준 갱신 작업).
## 4. 검증
- `packages/sw/tests/viewer-logo-document-menu.browser.mjs` 42/42 PASS (증거 `docs/sw/viewer-logo-document-menu-qa/`).
- `packages/sw/tests/viewer.browser.mjs` 70/70 PASS (참고 뷰어 비교 단계는 참고 URL 없이 생략). 제거된 샘플·파일 열기 버튼 대상 검사를 새 구조에 맞게 수정했습니다.
- 이 checkout의 `node_modules`에 workspace 링크(`@hm-design/sw` 등)와 `orgchart` 의존성이 없어 루트 `tsc -b`, `build-storybook`은 기존 환경 문제로 실패합니다. Storybook 실행을 위해 `node_modules/@hm-design/sw` → `packages/sw` Junction만 추가했습니다(git 무시 대상).
## 5. 환경 복구 후 재검증 (2026-10-21)
- 원인: `node_modules`가 2026-10-12에 설치된 뒤 Pull로 `package-lock.json`에 workspace 링크 3개(`@hm-design/sw`·`org-chart`·`org-chart-react`)와 `orgchart` 6.0.0 등 13개 패키지가 추가되었으나 `npm install`이 다시 실행되지 않았습니다(설치본에만 있는 패키지·버전 차이 없음).
- 복구: `npm install`(제자리, `npm ci` 아님). npm이 lockfile의 `org-chart-react` `peerDependencies` 키 순서만 바꿔 설치 직전 백업(=HEAD와 동일)으로 되돌렸습니다. `package.json`·lockfile 변경 없음, Git 상태 설치 전과 동일.
- `npm run typecheck`(루트 `tsc -b` + 전체 workspace) PASS, `build:packages` PASS, `build-storybook` PASS.
- Public API: 6/7, FAIL은 `SWViewerHeader.d.ts`뿐. HEAD(46119ea)로 별도 build한 선언 해시가 승인 해시와 같고, 현재와의 차이는 승인된 optional Props 4개, 이미 공개된 `SWHeaderDocument` type import, `document` Prop 설명 한 문장뿐입니다.
| 검사 (storybook-static) | 결과 | 비고 |
|---|---|---|
| viewer-logo-document-menu | 42/42 | |
| viewer-logo-document-menu-firefox (Firefox 158, BiDi) | 30/30 | 증거 `viewer-logo-document-menu-qa/firefox/` |
| viewer | 70/70 | |
| keyboard-dismiss / header-composition / home-menu-integration | 22/22 / 161/161 / 23/23 | |
| integrated-improvement / hover-stability(chromium) / storybook-simplification / footer-popup-viewer-refinement | 90/90 / 201/201 / 9/9 / 47/47 | |
| overview / dropdown-consolidation / storybook-unused-removal | 53/54 / 227/228 / 92/93 | FAIL은 모두 source guard: 승인 목록 밖 변경 파일로 이번 작업 파일만 표시 |
| text-weight | 실패(중단) → 수정 후 아래 §6 | 제거된 `파일 열기`·`샘플` 버튼과 `.hm-sw-viewer-header__document-name`을 찾는 기존 단언 |
| theme-inheritance | 실패(중단) → 수정 후 아래 §6 | Viewer Header 첫 `aria-haspopup="menu"`를 SWViewerMenu(샘플)로 가정, 지금은 로고 메뉴 |
| followup-four | 실패(중단) | 기존 문제: 5faa1f1에서 삭제된 `sw-header--property-bar` Story를 엶 |
## 6. text-weight · theme-inheritance 갱신 (2026-10-21, 사용자 승인)
- **text-weight:** Header의 Bold 대상(파일명·레이어·설정·속성·Model/Layout = 14px 700 + 1.3px 외곽선)은 그대로 단언하고, 제거된 `파일 열기`·`샘플`·`전체 보기`가 Header 라벨에 없음을 확인합니다. 로고 메뉴 Open 행과 샘플 목록 행은 같은 실행에서 SWHeader `...` 메뉴(400, 외곽선 없음)와 SWHeader 파일명 목록(14px 700 + 1.3px 외곽선)을 기준으로 측정해 굵기·크기·글꼴·외곽선이 같아야 PASS입니다. 로고 메뉴 타이틀은 700(외곽선 없음). Hover 시 글자 위치·크기·굵기 유지 검사를 Open 행·파일명·샘플 행·레이어·Layout에 적용합니다.
- **theme-inheritance:** 기존 Dropdown 글꼴 상속 검사를 로고 메뉴와 파일명 버튼·샘플 목록으로 옮겼습니다(기본 Noto Sans KR / 뷰어 테마 Dotum). 추가로 두 메뉴의 배경·글자·Border·Hover 색이 기본에서는 SWHeader 토큰 값과 같고, 가장 바깥 SW 범위에 설정한 색 토큰이 두 메뉴까지 상속되는지 확인합니다(검사 중에만 설정 후 제거). 로고 메뉴 면 `#0e3d2f`는 SWHeader `...` 메뉴의 고정값이라 그 값과 비교합니다. 전체 Story 회귀(새 상속 구조 = 기존 구조) 검사는 그대로입니다.
- **발견·수정한 결함:** theme-inheritance의 뷰어 테마 화면(레이어 패널이 로고 아래에 열린 상태)에서 로고 메뉴가 SWViewerPanel 제목 줄 아래에 깔려 Open을 누를 수 없었습니다. 두 메뉴의 바깥 요소가 z-index 2·1의 쌓임 맥락 안에 있었기 때문입니다. `sw-viewer.css`에 메뉴가 열린 동안만 그 바깥 요소를 기존 뷰어 Dropdown 층(SWViewerMenu, 60)으로 올리는 규칙 한 개를 추가했습니다(닫히면 원래 값). Chromium·Firefox 테스트에 패널 위 표시 검사를 추가했습니다.
| 재실행 (수정 후 storybook-static) | 결과 |
|---|---|
| text-weight / theme-inheritance(회귀 67/67 포함) | 51/51 / 28/28 |
| viewer-logo-document-menu / Firefox 158 | 46/46 / 31/31 |
| viewer / keyboard-dismiss / header-composition / home-menu-integration | 70/70 / 22/22 / 161/161 / 23/23 |
| integrated-improvement / hover-stability / storybook-simplification / footer-popup-viewer-refinement | 90/90 / 201/201 / 9/9 / 47/47 |
| overview / dropdown-consolidation / storybook-unused-removal | 53/54 / 227/228 / 92/93 — source guard만 FAIL(이번 작업 파일 4개) |
| `npm run typecheck`, `build-storybook`, Public API | PASS, PASS, 6/7(`SWViewerHeader.d.ts` 승인 Props 차이만) |
## 7. 반응형 · 레이어 · 로고 메뉴 스타일 재검증 (2026-10-21, 최종 소스)
이번 실행(영향 범위만): `npm run typecheck` PASS, `build-storybook` PASS, `check:ai-docs` PASS, Public API 6/7(`SWViewerHeader.d.ts`만: 승인 Props + 설명 주석).
| 검사 | 결과 |
|---|---|
| viewer-logo-document-menu (Chromium, 폭 Sweep·경계·파일명 길이·열린 상태 Resize·로고 하단 정렬·로고 가림 픽셀·SWViewerMenu 비교 포함) | 58/58 |
| viewer-logo-document-menu-firefox (Firefox 158) | 34/34 |
| viewer / text-weight / theme-inheritance(회귀 67/67 포함) | 70/70 / 52/52 / 28/28 |
이번에 재실행하지 않음(이번 라운드에서 `SWHeaderMore`·`sw.css` 등 SW Header 공통 소스 변경 없음, 직전 결과 유지): keyboard-dismiss 22/22, header-composition 161/161, home-menu-integration 23/23, integrated-improvement 90/90, hover-stability 201/201, storybook-simplification 9/9, footer-popup-viewer-refinement 47/47, overview 53/54·dropdown-consolidation 227/228·storybook-unused-removal 92/93(source guard만 FAIL).
## 8. 로고 메뉴 구성 · 파일명 Open · 최소 너비 · Dropdown 면 통일 (2026-10-21)
- 로고 메뉴: 타이틀 `파일` + 아이콘 `파일 열기`(Storybook fixture). 타이틀과 목록은 한 그룹이라 구분선 없음(`SWHeaderMore`가 타이틀 아래 구분선을 그리지 않음). `logoMenu`는 한 그룹 구조라 그룹 사이 구분선이 생길 경우가 없습니다(여러 그룹은 `SWViewerMenu`의 label·separator 데이터로 표현).
- 파일명 목록: `SWHeaderDocumentTitle`의 `showOpen` 재사용, 목록 아래 SWHeader와 같은 Open(+ New 없음). 목록 외곽 너비 ≥ max(파일명 버튼 외곽 너비, SWHeader 312px), 긴 항목이면 넓어짐(CSS `min-width`·`width: max-content`만). 실측 버튼→목록: 1440 359.5→361.5, 1100 274.5→312, 860 214.5→312, 520/400 140→312, 열린 채 Resize·짧은 파일명도 조건 유지.
- 두 파일 열기 진입점은 같은 `openFile` 핸들러(로고 항목 `onSelect`, 목록 `onDocumentOpen`). 목록 Open은 `onCommand({ id: "open", itemId: "document" })`도 보냄.
- Dropdown 면 통일: 기준 520px More 실측값(면 rgb(245,240,229), Border 1px rgb(137,116,69), 반경 4px, 그림자 0 0 12px rgba(0,0,0,.2), 글자 rgb(10,34,29) Noto Sans KR 14/20 400, 행 36px·좌우 12px·간격 8px, Hover rgba(189,223,212,.45)/rgb(18,51,40), 타이틀 rgb(110,103,89) 12/18 500, 구분선 1px rgb(148,136,113))을 기존 `--hm-sw-viewer-more-*`·`--hm-sw-viewer-menu-*` 토큰으로 로고 메뉴에 적용, Header 메뉴 버튼 Dropdown은 `surface="beige"`. 파일명 목록과 단독 `SWViewerMenu`(우클릭)는 제외.
- 공개 API 추가(보고): `logoMenu.items[].icon?`, `onDocumentOpen?`, `documentOpenLabel?`(모두 Optional, `SWViewerHeader.d.ts` 안, Export 170 유지). 아이콘·Open 핸들러를 넘길 기존 Props가 없어 추가했습니다.
- 검증(최종 소스): typecheck·build-storybook·check:ai-docs PASS, Public API 6/7(`SWViewerHeader.d.ts`만), viewer-logo-document-menu 63/63, Firefox 36/36, viewer 70/70, theme-inheritance 27/27(고정 면 검사 → 토큰 검사로 대체), 이전 실행(같은 라운드 SWHeaderMore 변경 후): text-weight 52/52, keyboard-dismiss 22/22, header-composition 161/161, integrated-improvement 90/90.
## 9. Dropdown 색 최종 (2026-10-21)
- 기준: 일반 SWHeader의 `...` 메뉴(`.hm-sw-header__more-menu`). 로고 메뉴(같은 컴포넌트)는 Viewer 색 재정의를 제거해 sw.css 색을 그대로 사용하고, Header 메뉴 버튼 Dropdown·More(SWViewerMenu, Header 안)는 Viewer 범위 CSS로 같은 값을 칠합니다: 면 #0e3d2f·그림자 0 4px 12px rgba(0,0,0,.5)(sw.css 고정값), Border `--hm-sw-header-object-border-color`, 글자 `--hm-sw-header-text-color`(#f6f4f2), 보조 글자 같은 색 80%, Hover `--hm-sw-header-hover`(rgba(255,255,255,.08), 글자 유지), 구분선 `--hm-sw-header-document-menu-divider-color`. 크기·간격·글자 크기는 SWViewerMenu 그대로. 파일명 목록·단독 SWViewerMenu는 제외.
- 검증(이번 실행): build-storybook, viewer-logo-document-menu 63/63(More·Dropdown·로고 메뉴 색 = 같은 실행에서 측정한 SWHeader `...` 메뉴), Firefox 36/36, viewer 70/70, theme-inheritance 28/28. 재사용: typecheck(직전 CSS 외 소스 변경 없음), text-weight 52/52, keyboard-dismiss 22/22, header-composition 161/161, integrated-improvement 90/90, Public API 6/7(`SWViewerHeader.d.ts`만).
## 10. 파일 열기 버튼으로 변경 (2026-10-21)
- 로고 클릭 메뉴 대신 로고 오른쪽 파일 열기 버튼: SWHeader `documentOpenPlacement="header"`와 같은 `SWToolButton appearance="document"` 면 + 폴더 아이콘(테두리·배경 없음, 사용자 요청) + `documentOpenLabel`. 기존 `onDocumentOpen`·`documentOpenLabel` 재사용(목록 아래 Open → Header 버튼). 공간이 부족하면 측정 2단계에서 라벨만 숨김(Tooltip·접근성 이름 `파일 열기`), More로 이동하지 않음.
- 파일명 목록: Open·+ New 없음(`showOpen={false}`), 최소 너비 조건 유지. 파일명 영역 컨테이너(`.hm-sw-viewer-header__document`)는 Open 버튼 뒤에서 왼쪽 padding 0, 파일명 버튼(`.hm-sw-header__document-button`) padding은 변경하지 않음(사용자 결정).
- 로고 메뉴: 뷰어 화면·Story·AI 예시에서 사용 중단. `logoMenu` Prop과 `SWHeaderMore` 확장(제목·아이콘·외부 트리거)은 공개·승인된 API라 코드 유지 — API 기준 갱신 작업에서 삭제(또는 유지) 결정 필요.
- 검증: viewer-logo-document-menu 47/47, Firefox 23/23, viewer 70/70, text-weight 50/50, theme-inheritance 25/25, check:ai-docs PASS.
Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@@ -0,0 +1,450 @@
{
"base": "http://127.0.0.1:6040",
"checks": [
{
"n": "SWHeader ... menu opens (reference colors)",
"ok": true,
"d": {
"colors": {
"background": "rgb(14, 61, 47)",
"border": "rgb(19, 86, 67)",
"shadow": "rgba(0, 0, 0, 0.5) 0px 4px 12px 0px",
"item": "rgb(246, 244, 242)"
},
"metrics": {
"border": "1px solid",
"radius": "3px",
"padding": "4px 4px",
"minWidth": "200px",
"font": "14px/normal 400",
"itemHeight": 32,
"itemInset": 5,
"itemPadding": "10px 10px",
"itemGap": "12px",
"itemRadius": "2px",
"itemFont": "Noto Sans KR 14px/normal 400"
}
}
},
{
"n": "SWHeader ... menu Escape closes, focus back on ...",
"ok": true
},
{
"n": "SWViewerMenu reference opens (36px rows, 4px padding)",
"ok": true,
"d": {
"border": "1px solid",
"radius": "4px",
"padding": "4px 4px",
"minWidth": "160px",
"font": "14px/20px 400",
"itemHeight": 36,
"itemInset": 5,
"itemPadding": "12px 12px",
"itemGap": "8px",
"itemRadius": "2px",
"itemFont": "Noto Sans KR 14px/20px 400"
}
},
{
"n": "logo opens no dropdown",
"ok": true,
"d": {
"menu": false,
"list": false,
"nameExpanded": "false",
"name": "C0060202-001-평면및종단면도.dwg",
"focus": "Name\n Description\n Default\n Control \n \n \n \n \n propertyName*\n \n This is a short description\n \n summary\n \n \n \n defaultValue\n \n Set string\n \n \n propertyName*\n \n This is a short description\n \n summary\n \n \n \n defaultValue\n \n Set string\n \n \n propertyName*\n \n This is a short description\n \n summary\n \n \n \n defaultValue\n \n Set string\n \n \n \n\n\n\n \n No Preview\n Sorry, but you either have no stories or none are selected somehow.\n \n Please check the Storybook config.\n Try reloading the page.\n \n \n If the problem persists, check the browser console, or the terminal you've run Storybook from.\n \n \n\n\n\n \n \n \n The component failed to render properly, likely due to a configuration issue in Storybook.\n Here are some common causes and how you can address them:\n \n \n \n Missing Context/Providers: You can use decorators to supply specific\n contexts or providers, which are sometimes necessary for components to render correctly. For\n detailed instructions on using decorators, please visit the\n Decorators documentation.\n \n \n Misconfigured Webpack or Vite: Verify that Storybook picks up all necessary\n settings for loaders, plugins, and other relevant parameters. You can find step-by-step\n guides for configuring\n Webpack or\n Vite\n with Storybook.\n \n \n Missing Environment Variables: Your Storybook may require specific\n environment variables to function as intended. You can set up custom environment variables\n as outlined in the\n Environment Variables documentation.\n \n \n \n \n\n\n HM DESIGN SYSTEM / SWSW Viewer — SWViewerHeader참고 DWG/DXF 뷰어의 상단 메뉴를 패키지 컴포넌트로 옮긴 뷰어 모드 전용 Header입니다. 로고 면 → 파일명 면 → 객체 수정 면(메뉴 그룹) → 오른쪽 패널·상태·정보 영역이며, 메뉴는 groups 데이터로 구성합니다. 레이어·설정·속성 토글은 팝업을 열고, 정보 버튼은 모달 팝업을 엽니다.기본형 (1440px, 도면 열림)로고 오른쪽 파일 열기 버튼(공간이 부족하면 테두리 아이콘만)으로 파일을 열고, 파일명을 누르면 샘플 도면 목록이 열립니다. Model/Layout 탭, 2.5D·어두운 배경 스위치, 패널 토글을 직접 눌러 보세요. Command 줄에 onCommand 결과가 표시됩니다.Command: 없음파일 열기C0060202-001-평면및종단면도.dwgModel17,377Layout237전체 보기2.5D어두운 배경레이어설정속성도면 열림: C0060202-001-평면및종단면도.dwg 열림\n \n \n function __onViteAppLoadingError(event) {\n const hostname = globalThis.location.hostname;\n if (hostname !== 'localhost' && globalThis.CONFIG_TYPE === 'DEVELOPMENT') {\n const message = `Failed to load the Storybook preview file 'vite-app.js':\n\n It looks like you're visiting the Storybook development server on another hostname than localhost: '${hostname}', but you haven't configured the necessary security features to support this.\n Please re-run your Storybook development server with the '--host ${hostname}' flag, or manually configure your Vite allowedHosts configuration with viteFinal.\n\n See:`;\n const docs = [\n 'https://storybook.js.org/docs/api/cli-options#dev',\n 'https://storybook.js.org/docs/api/main-config/main-config-vite-final',\n 'https://vite.dev/config/server-options.html#server-allowedhosts',\n ];\n console.error(`${message}\\n${docs.map((doc) => `- ${doc}`).join('\\n')}`);\n\n document.getElementById('storybook-root').innerHTML =\n `<p style=\"color: red; max-width: 70ch\">${message.replaceAll(\n '\\n',\n '<br/>'\n )}<ul>${docs.map((doc) => `<li><a href='${doc}' target='_blank'>${doc}</a></li>`).join('')}</ul></p>`;\n return;\n }\n }\n \n \n\n\n #storybook-highlights-menu {\n position: absolute;\n z-index: 2147483647;\n width: 300px;\n padding: 0px;\n margin: 15px 0 0 0;\n transform: translateX(-50%);\n font-family: \"Nunito Sans\", -apple-system, \".SFNSText-Regular\", \"San Francisco\", BlinkMacSystemFont, \"Segoe UI\", \"Helvetica Neue\", Helvetica, Arial, sans-serif;\n font-size: 12px;\n background: white;\n border: none;\n Line truncated
"log": "Command: 없음"
}
},
{
"n": "file open button after the logo: SWHeader document surface, icon + 파일 열기",
"ok": true,
"d": {
"surface": true,
"afterLogo": true,
"label": "파일 열기",
"icon": 1
}
},
{
"n": "file open icon: no border, transparent, no shadow",
"ok": true,
"d": "0px | none | rgb(240, 247, 245) | 0px | rgba(0, 0, 0, 0) | none"
},
{
"n": "file open button runs the existing file open",
"ok": true,
"d": {
"menu": false,
"list": false,
"nameExpanded": "false",
"name": "C0060202-001-평면및종단면도.dwg",
"focus": "파일 열기",
"log": "Command: 파일 선택창 (미연결) → document/open"
}
},
{
"n": "open list never covers the logo cap (pixels identical)",
"ok": true
},
{
"n": "file name click opens the 3 samples only (no Open / + New), SWHeader face",
"ok": true,
"d": {
"rows": [
"BasicSample.dwg",
"C0060202-001-평면및종단면도.dwg",
"simple.dxf"
],
"actions": 0,
"active": "C0060202-001-평면및종단면도.dwg",
"width": 361.5,
"button": 359.5,
"left": -2,
"right": 0,
"row": 32,
"gap": 0,
"background": "rgb(16, 49, 39)"
}
},
{
"n": "document list >= file-name button (outer) and >= 312px, covers it",
"ok": true,
"d": {
"rows": [
"BasicSample.dwg",
"C0060202-001-평면및종단면도.dwg",
"simple.dxf"
],
"actions": 0,
"active": "C0060202-001-평면및종단면도.dwg",
"width": 361.5,
"button": 359.5,
"left": -2,
"right": 0,
"row": 32,
"gap": 0,
"background": "rgb(16, 49, 39)"
}
},
{
"n": "list painted on top (not cut)",
"ok": true
},
{
"n": "sample select loads it, renames, closes, focus on the file name",
"ok": true,
"d": {
"menu": false,
"list": false,
"nameExpanded": "false",
"name": "simple.dxf",
"focus": "name",
"log": "Command: 파일 선택창 (미연결) → document/open → samples/sample-dxf"
}
},
{
"n": "ArrowDown opens the list on its first row",
"ok": true,
"d": {
"menu": false,
"list": true,
"nameExpanded": "true",
"name": "simple.dxf",
"focus": "BasicSample.dwg",
"log": "Command: 파일 선택창 (미연결) → document/open → samples/sample-dxf"
}
},
{
"n": "Escape closes the list and refocuses the file name",
"ok": true,
"d": {
"menu": false,
"list": false,
"nameExpanded": "false",
"name": "simple.dxf",
"focus": "name",
"log": "Command: 파일 선택창 (미연결) → document/open → samples/sample-dxf"
}
},
{
"n": "document list above an open SWViewerPanel",
"ok": true
},
{
"n": "no horizontal overflow at 1440 / 1100 / 860 / 520",
"ok": true,
"d": [
[
1440,
0,
0
],
[
1100,
0,
0
],
[
860,
0,
0
],
[
520,
0,
0
]
]
},
{
"n": "layer / settings / properties always in the header with tooltips",
"ok": true,
"d": [
[
1440,
[
[
"레이어",
true,
true
],
[
"설정",
true,
true
],
[
"속성",
true,
true
]
]
],
[
1100,
[
[
"레이어",
false,
true
],
[
"설정",
false,
true
],
[
"속성",
false,
true
]
]
],
[
860,
[
[
"레이어",
false,
true
],
[
"설정",
false,
true
],
[
"속성",
false,
true
]
]
],
[
520,
[
[
"레이어",
false,
true
],
[
"설정",
false,
true
],
[
"속성",
false,
true
]
]
]
]
},
{
"n": "1440 full (파일 열기 + 전체 보기 labels, no More); 1100 icons only, no More; 860 / 520 icons + More",
"ok": true,
"d": [
[
1440,
true,
false,
[
true,
"",
34
],
[
true,
true,
true
]
],
[
1100,
true,
false,
[
false,
"파일 열기",
34
],
[
false,
false,
false
]
],
[
860,
true,
true,
[
false,
"파일 열기",
34
],
[
false,
false,
false
]
],
[
520,
false,
true,
[
false,
"파일 열기",
34
],
[
false,
false,
false
]
]
]
},
{
"n": "logo, file open, file name and toolbar never overlap",
"ok": true,
"d": [
[
1440,
[
65,
65,
203.61666870117188,
205.61666870117188,
565.1166687011719,
567.11669921875
]
],
[
1100,
[
65,
65,
136,
138,
412.5,
414.5
]
],
[
860,
[
65,
65,
136,
138,
352.5,
354.5
]
],
[
520,
[
65,
65,
136,
138,
278,
280
]
]
]
},
{
"n": "520: document list shown inside the header width",
"ok": true
},
{
"n": "520: icon-only file open runs the file open",
"ok": true
},
{
"n": "narrow header (771px): icon toggles open / close panels, pressed #FFC600, tooltip",
"ok": true,
"d": [
{
"name": "레이어",
"pressed": "true",
"color": "rgb(255, 198, 0)",
"title": "레이어 패널 열기 / 닫기",
"open": true,
"closed": true
},
{
"name": "설정",
"pressed": "true",
"color": "rgb(255, 198, 0)",
"title": "옵션 패널 열기 / 닫기",
"open": true,
"closed": true
},
{
"name": "속성",
"pressed": "true",
"color": "rgb(255, 198, 0)",
"title": "속성 패널 열기 / 닫기",
"open": true,
"closed": true
}
]
},
{
"n": "no console errors",
"ok": true,
"d": []
}
],
"errors": []
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

@@ -0,0 +1,691 @@
{
"storybook": "http://127.0.0.1:6040",
"results": [
{
"name": "reference SWViewerMenu: 36px rows, 4px padding / radius, 14px 400, 4px under its trigger",
"result": "PASS",
"detail": {
"colors": {
"background": "rgb(14, 61, 47)",
"border": "rgb(19, 86, 67)",
"shadow": "rgba(0, 0, 0, 0.5) 0px 4px 12px 0px",
"item": "rgb(246, 244, 242)",
"label": "rgb(246, 244, 242)"
},
"metrics": {
"border": "1px solid",
"radius": "4px",
"padding": "4px",
"minWidth": "160px",
"font": "14px/20px 400",
"itemHeight": 36,
"itemInset": 5,
"itemPadding": "12px 12px",
"itemGap": "8px",
"itemRadius": "2px",
"itemFont": "\"Noto Sans KR\" 14px/20px 400",
"itemOutline": "none",
"label": "6px 12px 12px/18px 500",
"separator": "1px 4px -4px",
"icon": "16x16 @12",
"textLeft": 36
},
"place": {
"dy": 4,
"dx": 0
}
}
},
{
"name": "More (520px) colors = SWHeader ... menu (face, border, shadow, text); caption = text color",
"result": "PASS",
"detail": {
"more": {
"background": "rgb(14, 61, 47)",
"border": "rgb(19, 86, 67)",
"shadow": "rgba(0, 0, 0, 0.5) 0px 4px 12px 0px",
"item": "rgb(246, 244, 242)",
"label": "rgb(246, 244, 242)"
},
"hover": "rgba(255, 255, 255, 0.08) / rgb(246, 244, 242)",
"swHeader": {
"background": "rgb(14, 61, 47)",
"border": "rgb(19, 86, 67)",
"shadow": "rgba(0, 0, 0, 0.5) 0px 4px 12px 0px",
"item": "rgb(246, 244, 242)",
"label": null
}
}
},
{
"name": "header dropdown (상단 메뉴 + Dropdown) = More face and colors",
"result": "PASS",
"detail": {
"dropdown": {
"background": "rgb(14, 61, 47)",
"border": "rgb(19, 86, 67)",
"shadow": "rgba(0, 0, 0, 0.5) 0px 4px 12px 0px",
"item": "rgb(246, 244, 242)",
"label": "rgb(246, 244, 242)"
},
"more": {
"background": "rgb(14, 61, 47)",
"border": "rgb(19, 86, 67)",
"shadow": "rgba(0, 0, 0, 0.5) 0px 4px 12px 0px",
"item": "rgb(246, 244, 242)",
"label": "rgb(246, 244, 242)"
}
}
},
{
"name": "SWHeader ... menu still right-aligned under its trigger (no title, no divider)",
"result": "PASS",
"detail": ""
},
{
"name": "SWHeader ... menu Escape closes and refocuses ...",
"result": "PASS",
"detail": ""
},
{
"name": "logo opens no dropdown (no menu trigger)",
"result": "PASS",
"detail": ""
},
{
"name": "file open button after the logo: SWHeader document surface, icon + 파일 열기",
"result": "PASS",
"detail": {
"surface": true,
"afterLogo": true,
"beforeName": true,
"label": "파일 열기",
"icons": 1
}
},
{
"name": "file open icon: no border, transparent, no shadow; 34px slot",
"result": "PASS",
"detail": {
"style": "0px none rgb(240, 247, 245) | 0px | rgba(0, 0, 0, 0) | none",
"size": [
34,
34
]
}
},
{
"name": "file name area: no left padding, right after the file open button",
"result": "PASS",
"detail": [
"0px",
2
]
},
{
"name": "no 샘플 header button, no + New",
"result": "PASS",
"detail": ""
},
{
"name": "file open button runs the existing file open (onDocumentOpen + onCommand open/document)",
"result": "PASS",
"detail": "Command: 파일 선택창 (미연결) → document/open"
},
{
"name": "file name is the SWHeader document button",
"result": "PASS",
"detail": ""
},
{
"name": "open list never covers the logo cap (pixels identical)",
"result": "PASS",
"detail": ""
},
{
"name": "file name click opens the sample list",
"result": "PASS",
"detail": [
"BasicSample.dwg",
"C0060202-001-평면및종단면도.dwg",
"simple.dxf"
]
},
{
"name": "no Open and no + New below the samples",
"result": "PASS",
"detail": ""
},
{
"name": "current sample marked active",
"result": "PASS",
"detail": ""
},
{
"name": "SWHeader list face (32px rows, #103127, at least the SWHeader 312px)",
"result": "PASS",
"detail": {
"background": "rgb(16, 49, 39)",
"width": 362,
"row": 32
}
},
{
"name": "list opens directly below the file name",
"result": "PASS",
"detail": {
"list": {
"x": 202.90625,
"y": 368.5,
"width": 361.5,
"height": 98
},
"name": {
"x": 204.90625,
"y": 318.5,
"width": 359.5,
"height": 50
}
}
},
{
"name": "sample select loads it and renames the file",
"result": "PASS",
"detail": "Command: 파일 선택창 (미연결) → document/open → samples/sample-dxf"
},
{
"name": "list closes after select, focus on the file name",
"result": "PASS",
"detail": ""
},
{
"name": "ArrowDown opens the list on its first row",
"result": "PASS",
"detail": ""
},
{
"name": "Escape closes the list and refocuses the file name",
"result": "PASS",
"detail": ""
},
{
"name": "outside click closes the list",
"result": "PASS",
"detail": ""
},
{
"name": "1440: icon + 파일 열기; narrower: icon only (tooltip / name 파일 열기, 34px icon slot kept)",
"result": "PASS",
"detail": {
"first": {
"width": 1440,
"label": true,
"aria": null,
"title": "",
"icon": [
34,
34
],
"overflow": 0
},
"firstIcon": {
"width": 1080,
"label": false,
"aria": "파일 열기",
"title": "파일 열기",
"icon": [
34,
34
],
"overflow": 0
}
}
},
{
"name": "label comes back at the same widths when the header grows; no overflow",
"result": "PASS",
"detail": ""
},
{
"name": "file open never moves into More",
"result": "PASS",
"detail": [
"Model · 17,377개",
"Layout · 237개",
"전체 보기",
"2.5D",
"어두운 배경"
]
},
{
"name": "icon-only button runs the same file open",
"result": "PASS",
"detail": "Command: 파일 선택창 (미연결) → document/open → samples/sample-dxf → 파일 선택창 (미연결) → document/open"
},
{
"name": "document list above an open SWViewerPanel",
"result": "PASS",
"detail": "60"
},
{
"name": "closed list drops back (no raised layer at rest), cap over the open button and file name",
"result": "PASS",
"detail": ""
},
{
"name": "no horizontal overflow at 1440 / 1100 / 860 / 520",
"result": "PASS",
"detail": [
[
1440,
0,
0
],
[
1100,
0,
0
],
[
860,
0,
0
],
[
520,
0,
0
]
]
},
{
"name": "layer / settings / properties always in the header",
"result": "PASS",
"detail": [
[
"레이어",
"설정",
"속성"
],
[
"레이어",
"설정",
"속성"
],
[
"레이어",
"설정",
"속성"
],
[
"레이어",
"설정",
"속성"
]
]
},
{
"name": "1440: full items with labels (전체 보기 shown), no More; 1100: icons only, no More",
"result": "PASS",
"detail": [
[
1440,
true,
false
],
[
1100,
true,
false
],
[
860,
true,
true
],
[
520,
false,
true
]
]
},
{
"name": "860: icons only (tooltips kept) + More",
"result": "PASS",
"detail": {
"width": 860,
"overflow": 0,
"objectOverflow": 0,
"toggles": [
{
"name": "레이어",
"label": false,
"title": "레이어 패널 열기 / 닫기"
},
{
"name": "설정",
"label": false,
"title": "옵션 패널 열기 / 닫기"
},
{
"name": "속성",
"label": false,
"title": "속성 패널 열기 / 닫기"
}
],
"fitShown": true,
"more": true,
"logoRight": 65,
"docLeft": 138,
"docRight": 352.5,
"objectLeft": 354.5
}
},
{
"name": "520: icons only, then More for what still does not fit",
"result": "PASS",
"detail": {
"width": 520,
"overflow": 0,
"objectOverflow": 0,
"toggles": [
{
"name": "레이어",
"label": false,
"title": "레이어 패널 열기 / 닫기"
},
{
"name": "설정",
"label": false,
"title": "옵션 패널 열기 / 닫기"
},
{
"name": "속성",
"label": false,
"title": "속성 패널 열기 / 닫기"
}
],
"fitShown": false,
"more": true,
"logoRight": 65,
"docLeft": 138,
"docRight": 278,
"objectLeft": 280
}
},
{
"name": "logo, file name and toolbar never overlap",
"result": "PASS",
"detail": [
[
1440,
65,
204.90625,
564.40625,
566.40625
],
[
1100,
65,
138,
412.5,
414.5
],
[
860,
65,
138,
352.5,
354.5
],
[
520,
65,
138,
278,
280
]
]
},
{
"name": "520: header + More hold every item exactly once (panel toggles never in More)",
"result": "PASS",
"detail": {
"inHeader": [
"레이어",
"설정",
"속성",
"정보 및 라이선스"
],
"moreItems": [
"Model",
"Layout",
"전체 보기",
"2.5D",
"어두운 배경"
]
}
},
{
"name": "icon toggle pressed + command at 520",
"result": "PASS",
"detail": "Command: layers=true"
},
{
"name": "520: document list shown, inside the header width",
"result": "PASS",
"detail": {
"list": {
"x": 136,
"y": 1171.5,
"width": 312,
"height": 98
},
"header": {
"x": 40,
"y": 1120.5,
"width": 520,
"height": 52
}
}
},
{
"name": "narrow header (771px): icon toggles with tooltip, hover, pressed #FFC600, panel open / close",
"result": "PASS",
"detail": [
{
"name": "레이어",
"iconOnly": true,
"title": "레이어 패널 열기 / 닫기",
"hover": "rgb(38, 102, 81)",
"open": true,
"pressed": "true",
"color": "rgb(255, 198, 0)",
"closed": true
},
{
"name": "설정",
"iconOnly": true,
"title": "옵션 패널 열기 / 닫기",
"hover": "rgb(38, 102, 81)",
"open": true,
"pressed": "true",
"color": "rgb(255, 198, 0)",
"closed": true
},
{
"name": "속성",
"iconOnly": true,
"title": "속성 패널 열기 / 닫기",
"hover": "rgb(38, 102, 81)",
"open": true,
"pressed": "true",
"color": "rgb(255, 198, 0)",
"closed": true
}
]
},
{
"name": "document list >= file-name button (outer), covers it, >= 312px, no row cut — long / short name, 1440–400, resize while open",
"result": "PASS",
"detail": {
"bad": [],
"cases": [
{
"label": "long@1440",
"button": 359.5,
"list": 361.5,
"left": -2,
"right": 0,
"cut": []
},
{
"label": "long@1100",
"button": 274.5,
"list": 312,
"left": -2,
"right": 35.5,
"cut": []
},
{
"label": "long@860",
"button": 214.5,
"list": 312,
"left": -2,
"right": 95.5,
"cut": []
},
{
"label": "long@520",
"button": 140,
"list": 312,
"left": -2,
"right": 170,
"cut": []
},
{
"label": "long@400",
"button": 91,
"list": 312,
"left": -2,
"right": 219,
"cut": []
},
{
"label": "short@1440",
"button": 359.5,
"list": 361.5,
"left": -2,
"right": 0,
"cut": []
},
{
"label": "short@1100",
"button": 155.2,
"list": 312,
"left": -2,
"right": 154.8,
"cut": []
},
{
"label": "short@860",
"button": 171.8,
"list": 312,
"left": -2,
"right": 138.3,
"cut": []
},
{
"label": "short@520",
"button": 140,
"list": 312,
"left": -2,
"right": 170,
"cut": []
},
{
"label": "short@400",
"button": 91,
"list": 312,
"left": -2,
"right": 219,
"cut": []
},
{
"label": "open-resize@1200",
"button": 207.2,
"list": 312,
"left": -2,
"right": 102.8,
"cut": []
},
{
"label": "open-resize@900",
"button": 211.8,
"list": 312,
"left": -2,
"right": 98.3,
"cut": []
},
{
"label": "open-resize@600",
"button": 149.5,
"list": 312,
"left": -2,
"right": 160.5,
"cut": []
}
]
}
},
{
"name": "sweep: no overflow at any width (400–1440)",
"result": "PASS",
"detail": []
},
{
"name": "sweep: icons switch together and always before More",
"result": "PASS",
"detail": ""
},
{
"name": "sweep: narrower never shows more (monotonic: full → icons → More, fewer items)",
"result": "PASS",
"detail": ""
},
{
"name": "sweep: growing back gives the same state at every width (reverse order, no stuck item)",
"result": "PASS",
"detail": ""
},
{
"name": "sweep: 1440 full; each stage reached by measured room only",
"result": "PASS",
"detail": {
"icons": 1300,
"more": 910
}
},
{
"name": "sweep: shorter file name moves the steps to narrower widths",
"result": "PASS",
"detail": {
"long": {
"icons": 1300,
"more": 910
},
"short": {
"icons": 1080,
"more": 830
}
}
},
{
"name": "no page errors",
"result": "PASS",
"detail": ""
}
],
"errors": []
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB