docs(sw): align AI docs and comments with the 2026-10-21 implementation

Documentation and comment-only changes (no component logic, Props or design change):
- VIEWER.md / AI_GUIDE / AI_USAGE / catalog / patterns: the two SWViewerHeader versions
  (default, and minimize/close with SWHeaderWindowControls at the end of `trailing`);
  the earlier "never mix SWHeader parts" rule now names this approved exception.
- collapseBelow / iconOnlyBelow (types.ts JSDoc, VIEWER.md, AI_GUIDE): measured steps,
  the numbers only set the order, not pixel breakpoints. Viewer file open is the
  onDocumentOpen button (logoMenu is compatibility only).
- COMPONENT_CATALOG / DESIGN_RULES: recent files show at most 3 rows; preview caption is
  previewTitle or path + file name; body is the red no-preview sign on the brown panel;
  preview / renderPreview are accepted but not rendered (JSDoc updated to match).
- packages/sw/README.md and sw-header-usage.md: current Story list and counts (40, 14
  shown + 26 verification); 760px+ no-More condition; read-only Property Bar values as
  <output>.
- SWHeader.tsx comment: titles are kept (not hidden) from 760px up.
- AI_GUIDE synced into the package ai/ copies (npm run sync:ai-docs).
This commit is contained in:
shkim4 committed 2026-10-21 17:45:26 +09:00
1 parent 646fb60462
commit 8127cc72c4
16 files changed
+73 -42

No files matched your search

