Merge branch 'integration/sw-home-menu' into feature/sw-home-menu
Integrate the SW home (logo) menu into SWHeader for SW general mode only. SW viewer mode code and stories are unchanged. - Resolve conflicts: keep both sw-home-menu and sw-viewer exports; renumber AI docs (catalog viewer 8->9, patterns home menu 7->8, design rules home menu 11->12) - SWHeader: start the home menu after the file name when it sits inside controls, so the file name stays visible - AI guides: mark home menu as general-mode only; forbid it in viewer mode (AI_USAGE, catalog, design rules, patterns, VIEWER.md, AI_GUIDE) - Tests: ignore wildcard identifiers in ai-docs check; update expected Storybook SW titles (SW/Tool removed, SW/Viewer added) - Add merge QA report docs/sw/sw-home-menu-merge-2026-10-19.md
No files matched your search
@@ -212,7 +212,7 @@ import { Button, Card, CardContent, FilterBar, FilterBarActions, FilterBarSearch
|
||||
### 예시 B1 — SW 일반 모드 (편집 Header + 도구 막대 + 설정 Popup)
|
||||
|
||||
- 읽기: `node_modules/@hm-design/sw/ai/` → AI_USAGE §0(일반 모드) → COMPOSITION_PATTERNS "6. 파일 열기 + 도구 막대", 예제 `ai/examples/ExampleF-ViewerToolbar.tsx`(파일 이름의 Viewer는 과거 명칭이며 일반 모드 `SWHeader` 예제)
|
||||
- 재사용: `SWHeader`(`documentOpenPlacement="header"`, `controls`, `tools`), `SWToolGroup` + `SWToolButton`(Model/Layout 탭, Fit 등), `SWPopup` + `SWPopupSection` + `SWPopupRow` + `SWHeaderInput`(설정 Popup)
|
||||
- 재사용: `SWHeader`(`documentOpenPlacement="header"`, `controls`, `tools`), `SWToolGroup` + `SWToolButton`(Model/Layout 탭, Fit 등), `SWPopup` + `SWPopupSection` + `SWPopupRow` + `SWHeaderInput`(설정 Popup), 로고(홈) 버튼 메뉴는 `SWHeader`의 `homeMenu` + `SWRecentFiles`(**일반 모드 전용**, 예제 `ExampleG-HomeMenu.tsx`)
|
||||
- 신규: SW에 없는 기능(예: 검색) → 소비 프로젝트 컴포넌트, `<SWTheme>` 안, `--hm-sw-header-*` 토큰, 안쪽 입력은 `SWHeaderInput`(예제 `ExampleD-SearchExtension.tsx`)
|
||||
|
||||
```tsx
|
||||
@@ -225,7 +225,7 @@ import "@hm-design/sw/styles.css"
|
||||
- 읽기: `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`
|
||||
- 신규: 뷰어에 없는 패널 콘텐츠 → 소비 프로젝트 컴포넌트를 `SWViewerPanel`의 `children`에, 안쪽은 `SWViewerField` + `SWViewerInput` 등, 외형은 `--hm-sw-viewer-*` 토큰
|
||||
- 하지 않음: `SWHeader`·`SWPopup`을 뷰어 화면에 섞기, 로고가 없다고 임의 문자·아이콘 넣기, 연결할 명령이 없는 메뉴를 가짜 동작으로 만들기(제외 또는 `disabled` + 미연결 보고)
|
||||
- 하지 않음: `SWHeader`·`SWPopup`을 뷰어 화면에 섞기, 로고(홈) 메뉴(`SWHomeMenu`·`SWRecentFiles`·`homeMenu`)를 뷰어 화면에 넣거나 비슷하게 만들기(홈 메뉴는 일반 모드 전용), 로고가 없다고 임의 문자·아이콘 넣기, 연결할 명령이 없는 메뉴를 가짜 동작으로 만들기(제외 또는 `disabled` + 미연결 보고)
|
||||
|
||||
```tsx
|
||||
import { SWViewerHeader, SWViewerPanel, SWViewerSection, SWViewerField, SWViewerSwitch, type SWViewerHeaderGroup } from "@hm-design/sw"
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# AI 가이드 초안 — SW 로고 클릭 메뉴 (`SWHomeMenu`)
|
||||
|
||||
> 상태: **통합 전 초안**입니다(Task B 2단계, 2026-10-19). 통합 승인 후 `packages/sw/ai/COMPONENT_CATALOG.md`·`COMPOSITION_PATTERNS.md`·`DESIGN_RULES.md`로 옮깁니다. 지금은 `@hm-design/sw` 진입점에서 export되지 않으므로 소스 경로 `packages/sw/src/components/sw-home-menu`를 사용합니다.
|
||||
> 기준: Figma `rj6wpsfk24lOWvWiVRAANP` 26:10591(메뉴), 26:11355…26:11631(타일 15개), 26:12604…(섹션 제목), 662:67267·662:67331·662:67471(최근파일 하위 패널). 구현: `SWHomeMenu.tsx`, `SWRecentFiles.tsx`, `theme/sw-home-menu.css`.
|
||||
|
||||
## 1. 무엇인가
|
||||
|
||||
- SW Header의 **로고(홈) 버튼을 누르면 Header 위에 겹쳐 나타나는 2단 메뉴**입니다. 기본은 숨김이고, 로고를 다시 누르면 닫힙니다.
|
||||
- 구성: 섹션(제목 badge + 타일 그룹) 여러 개 → 섹션마다 80 × 46 타일 여러 개. 타일 하나가 하위 패널(예: 최근파일 목록)을 열 수 있습니다.
|
||||
- 운영체제 파일 선택창이 아닙니다. "찾아열기" 같은 항목의 실제 동작(파일 대화상자 등)은 host가 `onSelect`에서 수행합니다.
|
||||
|
||||
## 2. 최소 사용법
|
||||
|
||||
```tsx
|
||||
import { SWHeader, SWTheme } from "@hm-design/sw"
|
||||
import { SWHomeMenu, type SWHomeMenuSectionData } from "@hm-design/sw" // 통합 후 경로
|
||||
|
||||
const sections: SWHomeMenuSectionData[] = [
|
||||
{ id: "file", title: "작업중인 파일", items: [
|
||||
{ id: "save", label: "저장", icon: <SaveIcon />, toggle: false, onSelect: () => host.save() },
|
||||
{ id: "export", label: "내보내기", icon: <ExportIcon /> },
|
||||
] },
|
||||
]
|
||||
|
||||
function App() {
|
||||
const [open, setOpen] = useState(false)
|
||||
return (
|
||||
<div style={{ position: "relative" }}>
|
||||
<SWHeader onHomeClick={() => setOpen(v => !v)} /* 기존 props */ />
|
||||
{/* 통합 전: host가 위치를 지정합니다(Header 객체 수정 영역 왼쪽 - 2px, 창 영역 왼쪽까지). */}
|
||||
<div style={{ position: "absolute", top: 0, left: objectAreaLeft - 2, maxWidth: windowLeft - objectAreaLeft + 2, zIndex: 3 }}>
|
||||
<SWTheme><SWHomeMenu sections={sections} open={open} onOpenChange={setOpen} /></SWTheme>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## 3. 데이터 규칙 (새 메뉴를 만들 때)
|
||||
|
||||
| 하고 싶은 것 | 방법 |
|
||||
|---|---|
|
||||
| 항목 추가 | 섹션 `items`에 `{ id, label, icon }`을 추가합니다. 폭·구분선·모서리·섹션 폭은 자동입니다 |
|
||||
| 섹션 추가 | `{ id, title, items }`를 추가합니다. 제목 badge 폭은 글자 + 46px로 자동입니다 |
|
||||
| 숨김 | `hidden: true`. 자리를 남기지 않습니다 |
|
||||
| 비활성 | `disabled: true`. 기존 SW 규칙대로 opacity .4이며 Hover가 없습니다 |
|
||||
| 실행만 하는 버튼 | `toggle: false`. Active(Variant3)가 되지 않습니다 |
|
||||
| 켜고 끄는 버튼 | 기본값(`toggle` 생략)입니다. 클릭하면 Active, 다시 클릭하면 Default입니다 |
|
||||
| Active 제어 | `activeIds` + `onActiveChange`(controlled) 또는 `defaultActiveIds`. 하나만 켜려면 `selection="single"` |
|
||||
| 하위 패널 | `panel: <SWRecentFiles files={…} onFileOpen={…} />`. 패널은 한 번에 하나만 열리고 열린 동안 하단 강조가 전체 외곽선이 됩니다 |
|
||||
| 아이콘 | `currentColor` SVG 하나를 권장합니다(Default `#B6B4AF`, Hover·Active `#FF5C00` 자동). 색이 고정된 이미지 쌍이면 `hoverIcon`을 함께 줍니다 |
|
||||
| 이벤트 | 항목 `onSelect(event)` → 메뉴 `onItemSelect(event)`. `event.active`는 클릭 후 상태입니다 |
|
||||
|
||||
## 4. 상태와 닫기 (Figma 기준 + 접근성 보완)
|
||||
|
||||
| 동작 | 결과 | 근거 |
|
||||
|---|---|---|
|
||||
| 로고 클릭 | 열기 / 다시 클릭하면 닫기 | Figma btn_home ON_CLICK → OVERLAY, 재클릭 닫기는 사용자 확정 |
|
||||
| 타일 Hover | Variant2: 주황 gradient 1px 테두리, 아이콘·글자 `#FF5C00`, 1px 위·왼쪽 이동 | Figma Default ON_HOVER → V2 |
|
||||
| 타일 클릭 | Variant3(Active, V2와 같은 모양) → 다시 클릭하면 Default | Figma V2 → V3 → Default |
|
||||
| 메뉴 빈 곳 클릭 | 닫기 | Figma Group ON_CLICK → CLOSE (`closeOnBackgroundClick`) |
|
||||
| Escape | 열린 패널을 먼저 닫고, 다시 누르면 메뉴를 닫은 뒤 로고로 Focus 복귀 | Figma 없음 · 접근성 (`closeOnEscape`) |
|
||||
| 메뉴 밖 클릭 | 기본은 닫지 않음 | Figma overlay에 바깥 동작 없음 (`closeOnOutsideClick`) |
|
||||
| ←/→/Home/End | 타일 사이 이동. 열린 패널 타일에서 ↓를 누르면 패널로 이동, ↑/↓로 행 이동, Enter로 파일 열기 | Figma 없음 · 접근성 |
|
||||
| 최근파일 행 선택 | `onFileOpen(file)` 후 메뉴 닫기(`closeOnOpen`) | Figma 없음 · 검토 대상 |
|
||||
|
||||
## 5. 하지 말 것
|
||||
|
||||
- 타일·섹션·제목을 Figma 화면별로 따로 만들지 않습니다. 모든 메뉴는 `sections` 데이터 하나로 표현합니다.
|
||||
- `hm-sw-home-menu*` class를 소비 코드에서 붙여 외형을 만들지 않습니다(`className`은 확장용입니다).
|
||||
- 새 색·크기를 임의로 정하지 않습니다. 필요한 값은 `--hm-sw-home-menu-*`·`--hm-sw-recent-files-*` 토큰을 `style`로 덮어씁니다.
|
||||
- Font Weight는 400/500/700만 씁니다(타일 라벨·파일명 700, 섹션 제목 400).
|
||||
- 홈 버튼이나 창 영역을 메뉴 안에 다시 그리지 않습니다. 그것은 `SWHeader`가 담당합니다.
|
||||
@@ -0,0 +1,259 @@
|
||||
# 작업 B 1단계 — Figma 추가 디자인 분석·설계 보고 (2026-10-19)
|
||||
|
||||
> **2단계 이후 정정(우선 적용):** [2단계 보고](./stage2-report.md)를 기준으로 봅니다. 아래 1단계 본문은 당시 기록으로 남겨 둡니다.
|
||||
> - 로고 클릭 = 2단 Header 메뉴, 기본 숨김, 재클릭 닫힘(사용자 최종 정정).
|
||||
> - 662:67267은 Figma 주석 "최근파일" 상태 화면입니다. 최근파일 목록 662:67331과 2뎁스 미리보기 662:67471이 함께 붙으며, 두 노드는 `SWRecentFiles`로 구현했습니다.
|
||||
> - V3 = 클릭으로 켜지고 다시 클릭하면 꺼지는 Active 상태입니다(사용자 지시).
|
||||
> - 아이콘 prop은 `iconHover`가 아니라 `hoverIcon`입니다(Task A `SWToolButton`과 이름을 맞춤).
|
||||
> - `accent`의 기본값은 `"auto"`입니다(패널이 열린 동안 outline).
|
||||
|
||||
> 상태: **분석·설계 보고만 작성했습니다(코드 수정 없음)**. 구현, Commit, Push는 하지 않았고 2단계는 사용자 승인 후 시작합니다.
|
||||
> 대상: Figma `rj6wpsfk24lOWvWiVRAANP`, 속성Bar `26:10492`와 로고 클릭 노드 21개. 기준 브랜치는 `claude/sw-figma-logo-menu-36832b`(HEAD `5faa1f1`, `feature/sw-package`와 동일)입니다.
|
||||
> 수집 자료: [figma-nodes.json](./figma-nodes.json)(노드별 메타데이터·디자인 컨텍스트 정규화·Prototype 연결), [screenshots/](./screenshots/)(노드 22개 PNG, 1x).
|
||||
|
||||
## 0. 수집 방법과 한계
|
||||
|
||||
| 항목 | 내용 | 상태 |
|
||||
|---|---|---|
|
||||
| Figma 접근 | 공식 Figma MCP(`get_metadata`, `get_design_context`, `get_variable_defs`, `get_screenshot`)를 사용했습니다. `figma-design-to-code` 스킬을 먼저 불러왔습니다. 인증이 필요한 `plugin:figma:figma` 서버는 사용하지 않았습니다. | 완료 |
|
||||
| 세부 속성 | 읽기 전용 `use_figma`(Plugin API)로 reactions, gradient stop, stroke, effect, font, 변형 간 diff를 확인했습니다. 캔버스는 수정하지 않았습니다. | 완료 |
|
||||
| 디자인 컨텍스트 저장 | React/Tailwind 원문 대신 구현에 필요한 값만 `figma-nodes.json`에 정규화했습니다. 원문은 같은 node id로 다시 받을 수 있습니다. | 정규화본만 저장 |
|
||||
| Asset | `get_design_context`의 asset URL은 2026-10-26에 만료됩니다. 이번 단계에서는 asset을 받지 않았고, 2단계에서 다시 받습니다. | 미수집 |
|
||||
| Storybook 비교 | `localhost:6024`에서 이미 실행 중인 Storybook의 `SW/Header/Overview` 속성Bar를 측정했습니다. 그 서버가 어느 checkout에서 실행되는지는 확인하지 못했습니다. Pane 폭은 390px, DPR은 1.5였습니다. | 실측 1회, 출처 미확인 |
|
||||
| 형제 노트 | 공유 노트 폴더에 다른 세션의 노트가 없었습니다. 작업 A의 계획은 사용자 메모리의 확정 기록 기준으로 대조했습니다. | 확인 |
|
||||
|
||||
## 1. 노드별 표 (22개)
|
||||
|
||||
"로고 클릭 화면 21개"는 실제로 **화면 상태 2개**와 **구성 컴포넌트 19개**(섹션 제목 4개, 버튼 세트 15개 × 상태 3개)입니다. 다섯 번째 섹션 제목 `tit_마이세팅`(26:12628, "My Setting")은 요청 목록에 없지만 화면에서 사용되므로 참고로 함께 정리했습니다.
|
||||
|
||||
| # | Node | 이름 / 유형 | 크기 | 역할 | 상태·Prototype |
|
||||
|---|---|---|---|---|---|
|
||||
| 1 | 26:10492 | 속성Bar / FRAME (`header_green` → `선택한것` 26:10491 하위) | 1222×28 | 객체 속성 편집 strip | input ON_HOVER → hover(26:11659) |
|
||||
| 2 | 26:10591 | 헤더/꾸러미 ver2/home on / COMPONENT | 1920×77 | **로고 클릭 overlay 본체** | btn_home ON_CLICK → OVERLAY(TOP_LEFT, 배경 없음), 메뉴 그룹 ON_CLICK → CLOSE |
|
||||
| 3 | 662:67267 | 같은 이름 / FRAME | 1920×77(렌더 1935×193) | 2의 정적 사본. 팝업가이드 Variant5(K 미리보기)와 다른 하단 강조 | hover만 있음(open/close 없음) |
|
||||
| 4 | 26:12604 | tit_작업중인파일 | 124×19 | 섹션 제목 "작업중인 파일" | 없음 |
|
||||
| 5 | 26:12610 | tit_다른파일열기 | 123×19 | "다른파일 열기" | 없음 |
|
||||
| 6 | 26:12622 | tit_파일손상시 | 111×19 | "파일 손상시" | 없음 |
|
||||
| 7 | 26:12616 | tit_궁금하면 | 95×19 | "궁금하면" | 없음 |
|
||||
| 8 | 26:11355 | btn_저장 / COMPONENT_SET | 3×(80×46) | 저장 · save · 그룹 첫 버튼 | D→hover V2, V2 click→V3, V3 click→D |
|
||||
| 9 | 26:11371 | 복사저장 | 〃 | save_as · 중간 | 〃 |
|
||||
| 10 | 26:11387 | 내보내기 | 〃 | file_open · 중간 | 〃 |
|
||||
| 11 | 26:11403 | 도면묶기 | 〃 | inventory_2 · 마지막(구분선 없음) | 〃 |
|
||||
| 12 | 26:11416 | 찾아열기 | 〃 | folder_open · 첫 버튼. 아이콘 top 5 | 〃. 구분선 gradient가 다름 |
|
||||
| 13 | 26:11450 | 최근파일 | 〃 | 시계 붙은 파일 아이콘 · 중간 | **V2→V3 클릭 reaction 없음**. 구분선 gradient가 다름 |
|
||||
| 14 | 26:11485 | 새파일 | 〃 | insert_drive_file · 마지막 | 〃 |
|
||||
| 15 | 26:11507 | 복구하기 | 〃 | rotate_left · 첫 버튼 | 〃 |
|
||||
| 16 | 26:11529 | 임시파일 | 〃 | file_copy · 마지막 | 〃 |
|
||||
| 17 | 26:11542 | 디스플레이 | 〃 | monitor 24×22 · 첫 버튼. 라벨 "Display..."(원문에서 이미 말줄임) | 〃 |
|
||||
| 18 | 26:11558 | 저장하기 | 〃 | 설정 저장 · 중간. gap 2 | 〃 |
|
||||
| 19 | 26:11586 | 불러오기 | 〃 | save_alt · 마지막 | 〃 |
|
||||
| 20 | 26:11599 | 설명보기 | 〃 | content_paste · 첫 버튼 | 〃 |
|
||||
| 21 | 26:11615 | 문의하기 | 〃 | question_answer · 중간 | 〃 |
|
||||
| 22 | 26:11631 | 소프트웨어 정보 | 〃 | 라벨 "S/W정보" · 마지막 | **V3가 button이 아니고 Default로 돌아가는 reaction 없음** |
|
||||
|
||||
## 2. 속성Bar 차이표 (Figma 26:10492 ↔ 현재 구현·Storybook)
|
||||
|
||||
비교 기준은 `packages/sw/src/theme/sw.css`(`.hm-sw-property-bar*`, `.hm-sw-header__input`), `SWHeaderPropertyBar.tsx`, Storybook `SW/Header/Overview` 실측입니다. **실제 차이가 있는 항목만** 적었습니다. 수정 위치는 모두 기존 공유 파일인 `sw.css`이므로 **통합 단계 항목**입니다.
|
||||
|
||||
| # | 항목 | Figma | 현재 구현 / Storybook | 영향 | 수정 위치 |
|
||||
|---|---|---|---|---|---|
|
||||
| P1 | 테두리 색 | 좌·우·하 1px **세로 gradient**(`--gra-border-green`): `#08251C` 0% → `#208769` 37.1% → `#08241C` 80.5% → `#051612` 100%(위→아래) | 단색 `1px solid #08251C`. Storybook computed도 `rgb(8,37,28)` 단색 | 중간 높이의 초록 하이라이트(`#208769`)가 없음 | **통합 단계** `sw.css` `.hm-sw-property-bar`(gradient border는 기존 hover border 기법 재사용 검토) |
|
||||
| P2 | 객체명 글자 외곽선 | 검정 1.3px Stroke + `#FFC600` 0.3px. 기존 정책상 검정 외곽선만 적용 | `text-shadow: none`. 외곽선 목록(`sw.css` 약 1472–1477행)에 `.hm-sw-property-bar__object`가 없음 | 노란 글자 가장자리가 Figma보다 흐림 | **통합 단계** `sw.css`, `--hm-sw-header-text-outline` 적용 |
|
||||
| P3 | 라벨 글자 외곽선 | 14px 라벨에 검정 1px Stroke | `text-shadow: none` | 라벨 대비가 약함 | **통합 단계** `sw.css`, 1px 외곽선(`--hm-sw-header-label-outline`) 적용 검토 |
|
||||
| P4 | 라벨 박스 폭 | 고정 텍스트 박스(반지름 46, 지름·원주·면적 30), 가운데 정렬. 박스 안 좌 1.4–2.1px, 우 1.8–2.8px 여백 | 자연 폭(예: "원주" 26.9px). 라벨→입력 간격이 Figma보다 약 2–3px 좁음. 콘텐츠 폭은 527.6px로 Figma 538px보다 짧음 | 글꼴 대체(Dotum → Noto Sans KR) 영향과 섞여 있음 | **검토 대상**(§5 R-P1). 고친다면 통합 단계 `sw.css` |
|
||||
| P5 | 세로 위치 | 내용 Frame top 4px(20px 행) | 실측 top 3.5px(bar 875.2 → field 878.7, 27px 영역 가운데 정렬) | 0.5px | **검토 대상**(§5 R-P2) |
|
||||
|
||||
**의도된 차이(수정하지 않음):** 바 폭은 Figma 1222px 고정 대신 콘텐츠 폭입니다(2026-10-19 사용자 확정). 글꼴은 Dotum Regular + Stroke 대신 Noto Sans KR이며 객체명·값은 700입니다(기존 Typography 정책). 입력 폭은 Figma 고정 58/66px 대신 값 길이를 따릅니다. 필드 순서와 객체명 문구는 Story fixture 데이터입니다.
|
||||
**일치(차이 없음):** 높이 28, 배경 `#271F13`, 아래 radius 6, inset shadow `0 4 4 rgba(0,0,0,.6)`, 객체명 90px·`#FFC600`, 필드 간격 16·라벨-입력 6, 라벨 `#F6F4F2`·자간 4%, 입력 `#E60403`·자간 2%·padding 2/4/2/6·radius 2. 입력 상태 Default(면 없음) / hover(24px, 1px .25) / on(`#0F0D0A`, 1px .3)과 hover reaction도 같습니다. 아이콘은 Figma와 구현 모두 없습니다.
|
||||
**미검증:** 1920px 폭 실측, Hover·편집 상태 Storybook 실측, P1–P3를 고친 뒤의 렌더 비교.
|
||||
|
||||
## 3. 로고 클릭 화면 분석
|
||||
|
||||
### 3.1 화면 구성 (26:10591 기준, 1920 디자인)
|
||||
|
||||
```
|
||||
y 0 ─ border-top 2px #5F4B0F ───────────────────────────────────────────────────
|
||||
[btn_home 80] (기존 Header가 비쳐 보임: 문서 제목 등) [섹션 5개, x377–1678] [창 영역 240: 인쇄·K pill | 창이동 | – | ×]
|
||||
y 46 ─ border-bottom 2px #5F4B0F ───────────────
|
||||
y 47 ─ 하단 강조 strip 30px(섹션 뒤, 아래로 보이는 주황 gradient)
|
||||
y 77 ─ 메뉴 끝(Header 48px보다 29px 아래로 내려옴)
|
||||
```
|
||||
|
||||
- **Overlay**: 로고 클릭 시 `home on` 전체가 기존 Header 위에 TOP_LEFT로 겹칩니다(배경 없음). `bg black`과 `document_tit`가 숨겨져 있어 그 부분은 기존 Header가 그대로 보입니다. 즉 **새로 그려지는 것은 섹션 메뉴(x375–1680)이고**, 홈·창 영역은 기존 Header와 같은 컴포넌트 사본입니다.
|
||||
- **섹션**(5개): 제목 badge(19px, Union 도형) + 버튼 그룹 box(46px) + 섹션 면(75px). 섹션 폭 = 버튼 수 × 80 + 좌우 padding(10, 첫·마지막 섹션은 한쪽 11)입니다.
|
||||
- 작업중인 파일: 저장 · 복사저장 · 내보내기 · 도면묶기
|
||||
- 다른파일 열기: 찾아열기 · 최근파일 · 새파일
|
||||
- 파일 손상시: 복구하기 · 임시파일
|
||||
- My Setting: Display... · 저장하기 · 불러오기
|
||||
- 궁금하면: 설명보기 · 문의하기 · S/W정보
|
||||
- **버튼**: 80×46, 아이콘 18px(31,6), 라벨 14px(top 25). 마지막 버튼을 뺀 버튼 오른쪽에 1×44 gradient 구분선이 있습니다.
|
||||
|
||||
### 3.2 공통 영역과 화면별 차이
|
||||
|
||||
| 구분 | 내용 |
|
||||
|---|---|
|
||||
| 공통(두 화면 동일) | 섹션 5개·버튼 15개·제목·구분선·면 gradient, 홈 버튼, 창 영역(인쇄·K·창이동·최소·종료), border-top/bottom `#5F4B0F` |
|
||||
| 26:10591만 | 하단 강조 = 섹션별 사각형 5개(`#FEC501` + `#5F4B0F → #FF5100` gradient), btn_home 클릭 → open, 메뉴 클릭 → close |
|
||||
| 662:67267만 | 하단 강조 = 메뉴 전체 1305×30에 2px gradient stroke(`#FF5100 → #5F4B0F`, fill 숨김), 팝업가이드 Variant5(K 미리보기 209×186, "다중인쇄/도면관리") 표시, open/close reaction 없음, btn_home 배경 asset이 다름 |
|
||||
| 버튼 세트 공통 | Default / Variant2 / Variant3. **V2와 V3는 시각 속성이 같습니다**(Plugin API로 하위 노드 전체를 비교했고 위치만 다름) |
|
||||
| 버튼별 차이 | 아이콘 asset과 위치(찾아열기 top 5, Display 24×22), 라벨 박스 폭(33–70), 그룹 내 위치에 따른 hover radius(첫 3 0 0 3 / 중간 0 / 마지막 0 3 3 0), 구분선 유무(마지막 없음), 찾아열기·최근파일만 구분선 gradient가 다름, 저장하기·S/W정보만 flex gap 2 |
|
||||
|
||||
### 3.3 상태
|
||||
|
||||
| 상태 | Figma 근거 | 시각 |
|
||||
|---|---|---|
|
||||
| Default | 각 세트 Default | 면 없음, 아이콘 회색, 라벨 `#F6F4F2` + 검정 외곽선 |
|
||||
| Hover | Default ON_HOVER → V2 | 1px INSIDE 주황 세로 gradient 테두리(`#DA6813` 8.5% / `#FFBB89` 37.9% / `#E5690D` 71.9% / `#AE3607` 92.8%), 아이콘·라벨 `#FF5C00`, 아이콘·라벨 1px 위·왼쪽 이동 |
|
||||
| Pressed / 선택(?) | V2 ON_CLICK → V3, V3 ON_CLICK → Default | V2와 동일. 의미는 미정(§5 R-3) |
|
||||
| Open / Close | btn_home ON_CLICK → OVERLAY, 메뉴 영역 ON_CLICK → CLOSE | — |
|
||||
| Focus / Disabled / 키보드 | **Figma에 없음** | §5 검토 대상 |
|
||||
|
||||
### 3.4 전환 관계 (Prototype)
|
||||
|
||||
```
|
||||
[기존 Header] --btn_home click--> [home on overlay 26:10591]
|
||||
[home on] --메뉴 영역(Group 2882) click--> CLOSE → [기존 Header]
|
||||
버튼: Default --hover--> V2 --click--> V3 --click--> Default (최근파일: V2에서 끝, S/W정보: V3에서 끝)
|
||||
팝업가이드: Default --hover--> Variant5(K 미리보기) ※ 기존 SWHeaderKGuide와 같은 대상
|
||||
```
|
||||
|
||||
Figma에는 메뉴 밖 클릭, Escape, 로고 재클릭으로 닫기, 각 버튼의 실제 동작(파일 대화상자, 최근 파일 목록 등)이 정의되어 있지 않습니다(§5).
|
||||
|
||||
## 4. 공통 컴포넌트 구조·공개 API 초안
|
||||
|
||||
### 4.1 재사용 우선순위 판단
|
||||
|
||||
| 단계 | 후보 | 판단 |
|
||||
|---|---|---|
|
||||
| 기존 SW | `SWHeaderHome`(`onHomeClick`), `SWHeaderKGuide`, 창 제어, `useSWHeaderDropdown`의 닫기 계약(Escape·바깥 클릭·Tab 이탈·Focus 복귀), 텍스트 외곽선·focus 토큰, `SWTheme` | **재사용.** Header 쪽은 그대로 두고 메뉴만 새로 만듭니다. 홈·창 영역을 다시 그리지 않습니다 |
|
||||
| 작업 A 계획 | `SWToolButton`(pill/text/document), `SWToolGroup`(selection), `SWPopup*` | §4.4 참고. 그대로 쓸 수 있는 appearance가 없습니다 |
|
||||
| shadcn / Radix | Popover, NavigationMenu | `@hm-design/sw`는 `@hm-design/ui`·Radix에 의존하지 않습니다(독립 패키지). 새 의존성은 **별도 승인 대상**이므로 기존 SW hook 방식을 따릅니다 |
|
||||
| 최소 Custom | 섹션 badge, 그룹 box, 80×46 타일 버튼 | 시각 요소만 새로 만듭니다(Figma 근거 있음) |
|
||||
|
||||
### 4.2 구조
|
||||
|
||||
```
|
||||
<SWHomeMenu> overlay 패널(region/dialog 역할은 §5 R-6), open 상태·닫기 계약
|
||||
<SWHomeMenuSection title> 섹션 면 + 제목 badge(폭은 글자에 맞춤) + 그룹 box
|
||||
<SWHomeMenuItem icon label /> 80×46 타일 button. 마지막 항목은 구분선 자동 생략, 첫/마지막 hover radius 자동
|
||||
```
|
||||
|
||||
데이터형(`sections`)과 조합형(`children`) 중 하나만 써도 21개를 모두 표현할 수 있습니다. 위치·첫/마지막 여부·구분선·섹션 폭은 **항목 수에서 자동 계산**합니다. 따라서 AI가 항목만 추가해도 같은 스타일이 적용됩니다.
|
||||
|
||||
### 4.3 공개 API 초안
|
||||
|
||||
```ts
|
||||
export interface SWHomeMenuItemData {
|
||||
id: string
|
||||
label: string // 한 줄, 넘치면 말줄임 + title (기존 규칙)
|
||||
icon?: ReactNode // currentColor SVG 권장: Default 회색 → Hover #FF5C00을 CSS가 처리
|
||||
iconHover?: ReactNode // 색 고정 이미지 쌍일 때만(기존 close/close-hover 방식)
|
||||
disabled?: boolean // 시각은 검토 대상(R-4)
|
||||
hidden?: boolean // 미렌더, 자리 없음(작업 A 숨김 정책과 맞춤)
|
||||
title?: string // tooltip/전체 이름
|
||||
"aria-label"?: string
|
||||
closeOnSelect?: boolean // 기본 true (Figma: 메뉴 클릭 → CLOSE)
|
||||
onSelect?: (item: SWHomeMenuItemData) => void
|
||||
}
|
||||
export interface SWHomeMenuSectionData {
|
||||
id: string
|
||||
title: string
|
||||
items: readonly SWHomeMenuItemData[]
|
||||
hidden?: boolean
|
||||
}
|
||||
export interface SWHomeMenuProps extends Omit<HTMLAttributes<HTMLDivElement>, "onSelect"> {
|
||||
sections?: readonly SWHomeMenuSectionData[] // 또는 children(<SWHomeMenuSection>)
|
||||
open?: boolean // controlled
|
||||
defaultOpen?: boolean
|
||||
onOpenChange?: (open: boolean) => void
|
||||
onItemSelect?: (item: SWHomeMenuItemData, section: SWHomeMenuSectionData) => void
|
||||
/** 닫힐 때 Focus를 돌려줄 대상(기본: 연결된 홈 버튼). 통합 단계에서 SWHeader가 자동 연결 */
|
||||
returnFocusRef?: RefObject<HTMLElement>
|
||||
}
|
||||
export function SWHomeMenu(props: SWHomeMenuProps): JSX.Element
|
||||
export function SWHomeMenuSection(props: { title: string; children: ReactNode; className?: string }): JSX.Element
|
||||
export function SWHomeMenuItem(props: ButtonHTMLAttributes<HTMLButtonElement> & { label: string; icon?: ReactNode; iconHover?: ReactNode }): JSX.Element
|
||||
```
|
||||
|
||||
- **텍스트·아이콘·메뉴·이벤트·상태는 모두 외부에서 지정합니다.** 패키지에는 CAD 동작이 없습니다. Figma의 15개 메뉴는 Storybook fixture(또는 선택형 기본 아이콘 세트, §6 A-2)로만 제공합니다.
|
||||
- **독립 단계 연결 방식(SWHeader 무수정):** `<SWHeader onHomeClick={() => setOpen(v => !v)} />`와 `<SWHomeMenu open onOpenChange />`를 같은 상대 위치 컨테이너 안에 둡니다. 위치는 host가 `style`로 지정합니다.
|
||||
- **통합 단계 연결 방식(안):** `SWHeader`에 `homeMenu?: SWHomeMenuProps | ReactNode`를 추가합니다. 홈 버튼에 `aria-expanded`/`aria-controls`를 붙이고, 객체 영역 시작 x에 앵커하며, 열림 중 홈 버튼 상태를 표시합니다. 작업 A의 `leading` 슬롯·파일 열기 배치와 같은 파일을 수정하므로 통합 단계에서만 합니다.
|
||||
- **CSS:** 새 파일 `packages/sw/src/theme/sw-home-menu.css`에 `.hm-sw-home-menu*`만 둡니다. 새 토큰은 `--hm-sw-home-menu-*`이고, 기존 `--hm-sw-header-text-outline`·`--hm-sw-header-label-outline`·`--hm-sw-header-focus-color`·font 토큰은 그대로 읽습니다. 전역 선택자와 Dark 분기는 두지 않습니다. 단독 사용 시 `<SWTheme>` 안에서 렌더합니다.
|
||||
- **Font Weight:** 버튼 라벨 700(기존 "값·제목·토글 700"과 Figma 'Dotum/bold' 명칭), 제목 badge 12px 400(Stroke 없는 검정 글자)을 제안합니다. 600은 쓰지 않습니다. 최종값은 §5 R-5에서 확인합니다.
|
||||
|
||||
### 4.4 작업 A와 겹치는 부분
|
||||
|
||||
| 작업 A 항목 | 겹침 | 제안 |
|
||||
|---|---|---|
|
||||
| `SWToolButton` appearance pill/text/document | 80×46 세로 아이콘+라벨 타일은 승인된 appearance에 없습니다 | B는 `SWHomeMenuItem`을 독립으로 만듭니다. 통합 단계에서 `SWToolButton appearance="tile"`로 합칠지 정합니다(**별도 승인 대상** A-1) |
|
||||
| `SWToolGroup` selection="single" | 메뉴에는 선택 상태가 없습니다(V3 의미 미정) | 사용하지 않습니다. 키보드 이동 규칙만 맞춥니다 |
|
||||
| `SWPopup`/`Section`/`Row` | 밝은 속성창 Popup과 시각·역할이 다릅니다 | 재사용하지 않습니다. 이름이 비슷한 `Section`은 namespace(`SWHomeMenuSection`)로 구분합니다 |
|
||||
| `hiddenControls`·`show*` | 항목 숨김 개념이 같습니다 | 항목·섹션 `hidden`(미렌더, 자리 없음)으로 A와 같은 의미를 씁니다 |
|
||||
| `documentOpenPlacement`, 문서 New/Open | "찾아열기·새파일"이 `onDocumentOpen`/`onDocumentNew`와 중복될 수 있습니다 | **검토 대상**(R-7). 컴포넌트는 콜백만 받습니다 |
|
||||
| `leading` 슬롯·파일 열기 배치·반응형 | 메뉴 시작 x(Figma 375 = 문서 제목 끝)가 바뀝니다 | 통합 단계에서 실제 DOM 위치로 앵커합니다(x 하드코딩 금지) |
|
||||
| 속성Bar(`.hm-sw-property-bar`) | 같은 49px 아래 overlay 영역을 씁니다(메뉴는 77px까지) | z-index·동시 표시는 R-8 |
|
||||
|
||||
## 5. 검토 대상 (Figma에 없는 동작 — 확정하지 않음)
|
||||
|
||||
| ID | 항목 | 제안(미확정) |
|
||||
|---|---|---|
|
||||
| R-1 | 메뉴 닫기: 바깥 클릭·Escape·로고 재클릭·Tab 이탈 | 기존 SW Dropdown 계약과 같게 처리하고 Focus는 홈 버튼으로 복귀 |
|
||||
| R-2 | 항목 클릭 후 닫힘 | Figma CLOSE를 따라 기본 닫힘, `closeOnSelect`로 변경 |
|
||||
| R-3 | V3(클릭 후)의 의미: 누름(pressed) 순간 표시인지, 유지되는 선택 상태인지. 최근파일·S/W정보의 reaction 누락 | 누름(`:active`) 표시로만 구현하고 유지 상태는 만들지 않음 |
|
||||
| R-4 | Disabled·Focus-visible 시각 | 기존 SW 규칙(opacity .4, focus 2px `--hm-sw-header-focus-color`) |
|
||||
| R-5 | 라벨·제목 Font Weight(700/400), Stroke 적용 범위 | §4.3 제안 |
|
||||
| R-6 | 접근성 역할: `role="menu"` + 화살표 이동 vs region + button + Tab | button 목록 + 섹션 `aria-labelledby`, 화살표 이동은 작업 A 규칙과 맞춤 |
|
||||
| R-7 | 찾아열기·새파일과 기존 `onDocumentOpen`/`onDocumentNew` 관계, 최근파일 하위 목록 여부 | 컴포넌트는 콜백만 받고, 연결은 host 또는 통합 단계에서 결정 |
|
||||
| R-8 | 속성Bar·Dropdown·K 미리보기와의 z-index와 동시 표시 | 메뉴가 열리면 다른 Dropdown을 닫고 속성Bar보다 위에 표시 |
|
||||
| R-9 | 1920 미만 반응형(메뉴 1305px) | 가용 폭 안 내부 가로 스크롤(속성Bar 정책과 동일). 섹션 접기·줄바꿈은 새 디자인이 필요해 A-4로 분리 |
|
||||
| R-10 | 하단 강조 strip 두 가지(섹션별 vs 전체 2px) 중 기준 | 기본 디자인 컴포넌트인 26:10591(섹션별) |
|
||||
| R-11 | home on의 border-top/bottom `#5F4B0F`와 홈 버튼 배경 asset이 기존 Header와 다른지 | 2단계에서 기존 `home-bg*.svg`·Header 선과 대조한 뒤 보고 |
|
||||
| R-P1 | 속성Bar 라벨 고정 박스 여백(P4) | 글꼴 대체 영향이라 유지 권장 |
|
||||
| R-P2 | 속성Bar 세로 0.5px(P5) | 유지 권장 |
|
||||
|
||||
## 6. 별도 승인 대상 (새 디자인 판단 또는 승인 범위 초과)
|
||||
|
||||
| ID | 항목 | 이유 |
|
||||
|---|---|---|
|
||||
| A-1 | `SWToolButton`에 `tile` appearance 추가 또는 `SWHomeMenuItem` 별도 유지 | 작업 A에서 확정된 공개 API 변경 |
|
||||
| A-2 | Figma 메뉴 아이콘 15개(+hover 15개)를 패키지 기본 asset으로 제공할지, Story fixture로만 둘지 | 공개 asset·번들 크기 증가 |
|
||||
| A-3 | `SWHeader`에 `homeMenu` prop·홈 버튼 열림 상태 추가 | 기존 Header API·스타일 변경(통합 단계) |
|
||||
| A-4 | 좁은 폭에서 섹션 접기·More 이동 등 새 반응형 디자인 | Figma에 없는 디자인 |
|
||||
| A-5 | Radix 등 새 dependency 사용 | 현재 `@hm-design/sw`에는 런타임 의존성이 없음 |
|
||||
| A-6 | 속성Bar P1–P3 수정 | 기존 공유 CSS 변경이며, 사용자 확정 디자인 범위 안인지 확인 필요 |
|
||||
|
||||
## 7. 파일 경계
|
||||
|
||||
| 구분 | 파일 | 내용 |
|
||||
|---|---|---|
|
||||
| **B 독립(신규만)** | `packages/sw/src/components/sw-home-menu/**` | `SWHomeMenu.tsx`, `SWHomeMenuSection.tsx`, `SWHomeMenuItem.tsx`, `types.ts`, `index.ts`, (A-2 승인 시) `assets/` |
|
||||
| | `packages/sw/src/theme/sw-home-menu.css` | `.hm-sw-home-menu*` 스타일·토큰 |
|
||||
| | `src/sw/sw-home-menu.stories.tsx` | Figma 21개 재현, 단독, 동적 데이터, Header 조합(host wiring) Story. Story CSS는 새 파일만 |
|
||||
| | `docs/sw/figma-additional/**` | 이 보고서, 수집 자료, 2단계 검증 기록 |
|
||||
| **통합 단계로 미룸** | `packages/sw/src/index.ts`, 테마 진입 CSS(`sw.css` import 체인) | export·CSS 연결(2단계 Story는 상대 경로 import로 확인) |
|
||||
| | `packages/sw/src/components/sw-header/**`, `packages/sw/src/theme/sw.css` | `homeMenu` 연결(A-3), 속성Bar P1–P3(A-6) |
|
||||
| | `build.mjs`, `package.json` | asset·CSS 번들 포함 |
|
||||
| | `packages/sw/ai/**`, `packages/sw/README.md`, `docs/sw/sw-header-*.md` | AI 가이드·카탈로그·사용법. 2단계에서는 초안을 `docs/sw/figma-additional/`에 둠 |
|
||||
| | 기존 stories(`src/sw/sw-header*.stories.tsx`, `sw-header-overview.tsx`, `sw-stories.css`) | 수정하지 않음 |
|
||||
|
||||
## 8. 2단계 구현·검증 계획 (승인 후)
|
||||
|
||||
**구현 순서**
|
||||
1. Asset 재수집(만료 전) → A-2 결정에 따라 경로 확정 → 섹션 badge·그룹 box·구분선·타일 CSS
|
||||
2. `SWHomeMenuItem` → `SWHomeMenuSection` → `SWHomeMenu`(open 계약, R-1·R-2 결정 반영)
|
||||
3. Story `SW/Home Menu`: Figma 재현, 단독, 동적, Header 조합
|
||||
4. AI 가이드 초안(`docs/sw/figma-additional/ai-home-menu-draft.md`) → 통합 단계에서 `packages/sw/ai/`로 이동
|
||||
|
||||
**검증 범위**(실행하지 않은 항목은 미검증으로 기록)
|
||||
|
||||
| 범위 | 방법 | 기준 |
|
||||
|---|---|---|
|
||||
| Figma 비교 | 1920 viewport, Figma 스크린샷과 같은 배율 비교 + 요소 실측 | 섹션 x·폭(340/260/180/260/261), 높이 75·46·19, 색·gradient·shadow |
|
||||
| 21개 재현 | 제목 5개(참고 1개 포함), 버튼 15개 × Default/Hover/Pressed 상태 Story | 노드별 체크리스트 |
|
||||
| 단독 사용 | `<SWTheme>` 안 `SWHomeMenu`만 렌더 | Header 없이 동작·스타일 유지, 전역 CSS 누수 없음 |
|
||||
| 동적 콘텐츠 | 항목 추가·삭제·숨김, 긴 라벨, 아이콘 없음, 섹션 1개·6개, 비동기 갱신 | 폭·구분선·radius 자동 계산, 말줄임 |
|
||||
| Header/Popup 조합 | `onHomeClick` host wiring, 속성Bar·Dropdown·K 가이드 동시 상태 | 닫기 계약, Focus 복귀, z-index(R-8) |
|
||||
| 반응형 | 1920/1440/1280/1080/900/768/640/390 | 가로 넘침 없이 내부 스크롤(R-9) |
|
||||
| Storybook | 기존 SW Story 회귀 없음 | 기존 Story 무수정 확인 |
|
||||
| AI 가이드 | 문서만으로 새 메뉴 항목·섹션 생성 시나리오 1건 | 임의 값 없이 같은 스타일 |
|
||||
| 독립 React 19 소비 프로젝트 | 통합 단계 build 후 packed tarball 설치 | 타입·CSS·asset 로드. **dist·export 연결이 통합 단계라 2단계에서는 미검증 예정** |
|
||||
@@ -0,0 +1,115 @@
|
||||
{
|
||||
"source": {
|
||||
"fileKey": "rj6wpsfk24lOWvWiVRAANP",
|
||||
"collectedAt": "2026-10-19",
|
||||
"tools": "공식 Figma MCP get_metadata / get_design_context / get_variable_defs / get_screenshot + 읽기 전용 use_figma(Plugin API: reactions, fills, strokes, effects, fonts)",
|
||||
"page": "2. 헤더 (SECTION) — 속성Bar만 header_green 26:10332의 'Property 1=선택한것' 26:10491 하위",
|
||||
"note": "get_design_context의 React/Tailwind 원문은 화면 재현용 참고 코드라 그대로 저장하지 않고, 구현에 필요한 값만 아래에 정규화했습니다. 원문은 같은 node id로 다시 받을 수 있습니다. 응답에 들어 있던 asset URL은 2026-10-26에 만료되므로 2단계에서 다시 받아야 합니다. 스크린샷은 screenshots/<node>.png (1x)입니다."
|
||||
},
|
||||
"nodes": [
|
||||
{
|
||||
"id": "26:10492", "name": "속성Bar", "type": "FRAME", "size": [1222, 28],
|
||||
"screenshot": "screenshots/26-10492_property-bar.png",
|
||||
"variables": { "--accent-yellow": "#FFC600", "--text-brown100": "#F6F4F2", "--text-red": "#E60403", "--gra-border-green": "(gradient, 값 없음)" },
|
||||
"metadata": "Rectangle 7541 1222x28 + Frame(top 4, h 20) = 객체명 Dotum_14_bold 90x16 + Frame(gap 16) x4 [라벨 Dotum_regular_14 (46|30)x16 + gap 6 + input 58|66x20]",
|
||||
"designContext": {
|
||||
"background": "#271F13",
|
||||
"border": "좌·우·하 1px (상 0) GRADIENT_LINEAR 위→아래 #08251C 0% / #208769 37.1% / #08241C 80.5% / #051612 100%",
|
||||
"radius": "0 0 6 6",
|
||||
"effect": "INNER_SHADOW 0 4 4 rgba(0,0,0,.6)",
|
||||
"object": "90px 가운데 정렬, Dotum Regular 14 / 4%, #FFC600, 아래층 Stroke 검정 1.3px + 위층 #FFC600 0.3px",
|
||||
"label": "Dotum Regular 14 / 4%, #F6F4F2, Stroke 검정 1px, 고정 폭 텍스트 박스(46 또는 30) 가운데 정렬",
|
||||
"fieldGap": 16, "labelInputGap": 6,
|
||||
"input": "component set 'input'(26:11656) Default 26:11657 58x20 pad 2/4/2/6 r2 면·테두리 없음 / hover 26:11659 58x24 1px rgba(212,203,189,.25) / on 26:11661 58x24 #0F0D0A + 1px .3. 값 Dotum Regular 14 / 2%, 검정 1.3 + #E60403 0.3"
|
||||
},
|
||||
"reactions": ["input 4개: ON_HOVER → CHANGE_TO input hover(26:11659)"]
|
||||
},
|
||||
{
|
||||
"id": "26:10591", "name": "헤더/꾸러미 ver2/home on", "type": "COMPONENT", "size": [1920, 77],
|
||||
"screenshot": "screenshots/26-10591_home-on.png",
|
||||
"role": "로고(btn_home) 클릭 시 열리는 overlay 자체(TOP_LEFT, 배경 없음, 바깥 클릭 상호작용 없음)",
|
||||
"variables": { "--text-brown100": "#F6F4F2", "--gra-border-brown1": "(gradient)", "--gra-border-brown2": "(gradient)", "--accent-border-yellow": "(gradient)" },
|
||||
"hiddenLayers": ["bg balck 1920x48", "Rectangle 6671 310x48", "header/document_tit 310x48"],
|
||||
"metadata": "창 영역 240x48(x1680) · 최소/종료 32x48 x2 · pill(인쇄 + 팝업가이드) 85x42 · 창이동 motion 88x48 · border-top/bottom 1920x2 #5F4B0F · btn_home 80x48 · Group 2882 x375 1305x77 = 하단 강조 strip(y47, 30px, 섹션별 사각형 5개) + 섹션 5개(x377 1301x75)",
|
||||
"sections": [
|
||||
{ "frame": "Frame 2408", "width": 340, "padding": "5 10 5 11", "title": "tit_작업중인파일 26:12604 124x19", "group": "g_작업중인파일 320x46", "items": ["btn_저장 26:11355", "복사저장 26:11371", "내보내기 26:11387", "도면묶기 26:11403"] },
|
||||
{ "frame": "Frame 2409", "width": 260, "padding": "5 10", "title": "tit_다른파일열기 26:12610 123x19", "group": "g_다른파일열기 240x46", "items": ["찾아열기 26:11416", "최근파일 26:11450", "새파일 26:11485"] },
|
||||
{ "frame": "Frame 2410", "width": 180, "padding": "5 10", "title": "tit_파일손상시 26:12622 111x19", "group": "g_파일손상시 160x46", "items": ["복구하기 26:11507", "임시파일 26:11529"] },
|
||||
{ "frame": "Frame 2411", "width": 260, "padding": "5 10", "title": "tit_마이세팅 26:12628 107x19 (요청 목록 밖, 'My Setting')", "group": "Frame 2407 240x46", "items": ["디스플레이 26:11542", "저장하기 26:11558", "불러오기 26:11586"] },
|
||||
{ "frame": "Frame 2412", "width": 261, "padding": "5 10 5 11", "title": "tit_궁금하면 26:12616 95x19", "group": "g_궁금하면 240x46", "items": ["설명보기 26:11599", "문의하기 26:11615", "소프트웨어 정보 26:11631"] }
|
||||
],
|
||||
"designContext": {
|
||||
"section": "h75, 1px #081813, radius 0 0 3 3, fill 2중 gradient(세로 #18150E 0 / #262015 21.3% / #332B1C 47% / #38301F 75% / #18150E 100% + 가장자리 어둡게 #16120A 0 / 투명 54.5% / #16120A 100%), 안쪽 1px #20190E 사각형",
|
||||
"group": "h46, 1px GRADIENT stroke(#886D35 0 / #423625 8.8% / #F3DBA8 26.2% / #0E0B06 83.8% / #574B30 100%), radius 3, fill gradient(#19160E 2% / #38301F 43.7% / #463C29 67.7% / #2F281A 100%), DROP_SHADOW 0 2 radius 4 rgba(0,0,0,.6)",
|
||||
"title": "Union 도형(위아래 반전) 19px, fill gradient #887757 → #A8997C 75.6% → #C3B79E, stroke 1px #7B6332 → #9F8D68, DROP_SHADOW 0 -2 radius 4 rgba(0,0,0,.4), 글자 Dotum Regular 12 / 2% #000 가운데, group 위에 -1px 겹침",
|
||||
"bottomStrip": "y47 h30, 섹션별 사각형(343/263/181/262/264, -2px 겹침, radius 0 0 3 3) fill #FEC501 + GRADIENT #5F4B0F → #FF5100",
|
||||
"headerLines": "border-top 0-2px, border-bottom 46-48px, #5F4B0F 1920px"
|
||||
},
|
||||
"reactions": [
|
||||
"btn_home 26:10652 ON_CLICK → OVERLAY 26:10591",
|
||||
"Group 2882(메뉴 전체) ON_CLICK → CLOSE",
|
||||
"메뉴 버튼 15개 ON_HOVER → 각 Variant2",
|
||||
"header/btn_최소최대화·btn_종료 ON_HOVER → hover",
|
||||
"Component 743(인쇄) ON_HOVER → hover 26:224",
|
||||
"꾸러미ver2/팝업가이드 ON_HOVER → Variant5 26:426 (209x186, SMART_ANIMATE)",
|
||||
"창이동 motion ON_HOVER → 26:10258 SMART_ANIMATE",
|
||||
"header/document_tit(숨김) MOUSE_ENTER → OVERLAY 26:10667 hover"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "662:67267", "name": "헤더/꾸러미 ver2/home on", "type": "FRAME", "size": [1920, 77], "renderBounds": [1935, 193],
|
||||
"screenshot": "screenshots/662-67267_home-on-popup-guide.png",
|
||||
"role": "26:10591과 같은 구성의 독립 Frame. 팝업가이드가 Variant5(K 미리보기 209x186) 상태이고 하단 강조가 다름",
|
||||
"diffFrom_26:10591": [
|
||||
"하단 strip: 섹션별 사각형 4개 숨김, Rectangle 6991만 1305x30, fill(#FF5100) 숨김 + 2px GRADIENT stroke #FF5100 → #5F4B0F",
|
||||
"꾸러미ver2/팝업가이드 = Variant5 (검정 미리보기, '다중인쇄/도면관리')",
|
||||
"btn_home 클릭·메뉴 CLOSE reaction 없음 (정적 상태 화면)",
|
||||
"btn_home 배경 asset이 26:10591과 다른 파일(a80fd vs 9931a)"
|
||||
],
|
||||
"reactions": ["창 버튼·인쇄·창이동·메뉴 버튼 15개 ON_HOVER만 (26:10591과 같은 대상의 다른 사본 id)"]
|
||||
},
|
||||
{ "id": "26:12604", "name": "tit_작업중인파일", "type": "COMPONENT", "size": [124, 19], "text": "작업중인 파일", "textBox": [78, 14], "screenshot": "screenshots/26-12604_tit-working-file.png" },
|
||||
{ "id": "26:12610", "name": "tit_다른파일열기", "type": "COMPONENT", "size": [123, 19], "text": "다른파일 열기", "textBox": [78, 14], "screenshot": "screenshots/26-12610_tit-open-other.png" },
|
||||
{ "id": "26:12622", "name": "tit_파일손상시", "type": "COMPONENT", "size": [111, 19], "text": "파일 손상시", "textBox": [66, 14], "screenshot": "screenshots/26-12622_tit-file-damaged.png" },
|
||||
{ "id": "26:12616", "name": "tit_궁금하면", "type": "COMPONENT", "size": [95, 19], "text": "궁금하면", "textBox": [49, 14], "screenshot": "screenshots/26-12616_tit-curious.png" }
|
||||
],
|
||||
"titleWidthRule": "제목 폭 = 글자 폭 + 약 46px(좌우 약 23px). 5개 모두 같은 Union 도형을 폭만 늘린 것",
|
||||
"buttonSets": {
|
||||
"common": {
|
||||
"type": "COMPONENT_SET 120x218, variants 'Property 1' = Default / Variant2 / Variant3, 각 80x46",
|
||||
"default": "면 없음, 아이콘 18x18 at (31,6) 회색 #B6B4AF 계열 SVG, 라벨 Dotum Regular 14 / 4% #F6F4F2 + 검정 1.3 / 자기 색 0.3 Stroke, top 25 가운데",
|
||||
"variant2": "1px INSIDE GRADIENT stroke 위→아래 #DA6813 8.5% / #FFBB89 37.9% / #E5690D 71.9% / #AE3607 92.8%, 아이콘 hover SVG(#FF5C00), 라벨 #FF5C00, 아이콘·라벨 1px 위·왼쪽 이동(31,6→30,5 / top 25→24), radius = 그룹 첫 버튼 3 0 0 3, 마지막 0 3 3 0, 중간 0",
|
||||
"variant3": "Variant2와 시각 속성 동일(Plugin API 전체 하위 노드 비교 결과 위치 y만 다름)",
|
||||
"divider": "마지막 버튼을 뺀 버튼 오른쪽(-1px, hover -2px)에 1x44 세로선 GRADIENT(#886D35/#423625/#F3DBA8/#0E0B06/#574B30) + DROP_SHADOW -1 0 radius 2 #000",
|
||||
"prototype": "Default ON_HOVER → Variant2, Variant2 ON_CLICK → Variant3, Variant3 ON_CLICK → Default"
|
||||
},
|
||||
"items": [
|
||||
{ "set": "26:11355", "name": "btn_저장", "label": "저장", "icon": "save", "position": "first", "variants": ["26:11356", "26:11361", "26:11366"], "screenshot": "screenshots/26-11355_btn-save.png", "note": "Default에 숨김(visible=false) 노란 gradient stroke 존재" },
|
||||
{ "set": "26:11371", "name": "복사저장", "label": "복사저장", "icon": "save_as", "position": "middle", "variants": ["26:11372", "26:11377", "26:11382"], "screenshot": "screenshots/26-11371_btn-save-copy.png" },
|
||||
{ "set": "26:11387", "name": "내보내기", "label": "내보내기", "icon": "file_open", "position": "middle", "variants": ["26:11388", "26:11393", "26:11398"], "screenshot": "screenshots/26-11387_btn-export.png" },
|
||||
{ "set": "26:11403", "name": "도면묶기", "label": "도면묶기", "icon": "inventory_2", "position": "last", "variants": ["26:11404", "26:11408", "26:11412"], "screenshot": "screenshots/26-11403_btn-bundle.png" },
|
||||
{ "set": "26:11416", "name": "찾아열기", "label": "찾아열기", "icon": "folder_open (top 5)", "position": "first", "variants": ["26:11417", "26:11428", "26:11439"], "screenshot": "screenshots/26-11416_btn-find-open.png", "note": "구분선 gradient가 다른 버튼과 다름(0deg 갈색 #493927 계열)" },
|
||||
{ "set": "26:11450", "name": "최근파일", "label": "최근파일", "icon": "insert_drive_file(시계 포함)", "position": "middle", "variants": ["26:11451", "26:11462", "26:11473"], "screenshot": "screenshots/26-11450_btn-recent.png", "note": "구분선 gradient 2중(갈색 + 초록). Variant2 → Variant3 클릭 reaction 없음" },
|
||||
{ "set": "26:11485", "name": "새파일", "label": "새파일", "icon": "insert_drive_file", "position": "last", "variants": ["26:11486", "26:11493", "26:11500"], "screenshot": "screenshots/26-11485_btn-new-file.png" },
|
||||
{ "set": "26:11507", "name": "복구하기", "label": "복구하기", "icon": "rotate_left", "position": "first", "variants": ["26:11508", "26:11515", "26:11522"], "screenshot": "screenshots/26-11507_btn-recover.png" },
|
||||
{ "set": "26:11529", "name": "임시파일", "label": "임시파일", "icon": "file_copy", "position": "last", "variants": ["26:11530", "26:11534", "26:11538"], "screenshot": "screenshots/26-11529_btn-temp-file.png" },
|
||||
{ "set": "26:11542", "name": "디스플레이", "label": "Display...", "icon": "monitor 24x22 at (29,6)", "position": "first", "variants": ["26:11543", "26:11548", "26:11553"], "screenshot": "screenshots/26-11542_btn-display.png", "note": "라벨 박스 70px, Figma 원문 자체가 '...' 말줄임" },
|
||||
{ "set": "26:11558", "name": "저장하기", "label": "저장하기", "icon": "설정 저장(그룹 아이콘 16.3x16.5)", "position": "middle", "variants": ["26:11559", "26:11568", "26:11577"], "screenshot": "screenshots/26-11558_btn-setting-save.png", "note": "flex gap 2px" },
|
||||
{ "set": "26:11586", "name": "불러오기", "label": "불러오기", "icon": "save_alt", "position": "last", "variants": ["26:11587", "26:11591", "26:11595"], "screenshot": "screenshots/26-11586_btn-setting-load.png" },
|
||||
{ "set": "26:11599", "name": "설명보기", "label": "설명보기", "icon": "content_paste", "position": "first", "variants": ["26:11600", "26:11605", "26:11610"], "screenshot": "screenshots/26-11599_btn-help.png" },
|
||||
{ "set": "26:11615", "name": "문의하기", "label": "문의하기", "icon": "question_answer", "position": "middle", "variants": ["26:11616", "26:11621", "26:11626"], "screenshot": "screenshots/26-11615_btn-inquiry.png" },
|
||||
{ "set": "26:11631", "name": "소프트웨어 정보", "label": "S/W정보", "icon": "정보(Frame 2405)", "position": "last", "variants": ["26:11632", "26:11639", "26:11646"], "screenshot": "screenshots/26-11631_btn-sw-info.png", "note": "Variant3가 button 형태가 아니며 Default로 돌아가는 클릭 reaction 없음" }
|
||||
]
|
||||
},
|
||||
"supplementary": {
|
||||
"note": "요청 21개 밖, 2단계 재확인(2026-10-19)에서 찾은 노드. 같은 SECTION '2. 헤더'의 주석 '최근파일'(26:11784) 아래 662:67267과 함께 배치. 서로 연결된 prototype reaction 없음(drop ON_CLICK의 action 비어 있음).",
|
||||
"nodes": [
|
||||
{ "id": "662:67331", "name": "최근파일", "type": "FRAME", "size": [354, 750], "screenshot": "screenshots/662-67331_recent-files.png",
|
||||
"position": "662:67267 기준 x 808, y 77 (최근파일 타일 왼쪽 + 1, 메뉴 바로 아래)",
|
||||
"designContext": "frame 1px #DA6813 좌·우·하, radius 0 0 3 3 / drop 354x750 #27241D, 1px #27241D, radius 0 0 4 4, shadow 0 2 4 rgba(0,0,0,.25) + inset 0 4 8 rgba(0,0,0,.4) / row 'header/최근파일/list' 50px, px 8, gap 8 (배지 없는 행 px 45) / icon_dualmonitor 29x28 (84a46 왼쪽 노랑, 9967e 오른쪽 노랑) / 이름 Dotum 14 4% #F6F4F2 (Stroke 2층) / 경로 12 4% #D4CBBD 60% / Hover 행 #62533D + 이름 #FF5C00" },
|
||||
{ "id": "662:67471", "name": "파일출력 2뎁스", "type": "FRAME", "size": [310, 270], "screenshot": "screenshots/662-67471_file-preview-2depth.png",
|
||||
"position": "최근파일 오른쪽 +1, Hover 행 top +1",
|
||||
"designContext": "#62533D, 1px #62533D 상·우·하, radius 0 3 4 0, drop-shadow 0 2 1 rgba(0,0,0,.5), inset 2 0 2.5 rgba(0,0,0,.5), py 4 gap 4 / 경로 14px #FF5C00 (아래층 #D7D2B0) / image 918 310x244 (crop 158.67% x 191.87%, -25.97% / -52.46%)" }
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
# Task A + B 통합 계획 — 로고 클릭 2단 Header 메뉴 (초안, 2026-10-19)
|
||||
|
||||
> 상태: **미승인 계획**입니다. Task B는 신규 파일만 작성했고 아래 공유 파일은 아직 수정하지 않았습니다. 통합은 사용자 별도 승인 후 진행합니다.
|
||||
> 근거: [2단계 보고](./stage2-report.md), Task A 현황(읽기 전용 확인, 미커밋): SWHeader `leading` 슬롯, `documentOpenPlacement`, CSS 변수 `--hm-sw-header-leading-width`, `components/sw-tool/`.
|
||||
|
||||
## 1. 순서
|
||||
|
||||
1. Task A 변경을 먼저 확정한 뒤, 그 위에 Task B 신규 파일을 merge합니다. B 파일은 A 파일과 겹치지 않습니다.
|
||||
2. export 연결 → 3. SWHeader 연결 → 4. 속성Bar 수정(선택) → 5. 문서·AI → 6. 검증.
|
||||
|
||||
## 2. 작업 목록과 적용 방법
|
||||
|
||||
| # | 파일 | 변경 | 적용 방법 |
|
||||
|---|---|---|---|
|
||||
| I-1 | `packages/sw/src/index.ts` | 공개 export | `export * from "./components/sw-home-menu"` 한 줄. CSS는 `SWHomeMenu.tsx`·`SWRecentFiles.tsx`에서 import하므로 index에 CSS import를 추가하지 않습니다(`sideEffects` 때문에 index에서만 import하면 빌드에서 빠지는 것을 확인함) |
|
||||
| I-2 | `components/sw-header/types.ts` | Header prop | `homeMenu?: Omit<SWHomeMenuProps, "open" \| "onOpenChange" \| "returnFocusRef">`, `homeMenuOpen?`, `defaultHomeMenuOpen?`(false), `onHomeMenuOpenChange?(open, reason)`. 미전달이면 기존 동작과 같습니다(하위 호환) |
|
||||
| I-3 | `components/sw-header/SWHeader.tsx` | 연결 | `homeMenu`가 있을 때만 홈 클릭으로 열림을 토글합니다. 기존 `onHomeClick`은 항상 먼저 호출합니다. 메뉴는 `<header>` 안의 absolute 레이어(`top: 0`)로 렌더링하고, `returnFocusRef`는 홈 버튼입니다. 메뉴가 열리면 기존 필드 Dropdown(`activeMenu`)을 닫습니다 |
|
||||
| I-4 | 같은 파일 | 앵커 | 시작 x = 객체 수정 영역 `getBoundingClientRect().left − header.left − 2px`, 폭 = 창 영역 left − 그 값입니다. 고정 375px을 쓰지 않습니다. Task A `leading` 슬롯이 있으면 `--hm-sw-header-leading-width`만큼 객체 영역이 이동하므로 **DOM 실측을 기준으로 하고 변수는 보조로 씁니다**. 기존 속성Bar `barOffset` 측정 방식과 같은 패턴입니다 |
|
||||
| I-5 | `components/sw-header/components/SWHeaderHome.tsx` | 접근성 | `aria-expanded`·`aria-controls`(메뉴 id)를 추가합니다. Figma에 열림 시각 변화가 없으므로 스타일은 바꾸지 않습니다 |
|
||||
| I-6 | `theme/sw.css` | z-index | 메뉴 레이어를 속성Bar 앵커(z 2)보다 위, 기존 Dropdown 패널과 같은 층에 둡니다. 동시에 열리지 않게 I-3에서 닫습니다 |
|
||||
| I-7 | `theme/sw.css` (선택, A-6 승인 필요) | 속성Bar | P1: 좌·우·하 테두리를 세로 gradient `#08251C 0% / #208769 37.1% / #08241C 80.5% / #051612 100%`로 바꿉니다(기존 hover-ring mask 기법). P2: `.hm-sw-property-bar__object`에 `--hm-sw-header-text-outline`. P3: 라벨에 `--hm-sw-header-label-outline` |
|
||||
| I-8 | `theme/sw.css` (검토) | home on 상·하단 선 | Figma 열림 상태의 1920 전폭 `#5F4B0F` 2px 선(y 0–2, 46–48)을 `.hm-sw-header[data-home-menu-open]`에서만 적용할지 사용자가 확인해야 합니다 |
|
||||
| I-9 | `build.mjs`, `package.json` | 번들 | 변경이 필요 없을 것으로 예상합니다. `dist/styles.css`에 `.hm-sw-home-menu`가 포함되는지, `title-badge.svg`가 inline 또는 asset으로 나오는지 build 후 확인합니다 |
|
||||
| I-10 | `packages/sw/ai/**`, `README.md`, `docs/sw/sw-header-usage.md` | 문서 | [AI 가이드 초안](./ai-home-menu-guide.md)을 COMPONENT_CATALOG·COMPOSITION_PATTERNS·DESIGN_RULES로 옮기고, `homeMenu` prop 사용 예를 추가합니다 |
|
||||
| I-11 | 기존 stories | 예시 | `src/sw/sw-home-menu.stories.tsx`의 소스 경로 import를 `@hm-design/sw`로 바꿉니다. host wiring 예시는 I-3 이후 `SWHeader homeMenu` 예시로 대체합니다 |
|
||||
|
||||
## 3. Task A와 맞출 결정 (사용자 확인)
|
||||
|
||||
- `SWToolButton`에 `tile` appearance를 둘지, `SWHomeMenuItem`을 별도로 유지할지(A-1).
|
||||
- 토글 이름: A는 `pressed`, B는 `active`/`activeIds`(별칭 추가 여부).
|
||||
- `documentOpenPlacement` 기본값 `"menu"`는 메뉴와 독립적입니다. 찾아열기·새파일은 host가 `onDocumentOpen`·`onDocumentNew`와 같은 handler를 연결합니다(내부 결합 없음).
|
||||
- Figma 아이콘 15쌍을 패키지 asset으로 둘지 여부(A-2). 현재는 Story fixture에만 있습니다.
|
||||
|
||||
## 4. 통합 검증
|
||||
|
||||
1920·1440·1080·768·390 폭에서 확인합니다.
|
||||
- 로고 열기·재클릭 닫기, 그리고 `leading`·문서 Dropdown(New/Open)과 겹치지 않는지.
|
||||
- 기존 Header·속성Bar·Dropdown·K 가이드 회귀 없음.
|
||||
- `tsc`, library build, packed tarball을 React 19 소비 프로젝트에 설치했을 때 동작하는지.
|
||||
- Storybook 전체 Story(포트 충돌 주의).
|
||||
|
After Width: | Height: | Size: 9.1 KiB |
|
After Width: | Height: | Size: 105 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 6.9 KiB |
|
After Width: | Height: | Size: 8.5 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 8.8 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 6.5 KiB |
|
After Width: | Height: | Size: 7.6 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 95 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 179 KiB |
|
After Width: | Height: | Size: 98 KiB |
|
After Width: | Height: | Size: 25 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,129 @@
|
||||
# 작업 B 2단계 — 로고 클릭 2단 Header 메뉴 구현·검증 보고 (2026-10-19)
|
||||
|
||||
> 기준: 사용자 최종 정정(로고 클릭 = 2단 Header 메뉴, 기본 숨김, 재클릭 닫힘), [1단계 분석](./analysis-report.md), Figma `rj6wpsfk24lOWvWiVRAANP`.
|
||||
> 범위: Task B worktree `claude/sw-figma-logo-menu-36832b`(HEAD `5faa1f1`)의 **신규 파일만** 수정했습니다. Task A 공유 파일은 수정하지 않았고, Commit·Push·Merge·Publish도 하지 않았습니다.
|
||||
|
||||
## 0. 해석 변경 이력
|
||||
|
||||
| 시점 | 해석 | 최종 |
|
||||
|---|---|---|
|
||||
| 1단계 | 로고 클릭 → `home on` 2단 메뉴 overlay | **유지(최종 정정과 일치)** |
|
||||
| 2단계 중간 요청 | 로고 클릭 → "파일 열기 창" | Figma 재확인 결과 앱 내부 UI이며 OS 파일 선택창 호출 구조가 아닙니다. 이후 사용자 최종 정정으로 2단 메뉴로 돌아왔습니다 |
|
||||
| 재확인으로 바뀐 점 | 662:67267을 단순 "하단 강조 outline 변형"으로 본 것 | 실제로는 Figma 주석 **"최근파일" 상태 화면**입니다. 최근파일이 열려 메뉴 전체에 주황 외곽선이 생기고, 그 아래 최근파일 목록(662:67331)과 2뎁스 미리보기(662:67471)가 붙습니다. 두 노드는 요청한 21개 목록 밖이라 **선택 기능**(`panel` + `SWRecentFiles`)으로 구현했습니다 |
|
||||
|
||||
## 1. 구현 컴포넌트
|
||||
|
||||
| 파일(모두 신규) | 내용 |
|
||||
|---|---|
|
||||
| `packages/sw/src/components/sw-home-menu/SWHomeMenu.tsx` | `SWHomeMenu`(메뉴·열림·Active·닫기·키보드), `SWHomeMenuSection`(제목 badge + 그룹), `SWHomeMenuItem`(80×46 타일, Default / Variant2 / Variant3), `useSWHomeMenuClose` |
|
||||
| `…/sw-home-menu/SWRecentFiles.tsx` | 선택 하위 패널: 최근파일 목록 354px, 행 50px, 2뎁스 미리보기 310×270 |
|
||||
| `…/sw-home-menu/types.ts`, `index.ts` | 공개 타입·export |
|
||||
| `…/sw-home-menu/assets/title-badge.svg` | Figma tit_* Union 도형(9-slice로 폭 가변) |
|
||||
| `packages/sw/src/theme/sw-home-menu.css` | `.hm-sw-home-menu*`, `.hm-sw-recent-files*` 스타일과 `--hm-sw-home-menu-*`·`--hm-sw-recent-files-*` 토큰. 기존 SW 토큰(글꼴, 외곽선, focus, 행 Hover)을 그대로 읽습니다 |
|
||||
| `src/sw/sw-home-menu.stories.tsx`, `sw-home-menu-fixture.tsx`, `sw-home-menu.css`, `src/sw/assets/home-menu/**` | Storybook `SW/Home Menu`(Figma 재현, Header 조합, 3 Variant, 데이터 변경, 단독, 최근파일, 속성Bar 비교, 비교 캔버스). Figma 아이콘 30개·미리보기 이미지는 **Story fixture 전용**입니다 |
|
||||
| `docs/sw/figma-additional/ai-home-menu-guide.md`, `stage2-report.md`, `stage2-qa/**`, `screenshots/662-67331·662-67471` | AI 가이드 초안, 이 보고서, 검증 스크린샷 |
|
||||
|
||||
## 2. Figma 노드 대응 (22 + 참고 3)
|
||||
|
||||
| Node | 대응 | 상태 |
|
||||
|---|---|---|
|
||||
| 26:10492 속성Bar | 기존 `SWHeaderPropertyBar`(수정 없음). Story `속성Bar 비교`에서 Figma 값으로 비교 | 차이 3건은 통합 단계(§8) |
|
||||
| 26:10591 home on | `SWHomeMenu` + `accent="auto"`(패널 없음 → 섹션별 강조) | 재현 |
|
||||
| 662:67267 home on(최근파일 상태) | 최근파일 타일 Active + 패널 → `accent` 자동 외곽선. K 미리보기는 기존 `SWHeaderKGuide` Hover | 재현 |
|
||||
| 26:12604 / 12610 / 12622 / 12616 (+26:12628 참고) | `SWHomeMenuSection.title` (badge 공통 1개) | 재현 |
|
||||
| 26:11355 … 26:11631 버튼 세트 15개 | `SWHomeMenuItem` 1개 + Story 데이터 15건. Default / V2(Hover) / V3(Active) | 재현 |
|
||||
| 662:67331 최근파일 / 662:67471 2뎁스 (참고) | `SWRecentFiles` (`panel`로 연결) | 재현(선택 기능) |
|
||||
|
||||
## 3. 공개 API
|
||||
|
||||
```ts
|
||||
<SWHomeMenu
|
||||
sections?: SWHomeMenuSectionData[] // 또는 children(<SWHomeMenuSection>/<SWHomeMenuItem>)
|
||||
open? defaultOpen?=false onOpenChange?(open, reason: "escape"|"background"|"outside"|"select"|"file")
|
||||
activeIds? defaultActiveIds? onActiveChange?(ids, event) selection?: "multiple"|"single"
|
||||
onItemSelect?(event) closeOnBackgroundClick?=true closeOnSelect?=false closeOnEscape?=true closeOnOutsideClick?=false
|
||||
autoFocus?=true returnFocusRef? accent?: "auto"|"sections"|"outline" />
|
||||
SWHomeMenuItemData { id, label, icon?, hoverIcon?, toggle?=true, panel?, panelLabel?, disabled?, hidden?, title?, aria-label?, onSelect? }
|
||||
SWHomeMenuSectionData { id, title, items, hidden? }
|
||||
<SWRecentFiles files: SWFileEntry[] onFileOpen? onFileHover? renderPreview? closeOnOpen?=true emptyText? />
|
||||
SWFileEntry { id, name, path?, badge?, preview?, previewTitle?, disabled?, title? }
|
||||
```
|
||||
|
||||
- 텍스트·아이콘·항목·이벤트·Active·하위 패널은 모두 외부 데이터입니다. 패키지에는 메뉴 항목이나 CAD 동작이 들어 있지 않습니다.
|
||||
- 단독 사용: `<SWTheme>` 안에 둡니다. 메뉴 밖에서 쓰는 `SWHomeMenuItem`도 `active`·`onActiveChange`로 동작합니다.
|
||||
- Header 연동(통합 전): 기존 `SWHeader onHomeClick`으로 `open`을 토글합니다. 위치는 host가 Header DOM(객체 수정 영역 왼쪽 − 2px부터 창 영역 왼쪽까지)에서 측정합니다.
|
||||
|
||||
## 4. Figma 디자인 일치
|
||||
|
||||
1x 캡처는 headless Chromium의 1920px 화면에서 Figma 스크린샷과 2배 확대로 비교했습니다(`stage2-qa/compare-*.png`).
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| 위치·크기 | 1920에서 메뉴 left 375, 폭 1305, 높이 77이며 섹션 폭은 340/260/180/260/261입니다. 그룹 46, 타일 80×46, 아이콘 18 슬롯(31,6), 라벨 top 25 **일치** |
|
||||
| 색·gradient·그림자 | 섹션 면·안쪽 선, 그룹 테두리 gradient(위→아래)·면(아래→위)·그림자, 구분선, 주황 Hover 테두리, 하단 강조 2종 **일치**(Plugin API 값과 gradientTransform 방향 반영) |
|
||||
| 아이콘 | Figma SVG 30개(Default·Hover)를 그대로 사용하고, 위치 보정 값은 fixture에 둠 **일치** |
|
||||
| 최근파일·미리보기 | 354 폭, 행 50, 배경 `#27241D`, 주황 테두리, Hover 행 `#62533D` + `#FF5C00`, 미리보기 310×270 **일치**(DPR 1.5 화면에서는 테두리 반올림으로 269.3) |
|
||||
| 의도된 차이 | 글꼴은 Dotum → Noto Sans KR이고, 타일 라벨·파일명은 700(기존 SW 정책)이라 Figma보다 굵습니다. 섹션 제목은 400이며 Figma보다 가늘고 badge가 7px 좁습니다(116.6 vs 124). 첫 섹션 면 gradient stop은 나머지 4개 기준으로 통일했습니다. 찾아열기·최근파일 구분선의 다른 gradient는 공통 구분선으로 통일했습니다 |
|
||||
| Header 소유 차이 | home on의 1920 전폭 상·하단 `#5F4B0F` 2px 선은 Header 소유라 미구현(통합 검토) |
|
||||
|
||||
## 5. 상태 전환·Overlay 테스트 (Storybook 6031, 실제 실행)
|
||||
|
||||
| 테스트 | 결과 |
|
||||
|---|---|
|
||||
| 기본 숨김 | PASS (`hiddenByDefault: true`, consumer DOM에도 없음) |
|
||||
| 로고 클릭 열기 / 재클릭 닫기 | PASS |
|
||||
| Hover → V2 (실제 마우스) | PASS (테두리 opacity 1, `#FF5C00`, Hover 아이콘 표시, -1px 이동) |
|
||||
| 클릭 → V3, 재클릭 → Default | PASS (`aria-pressed` true/false) |
|
||||
| 메뉴 빈 곳 클릭 닫기 / Escape 닫기 + 로고 Focus 복귀 | PASS |
|
||||
| ←/→/Home/End, Tab Focus 2px 윤곽선, Enter 실행 | PASS (실제 키 입력) |
|
||||
| 최근파일: 열기 → 외곽선 강조, `aria-expanded`, 재클릭 닫기, ↓로 목록 진입, 행 Hover·Focus 미리보기, Enter/클릭 → `onFileOpen` + 메뉴 닫기, 다시 열면 Default | PASS |
|
||||
| Escape 단계(패널 → 메뉴) | PASS |
|
||||
| 데이터 변경(추가·삭제·숨김·disabled·긴 라벨·섹션 추가·single·accent) | PASS |
|
||||
| 반응형 1920/1440/1080/768/390 | PASS: 로고·창 영역과 겹치지 않고 메뉴 안에서 가로 스크롤. 좁은 폭 접기 디자인은 없음(별도 승인) |
|
||||
| 타입 | PASS: `tsc` SW 패키지, Story, 독립 consumer |
|
||||
| 독립 React 19.3 consumer(Vite 빌드, 소스 import) | PASS: 렌더, 기본 숨김, CSS·SVG 번들 포함 |
|
||||
|
||||
참고: 브라우저 pane은 background page라 ResizeObserver 측정을 할 수 없어, 반응형은 headless Chromium으로 확인했습니다. 메뉴 CSS를 `index.ts`에서만 import하면 `sideEffects` 설정 때문에 빌드에서 빠지는 것을 발견했고, 사용하는 컴포넌트 파일에서 import하도록 고쳤습니다.
|
||||
|
||||
## 6. AI 재사용
|
||||
|
||||
[AI 가이드 초안](./ai-home-menu-guide.md) 기준입니다. 새 메뉴는 `sections` 데이터만으로 같은 스타일이 적용되고, 폭·구분선·모서리·badge 폭이 자동으로 계산됩니다. 소비 프로젝트에서 새 섹션 2개·항목 5개(currentColor 아이콘, `toggle:false`, 패널)를 만든 결과가 같은 스타일로 렌더링되는 것을 확인했습니다(`stage2-qa/react19-consumer.png`). AI 가이드를 `packages/sw/ai/`로 옮기는 작업은 통합 단계입니다.
|
||||
|
||||
## 7. Task A와의 중복·충돌
|
||||
|
||||
| 항목 | 내용 | 조치 |
|
||||
|---|---|---|
|
||||
| `SWToolButton` (pill/text/document) | 80×46 타일 appearance가 없어 중복 구현은 아닙니다 | 통합 때 `tile` appearance로 합칠지 결정(별도 승인) |
|
||||
| 아이콘 prop 이름 | A `hoverIcon` | B도 `hoverIcon`으로 맞춤 |
|
||||
| 토글 상태 이름 | A `pressed` / B `active`·`activeIds` | 메뉴는 여러 항목 상태라 `activeIds` 유지. 통합 때 별칭 검토 |
|
||||
| CSS | A `hm-sw-tool__*` / B `hm-sw-home-menu__*`, `hm-sw-recent-files__*` | 충돌 없음 |
|
||||
| `documentOpenPlacement` | 사용자 최종 지시: 기본 `"menu"`(Open은 문서 Dropdown 안), 로고 옆 독립 Open 없음 | B 메뉴는 이 값과 무관합니다. B Story의 Header(5faa1f1)에는 독립 Open이 없습니다 |
|
||||
| `leading` 슬롯 | 객체 수정 영역 시작 x가 바뀝니다 | B는 DOM 측정이라 하드코딩 없음 |
|
||||
| 찾아열기·새파일 ↔ `onDocumentOpen`·`onDocumentNew` | 동작이 중복될 수 있습니다 | 컴포넌트는 콜백만 받습니다. host가 같은 handler를 연결하는 방식을 권장합니다(통합 문서에 기재) |
|
||||
|
||||
## 8. 통합 시 수정할 공유 파일
|
||||
|
||||
| 파일 | 변경 |
|
||||
|---|---|
|
||||
| `packages/sw/src/index.ts` | `export * from "./components/sw-home-menu"` |
|
||||
| `components/sw-header/SWHeader.tsx`, `types.ts` | (안) `homeMenu?: SWHomeMenuProps`, `homeMenuOpen`/`defaultHomeMenuOpen`/`onHomeMenuOpenChange`. 홈 버튼 `aria-expanded`·`aria-controls`, 객체 수정 영역 −2px ~ 창 영역 앵커, 열릴 때 다른 Dropdown 닫기, 기존 `onHomeClick` 유지(하위 호환) |
|
||||
| `components/sw-header/components/SWHeaderHome.tsx` | 열림 상태 전달(시각 변화는 Figma에 없음) |
|
||||
| `theme/sw.css` | 속성Bar P1 gradient 테두리, P2·P3 객체명·라벨 외곽선. home on 상·하단 `#5F4B0F` 선 적용 여부(검토) |
|
||||
| `build.mjs`, `package.json` | 새 CSS가 `styles.css`에 포함되는지 확인(컴포넌트 import 방식이라 변경 불필요 예상, 미검증) |
|
||||
| `packages/sw/ai/**`, `README.md`, `docs/sw/sw-header-usage.md` | AI 가이드 초안 반영 |
|
||||
| 기존 stories | 필요하면 Header Overview에 메뉴 연결 |
|
||||
|
||||
## 9. 판정
|
||||
|
||||
**PARTIAL** — 독립 구현과 검증은 PASS입니다. 다만 공유 파일 연동(공개 export, `SWHeader` 내장 연결, 속성Bar 수정)은 승인 범위상 통합 단계로 남았습니다.
|
||||
|
||||
미검증 항목:
|
||||
- 패키지 진입점(`@hm-design/sw`) export와 `build.mjs` dist의 `styles.css` 포함 여부. tarball 소비 검증도 하지 않았습니다(통합 단계).
|
||||
- Task A 최신 Header(leading·documentOpenPlacement 반영본)와의 실제 조합. B는 5faa1f1 기준 Header로만 검증했습니다.
|
||||
- Firefox·Safari 렌더링(Chromium만 확인).
|
||||
- 속성Bar P1–P3를 수정한 뒤의 비교.
|
||||
- 스크린리더 실제 낭독.
|
||||
|
||||
검토 대상: V3의 의미(유지 토글 vs 순간 누름; 현재는 사용자 지시대로 유지 토글), 파일 열기 후 닫기 기본값, 최근파일 패널 열기 방식(Figma 연결 없음 → 클릭), 빈 목록 문구, 좁은 폭 디자인, Header 상·하단 선.
|
||||
|
||||
로컬 검증 환경(커밋 대상 아님): worktree `node_modules` junction(→ HMDesign-SW), `.storybook-task-b/`(신규 Story만 로드하는 설정과 웹폰트 fs.allow), `.claude/launch.json`. 실행 중인 Storybook 포트는 6031입니다. 정리 여부는 사용자가 결정합니다.
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"browser": "155.0.8059.80",
|
||||
"shots": [
|
||||
"overview-1920.png",
|
||||
"detail-buttons-1920.png",
|
||||
"interactive-states-1920.png",
|
||||
"window-move-motion-1920.png",
|
||||
"logo-slot-1920.png",
|
||||
"document-dropdown-1920.png",
|
||||
"responsive-more-1920.png",
|
||||
"k-guide-1920.png",
|
||||
"property-panel-compare-1920.png",
|
||||
"composed-controls-1920.png",
|
||||
"overview-1800.png",
|
||||
"overview-1440.png",
|
||||
"overview-1180.png",
|
||||
"overview-1080.png",
|
||||
"overview-900.png",
|
||||
"overview-768.png",
|
||||
"overview-640.png",
|
||||
"overview-520.png",
|
||||
"overview-390.png",
|
||||
"responsive-more-1800.png",
|
||||
"responsive-more-1440.png",
|
||||
"responsive-more-1180.png",
|
||||
"responsive-more-1080.png",
|
||||
"responsive-more-900.png",
|
||||
"responsive-more-768.png",
|
||||
"responsive-more-640.png",
|
||||
"responsive-more-520.png",
|
||||
"responsive-more-390.png"
|
||||
],
|
||||
"errors": []
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"browser": "155.0.8059.80",
|
||||
"shots": [
|
||||
"overview-1920.png",
|
||||
"detail-buttons-1920.png",
|
||||
"interactive-states-1920.png",
|
||||
"window-move-motion-1920.png",
|
||||
"logo-slot-1920.png",
|
||||
"document-dropdown-1920.png",
|
||||
"responsive-more-1920.png",
|
||||
"k-guide-1920.png",
|
||||
"property-panel-compare-1920.png",
|
||||
"composed-controls-1920.png",
|
||||
"overview-1800.png",
|
||||
"overview-1440.png",
|
||||
"overview-1180.png",
|
||||
"overview-1080.png",
|
||||
"overview-900.png",
|
||||
"overview-768.png",
|
||||
"overview-640.png",
|
||||
"overview-520.png",
|
||||
"overview-390.png",
|
||||
"responsive-more-1800.png",
|
||||
"responsive-more-1440.png",
|
||||
"responsive-more-1180.png",
|
||||
"responsive-more-1080.png",
|
||||
"responsive-more-900.png",
|
||||
"responsive-more-768.png",
|
||||
"responsive-more-640.png",
|
||||
"responsive-more-520.png",
|
||||
"responsive-more-390.png"
|
||||
],
|
||||
"errors": []
|
||||
}
|
||||
|
After Width: | Height: | Size: 57 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,363 @@
|
||||
{
|
||||
"browser": "155.0.8059.80",
|
||||
"checks": [
|
||||
{
|
||||
"name": "Storybook SW titles: Header, Tool, Popup, Home Menu; no SW/Header/Options",
|
||||
"status": "PASS",
|
||||
"data": [
|
||||
"SW/Header",
|
||||
"SW/Home Menu",
|
||||
"SW/Popup",
|
||||
"SW/Tool"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "menu at Figma x 375, width 1305, height 77 (measured, no fixed x); header stays 49",
|
||||
"status": "PASS",
|
||||
"data": {
|
||||
"left": 375,
|
||||
"width": 1305,
|
||||
"height": 77,
|
||||
"headerHeight": 49,
|
||||
"sections": [
|
||||
340,
|
||||
260,
|
||||
180,
|
||||
260,
|
||||
261
|
||||
],
|
||||
"tiles": 15,
|
||||
"tileSizes": [
|
||||
"80x46"
|
||||
],
|
||||
"titles": 5,
|
||||
"badgeInlined": true,
|
||||
"icons": 30,
|
||||
"iconsLoaded": 30
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "5 sections 340/260/180/260/261, 15 tiles 80x46, 5 titles",
|
||||
"status": "PASS",
|
||||
"data": {
|
||||
"left": 375,
|
||||
"width": 1305,
|
||||
"height": 77,
|
||||
"headerHeight": 49,
|
||||
"sections": [
|
||||
340,
|
||||
260,
|
||||
180,
|
||||
260,
|
||||
261
|
||||
],
|
||||
"tiles": 15,
|
||||
"tileSizes": [
|
||||
"80x46"
|
||||
],
|
||||
"titles": 5,
|
||||
"badgeInlined": true,
|
||||
"icons": 30,
|
||||
"iconsLoaded": 30
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title badge SVG and 30 tile icons render",
|
||||
"status": "PASS",
|
||||
"data": {
|
||||
"left": 375,
|
||||
"width": 1305,
|
||||
"height": 77,
|
||||
"headerHeight": 49,
|
||||
"sections": [
|
||||
340,
|
||||
260,
|
||||
180,
|
||||
260,
|
||||
261
|
||||
],
|
||||
"tiles": 15,
|
||||
"tileSizes": [
|
||||
"80x46"
|
||||
],
|
||||
"titles": 5,
|
||||
"badgeInlined": true,
|
||||
"icons": 30,
|
||||
"iconsLoaded": 30
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "hidden by default (not in DOM), logo aria-expanded=false",
|
||||
"status": "PASS"
|
||||
},
|
||||
{
|
||||
"name": "logo click opens, aria-expanded=true, aria-controls → menu id",
|
||||
"status": "PASS"
|
||||
},
|
||||
{
|
||||
"name": "tile Hover: label #FF5C00",
|
||||
"status": "PASS",
|
||||
"data": "rgb(255, 92, 0)"
|
||||
},
|
||||
{
|
||||
"name": "tile click → Active (aria-pressed true), re-click → Default",
|
||||
"status": "PASS",
|
||||
"data": "true"
|
||||
},
|
||||
{
|
||||
"name": "logo re-click closes; onHomeClick never called while homeMenu is set",
|
||||
"status": "PASS",
|
||||
"data": "onHomeMenuOpenChange(false, home)\nonItemSelect(working / save, active=false)\nonActiveChange([])\nonItemSelect(working / save, active=true)\nonActiveChange([save])\nonHomeMenuOpenChange(true, home)"
|
||||
},
|
||||
{
|
||||
"name": "Escape inside closes the menu and returns focus to the logo",
|
||||
"status": "PASS"
|
||||
},
|
||||
{
|
||||
"name": "click on the menu background closes it (Figma CLOSE)",
|
||||
"status": "PASS"
|
||||
},
|
||||
{
|
||||
"name": "recent panel opens (outline accent); Escape closes the panel, then the menu",
|
||||
"status": "PASS",
|
||||
"data": {
|
||||
"panelOpen": true,
|
||||
"panelFirst": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "opening the document dropdown closes the menu (reason document); New / Open stay in the dropdown",
|
||||
"status": "PASS",
|
||||
"data": "C0020101-006-평면및종단면도(06)\nC0020101-007-횡단면도\n+ New\nOpen"
|
||||
},
|
||||
{
|
||||
"name": "opening the menu closes the document dropdown",
|
||||
"status": "PASS"
|
||||
},
|
||||
{
|
||||
"name": "no separate Open button next to the logo (documentOpenPlacement=\"menu\")",
|
||||
"status": "PASS"
|
||||
},
|
||||
{
|
||||
"name": "focus outside: Escape closes the most recent (menu), then the popup",
|
||||
"status": "PASS",
|
||||
"data": {
|
||||
"step1": {
|
||||
"menu": 0,
|
||||
"popup": 1
|
||||
},
|
||||
"step2": {
|
||||
"menu": 0,
|
||||
"popup": 0
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "popup opened after the menu: Escape outside closes the popup first, menu stays",
|
||||
"status": "PASS",
|
||||
"data": {
|
||||
"menu": 1,
|
||||
"popup": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Escape inside the popup closes only the popup",
|
||||
"status": "PASS",
|
||||
"data": {
|
||||
"menu": 1,
|
||||
"popup": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "Escape inside the menu closes only the menu (popup stays), focus → logo",
|
||||
"status": "PASS",
|
||||
"data": {
|
||||
"menu": 0,
|
||||
"popup": 1,
|
||||
"focusHome": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "every width: menu starts at object area −2px, ends at the window area, covers neither logo nor file name, header 49, no page scroll",
|
||||
"status": "PASS",
|
||||
"data": [
|
||||
{
|
||||
"width": 1920,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 1305,
|
||||
"scrolls": false,
|
||||
"pageScroll": false
|
||||
},
|
||||
{
|
||||
"width": 1440,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 825,
|
||||
"scrolls": true,
|
||||
"pageScroll": false
|
||||
},
|
||||
{
|
||||
"width": 1080,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 639,
|
||||
"scrolls": true,
|
||||
"pageScroll": false
|
||||
},
|
||||
{
|
||||
"width": 768,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 288,
|
||||
"scrolls": true,
|
||||
"pageScroll": false
|
||||
},
|
||||
{
|
||||
"width": 520,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 288,
|
||||
"scrolls": true,
|
||||
"pageScroll": false
|
||||
},
|
||||
{
|
||||
"width": 390,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 165,
|
||||
"scrolls": true,
|
||||
"pageScroll": false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "without homeMenu: logo click calls onHomeClick, renders no menu, no aria-expanded",
|
||||
"status": "PASS",
|
||||
"data": [
|
||||
"[SWHeader Overview] onHomeClick"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "no page / console errors",
|
||||
"status": "PASS",
|
||||
"data": []
|
||||
}
|
||||
],
|
||||
"errors": [],
|
||||
"data": {
|
||||
"titles": [
|
||||
"SW/Header",
|
||||
"SW/Home Menu",
|
||||
"SW/Popup",
|
||||
"SW/Tool"
|
||||
],
|
||||
"geometry": {
|
||||
"left": 375,
|
||||
"width": 1305,
|
||||
"height": 77,
|
||||
"headerHeight": 49,
|
||||
"sections": [
|
||||
340,
|
||||
260,
|
||||
180,
|
||||
260,
|
||||
261
|
||||
],
|
||||
"tiles": 15,
|
||||
"tileSizes": [
|
||||
"80x46"
|
||||
],
|
||||
"titles": 5,
|
||||
"badgeInlined": true,
|
||||
"icons": 30,
|
||||
"iconsLoaded": 30
|
||||
},
|
||||
"responsive": [
|
||||
{
|
||||
"width": 1920,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 1305,
|
||||
"scrolls": false,
|
||||
"pageScroll": false
|
||||
},
|
||||
{
|
||||
"width": 1440,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 825,
|
||||
"scrolls": true,
|
||||
"pageScroll": false
|
||||
},
|
||||
{
|
||||
"width": 1080,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 639,
|
||||
"scrolls": true,
|
||||
"pageScroll": false
|
||||
},
|
||||
{
|
||||
"width": 768,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 288,
|
||||
"scrolls": true,
|
||||
"pageScroll": false
|
||||
},
|
||||
{
|
||||
"width": 520,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 288,
|
||||
"scrolls": true,
|
||||
"pageScroll": false
|
||||
},
|
||||
{
|
||||
"width": 390,
|
||||
"headerHeight": 49,
|
||||
"start": 0,
|
||||
"endGap": 0,
|
||||
"coversHome": false,
|
||||
"coversDocument": false,
|
||||
"menuWidth": 165,
|
||||
"scrolls": true,
|
||||
"pageScroll": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
{
|
||||
"total": 28,
|
||||
"same": 28,
|
||||
"results": [
|
||||
{
|
||||
"file": "composed-controls-1920.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "detail-buttons-1920.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "document-dropdown-1920.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "interactive-states-1920.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "k-guide-1920.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "logo-slot-1920.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "overview-1080.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "overview-1180.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "overview-1440.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "overview-1800.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "overview-1920.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "overview-390.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "overview-520.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "overview-640.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "overview-768.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "overview-900.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "property-panel-compare-1920.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "responsive-more-1080.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "responsive-more-1180.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "responsive-more-1440.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "responsive-more-1800.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "responsive-more-1920.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "responsive-more-390.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "responsive-more-520.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "responsive-more-640.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "responsive-more-768.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "responsive-more-900.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
},
|
||||
{
|
||||
"file": "window-move-motion-1920.png",
|
||||
"status": "SAME",
|
||||
"changed": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 53 KiB |
|
After Width: | Height: | Size: 81 KiB |
@@ -633,3 +633,20 @@ Storybook: `Property Panel · 선택 없음 / 전체 선택 / 원 1개 / 모두
|
||||
- `undefined`(미지정): Figma 원형 그림(기존 기본). `null` / `false`: 빈 영역 — 크기·치수 라벨 위치·패널 높이 유지. ReactNode: 외부 컴포넌트. 함수: `{ selection, group, properties }`(해당 그룹 항목)를 받아 렌더링 — 객체 유형별 교체에 사용합니다.
|
||||
- 내용은 영역 안에 맞춰지고(overflow hidden) 원주·지름·반지름·면적 입력은 그 위에 그대로 표시됩니다. 값은 `properties`로만 전달되며 형상 Slot과 독립입니다. 입력은 기존 SWHeaderInput 검증·Enter/Blur 확정·Escape 취소를 사용합니다.
|
||||
- 패키지는 CAD 계산·형상 렌더링 엔진을 포함하지 않습니다. Storybook의 사각형/점선 원 SVG는 Story 전용 샘플입니다.
|
||||
|
||||
## 로고 클릭 메뉴 — `homeMenu` (2026-10-19)
|
||||
|
||||
Figma 26:10591의 2단 Header 메뉴입니다. 기본 숨김, 로고 클릭으로 열고 다시 클릭하면 닫힙니다.
|
||||
|
||||
```tsx
|
||||
<SWHeader documentTitle={name} documents={documents} onDocumentNew={newFile} onDocumentOpen={openFile}
|
||||
homeMenu={{ sections, onItemSelect }} // SWHomeMenu Props (열림 상태 제외)
|
||||
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={…} />`(선택).
|
||||
- 이벤트 우선순위: `homeMenu`가 있으면 로고 클릭은 메뉴 토글만 하고 `onHomeClick`은 호출되지 않습니다. `homeMenu`가 없으면 기존과 같이 `onHomeClick`만 호출됩니다.
|
||||
- New / Open은 문서 Dropdown 안 그대로입니다(`documentOpenPlacement` 기본 `"menu"`). 메뉴가 열리면 필드·문서 Dropdown이 닫히고, 문서 Dropdown을 열면 메뉴가 닫힙니다.
|
||||
- 위치: 객체 수정 영역 −2px ~ 창 영역(DOM 측정, 1920에서 x 375 · 폭 1305). 좁은 폭에서는 메뉴 안 가로 스크롤.
|
||||
- Escape: 하위 패널 → 메뉴 순서, 닫히면 로고로 Focus. 속성창·`SWPopup`과 같은 Escape 순서(Focus가 모두 밖이면 최근 사용 하나).
|
||||
- 상세: 패키지 [카탈로그 5절](../../packages/sw/ai/COMPONENT_CATALOG.md), [통합 검증 기록](./sw-home-menu-integration-2026-10-19.md).
|
||||
@@ -0,0 +1,38 @@
|
||||
# SW Task A + B 통합 — 로고 클릭 메뉴 (2026-10-19)
|
||||
|
||||
> 통합 Worktree `HMDesign-SW-Integration`, 브랜치 `integration/sw-home-menu`(기준 Task A `e8409ef`) + Task B `396539d`의 신규 파일 84개. 미커밋.
|
||||
|
||||
## 결합 방식
|
||||
|
||||
- Task B 파일 84개는 모두 신규이며 Task A 파일과 겹치지 않아 Git 충돌이 없습니다(`5faa1f1` → `e8409ef` 조상 관계).
|
||||
- 공유 파일 수정은 연결에 필요한 최소 범위입니다.
|
||||
|
||||
| 파일 | 변경 |
|
||||
|---|---|
|
||||
| `packages/sw/src/index.ts` | `export * from "./components/sw-home-menu"` |
|
||||
| `sw-header/types.ts`, `index.ts` | `homeMenu`, `homeMenuOpen`, `defaultHomeMenuOpen`, `onHomeMenuOpenChange`, 타입 `SWHeaderHomeMenu`, `SWHeaderHomeMenuChangeReason` |
|
||||
| `sw-header/SWHeader.tsx` | `homeMenu`가 있을 때만: 로고 클릭 = 메뉴 토글(`onHomeClick` 미호출), DOM 측정 배치(객체 수정 영역 −2px ~ 창 영역), 메뉴와 문서·필드 Dropdown 상호 닫기. `homeMenu`가 없으면 기존 동작 그대로 |
|
||||
| `components/SWHeaderHome.tsx` | `aria-expanded`·`aria-controls` 전달(시각 변화 없음) |
|
||||
| `components/SWHeaderPropertyPanel.tsx` | 내부 `useSWPopupStack`에 `export`만 추가(패키지 공개 아님) |
|
||||
| `theme/sw.css` | `.hm-sw-header__home-menu` 레이어 1줄(top 0, z-index 20) |
|
||||
| `sw-home-menu/SWHomeMenu.tsx`(Task B) | 열린 동안 Popup Escape 스택에 합류(하위 패널 → 메뉴 순서는 그대로) |
|
||||
| Story·Fixture(Task B) | `@hm-design/sw` 공식 Import, `SWHeader homeMenu` 사용, `통합 · Popup · Escape` Story 추가 |
|
||||
| AI 문서·README·사용 가이드 | 카탈로그 5절, 조합 패턴 7절, 디자인 규칙 11절, 예제 G |
|
||||
|
||||
## 검증 (실행 결과)
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| SW typecheck / 앱 `tsc -b` / SW build / Storybook build | PASS / PASS / PASS / PASS |
|
||||
| `dist/styles.css` 홈 메뉴 스타일, `dist/index.d.ts` 공개 Export | 포함 / 포함 |
|
||||
| AI 문서·공개 API 정합성(`ai-docs.check.mjs`) | 22/22 PASS |
|
||||
| 기존 SW Story 28장 픽셀 비교(통합 전 기준선 대비, threshold 0) | 28/28 동일 |
|
||||
| 기존 Header·Tool·Popup 기능 회귀(`integrated-improvement.browser.mjs`) | 96 PASS / 0 FAIL(통합 전과 동일) |
|
||||
| 홈 메뉴 통합(`home-menu-integration.browser.mjs`) | 22/22 PASS |
|
||||
|
||||
## 남은 항목
|
||||
|
||||
- 미실행: 독립 React 19 Tarball 소비 설치, SW + HM UI CSS 동시 사용, Figma 스크린샷 픽셀 비교(배치·크기는 DOM 실측으로만 확인), Firefox·Safari.
|
||||
- 좁은 폭: 메뉴는 접히지 않고 메뉴 안에서 가로 스크롤(1440 825px, 1080 639px, 768·520 288px, 390 165px). 좁은 폭 디자인은 없습니다.
|
||||
- 디자인 승인 대기(미적용): 속성Bar 차이 3건, 열림 상태 Header 상·하단 `#5F4B0F` 선, 폰트 차이.
|
||||
- Task A의 기존 제한(넓은 `tools` + 390px에서 파일명·레이어 폭 0)은 이번 통합 범위 밖입니다.
|
||||
@@ -0,0 +1,49 @@
|
||||
# 홈 메뉴 Merge — `integration/sw-home-menu` → `feature/sw-package` (2026-10-19)
|
||||
|
||||
> 로컬 `git merge --no-commit --no-ff integration/sw-home-menu` (기준 `b434f27`, 대상 `1472e51`). **Merge Commit·Push는 하지 않았습니다(승인 대기).**
|
||||
|
||||
## 적용 범위 (사용자 정정 반영)
|
||||
|
||||
- 홈 메뉴(Figma 45개 노드, `SWHeader homeMenu` / `SWHomeMenu` / `SWRecentFiles`)는 **SW 일반 모드 전용**입니다.
|
||||
- SW 뷰어 모드(`SWViewer*`)에는 홈 메뉴를 적용하지 않습니다. 뷰어 코드·Story는 변경하지 않았습니다(`sw-viewer` 소스에 홈 메뉴 참조 없음).
|
||||
- AI 지침: AI_USAGE §0.2 표에 "로고(홈) 버튼 메뉴" 행(일반 = `homeMenu`, 뷰어 = 사용하지 않음), §0.3 혼합 금지 항목, 카탈로그 §5·DESIGN_RULES §12·PATTERNS 8의 "일반 모드 전용" 표기, VIEWER.md 잘못된 사용 행, 공통 AI_GUIDE 예시 B/B2.
|
||||
|
||||
## 충돌 해결
|
||||
|
||||
| 파일 | 해결 |
|
||||
|---|---|
|
||||
| `packages/sw/src/index.ts` | 두 Export 모두 유지(`sw-home-menu`, `sw-viewer`) |
|
||||
| `ai/AI_USAGE.md` | 뷰어 모드 문장 유지 + 홈 메뉴 문장 추가 |
|
||||
| `ai/COMPONENT_CATALOG.md` | 홈 메뉴 §5 추가, 이후 6 타입·7 비공개·8 제약, **뷰어 §8 → §9**(참조 갱신: AI_USAGE, VIEWER.md, DESIGN_RULES §11) |
|
||||
| `ai/COMPOSITION_PATTERNS.md` | 뷰어 7 유지, **홈 메뉴 7 → 8**, 하지 말 것 8 → 9 |
|
||||
| `ai/DESIGN_RULES.md` | 뷰어 §11 유지, **홈 메뉴 §11 → §12** |
|
||||
|
||||
`docs/sw/sw-home-menu-integration-2026-10-19.md`의 "패턴 7절, 디자인 규칙 11절"은 통합 당시 기록이라 그대로 둡니다(현재 번호는 위 표).
|
||||
|
||||
## Merge 중 추가 수정
|
||||
|
||||
| 파일 | 내용 | 이유 |
|
||||
|---|---|---|
|
||||
| `sw-header/SWHeader.tsx` | 홈 메뉴 시작 위치 = max(객체 수정 영역, `controls` 안 파일명 오른쪽) − 2px | `controls`로 파일명이 객체 수정 영역 안에 있는 Header(실제 SW 화면)에서 메뉴가 파일명을 덮음(x 63 → 378). 기본 Header 배치는 동일(캡처 바이트 동일) |
|
||||
| `tests/ai-docs.check.mjs` | `` `SWViewer*` `` 같은 와일드카드 표기를 Export 이름으로 보지 않음 | Merge 전 뷰어 문서에서도 실패하던 오탐 |
|
||||
| `tests/home-menu-integration.browser.mjs` | Story 목록 기대값에서 `SW/Tool` 제외, `SW/Viewer` 포함 | `SW/Tool`은 뷰어 작업(7f42923)에서 사용자 요청으로 삭제됨 |
|
||||
|
||||
## 검증
|
||||
|
||||
| 항목 | 결과 |
|
||||
|---|---|
|
||||
| SW typecheck / 앱 `tsc -b` / SW build / Storybook build | PASS / PASS / PASS / PASS |
|
||||
| AI 문서(`check:ai-docs`, `ai-docs.check.mjs`) | PASS / 32/32 |
|
||||
| 홈 메뉴 Storybook(`home-menu-integration.browser.mjs`, 배치 수정 후 재실행) | 22/22 |
|
||||
| 기존 Header·Popup 기능(`integrated-improvement` options·popup) | 89/0 |
|
||||
| 뷰어 Story 기능(`viewer.browser.mjs`, 참고 뷰어 없이) | 70/70 |
|
||||
| Merge 전(b434f27) 대비 픽셀: Header·Popup 요소(`viewer-regression`) / SW Header·Popup·Viewer 전체 Story 48장(1440·768, threshold 0) | 39/39 / 48/48 동일 (배치 수정 전 측정, 수정은 메뉴가 열린 동안만 실행) |
|
||||
| Figma 26:10591 | 현재 Figma 원본 = 저장 이미지(바이트 동일). 섹션 5·타일 15·크기 80×46·메뉴 1305×77·색·아이콘 일치. 글자는 Figma Dotum 14/12px vs HM Noto Sans KR(기존 "폰트 차이 · 디자인 승인 대기") |
|
||||
| 실제 SW 화면(`D:\디자인시스템\HMDesignSwTest`, 0.2.0 로컬 tarball) | typecheck·build PASS, 브라우저 14/14(열기·다시 클릭 닫기·Hover `#FF5C00`·최근파일·Escape 순서·문서 Dropdown·속성 패널 토글). 콘솔 404 1건은 앱의 `/favicon.ico` 없음(기존) |
|
||||
|
||||
## NOT VERIFIED
|
||||
|
||||
- `SW/Tool` 섹션 회귀(Story 삭제됨), 참고 DWG/DXF 뷰어 대비 비교, Firefox·Safari, SW + HM UI CSS 동시 사용, Registry Publish.
|
||||
- 배치 수정 후 Header·Popup·Viewer 픽셀 회귀 재실행(코드 경로상 메뉴가 닫혀 있으면 실행되지 않아 재사용).
|
||||
- 실제 SW 화면의 미연결 타일 12개(저장·복사저장·내보내기·도면묶기·복구하기·임시파일·Display·저장하기·불러오기·설명보기·문의하기·S/W정보): 뷰어 앱에 해당 기능이 없어 실행 동작 없음(`toggle: false`, 툴팁 "뷰어에서 지원하지 않는 기능").
|
||||
- 실제 SW 화면은 `logoSrc`를 주지 않아 로고 면이 비어 있음(기존 상태).
|
||||