# SW Header 사용 가이드
> **0.2.0 통합 개선(2026-10-19):** 로고 오른쪽 파일 열기(`documentOpenPlacement="header"`, `documentOpenLabel` 재사용, 실측 기반 More 이동), `leading` 슬롯, 하위 항목 숨김(`hiddenControls`의 `documentNew`·`documentOpen`·`layerFreeze`·`layerLock`), 도구 막대 `SWToolButton` / `SWToolGroup`, 속성창 프레임 `SWPopup`(자유 `children`)과 `SWHeaderPropertyPanel`의 `children`·`title`을 추가했습니다. API·예제는 패키지 [AI 카탈로그](../../packages/sw/ai/COMPONENT_CATALOG.md)와 [조합 패턴](../../packages/sw/ai/COMPOSITION_PATTERNS.md), Story는 `SW / Header`의 Document Open·Hidden Sub Controls, `SW / Tool`, `SW / Popup`, 검증은 [통합 개선 기록](./sw-integrated-improvement-2026-10-19.md)을 따릅니다.
> **현재 기준 색인(2026-10-19 1차 정리):** 아래 날짜별 안내 중 최신 항목이 같은 주제의 이전 설명보다 우선합니다. 속성 필드 13개·필드 Dropdown(`picker`·`menus`·`decimals`)은 [속성창·K 가이드 추가 연결](#속성창k-가이드-추가-연결-2026-10-16)의 2026-10-19 항목, 표시·숨김(`hiddenControls`)·메뉴 순서(`menuOrder`)·속성창 4상태·형상 Slot은 [3차 절](#구성요소-표시메뉴-순서속성창-4상태형상-slot-2026-10-16-3차), 속성Bar·`controls`·Colorize는 [후속 절](#후속-5개-요구사항-연결-2026-10-16)을 봅니다. 현재 Storybook 33개 목록은 [§12](#12-storybook)와 [1차 정리 보고](./sw-first-pass-cleanup-review-2026-10-19.md)에 있습니다.
> **상세 버튼 최신 기준(2026-10-16):** 레이어·색상·선·문자·치수 상세는 문자스타일 디자인의 `SWHeaderMenuDetail`을 공통 사용합니다. `on*Detail` 미전달 시 Hover 유지·클릭 무동작이며, Callback 전달 시 기존 1회 호출·닫힘·Focus 복귀를 유지합니다. 내부 공통 컴포넌트는 `label`, `icon`, `onClick`, `disabled`, `className`, `aria-label` 등 native 버튼 Props를 지원합니다. 새 공개 export는 추가하지 않았습니다. 비교 Story는 `SW/Header → Detail Buttons (Shared States)`(verification 태그, 직접 ID `sw-header--detail-buttons`)입니다. 과거의 Callback 미전달=Disabled 및 치수만 밝은 Hover 설명보다 이 기준이 우선합니다. [이번 검증 기록](./sw-header-detail-buttons-verification-2026-10-16.md).
> **3차 기준(2026-10-16):** 로고 외 개별 제거 `hiddenControls`, 유사 메뉴 순서 `menuOrder`, 속성창 4상태 Story·비교, 형상 Slot `shapeContent`를 추가했습니다. [3차 절](#구성요소-표시메뉴-순서속성창-4상태형상-slot-2026-10-16-3차)과 [3차 검증 기록](./sw-header-composition-shape-verification-2026-10-16.md)이 같은 주제의 이전 설명보다 우선합니다.
> **후속 5개 요구사항 기준(2026-10-16):** `SWHeaderPropertyBar`, 메뉴 단위 `controls`, `btn_colorize` Default/Hover를 추가했습니다. 상세 pill의 `solid-stroke-width`도 제거했습니다. 아래 후속 연결 절과 [새 검증 기록](./sw-header-followup-five-verification-2026-10-16.md)이 현재 기준입니다. 과거 실행 기록은 보존합니다.
> **최신 API/동작:** 아래 [속성창·K 가이드 추가 연결](#속성창k-가이드-추가-연결-2026-10-16)을 먼저 확인하세요. 색상 기준은 정확히 하나 표시되며, 상세 버튼 Hover와 목록 Selected 정책이 변경됐습니다. 과거 예시의 `colorMode=null`은 직접 색상 의미를 유지하지만 모드 표시까지 해제하지 않습니다.
`@hm-design/sw`의 SW Header를 화면에 연결하는 방법입니다. 디자인 값과 판정은 [디자인 명세](./sw-header-design-spec.md)를 따릅니다.
> `@hm-design/sw` 패키지는 npm Registry로 배포하지 않습니다. 다른 프로젝트는 [Git Submodule + npm Workspace](../guidelines/external-install-submodule.md)로 연결하며, 로컬 tarball은 검증용 대안입니다.
## 1. 패키지 구조
```text
packages/sw/
├── src/
│ ├── assets/ Figma 원본 SVG와 Hover Variant (build 시 data URI로 포함, 로고 제외)
│ ├── components/
│ │ ├── sw-header/
│ │ │ ├── SWHeader.tsx 전체 헤더
│ │ │ ├── components/ 하위 컨트롤 (공개 + 내부)
│ │ │ ├── types.ts Props / 값 타입
│ │ │ └── index.ts
│ │ └── sw-theme/SWTheme.tsx 하위 컨트롤 단독 사용 시 토큰 범위
│ ├── theme/sw.css SW 전용 토큰·스타일
│ └── index.ts
├── build.mjs 독립 library build (Vite lib + tsc 선언)
├── package.json exports: "." , "./styles.css"
└── README.md
```
`@hm-design/ui`·`@hm-design/center-graphic`·OrgChart 패키지를 import하지 않습니다. Peer는 `react`·`react-dom` `^19.0.0`뿐입니다.
## 2. 설치
### HMDesign Workspace 안
루트 `package.json`의 Workspace(`packages/*`)에 포함되어 있습니다.
```bash
npm install
npm run build:sw
```
`build:packages`(typecheck·build·Storybook 전처리)도 마지막에 `build:sw`를 실행합니다.
### 다른 React 프로젝트(Git Submodule + npm Workspace, 기본)
HMDesign을 Submodule로 고정하고 HMDesign 안에서 `npm ci`·`npm run build:packages`를 실행한 뒤, 소비 프로젝트 `workspaces`에 `HMDesign/packages/sw`를, dependency에 `"@hm-design/sw": "*"`를 추가하고 `npm install`합니다. 상세는 [외부 프로젝트 설치](../guidelines/external-install-submodule.md)를 따릅니다.
### 다른 React 프로젝트(로컬 tarball, 검증용)
```bash
npm pack -w @hm-design/sw --pack-destination ../packs
```
```bash
npm install ../packs/hm-design-sw-0.2.0.tgz
```
`hm-design-sw-0.2.0.tgz`는 배포 파일이 아니라 위 `npm pack`이 로컬에서 만드는 파일 이름의 예시입니다. 실제 이름은 `packages/sw/package.json`의 `version`을 따르며 `npm pack` 출력에서 확인합니다.
소비 프로젝트는 `src` 내부 파일을 참조하지 않고 `dist` 공개 API만 사용합니다.
## 3. Import
```tsx
import { SWHeader } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
```
- CSS는 JS에서 자동 import되지 않으므로 앱 진입점 등에서 한 번 import합니다.
- TypeScript 7에서 CSS side-effect import 타입 오류가 나면 Vite 프로젝트의 `/// `(또는 `declare module "*.css"`)를 둡니다.
- 글꼴: 패키지는 폰트 파일을 포함하지 않습니다. 앱에서 Noto Sans KR(400/700)을 로드합니다.
## 4. 공개 API
| Export | 용도 |
|---|---|
| `SWHeader` | 전체 헤더. `ref`는 `` |
| `SWHeaderHome` | 홈 버튼(로고 영역). `logoSrc` / `logo` / `logoAlt` / `onClick` |
| `SWHeaderDocumentTitle` | 파일명 버튼. `documents`를 주면 문서 Dropdown(`title`, `onClick`, `documents`, `menuOpen`, `onMenuOpenChange`, `onDocumentSelect`, `onNew`, `onOpen`, `documentIcon`, `newLabel`, `openLabel`) |
| `SWHeaderLayerControl`, `SWHeaderColorControl`, `SWHeaderLineStyle`, `SWHeaderTextStyle`, `SWHeaderDimensionStyle` | 속성 필드 5종. `SWHeaderLayerControl`은 선택 `state`(현재 레이어의 표시·동결·잠금)로 필드 아이콘을 회색으로 표시 |
| `SWHeaderLayerMenu` | 레이어 필드 + 레이어 Dropdown(`value`, `layers`, `activeLayerId`, `open`, `onOpenChange`, `onClick`, `onLayerSelect`, `onVisibilityChange`, `onFreezeChange`, `onLockChange`, `onDetail`). `SWHeader`는 `layers`가 있을 때 이것을 사용 |
| `SWHeaderColorMenu` | 색상 필드 + 색상 Dropdown(2026-10-16, `value`, `colors`, `userColors`, `mode`, `open`, `onOpenChange`, `onClick`, `onColorSelect`, `onModeChange`, `onColorInput`, `onPick`, `onDetail`). `SWHeader`는 `colors`가 있을 때 사용 |
| `SWHeaderLineStyleMenu` | 선스타일 필드 + 3열 Dropdown(`value`, `lineTypes`, `lineWeights`, `activeLineTypeId`, `activeLineWeightId`, `onLineTypeSelect`, `onLineWeightSelect`, `onLineWeightValueChange`, `onScaleChange`, `onDetail`, 열림 Props). `SWHeader`는 `lineTypes` 또는 `lineWeights`가 있을 때 사용 |
| `SWHeaderTextStyleMenu`, `SWHeaderDimensionStyleMenu` | 문자·치수스타일 필드 + 목록 Dropdown(`value`, `textStyles` / `dimensionStyles`, `active*Id`, `on*Select`, `onDetail`, 열림 Props) |
| `SWHeaderLinePreview`, `swHeaderLinePatterns` | 70 × 2 선 미리보기와 Figma CENTER(22·7·22·7) / HIDDEN(33·33) 패턴 |
| `SWHeaderInput` | 수정 가능한 SW Input(Figma input 26:11656 · cursor 26:11664). 레이어 상세 등 상세 영역의 값 편집용 |
| `parseSWHeaderColor`, `parseSWHeaderNumber` | Input 유효성 검사 도우미(`r,g,b` / `#rrggbb`, 소수) |
| `SWHeaderProperties` | 상세보기(속성Bar / 속성창) |
| `SWHeaderActions` | 인쇄 · 팝업 가이드 |
| `SWHeaderWindowControls` | 창 이동 · 최소화 · 종료 |
| `SWTheme` | 하위 컨트롤을 헤더 밖에서 쓸 때 토큰·글꼴·버튼 초기화 범위 |
| 타입 | `SWHeaderProps`, `SWHeaderMenuId`, `SWHeaderLayerValue`, `SWHeaderLayer`, `SWHeaderColorValue`, `SWHeaderLineStyleValue`, `SWHeaderTextStyleValue`, `SWHeaderDimensionStyleValue`, `SWHeaderDocument`, `SWHeaderColorMode`, `SWHeaderColorOption`, `SWHeaderColorInputValue`, `SWHeaderLineType`, `SWHeaderLineWeight`, `SWHeaderTextStyleOption`, `SWHeaderDimensionStyleOption`, `SWHeaderInputValueType`, 각 하위 컴포넌트 `*Props`, `SWThemeProps` |
내부 구현(`SWHeaderField`, `SWHeaderMore`, `useSWHeaderDropdown`, `SWHeaderMenuDetail`)은 공개하지 않습니다.
## 5. SWHeader Props
`HTMLAttributes`(단 `title`, `children`, `color` 제외)를 함께 받습니다. `className`·`style`·`aria-*`는 ``에 전달됩니다.
| Prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| `documentTitle` | `string` | 필수 | 파일명. 좁으면 말줄임, 전체 이름은 `title` 툴팁 |
| `drawingModeLabel` | `string` | `"그리기모드"` | 그리기 모드 라벨 |
| `layer` | `{ name }` | `{ name: "0" }` | 현재 레이어(`layers`에 `activeLayerId`와 같은 항목이 없을 때 필드에 표시) |
| `layers` | `SWHeaderLayer[]` | — | 레이어 필드 아래 Dropdown 목록(배열 순서 그대로). 없으면 Dropdown 없이 기존 필드 버튼, `[]`이면 하단(레이어 상세)만 |
| `activeLayerId` | `string` | — | 현재 레이어. 필드에 이름·상태 아이콘 표시, 목록에서 이름만 `#FFC600` |
| `layerMenuOpen` | `boolean` | — | 레이어 Dropdown 열림 상태(제어형). 없으면 내부에서 관리 |
| `color` | `{ value, swatch? }` | `{ value: "ByLayer", swatch: "#ffffff" }` | 색상 값 표시와 칩 색 |
| `lineStyle` | `{ name, weight, scale }` | `ByLayer / ByLayer / 1.00` | 선스타일 |
| `textStyle` | `{ font, name }` | `Arial / Standard` | 문자스타일 |
| `dimensionStyle` | `{ name }` | `Standard` | 치수스타일 |
| `activeMenu` | `"layer" \| "color" \| "lineStyle" \| "textStyle" \| "dimensionStyle" \| null` | `null` | 선택(열림) 표시 |
| `propertyBarOpen` | `boolean` | `false` | 속성Bar 눌림 |
| `propertyPanelOpen` | `boolean` | `false` | 속성창 눌림 |
| `disabled` | `boolean` | `false` | 메뉴·속성·인쇄/가이드 비활성(창 제어 제외) |
| `tools` | `ReactNode` | — | 객체 수정 영역의 필드 뒤 추가 도구 |
| `logoSrc` | `string` | — | 로고 이미지 URL. 32 × 17 로고 영역에 `contain`으로 맞춤 |
| `logo` | `ReactNode` | — | 로고 요소(인라인 SVG 등). `logoSrc`보다 우선 |
| `logoAlt` | `string` | — | 제품명. 홈 버튼 이름이 " 홈"(없으면 "홈") |
| `documents` | `SWHeaderDocument[]` | — | 파일명 아래 문서 Dropdown 목록. 없으면 Dropdown 없이 기존 버튼, `[]`이면 New / Open만 |
| `documentMenuOpen` | `boolean` | — | 문서 Dropdown 열림 상태(제어형). 없으면 내부에서 관리 |
| `activeDocumentId` | `string` | — | 현재 문서. 목록 순서는 그대로 두고 이 문서의 이름만 `#FFC600` |
| `documentIcon` | `ReactNode` | Figma 문서 아이콘 | `icon`이 없는 문서의 아이콘(29 × 12 영역) |
| `documentNewLabel` / `documentOpenLabel` | `string` | `+ New` / `Open` | 하단 버튼 글자 |
| `colors` | `SWHeaderColorOption[]` | — | 색상 Dropdown 기본 팔레트(2026-10-16). 없으면 Dropdown 없이 기존 필드 버튼 |
| `userColors` | `SWHeaderColorOption[]` | — | 사용자 색상(개수 가변, 2px 간격 줄바꿈, 4행 초과 시 팔레트 영역 스크롤) |
| `colorMode` | `"byLayer" \| "byBlock" \| null` | — | 눌린 ByLayer / ByBlock. `null`·미지정 = 명시 색 |
| `colorMenuOpen` | `boolean` | — | 색상 Dropdown 열림(제어형). 없으면 내부 관리 |
| `lineTypes` / `lineWeights` | `SWHeaderLineType[]` / `SWHeaderLineWeight[]` | — | 선스타일 Dropdown의 선 종류 / 가중치 열. 둘 다 없으면 기존 필드 버튼. 스케일 열은 `lineStyle.scale` |
| `activeLineTypeId` / `activeLineWeightId` | `string` | `lineStyle.name` / `.weight`와 같은 이름의 행 | 현재 행(이름 `#FFC600`) |
| `lineStyleMenuOpen` | `boolean` | — | 선스타일 Dropdown 열림(제어형) |
| `textStyles` / `dimensionStyles` | `SWHeaderTextStyleOption[]` / `SWHeaderDimensionStyleOption[]` | — | 문자 / 치수스타일 Dropdown 행. 없으면 기존 필드 버튼 |
| `activeTextStyleId` / `activeDimensionStyleId` | `string` | `textStyle.name` / `dimensionStyle.name`과 같은 이름의 행 | 현재 행 |
| `textStyleMenuOpen` / `dimensionStyleMenuOpen` | `boolean` | — | 각 Dropdown 열림(제어형) |
Dropdown 데이터 타입(모두 `id`로 식별, 배열 순서 = 표시 순서, 패키지에 고정 데이터 없음):
| 타입 | 필드 |
|---|---|
| `SWHeaderColorOption` | `{ id; color; value?; label?; disabled? }` — `color` CSS 색, `value` 보고·표시 값(기본 `color`의 `r,g,b`), `label` 접근성 이름 |
| `SWHeaderColorInputValue` | `{ value: "r,g,b"; rgb: [r, g, b]; hex: "#rrggbb" }` |
| `SWHeaderLineType` | `{ id; name; pattern?: number[]; disabled? }` — `pattern` 70px 미리보기의 대시 길이(4px 간격, 비율 맞춤), 없으면 실선 |
| `SWHeaderLineWeight` | `{ id; name; value?; editable?; disabled? }` — `value` 오른쪽 값(ByDefault 0.25), `editable` 이면 값 Input(User) |
| `SWHeaderTextStyleOption` | `{ id; name; font; disabled? }` |
| `SWHeaderDimensionStyleOption` | `{ id; name; disabled? }` |
`SWHeaderDocument`: `{ id: string; name: string; title?: string; icon?: ReactNode; disabled?: boolean }` — `title`은 툴팁(기본 `name`).
`SWHeaderLayer`: `{ id: string; name: string; color?: string; visible?: boolean; frozen?: boolean; locked?: boolean; disabled?: boolean }` — `color`는 16px 견본 색(기본 `#ffffff`), 기본값은 `visible: true`, `frozen: false`, `locked: false`. 확정 매핑([명세 §5.2.1](./sw-header-design-spec.md#521-아이콘-onoff와-데이터-의미)):
| 아이콘 | 컬러 | 회색 | 클릭 시 Callback 값 |
|---|---|---|---|
| 전구 | `visible=true` | `visible=false` | `onLayerVisibilityChange(layer, !visible)` |
| 눈꽃 | `frozen=false`(동결 해제) | `frozen=true`(동결) | `onLayerFreezeChange(layer, !frozen)` |
| 자물쇠 | `locked=false`(잠금 해제) | `locked=true`(잠금) | `onLayerLockChange(layer, !locked)` |
헤더는 값을 직접 바꾸지 않으므로 호스트가 받은 값으로 `layers`를 갱신해야 아이콘 색이 바뀝니다.
> **로고는 확정 디자인이 아닙니다.** 로고 영역의 위치·크기는 공통 디자인이고, 로고 이미지는 사용하는 SW 프로그램이 정합니다. 패키지에는 로고가 들어 있지 않으며 `logo`·`logoSrc`를 전달하지 않으면 로고 없이 표시됩니다(배치는 그대로). Storybook의 기본 로고는 임시 자산(`sw-header-logo-placeholder.svg`)입니다.
기본값은 Figma 표시값이 아니라 중립값입니다. Figma 샘플 값은 Storybook `figmaArgs`를 참고합니다.
## 6. Callback
| Callback | 호출 시점 |
|---|---|
| `onHomeClick` | 홈(로고) 클릭 |
| `onDocumentClick` | 파일명 클릭(Dropdown 열고 닫을 때도 호출) |
| `onDocumentMenuOpenChange(open)` | 문서 Dropdown 열림·닫힘 |
| `onDocumentSelect(document)` | 문서 항목 선택. 문서를 여는 동작은 호스트가 구현 |
| `onDocumentClose(document)` | 행의 X(문서 닫기). 주면 모든 행에 X가 생기고 Hover·키보드 Focus한 행에만 보입니다. 선택 Callback과 중복되지 않습니다. 실제 닫기·목록 갱신·다음 선택은 호스트가 처리 |
| `onDocumentNew`, `onDocumentOpen` | Dropdown 하단 `+ New` / `Open` |
| `onLayerOpen`, `onColorOpen`, `onLineStyleOpen`, `onTextStyleOpen`, `onDimensionStyleOpen` | 각 필드 클릭(Dropdown이 있으면 열고 닫을 때도 호출) 또는 더보기 항목 선택 1회(대표 필드 전환, §7 "반응형 More") |
| `onLayerMenuOpenChange(open)` | 레이어 Dropdown 열림·닫힘 |
| `onLayerSelect(layer)` | 레이어 행(견본·이름) 선택. 목록이 닫힙니다. 현재 레이어 변경은 호스트가 `activeLayerId`로 반영 |
| `onLayerVisibilityChange(layer, nextVisible)` | 전구 클릭. 목록 유지, 선택 Callback 없음 |
| `onLayerFreezeChange(layer, nextFrozen)` | 눈꽃 클릭. 목록 유지, 선택 Callback 없음 |
| `onLayerLockChange(layer, nextLocked)` | 자물쇠 클릭. 목록 유지, 선택 Callback 없음 |
| `onLayerDetail()` | 하단 `레이어 상세`. 호출 후 목록이 닫힙니다. 없으면 버튼은 Figma 외형 그대로 비활성 |
| `onColorMenuOpenChange(open)` 등 `on*MenuOpenChange` | 각 Dropdown 열림·닫힘(색상·선/문자/치수스타일) |
| `onColorSelect(option)` | 팔레트·사용자 색상 칩. 목록 유지 |
| `onColorModeChange(mode)` | ByLayer / ByBlock. 목록 유지. 상호 배타 표시는 호스트가 `colorMode`로 반영 |
| `onColorInput(value)` | RGB 입력 확정(Enter·Focus 이동·목록 닫힘). 유효한 `r,g,b`(0–255) 또는 `#rrggbb`만, 값이 바뀐 경우만 |
| `onColorPick()` | 색상 추출 버튼. 목록이 닫히고 실제 추출(도면 픽셀 등)은 호스트가 구현 |
| `onLineTypeSelect(type)` / `onLineWeightSelect(weight)` | 선 종류 / 가중치 행. 다른 열 값은 건드리지 않고 목록 유지 |
| `onLineWeightValueChange(weight, value)` | `editable` 가중치 Input 확정(0 이상 소수) |
| `onLineScaleChange(value)` | 스케일 Input 확정(0 초과 소수) |
| `onTextStyleSelect(style)` / `onDimensionStyleSelect(style)` | 행 선택 후 목록 닫힘 |
| `onColorDetail`, `onLineStyleDetail`, `onTextStyleDetail`, `onDimensionStyleDetail` | 하단 `… 상세`. 전달 시 호출 후 목록 닫힘. 없으면 공통 Default/Hover를 유지하고 클릭은 무동작. 상세 팝업은 패키지 범위 밖(추후 Figma 전달 후) |
| `onPropertyBarToggle`, `onPropertyPanelToggle` | 속성 토글 클릭 또는 더보기 항목 선택 |
| `onPrint`, `onPopupGuide` | 인쇄·팝업 가이드 |
| `onWindowMoveStart(event)` | 창 이동 영역 pointer down |
| `onMinimize`, `onClose` | 최소화·종료 |
헤더는 선택·열림 상태를 스스로 저장하지 않습니다. 호스트가 Callback에서 상태를 바꾸고 Props로 다시 내려줍니다. 실제 드롭다운 목록, 파일 열기, 창 이동·최소화·종료(브라우저 / Electron / 설치형 SW마다 다름)는 호스트가 구현합니다.
## 7. 전체 헤더 예제
```tsx
import { useState } from "react"
import { SWHeader, type SWHeaderMenuId } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
export function DrawingHeader({ fileName }: { fileName: string }) {
const [activeMenu, setActiveMenu] = useState(null)
const [panelOpen, setPanelOpen] = useState(false)
const toggle = (id: SWHeaderMenuId) => () => setActiveMenu(current => (current === id ? null : id))
return (
setPanelOpen(open => !open)}
onClose={() => window.close()}
/>
)
}
```
### 로고 지정 예
```tsx
import productLogo from "./assets/product-logo.svg" // 각 프로그램의 로고 파일
// React 요소로 전달(인라인 SVG 등). logoSrc보다 우선합니다.
} logoAlt="제품명" />
```
- 로고 영역은 32 × 17px입니다. 가로·세로 비율이 달라도 영역 안에 맞춰지고 잘리지 않습니다. 선명하게 보이도록 SVG 또는 64 × 34px 이상 이미지를 권장합니다.
- 로고 영역 크기는 디자인 기준이므로 바꾸지 않는 것을 권장합니다(토큰 `--hm-sw-header-logo-width` / `-logo-height`).
### 문서 Dropdown 예
```tsx
const [openDocuments, setOpenDocuments] = useState([])
activate(document.id)}
onDocumentClose={document => closeDocument(document.id)} // 목록·현재 문서는 호스트가 갱신
onDocumentNew={createDocument}
onDocumentOpen={showOpenDialog} // 파일 열기는 호스트가 구현
/>
```
- 클릭으로 열고 닫습니다(Hover로 열리지 않음). 바깥 클릭·Escape·항목 선택·New/Open 후 닫힙니다.
- Dropdown은 헤더 아래로 겹쳐 열립니다. SWHeader의 조상 요소에 `overflow: hidden`이 있으면 잘리므로, 헤더는 페이지 최상단처럼 잘리지 않는 위치에 둡니다.
### 레이어 Dropdown 예
```tsx
const [layers, setLayers] = useState(() => drawing.layers.map(toHeaderLayer))
const patch = (id: string, change: Partial) =>
setLayers(list => list.map(layer => (layer.id === id ? { ...layer, ...change } : layer)))
setCurrentLayer(layer.id)}
onLayerVisibilityChange={(layer, visible) => { viewer.setLayerVisible(layer.id, visible); patch(layer.id, { visible }) }}
onLayerFreezeChange={(layer, frozen) => { viewer.setLayerFrozen(layer.id, frozen); patch(layer.id, { frozen }) }}
onLayerLockChange={(layer, locked) => { viewer.setLayerLocked(layer.id, locked); patch(layer.id, { locked }) }}
onLayerDetail={openLayerManager} // 없으면 버튼 비활성(임의 팝업 없음)
/>
```
- 레이어 필드를 클릭해야 열립니다(Hover는 테두리·글자색만). 행 선택·Escape·바깥 클릭·Tab 이탈·레이어 상세 후 닫힙니다. 전구·눈꽃·자물쇠 클릭은 목록을 유지합니다.
- 헤더는 레이어 상태를 저장하지 않습니다. Callback에서 도면(`viewer.*`은 예시)과 `layers`를 함께 갱신해야 아이콘이 바뀝니다. 실제 표시·동결·잠금 처리와 편집 가능 여부 판단은 호스트 책임입니다.
- 키보드: 필드에서 ↓로 열면 현재 레이어에 Focus, ↓↑ 행 이동(같은 열), ←→ 행 안 이동, Enter/Space 실행. 상세 규격은 [디자인 명세 §5.2](./sw-header-design-spec.md#52-레이어-dropdown-figma-headermenu레이어-2610940-2026-10-16).
### 색상 · 선/문자/치수스타일 Dropdown 예 (2026-10-16)
```tsx
const [color, setColor] = useState({ value: "ByLayer", swatch: "#ffffff" })
const [colorMode, setColorMode] = useState("byLayer")
const [lineStyle, setLineStyle] = useState({ name: "Continuous", weight: "0.25", scale: "1.00" })
{ viewer.setColor(option.color); setColor({ value: option.value ?? option.color, swatch: option.color }); setColorMode(null) }}
onColorModeChange={mode => { viewer.setColorMode(mode); setColorMode(mode) }}
onColorInput={({ value, hex }) => { viewer.setColor(hex); setColor({ value, swatch: hex }); setColorMode(null) }}
onColorPick={startPickFromDrawing} // 실제 픽셀 추출은 호스트
onColorDetail={openColorDetail} // 없으면 Hover 유지·클릭 무동작(상세 팝업 미제공)
lineStyle={lineStyle}
lineTypes={program.lineTypes} // [{ id: "center", name: "CENTER", pattern: swHeaderLinePatterns.center }, ...]
lineWeights={program.lineWeights} // [{ id: "user", name: "User", value: "0.25", editable: true }, ...]
onLineTypeSelect={type => setLineStyle(s => ({ ...s, name: type.name }))} // 가중치·스케일 유지
onLineWeightSelect={weight => setLineStyle(s => ({ ...s, weight: weight.value ?? weight.name }))}
onLineWeightValueChange={(weight, value) => updateWeight(weight.id, value)}
onLineScaleChange={scale => setLineStyle(s => ({ ...s, scale }))}
textStyle={textStyle}
textStyles={program.textStyles} // [{ id, font: "Arial", name: "Standard" }, ...]
onTextStyleSelect={style => setTextStyle({ font: style.font, name: style.name })}
dimensionStyle={dimensionStyle}
dimensionStyles={program.dimensionStyles} // [{ id, name: "ISO - 25" }, ...]
onDimensionStyleSelect={style => setDimensionStyle({ name: style.name })}
/>
```
- 클릭으로 열고 닫습니다(Hover는 노란 테두리·글자만). 다른 Dropdown을 열면 앞의 것이 닫히고, 바깥 클릭·Escape·Tab 이탈로 닫힙니다. 헤더 높이(49px)와 다른 필드 위치는 변하지 않습니다.
- 색상·선스타일은 여러 속성을 이어서 바꿀 수 있도록 선택 후에도 열려 있고, 문자·치수스타일은 행 선택 후 닫힙니다. 색상 추출과 `… 상세`는 호출 후 닫힙니다.
- 헤더는 값을 저장하지 않습니다. Callback 값으로 `color` / `colorMode` / `lineStyle` 등을 갱신해야 필드가 바뀝니다. 명시 색을 고를 때 ByLayer/ByBlock을 해제할지는 호스트 정책입니다(Storybook 예제는 해제).
- 키보드: 필드에서 ↓로 열면 현재(눌린) 항목 또는 첫 항목에 Focus, ↓↑로 Dropdown 안 버튼·Input 순서대로 이동, Enter/Space 실행, Escape 닫기(필드로 Focus 복귀).
- Hover: 상세 버튼 5개는 문자스타일 상세 기준 공통 Hover(배경 .25·radius 25·아이콘 35%·글자 35%)를 적용합니다. ByLayer/ByBlock은 기존 후속 Hover 기준을 따릅니다. 색상 칩·색상 추출·목록 행(선 종류·가중치·스케일·문자·치수)·Input의 현재 동작은 유지합니다.
- 좁은 헤더: 목록은 헤더 안으로 옮겨지며, 468px 선스타일 목록은 헤더 폭 480 미만에서 세로로 쌓입니다.
### 동적 데이터 구조
모든 Dropdown(문서·레이어·색상·선 종류·가중치·문자·치수스타일)은 Props 배열로만 그립니다.
- 항목 추가·삭제·이름/값 변경·순서 변경은 배열을 바꾸면 되고 컴포넌트 소스 수정이 필요 없습니다. 각 항목은 `id`로 식별되어 바뀐 항목만 다시 그리고 다른 행의 상태(입력 중 값 등)를 보존합니다.
- 현재 항목(`active*Id`)이 배열에서 사라지면 선택 표시가 없어지고 필드는 값 Props(`layer`, `lineStyle` 등)를 그대로 표시합니다. 다음 현재 항목은 호스트가 정합니다.
- `disabled`(선택 불가), `editable`(가중치 값 Input), Input의 `readOnly` / `disabled`를 항목별로 줄 수 있습니다.
- 목록이 길면 Dropdown 안에서만 스크롤합니다(행 목록 최대 240px = 8행, 사용자 색상은 팔레트 118px). 헤더 레이아웃은 유지됩니다.
- Storybook `SW / Header / Dynamic Data`에서 추가·삭제·이름·값·순서 변경을 직접 확인할 수 있습니다.
### 수정 가능한 Input — `SWHeaderInput`
레이어 상세 등 상세 영역에서 속성값을 직접 고칠 때 씁니다(Figma input 26:11656, cursor 26:11664). 상세 팝업 레이아웃은 아직 없으므로 Input과 계약만 제공합니다.
```tsx
import { SWHeaderInput, parseSWHeaderNumber, SWTheme } from "@hm-design/sw"
preview(draft)} // 편집 중 임시값(선택)
onCommit={next => updateEntity({ x: next })} // Enter · Focus 이동 · 감싼 Dropdown 닫힘, 유효 + 변경 시만
onCancel={() => clearPreview()} // Escape: 확정값으로 복원
parse={draft => parseSWHeaderNumber(draft, { min: 0 })} // 선택: 형식·범위 직접 지정
/>
```
- 상태: Default(배경·테두리 없음, 58 × 20 기준) / Hover(1px `rgba(212,203,189,.25)`, 24px) / 편집 중(`#0F0D0A`, 1px `.3`, 24px). 글자 `#E60403` 14px + 검정 Text Stroke, 글자 위치는 상태와 무관하게 고정. 폭은 값 길이에 맞춥니다.
- 커서: 네이티브 caret을 `#E60403`(`caret-color`)으로 표시하고 깜박임도 네이티브입니다(가짜 커서 없음). 선택·IME(한글 조합 중 Enter는 확정하지 않음)는 브라우저 기본 동작입니다.
- `onCommit`이 없거나 `readOnly`이면 읽기 전용(Hover·편집 표시 없음), `disabled`는 40%.
- 선택형(목록) 입력은 Figma 디자인 전달 후 같은 데이터 정의(`id`·이름·값·타입)로 확장합니다. 현재는 텍스트·숫자와 사용자 `parse`(임의 형식)를 지원합니다.
### 반응형 More — 대표 필드 전환
> **2026-10-21 (사용자 결정):** 헤더 폭 760px 이상에서는 More를 쓰지 않습니다. 5개 메뉴는 로고 옆 Open 유무와 관계없이 항상 Header에 남고, 좁아질수록 간격 축소 → 파일명 `…` → 정보값 `…` 순으로 줄어듭니다. 메뉴 타이틀은 모든 폭에서 표시하며 공간이 부족하면 `…`로 줄어듭니다. 아래 내용(대표 필드 전환·실측 More)은 760px 미만에만 적용됩니다. 상세는 [DESIGN_RULES](../../packages/sw/ai/DESIGN_RULES.md)의 반응형 항목.
헤더가 좁아 필드가 더보기(…)로 옮겨졌을 때, 더보기에서 필드 항목을 고르면 그 필드가 객체 수정 영역 첫 칸(레이어 필드 자리)에 표시되고 Dropdown이 바로 아래에 열립니다. Focus는 그 필드로 옮겨갑니다.
- 레이어는 그동안 더보기로 이동하며, 더보기 → 레이어로 원래 배치로 돌아갑니다. 이미 화면에 있는 필드는 더보기에 나오지 않습니다.
- 전환은 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. 하위 컴포넌트 재사용
헤더 밖에서는 `SWTheme`로 감쌉니다. 소비자가 `hm-sw-*` class를 붙이지 않습니다.
```tsx
import { SWTheme, SWHeaderLayerControl, SWHeaderColorControl, SWHeaderProperties } from "@hm-design/sw"
```
`SWTheme`는 배치(flex 등)를 정하지 않습니다. 배치는 `className`·`style`로 소비 화면에서 정합니다.
## 9. 테마 커스터마이징
`--hm-sw-header-*` 토큰만 바꿉니다. 사용자 확정 3개 면(파일명·객체 수정·속성 입력)은 기본값을 유지하는 것을 권장합니다.
```tsx
import type { CSSProperties } from "react"
```
토큰 목록과 사용처는 [디자인 명세 §14](./sw-header-design-spec.md#14-sw-전용-토큰-목록)를 따릅니다. `@hm-design/ui` Theme Runtime(JSON)과는 연결되어 있지 않습니다.
## 10. DWG/DXF 뷰어 연동 시 고려사항
- 기존 뷰어 `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-반응형)를 따르고, 그 위에서 실제 남은 공간을 측정해 부족한 메뉴만 더보기로 옮깁니다([§12.1](./sw-header-design-spec.md#121-실측-more--실제-사용-가능한-공간-기준-2026-10-19)).
- 가용 공간 분배(2026-10-23): 헤더 폭 760~1920px에서는 레이어~치수스타일 5개 그룹이 속성보기 앞의 남는 공간을 같은 양씩 나눠 속성보기 바로 옆까지 채우고, 1920px을 넘으면 1920px 폭(234 / 192 / 284 / 164 / 164px)에 고정되어 남는 공간은 속성보기 앞 여백이 됩니다. 숨김·More 단계와 창 영역 위치는 그대로이며 `controls` 구성 Header에는 적용하지 않습니다([AI 디자인 규칙](../../packages/sw/ai/DESIGN_RULES.md)).
- 뷰어는 현재 `@hm-design/ui`의 SWHeader 패턴을 사용합니다. 이 패키지로 바꾸는 작업은 이번 범위가 아닙니다.
## 10.1 상호작용 상태
- 그룹 필드 Hover(현재 추가 기준): 레이어·색상·선/문자/치수스타일은 버튼 전체 Hover 시 라벨·값과 지정된 선/폰트/치수 미리보기가 `#FFC600`이 됩니다. 기존 Gradient Hover Border와 검정 외곽선은 유지하며 색상 견본·레이어 상태 아이콘은 바꾸지 않습니다. Disabled는 새 Hover 효과를 적용하지 않고 Selected 색은 기존대로입니다. 상세보기의 텍스트 전용 Hover는 변경하지 않습니다. [디자인 명세 §11.5](./sw-header-design-spec.md#115-현재-추가-기준--1400ms-및-그룹-필드-hover-2026-10-16).
- Hover Border: 테두리가 있는 버튼(홈, 5개 속성 필드, 더보기, 인쇄, 팝업 가이드)은 마우스를 올리면 테두리가 Figma 홈 버튼의 노란 gradient로 바뀝니다. 5개 필드 내부 텍스트·미리보기는 위 추가 기준을 함께 적용합니다. 파일명과 속성Bar·속성창은 예외로, 테두리 없이 글자만 `#FFC600`으로 바뀝니다(파일명 Dropdown은 클릭해야 열림). 문서 Dropdown 안은 Figma hover를 따릅니다: 목록 항목은 검정 40% 배경, `+ New` / `Open`은 배경이 진해지고(20% → 30%) 글자가 검정 100%가 됩니다(노란 테두리 없음). 배경·크기·위치는 변하지 않습니다. 비활성 버튼은 바뀌지 않습니다.
- Hover(창 제어 · 바로가기 아이콘): 인쇄·팝업 가이드는 노란 테두리와 함께 아이콘의 `#B6D0C9` 부분만 `#FFC600`으로 바뀝니다(외곽선·그림자·타일 색은 그대로). 최소화는 검정 20% 배경 + 흰 막대, 종료는 `#E40000` 배경 + 흰 X로 바뀝니다(Figma 26:12156 / 26:12161, 32 × 48 유지, 노란 테두리 없음). 소비자는 class나 Prop을 추가하지 않습니다. 상세는 [디자인 명세 §11.2](./sw-header-design-spec.md#112-창-제어--바로가기-아이콘-hover-2026-10-16).
- Selected(`activeMenu`, `propertyBarOpen`, `propertyPanelOpen`): 글자만 `#FFC600`. Hover와 함께 표시됩니다.
- Focus: 키보드 Focus 윤곽선은 Hover 테두리 위에 표시됩니다.
- Hover 테두리 색은 `--hm-sw-header-hover-border-gradient` 토큰, 최소화 Hover 배경·막대 색은 `--hm-sw-header-window-button-hover-background` / `--hm-sw-header-window-icon-hover-color` 토큰으로 바꿀 수 있습니다. 종료·인쇄·팝업 가이드 Hover 아이콘은 패키지 SVG Asset입니다. 대상·제외 목록은 [디자인 명세 §11.1](./sw-header-design-spec.md#111-hover-테두리-공통-상호작용-규칙)을 따릅니다.
- Text Stroke(2026-10-16): Figma에 Stroke가 있는 헤더·Dropdown 텍스트는 검정 외곽선(14·16px 1.3px, 12px 라벨 1px)을 표시합니다(자기 색 0.3px Stroke는 2026-10-16 추가 수정으로 제거). Hover·Selected는 채움색만 바뀌고 외곽선은 유지됩니다. 더보기 메뉴·창 이동 Tooltip은 Figma에 Stroke가 없어 적용하지 않고, `+ New` / `Open`은 요청에 따라 기존 그대로입니다. 외곽선 색은 `--hm-sw-header-text-outline-color`, 외곽선 전체는 `--hm-sw-header-text-outline` / `--hm-sw-header-label-outline`으로 바꿀 수 있습니다(`--hm-sw-header-text-stroke-width`는 제거됨)([명세 §6.1](./sw-header-design-spec.md#61-text-stroke-2026-10-16)).
## 10.2 창 이동 Hover Motion
별도 Prop이나 의존성 없이 Hover하면 GIF의 경로·구간 비율을 유지하면서 후속 사용자 요청에 따라 **1.4초마다 반복**합니다(원본 GIF 1.64초와 다름). 커서가 아래에서 위로 먼저 올라가고 약 444ms에 문구가 나타난 뒤 창·커서·문구가 오른쪽 위로 이동합니다. 약 888ms에 이동을 마치고 약 512ms 유지한 뒤 반복합니다. 28px / −4.5px 이동과 가속·감속 비율은 그대로이며 이탈 시 즉시 기본 SVG로 돌아갑니다. 크기·줄무늬·SVG·Tooltip 규격은 SW/Figma를 유지합니다.
`onWindowMoveStart`는 기존 `PointerEventHandler`입니다. 예를 들어 호스트는 `event.currentTarget.setPointerCapture(event.pointerId)`로 이후 포인터를 추적할 수 있습니다. 패키지는 포인터를 캡처하거나 실제 창을 이동시키지 않습니다. 아이콘과 `창 이동` Tooltip은 `pointer-events: none`이며 Callback 이벤트와 최소화·종료 클릭을 가로채지 않습니다. 누르는 동안 CSS `:active`로 모션을 멈추고 Tooltip을 숨깁니다. Tooltip은 창 이동 영역 밖의 최소화 및 종료 왼쪽 일부에 겹칠 수 있습니다.
- 헤더 폭 ≥1080px: 88px 영역, GIF 기준 반복 모션과 Tooltip.
- 640–1079px: 24px 드래그 영역, 그림은 숨기고 정적 Tooltip만 표시.
- <640px: 기존대로 창 이동 영역 숨김.
- Reduced Motion: 기본 아이콘과 정적 Hover Tooltip을 유지하고 애니메이션을 끕니다. 24px 단계에서는 기존대로 그림이 숨겨집니다. OS 설정을 재생 중 바꾸어도 적용됩니다.
- Focus: 창 이동 div는 기존대로 Tab 대상이 아니며 키보드 창 이동·Focus Tooltip을 제공하지 않습니다. 실제 호스트의 키보드 창 이동 대안과 보조기술 검증은 별도로 필요합니다. 최소화·종료는 이름이 있는 기본 버튼이며 Focus 표시가 유지됩니다.
Tooltip은 45 × 17px, 검정 80% 배경과 흰색 12px/400 글자입니다. Tooltip만 Figma의 Inter를 우선하고 한글·미설치 환경은 기존 SW 글꼴로 fallback합니다. 별도 폰트 설치는 하지 않습니다. 경로·자산은 [명세 §11.4](./sw-header-design-spec.md#114-현재-창-이동-motion--ani_hover_movegif-우선-2026-10-16), 현재 시간과 필드 Hover는 [명세 §11.5](./sw-header-design-spec.md#115-현재-추가-기준--1400ms-및-그룹-필드-hover-2026-10-16), 결과는 [현재 통합 검증](./sw-header-speed-composed-hover-verification-2026-10-16.md)을 참고합니다. 원본 GIF 속도 검증과 GIF 전달 전 결과는 각각 [GIF 검증 기록](./sw-header-window-motion-gif-verification-2026-10-16.md), [과거 검증 기록](./sw-header-window-motion-verification-2026-10-16.md)에 보존합니다.
## 11. 지원 · 미지원
| 지원 | 미지원(호스트 또는 후속 작업) |
|---|---|
| Figma 헤더 1개(26:8944) 표시·선택·Focus·Disabled | 색상·선스타일·문자스타일·치수·레이어 **상세 팝업**(Figma 전달 후), 색 선택 팝업(Figma "추후 디자인 진행예정") |
| 색상(26:10986 / 26:11036)·선스타일(26:11104)·문자스타일(26:11181)·치수스타일(26:11215) Dropdown, ByLayer/ByBlock, RGB·가중치·스케일 Input(2026-10-16) | 실제 색 적용·픽셀 색상 추출·선/문자/치수 스타일 적용(호스트 구현) |
| 반응형 More 대표 필드 전환, `SWHeaderInput`(Default / Hover / 편집 / 커서), Text Stroke(2026-10-16) | 선택형 상세 Input(Figma 미전달) |
| 문서 Dropdown(26:10662) 목록·선택·New/Open Callback·키보드 | 실제 파일 열기·새 문서 만들기(호스트 구현) |
| 레이어 Dropdown(26:10940) 목록·선택·전구/눈꽃/자물쇠 Callback·레이어 상세 Callback·키보드 | 실제 레이어 표시·동결·잠금과 레이어 상세 화면(호스트 구현) |
| 1800 / 1180 / 1080 / 900 / 640 / 520 반응형 + 더보기(옮긴 기능도 같은 Props·Callback) | SW 팝업(DraggablePanel) |
| SSR 문자열 렌더링(window 접근 없음) | 창 이동·최소화·종료의 실제 동작 |
| React 19, ESM, 타입 선언 | CommonJS 빌드, Light/Dark 테마, Runtime Theme JSON 연결 |
## 12. Storybook
```bash
npm run storybook
```
> **현재 기준(2026-10-21):** SW Story는 40개입니다. `SW / Header` 20개(사이드바 기본 표시 4개: `SW Header`(`sw-header--overview`)·`Home Menu`·`Logo Slot`·`Responsive More` + `verification` 태그 16개), `SW / Footer` 6개(기본 2 + verification 4), `SW / Popup` 3개(기본 3), `SW / Viewer` 10개(기본 5 + verification 5), `SW / Theme` 1개(verification). 최신 목록은 [패키지 README](../../packages/sw/README.md#storybook-hmdesign-저장소)를 따르고, 아래 2026-10-20 이전 문단은 당시 기록입니다.
> **(2026-10-20 기록) SW Storybook 간소화:** SW 메뉴 순서는 `Header → Footer → Popup → Viewer → Theme`입니다. `SW / Header`에는 20개 Story가 있습니다(사이드바 기본 표시 10개 + `verification` 태그 10개). `Property Panel · 4개 비교`는 사이드바에서 `SW / Popup` 아래로 옮겼고 ID(`sw-header--property-panel-compare`)는 그대로입니다. `Window Move Motion`·`K Guide`는 `verification` 태그로 숨겼고, `Home Menu · 단독 사용 · 조합형`은 개별 부품 예제로 사이드바에 표시합니다. 테스트·문서에서 쓰지 않던 Home Menu Figma 비교 Story 6개(`sw-header--home-menu-figma`, `-capture-active`, `-capture-outline`, `-capture-recent`, `-variants`, `-recent-files`)와 `Home Menu`의 Figma 스크린샷 비교 섹션은 제거했습니다. Figma 원본은 `docs/sw/figma-additional/screenshots/`에 있습니다. 상세: [SW Storybook 간소화 보고](./sw-storybook-simplification-2026-10-20.md). 아래는 작성 당시 기록입니다.
(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`에는 없습니다.
(2026-10-16 기록 — 아래 이름 중 Default·Controls·Responsive·Composed Controls·Layer Dropdown 등 다수는 이후 제거·통합되어 현재 없습니다) `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를 바꾸지 않아도 확인할 수 있습니다.
`Window Move Motion`은 GIF 주요 3상태와 속도 변경 후 약 0 / 205 / 410 / 444 / 683 / 888 / 1366 / 1400ms 프레임, 실제 Hover·Drag·최소화·종료 Callback 및 반복(`motion cycle`) 기록, 축소 폭 예제를 제공합니다. 내부 고정 시간은 원본 GIF 시간 × 1400/1640의 정확한 값입니다. 고정 프레임은 Storybook 전용 Hover attribute와 Web Animations API로 시간을 멈춥니다. Reduced Motion에서는 기본 아이콘·정적 Tooltip 정책을 따르므로 정상 타임라인 비교는 해당 설정을 끄고 Story를 다시 로드합니다.
2026-10-16 추가 Story: `Color Dropdown`, `Line Style Dropdown`, `Text · Dimension Style Dropdown`(Default / Hover / Expanded / 행 Hover / 독립 버튼 Hover 무변화 / Callback 기록 / 좁은 화면), `Responsive More`(대표 필드 전환 Callback 기록 1440·768·390 + 16개 폭), `Text Stroke`(Figma 원본 이미지 비교 · 3배 확대 · 말줄임), `Layer Detail · Input`(Figma 참조 이미지 · 레이어 상세 Default/Hover 무변화/Disabled/Callback · Input Default/Hover/ReadOnly/Disabled/편집/확정·취소·외부 값·IME), `Dynamic Data`(항목 추가·삭제·이름·값·순서 변경). `Document Dropdown`의 첫 예제는 현재 열린 문서 1개이며, 기본 `figmaArgs`도 그 문서 1개를 전달합니다(`documents=[]`는 예외 상태 예제로만 남김). 검증 결과는 [Dropdown·More·Text Stroke·Input 검증 기록](./sw-header-dropdowns-stroke-input-verification-2026-10-16.md)에 있습니다.
`Composed Controls`에는 기존 레이어·색상·상세보기와 공개 선/문자/치수 컨트롤, 5개 필드의 고정 Hover 비교가 있습니다. 실제 공용 컴포넌트를 렌더링하며 60 × 15px 치수 Hover SVG는 흰 foreground만 노란색으로 바꾸고 검정 mask 외곽선을 유지합니다. 기존 Story만 보완했으며 새 Story는 추가하지 않았습니다. [현재 통합 검증](./sw-header-speed-composed-hover-verification-2026-10-16.md)을 참고합니다.
## 속성창·K 가이드 추가 연결 (2026-10-16)
기존 공개 Props를 유지한 추가 API입니다. `propertyPanel`을 전달하면 기존 속성창 버튼에 패널이 연결됩니다. 미전달 시 기존 토글/Callback 계약만 유지하며 임의의 CAD 데이터/편집 창을 만들지 않습니다.
```tsx
import { useState } from "react"
import { SWHeader, type SWHeaderProperty } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
function DrawingHeader() {
const [properties, setProperties] = useState([
{ id: "layer", groupId: "common", name: "레이어", type: "text", value: "층_01" },
{ id: "weight", groupId: "common", name: "선가중치", type: "number", value: "0.25", min: 0, unit: "mm" },
])
return {
// 실제 CAD 반영 및 계산은 호스트에서 수행한 뒤 결과를 Props로 돌려줍니다.
setProperties(items => items.map(item => item.id === property.id ? { ...item, value } : item))
},
onToolActiveChange: active => console.info("호스트 선택 도구", active),
}}
onPropertyPanelOpenChange={open => console.info("속성창 열림", open)}
/>
}
```
- Uncontrolled: `propertyPanelOpen`을 생략합니다. Controlled: `propertyPanelOpen={open}`, `onPropertyPanelOpenChange={setOpen}`을 함께 전달합니다. 기존 `onPropertyPanelToggle`은 버튼/More 클릭 시 그대로 호출됩니다. 이전 Controlled 호스트가 새 Open Callback 없이 기존 Toggle Callback만 사용한다면 X/Escape 닫기에서도 기존 Toggle로 알립니다.
- 선택 대상이 바뀔 때 `selection.id`도 반드시 변경합니다. 같은 property id라도 이전 객체의 임시 편집값은 재사용하지 않습니다.
- `selection.kind`: none/all/single. `selection.title`·`counts`·`overviewTitle`은 호스트 데이터입니다. counts의 선택적 row/column은 Figma 집계 배치를 표현할 수 있습니다. `onOverviewClick`도 선택 변경을 요청할 뿐 내부 CAD 동작은 없습니다. 2026-10-21: 전체 선택 하단의 객체 유형·개수 목록은 표시 전용이며, 각 항목에 마우스를 올리면 그 항목 글자만 `#FF5C00`으로 바뀝니다(클릭·선택·필터 동작 없음).
- 항목 타입: text/number/select/color/slider/computed. `id/name/value/type/groupId`가 필수이고, `order`로 표시 순서를 지정합니다. 중복 id를 사용하지 마세요.
- `options`는 select의 id/name/disabled 목록입니다. 선택은 Enter 또는 Focus 이동으로 확정하고 Escape로 취소합니다. 삭제되거나 disabled가 된 option은 새 값으로 확정하지 않습니다. 옵션 데이터가 바뀌면 임시 선택을 현재 호스트 값으로 돌립니다.
- `color`는 기존 RGB/HEX parser를 재사용합니다. `swatch`로 별도 견본을 제공할 수 있습니다. `linePreview/linePattern`은 기존 선 미리보기를 재사용합니다. `annotation`은 (ByL) 같은 보조 정보, `unit`은 mm/% 같은 단위입니다.
- number/slider는 `min/max`, slider는 `step`을 지원합니다. slider의 기본 범위는 0~100이며 Story의 0~1 등은 호스트가 지정한 범위입니다. 수치를 계산하거나 Figma의 시각적 thumb 위치에 맞춰 임의 변환하지 않습니다.
- `validate(draft)`가 false면 거부합니다. 항목의 `onCommit(value, property)`와 패널의 `onPropertyChange(property, value)`는 서로 다른 통지 지점입니다. 둘 다 연결하면 각각 통지됩니다.
- 그룹은 `layout=rows/circle/position`, `order/name/collapsedCount/positionLabel`을 받습니다. circle은 원본 SVG 위 `graphicSlot=circumference/diameter/radius/area`의 항목을 배치합니다. 다른 항목은 그래픽 아래에 배치합니다. 계산값은 computed/readOnly로 전달합니다.
- `expandedGroupIds/onExpandedGroupsChange`로 펼침을 제어하거나, Uncontrolled일 때 `defaultExpandedGroupIds`로 시작합니다. 펼침은 property Callback을 실행하지 않습니다.
- `toolActive/onToolActiveChange/toolLabel`로 내부 아이콘을 연결합니다. 현재 의미는 호스트가 정하는 선택 도구 상태 통지이며 CAD 기능을 내장하지 않습니다.
- 단독 사용: ``. 단독 배치는 호스트의 `style`로 지정할 수 있습니다.
- 동적 패널 입력은 `SWHeaderInput commitOnUnmount={false}`를 사용합니다. 기존 Input의 기본 `true` 동작은 유지합니다.
- 속성 필드 상태(2026-10-19, Figma 26:12388 ~ 26:12649): 공통 행은 200 × 28 필드(선가중치처럼 단위 없는 숫자와 투명도 값은 41px 내외 Hug)입니다. Default는 값만, Hover는 `rgba(33,29,19,.4)` 테두리, 숫자 편집 On은 `#0F0D0A` + 흰 단위, Dropdown On은 `#211D13`입니다. 하이퍼링크(text)·재료(computed)는 Figma에 On이 없어 Default/Hover만 적용합니다. [검증 기록](./sw-property-fields-verification-2026-10-19.md).
- 필드 Dropdown: `property.picker`(`"layer" | "lineStyle" | "color" | false`)와 패널 `menus`(`SWHeaderPropertyMenus`: layers·toggle Callback, lineTypes·lineWeights·lineScale, colors·colorMode·onColorPick 등)를 전달합니다. `type: "color"`는 색상, `type: "select"` + `linePreview`는 선스타일 Dropdown이 기본이며 레이어는 `picker: "layer"`를 지정합니다. 목록 선택은 해당 속성의 `onPropertyChange`(값 = 행 id)로 확정되고, 레이어 Toggle·가중치·스케일·ByLayer/ByBlock·색상 추출·상세는 `menus` Callback만 호출합니다. `menus`가 없으면 `options`로 목록을 만듭니다. `mixed`는 `다양함`으로 표시하며 현재 행을 표시하지 않습니다.
- 2026-10-21 디자인 검토: 속성창 선꼴 Dropdown은 선 종류 목록 1열만 표시합니다(`menus.lineWeights`·`lineScale`·가중치/스케일 Callback은 속성창 Dropdown에서 쓰이지 않으며 SWHeader 선스타일 필드는 3열 유지). 숫자 편집 Input은 값 영역 전체 너비를 채우고, 투명도는 값 + `annotation`이 하나의 편집 상자입니다. 수정 불가능한 값(readOnly·computed·Disabled)은 속성창에서 검정, 속성Bar에서 Bar 편집이 없는 값은 포커스 없는 텍스트(`