+3 -3
View File
@@ -164,7 +164,7 @@ SW 일반 모드와 뷰어 모드는 같은 패키지·같은 CSS 파일이지
1. Typecheck(`tsc --noEmit` 등)와 Production Build
2. 브라우저 렌더링: 재사용한 컴포넌트가 HM 외형으로 보이는지(CSS 진입점 누락 확인), 신규 UI가 같은 토큰을 쓰는지
3. 상태·동작: Hover / Focus(키보드) / Disabled, Popup 열기·닫기·Escape·Focus 복귀, 선택 패키지의 핵심 동작
4. 폭: Web 390 / 768 / 1280 / 1440px, SW 일반 모드는 대상 창 폭(Header는 반응형 More 포함), SW 뷰어 모드는 대상 창 폭과 항목 `collapseBelow`·`iconOnlyBelow` 경계 전후(More 이동·아이콘만 표시), OrgChart는 캔버스 크기·초기 Fit
4. 폭: Web 390 / 768 / 1280 / 1440px, SW 일반 모드는 대상 창 폭(Header는 반응형 More 포함), SW 뷰어 모드는 대상 창 폭을 줄였다 늘리며 실제 공간 부족에 따른 단계(`iconOnlyBelow` 항목 아이콘만 → `collapseBelow` 항목 More 이동, 숫자는 px 경계가 아니라 순서) 전후, OrgChart는 캔버스 크기·초기 Fit
5. 혼합이면 영역 경계에서 스타일 침범이 없는지, SW면 선택하지 않은 모드의 컴포넌트·토큰이 섞이지 않았는지(§5)
6. 실행하지 못한 항목은 `NOT VERIFIED`로 남깁니다.
@@ -223,9 +223,9 @@ import "@hm-design/sw/styles.css"
### 예시 B2 — SW 뷰어 모드 (뷰어 Header + 메뉴 + 패널)
- 읽기: `node_modules/@hm-design/sw/ai/` → AI_USAGE §0(뷰어 모드) → `VIEWER.md`, 예제 `ai/examples/ExampleH-ViewerMode.tsx`
- 재사용: `SWViewerHeader`(`groups` 데이터: `button`·`toggle`·`menu`·`tabs`·`switch`·`custom` 항목, `onCommand`, `trailing`에 `SWViewerStatus`·`SWViewerHeaderButton`), Dropdown은 `type: "menu"` 항목의 `entries`(단독·우클릭은 `SWViewerMenu`), 팝업은 `SWViewerPanel` + `SWViewerSection`·`SWViewerField`·`SWViewerSwitch`·`SWViewerSlider`·`SWViewerInput`·`SWViewerButton`
- 재사용: `SWViewerHeader`(`groups` 데이터: `button`·`toggle`·`menu`·`tabs`·`switch`·`custom` 항목, `onCommand`, `trailing`에 `SWViewerStatus`·`SWViewerHeaderButton`. 창 최소화·닫기가 필요한 버전은 `trailing` 맨 끝에 `SWHeaderWindowControls showWindowMove={false}` — VIEWER.md §2.1), Dropdown은 `type: "menu"` 항목의 `entries`(단독·우클릭은 `SWViewerMenu`), 팝업은 `SWViewerPanel` + `SWViewerSection`·`SWViewerField`·`SWViewerSwitch`·`SWViewerSlider`·`SWViewerInput`·`SWViewerButton`
- 신규: 뷰어에 없는 패널 콘텐츠 → 소비 프로젝트 컴포넌트를 `SWViewerPanel`의 `children`에, 안쪽은 `SWViewerField` + `SWViewerInput` 등, 외형은 `--hm-sw-viewer-*` 토큰
- 하지 않음: `SWHeader`·`SWPopup`을 뷰어 화면에 섞기, 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`·`homeMenu`)를 뷰어 화면에 넣거나 비슷하게 만들기(홈 메뉴는 일반 모드 전용), 로고가 없다고 임의 문자·아이콘 넣기, 연결할 명령이 없는 메뉴를 가짜 동작으로 만들기(제외 또는 `disabled` + 미연결 보고)
- 하지 않음: `SWHeader`·`SWPopup`을 뷰어 화면에 섞기(예외: 위 `trailing` 끝의 `SWHeaderWindowControls` 최소화·닫기), 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`·`homeMenu`)를 뷰어 화면에 넣거나 비슷하게 만들기(홈 메뉴는 일반 모드 전용), 로고가 없다고 임의 문자·아이콘 넣기, 연결할 명령이 없는 메뉴를 가짜 동작으로 만들기(제외 또는 `disabled` + 미연결 보고)
```tsx
import { SWViewerHeader, SWViewerPanel, SWViewerSection, SWViewerField, SWViewerSwitch, type SWViewerHeaderGroup } from "@hm-design/sw"
+3 -3
View File
@@ -164,7 +164,7 @@ SW 일반 모드와 뷰어 모드는 같은 패키지·같은 CSS 파일이지
1. Typecheck(`tsc --noEmit` 등)와 Production Build
2. 브라우저 렌더링: 재사용한 컴포넌트가 HM 외형으로 보이는지(CSS 진입점 누락 확인), 신규 UI가 같은 토큰을 쓰는지
3. 상태·동작: Hover / Focus(키보드) / Disabled, Popup 열기·닫기·Escape·Focus 복귀, 선택 패키지의 핵심 동작
4. 폭: Web 390 / 768 / 1280 / 1440px, SW 일반 모드는 대상 창 폭(Header는 반응형 More 포함), SW 뷰어 모드는 대상 창 폭과 항목 `collapseBelow`·`iconOnlyBelow` 경계 전후(More 이동·아이콘만 표시), OrgChart는 캔버스 크기·초기 Fit
4. 폭: Web 390 / 768 / 1280 / 1440px, SW 일반 모드는 대상 창 폭(Header는 반응형 More 포함), SW 뷰어 모드는 대상 창 폭을 줄였다 늘리며 실제 공간 부족에 따른 단계(`iconOnlyBelow` 항목 아이콘만 → `collapseBelow` 항목 More 이동, 숫자는 px 경계가 아니라 순서) 전후, OrgChart는 캔버스 크기·초기 Fit
5. 혼합이면 영역 경계에서 스타일 침범이 없는지, SW면 선택하지 않은 모드의 컴포넌트·토큰이 섞이지 않았는지(§5)
6. 실행하지 못한 항목은 `NOT VERIFIED`로 남깁니다.
@@ -223,9 +223,9 @@ import "@hm-design/sw/styles.css"
### 예시 B2 — SW 뷰어 모드 (뷰어 Header + 메뉴 + 패널)
- 읽기: `node_modules/@hm-design/sw/ai/` → AI_USAGE §0(뷰어 모드) → `VIEWER.md`, 예제 `ai/examples/ExampleH-ViewerMode.tsx`
- 재사용: `SWViewerHeader`(`groups` 데이터: `button`·`toggle`·`menu`·`tabs`·`switch`·`custom` 항목, `onCommand`, `trailing`에 `SWViewerStatus`·`SWViewerHeaderButton`), Dropdown은 `type: "menu"` 항목의 `entries`(단독·우클릭은 `SWViewerMenu`), 팝업은 `SWViewerPanel` + `SWViewerSection`·`SWViewerField`·`SWViewerSwitch`·`SWViewerSlider`·`SWViewerInput`·`SWViewerButton`
- 재사용: `SWViewerHeader`(`groups` 데이터: `button`·`toggle`·`menu`·`tabs`·`switch`·`custom` 항목, `onCommand`, `trailing`에 `SWViewerStatus`·`SWViewerHeaderButton`. 창 최소화·닫기가 필요한 버전은 `trailing` 맨 끝에 `SWHeaderWindowControls showWindowMove={false}` — VIEWER.md §2.1), Dropdown은 `type: "menu"` 항목의 `entries`(단독·우클릭은 `SWViewerMenu`), 팝업은 `SWViewerPanel` + `SWViewerSection`·`SWViewerField`·`SWViewerSwitch`·`SWViewerSlider`·`SWViewerInput`·`SWViewerButton`
- 신규: 뷰어에 없는 패널 콘텐츠 → 소비 프로젝트 컴포넌트를 `SWViewerPanel`의 `children`에, 안쪽은 `SWViewerField` + `SWViewerInput` 등, 외형은 `--hm-sw-viewer-*` 토큰
- 하지 않음: `SWHeader`·`SWPopup`을 뷰어 화면에 섞기, 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`·`homeMenu`)를 뷰어 화면에 넣거나 비슷하게 만들기(홈 메뉴는 일반 모드 전용), 로고가 없다고 임의 문자·아이콘 넣기, 연결할 명령이 없는 메뉴를 가짜 동작으로 만들기(제외 또는 `disabled` + 미연결 보고)
- 하지 않음: `SWHeader`·`SWPopup`을 뷰어 화면에 섞기(예외: 위 `trailing` 끝의 `SWHeaderWindowControls` 최소화·닫기), 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`·`homeMenu`)를 뷰어 화면에 넣거나 비슷하게 만들기(홈 메뉴는 일반 모드 전용), 로고가 없다고 임의 문자·아이콘 넣기, 연결할 명령이 없는 메뉴를 가짜 동작으로 만들기(제외 또는 `disabled` + 미연결 보고)
```tsx
import { SWViewerHeader, SWViewerPanel, SWViewerSection, SWViewerField, SWViewerSwitch, type SWViewerHeaderGroup } from "@hm-design/sw"
+5 -5
View File
@@ -53,13 +53,13 @@ import "@hm-design/sw/styles.css" // 앱 진입점에서 한 번
## Storybook (HMDesign 저장소)
SW Story는 40개입니다(`SW/Header` 20 · `SW/Footer` 6 · `SW/Popup` 3 · `SW/Viewer` 10 · `SW/Theme` 1). 이 중 11개는 `verification` 태그라 사이드바에서 기본으로 숨겨지며 직접 URL·태그 필터로 엽니다.
SW Story는 40개입니다(`SW/Header` 20 · `SW/Footer` 6 · `SW/Popup` 3 · `SW/Viewer` 10 · `SW/Theme` 1, 2026-10-21 기준). 사이드바 기본 표시는 14개이고 나머지 26개는 `verification` 태그라 기본으로 숨겨지며 직접 URL·태그 필터로 엽니다.
- `SW/Header`: Overview, Interactive States, Logo Slot, Document Dropdown, Responsive More, Dropdown(개별 컨트롤 조합), Document Open, Hidden Sub Controls, Home Menu, Home Menu · 단독 사용 · 조합형 (+ verification 10)
- `SW/Footer`: 전체 Footer · 실제 조합, 도구 그룹별 표시, 도구 및 메뉴 변경 예제, 화면 폭 변화, 개별 부품 (+ verification 1)
- `SW/Header`: SW Header(`sw-header--overview`), Home Menu, Logo Slot, Responsive More (+ verification 16: Dropdown, Document Open, Hidden Sub Controls, Detail Buttons, Interactive States, Window Move Motion, Document Dropdown, K Guide, Home Menu 하위 8개)
- `SW/Footer`: SW Footer(`sw-footer--full-footer`), Responsive More (+ verification 4: 도구 그룹별 표시, Figma 디자인별 Variant, 도구 및 메뉴 변경 예제, 개별 부품)
- `SW/Popup`: Custom Content, Multiple Popups, Property Panel · 4개 비교(Story ID `sw-header--property-panel-compare`)
- `SW/Viewer`: Viewer Header, Menu Groups, Item States, Header + Dropdown, Settings Popup, Popup Content Variants, Variable Size Popups, Responsive Widths, SWHeader vs Viewer Header, Logo
- `SW/Theme`: Theme Inheritance
- `SW/Viewer`: Viewer Header(기본 + 최소화·닫기 버전), Settings Popup(`sw-viewer--content-variants`), Responsive More, SWHeader vs Viewer Header, Logo (+ verification 5: Menu Groups, Item States, Header + Dropdown, Settings Popup(`sw-viewer--settings-panel`), Variable Size Popups)
- `SW/Theme`: (verification 1: Theme Inheritance)
## 빌드 (HMDesign 저장소)
+3 -3
View File
@@ -164,7 +164,7 @@ SW 일반 모드와 뷰어 모드는 같은 패키지·같은 CSS 파일이지
1. Typecheck(`tsc --noEmit` 등)와 Production Build
2. 브라우저 렌더링: 재사용한 컴포넌트가 HM 외형으로 보이는지(CSS 진입점 누락 확인), 신규 UI가 같은 토큰을 쓰는지
3. 상태·동작: Hover / Focus(키보드) / Disabled, Popup 열기·닫기·Escape·Focus 복귀, 선택 패키지의 핵심 동작
4. 폭: Web 390 / 768 / 1280 / 1440px, SW 일반 모드는 대상 창 폭(Header는 반응형 More 포함), SW 뷰어 모드는 대상 창 폭과 항목 `collapseBelow`·`iconOnlyBelow` 경계 전후(More 이동·아이콘만 표시), OrgChart는 캔버스 크기·초기 Fit
4. 폭: Web 390 / 768 / 1280 / 1440px, SW 일반 모드는 대상 창 폭(Header는 반응형 More 포함), SW 뷰어 모드는 대상 창 폭을 줄였다 늘리며 실제 공간 부족에 따른 단계(`iconOnlyBelow` 항목 아이콘만 → `collapseBelow` 항목 More 이동, 숫자는 px 경계가 아니라 순서) 전후, OrgChart는 캔버스 크기·초기 Fit
5. 혼합이면 영역 경계에서 스타일 침범이 없는지, SW면 선택하지 않은 모드의 컴포넌트·토큰이 섞이지 않았는지(§5)
6. 실행하지 못한 항목은 `NOT VERIFIED`로 남깁니다.
@@ -223,9 +223,9 @@ import "@hm-design/sw/styles.css"
### 예시 B2 — SW 뷰어 모드 (뷰어 Header + 메뉴 + 패널)
- 읽기: `node_modules/@hm-design/sw/ai/` → AI_USAGE §0(뷰어 모드) → `VIEWER.md`, 예제 `ai/examples/ExampleH-ViewerMode.tsx`
- 재사용: `SWViewerHeader`(`groups` 데이터: `button`·`toggle`·`menu`·`tabs`·`switch`·`custom` 항목, `onCommand`, `trailing`에 `SWViewerStatus`·`SWViewerHeaderButton`), Dropdown은 `type: "menu"` 항목의 `entries`(단독·우클릭은 `SWViewerMenu`), 팝업은 `SWViewerPanel` + `SWViewerSection`·`SWViewerField`·`SWViewerSwitch`·`SWViewerSlider`·`SWViewerInput`·`SWViewerButton`
- 재사용: `SWViewerHeader`(`groups` 데이터: `button`·`toggle`·`menu`·`tabs`·`switch`·`custom` 항목, `onCommand`, `trailing`에 `SWViewerStatus`·`SWViewerHeaderButton`. 창 최소화·닫기가 필요한 버전은 `trailing` 맨 끝에 `SWHeaderWindowControls showWindowMove={false}` — VIEWER.md §2.1), Dropdown은 `type: "menu"` 항목의 `entries`(단독·우클릭은 `SWViewerMenu`), 팝업은 `SWViewerPanel` + `SWViewerSection`·`SWViewerField`·`SWViewerSwitch`·`SWViewerSlider`·`SWViewerInput`·`SWViewerButton`
- 신규: 뷰어에 없는 패널 콘텐츠 → 소비 프로젝트 컴포넌트를 `SWViewerPanel`의 `children`에, 안쪽은 `SWViewerField` + `SWViewerInput` 등, 외형은 `--hm-sw-viewer-*` 토큰
- 하지 않음: `SWHeader`·`SWPopup`을 뷰어 화면에 섞기, 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`·`homeMenu`)를 뷰어 화면에 넣거나 비슷하게 만들기(홈 메뉴는 일반 모드 전용), 로고가 없다고 임의 문자·아이콘 넣기, 연결할 명령이 없는 메뉴를 가짜 동작으로 만들기(제외 또는 `disabled` + 미연결 보고)
- 하지 않음: `SWHeader`·`SWPopup`을 뷰어 화면에 섞기(예외: 위 `trailing` 끝의 `SWHeaderWindowControls` 최소화·닫기), 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`·`homeMenu`)를 뷰어 화면에 넣거나 비슷하게 만들기(홈 메뉴는 일반 모드 전용), 로고가 없다고 임의 문자·아이콘 넣기, 연결할 명령이 없는 메뉴를 가짜 동작으로 만들기(제외 또는 `disabled` + 미연결 보고)
```tsx
import { SWViewerHeader, SWViewerPanel, SWViewerSection, SWViewerField, SWViewerSwitch, type SWViewerHeaderGroup } from "@hm-design/sw"
+1 -1
View File
@@ -57,7 +57,7 @@ SW 패키지에는 Header·Popup 계열이 두 벌 있습니다. 화면마다 **
### 0.3 모드 혼합 금지
- **한 화면에 Header 하나.** `SWHeader`와 `SWViewerHeader`를 같은 화면에 함께 두지 않습니다. 두 모드를 오가는 프로그램은 상태를 유지한 채 Header 컴포넌트만 바꿉니다(VIEWER.md §1).
- **뷰어 화면:** `SWHeader`에 뷰어 메뉴를 넣거나 `SWHeader`·`SWPopup`을 뷰어처럼 재스타일하지 않습니다. 뷰어 팝업은 `SWViewerPanel`입니다.
- **뷰어 화면:** `SWHeader`에 뷰어 메뉴를 넣거나 `SWHeader`·`SWPopup`을 뷰어처럼 재스타일하지 않습니다. 뷰어 팝업은 `SWViewerPanel`입니다. 예외: 창 최소화·닫기 버전은 `SWViewerHeader`의 `trailing` 맨 끝에 `SWHeaderWindowControls`(`showWindowMove={false}`)를 그대로 둡니다(VIEWER.md §2.1, 2026-10-21).
- **일반 화면:** `SWViewerHeader`·`SWViewerMenu`·`SWViewerPanel`과 패널 컨트롤(`SWViewerSection` …)을 쓰지 않습니다.
- **로고(홈) 메뉴는 SW 일반 모드 전용:** `SWHomeMenu`·`SWHomeMenuSection`·`SWHomeMenuItem`·`SWRecentFiles`·`useSWHomeMenuClose`와 `SWHeader`의 `homeMenu`·`homeMenuOpen`·`defaultHomeMenuOpen`·`onHomeMenuOpenChange`는 일반 모드 `SWHeader` 화면에서만 씁니다. 뷰어 화면에는 홈 메뉴를 만들지 않고(`SWViewerHeader` 옆·`custom` 항목·`SWViewerPanel` 안에 넣거나 비슷하게 다시 만들지 않음), 뷰어 Header와 기능은 그대로 둡니다.
- **토큰:** `--hm-sw-viewer-*` 토큰을 일반 모드 신규 UI에 쓰지 않고, 일반 모드 전용 모양(속성창 행 `SWPopupRow`, `SWToolButton`의 `pill`·`text` 모양, 일반 필드 Dropdown)을 뷰어 신규 UI에 가져오지 않습니다. 두 토큰은 같은 범위(`.hm-sw-header`, `.hm-sw-theme`)에 함께 선언되어 있어 기술적으로 읽히지만, 읽힌다는 것이 허용을 뜻하지 않습니다.
+2 -2
View File
@@ -7,7 +7,7 @@
- **열림 상태:** Dropdown은 `open` / `onOpenChange`(Controlled)를 주지 않으면 내부 상태로 동작합니다.
- **공통 키보드:** Click·Enter·Space 열기/닫기(Hover로는 열리지 않음), ArrowDown 열고 첫(현재) 항목 Focus, Arrow 이동, Escape·바깥 클릭·Tab 이탈 닫기(Escape는 Trigger로 Focus 복귀).
- **검증 표시:** ✅ = 2026-10-19 독립 소비 프로젝트(Tarball 설치)에서 브라우저 검증, ◻ = 소비 검증 범위 밖(Storybook 검증 기록만 있음).
- **먼저 모드:** SW 일반 모드(편집) / SW 뷰어 모드(도면 보기)를 [AI_USAGE.md](./AI_USAGE.md) §0으로 정합니다. **뷰어 모드는 §9(`SWViewer*`)만** 쓰고, 아래 §1~8은 일반 모드용입니다. 두 모드를 한 화면에 섞지 않습니다.
- **먼저 모드:** SW 일반 모드(편집) / SW 뷰어 모드(도면 보기)를 [AI_USAGE.md](./AI_USAGE.md) §0으로 정합니다. **뷰어 모드는 §9(`SWViewer*`)만** 쓰고(예외: 창 최소화·닫기 버전의 `trailing` 끝 `SWHeaderWindowControls`, [VIEWER.md](./VIEWER.md) §2.1), 아래 §1~8은 일반 모드용입니다. 두 모드를 한 화면에 섞지 않습니다.
- **무엇을 쓸까(일반 모드):** Header 전체 → `SWHeader`(1). Header 부품 조합 → 개별 컨트롤(2). 팝업·설정창·정보창 → `SWPopup`(3). 도구 막대·탭·토글 → `SWToolButton` / `SWToolGroup`(4). 로고 클릭 2단 메뉴·최근파일 → `SWHeader homeMenu` / `SWHomeMenu`(5). 게이지바(투명도 등)는 `SWSlider`, 선택 목록(단위 등)은 `SWSelect`(3). Switch·Badge·Progress·Dialog(모달)·Label·FilterBar는 SW에 없습니다 → `@hm-design/ui`를 그대로 씁니다(필요하면 `SWPopup` children 안에).
- **무엇을 쓸까(일반 모드 하단):** 하단 명령 막대(보조기능·명령 칸·분류 탭·명령어 전체보기) → `SWFooter`, 도면 공간 탭(Model/Layout) → `SWFooterLayoutTabs`, 명령창 → `SWFooterCommandLine`(10, [FOOTER.md](./FOOTER.md)).
- **무엇을 쓸까(뷰어 모드):** Header → `SWViewerHeader`(`groups` + `trailing`), Dropdown·우클릭 → `SWViewerMenu`, 팝업·설정창·모달 → `SWViewerPanel` + `SWViewerSection`/`SWViewerField`/`SWViewerSwitch`/`SWViewerSlider`/`SWViewerInput`/`SWViewerButton`, 상태 → `SWViewerStatus`(9).
@@ -105,7 +105,7 @@ Figma 26:10591(home on), 타일 15종 26:11355 … 26:11631, 섹션 제목 26:12
| `SWHomeMenu` | 2단 메뉴 전체(섹션 여러 개). 보통 `SWHeader homeMenu`로 씀 | `sections`(`SWHomeMenuSectionData[]`) 또는 `children`, `open`/`defaultOpen`(false)/`onOpenChange(open, reason)`, `activeIds`/`defaultActiveIds`/`onActiveChange`, `selection`(`multiple` 기본 / `single`), `onItemSelect`, `closeOnBackgroundClick`(true), `closeOnSelect`(false), `closeOnEscape`(true), `closeOnOutsideClick`(false), `autoFocus`(true), `returnFocusRef`, `accent`(`auto`/`sections`/`outline`), `moreLabel`("더보기") | 높이 77(섹션 75 + 강조 2), 섹션 폭은 타일 수로 자동. 좁으면 들어가는 섹션만 표시하고 나머지는 더보기(⋯) Dropdown의 텍스트 메뉴(섹션 제목 + 항목, 같은 Active·Callback·하위 패널)로 이동(가로 스크롤 없음, 넓어지면 복귀) | ✅ |
| `SWHomeMenuSection` | 섹션(제목 badge + 타일 그룹) | `title`, `children` | badge 폭 = 글자 + 46px | ✅ |
| `SWHomeMenuItem` | 80 × 46 타일(Default / Hover / Active) | 데이터 `SWHomeMenuItemData`: `id`, `label`, `icon`, `hoverIcon`, `toggle`(기본: `panel` 있는 항목만 true = 패널이 열린 동안 Active, 나머지는 실행 버튼 = 클릭 후 ON 없음. true = 켜고 끄기 유지), `panel`, `panelLabel`, `disabled`, `hidden`, `title`, `onSelect`. 메뉴 밖 단독이면 `active`/`defaultActive`/`onActiveChange` | Hover·Active = 주황 gradient 1px 테두리 + `#FF5C00` 아이콘·글자 | ✅ |
| `SWRecentFiles` | 선택 하위 패널: 최근파일 목록(2026-10-21부터 앞 3개만 표시. Hover·Focus 시 2뎁스 미리보기 영역(제목 = 해당 파일의 `previewTitle` 또는 `name` + 검정 빈 본문)만 표시하고 `preview`·`renderPreview` 이미지는 호환용으로 받기만 하고 렌더링하지 않음) | `files`(`SWFileEntry[]`: `id`, `name`, `path`, `badge`, `preview`, `previewTitle`, `disabled`), `onFileOpen`, `onFileHover`, `renderPreview`, `closeOnOpen`(true), `emptyText` | 폭 354, 행 50, 미리보기 310 × 270 | ✅ |
| `SWRecentFiles` | 선택 하위 패널: 최근파일 목록(2026-10-21부터 `files` 중 앞 **최대 3개**만 표시, 배열 자체는 자르지 않음). Hover·Focus한 행 옆에 2뎁스 미리보기 패널(310 × 270)을 표시합니다: 제목 = `previewTitle`, 없으면 **`path` + 파일명**(`path`가 이미 파일명으로 끝나면 그대로, `path`가 없으면 `name`), 본문 = 미리보기 없음 표시(**갈색 패널 `#62533D` 가운데 빨간 금지 표시 `#FF0000`**). `preview`·`renderPreview`는 호환용으로 받기만 하고 이미지·썸네일을 렌더링하지 않음(타입 변경 없음) | `files`(`SWFileEntry[]`: `id`, `name`, `path`, `badge`, `preview`, `previewTitle`, `disabled`), `onFileOpen`, `onFileHover`, `renderPreview`, `closeOnOpen`(true), `emptyText` | 폭 354, 행 50, 미리보기 310 × 270 | ✅ |
- **Header 연결:** `SWHeader`의 `homeMenu`에 `SWHomeMenu` Props를 주면 됩니다. 기본 숨김, 로고 클릭 열기, 다시 클릭 닫기. 위치는 SWHeader가 실제 DOM으로 정합니다(객체 수정 영역 −2px ~ 창 영역, 고정 x 없음. `controls`에 `document`가 있으면 파일명 오른쪽 −2px부터). 로고·파일명·창 영역은 가리지 않습니다.
- **이벤트 우선순위:** `homeMenu`가 있으면 로고 클릭은 메뉴 열기·닫기만 하고 `onHomeClick`을 호출하지 않습니다(의도하지 않은 홈 이동 방지). 상태 알림은 `onHomeMenuOpenChange`(`reason`: `home`, `escape`, `background`, `outside`, `select`, `file`, `document`).
+1 -1
View File
@@ -131,7 +131,7 @@
## 7. 뷰어 모드 기본 조합 — [ExampleH-ViewerMode.tsx](./examples/ExampleH-ViewerMode.tsx)
SW **뷰어 모드**(도면 보기) 화면입니다. 모드 선택은 [AI_USAGE.md](./AI_USAGE.md) §0, Props·상태는 [VIEWER.md](./VIEWER.md), 토큰은 [DESIGN_RULES.md](./DESIGN_RULES.md) §11. 이 패턴에는 1~6의 일반 모드 컴포넌트(`SWHeader`, `SWPopup`, `SWToolButton` …)를 섞지 않습니다.
SW **뷰어 모드**(도면 보기) 화면입니다. 모드 선택은 [AI_USAGE.md](./AI_USAGE.md) §0, Props·상태는 [VIEWER.md](./VIEWER.md), 토큰은 [DESIGN_RULES.md](./DESIGN_RULES.md) §11. 이 패턴에는 1~6의 일반 모드 컴포넌트(`SWHeader`, `SWPopup`, `SWToolButton` …)를 섞지 않습니다(예외: 창 최소화·닫기 버전의 `trailing` 끝 `SWHeaderWindowControls`, VIEWER.md §2.1).
```tsx
const groups: SWViewerHeaderGroup[] = [
+1 -1
View File
@@ -175,7 +175,7 @@
| 섹션 제목 | Figma tit_* badge(9-slice), 글자 400 검정 | `--hm-sw-home-menu-title-color` |
| 타일 상태 | Default 아이콘 `#B6B4AF` · Hover·Pressed(누르는 동안)·Active 주황 gradient 1px 테두리 + 아이콘·글자 `#FF5C00`. 아이콘·라벨의 위치·크기·간격과 타일 크기는 Default와 같음(2026-10-20 사용자 확정, Figma의 1px 위·왼쪽 이동은 적용하지 않음). 라벨 700. 최근파일 외 항목은 실행 버튼: 클릭 후 ON 유지 없음(2026-10-19 사용자 확정). Active 유지는 하위 패널 타일(최근파일, 목록이 열린 동안)과 더보기·홈 버튼(메뉴가 열린 동안)만. 유지형이 필요하면 `toggle: true` | `--hm-sw-home-menu-item-*`, `--hm-sw-home-menu-icon-color` |
| 하단 강조 | `sections` = 섹션마다 주황 띠, `outline` = 메뉴 전체 2px 외곽선(하위 패널이 열린 동안, `auto` 기본) | `--hm-sw-home-menu-accent-*` |
| 최근파일 패널 | 폭 354, 행 50, 배경 `#27241D`, 주황 테두리, 행 Hover `#62533D` + `#FF5C00`, 미리보기 310 × 270. 미리보기 위치: 목록 오른쪽(Figma) → 오른쪽에 공간이 없으면 왼쪽 → 좌우 모두 없으면(예: 390) 목록 위에 겹쳐 Hover 행 바로 아래(목록 왼쪽 1px 안쪽, 4면 테두리·radius 4, 크기·색 동일), 화면 아래 공간이 없으면 행 바로 위(2026-10-19 사용자 확정, Figma 없음). 가로 스크롤 없음 | `--hm-sw-recent-files-*` |
| 최근파일 패널 | 폭 354, 행 50, 배경 `#27241D`, 주황 테두리, 행 Hover `#62533D` + `#FF5C00`, 최대 3행(2026-10-21), 미리보기 310 × 270(제목 경로 + 파일명, 본문은 갈색 `#62533D` 면 가운데 빨간 금지 표시 `--hm-sw-recent-files-preview-none-color` `#FF0000`, 이미지 렌더링 없음). 미리보기 위치: 목록 오른쪽(Figma) → 오른쪽에 공간이 없으면 왼쪽 → 좌우 모두 없으면(예: 390) 목록 위에 겹쳐 Hover 행 바로 아래(목록 왼쪽 1px 안쪽, 4면 테두리·radius 4, 크기·색 동일), 화면 아래 공간이 없으면 행 바로 위(2026-10-19 사용자 확정, Figma 없음). 가로 스크롤 없음 | `--hm-sw-recent-files-*` |
| 동작 | 기본 숨김 → 로고 클릭 열기 → 다시 클릭 닫기. 메뉴 빈 곳 클릭 닫기(Figma CLOSE). Escape: 하위 패널 → 메뉴, 닫히면 로고로 Focus. 메뉴 밖 클릭은 닫지 않음(Figma overlay 기준) | — |
| 좁은 폭 | 측정해 들어가는 섹션만 표시, 나머지는 끝의 더보기(⋯) 섹션(기존 섹션·타일 부품, 배지 `moreLabel` 기본 "더보기", 타일 "N개 그룹")으로 이동. 메뉴 배경·폭·높이·테두리·강조선은 그대로(더보기 섹션 면이 남는 폭을 채워 객체 수정 영역을 계속 덮음)이고, 더보기 배지·⋯ 타일만 마지막으로 표시된 섹션 바로 오른쪽에 붙음. 전체가 들어가는 폭에서는 더보기 숨김. 더보기 Dropdown과 최근파일 목록은 동시에 열리지 않음: 더보기를 열면 열린 목록을 먼저 닫고, Dropdown에서 최근파일을 고르면 Dropdown을 닫고 목록을 엶. Dropdown = 텍스트 메뉴(SW brown Dropdown: 배경 `#27241D`·테두리 `#000`·radius 3, 제목 12px 700 `#D4CBBD`, 행 30px·14px 700·좌우 12, 그룹 구분선 = 레이어 footer 선), ⋯ 타일 아래 왼쪽 정렬(화면 안으로 맞춤). 행 Hover = Pressed = Active = `#62533D` + `#FF5C00`. 일반 행은 클릭 후 ON·✓ 없음, 최근파일 행(›)만 목록이 열린 동안 Active. ✓는 `toggle: true` 행에만. 클릭·Callback·상태 규칙은 타일과 같음. ↓(더보기) = 첫 행, ↑/↓ 이동. Escape: Dropdown → 하위 패널 → 메뉴. 더보기 안 최근파일은 Dropdown을 닫고 더보기 타일 아래(화면 안으로 맞춤)에 기존 최근파일 목록으로 열림, 미리보기는 오른쪽에 공간이 없으면 왼쪽, 좌우 모두 없으면 목록 위 행 아래(최근파일 패널 행 참고). 목록이 메뉴 밖으로 나가는 부분은 메뉴 하단 강조선(2px, 같은 gradient)이 목록 상단으로 이어짐(실측 위치 기준, `--hm-sw-home-menu-accent-outline-width`). 가로 스크롤 없음 | `--hm-sw-home-menu-more-*` |
+24 -6
View File
@@ -4,7 +4,7 @@
> **모드 선택 먼저:** 이 문서는 SW **뷰어 모드** 전용입니다. 화면이 뷰어 모드인지는 [AI_USAGE.md](./AI_USAGE.md) §0(설정 `HMDesign 사용 유형: SW 뷰어` → 요청 문구 → 불명확하면 질문)으로 정합니다. 전체 Export 목록은 [COMPONENT_CATALOG.md](./COMPONENT_CATALOG.md) §9, 토큰·상태 요약은 [DESIGN_RULES.md](./DESIGN_RULES.md) §11, 조합은 [COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md) 7입니다.
>
> **일반 모드 금지 사항(상호 참조):** 뷰어 화면에 `SWHeader`·`SWPopup`·`SWPopupSection`·`SWPopupRow`·`SWToolButton`·`SWToolGroup`·`SWHeaderPropertyPanel`을 쓰거나 재스타일하지 않고, 일반 모드 화면에 `SWViewer*`와 `--hm-sw-viewer-*` 토큰을 쓰지 않습니다. `SWViewerPanel`이 내부에서 `SWPopup` 프레임을 렌더링하는 것과 두 Header가 `--hm-sw-header-document-*`·`object-*`·`property-*`·외곽선 토큰을 공유하는 것은 패키지 구현이며 모드 혼합이 아닙니다([AI_USAGE.md](./AI_USAGE.md) §0.3). 패키지에 없는 뷰어 UI는 [AI_USAGE.md](./AI_USAGE.md) §0.4 뷰어 열을 따릅니다.
> **일반 모드 금지 사항(상호 참조):** 뷰어 화면에 `SWHeader`·`SWPopup`·`SWPopupSection`·`SWPopupRow`·`SWToolButton`·`SWToolGroup`·`SWHeaderPropertyPanel`을 쓰거나 재스타일하지 않고, 일반 모드 화면에 `SWViewer*`와 `--hm-sw-viewer-*` 토큰을 쓰지 않습니다. `SWViewerPanel`이 내부에서 `SWPopup` 프레임을 렌더링하는 것과 두 Header가 `--hm-sw-header-document-*`·`object-*`·`property-*`·외곽선 토큰을 공유하는 것은 패키지 구현이며 모드 혼합이 아닙니다([AI_USAGE.md](./AI_USAGE.md) §0.3). 유일한 예외는 창 최소화·닫기 버전입니다: `SWHeaderWindowControls`(창 이동 없이 최소화·닫기)를 `trailing` 오른쪽 끝에 그대로 두는 구성은 2026-10-21 승인된 구성입니다(§2.1). 패키지에 없는 뷰어 UI는 [AI_USAGE.md](./AI_USAGE.md) §0.4 뷰어 열을 따릅니다.
## 1. 언제 무엇을 쓰나
@@ -27,7 +27,7 @@
| Props | 설명 |
|---|---|
| `groups: SWViewerHeaderGroup[]` | 메뉴 데이터(순서대로 배치). 그룹: `id`, `label`(접근성 이름·More 구획 제목), `moreLabel?`, `surface?: "plain" \| "field"`(field = 사용자 확정 속성 입력 면), `placement?: "start" \| "end"`, `hidden?`, `items` |
| 항목 `SWViewerHeaderItem` | 공통: `id`, `label`, `icon?`(16px, 프로그램 제공), `title?`, `disabled?`, `hidden?`, `collapseBelow?`(Header 폭 미만이면 More로 이동), `iconOnlyBelow?`(미만이면 아이콘만), `iconOnly?`, `moreLabel?` |
| 항목 `SWViewerHeaderItem` | 공통: `id`, `label`, `icon?`(16px, 프로그램 제공), `title?`, `disabled?`, `hidden?`, `collapseBelow?`(공간이 부족할 때 More로 옮기는 대상·순서 — 값이 큰 것부터, px 기준 아님), `iconOnlyBelow?`(공간이 부족할 때 아이콘만으로 줄이는 대상, `icon` 필요 — 값의 크기는 무관), `iconOnly?`, `moreLabel?` |
| `type: "button"` | `onSelect` |
| `type: "toggle"` | `pressed`, `onPressedChange` (패널 열기/닫기 등) |
| `type: "menu"` | `entries: SWViewerMenuEntry[]`, `menuWidth?`, `menuAlign?: "start" \| "end"` |
@@ -40,15 +40,33 @@
| `onDocumentOpen?()`, `documentOpenLabel?` | 로고 오른쪽 파일 열기 버튼(`SWHeader` `documentOpenPlacement="header"`와 같은 파일명 면 `SWToolButton appearance="document"` + `SWHeader` `...` 버튼 테두리의 폴더 아이콘 + `documentOpenLabel`, 기본 `Open`). 공간이 부족하면 아이콘만(라벨은 Tooltip·접근성 이름) 표시하고 More로 옮기지 않습니다. 실행 후 `onCommand({ id: "open", itemId: "document" })` |
| `logoMenu?: { title?, items }` | (2026-10-21 이후 뷰어 화면에서 사용하지 않음, 호환용) 로고 클릭 메뉴. 파일 열기는 `onDocumentOpen` 버튼을 씁니다. 항목 `{ id, label, icon?, value?, checked?, disabled?, hidden?, onSelect? }` |
| `logo?`, `logoSrc?`, `logoAlt?`, `onHomeClick?` | `SWHeader`와 같은 로고 Props. **주지 않으면 아무것도 그리지 않습니다**(“HM” 문자·기본 아이콘 없음, 로고 면은 사선 끝 24px만). 주면 72px 면(로고가 더 넓으면 그만큼), 높이 최대 24px |
| `trailing?: ReactNode` | 오른쪽 끝 자유 영역: `SWViewerStatus`, `SWViewerHeaderButton` |
| `trailing?: ReactNode` | 오른쪽 끝 자유 영역: `SWViewerStatus`, `SWViewerHeaderButton`, 창 최소화·닫기 버전이면 맨 끝에 `SWHeaderWindowControls`(§2.1) |
| `moreLabel?`, `toolbarLabel?`, `<header>` 속성, `ref` | |
- **반응형(2026-10-21, 실제 가용 폭 기준 3단계):** 고정 Breakpoint를 쓰지 않고 Header 행의 실제 남는 폭(로고·파일명·메뉴·Gap·Padding·More 버튼 포함, 파일명은 글자 폭까지만)을 측정합니다. ① 모두 들어가면 기본 형태(아이콘+텍스트), ② 부족하면 `icon`과 `iconOnlyBelow`가 있는 항목을 모두 아이콘만(라벨은 Tooltip·접근성 이름), ③ 그래도 부족하면 `collapseBelow` 항목을 하나씩 More(⋯, 베이지 260px Dropdown)로 옮깁니다(값이 큰 것부터, 같으면 오른쪽 먼저). 넓어지면 More → 아이콘 → 기본 순서로 복귀하고, 상단과 More에 같은 항목이 동시에 나오지 않습니다. `collapseBelow`·`iconOnlyBelow`의 숫자는 순서만 정합니다(Props 형식 동일). 파일 열기는 `logoMenu`, 샘플은 `documents`. 레이어·설정·속성은 `collapseBelow` 없이 아이콘까지만. 1180px 미만 간격 2px, 560px 미만 상태 글자 숨김(CSS container)은 그대로입니다. 가로 스크롤은 만들지 않습니다.
- **반응형(2026-10-21, 실제 가용 폭 기준 3단계):** 고정 Breakpoint를 쓰지 않고 Header 행의 실제 남는 폭(로고·파일명·메뉴·Gap·Padding·More 버튼 포함, 파일명은 글자 폭까지만)을 측정합니다. ① 모두 들어가면 기본 형태(아이콘+텍스트), ② 부족하면 `icon`과 `iconOnlyBelow`가 있는 항목을 모두 아이콘만(라벨은 Tooltip·접근성 이름), ③ 그래도 부족하면 `collapseBelow` 항목을 하나씩 More(⋯, 베이지 260px Dropdown)로 옮깁니다(값이 큰 것부터, 같으면 오른쪽 먼저). 넓어지면 More → 아이콘 → 기본 순서로 복귀하고, 상단과 More에 같은 항목이 동시에 나오지 않습니다. `collapseBelow`·`iconOnlyBelow`의 숫자는 px Breakpoint가 아니며 순서만 정합니다(`iconOnlyBelow`는 지정 여부만, `collapseBelow`는 값이 큰 것부터; Props 형식 동일). 로고 옆 파일 열기 버튼(`onDocumentOpen`)은 ② 단계에서 아이콘만으로 줄고 More로 옮기지 않으며, 샘플은 `documents`. 레이어·설정·속성은 `collapseBelow` 없이 아이콘까지만. 1180px 미만 간격 2px, 560px 미만 상태 글자 숨김(CSS container)은 그대로입니다. 가로 스크롤은 만들지 않습니다.
- More는 그룹/항목 단위 구획으로 자동 구성됩니다: menu·tabs는 자기 구획(`moreLabel`), button·toggle·switch는 그룹 구획(`group.moreLabel ?? group.label`). toggle·switch는 체크 항목, tabs는 라디오 항목이 됩니다.
- **Dropdown 색(2026-10-21):** Header 안의 Dropdown — 메뉴 버튼(`type: "menu"`), More(⋯), 로고 메뉴 — 은 크기·간격·글자는 `SWViewerMenu` 그대로, 색은 일반 `SWHeader`의 `...` 메뉴와 같습니다(면 #0e3d2f, Border `--hm-sw-header-object-border-color`, 그림자 0 4px 12px rgba(0,0,0,.5), 글자 `--hm-sw-header-text-color`, 보조 글자는 같은 색 80%, Hover `--hm-sw-header-hover`, 구분선 `--hm-sw-header-document-menu-divider-color`). 파일명 목록(`documents`)은 `SWHeader` 파일명 목록 모양을 유지하고, 단독 `SWViewerMenu`(우클릭 메뉴 등)는 기존 색 그대로입니다.
- **상태 표현:** Hover = #266651 면, Active·Pressed·열림·선택 탭 = 글자·아이콘 #FFC600(선택 박스 없음), Disabled = 60% 불투명, Focus = 2px #FFC600 외곽선.
- **글자·아이콘 외곽선(SWHeader와 동일):** 14px 글자 `--hm-sw-header-text-outline`(검정 1.3px), 11px 캡션·개수·상태 `--hm-sw-header-label-outline`(검정 1px), 아이콘 `SWViewerIcon`(SWHeader 아이콘 자산의 1px 검정 바깥 Stroke를 **SVG stroke**로 재현: 아이콘 뒤 복사본의 모든 도형을 검정, 원래 선 두께 + 화면 기준 양쪽 1px(`--hm-sw-viewer-icon-outline-width`)로 그림. 위 아이콘은 그대로라 색·모양·선 두께 유지. CSS `filter` 미사용, glow는 생략). Header 항목·`SWViewerHeaderButton`의 `icon`은 자동 적용, `custom` / `trailing`의 직접 그린 아이콘은 `<SWViewerIcon>`으로 감쌉니다. 색·크기·굵기·간격은 그대로이고 Hover/Active는 채움 색만 바뀝니다. Dropdown·패널 안에는 외곽선이 없습니다(SWHeader More 메뉴와 같음). 프로그램 아이콘은 `currentColor`로 그리면 상태 색과 외곽선이 자동 적용됩니다.
### 2.1 두 가지 구성 (2026-10-21)
| 버전 | 구성 | 사용 |
|---|---|---|
| 기본 | `trailing`에 `SWViewerStatus`·`SWViewerHeaderButton` 등만 | 창 버튼이 없는 화면(웹 뷰어, 프로그램이 창 틀을 따로 가짐) |
| 최소화·닫기 | 기존 `trailing` 항목 **뒤 맨 끝**에 `<SWHeaderWindowControls showWindowMove={false} onMinimize={…} onClose={…} />` | 설치형 프로그램처럼 Header 오른쪽 끝에 창 최소화·닫기가 필요한 화면 |
```tsx
<SWViewerHeader groups={groups} onCommand={run} trailing={<>
<SWViewerStatus … />
<SWHeaderWindowControls showWindowMove={false} onMinimize={minimizeWindow} onClose={closeWindow} />
</>} />
```
- 새 API는 없습니다(`trailing` 슬롯 사용). 버튼 아이콘·크기·Hover는 `SWHeader`와 같고, 맨 끝에 두면 오른쪽 여백 없이 Header 끝에 붙습니다(패키지 CSS가 처리). 창 이동(`showWindowMove`)은 뷰어 버전에 넣지 않습니다.
- 실제 창 최소화·닫기는 프로그램 담당입니다(`onMinimize`·`onClose`). Storybook: `SW/Viewer › Viewer Header`의 두 번째 섹션.
- 이 버튼 외의 `SWHeader` 부품(필드·Dropdown·`SWHeaderActions` 등)은 뷰어 화면에 넣지 않습니다.
## 3. `SWViewerMenu` / `SWViewerMenuEntry`
`entries`: `item`(`icon?`, `shortcut?`, `onSelect`), `checkbox`(`checked`, `onCheckedChange`), `radio`(`checked`, `onSelect`), `submenu`(`entries` — 다단계), `label`, `separator`. 공통 `disabled?`, `hidden?`, `keepOpen?`.
@@ -103,13 +121,13 @@ const groups: SWViewerHeaderGroup[] = [
| 하지 말 것 | 이유 / 대신 |
|---|---|
| `<SWViewerHeader logo="HM" />`처럼 로고가 없다고 임의 문자·아이콘 넣기 | 로고는 프로그램이 정합니다. 없으면 생략 |
| 뷰어 화면에 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`, `SWHeader homeMenu`) 넣기 또는 비슷한 2단 메뉴 직접 만들기 | 홈 메뉴는 SW 일반 모드 전용입니다. 뷰어 로고의 파일 열기 메뉴는 `logoMenu`, 샘플 목록은 `documents`(직접 만든 Dropdown 금지) |
| 뷰어 화면에 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`, `SWHeader homeMenu`) 넣기 또는 비슷한 2단 메뉴 직접 만들기 | 홈 메뉴는 SW 일반 모드 전용입니다. 뷰어의 파일 열기는 로고 옆 `onDocumentOpen` 버튼, 샘플 목록은 `documents`(직접 만든 Dropdown 금지) |
| 뷰어 Header를 `div` + `button`으로 다시 만들기, `hm-sw-viewer-*` class를 소비 코드에 붙이기 | 데이터(`groups`)와 `custom` / `trailing`으로 구성 |
| Header 버튼 글자에 `-webkit-text-stroke`·`border`로 외곽선 만들기 | 외곽선은 패키지 토큰이 이미 적용합니다 |
| 메뉴 색·간격을 `#123456`, `margin` 등 임의 값으로 덮기 | 토큰 `--hm-sw-viewer-*` / `--hm-sw-header-*`만 가장 바깥 SW 범위(`<SWTheme>` 또는 단독 컴포넌트 루트)의 `style`로 조정(기본값 유지 권장, [AI_USAGE.md](./AI_USAGE.md) §3.1) |
| Header·Dropdown·More·Panel의 글꼴을 각각 따로 지정, `.hm-sw-viewer-menu` 등 class로 덮기 | 화면을 `<SWTheme style={theme}>` 하나로 감싸면 Header·Dropdown·More·Panel이 같은 값을 상속합니다. 글꼴 기본값은 Noto Sans KR이며 Web UI 글꼴 토큰을 복사하지 않습니다 |
| 존재하지 않는 Props 추측(`items`, `content`, `renderBody` …) | 위 표와 `dist/index.d.ts`가 전부 |
| 일반 모드 화면에서 `SWViewerHeader` 사용, 뷰어에서 `SWHeader` 재스타일 | 모드별 Header를 바꿔 끼웁니다 |
| 일반 모드 화면에서 `SWViewerHeader` 사용, 뷰어에서 `SWHeader` 재스타일 | 모드별 Header를 바꿔 끼웁니다(창 최소화·닫기 `SWHeaderWindowControls`를 `trailing` 끝에 두는 §2.1 구성은 허용) |
| 기능 없는 메뉴를 가짜 동작으로 연결 | 항목 제외 또는 `disabled` + 미연결 보고 |
## 7. 디자인 변경 시 주의
@@ -321,7 +321,8 @@ export const SWHeader = forwardRef<HTMLElement, SWHeaderProps>(function SWHeader
const baseMenu = menuIds.includes("layer") ? "layer" : menuIds[0]
const validPromoted = promoted && enabled(promoted) ? promoted : null
// 2026-10-21 (user decision): no responsive More from 760px up. Nothing moves out of the header there; narrower headers
// hide the field titles, then shrink the file name, then the field values (ellipsis, sw.css "Responsive"). Below 760px
// keep the field titles (they and the values ellipsize), hide the 그리기모드 text and pictures, then shrink the file
// name, then the field values (ellipsis, sw.css "Responsive"). Below 760px
// (phones, e.g. 390) the former breakpoints and More are unchanged.
const breakpointOverflowAt = (id: SWHeaderControlId, width: number) => width < MORE_BELOW && (id === "color" ? width < 900 : ["lineStyle", "textStyle", "dimensionStyle"].includes(id) ? width < 1800 : ["propertyBar", "propertyPanel", "details"].includes(id) ? width < 1080 : ["print", "popupGuide"].includes(id) ? width < 640 : id === "drawingMode" ? width < 1180 : false)
const breakpointOverflow = (id: SWHeaderControlId) => breakpointOverflowAt(id, headerWidth)
@@ -23,7 +23,7 @@ export interface SWRecentFilesProps extends Omit<HTMLAttributes<HTMLDivElement>,
onFileOpen?: (file: SWFileEntry) => void
/** Hover / focus change of the previewed row (null when none). */
onFileHover?: (file: SWFileEntry | null) => void
/** Preview content when a file has no `preview` (e.g. a thumbnail loaded by the host). */
/** Accepted for compatibility, not called (2026-10-21): the preview shows the "no preview" sign, never host content. */
renderPreview?: (file: SWFileEntry) => ReactNode
/** Inside <SWHomeMenu>: close the menu after `onFileOpen`. Default true. */
closeOnOpen?: boolean
@@ -36,7 +36,8 @@ export interface SWRecentFilesProps extends Omit<HTMLAttributes<HTMLDivElement>,
* Figma 최근파일 662:67331: 354px recent-file list (rows 50px: optional 29 x 28 badge, name 14px, path 12px)
* with the 2-depth preview 662:67471 beside the hovered / focused row. The list height follows the rows and is
* capped (`--hm-sw-recent-files-max-height`, Figma 750 = 15 rows); longer lists scroll inside. Only the first 3
* files are shown (2026-10-21).
* files are shown (2026-10-21); the preview caption is `previewTitle` or the `path` + file name, and its body is the
* red "no preview" sign on the brown panel (no image is rendered).
*/
export function SWRecentFiles({
files, onFileOpen, onFileHover, renderPreview, closeOnOpen = true, emptyText = "최근 파일이 없습니다",
@@ -107,7 +107,10 @@ export interface SWFileEntry {
path?: string
/** 29 x 28 slot before the text (Figma icon_dualmonitor: the file is open on monitor 1 / 2). Rows without it keep the text column. */
badge?: ReactNode
/** 2-depth preview shown beside the hovered / focused row (Figma 파일출력 2뎁스 662:67471). */
/**
* Accepted for compatibility, not rendered (2026-10-21): the 2-depth preview beside the hovered / focused row shows the
* "no preview" sign instead of any image (Figma 파일출력 2뎁스 662:67471).
*/
preview?: ReactNode
/** Preview caption; defaults to `path` + `name` (2026-10-21; a `path` already ending with the name is not doubled). */
previewTitle?: string
@@ -70,9 +70,15 @@ interface SWViewerHeaderItemBase {
disabled?: boolean
/** Removed from the header and from More. */
hidden?: boolean
/** Header width (px) below which the item moves into the More dropdown. */
/**
* Moves into the More dropdown when the measured header row has no room left, after the icons-only step. Not a pixel
* breakpoint: the value only sets the order (higher first; same value, rightmost first).
*/
collapseBelow?: number
/** Header width (px) below which only the icon is shown (label kept as the accessible name). */
/**
* Shows only the icon (label kept as tooltip and accessible name) when the measured header row has no room left; all
* such items switch together, before any item moves into More. Needs `icon`. Not a pixel breakpoint: any value.
*/
iconOnlyBelow?: number
/** Always icon only (e.g. the info button). Needs `icon`. */
iconOnly?: boolean
+3 -3
View File
@@ -164,7 +164,7 @@ SW 일반 모드와 뷰어 모드는 같은 패키지·같은 CSS 파일이지
1. Typecheck(`tsc --noEmit` 등)와 Production Build
2. 브라우저 렌더링: 재사용한 컴포넌트가 HM 외형으로 보이는지(CSS 진입점 누락 확인), 신규 UI가 같은 토큰을 쓰는지
3. 상태·동작: Hover / Focus(키보드) / Disabled, Popup 열기·닫기·Escape·Focus 복귀, 선택 패키지의 핵심 동작
4. 폭: Web 390 / 768 / 1280 / 1440px, SW 일반 모드는 대상 창 폭(Header는 반응형 More 포함), SW 뷰어 모드는 대상 창 폭과 항목 `collapseBelow`·`iconOnlyBelow` 경계 전후(More 이동·아이콘만 표시), OrgChart는 캔버스 크기·초기 Fit
4. 폭: Web 390 / 768 / 1280 / 1440px, SW 일반 모드는 대상 창 폭(Header는 반응형 More 포함), SW 뷰어 모드는 대상 창 폭을 줄였다 늘리며 실제 공간 부족에 따른 단계(`iconOnlyBelow` 항목 아이콘만 → `collapseBelow` 항목 More 이동, 숫자는 px 경계가 아니라 순서) 전후, OrgChart는 캔버스 크기·초기 Fit
5. 혼합이면 영역 경계에서 스타일 침범이 없는지, SW면 선택하지 않은 모드의 컴포넌트·토큰이 섞이지 않았는지(§5)
6. 실행하지 못한 항목은 `NOT VERIFIED`로 남깁니다.
@@ -223,9 +223,9 @@ import "@hm-design/sw/styles.css"
### 예시 B2 — SW 뷰어 모드 (뷰어 Header + 메뉴 + 패널)
- 읽기: `node_modules/@hm-design/sw/ai/` → AI_USAGE §0(뷰어 모드) → `VIEWER.md`, 예제 `ai/examples/ExampleH-ViewerMode.tsx`
- 재사용: `SWViewerHeader`(`groups` 데이터: `button`·`toggle`·`menu`·`tabs`·`switch`·`custom` 항목, `onCommand`, `trailing`에 `SWViewerStatus`·`SWViewerHeaderButton`), Dropdown은 `type: "menu"` 항목의 `entries`(단독·우클릭은 `SWViewerMenu`), 팝업은 `SWViewerPanel` + `SWViewerSection`·`SWViewerField`·`SWViewerSwitch`·`SWViewerSlider`·`SWViewerInput`·`SWViewerButton`
- 재사용: `SWViewerHeader`(`groups` 데이터: `button`·`toggle`·`menu`·`tabs`·`switch`·`custom` 항목, `onCommand`, `trailing`에 `SWViewerStatus`·`SWViewerHeaderButton`. 창 최소화·닫기가 필요한 버전은 `trailing` 맨 끝에 `SWHeaderWindowControls showWindowMove={false}` — VIEWER.md §2.1), Dropdown은 `type: "menu"` 항목의 `entries`(단독·우클릭은 `SWViewerMenu`), 팝업은 `SWViewerPanel` + `SWViewerSection`·`SWViewerField`·`SWViewerSwitch`·`SWViewerSlider`·`SWViewerInput`·`SWViewerButton`
- 신규: 뷰어에 없는 패널 콘텐츠 → 소비 프로젝트 컴포넌트를 `SWViewerPanel`의 `children`에, 안쪽은 `SWViewerField` + `SWViewerInput` 등, 외형은 `--hm-sw-viewer-*` 토큰
- 하지 않음: `SWHeader`·`SWPopup`을 뷰어 화면에 섞기, 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`·`homeMenu`)를 뷰어 화면에 넣거나 비슷하게 만들기(홈 메뉴는 일반 모드 전용), 로고가 없다고 임의 문자·아이콘 넣기, 연결할 명령이 없는 메뉴를 가짜 동작으로 만들기(제외 또는 `disabled` + 미연결 보고)
- 하지 않음: `SWHeader`·`SWPopup`을 뷰어 화면에 섞기(예외: 위 `trailing` 끝의 `SWHeaderWindowControls` 최소화·닫기), 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`·`homeMenu`)를 뷰어 화면에 넣거나 비슷하게 만들기(홈 메뉴는 일반 모드 전용), 로고가 없다고 임의 문자·아이콘 넣기, 연결할 명령이 없는 메뉴를 가짜 동작으로 만들기(제외 또는 `disabled` + 미연결 보고)
```tsx
import { SWViewerHeader, SWViewerPanel, SWViewerSection, SWViewerField, SWViewerSwitch, type SWViewerHeaderGroup } from "@hm-design/sw"