Files
HMDesign/packages/sw/ai/FOOTER.md
T

14 KiB
Raw Blame History

SW Footer — SWFooter 계열 (SW 일반 모드)

SW 프로그램 하단 명령 막대입니다. 기준은 Figma 이지빔-SW 레이아웃 분리(26:10097) 하단 72px과 부품 노드 34개(Plugin API로 읽은 값, 2026-10-20)이고, 바 안 명령창·좌표·좁은 폭 동작은 사용자 제공 실제 화면(2026-10-20)을 따릅니다. 두 기준이 다르면 Figma가 우선입니다(예: 선택된 Model 탭은 Figma On 갈색). SW 일반 모드(편집 화면) 전용이며 뷰어 모드(SWViewer*) 화면에는 쓰지 않습니다(AI_USAGE.md §0).

컴포넌트는 표시와 사용자 입력 전달만 합니다. CAD 명령 실행, 명령 등록, 레이아웃 생성·삭제, 명령 해석은 소비 프로그램이 Callback을 받아 처리하고 Props를 갱신합니다.

  1. 존재 확인: node_modules/@hm-design/sw/ai/FOOTER.md(이 문서)와 COMPONENT_CATALOG.md §10이 있는지 확인합니다. 있으면 Footer를 새로 만들지 않습니다.
  2. Export 확인: node_modules/@hm-design/sw/dist/index.d.ts에서 SWFooter·SWFooterLayoutTabs·SWFooterCommandLine·swFooterToolIcons가 Export되는지 확인합니다. CSS는 @hm-design/sw/styles.css 한 개(이미 Import했다면 추가 없음).
  3. 기존 Footer 우선: 하단 막대 전체 → SWFooter. 일부만(보조기능만, 분류 탭만 …) → 아래 §3 부품을 <SWTheme> 안에서 조합.
  4. Props로 구성: 분류·명령·보조기능·문구·아이콘은 데이터(categories, auxItems, auxAll, commandAll, slotGuide)로 넣습니다. 선택 상태(categoryId, activeToolId, pressed, value)는 호스트 상태로 제어합니다.
  5. Variant: Figma 상태는 Props로만 나옵니다(§4 표). Hover·Focus는 CSS가 처리합니다.
  6. 재구현 금지: 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 Figma 가이드 빈 칸 Hover·키보드 Focus 시 위에 뜨는 208 × 149 안내 { title = "명령어 등록", caption = "빠른사용", media? }. media는 194 × 111 영상·이미지(프로그램 제공). false = 없음
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로 전체 이름을 줍니다. 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 속성. 연속 배치하면 칸 사이 1px 금색 구분선
SWFooterAddSlot 68 × 47 칸(+ 68 × 46) guide(기본 Figma 가이드 / false), 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 주황 점선 링 + 주황 + Hover·Focus 시 slotGuide 표시
Layout 탭 Default 베이지 #C3B79E / Hover(×) / On 갈색 그라데이션·흰 글자 / On hover / Variant5 이름 편집(#0F0D0A, 빨강 #E60403) / Variant6 편집 + Hover value, 편집 = 더블클릭·F2 또는 editingId
Model 탭 Default / Hover 주황 / On 갈색 value === SW_FOOTER_MODEL
명령창 화살표 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 Figma 없음 Footer Hover 규칙 재사용: 글자 #FF5C00 (디자인 확인 필요)

4.1 좁은 폭 (Figma 없음, 실제 화면 2026-10-20)

  1. 칸 영역은 바 폭에서 정해집니다. 명령창이 없으면 그룹이 바를 채우고(최대 slotCount 17), 명령창(command)이 있으면 남는 폭(바 − 보조기능 영역 − 명령어 전체보기)의 --hm-sw-footer-slot-share(0.57)를 칸 영역, 나머지를 명령창(최소 280px)이 씁니다. 1920 → 12칸·명령창 629px, 1440 → 8칸, 1280 → 6칸, 950 → 5칸.
  2. 빈 칸(+)이 먼저 줄어듭니다.
  3. 명령이 남는 칸보다 많으면 들어가는 명령만 두고 마지막 칸이 ▲(Figma ico_arrow)가 됩니다. ▲를 누르면 나머지 명령이 명령 칸 바로 위(오른쪽 맞춤, 같은 칸 모양)에 펼쳐지고 ▼가 됩니다. 명령을 고르거나 Escape·바깥 클릭이면 닫힙니다. 숨은 명령이 선택 중이면 ▲가 주황입니다.
  4. 분류 탭이 넘치면 탭 영역 안에서 잘리고 ◀ ▶(스크롤)와 +가 오른쪽에 붙습니다. 현재 탭과 Focus 탭은 자동으로 보이게 스크롤됩니다.
  5. 바 폭 1279px 이하에서 보조기능은 아이콘만(45 × 33, 이름은 접근성 이름·tooltip)입니다.
  6. 바 안 명령창은 최소 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 등)는 없습니다.