User-approved design review (2026-10-21): - SW Header: from 760px up nothing moves into More; the five menus stay with their titles (titles and values ellipsize), file name shrinks to 96px. Open next to the logo no longer blocks field shrinking above 760px (760/768 overflow fixed, also for configured `controls` headers). Dropdown yellow ring inside the button, black seam. - Property Bar values as read-only red text; property popup "=" labels, read-only black values, per-entry hover with pointer. - Home Menu recent files (3 rows, path + name caption, empty preview), Footer + slot guide off by default, Viewer file list without icons, Storybook widths 1920-760. - Tests: old More expectations (integrated-improvement, keyboard-dismiss, footer-popup-viewer-refinement, storybook-cleanup-stage1) moved to the new rule; functional checks kept.
15 KiB
SW Footer — SWFooter 계열 (SW 일반 모드)
SW 프로그램 하단 명령 막대입니다. 기준은 Figma 이지빔-SW 레이아웃 분리(26:10097) 하단 72px과 부품 노드 34개(Plugin API로 읽은 값, 2026-10-20)이고, 바 안 명령창·좌표·좁은 폭 동작은 사용자 제공 실제 화면(2026-10-20)을 따릅니다. 두 기준이 다르면 Figma가 우선입니다. 단, 2026-10-20 사용자 확정으로 선택된 Model 탭과 Layout 탭 Hover는 주황 그라데이션(Figma gra-border-orange)입니다. SW 일반 모드(편집 화면) 전용이며 뷰어 모드(SWViewer*) 화면에는 쓰지 않습니다(AI_USAGE.md §0).
컴포넌트는 표시와 사용자 입력 전달만 합니다. CAD 명령 실행, 명령 등록, 레이아웃 생성·삭제, 명령 해석은 소비 프로그램이 Callback을 받아 처리하고 Props를 갱신합니다.
1. AI 작업 순서 (Footer 요청을 받았을 때)
- 존재 확인:
node_modules/@hm-design/sw/ai/FOOTER.md(이 문서)와 COMPONENT_CATALOG.md §10이 있는지 확인합니다. 있으면 Footer를 새로 만들지 않습니다. - Export 확인:
node_modules/@hm-design/sw/dist/index.d.ts에서SWFooter·SWFooterLayoutTabs·SWFooterCommandLine·swFooterToolIcons가 Export되는지 확인합니다. CSS는@hm-design/sw/styles.css한 개(이미 Import했다면 추가 없음). - 기존 Footer 우선: 하단 막대 전체 →
SWFooter. 일부만(보조기능만, 분류 탭만 …) → 아래 §3 부품을<SWTheme>안에서 조합. - Props로 구성: 분류·명령·보조기능·문구·아이콘은 데이터(
categories,auxItems,auxAll,commandAll,slotGuide)로 넣습니다. 선택 상태(categoryId,activeToolId,pressed,value)는 호스트 상태로 제어합니다. - Variant: Figma 상태는 Props로만 나옵니다(§4 표). Hover·Focus는 CSS가 처리합니다.
- 재구현 금지: Footer의 배경·크기·Pocket 탭·구분선·글자 외곽선을
div와 CSS로 다시 그리지 않습니다.hm-sw-footer*class를 소비 CSS에서 재정의하거나 복사하지 않습니다. 내용(명령·메뉴·아이콘·문구)만 바꿉니다.
판단을 결과 보고에 남깁니다: "SWFooter 재사용 — categories 3개, 보조기능 6개, Layout 탭 연결, 명령 실행은 호스트 onToolSelect".
2. SWFooter (완성형)
import { SWFooter, SWFooterLayoutTabs, SW_FOOTER_MODEL, swFooterToolIcons, swFooterAuxIcons } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
<SWFooter
categories={[{ id: "shape", label: "도형", tools: [{ id: "line", label: "Line", ...swFooterToolIcons.line }] }]}
categoryId={categoryId} onCategoryChange={setCategoryId}
activeToolId={running} onToolSelect={(toolId, categoryId, tool) => runCommand(toolId)}
onSlotAdd={(slotIndex, categoryId) => openRegisterDialog(slotIndex)}
auxItems={[{ id: "snap", label: "객체스냅", icon: swFooterAuxIcons.objectSnap, pressed: snap }]}
onAuxPressedChange={(id, pressed) => setSnap(pressed)}
commandAll={{ onClick: toggleCommandList, pressed: listOpen }}
status={`${x}, ${y}, ${z}`}
command={<SWFooterCommandLine history={history} onSubmit={runCommandText} expanded={listOpen} onExpandedChange={setListOpen} />}
layoutTabs={<SWFooterLayoutTabs layouts={layouts} value={space} onValueChange={setSpace} onAdd={addLayout} onRemove={removeLayout} onRename={renameLayout} />}
/>
| Prop | 기본값 | 설명 |
|---|---|---|
categories |
(필수) | SWFooterCategory[] — { id, label, tools: SWFooterTool[], disabled? }. 아래 분류 행과 위 명령 칸 |
categoryId / defaultCategoryId / onCategoryChange |
첫 분류 | 현재 분류(Controlled / Uncontrolled) |
activeToolId / onToolSelect(toolId, categoryId, tool) |
null |
실행 중 명령 = 주황(Figma Variant3, aria-pressed) |
slotCount |
17 / command가 있으면 제한 없음 |
명령 + 빈 칸(+)의 최대 칸 수. 칸 영역의 폭은 바 폭으로만 정해지고 분류의 명령 수와 무관합니다(분류를 바꿔도 칸 영역·명령창·명령어 전체보기·Layout 탭 위치가 그대로). 명령이 적으면 빈 칸, 많으면 마지막 칸의 ▲ 패널(§4.1) |
onSlotAdd(slotIndex, categoryId) |
없음 | 빈 칸 클릭(명령 등록). 없으면 빈 칸은 모양만 있고 반응하지 않음 |
slotGuide |
false |
빈 칸 Hover·키보드 Focus 시 위에 뜨는 208 × 149 안내(선택 사용). 2026-10-21부터 기본은 없음(+ 위 미리보기 제거). 필요할 때만 {}(Figma 가이드 명령어 등록 / 빠른사용) 또는 { title, caption, media }를 전달. media는 194 × 111 영상·이미지(프로그램 제공) |
onCategoryAdd |
없음 | 분류 행 끝 + 버튼 표시 |
auxItems / onAuxPressedChange(id, pressed) / onAuxItemClick(id) |
없음 | 3 × 2 보조기능 토글(SWFooterAuxItem: id, label, icon?, pressed?, title?, disabled?). pressed를 주면 토글 |
auxAll / commandAll |
표시 | 양끝 74 × 71 버튼 { title, subtitle, tooltip, onClick, pressed, disabled }. 기본 문구 보조기능 / 전체보기, 명령어 / 전체보기. false = 숨김 |
command |
없음 | 바 안 명령창(보통 <SWFooterCommandLine>, 실제 화면 구성). 보조기능 격자와 명령 칸 사이의 남는 폭(최소 280px)을 쓰고, 명령 칸은 오른쪽 명령어 전체보기 바로 앞으로 붙습니다. 펼치면 위(도면 영역)로 커집니다 |
status |
없음 | 바 위 왼쪽 글자(커서 좌표 등, 12px) |
layoutTabs |
없음 | 바 위 오른쪽 끝에 붙는 탭(보통 SWFooterLayoutTabs) |
trailing |
없음 | 바 오른쪽 빈 영역 콘텐츠(좌표·상태) |
label |
명령 도구 |
<footer> 접근성 이름 |
SWFooterTool: { id, label, icon?, activeIcon?, title?, ariaLabel?, disabled? }. icon = 기본(18 × 18), activeIcon = Hover·선택 아이콘(없으면 icon 유지). 줄인 라벨(Rect...)은 ariaLabel로 전체 이름을 줍니다. 칸보다 긴 라벨(POLYGON, WIPEOUT …)은 68 × 47 칸과 14px 글자 그대로 …로 줄고, title이 없으면 잘린 동안만 전체 라벨이 Hover·Focus Tooltip(native title)으로 나옵니다(접근성 이름은 항상 전체 라벨 또는 ariaLabel). 라벨을 임의로 줄이거나(Poly) 칸·글자 크기를 바꾸지 않습니다. Figma 명령 아이콘은 swFooterToolIcons.line / pline / arc / circle / ellipse / rect / poly / point / hatch / ncopy(각 { icon, activeIcon }), 보조기능 아이콘은 swFooterAuxIcons.objectSnap / selectionEffect / orthogonal / gridSnap / gridBackground / lineWeight(인라인 SVG, 채움 currentColor: 버튼 상태 색을 그대로 받음). 프로그램이 직접 넣는 보조기능 아이콘도 currentColor로 그리면 Selected 주황이 적용됩니다(고정 색 <img>는 색이 바뀌지 않음).
3. 부품 (조합용, <SWTheme> 안)
| 컴포넌트 | 크기 | 주요 Props |
|---|---|---|
SWFooterSideButton |
74 × 71 | title(필수), subtitle, side(start/end, 그림자 방향), tooltip, button 속성 |
SWFooterAuxGroup |
104 × 33 칸, gap 2 | items(필수), onPressedChange, onItemClick, columns(3), label |
SWFooterAuxButton |
104 × 33 | label(필수), icon, pressed, button 속성 |
SWFooterToolButton |
68 × 47 칸 | label(필수), icon, activeIcon, pressed, button 속성(title 생략 시 잘린 라벨만 자동 Tooltip). 연속 배치하면 칸 사이 1px 금색 구분선 |
SWFooterAddSlot |
68 × 47 칸(+ 68 × 46) | guide(기본 false = 없음 / {} Figma 가이드 / 내용 객체), guideOpen(Controlled), onGuideOpenChange, button 속성 |
SWFooterGuide |
208 × 149 | title, caption, media, div 속성. 단독 카드로 둘 때 data-standalone="" |
SWFooterMenu |
높이 24 | items({ id, label, disabled? }[], 필수), value(필수), onValueChange, onAdd, addLabel, label, panelId, tabIdPrefix. 현재 탭은 위 칸과 이어지는 Pocket 모양(SVG, 위치 자동 계산) |
SWFooterLayoutTabs |
높이 22 | layouts(SWFooterLayout[] = { id, label, locked?, shape? }, 필수), value(layout id 또는 SW_FOOTER_MODEL), onValueChange, onAdd, onRemove, onRename, editingId/onEditingChange, modelLabel("Model" / false), label, addLabel, removeLabel |
SWFooterCommandLine |
566 × 71 / 270 (SWFooter command 안에서는 남는 폭) |
history(ReactNode[], 필수), value/onValueChange, onSubmit, prompt(Command:), expanded/defaultExpanded/onExpandedChange, inputProps, historyLabel, expandLabel, collapseLabel |
SWFooter·SWFooterCommandLine은 루트에hm-sw-theme이 있어<SWTheme>가 필요 없습니다. 나머지 부품은<SWTheme>안에 둡니다.SWFooterMenu의 Pocket은 부모 배경(명령 칸 면)을 비춰 보입니다. 단독으로 둘 때 부모에background: var(--hm-sw-footer-group-background)를 줍니다.SWFooterLayoutTabs의 마지막 탭은 사선 모양(Figma layout01, × = btn_close2), 나머지는 둥근 모양(layout02/03, × = btn_delete)입니다.shape로 바꿀 수 있습니다. 탭 순서는 배열 순서 그대로(Figma 예시:+, Layout 03, 02, 01, Model).SWFooterCommandLine은SWFooter의command에 넣는 것이 기본 구성입니다(실제 화면). 바 밖에 두면 위치(보통 도면 영역 왼쪽 아래)는 소비 레이아웃이 정합니다. 접힘 = 마지막 3줄, 펼침 = 전체 기록 스크롤.
4. 상태 (Figma Variant ↔ Props)
| 요소 | Figma | 구현 |
|---|---|---|
| 명령 | Default #F6F4F2 / Variant2 Hover = Variant3 선택: #FF5C00 + 주황 아이콘 |
Hover는 CSS, 선택은 activeToolId·pressed. 아이콘·라벨 위치·크기와 칸 크기는 모든 상태에서 같음 |
| 분류 탭 | Default #D4CBBD / Hover·현재 #FF5C00, 현재 탭은 Pocket |
categoryId·value |
| 빈 칸 | Default + / Hover 주황 점선 링 + 주황 + | slotGuide를 전달한 경우에만 Hover·Focus 시 안내 표시(기본 없음) |
| Layout 탭 | Default 베이지 #C3B79E / Hover(선택 아님, ×) = Model과 같은 주황 그라데이션 면(글자는 Default와 동일: 검정 #161410, 크기·굵기·위치 불변) / On 갈색 그라데이션·흰 글자 / On hover / Variant5 이름 편집(#0F0D0A, 빨강 #E60403) / Variant6 편집 + Hover |
value, 편집 = 더블클릭·F2 또는 editingId |
| Model 탭 | Default 베이지 / Hover·On = 주황 그라데이션 면(gra-border-orange) + 검정 #161410 글자 |
value === SW_FOOTER_MODEL |
+ 버튼 |
Layout 옆 + Hover = 같은 주황 그라데이션 면 + +. 분류 행 + Hover = + #FF5C00 |
onAdd / onCategoryAdd |
| 명령창 화살표 | Default ↑ / Hover 주황 ↑ / On ↓ / On hover 주황 ↓ | expanded |
| Disabled | Figma 없음 | SW 공통 40%(명령·빈 칸·탭·보조기능) |
| 보조기능 | Figma btn_객체스냅 26:9248 … btn_선가중치 26:9412: Default 갈색 그라데이션 테두리·아이콘 #F1EDE5·글자 #F6F4F2 / Variant2 = Variant3 주황 그라데이션 테두리·아이콘·글자 #FF5C00 |
Default 그대로. Hover = 노랑 테두리만(SW Header Figma Hover 토큰 --hm-sw-header-hover-border-gradient, 1px, 아이콘·글자 기본색). Selected(pressed) = 주황 테두리 + 주황 아이콘 + 주황 글자 동시, Hover 중에도·마우스가 벗어나도 유지. 크기·위치 변화 없음 |
| 측면 버튼 Hover·Pressed | Figma 없음 | Footer 규칙 재사용(2026-10-20): 제목·부제 모두 #FF5C00 + 테두리 주황 그라데이션(보조기능 Selected 테두리). 크기·면·그림자 그대로, Disabled는 기본 모양 (디자인 확인 필요) |
4.1 좁은 폭 (Figma 없음, 실제 화면 2026-10-20)
- 칸 영역은 바 폭에서 정해집니다. 명령창이 없으면 그룹이 바를 채우고(최대
slotCount17), 명령창(command)이 있으면 남는 폭(바 − 보조기능 영역 − 명령어 전체보기)의--hm-sw-footer-slot-share(0.57)를 칸 영역, 나머지를 명령창(최소 280px)이 씁니다. 1920 → 12칸·명령창 629px, 1440 → 8칸, 1280 → 6칸, 950 → 5칸. - 빈 칸(+)이 먼저 줄어듭니다.
- 명령이 남는 칸보다 많으면 들어가는 명령만 두고 마지막 칸이 ▲(Figma ico_arrow)가 됩니다. ▲를 누르면 나머지 명령이 명령 칸 바로 위(오른쪽 맞춤, 같은 칸 모양)에 펼쳐지고 ▼가 됩니다. 명령을 고르거나 Escape·바깥 클릭이면 닫힙니다. 숨은 명령이 선택 중이면 ▲가 주황입니다.
- 분류 탭이 넘치면 탭 영역 안에서 잘리고 ◀ ▶(스크롤)와 +가 오른쪽에 붙습니다. 현재 탭과 Focus 탭은 자동으로 보이게 스크롤됩니다.
- 바 폭 1279px 이하에서 보조기능은 아이콘만(45 × 33, 이름은 접근성 이름·tooltip)입니다.
- 바 안 명령창은 최소 280px을 유지합니다. 측면 버튼·Layout 탭은 크기가 바뀌지 않습니다(▲ 패널은 Layout 탭 위에 겹침).
5. 키보드·접근성
- 명령 칸:
role="toolbar", 칸 하나만 Tab 정지, ←/→/Home/End 이동. 명령 버튼aria-pressed. ▲ =aria-expanded, 열면 첫 명령으로 Focus, Escape = 닫고 ▲로 Focus. - 분류 행:
role="tablist", ←/→/Home/End = 이동 + 선택, 명령 칸이tabpanel. 분류 + 버튼은 tablist 밖의 일반 버튼. ◀ ▶는 마우스용(tabIndex -1, 키보드는 탭 이동이 자동 스크롤). - 빈 칸: 이름
명령어 등록 (n번 칸), 가이드가 보이면aria-describedby. - Layout 탭:
role="tablist", ←/→ 선택, Delete = 삭제(onRemove), F2·더블클릭 = 이름 편집(Enter 저장, Escape 취소, 바깥 클릭 저장). ×는 마우스 전용(tabIndex -1). - 명령창: 기록
role="log"(aria-live="polite"), 입력<label>연결, Enter =onSubmit(한글 조합 중 Enter 무시), 화살표aria-expanded. - Focus: SW 공통 2px
#F0F7F5.
6. 하지 말 것
- 뷰어 모드 화면에
SWFooter를 넣지 않습니다(뷰어는SWViewerHeader탭·버튼으로 Model/Layout을 다룸). - 명령 실행·명령 등록 로직을 컴포넌트 안에 넣으려고 래핑하지 않습니다. Callback에서 처리합니다.
- 칸 크기(68 × 47)·바 높이(72)·탭 높이(22)를 바꾸려고 class를 덮어쓰지 않습니다. 필요한 경우 공개 토큰(
--hm-sw-footer-*)을style로 바꾸고 결과 보고에 "디자인 확인 필요"로 남깁니다. - 아이콘을 Figma 자산 대신 임의 SVG로 바꿔 "Figma 아이콘"이라고 보고하지 않습니다. 새 아이콘은 프로그램 제공 18 × 18(보조 14~18) 요소입니다.
- 이 문서에 없는 Props(
items,tools최상위,onCommand등)는 없습니다.