docs(sw): define header tool icon reuse and window control rules

- AI_USAGE §5: single rule for design-based header tools (icon priority
  1-3, Storybook fixtures vs public API, pill icon states, window control
  omission via hiddenControls, existing-screen preservation, web/desktop)
- Reference §5 from AI_GUIDE (+4 synced copies), COMPOSITION_PATTERNS,
  COMPONENT_CATALOG, DESIGN_RULES §9
- Process header spec §13: Figma tool icon export on hold, proposed
  swProcessToolIcons and preconditions (no source/API change)
This commit is contained in:
shkim4 committed 2026-11-05 15:35:17 +09:00
1 parent 69677ace2c
commit 9a861a8d55
11 files changed
+161 -9

No files matched your search

+122 -1
View File
@@ -45,7 +45,7 @@ SW 패키지에는 Header·Popup 계열이 두 벌 있습니다. 화면마다 **
| 로고(홈) 버튼 메뉴 | `SWHeader`의 `homeMenu`(`SWHomeMenu` 데이터, 최근파일 `SWRecentFiles`, Figma 26:10591). 기본 숨김 → 로고 클릭 열기 → 다시 클릭 닫기 | **사용하지 않음.** `SWViewerHeader`에는 홈 메뉴 Props가 없고 로고 클릭은 `onHomeClick`만 호출합니다. 파일 열기는 로고 오른쪽 버튼(`onDocumentOpen`·`documentOpenLabel`), 샘플 목록은 파일명 클릭 목록(`documents`·`activeDocumentId`·`onDocumentSelect`)이며 `groups` 항목으로 만들지 않습니다([VIEWER.md](./VIEWER.md) §2) |
| 하단 Footer | `SWFooter`(+ 바 안 명령창 `command={<SWFooterCommandLine />}`, 좌표 `status`, `SWFooterLayoutTabs`, 부품 `SWFooter*`), [FOOTER.md](./FOOTER.md) | **사용하지 않음.** 뷰어 화면에는 하단 명령 막대가 없습니다. 도면 공간 탭은 `SWViewerHeader`의 `tabs` 항목 |
| 버튼·탭·토글 | `SWToolButton` / `SWToolGroup` | Header 항목 `type: "button" \| "toggle" \| "tabs" \| "switch"`, 패널 안 `SWViewerButton` |
| 아이콘 | 패키지 자산(필드 안 아이콘) | 프로그램 아이콘 16px, 직접 그린 아이콘은 `SWViewerIcon`으로 감쌈 |
| 아이콘 | 패키지 자산(필드 안 아이콘). 시안의 도구 아이콘 선택은 §5.2 | 프로그램 아이콘 16px, 직접 그린 아이콘은 `SWViewerIcon`으로 감쌈. 시안의 도구 아이콘 선택은 §5.2 |
| 범위 | Header 밖 부품은 `<SWTheme>` 안 | `SWViewerHeader`·`SWViewerMenu`·`SWViewerPanel`은 루트에 `hm-sw-theme`이 있어 `<SWTheme>` 불필요. 패널 컨트롤은 `SWViewerPanel` 안 |
| 토큰 | `--hm-sw-header-*`, Popup 콘텐츠 `--hm-sw-property-*` | `--hm-sw-viewer-*` + 공유 `--hm-sw-header-*`(아래 0.3) |
| 읽을 문서 | [COMPONENT_CATALOG.md](./COMPONENT_CATALOG.md) §1~8·§10·§11, [DESIGN_RULES.md](./DESIGN_RULES.md) §1~10·§12·§13·§14, [COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md) 1~6·8·10·12, [FOOTER.md](./FOOTER.md), 예제 A~G·I | [VIEWER.md](./VIEWER.md), [COMPONENT_CATALOG.md](./COMPONENT_CATALOG.md) §9, [DESIGN_RULES.md](./DESIGN_RULES.md) §11, [COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md) 7, [예제 H](./examples/ExampleH-ViewerMode.tsx) |
@@ -148,3 +148,124 @@ SW 토큰(`--hm-sw-header-*`, `--hm-sw-property-*`, `--hm-sw-viewer-*`, `--hm-sw
| 예: 뷰어 측정 패널 | 신규 콘텐츠(소비 프로젝트) | `SWViewerPanel` children에 `SWViewerField` + `SWViewerInput`. 토큰에 없는 값은 "AI 구성 값 — 디자인 확인 필요" |
새 컴포넌트를 SW 패키지 공식 컴포넌트로 승격할지는 별도 검토 대상이다. 임의로 패키지에 추가하지 않는다.
## 5. 디자인 시안 기반 Header 구현 — 도구 아이콘·창 제어 버튼 판단 (2026-11-05)
화면 이미지·Figma·캡처 같은 **디자인 시안**을 보고 SW Header(`SWHeader`·`SWProcessHeader`·`SWViewerHeader`)의 도구 버튼과 창 제어 버튼을 구현할 때 따르는 **단일 기준**입니다. 다른 문서는 이 절을 요약·참조만 합니다. 크기·색·상태 값은 [DESIGN_RULES.md](./DESIGN_RULES.md)(§9·§14, 뷰어는 [VIEWER.md](./VIEWER.md))를 따르며 이 절에서 다시 정하지 않습니다.
### 5.1 컴포넌트 기본 구성과 시안 재현은 다릅니다
| 구분 | 뜻 | 바꾸는 방법 |
|---|---|---|
| 컴포넌트 기본 구성 | Props를 생략했을 때 컴포넌트가 그리는 구성(예: `SWProcessHeader`는 `hiddenControls`가 없으면 창 이동·최소화·최대화/복원·종료를 모두 그림). 기존 소비 프로젝트가 의존하는 패키지 기본값 | 바꾸지 않습니다. 패키지 기본값·CSS를 고치거나 `hm-sw-*` class로 숨기지 않습니다 |
| 시안 재현 | 시안에 **보이는** 구성만 화면에 두는 것 | 공개 Props(`hiddenControls`·`tools`·`trailing` 등)로만 맞춥니다 |
- 카탈로그 예시·예제 파일·Storybook에 어떤 버튼이나 아이콘이 있다는 사실은 시안에 그것을 만들 근거가 아닙니다.
- **기존 화면 작업**(기존 화면을 HMDesign으로 옮기기, 이미 HMDesign을 쓰는 화면에 새 시안 적용)에는 [AI_GUIDE.md](./AI_GUIDE.md) 4단계 "기존 화면 전환 시 원본 보존"·"디자인 적용 시 변경 범위 제한"이 우선합니다. 원본에 있는 버튼·아이콘·창 제어 기능을 "시안에 없다"는 이유만으로 지우거나 `hiddenControls`를 더해 숨기지 않고, 원본에 없는 창 제어 버튼을 시안만 근거로 더하지 않습니다. 시안과 원본이 다르면 결과 보고의 "원본 대비 변경"에 확인 대상으로 적습니다. §5.5의 생략·추가 규칙은 신규 화면 기준입니다.
- 사용자가 창 제어·도구 버튼 구성 변경을 **명시적으로 요청**했으면 요청한 버튼에 한해 공개 Props로 바꾸고, "원본 대비 변경"의 근거를 "사용자 승인"으로 적습니다. 요청 범위 밖의 버튼은 그대로 둡니다.
### 5.2 도구 아이콘 선택 우선순위
| 순위 | 조건 | 할 일 |
|---|---|---|
| 1 | 사용자가 이번 작업에 제공했거나 사용을 승인한 이미지·SVG, 또는 소비 프로젝트에 이미 등록된 공식 아이콘이 있음 | 그 에셋을 씁니다. 상태별 그림이 있으면 기본은 `icon`, Hover 그림은 `hoverIcon`에 넣습니다(§5.4) |
| 2 | 제공된 에셋이 없고, `@hm-design/sw`가 **공개 Export**하는 아이콘·부품 중 **기능과 의미가 같은 것**이 있음 | 그것을 재사용합니다. 시안과 모양이 조금 달라도 기능이 같고 그 아이콘의 사용 위치·크기·상태 규칙 안이면 HMDesign 것을 우선합니다 |
| 3 | 1·2 모두 없음 | 대체하지 않고 **미지원으로 보고**합니다(아래) |
- **시안 속 아이콘의 사용 권한:** Figma 등 시안에 그려져 있다는 사실만으로 사용 권한이 있다고 보지 않습니다. 시안에서 아이콘을 추출해 쓰려면 사용자가 그 에셋을 제공했거나 사용을 명시적으로 승인했는지 먼저 확인하고, 확인 전에는 1순위로 쓰지 않습니다(2·3순위로 판단). 이 승인은 해당 소비 프로젝트에서의 사용에만 해당하며, HMDesign 패키지 공식 등록·공개 배포 권한은 별도로 확인합니다(§5.3).
**2순위 — 현재 공개된 아이콘·아이콘 포함 부품**(목록 밖 이름은 [COMPONENT_CATALOG.md](./COMPONENT_CATALOG.md)와 `dist/index.d.ts`에서 확인):
| 공개 Export | 기능 | 사용 위치 |
|---|---|---|
| `SWHeader` 기본 부품(인쇄 `print`, K 가이드 `popupGuide`, 창 이동·최소화·종료) | 인쇄, 단축키 안내, 창 제어 | `SWHeader` 안(`hiddenControls`로 개별 제거). 아이콘만 꺼내 쓰는 API는 없습니다 |
| `SWHeaderActions` | 인쇄·K 가이드 버튼 pill(`onPrint`·`onPopupGuide`·`showPrint`·`showPopupGuide`·`disabled`) | `<SWTheme>` 안 SW 일반 모드 조합 Header([COMPOSITION_PATTERNS.md](./COMPOSITION_PATTERNS.md) 2). 컴포넌트 단위로만 재사용합니다 — 인쇄·K 아이콘 단독 Export는 없어 `SWToolButton`의 `icon`에 넣을 수 없습니다. `SWHeader`(이미 내장)·`SWProcessHeader` `tools`·뷰어 화면([VIEWER.md](./VIEWER.md) §2.1)에는 넣지 않습니다(검증된 조합 아님) |
| `SWHeaderWindowControls` | 창 이동·최소화·종료 | SW 일반 모드 조합 Header, 뷰어 `trailing` 끝(최소화·닫기만, `showWindowMove={false}`, VIEWER.md §2.1) |
| `SWProcessSplitButton` | 화면 분할 | `SWProcessHeader`의 `tools` |
| `SWLayerVisibilityIcon` | 레이어 표시·숨김 전구 | 뷰어 레이어 Popup(VIEWER.md §4.1) |
| `swFooterToolIcons` · `swFooterAuxIcons` | Footer 명령(Line·Circle …)·보조기능(객체스냅 …) | `SWFooter` 칸([FOOTER.md](./FOOTER.md)). 크기·상태 색이 Header 도구 버튼과 달라 Header pill에는 쓰지 않습니다 |
- 모양이 비슷하다는 이유만으로 기능이 다른 아이콘을 쓰지 않습니다(예: `swFooterToolIcons.rect`를 최대화 아이콘으로 쓰기 금지).
- `SWViewerIcon`은 외곽선만 더하는 감싸기 부품이며 그림을 제공하지 않습니다.
- **Header 도구 버튼용 범용 아이콘(도움말·3D 보기·설정·메뉴·나가기 등)은 현재 공개 Export가 없습니다.** 이런 아이콘이 시안에 있고 1순위 에셋도 없으면 3순위입니다.
**3순위 — 공식 아이콘이 없을 때:**
1. 외부 아이콘 라이브러리(`lucide-react` 등 — Web 패키지 의존성이지만 SW 화면에 쓰지 않음), AI가 그린 SVG, Storybook 파일 복사로 대신하지 않습니다.
2. 코드를 쓰기 전에 필요한 아이콘·기능·필요한 상태(기본·Hover 등)를 사용자에게 알리고 에셋 제공 또는 HMDesign 공식 아이콘 등록이 필요하다고 요청합니다.
3. 답을 기다리지 않고 진행해야 하면 그 도구 버튼을 넣지 않거나, 사용자가 승인한 임시 표시만 쓰고 결과 보고에 "미지원 아이콘 — 에셋 필요"로 적습니다. 승인 없이 임시 아이콘을 최종 디자인으로 확정하지 않습니다.
### 5.3 Storybook 전용 에셋과 공개 에셋의 차이
| 구분 | 공개 에셋·API | Storybook 전용 에셋(Fixture) |
|---|---|---|
| 위치 | `@hm-design/sw` 진입점 Export(카탈로그에 이름이 있음). SVG는 JS에 data URI로 포함 | HMDesign 저장소의 Story 데모 파일. 설치 패키지(`dist`·`ai`)에 포함되지 않음 |
| 보장 | 버전 호환·사용 규칙 문서화 대상 | 데모용. 출처·사용 권한·유지를 보장하지 않음 |
| 소비 프로젝트 | Import해서 사용 | Import·복사·경로 참조 금지(Git Submodule로 저장소 파일이 보여도 같음) |
- Storybook 화면에 보인다는 것은 공개 API라는 뜻이 아닙니다. Storybook은 조합·외형 참고용입니다([AI_GUIDE.md](./AI_GUIDE.md) 4단계 "Git Submodule 사용 규칙").
- **2026-11-05 현재 Storybook 전용(공개 API 아님):** `SW / Step Header` Story의 도구 아이콘 — Multi Step의 도움말·3D 보기·설정·메뉴·나가기, Default의 View Property·Sub 창. 출처·사용 권한 확인 전이라 패키지에 등록하지 않았습니다. 시안에 이 아이콘이 있으면 1순위(사용자·프로젝트 제공)로 받거나 3순위로 보고합니다.
### 5.4 아이콘 상태 처리 — 기존 공개 API 방식 그대로
- `SWToolButton appearance="pill"`의 `icon`(기본 그림)과 `hoverIcon`(상태 그림)만 씁니다. Disabled 전용 아이콘 Prop은 없습니다. 상태별 현재 동작(패키지 CSS 기준)은 버튼이 놓인 곳에 따라 다릅니다.
| 상태 | `SWProcessHeader`의 `tools` 안 | 그 밖의 `pill`(`SWHeader`의 `tools`, `<SWTheme>` 조합) |
|---|---|---|
| Default | `icon`. `currentColor` = `#B6D0C9`(`--hm-sw-process-tool-icon-color`) | `icon`. `currentColor` = 상속 글자색 `--hm-sw-header-text-color`(`#F6F4F2`), `#B6D0C9` 아님 |
| Hover | 노란 링 + `currentColor` `#FFC600` + `hoverIcon`이 있으면 그 그림 | 노란 링 + `hoverIcon`이 있으면 그 그림. `currentColor` 색은 바뀌지 않음 |
| Pressed(`pressed` Prop, `aria-pressed`) | Hover와 같은 모양(노란 링 + `#FFC600` + `hoverIcon`) | `currentColor`만 `#FFC600`. 노란 링·`hoverIcon` 전환 없음 |
| Disabled | Hover 반응 없음, 기본 그림 유지, 투명도 0.4 | 같음 |
- 누르는 순간(`:active`)의 별도 모양은 없습니다. 위 Pressed는 `pressed` Prop 상태입니다.
- 색이 고정된 이미지는 기본·Hover 두 장을 줍니다. Hover 그림이 제공되지 않으면 `hoverIcon`을 생략하며(Hover에도 기본 그림 유지), 제공된 그림의 색을 바꿔 Hover 그림을 만들지 않습니다. `currentColor` 한 장으로 상태 색을 따르는 방식은 `SWProcessHeader`의 `tools`에서만 `#B6D0C9` → `#FFC600`이 됩니다. 그 밖의 `pill`에서 시안 색이 인쇄 버튼처럼 `#B6D0C9` → `#FFC600`이면 기본·Hover 두 장 이미지를 씁니다(값은 [DESIGN_RULES.md](./DESIGN_RULES.md) §9·§14).
- 위 표와 다른 상태 모양이 시안에 있으면 CSS로 맞추지 않고 지원 범위 밖으로 보고합니다.
- 그림은 장식이므로 `<img alt="" />`로 두고 버튼 이름은 `children`으로 줍니다.
- 상태를 맞추려고 `hm-sw-*` class나 패키지 CSS를 덮지 않습니다.
### 5.5 창 제어 버튼(창 이동·최소화·최대화·닫기)
1. 시안에 **명확히 보이는** 창 제어 버튼만 구현합니다(웹 화면은 4번, 기존 화면은 §5.1이 우선).
2. 시안에 없고 별도 요구도 없으면 만들지 않는 것이 기본입니다. 컴포넌트 기본 구성(§5.1)에 있어도 공개 숨김 API로 뺍니다(신규 화면 기준, 기존 화면은 §5.1).
3. 시안의 오른쪽이 잘렸거나 일부가 보이지 않으면 보이지 않는 버튼의 존재를 확정하지 않습니다. 코드를 쓰기 전에 묻고, 답 없이 진행해야 하면 보이는 범위만 구현한 뒤 결과 보고에 "확인 필요(시안 잘림)"로 적습니다.
4. 브라우저에서 쓰는 웹 화면에서는 운영체제 창 제어 버튼을 새로 만들지 않는 것이 기본입니다. 시안에 창 제어 버튼이 보여도 실제로 연결할 창 동작(최소화·최대화·닫기를 처리하는 셸·Callback 요구)이 확인되지 않으면 임의로 구현하지 않습니다. 코드를 쓰기 전에 묻고, 답 없이 진행해야 하면 신규 창 제어 버튼 생성을 보류하고(아래 공개 숨김 API로 생략) 결과 보고에 "보류 — 창 동작 요구 확인 필요"로 적습니다. 빈 Callback·임의 `window.close()` 연결 같은 가짜 동작을 만들지 않습니다. 기존 화면에 이미 있는 창 제어 기능은 이 규칙을 이유로 별도 승인 없이 제거하지 않습니다(§5.1).
5. 데스크톱 프로그램(설치형·WebView 셸 등)처럼 창 제어가 필수인 환경은 플랫폼 요구와 사용자 명세를 우선합니다. 시안에 없는데 플랫폼상 필요해 보이면 임의로 더하지 않고 확인합니다. 실제 창 이동·최소화·최대화·종료는 프로그램 담당입니다(Callback 연결).
6. 숨김·생략은 아래 공개 API로만 합니다. 적절한 공개 API가 없으면 CSS로 감추는 우회 대신 지원 여부와 제약을 보고합니다.
| Header | 기본 구성 | 시안에 없을 때(공개 API) |
|---|---|---|
| `SWProcessHeader` | 창 이동·최소화·최대화/복원·종료 표시 | `hiddenControls`에 `"windowMove"`·`"minimize"`·`"maximize"`·`"close"` 중 없는 것. 넷 다 숨기고 `tools`도 없으면 창 영역 자체가 렌더링되지 않음 |
| `SWHeader` | 창 이동·최소화·종료 표시 | `hiddenControls`에 `"windowMove"`·`"minimize"`·`"close"` 중 없는 것 |
| `SWViewerHeader` | 창 제어 없음 | `trailing`에 `SWHeaderWindowControls`를 넣지 않음. 시안에 최소화·닫기가 있으면 `trailing` 맨 끝에 `showWindowMove={false}`로만 둠(VIEWER.md §2.1 승인 구성). 창 이동은 뷰어에서 지원하지 않으므로 시안에 있어도 넣지 않고 미지원으로 보고 |
| `<SWTheme>` 안 조합 Header | — | `SWHeaderWindowControls`를 두지 않거나 `showWindowMove`·`showMinimize`·`showClose`로 일부만 표시 |
### 5.6 공식 구현 예시 (공개 API만)
```tsx
import { SWHeader, SWProcessHeader, SWToolButton } from "@hm-design/sw"
// 1순위 에셋: 사용자·소비 프로젝트가 제공한 파일(HMDesign 패키지·Storybook 파일이 아님)
import helpIcon from "./assets/help.svg"
import helpIconHover from "./assets/help-hover.svg"
// 시안: 창 제어 버튼 없음, 도구는 도움말 하나
<SWProcessHeader appearance="multi" documentTitle={projectName} logoText="StrAna" onHomeClick={toggleFileMenu}
process={{ groups, activeStepId: step, onStepSelect: s => setStep(s.id) }}
tools={<SWToolButton appearance="pill" icon={<img src={helpIcon} alt="" />} hoverIcon={<img src={helpIconHover} alt="" />} onClick={openHelp}>도움말</SWToolButton>}
hiddenControls={["windowMove", "minimize", "maximize", "close"]} />
// 시안: 최소화·닫기만 있음(창 이동·최대화 없음)
<SWProcessHeader documentTitle={fileName} process={processProps}
hiddenControls={["windowMove", "maximize"]} onMinimize={minimize} onClose={closeWindow} />
// 시안: 완성형 SWHeader에 창 제어 버튼 없음
<SWHeader documentTitle={fileName} {...headerProps} hiddenControls={["windowMove", "minimize", "close"]} />
```
### 5.7 결과 보고에 남길 것
§4 표에 다음 판단을 함께 적습니다.
| 요청 항목 | 판단 | 근거 |
|---|---|---|
| 예: 도움말 아이콘 | 1순위 사용 / 2순위 재사용 / 3순위 미지원 | 사용자 제공 파일명 / 재사용한 공개 Export 이름 / "공개 아이콘 없음 — 에셋 제공 또는 공식 등록 필요(기능·필요 상태)" |
| 예: 창 제어 버튼 | 구현 / 생략 / 보류 / 원본 유지 / 확인 필요 | 시안에 보인 버튼 목록, 사용한 `hiddenControls` 값, 시안 잘림·웹/데스크톱·플랫폼 요구 여부, 기존 화면이면 원본 구성과 사용자 승인 여부 |