feat(sw): integrate responsive home menu into header

- Merge SW / Home Menu Stories into SW / Header (sidebar: Home Menu; others verification-tagged)
- Home On: logo hover ring and header top/bottom #5F4B0F lines while the menu is open
- Measured More in SWHeader; home menu narrow widths use a More (...) section with a grouped
  text menu instead of horizontal scroll, placed right after the last visible section
- Action tiles no longer stay on after a click; 최근파일 stays Active only while its list is open
- More dropdown and recent files list never open together; list top joins the accent line
- Recent files preview falls back left / over the list at narrow widths (390px)
- Update Storybook, browser tests, docs, AI guides and QA evidence
This commit is contained in:
shkim4 committed 2026-10-19 22:46:28 +09:00
1 parent c542bdc282
commit 170a5c23ae
71 files changed
+12535 -150

No files matched your search

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

@@ -0,0 +1,219 @@
{
"checks": [
{
"name": "1920 tiles: 14 action tiles — no Active / aria-pressed / ring after the click",
"ok": true,
"data": []
},
{
"name": "Hover kept: Variant2 (ring + #FF5C00) while the pointer is on the tile",
"ok": true,
"data": {
"active": null,
"pressed": null,
"expanded": null,
"mark": "\"\"",
"ring": "1",
"color": "rgb(255, 92, 0)",
"bg": "rgba(0, 0, 0, 0)"
}
},
{
"name": "Pressed: Variant3 only while held (Space down), Default after release",
"ok": true,
"data": {
"held": {
"active": null,
"pressed": null,
"expanded": null,
"mark": "\"\"",
"ring": "1",
"color": "rgb(255, 92, 0)",
"bg": "rgba(0, 0, 0, 0)"
},
"released": {
"active": null,
"pressed": null,
"expanded": null,
"mark": "\"\"",
"ring": "0",
"color": "rgb(246, 244, 242)",
"bg": "rgba(0, 0, 0, 0)"
}
}
},
{
"name": "최근파일 tile: Variant3 (Active, aria-expanded) while the list is open; re-click closes list and Active",
"ok": true,
"data": {
"rOpen": {
"tile": {
"active": "true",
"pressed": null,
"expanded": "true",
"mark": "\"\"",
"ring": "1",
"color": "rgb(255, 92, 0)",
"bg": "rgba(0, 0, 0, 0)"
},
"list": true
},
"rClosed": {
"tile": {
"active": null,
"pressed": null,
"expanded": "false",
"mark": "\"\"",
"ring": "0",
"color": "rgb(246, 244, 242)",
"bg": "rgba(0, 0, 0, 0)"
},
"list": false
}
}
},
{
"name": "최근파일: Escape closes the list → Active off",
"ok": true,
"data": {
"tile": {
"active": null,
"pressed": null,
"expanded": "false",
"mark": "\"\"",
"ring": "0",
"color": "rgb(246, 244, 242)",
"bg": "rgba(0, 0, 0, 0)"
},
"list": false
}
},
{
"name": "최근파일: opening a file closes the menu; reopening starts with 최근파일 off",
"ok": true,
"data": {
"afterFile": false,
"reopened": {
"active": null,
"pressed": null,
"expanded": "false",
"mark": "\"\"",
"ring": "0",
"color": "rgb(246, 244, 242)",
"bg": "rgba(0, 0, 0, 0)"
}
}
},
{
"name": "home button keeps Home On while the menu is open",
"ok": true,
"data": true
},
{
"name": "More tile keeps its Variant3 while its dropdown is open",
"ok": true,
"data": {
"active": "true",
"ring": "1",
"expanded": "true"
}
},
{
"name": "1080 More text menu: 10 action rows — no Active, no ✓, no brown row after the click (dropdown stays open)",
"ok": true,
"data": {
"rowLeft": [],
"stillOpen": true
}
},
{
"name": "text row Hover kept (#62533D + #FF5C00)",
"ok": true,
"data": {
"active": null,
"pressed": null,
"expanded": null,
"mark": "\"\"",
"ring": null,
"color": "rgb(255, 92, 0)",
"bg": "rgb(98, 83, 61)"
}
},
{
"name": "text row Pressed only while held, nothing after release",
"ok": true,
"data": {
"rowHeld": {
"active": null,
"pressed": null,
"expanded": null,
"mark": "\"\"",
"ring": null,
"color": "rgb(255, 92, 0)",
"bg": "rgb(98, 83, 61)"
},
"rowReleased": {
"active": null,
"pressed": null,
"expanded": null,
"mark": "\"\"",
"ring": null,
"color": "rgb(246, 244, 242)",
"bg": "rgba(0, 0, 0, 0)"
}
}
},
{
"name": "최근파일 row: list opens; More stays Variant3; reopened dropdown shows 최근파일 Active with › (no ✓)",
"ok": true,
"data": {
"listOpen": true,
"moreWhileList": "true",
"rRow": {
"active": "true",
"pressed": null,
"expanded": "true",
"mark": "\"›\"",
"ring": null,
"color": "rgb(255, 92, 0)",
"bg": "rgb(98, 83, 61)"
}
}
},
{
"name": "Escape (dropdown, then list): list closed → 최근파일 row and More off",
"ok": true,
"data": {
"rAfter": {
"list": false,
"more": null
},
"rRow2": {
"active": null,
"pressed": null,
"expanded": "false",
"mark": "\"›\"",
"ring": null,
"color": "rgb(246, 244, 242)",
"bg": "rgba(0, 0, 0, 0)"
}
}
},
{
"name": "More tile off once its dropdown and the list are closed",
"ok": true,
"data": null
},
{
"name": "callbacks: action tile / text row → onItemSelect(active=false) without onActiveChange; 최근파일 → onActiveChange([recent]) + onItemSelect(active=true)",
"ok": true,
"data": "onItemSelect(open / recent, active=true)\nonActiveChange([recent])\nonItemSelect(setting / display, active=false)\nonItemSelect(working / save, active=false)\nonHomeMenuOpenChange(true, home)"
},
{
"name": "no page errors",
"ok": true,
"data": []
}
],
"errors": []
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

@@ -0,0 +1,271 @@
{
"results": [
{
"vw": 1920,
"menu": {
"x": 375,
"y": 0,
"width": 1305,
"height": 77,
"top": 0,
"right": 1680,
"bottom": 77,
"left": 375
},
"panel": {
"x": 808,
"y": 77,
"width": 354,
"height": 752,
"top": 77,
"right": 1162,
"bottom": 829,
"left": 808
},
"list": {
"x": 808,
"y": 77,
"width": 354,
"height": 752,
"top": 77,
"right": 1162,
"bottom": 829,
"left": 808
},
"accent": "outline",
"listBorder": [
"0px",
"1px",
"rgb(218, 104, 19)"
],
"accentBox": {
"top": "47px",
"height": "30px",
"padding": "2px",
"radius": "3px"
},
"joint": [],
"pageScroll": 0
},
{
"vw": 1440,
"menu": {
"x": 375,
"y": 0,
"width": 825,
"height": 77,
"top": 0,
"right": 1200,
"bottom": 77,
"left": 375
},
"panel": {
"x": 808,
"y": 77,
"width": 354,
"height": 752,
"top": 77,
"right": 1162,
"bottom": 829,
"left": 808
},
"list": {
"x": 808,
"y": 77,
"width": 354,
"height": 752,
"top": 77,
"right": 1162,
"bottom": 829,
"left": 808
},
"accent": "outline",
"listBorder": [
"0px",
"1px",
"rgb(218, 104, 19)"
],
"accentBox": {
"top": "47px",
"height": "30px",
"padding": "2px",
"radius": "3px"
},
"joint": [],
"pageScroll": 0
},
{
"vw": 1024,
"menu": {
"x": 364,
"y": 0,
"width": 484,
"height": 77,
"top": 0,
"right": 848,
"bottom": 77,
"left": 364
},
"panel": {
"x": 670,
"y": 77,
"width": 354,
"height": 752,
"top": 77,
"right": 1024,
"bottom": 829,
"left": 670
},
"list": {
"x": 670,
"y": 77,
"width": 354,
"height": 752,
"top": 77,
"right": 1024,
"bottom": 829,
"left": 670
},
"accent": "outline",
"listBorder": [
"0px",
"1px",
"rgb(218, 104, 19)"
],
"accentBox": {
"top": "47px",
"height": "30px",
"padding": "2px",
"radius": "3px"
},
"joint": [
[
845,
1024,
75,
77,
"linear-gradient(0deg, rgb(255, 81, 0) 0%"
]
],
"pageScroll": 0
},
{
"vw": 768,
"menu": {
"x": 304,
"y": 0,
"width": 288,
"height": 77,
"top": 0,
"right": 592,
"bottom": 77,
"left": 304
},
"panel": {
"x": 414,
"y": 77,
"width": 354,
"height": 752,
"top": 77,
"right": 768,
"bottom": 829,
"left": 414
},
"list": {
"x": 414,
"y": 77,
"width": 354,
"height": 752,
"top": 77,
"right": 768,
"bottom": 829,
"left": 414
},
"accent": "outline",
"listBorder": [
"0px",
"1px",
"rgb(218, 104, 19)"
],
"accentBox": {
"top": "47px",
"height": "30px",
"padding": "2px",
"radius": "3px"
},
"joint": [
[
589,
768,
75,
77,
"linear-gradient(0deg, rgb(255, 81, 0) 0%"
]
],
"pageScroll": 0
},
{
"vw": 390,
"menu": {
"x": 161,
"y": 0,
"width": 165,
"height": 77,
"top": 0,
"right": 326,
"bottom": 77,
"left": 161
},
"panel": {
"x": 36,
"y": 77,
"width": 354,
"height": 752,
"top": 77,
"right": 390,
"bottom": 829,
"left": 36
},
"list": {
"x": 36,
"y": 77,
"width": 354,
"height": 752,
"top": 77,
"right": 390,
"bottom": 829,
"left": 36
},
"accent": "outline",
"listBorder": [
"0px",
"1px",
"rgb(218, 104, 19)"
],
"accentBox": {
"top": "47px",
"height": "30px",
"padding": "2px",
"radius": "3px"
},
"joint": [
[
36,
164,
75,
77,
"linear-gradient(0deg, rgb(255, 81, 0) 0%"
],
[
323,
390,
75,
77,
"linear-gradient(0deg, rgb(255, 81, 0) 0%"
]
],
"pageScroll": 0
}
],
"errors": []
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 332 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

+13
View File
@@ -517,6 +517,18 @@ Composed Controls의 기존 레이어·색상·상세보기 예제는 보존하
검증(2026-10-16, Storybook `SW / Header / Responsive`, 18개 폭): 1920 / 1800 / 1799 / 1770 / 1600 / 1440 / 1280 / 1180 / 1179 / 1080 / 1079 / 900 / 899 / 768 / 640 / 639 / 560 / 390에서 헤더 가로 스크롤 0, 요소 겹침 0, 객체 수정 면 밖으로 넘침 0. 필드 값·라벨 잘림은 390px의 레이어명 1건뿐입니다. 각 폭에서 더보기 항목 선택 시 Callback이 호출되고, 선택 상태가 반영되며, 메뉴가 닫히는 것을 확인했습니다(속성Bar·속성창 체크 전환, 인쇄·팝업 가이드 Callback 포함). 실제 Viewport 1799 / 1079 / 390에서도 같은 결과입니다.
### 12.1 실측 More — 실제 사용 가능한 공간 기준 (2026-10-19)
2026-10-19 사용자 요청("고정된 화면 너비만 기준으로 처리하지 말고 실제 사용 가능한 공간을 기준으로")에 따라 위 Breakpoint 위에 실측 단계를 더했습니다. Breakpoint 표는 그대로이며, 기본 구성(추가 요소 없음)의 배치는 바뀌지 않습니다.
- 측정 대상: 객체 수정 영역의 in-flow 내용. 측정하는 동안(`data-measuring`, 같은 Layout Effect 안에서 설정·해제, 그려지지 않음) 영역 안 요소는 줄거나 늘지 않고 auto margin도 0이므로 실제로 필요한 폭이 드러납니다. 구성형(`controls`) 파일명은 기본 디자인의 최소 파일명 폭(96px)으로 계산합니다.
- 이동 순서: Breakpoint 단계 순서 그대로 선·문자·치수스타일 → 그리기모드(라벨만 숨김) → 속성 블록(상세보기·속성Bar·속성창, 기본 구성에서는 한 덩어리) → 색상 → 인쇄·K(구성형만, 기본 구성에서는 창 영역이라 Breakpoint 유지). 같은 단계는 오른쪽부터. 대표 칸(레이어 또는 More에서 고른 필드)은 이동하지 않습니다. 로고 오른쪽 Open(`documentOpenPlacement="header"`)은 가장 먼저 옮겨지고 가장 늦게 돌아옵니다.
- 복귀: 옮긴 직후 그 이동으로 생긴 공간(More 버튼 등장·여백 변화 포함)을 한 번 측정해 두고, 남은 공간이 그만큼 생기면 역순으로 원래 자리에 돌아옵니다(경계에서 깜박임 없음).
- 메뉴를 열고 닫아도 위치·폭은 바뀌지 않습니다. Dropdown·로고 메뉴·More 목록·Tooltip은 positioned 층이라 측정에서 제외됩니다.
- 한계: `leading`·`tools`처럼 호스트가 넣은 요소는 More로 옮기지 않습니다(More 항목이 없음). 그 요소만으로도 공간이 부족하면 기존처럼 대표 필드가 줄어듭니다.
상세 검증: [홈 메뉴 통합·실측 More 보고](./sw-header-home-menu-responsive-2026-10-19.md).
## 13. 컴포넌트 분리 기준
- 공개: `SWHeader`(전체), `SWHeaderHome`, `SWHeaderDocumentTitle`, `SWHeaderLayerControl`, `SWHeaderLayerMenu`(레이어 필드 + Dropdown, §5.2), `SWHeaderColorMenu` · `SWHeaderLineStyleMenu` · `SWHeaderTextStyleMenu` · `SWHeaderDimensionStyleMenu`(§5.3), `SWHeaderLinePreview` · `swHeaderLinePatterns`, `SWHeaderInput` · `parseSWHeaderColor` · `parseSWHeaderNumber`(§5.5), `SWHeaderColorControl`, `SWHeaderLineStyle`, `SWHeaderTextStyle`, `SWHeaderDimensionStyle`, `SWHeaderProperties`, `SWHeaderActions`, `SWHeaderWindowControls`, `SWTheme`(단독 사용 시 토큰 범위).
@@ -540,6 +552,7 @@ Composed Controls의 기존 레이어·색상·상세보기 예제는 보존하
| `--hm-sw-header-arrow-color` | `#47AF91` | 파일명 화살표 |
| `--hm-sw-header-hover` | `rgba(255,255,255,.08)` | 더보기 메뉴 항목 Hover 배경 |
| `--hm-sw-header-hover-border-gradient` / `-hover-border-width` | Figma 홈 hover gradient / 1px | Hover 테두리(§11.1) |
| `--hm-sw-header-home-on-line-color` / `-home-on-line-width` | `#5F4B0F` / 2px | Home On(로고 메뉴 열림, Figma 662:67267): Header 전폭 상·하단 선. 로고는 Hover 테두리 유지 |
| `--hm-sw-header-document-menu-width` / `-document-menu-background` / `-document-menu-border-color` / `-document-menu-border-width` | 310px / `#103127` / `#041611` / 1px | 문서 Dropdown(§5.1) |
| `--hm-sw-header-document-menu-shadow` / `-document-menu-inset-shadow` / `-document-menu-divider-color` | `0 2px 4px rgba(0,0,0,.25)` / `inset 0 4px 8px rgba(0,0,0,.4)` / `rgba(194,217,210,.2)` | 문서 Dropdown(§5.1) |
| `--hm-sw-header-document-list-max-height` | 320px | 목록 내부 스크롤 시작 높이(구현값) |
@@ -0,0 +1,76 @@
# SW Header 홈 메뉴 통합 · 실측 More (2026-10-19)
> 브랜치 `feature/sw-home-menu`(기준 `c542bdc`, 작업 전 미커밋 `packages/sw/src/theme/sw-viewer.css` 3줄 보존). **Commit·Push 하지 않았습니다(승인 대기).**
> 검증: Storybook 개발 서버(포트 6025, 6024는 다른 세션이 사용 중)와 Claude 내장 브라우저(Chromium). 이 PC에는 기존 `*.browser.mjs`가 쓰는 Playwright 런타임이 없어 그 스크립트는 실행하지 않았습니다.
## 1. 요청과 결과
| 요청 | 결과 |
|---|---|
| `SW / Home Menu` 기능·디자인을 `SW / Header`로 통합 | `SW / Header` → `Home Menu`(`sw-header--home-menu`) 한 페이지에 로고 클릭 열기/닫기·이벤트 기록, 헤더 폭 조절, Figma 26:10591 비교, Default/Hover/Active, 최근파일 하위 패널, 속성창·SWPopup·Escape 통합. `Overview` 로고도 같은 홈 메뉴를 엶 |
| 홈 버튼 클릭 → 열림, 다시 클릭 → 닫힘 | 기존 `SWHeader homeMenu` 동작 그대로(코드 변경 없음). 5개 섹션·15개 타일·Hover·하위 패널 유지 |
| SW 일반 모드 전용 | `SWViewer*` 코드·Story 변경 없음. 뷰어 Story에 홈 메뉴 0개 |
| 반응형 더보기: 실제 사용 가능한 공간 기준, 넓어지면 원위치, 순서·기능 유지 | `SWHeader` 실측 More 추가(아래 §2). Breakpoint는 그대로 |
| 메뉴 열고 닫을 때 헤더 요소 위치·폭 유지 | 측정에서 positioned 층(Dropdown·홈 메뉴·More 목록·Tooltip) 제외. 열기/닫기 전후 좌표 동일 확인 |
| `SW / Home Menu` 사이드바 제거, 컴포넌트·Export·스타일·AI 지침 유지 | Story 파일을 `sw-header-home-menu.stories.tsx`로 옮기고 title `SW/Header`. `SWHomeMenu`·`SWRecentFiles`·Export·`sw-home-menu.css`(패키지)·AI 지침 삭제 없음 |
## 2. 실측 More 동작
- 기존 Breakpoint(1800 / 1180 / 1080 / 900 / 640 / 520)를 먼저 적용하고, 그 위에서 객체 수정 영역의 실제 남은 공간을 측정합니다.
- 측정: Layout Effect 안에서 `data-measuring`을 잠깐 붙여(그려지지 않음) 영역 안 요소가 줄거나 늘지 않고 auto margin이 0인 상태의 in-flow 폭을 읽습니다. 구성형 파일명은 최소 96px로 계산합니다.
- 이동 순서: Open(로고 오른쪽) → 선·문자·치수스타일 → 그리기모드 → 속성 블록 → 색상 → (구성형) 인쇄·K. 같은 단계는 오른쪽부터. 대표 칸은 이동하지 않습니다.
- 복귀: 옮긴 직후 생긴 공간을 한 번 측정해 두고 그만큼 공간이 생기면 역순으로 복귀. 기본 구성은 아무것도 옮기지 않으면 기존 CSS 전용 배치를 그대로 씁니다.
- 공개 API 변경 없음(추가 Prop 없음). 내부 상태·`data-squeezed` 속성·CSS만 추가.
## 3. 변경 파일
| 파일 | 내용 |
|---|---|
| `packages/sw/src/components/sw-header/SWHeader.tsx` | 실측 More(측정 함수·이동 순서·상태·Layout Effect), Open 이동 판정을 같은 측정으로 통일 |
| `packages/sw/src/theme/sw.css` | `data-measuring` 측정 규칙, 옮겨진 그리기모드·속성 블록 자리 보정 |
| `src/sw/sw-home-menu.stories.tsx` → `src/sw/sw-header-home-menu.stories.tsx` | title `SW/Header`, 대표 Story `Home Menu`, 나머지 `verification` 태그(`sw-header--home-menu-*`) |
| `src/sw/sw-header.stories.tsx` | `Overview`에 홈 메뉴 연결, `Responsive More`에 "실제 사용 가능한 공간 기준" 섹션 |
| `src/sw/sw-header-overview.tsx` | 선택 `homeMenuSections` Prop |
| `src/sw/sw-home-menu.css`, `src/sw/sw-stories.css` | Story 전용 폭 조절 컨트롤 배치 |
| `packages/sw/tests/home-menu-integration.browser.mjs`, `packages/sw/tests/storybook-inventory.mjs` | 새 Story ID·제목 기대값, 이동 ID 표 |
| `docs/sw/sw-header-design-spec.md` §12.1, `docs/sw/sw-header-usage.md`, `packages/sw/README.md`, `packages/sw/ai/DESIGN_RULES.md` §7, `packages/sw/ai/COMPONENT_CATALOG.md` | 실측 More·Storybook 위치 |
보존: `packages/sw/src/theme/sw-viewer.css`의 작업 전 미커밋 변경(`.hm-sw-viewer-menu-root { display: contents; }`)은 손대지 않았습니다. 과거 검증 보고서는 수정하지 않았습니다.
## 4. 검증
| 항목 | 결과 | 방법 |
|---|---|---|
| SW typecheck / 앱 `tsc -b` / SW build | PASS | `npm run typecheck -w @hm-design/sw`, `npx tsc -b`, `npm run build -w @hm-design/sw` |
| AI 문서 | PASS / 32/32 | `npm run check:ai-docs`, `node packages/sw/tests/ai-docs.check.mjs` |
| Storybook 메뉴 | PASS | `index.json`: SW title = Header·Popup·Viewer(`SW/Home Menu` 없음), `sw-header--home-menu` 표시, 홈 메뉴 하위 14개 `verification` |
| 기존 Header Story 배치 회귀 | PASS(기본 구성 변화 없음) | 작업 전 dist로 기록한 요소 좌표·폭과 비교: Overview·Interactive States·Logo Slot·Document Dropdown·Responsive More(19폭)·K Guide·Hidden Sub Controls 동일. 차이는 `Document Open`(§5)뿐 |
| 이동한 홈 메뉴 Story 배치 | PASS 18/18 | 옛 `sw-home-menu--*`와 새 `sw-header--home-menu-*` 6개 × 1920/1280/768 좌표 동일(메뉴 x·폭 포함) |
| 홈 메뉴 열기/닫기 | PASS | 로고 클릭 → 메뉴 1·섹션 5·타일 15·`aria-expanded=true`·Focus 메뉴 안, 재클릭 → 닫힘, `onHomeClick` 미호출, 열기/닫기 전후 Header 좌표 동일. `Overview`에서도 열기/닫기 확인 |
| Hover·하위 패널·Escape | PASS | 실제 포인터 Hover 시 라벨 `#FF5C00`, 최근파일 → 하위 패널·outline 강조, Escape 1회 패널만 닫힘, 2회 메뉴 닫힘·로고로 Focus |
| 좁은 화면 더보기 · 폭 변경 재배치 | PASS | `Responsive More` 실측 섹션(로고 오른쪽 버튼 2개 + 도구 1개): 1920→390 축소 시 치수 → 문자 → 그리기모드 → 속성 블록 → 색상 순 이동, 겹침·가로 스크롤 0(520까지), 390→1920 확대 시 역순 복귀, 추가 요소 끄면 기본 CSS 배치로 복귀 |
| 더보기 안 메뉴 선택 · 팝업 | PASS | 1180: More 목록 = 선/문자/치수스타일·속성Bar·속성창(순서 유지). 속성창 → 팝업(`role=dialog`) 열림·Focus 안, Escape 닫힘·More로 Focus 복귀. 선스타일 → 대표 칸·Dropdown 열림·Focus, 레이어는 More로 |
| 헤더 요소 위치·폭 유지 | PASS | More 열기, 속성창 열기/닫기, 홈 메뉴, 레이어 Dropdown, 문서 Dropdown 각각 전후 좌표 동일 |
| SW 뷰어 모드 영향 | PASS | 뷰어 코드·Story diff 없음, `SW/Viewer` 3개 Story 렌더 오류 0·홈 메뉴 0 |
| Storybook production build | PASS | `npm run build-storybook`(인덱스 358개, SW title = Header·Popup·Viewer, `SW/Header` 27개) |
참고: 내장 브라우저 창이 숨겨진 상태라 ResizeObserver가 프레임이 그려질 때만 실행됩니다. 폭 변경 검증은 각 단계마다 화면 프레임을 갱신한 뒤 측정했습니다.
## 5. 의도된 배치 변화 (`Document Open` Story)
로고 오른쪽 Open을 쓰는 기본 구성은 작업 전 아래 폭에서 실제로 겹쳤습니다(기존 판정이 `scrollWidth`만 봐서 감지 못함). 이제 Open(필요하면 메뉴 1개)이 More로 이동합니다.
| 프레임 | 작업 전 | 작업 후 |
|---|---|---|
| 1800 | 치수스타일이 속성 블록과 겹침 | Open·치수스타일 → More |
| 1180 / 1080 | 색상이 More와 겹침 | Open → More |
| 900 | 색상이 More와 3px 겹침 | Open → More |
| 640 | 레이어가 More와 겹침 | Open → More |
| 구성형 768 | Open이 More에 있음 | Open 표시(파일명 200px ≥ 최소 96px) |
## 6. NOT VERIFIED · 한계
- 기존 Playwright 스크립트(`home-menu-integration.browser.mjs` 등): 이 PC에 런타임 없음. ID만 갱신했고 실행하지 않았습니다.
- Firefox·Safari, 실제 소비 앱(`HMDesignSwTest`) 재검증, 픽셀 단위 스크린샷 비교(좌표·폭 비교로 대신함).
- 호스트 요소(`leading`·`tools`)는 More로 옮기지 않습니다. 390px에 로고 오른쪽 버튼 2개 + 도구를 함께 넣으면 대표 필드가 0까지 줄고 도구와 More가 겹칩니다(옮길 메뉴가 더 없음).
- 기존 동작 관찰(이번 변경과 무관, 수정하지 않음): 홈 메뉴에서 Escape로 닫을 때 Story 기록에 `onHomeMenuOpenChange(false, escape)`·`onActiveChange([])`가 2회씩 남습니다. 이전 Story(`sw-header--home-menu-header-composition`, 구 `sw-home-menu--header-composition`)에서도 같습니다.
+7 -4
View File
@@ -371,6 +371,7 @@ import { SWHeaderInput, parseSWHeaderNumber, SWTheme } from "@hm-design/sw"
- 레이어는 그동안 더보기로 이동하며, 더보기 → 레이어로 원래 배치로 돌아갑니다. 이미 화면에 있는 필드는 더보기에 나오지 않습니다.
- 전환은 UI 상태입니다. 데이터 Callback은 호출되지 않고, 필드 클릭과 같은 `on*Open` 1회와 `on*MenuOpenChange(true)`만 호출됩니다. 값은 그대로 유지됩니다.
- 폭이 넓어져 그 필드가 다시 보이는 단계에서는 1920 기준 배치로 돌아갑니다(Breakpoint 1800 / 1180 / 1080 / 900 / 640 / 520은 그대로).
- 실측 More(2026-10-19): Breakpoint 위에서 객체 수정 영역의 실제 남은 공간도 측정합니다. `leading`·`tools`·긴 라벨·사이드 패널 등으로 공간이 모자라면 표시할 수 없는 메뉴만 더보기로 옮기고, 공간이 다시 생기면 원래 자리·순서로 돌려놓습니다. 메뉴를 열고 닫는 것만으로는 Header 요소의 위치·폭이 바뀌지 않습니다([명세 §12.1](./sw-header-design-spec.md#121-실측-more--실제-사용-가능한-공간-기준-2026-10-19)).
- Dropdown 데이터(`colors` 등)가 없는 필드도 같은 방식으로 대표 칸에 표시됩니다(목록 없이 `on*Open`만).
## 8. 하위 컴포넌트 재사용
@@ -406,7 +407,7 @@ import type { CSSProperties } from "react"
- 기존 뷰어 `Toolbar.tsx`의 파일명(`fileName`)은 `documentTitle`로, 패널 토글(`propsOpen` 등)은 `propertyPanelOpen` + `onPropertyPanelToggle`로 연결할 수 있습니다.
- 뷰어의 파일 열기·샘플·Model/Layout 탭·2.5D·어두운 배경·로드 상태는 Figma SW Header에 없는 뷰어 전용 도구입니다. 필요하면 `tools` 슬롯에 호스트가 렌더링합니다(스타일은 호스트 책임).
- 헤더 높이는 49px입니다. 고정 배치(`position: fixed`)는 `className`·`style`로 호스트에서 지정합니다.
- 반응형은 모두 헤더 자신의 폭(container query)을 기준으로 하므로 사이드 패널 때문에 헤더가 좁아져도 동작합니다. 단계(1800 / 1180 / 1080 / 900 / 640 / 520)는 [디자인 명세 §12](./sw-header-design-spec.md#12-반응형)를 따릅니다.
- 반응형은 모두 헤더 자신의 폭(container query)을 기준으로 하므로 사이드 패널 때문에 헤더가 좁아져도 동작합니다. 단계(1800 / 1180 / 1080 / 900 / 640 / 520)는 [디자인 명세 §12](./sw-header-design-spec.md#12-반응형)를 따르고, 그 위에서 실제 남은 공간을 측정해 부족한 메뉴만 더보기로 옮깁니다([§12.1](./sw-header-design-spec.md#121-실측-more--실제-사용-가능한-공간-기준-2026-10-19)).
- 뷰어는 현재 `@hm-design/ui`의 SWHeader 패턴을 사용합니다. 이 패키지로 바꾸는 작업은 이번 범위가 아닙니다.
## 10.1 상호작용 상태
@@ -454,7 +455,9 @@ Tooltip은 45 × 17px, 검정 80% 배경과 흰색 12px/400 글자입니다. Too
npm run storybook
```
현재(2026-10-19) `SW / Header`에는 33개 Story가 있습니다. Story ID·분류·용도 전체 표는 [1차 정리 보고 §2](./sw-first-pass-cleanup-review-2026-10-19.md#2-storybook-전체-목록-및-분류)를 봅니다. 아래 문단은 Story를 추가한 시점별 설명입니다.
현재(2026-10-19, 홈 메뉴 통합 후) `SW / Header`에는 27개 Story가 있습니다(사이드바 기본 표시 12개 + `verification` 태그 15개). 이전 시점의 Story 표는 [1차 정리 보고 §2](./sw-first-pass-cleanup-review-2026-10-19.md#2-storybook-전체-목록-및-분류), 홈 메뉴 통합 후 목록은 [홈 메뉴 통합·실측 More 보고](./sw-header-home-menu-responsive-2026-10-19.md)를 봅니다. 아래 문단은 Story를 추가한 시점별 설명입니다.
2026-10-19 홈 메뉴 통합: 기존 `SW / Home Menu` 메뉴는 사이드바에서 제거하고 `SW / Header`로 옮겼습니다. 사이드바에는 `Home Menu`(`sw-header--home-menu`: 로고 클릭 열기/닫기·이벤트 기록, 헤더 폭 조절, Figma 26:10591 비교, Default/Hover/Active, 최근파일 하위 패널, 속성창·SWPopup·Escape 통합) 하나만 표시합니다. Figma 비교 캔버스·Variant·데이터 변경·단독 사용·속성Bar 비교 등은 `sw-header--home-menu-*` ID의 `verification` 태그 Story로 남아 직접 URL과 사이드바 태그 필터로 엽니다. `Overview`의 로고도 같은 홈 메뉴를 엽니다. `Responsive More` 첫 섹션 "실제 사용 가능한 공간 기준"은 헤더 폭 Range와 로고 오른쪽 버튼·추가 도구·홈 메뉴 On/Off로 실측 More의 이동·복귀를 확인합니다. 홈 메뉴는 SW 일반 모드 전용이며 `SW / Viewer`에는 없습니다.
`SW / Header` 아래 Story: Overview, Default, Controls, Interactive States(인쇄·팝업 가이드·최소화·종료 Default/Hover 비교와 Callback 기록 포함), Responsive, Composed Controls, Logo Slot(기본 임시 로고 / 사용자 지정 로고 / 로고 미전달), Document Dropdown(Default / Hover / Open / 선택 / 여러 문서 / 문서 없음 / 긴 파일명 / 좁은 화면), Layer Dropdown(Layer Default / Hover / Open(Figma 8행) / Row Hover / 전구·눈꽃·자물쇠 4상태 3배 확대 / 선택·Toggle Callback 기록과 상태표 / 여러 상태·현재 레이어 / 긴 레이어명 / 20개 스크롤 / 레이어 없음 / 레이어 상세 / 상태 표시 전용 / 좁은 화면 1440–390). Responsive Story는 헤더 폭 18개(1920–390, 각 단계 경계 전후 포함) 프레임을 나열합니다. 모든 단계가 헤더 폭 기준이므로 Viewport를 바꾸지 않아도 확인할 수 있습니다.
@@ -644,9 +647,9 @@ Figma 26:10591의 2단 Header 메뉴입니다. 기본 숨김, 로고 클릭으
onHomeMenuOpenChange={(open, reason) => {}} /> // reason: home | escape | background | outside | select | file | document
```
- `sections`: `{ id, title, items: [{ id, label, icon, hoverIcon?, toggle?, panel?, disabled?, hidden?, onSelect? }] }[]`. 최근파일은 타일의 `panel: <SWRecentFiles files={…} onFileOpen={…} />`(선택).
- `sections`: `{ id, title, items: [{ id, label, icon, hoverIcon?, toggle?, panel?, disabled?, hidden?, onSelect? }] }[]`. 최근파일은 타일의 `panel: <SWRecentFiles files={…} onFileOpen={…} />`(선택). `panel`이 없는 항목은 실행 버튼입니다(클릭 시 `onSelect`/`onItemSelect`, 클릭 후 ON 유지 없음). 최근파일만 목록이 열린 동안 Active이며, 유지형이 필요하면 `toggle: true`.
- 이벤트 우선순위: `homeMenu`가 있으면 로고 클릭은 메뉴 토글만 하고 `onHomeClick`은 호출되지 않습니다. `homeMenu`가 없으면 기존과 같이 `onHomeClick`만 호출됩니다.
- New / Open은 문서 Dropdown 안 그대로입니다(`documentOpenPlacement` 기본 `"menu"`). 메뉴가 열리면 필드·문서 Dropdown이 닫히고, 문서 Dropdown을 열면 메뉴가 닫힙니다.
- 위치: 객체 수정 영역 −2px ~ 창 영역(DOM 측정, 1920에서 x 375 · 폭 1305). 좁은 폭에서는 메뉴 안 가로 스크롤.
- 위치: 객체 수정 영역 −2px ~ 창 영역(DOM 측정, 1920에서 x 375 · 폭 1305). 좁은 폭에서는 가로 스크롤 없이 들어가는 섹션만 표시하고 나머지는 끝의 더보기(⋯) 섹션으로 옮깁니다(Dropdown은 섹션 제목 + 기능 텍스트 목록, 타일과 같은 Hover·Active·클릭, 폭이 넓어지면 자동 복귀, 배지 문구 `homeMenu.moreLabel`).
- Escape: 하위 패널 → 메뉴 순서, 닫히면 로고로 Focus. 속성창·`SWPopup`과 같은 Escape 순서(Focus가 모두 밖이면 최근 사용 하나).
- 상세: 패키지 [카탈로그 5절](../../packages/sw/ai/COMPONENT_CATALOG.md), [통합 검증 기록](./sw-home-menu-integration-2026-10-19.md).
@@ -33,6 +33,6 @@
## 남은 항목
- 미실행: 독립 React 19 Tarball 소비 설치, SW + HM UI CSS 동시 사용, Figma 스크린샷 픽셀 비교(배치·크기는 DOM 실측으로만 확인), Firefox·Safari.
- 좁은 폭: 메뉴는 접히지 않고 메뉴 안에서 가로 스크롤(1440 825px, 1080 639px, 768·520 288px, 390 165px). 좁은 폭 디자인은 없습니다.
- 디자인 승인 대기(미적용): 속성Bar 차이 3건, 열림 상태 Header 상·하단 `#5F4B0F` 선, 폰트 차이.
- 좁은 폭: (2026-10-19 후속 수정) 가로 스크롤 대신 측정형 더보기(⋯). 들어가는 섹션만 앞에서부터 Figma 타일로 표시하고 나머지는 더보기 Dropdown의 텍스트 메뉴(섹션 제목 + 항목, 구분선)로 이동, 폭이 넓어지면 복귀. 실측(capture-responsive): 1920 5개, 1680 3+더보기, 1440·1280·1180 2+더보기, 1080·960 1+더보기, 768 이하 더보기만. 메뉴 행·페이지 가로 스크롤 0.
- 디자인 승인 대기(미적용): 속성Bar 차이 3건, 폰트 차이. 열림 상태 Header 상·하단 `#5F4B0F` 선과 로고 Home On은 2026-10-19 사용자 요청으로 적용(Figma 662:67267).
- Task A의 기존 제한(넓은 `tools` + 390px에서 파일명·레이어 폭 0)은 이번 통합 범위 밖입니다.