Files
HMDesign/docs/sw/sw-header-usage.md
T
shkim4 8127cc72c4 docs(sw): align AI docs and comments with the 2026-10-21 implementation
Documentation and comment-only changes (no component logic, Props or design change):
- VIEWER.md / AI_GUIDE / AI_USAGE / catalog / patterns: the two SWViewerHeader versions
  (default, and minimize/close with SWHeaderWindowControls at the end of `trailing`);
  the earlier "never mix SWHeader parts" rule now names this approved exception.
- collapseBelow / iconOnlyBelow (types.ts JSDoc, VIEWER.md, AI_GUIDE): measured steps,
  the numbers only set the order, not pixel breakpoints. Viewer file open is the
  onDocumentOpen button (logoMenu is compatibility only).
- COMPONENT_CATALOG / DESIGN_RULES: recent files show at most 3 rows; preview caption is
  previewTitle or path + file name; body is the red no-preview sign on the brown panel;
  preview / renderPreview are accepted but not rendered (JSDoc updated to match).
- packages/sw/README.md and sw-header-usage.md: current Story list and counts (40, 14
  shown + 26 verification); 760px+ no-More condition; read-only Property Bar values as
  <output>.
- SWHeader.tsx comment: titles are kept (not hidden) from 760px up.
- AI_GUIDE synced into the package ai/ copies (npm run sync:ai-docs).
2026-10-21 17:45:26 +09:00

69 KiB
Raw Blame History

SW Header 사용 가이드

0.2.0 통합 개선(2026-10-19): 로고 오른쪽 파일 열기(documentOpenPlacement="header", documentOpenLabel 재사용, 실측 기반 More 이동), leading 슬롯, 하위 항목 숨김(hiddenControls의 documentNew·documentOpen·layerFreeze·layerLock), 도구 막대 SWToolButton / SWToolGroup, 속성창 프레임 SWPopup(자유 children)과 SWHeaderPropertyPanel의 children·title을 추가했습니다. API·예제는 패키지 AI 카탈로그와 조합 패턴, Story는 SW / Header의 Document Open·Hidden Sub Controls, SW / Tool, SW / Popup, 검증은 통합 개선 기록을 따릅니다.

현재 기준 색인(2026-10-19 1차 정리): 아래 날짜별 안내 중 최신 항목이 같은 주제의 이전 설명보다 우선합니다. 속성 필드 13개·필드 Dropdown(picker·menus·decimals)은 속성창·K 가이드 추가 연결의 2026-10-19 항목, 표시·숨김(hiddenControls)·메뉴 순서(menuOrder)·속성창 4상태·형상 Slot은 3차 절, 속성Bar·controls·Colorize는 후속 절을 봅니다. 현재 Storybook 33개 목록은 §12와 1차 정리 보고에 있습니다.

상세 버튼 최신 기준(2026-10-16): 레이어·색상·선·문자·치수 상세는 문자스타일 디자인의 SWHeaderMenuDetail을 공통 사용합니다. on*Detail 미전달 시 Hover 유지·클릭 무동작이며, Callback 전달 시 기존 1회 호출·닫힘·Focus 복귀를 유지합니다. 내부 공통 컴포넌트는 label, icon, onClick, disabled, className, aria-label 등 native 버튼 Props를 지원합니다. 새 공개 export는 추가하지 않았습니다. 비교 Story는 SW/Header → Detail Buttons (Shared States)(verification 태그, 직접 ID sw-header--detail-buttons)입니다. 과거의 Callback 미전달=Disabled 및 치수만 밝은 Hover 설명보다 이 기준이 우선합니다. 이번 검증 기록.

3차 기준(2026-10-16): 로고 외 개별 제거 hiddenControls, 유사 메뉴 순서 menuOrder, 속성창 4상태 Story·비교, 형상 Slot shapeContent를 추가했습니다. 3차 절과 3차 검증 기록이 같은 주제의 이전 설명보다 우선합니다.

후속 5개 요구사항 기준(2026-10-16): SWHeaderPropertyBar, 메뉴 단위 controls, btn_colorize Default/Hover를 추가했습니다. 상세 pill의 solid-stroke-width도 제거했습니다. 아래 후속 연결 절과 새 검증 기록이 현재 기준입니다. 과거 실행 기록은 보존합니다.

최신 API/동작: 아래 속성창·K 가이드 추가 연결을 먼저 확인하세요. 색상 기준은 정확히 하나 표시되며, 상세 버튼 Hover와 목록 Selected 정책이 변경됐습니다. 과거 예시의 colorMode=null은 직접 색상 의미를 유지하지만 모드 표시까지 해제하지 않습니다.

@hm-design/sw의 SW Header를 화면에 연결하는 방법입니다. 디자인 값과 판정은 디자인 명세를 따릅니다.

패키지는 아직 npm Registry에 게시하지 않았습니다. 아래 설치 방법은 HMDesign Workspace 또는 로컬 tarball 기준입니다.

1. 패키지 구조

packages/sw/
├── src/
│   ├── assets/                     Figma 원본 SVG와 Hover Variant (build 시 data URI로 포함, 로고 제외)
│   ├── components/
│   │   ├── sw-header/
│   │   │   ├── SWHeader.tsx        전체 헤더
│   │   │   ├── components/         하위 컨트롤 (공개 + 내부)
│   │   │   ├── types.ts            Props / 값 타입
│   │   │   └── index.ts
│   │   └── sw-theme/SWTheme.tsx    하위 컨트롤 단독 사용 시 토큰 범위
│   ├── theme/sw.css                SW 전용 토큰·스타일
│   └── index.ts
├── build.mjs                       독립 library build (Vite lib + tsc 선언)
├── package.json                    exports: "." , "./styles.css"
└── README.md

@hm-design/ui·@hm-design/center-graphic·OrgChart 패키지를 import하지 않습니다. Peer는 react·react-dom ^19.0.0뿐입니다.

2. 설치

HMDesign Workspace 안

루트 package.json의 Workspace(packages/*)에 포함되어 있습니다.

npm install
npm run build:sw

build:packages(typecheck·build·Storybook 전처리)도 마지막에 build:sw를 실행합니다.

다른 React 프로젝트(로컬 tarball)

npm pack -w @hm-design/sw --pack-destination ../packs
npm install ../packs/hm-design-sw-0.1.0.tgz

소비 프로젝트는 src 내부 파일을 참조하지 않고 dist 공개 API만 사용합니다.

3. Import

import { SWHeader } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
  • CSS는 JS에서 자동 import되지 않으므로 앱 진입점 등에서 한 번 import합니다.
  • TypeScript 7에서 CSS side-effect import 타입 오류가 나면 Vite 프로젝트의 /// <reference types="vite/client" />(또는 declare module "*.css")를 둡니다.
  • 글꼴: 패키지는 폰트 파일을 포함하지 않습니다. 앱에서 Noto Sans KR(400/700)을 로드합니다.

4. 공개 API

Export 용도
SWHeader 전체 헤더. ref는 <header>
SWHeaderHome 홈 버튼(로고 영역). logoSrc / logo / logoAlt / onClick
SWHeaderDocumentTitle 파일명 버튼. documents를 주면 문서 Dropdown(title, onClick, documents, menuOpen, onMenuOpenChange, onDocumentSelect, onNew, onOpen, documentIcon, newLabel, openLabel)
SWHeaderLayerControl, SWHeaderColorControl, SWHeaderLineStyle, SWHeaderTextStyle, SWHeaderDimensionStyle 속성 필드 5종. SWHeaderLayerControl은 선택 state(현재 레이어의 표시·동결·잠금)로 필드 아이콘을 회색으로 표시
SWHeaderLayerMenu 레이어 필드 + 레이어 Dropdown(value, layers, activeLayerId, open, onOpenChange, onClick, onLayerSelect, onVisibilityChange, onFreezeChange, onLockChange, onDetail). SWHeader는 layers가 있을 때 이것을 사용
SWHeaderColorMenu 색상 필드 + 색상 Dropdown(2026-10-16, value, colors, userColors, mode, open, onOpenChange, onClick, onColorSelect, onModeChange, onColorInput, onPick, onDetail). SWHeader는 colors가 있을 때 사용
SWHeaderLineStyleMenu 선스타일 필드 + 3열 Dropdown(value, lineTypes, lineWeights, activeLineTypeId, activeLineWeightId, onLineTypeSelect, onLineWeightSelect, onLineWeightValueChange, onScaleChange, onDetail, 열림 Props). SWHeader는 lineTypes 또는 lineWeights가 있을 때 사용
SWHeaderTextStyleMenu, SWHeaderDimensionStyleMenu 문자·치수스타일 필드 + 목록 Dropdown(value, textStyles / dimensionStyles, active*Id, on*Select, onDetail, 열림 Props)
SWHeaderLinePreview, swHeaderLinePatterns 70 × 2 선 미리보기와 Figma CENTER(22·7·22·7) / HIDDEN(33·33) 패턴
SWHeaderInput 수정 가능한 SW Input(Figma input 26:11656 · cursor 26:11664). 레이어 상세 등 상세 영역의 값 편집용
parseSWHeaderColor, parseSWHeaderNumber Input 유효성 검사 도우미(r,g,b / #rrggbb, 소수)
SWHeaderProperties 상세보기(속성Bar / 속성창)
SWHeaderActions 인쇄 · 팝업 가이드
SWHeaderWindowControls 창 이동 · 최소화 · 종료
SWTheme 하위 컨트롤을 헤더 밖에서 쓸 때 토큰·글꼴·버튼 초기화 범위
타입 SWHeaderProps, SWHeaderMenuId, SWHeaderLayerValue, SWHeaderLayer, SWHeaderColorValue, SWHeaderLineStyleValue, SWHeaderTextStyleValue, SWHeaderDimensionStyleValue, SWHeaderDocument, SWHeaderColorMode, SWHeaderColorOption, SWHeaderColorInputValue, SWHeaderLineType, SWHeaderLineWeight, SWHeaderTextStyleOption, SWHeaderDimensionStyleOption, SWHeaderInputValueType, 각 하위 컴포넌트 *Props, SWThemeProps

내부 구현(SWHeaderField, SWHeaderMore, useSWHeaderDropdown, SWHeaderMenuDetail)은 공개하지 않습니다.

5. SWHeader Props

HTMLAttributes<HTMLElement>(단 title, children, color 제외)를 함께 받습니다. className·style·aria-*는 <header>에 전달됩니다.

Prop 타입 기본값 설명
documentTitle string 필수 파일명. 좁으면 말줄임, 전체 이름은 title 툴팁
drawingModeLabel string "그리기모드" 그리기 모드 라벨
layer { name } { name: "0" } 현재 레이어(layers에 activeLayerId와 같은 항목이 없을 때 필드에 표시)
layers SWHeaderLayer[] — 레이어 필드 아래 Dropdown 목록(배열 순서 그대로). 없으면 Dropdown 없이 기존 필드 버튼, []이면 하단(레이어 상세)만
activeLayerId string — 현재 레이어. 필드에 이름·상태 아이콘 표시, 목록에서 이름만 #FFC600
layerMenuOpen boolean — 레이어 Dropdown 열림 상태(제어형). 없으면 내부에서 관리
color { value, swatch? } { value: "ByLayer", swatch: "#ffffff" } 색상 값 표시와 칩 색
lineStyle { name, weight, scale } ByLayer / ByLayer / 1.00 선스타일
textStyle { font, name } Arial / Standard 문자스타일
dimensionStyle { name } Standard 치수스타일
activeMenu "layer" | "color" | "lineStyle" | "textStyle" | "dimensionStyle" | null null 선택(열림) 표시
propertyBarOpen boolean false 속성Bar 눌림
propertyPanelOpen boolean false 속성창 눌림
disabled boolean false 메뉴·속성·인쇄/가이드 비활성(창 제어 제외)
tools ReactNode — 객체 수정 영역의 필드 뒤 추가 도구
logoSrc string — 로고 이미지 URL. 32 × 17 로고 영역에 contain으로 맞춤
logo ReactNode — 로고 요소(인라인 SVG 등). logoSrc보다 우선
logoAlt string — 제품명. 홈 버튼 이름이 " 홈"(없으면 "홈")
documents SWHeaderDocument[] — 파일명 아래 문서 Dropdown 목록. 없으면 Dropdown 없이 기존 버튼, []이면 New / Open만
documentMenuOpen boolean — 문서 Dropdown 열림 상태(제어형). 없으면 내부에서 관리
activeDocumentId string — 현재 문서. 목록 순서는 그대로 두고 이 문서의 이름만 #FFC600
documentIcon ReactNode Figma 문서 아이콘 icon이 없는 문서의 아이콘(29 × 12 영역)
documentNewLabel / documentOpenLabel string + New / Open 하단 버튼 글자
colors SWHeaderColorOption[] — 색상 Dropdown 기본 팔레트(2026-10-16). 없으면 Dropdown 없이 기존 필드 버튼
userColors SWHeaderColorOption[] — 사용자 색상(개수 가변, 2px 간격 줄바꿈, 4행 초과 시 팔레트 영역 스크롤)
colorMode "byLayer" | "byBlock" | null — 눌린 ByLayer / ByBlock. null·미지정 = 명시 색
colorMenuOpen boolean — 색상 Dropdown 열림(제어형). 없으면 내부 관리
lineTypes / lineWeights SWHeaderLineType[] / SWHeaderLineWeight[] — 선스타일 Dropdown의 선 종류 / 가중치 열. 둘 다 없으면 기존 필드 버튼. 스케일 열은 lineStyle.scale
activeLineTypeId / activeLineWeightId string lineStyle.name / .weight와 같은 이름의 행 현재 행(이름 #FFC600)
lineStyleMenuOpen boolean — 선스타일 Dropdown 열림(제어형)
textStyles / dimensionStyles SWHeaderTextStyleOption[] / SWHeaderDimensionStyleOption[] — 문자 / 치수스타일 Dropdown 행. 없으면 기존 필드 버튼
activeTextStyleId / activeDimensionStyleId string textStyle.name / dimensionStyle.name과 같은 이름의 행 현재 행
textStyleMenuOpen / dimensionStyleMenuOpen boolean — 각 Dropdown 열림(제어형)

Dropdown 데이터 타입(모두 id로 식별, 배열 순서 = 표시 순서, 패키지에 고정 데이터 없음):

타입 필드
SWHeaderColorOption { id; color; value?; label?; disabled? } — color CSS 색, value 보고·표시 값(기본 color의 r,g,b), label 접근성 이름
SWHeaderColorInputValue { value: "r,g,b"; rgb: [r, g, b]; hex: "#rrggbb" }
SWHeaderLineType { id; name; pattern?: number[]; disabled? } — pattern 70px 미리보기의 대시 길이(4px 간격, 비율 맞춤), 없으면 실선
SWHeaderLineWeight { id; name; value?; editable?; disabled? } — value 오른쪽 값(ByDefault 0.25), editable 이면 값 Input(User)
SWHeaderTextStyleOption { id; name; font; disabled? }
SWHeaderDimensionStyleOption { id; name; disabled? }

SWHeaderDocument: { id: string; name: string; title?: string; icon?: ReactNode; disabled?: boolean } — title은 툴팁(기본 name).

SWHeaderLayer: { id: string; name: string; color?: string; visible?: boolean; frozen?: boolean; locked?: boolean; disabled?: boolean } — color는 16px 견본 색(기본 #ffffff), 기본값은 visible: true, frozen: false, locked: false. 확정 매핑(명세 §5.2.1):

아이콘 컬러 회색 클릭 시 Callback 값
전구 visible=true visible=false onLayerVisibilityChange(layer, !visible)
눈꽃 frozen=false(동결 해제) frozen=true(동결) onLayerFreezeChange(layer, !frozen)
자물쇠 locked=false(잠금 해제) locked=true(잠금) onLayerLockChange(layer, !locked)

헤더는 값을 직접 바꾸지 않으므로 호스트가 받은 값으로 layers를 갱신해야 아이콘 색이 바뀝니다.

로고는 확정 디자인이 아닙니다. 로고 영역의 위치·크기는 공통 디자인이고, 로고 이미지는 사용하는 SW 프로그램이 정합니다. 패키지에는 로고가 들어 있지 않으며 logo·logoSrc를 전달하지 않으면 로고 없이 표시됩니다(배치는 그대로). Storybook의 기본 로고는 임시 자산(sw-header-logo-placeholder.svg)입니다.

기본값은 Figma 표시값이 아니라 중립값입니다. Figma 샘플 값은 Storybook figmaArgs를 참고합니다.

6. Callback

Callback 호출 시점
onHomeClick 홈(로고) 클릭
onDocumentClick 파일명 클릭(Dropdown 열고 닫을 때도 호출)
onDocumentMenuOpenChange(open) 문서 Dropdown 열림·닫힘
onDocumentSelect(document) 문서 항목 선택. 문서를 여는 동작은 호스트가 구현
onDocumentClose(document) 행의 X(문서 닫기). 주면 모든 행에 X가 생기고 Hover·키보드 Focus한 행에만 보입니다. 선택 Callback과 중복되지 않습니다. 실제 닫기·목록 갱신·다음 선택은 호스트가 처리
onDocumentNew, onDocumentOpen Dropdown 하단 + New / Open
onLayerOpen, onColorOpen, onLineStyleOpen, onTextStyleOpen, onDimensionStyleOpen 각 필드 클릭(Dropdown이 있으면 열고 닫을 때도 호출) 또는 더보기 항목 선택 1회(대표 필드 전환, §7 "반응형 More")
onLayerMenuOpenChange(open) 레이어 Dropdown 열림·닫힘
onLayerSelect(layer) 레이어 행(견본·이름) 선택. 목록이 닫힙니다. 현재 레이어 변경은 호스트가 activeLayerId로 반영
onLayerVisibilityChange(layer, nextVisible) 전구 클릭. 목록 유지, 선택 Callback 없음
onLayerFreezeChange(layer, nextFrozen) 눈꽃 클릭. 목록 유지, 선택 Callback 없음
onLayerLockChange(layer, nextLocked) 자물쇠 클릭. 목록 유지, 선택 Callback 없음
onLayerDetail() 하단 레이어 상세. 호출 후 목록이 닫힙니다. 없으면 버튼은 Figma 외형 그대로 비활성
onColorMenuOpenChange(open) 등 on*MenuOpenChange 각 Dropdown 열림·닫힘(색상·선/문자/치수스타일)
onColorSelect(option) 팔레트·사용자 색상 칩. 목록 유지
onColorModeChange(mode) ByLayer / ByBlock. 목록 유지. 상호 배타 표시는 호스트가 colorMode로 반영
onColorInput(value) RGB 입력 확정(Enter·Focus 이동·목록 닫힘). 유효한 r,g,b(0–255) 또는 #rrggbb만, 값이 바뀐 경우만
onColorPick() 색상 추출 버튼. 목록이 닫히고 실제 추출(도면 픽셀 등)은 호스트가 구현
onLineTypeSelect(type) / onLineWeightSelect(weight) 선 종류 / 가중치 행. 다른 열 값은 건드리지 않고 목록 유지
onLineWeightValueChange(weight, value) editable 가중치 Input 확정(0 이상 소수)
onLineScaleChange(value) 스케일 Input 확정(0 초과 소수)
onTextStyleSelect(style) / onDimensionStyleSelect(style) 행 선택 후 목록 닫힘
onColorDetail, onLineStyleDetail, onTextStyleDetail, onDimensionStyleDetail 하단 … 상세. 전달 시 호출 후 목록 닫힘. 없으면 공통 Default/Hover를 유지하고 클릭은 무동작. 상세 팝업은 패키지 범위 밖(추후 Figma 전달 후)
onPropertyBarToggle, onPropertyPanelToggle 속성 토글 클릭 또는 더보기 항목 선택
onPrint, onPopupGuide 인쇄·팝업 가이드
onWindowMoveStart(event) 창 이동 영역 pointer down
onMinimize, onClose 최소화·종료

헤더는 선택·열림 상태를 스스로 저장하지 않습니다. 호스트가 Callback에서 상태를 바꾸고 Props로 다시 내려줍니다. 실제 드롭다운 목록, 파일 열기, 창 이동·최소화·종료(브라우저 / Electron / 설치형 SW마다 다름)는 호스트가 구현합니다.

7. 전체 헤더 예제

import { useState } from "react"
import { SWHeader, type SWHeaderMenuId } from "@hm-design/sw"
import "@hm-design/sw/styles.css"

export function DrawingHeader({ fileName }: { fileName: string }) {
  const [activeMenu, setActiveMenu] = useState<SWHeaderMenuId | null>(null)
  const [panelOpen, setPanelOpen] = useState(false)
  const toggle = (id: SWHeaderMenuId) => () => setActiveMenu(current => (current === id ? null : id))

  return (
    <SWHeader
      documentTitle={fileName}
      layer={{ name: "0_Layer_Cludmark" }}
      color={{ value: "255,229,0", swatch: "#ffe500" }}
      lineStyle={{ name: "Continuous", weight: "0.25", scale: "1.00" }}
      textStyle={{ font: "Arial", name: "Standard" }}
      dimensionStyle={{ name: "Standard" }}
      activeMenu={activeMenu}
      propertyPanelOpen={panelOpen}
      onLayerOpen={toggle("layer")}
      onColorOpen={toggle("color")}
      onLineStyleOpen={toggle("lineStyle")}
      onTextStyleOpen={toggle("textStyle")}
      onDimensionStyleOpen={toggle("dimensionStyle")}
      onPropertyPanelToggle={() => setPanelOpen(open => !open)}
      onClose={() => window.close()}
    />
  )
}

로고 지정 예

import productLogo from "./assets/product-logo.svg" // 각 프로그램의 로고 파일

<SWHeader documentTitle={fileName} logoSrc={productLogo} logoAlt="제품명" />

// React 요소로 전달(인라인 SVG 등). logoSrc보다 우선합니다.
<SWHeader documentTitle={fileName} logo={<ProductMark />} logoAlt="제품명" />
  • 로고 영역은 32 × 17px입니다. 가로·세로 비율이 달라도 영역 안에 맞춰지고 잘리지 않습니다. 선명하게 보이도록 SVG 또는 64 × 34px 이상 이미지를 권장합니다.
  • 로고 영역 크기는 디자인 기준이므로 바꾸지 않는 것을 권장합니다(토큰 --hm-sw-header-logo-width / -logo-height).

문서 Dropdown 예

const [openDocuments, setOpenDocuments] = useState<SWHeaderDocument[]>([])

<SWHeader
  documentTitle={current.name}
  documents={openDocuments}                       // 각 프로그램의 문서 목록(표시 순서 그대로)
  activeDocumentId={current.id}                   // 이름만 #FFC600, 순서는 그대로
  onDocumentSelect={document => activate(document.id)}
  onDocumentClose={document => closeDocument(document.id)} // 목록·현재 문서는 호스트가 갱신
  onDocumentNew={createDocument}
  onDocumentOpen={showOpenDialog}                 // 파일 열기는 호스트가 구현
/>
  • 클릭으로 열고 닫습니다(Hover로 열리지 않음). 바깥 클릭·Escape·항목 선택·New/Open 후 닫힙니다.
  • Dropdown은 헤더 아래로 겹쳐 열립니다. SWHeader의 조상 요소에 overflow: hidden이 있으면 잘리므로, 헤더는 페이지 최상단처럼 잘리지 않는 위치에 둡니다.

레이어 Dropdown 예

const [layers, setLayers] = useState<SWHeaderLayer[]>(() => drawing.layers.map(toHeaderLayer))
const patch = (id: string, change: Partial<SWHeaderLayer>) =>
  setLayers(list => list.map(layer => (layer.id === id ? { ...layer, ...change } : layer)))

<SWHeader
  documentTitle={fileName}
  layers={layers}                                   // 각 프로그램의 레이어 목록(표시 순서 그대로)
  activeLayerId={currentLayerId}                    // 필드 표시 + 목록에서 이름만 #FFC600
  onLayerSelect={layer => setCurrentLayer(layer.id)}
  onLayerVisibilityChange={(layer, visible) => { viewer.setLayerVisible(layer.id, visible); patch(layer.id, { visible }) }}
  onLayerFreezeChange={(layer, frozen) => { viewer.setLayerFrozen(layer.id, frozen); patch(layer.id, { frozen }) }}
  onLayerLockChange={(layer, locked) => { viewer.setLayerLocked(layer.id, locked); patch(layer.id, { locked }) }}
  onLayerDetail={openLayerManager}                  // 없으면 버튼 비활성(임의 팝업 없음)
/>
  • 레이어 필드를 클릭해야 열립니다(Hover는 테두리·글자색만). 행 선택·Escape·바깥 클릭·Tab 이탈·레이어 상세 후 닫힙니다. 전구·눈꽃·자물쇠 클릭은 목록을 유지합니다.
  • 헤더는 레이어 상태를 저장하지 않습니다. Callback에서 도면(viewer.*은 예시)과 layers를 함께 갱신해야 아이콘이 바뀝니다. 실제 표시·동결·잠금 처리와 편집 가능 여부 판단은 호스트 책임입니다.
  • 키보드: 필드에서 ↓로 열면 현재 레이어에 Focus, ↓↑ 행 이동(같은 열), ←→ 행 안 이동, Enter/Space 실행. 상세 규격은 디자인 명세 §5.2.

색상 · 선/문자/치수스타일 Dropdown 예 (2026-10-16)

const [color, setColor] = useState<SWHeaderColorValue>({ value: "ByLayer", swatch: "#ffffff" })
const [colorMode, setColorMode] = useState<SWHeaderColorMode | null>("byLayer")
const [lineStyle, setLineStyle] = useState({ name: "Continuous", weight: "0.25", scale: "1.00" })

<SWHeader
  documentTitle={fileName}
  color={color}
  colors={program.palette}                           // [{ id: "red", color: "#ff0000", label: "빨강" }, ...]
  userColors={program.userColors}                    // 개수 가변
  colorMode={colorMode}
  onColorSelect={option => { viewer.setColor(option.color); setColor({ value: option.value ?? option.color, swatch: option.color }); setColorMode(null) }}
  onColorModeChange={mode => { viewer.setColorMode(mode); setColorMode(mode) }}
  onColorInput={({ value, hex }) => { viewer.setColor(hex); setColor({ value, swatch: hex }); setColorMode(null) }}
  onColorPick={startPickFromDrawing}                 // 실제 픽셀 추출은 호스트
  onColorDetail={openColorDetail}                    // 없으면 Hover 유지·클릭 무동작(상세 팝업 미제공)
  lineStyle={lineStyle}
  lineTypes={program.lineTypes}                      // [{ id: "center", name: "CENTER", pattern: swHeaderLinePatterns.center }, ...]
  lineWeights={program.lineWeights}                  // [{ id: "user", name: "User", value: "0.25", editable: true }, ...]
  onLineTypeSelect={type => setLineStyle(s => ({ ...s, name: type.name }))}       // 가중치·스케일 유지
  onLineWeightSelect={weight => setLineStyle(s => ({ ...s, weight: weight.value ?? weight.name }))}
  onLineWeightValueChange={(weight, value) => updateWeight(weight.id, value)}
  onLineScaleChange={scale => setLineStyle(s => ({ ...s, scale }))}
  textStyle={textStyle}
  textStyles={program.textStyles}                    // [{ id, font: "Arial", name: "Standard" }, ...]
  onTextStyleSelect={style => setTextStyle({ font: style.font, name: style.name })}
  dimensionStyle={dimensionStyle}
  dimensionStyles={program.dimensionStyles}          // [{ id, name: "ISO - 25" }, ...]
  onDimensionStyleSelect={style => setDimensionStyle({ name: style.name })}
/>
  • 클릭으로 열고 닫습니다(Hover는 노란 테두리·글자만). 다른 Dropdown을 열면 앞의 것이 닫히고, 바깥 클릭·Escape·Tab 이탈로 닫힙니다. 헤더 높이(49px)와 다른 필드 위치는 변하지 않습니다.
  • 색상·선스타일은 여러 속성을 이어서 바꿀 수 있도록 선택 후에도 열려 있고, 문자·치수스타일은 행 선택 후 닫힙니다. 색상 추출과 … 상세는 호출 후 닫힙니다.
  • 헤더는 값을 저장하지 않습니다. Callback 값으로 color / colorMode / lineStyle 등을 갱신해야 필드가 바뀝니다. 명시 색을 고를 때 ByLayer/ByBlock을 해제할지는 호스트 정책입니다(Storybook 예제는 해제).
  • 키보드: 필드에서 ↓로 열면 현재(눌린) 항목 또는 첫 항목에 Focus, ↓↑로 Dropdown 안 버튼·Input 순서대로 이동, Enter/Space 실행, Escape 닫기(필드로 Focus 복귀).
  • Hover: 상세 버튼 5개는 문자스타일 상세 기준 공통 Hover(배경 .25·radius 25·아이콘 35%·글자 35%)를 적용합니다. ByLayer/ByBlock은 기존 후속 Hover 기준을 따릅니다. 색상 칩·색상 추출·목록 행(선 종류·가중치·스케일·문자·치수)·Input의 현재 동작은 유지합니다.
  • 좁은 헤더: 목록은 헤더 안으로 옮겨지며, 468px 선스타일 목록은 헤더 폭 480 미만에서 세로로 쌓입니다.

동적 데이터 구조

모든 Dropdown(문서·레이어·색상·선 종류·가중치·문자·치수스타일)은 Props 배열로만 그립니다.

  • 항목 추가·삭제·이름/값 변경·순서 변경은 배열을 바꾸면 되고 컴포넌트 소스 수정이 필요 없습니다. 각 항목은 id로 식별되어 바뀐 항목만 다시 그리고 다른 행의 상태(입력 중 값 등)를 보존합니다.
  • 현재 항목(active*Id)이 배열에서 사라지면 선택 표시가 없어지고 필드는 값 Props(layer, lineStyle 등)를 그대로 표시합니다. 다음 현재 항목은 호스트가 정합니다.
  • disabled(선택 불가), editable(가중치 값 Input), Input의 readOnly / disabled를 항목별로 줄 수 있습니다.
  • 목록이 길면 Dropdown 안에서만 스크롤합니다(행 목록 최대 240px = 8행, 사용자 색상은 팔레트 118px). 헤더 레이아웃은 유지됩니다.
  • Storybook SW / Header / Dynamic Data에서 추가·삭제·이름·값·순서 변경을 직접 확인할 수 있습니다.

수정 가능한 Input — SWHeaderInput

레이어 상세 등 상세 영역에서 속성값을 직접 고칠 때 씁니다(Figma input 26:11656, cursor 26:11664). 상세 팝업 레이아웃은 아직 없으므로 Input과 계약만 제공합니다.

import { SWHeaderInput, parseSWHeaderNumber, SWTheme } from "@hm-design/sw"

<SWTheme>
  <SWHeaderInput
    aria-label="X 좌표"
    valueType="number"                        // "text"(기본) | "number"
    value={entity.x}                          // 확정값(호스트). 바뀌면 최신 값 표시
    onValueChange={draft => preview(draft)}   // 편집 중 임시값(선택)
    onCommit={next => updateEntity({ x: next })} // Enter · Focus 이동 · 감싼 Dropdown 닫힘, 유효 + 변경 시만
    onCancel={() => clearPreview()}           // Escape: 확정값으로 복원
    parse={draft => parseSWHeaderNumber(draft, { min: 0 })} // 선택: 형식·범위 직접 지정
  />
</SWTheme>
  • 상태: Default(배경·테두리 없음, 58 × 20 기준) / Hover(1px rgba(212,203,189,.25), 24px) / 편집 중(#0F0D0A, 1px .3, 24px). 글자 #E60403 14px + 검정 Text Stroke, 글자 위치는 상태와 무관하게 고정. 폭은 값 길이에 맞춥니다.
  • 커서: 네이티브 caret을 #E60403(caret-color)으로 표시하고 깜박임도 네이티브입니다(가짜 커서 없음). 선택·IME(한글 조합 중 Enter는 확정하지 않음)는 브라우저 기본 동작입니다.
  • onCommit이 없거나 readOnly이면 읽기 전용(Hover·편집 표시 없음), disabled는 40%.
  • 선택형(목록) 입력은 Figma 디자인 전달 후 같은 데이터 정의(id·이름·값·타입)로 확장합니다. 현재는 텍스트·숫자와 사용자 parse(임의 형식)를 지원합니다.

반응형 More — 대표 필드 전환

2026-10-21 (사용자 결정): 헤더 폭 760px 이상에서는 More를 쓰지 않습니다. 5개 메뉴는 로고 옆 Open 유무와 관계없이 항상 Header에 남고, 좁아질수록 간격 축소 → 파일명 … → 정보값 … 순으로 줄어듭니다. 메뉴 타이틀은 모든 폭에서 표시하며 공간이 부족하면 …로 줄어듭니다. 아래 내용(대표 필드 전환·실측 More)은 760px 미만에만 적용됩니다. 상세는 DESIGN_RULES의 반응형 항목.

헤더가 좁아 필드가 더보기(…)로 옮겨졌을 때, 더보기에서 필드 항목을 고르면 그 필드가 객체 수정 영역 첫 칸(레이어 필드 자리)에 표시되고 Dropdown이 바로 아래에 열립니다. Focus는 그 필드로 옮겨갑니다.

  • 레이어는 그동안 더보기로 이동하며, 더보기 → 레이어로 원래 배치로 돌아갑니다. 이미 화면에 있는 필드는 더보기에 나오지 않습니다.
  • 전환은 UI 상태입니다. 데이터 Callback은 호출되지 않고, 필드 클릭과 같은 on*Open 1회와 on*MenuOpenChange(true)만 호출됩니다. 값은 그대로 유지됩니다.
  • 폭이 넓어져 그 필드가 다시 보이는 단계에서는 1920 기준 배치로 돌아갑니다(Breakpoint 1800 / 1180 / 1080 / 900 / 640 / 520은 그대로).
  • 실측 More(2026-10-19): Breakpoint 위에서 객체 수정 영역의 실제 남은 공간도 측정합니다. leading·tools·긴 라벨·사이드 패널 등으로 공간이 모자라면 표시할 수 없는 메뉴만 더보기로 옮기고, 공간이 다시 생기면 원래 자리·순서로 돌려놓습니다. 메뉴를 열고 닫는 것만으로는 Header 요소의 위치·폭이 바뀌지 않습니다(명세 §12.1).
  • Dropdown 데이터(colors 등)가 없는 필드도 같은 방식으로 대표 칸에 표시됩니다(목록 없이 on*Open만).

8. 하위 컴포넌트 재사용

헤더 밖에서는 SWTheme로 감쌉니다. 소비자가 hm-sw-* class를 붙이지 않습니다.

import { SWTheme, SWHeaderLayerControl, SWHeaderColorControl, SWHeaderProperties } from "@hm-design/sw"

<SWTheme>
  <SWHeaderLayerControl value={{ name: "0" }} onClick={openLayers} />
  <SWHeaderColorControl value={{ value: "255,229,0", swatch: "#ffe500" }} selected />
  <SWHeaderProperties panelOpen={panelOpen} onPanelToggle={togglePanel} />
</SWTheme>

SWTheme는 배치(flex 등)를 정하지 않습니다. 배치는 className·style로 소비 화면에서 정합니다.

9. 테마 커스터마이징

--hm-sw-header-* 토큰만 바꿉니다. 사용자 확정 3개 면(파일명·객체 수정·속성 입력)은 기본값을 유지하는 것을 권장합니다.

import type { CSSProperties } from "react"

<SWHeader documentTitle="a.dwg" style={{ "--hm-sw-header-selected-color": "#ffd84d" } as CSSProperties} />

토큰 목록과 사용처는 디자인 명세 §14를 따릅니다. @hm-design/ui Theme Runtime(JSON)과는 연결되어 있지 않습니다.

10. DWG/DXF 뷰어 연동 시 고려사항

  • 기존 뷰어 Toolbar.tsx의 파일명(fileName)은 documentTitle로, 패널 토글(propsOpen 등)은 propertyPanelOpen + onPropertyPanelToggle로 연결할 수 있습니다.
  • 뷰어의 파일 열기·샘플·Model/Layout 탭·2.5D·어두운 배경·로드 상태는 Figma SW Header에 없는 뷰어 전용 도구입니다. 필요하면 tools 슬롯에 호스트가 렌더링합니다(스타일은 호스트 책임).
  • 헤더 높이는 49px입니다. 고정 배치(position: fixed)는 className·style로 호스트에서 지정합니다.
  • 반응형은 모두 헤더 자신의 폭(container query)을 기준으로 하므로 사이드 패널 때문에 헤더가 좁아져도 동작합니다. 단계(1800 / 1180 / 1080 / 900 / 640 / 520)는 디자인 명세 §12를 따르고, 그 위에서 실제 남은 공간을 측정해 부족한 메뉴만 더보기로 옮깁니다(§12.1).
  • 뷰어는 현재 @hm-design/ui의 SWHeader 패턴을 사용합니다. 이 패키지로 바꾸는 작업은 이번 범위가 아닙니다.

10.1 상호작용 상태

  • 그룹 필드 Hover(현재 추가 기준): 레이어·색상·선/문자/치수스타일은 버튼 전체 Hover 시 라벨·값과 지정된 선/폰트/치수 미리보기가 #FFC600이 됩니다. 기존 Gradient Hover Border와 검정 외곽선은 유지하며 색상 견본·레이어 상태 아이콘은 바꾸지 않습니다. Disabled는 새 Hover 효과를 적용하지 않고 Selected 색은 기존대로입니다. 상세보기의 텍스트 전용 Hover는 변경하지 않습니다. 디자인 명세 §11.5.

  • Hover Border: 테두리가 있는 버튼(홈, 5개 속성 필드, 더보기, 인쇄, 팝업 가이드)은 마우스를 올리면 테두리가 Figma 홈 버튼의 노란 gradient로 바뀝니다. 5개 필드 내부 텍스트·미리보기는 위 추가 기준을 함께 적용합니다. 파일명과 속성Bar·속성창은 예외로, 테두리 없이 글자만 #FFC600으로 바뀝니다(파일명 Dropdown은 클릭해야 열림). 문서 Dropdown 안은 Figma hover를 따릅니다: 목록 항목은 검정 40% 배경, + New / Open은 배경이 진해지고(20% → 30%) 글자가 검정 100%가 됩니다(노란 테두리 없음). 배경·크기·위치는 변하지 않습니다. 비활성 버튼은 바뀌지 않습니다.

  • Hover(창 제어 · 바로가기 아이콘): 인쇄·팝업 가이드는 노란 테두리와 함께 아이콘의 #B6D0C9 부분만 #FFC600으로 바뀝니다(외곽선·그림자·타일 색은 그대로). 최소화는 검정 20% 배경 + 흰 막대, 종료는 #E40000 배경 + 흰 X로 바뀝니다(Figma 26:12156 / 26:12161, 32 × 48 유지, 노란 테두리 없음). 소비자는 class나 Prop을 추가하지 않습니다. 상세는 디자인 명세 §11.2.

  • Selected(activeMenu, propertyBarOpen, propertyPanelOpen): 글자만 #FFC600. Hover와 함께 표시됩니다.

  • Focus: 키보드 Focus 윤곽선은 Hover 테두리 위에 표시됩니다.

  • Hover 테두리 색은 --hm-sw-header-hover-border-gradient 토큰, 최소화 Hover 배경·막대 색은 --hm-sw-header-window-button-hover-background / --hm-sw-header-window-icon-hover-color 토큰으로 바꿀 수 있습니다. 종료·인쇄·팝업 가이드 Hover 아이콘은 패키지 SVG Asset입니다. 대상·제외 목록은 디자인 명세 §11.1을 따릅니다.

  • Text Stroke(2026-10-16): Figma에 Stroke가 있는 헤더·Dropdown 텍스트는 검정 외곽선(14·16px 1.3px, 12px 라벨 1px)을 표시합니다(자기 색 0.3px Stroke는 2026-10-16 추가 수정으로 제거). Hover·Selected는 채움색만 바뀌고 외곽선은 유지됩니다. 더보기 메뉴·창 이동 Tooltip은 Figma에 Stroke가 없어 적용하지 않고, + New / Open은 요청에 따라 기존 그대로입니다. 외곽선 색은 --hm-sw-header-text-outline-color, 외곽선 전체는 --hm-sw-header-text-outline / --hm-sw-header-label-outline으로 바꿀 수 있습니다(--hm-sw-header-text-stroke-width는 제거됨)(명세 §6.1).

10.2 창 이동 Hover Motion

별도 Prop이나 의존성 없이 Hover하면 GIF의 경로·구간 비율을 유지하면서 후속 사용자 요청에 따라 1.4초마다 반복합니다(원본 GIF 1.64초와 다름). 커서가 아래에서 위로 먼저 올라가고 약 444ms에 문구가 나타난 뒤 창·커서·문구가 오른쪽 위로 이동합니다. 약 888ms에 이동을 마치고 약 512ms 유지한 뒤 반복합니다. 28px / −4.5px 이동과 가속·감속 비율은 그대로이며 이탈 시 즉시 기본 SVG로 돌아갑니다. 크기·줄무늬·SVG·Tooltip 규격은 SW/Figma를 유지합니다.

onWindowMoveStart는 기존 PointerEventHandler<HTMLDivElement>입니다. 예를 들어 호스트는 event.currentTarget.setPointerCapture(event.pointerId)로 이후 포인터를 추적할 수 있습니다. 패키지는 포인터를 캡처하거나 실제 창을 이동시키지 않습니다. 아이콘과 창 이동 Tooltip은 pointer-events: none이며 Callback 이벤트와 최소화·종료 클릭을 가로채지 않습니다. 누르는 동안 CSS :active로 모션을 멈추고 Tooltip을 숨깁니다. Tooltip은 창 이동 영역 밖의 최소화 및 종료 왼쪽 일부에 겹칠 수 있습니다.

  • 헤더 폭 ≥1080px: 88px 영역, GIF 기준 반복 모션과 Tooltip.
  • 640–1079px: 24px 드래그 영역, 그림은 숨기고 정적 Tooltip만 표시.
  • <640px: 기존대로 창 이동 영역 숨김.
  • Reduced Motion: 기본 아이콘과 정적 Hover Tooltip을 유지하고 애니메이션을 끕니다. 24px 단계에서는 기존대로 그림이 숨겨집니다. OS 설정을 재생 중 바꾸어도 적용됩니다.
  • Focus: 창 이동 div는 기존대로 Tab 대상이 아니며 키보드 창 이동·Focus Tooltip을 제공하지 않습니다. 실제 호스트의 키보드 창 이동 대안과 보조기술 검증은 별도로 필요합니다. 최소화·종료는 이름이 있는 기본 버튼이며 Focus 표시가 유지됩니다.

Tooltip은 45 × 17px, 검정 80% 배경과 흰색 12px/400 글자입니다. Tooltip만 Figma의 Inter를 우선하고 한글·미설치 환경은 기존 SW 글꼴로 fallback합니다. 별도 폰트 설치는 하지 않습니다. 경로·자산은 명세 §11.4, 현재 시간과 필드 Hover는 명세 §11.5, 결과는 현재 통합 검증을 참고합니다. 원본 GIF 속도 검증과 GIF 전달 전 결과는 각각 GIF 검증 기록, 과거 검증 기록에 보존합니다.

11. 지원 · 미지원

지원 미지원(호스트 또는 후속 작업)
Figma 헤더 1개(26:8944) 표시·선택·Focus·Disabled 색상·선스타일·문자스타일·치수·레이어 상세 팝업(Figma 전달 후), 색 선택 팝업(Figma "추후 디자인 진행예정")
색상(26:10986 / 26:11036)·선스타일(26:11104)·문자스타일(26:11181)·치수스타일(26:11215) Dropdown, ByLayer/ByBlock, RGB·가중치·스케일 Input(2026-10-16) 실제 색 적용·픽셀 색상 추출·선/문자/치수 스타일 적용(호스트 구현)
반응형 More 대표 필드 전환, SWHeaderInput(Default / Hover / 편집 / 커서), Text Stroke(2026-10-16) 선택형 상세 Input(Figma 미전달)
문서 Dropdown(26:10662) 목록·선택·New/Open Callback·키보드 실제 파일 열기·새 문서 만들기(호스트 구현)
레이어 Dropdown(26:10940) 목록·선택·전구/눈꽃/자물쇠 Callback·레이어 상세 Callback·키보드 실제 레이어 표시·동결·잠금과 레이어 상세 화면(호스트 구현)
1800 / 1180 / 1080 / 900 / 640 / 520 반응형 + 더보기(옮긴 기능도 같은 Props·Callback) SW 팝업(DraggablePanel)
SSR 문자열 렌더링(window 접근 없음) 창 이동·최소화·종료의 실제 동작
React 19, ESM, 타입 선언 CommonJS 빌드, Light/Dark 테마, Runtime Theme JSON 연결

12. Storybook

npm run storybook

현재 기준(2026-10-21): SW Story는 40개입니다. SW / Header 20개(사이드바 기본 표시 4개: SW Header(sw-header--overview)·Home Menu·Logo Slot·Responsive More + verification 태그 16개), SW / Footer 6개(기본 2 + verification 4), SW / Popup 3개(기본 3), SW / Viewer 10개(기본 5 + verification 5), SW / Theme 1개(verification). 최신 목록은 패키지 README를 따르고, 아래 2026-10-20 이전 문단은 당시 기록입니다.

(2026-10-20 기록) SW Storybook 간소화: SW 메뉴 순서는 Header → Footer → Popup → Viewer → Theme입니다. SW / Header에는 20개 Story가 있습니다(사이드바 기본 표시 10개 + verification 태그 10개). Property Panel · 4개 비교는 사이드바에서 SW / Popup 아래로 옮겼고 ID(sw-header--property-panel-compare)는 그대로입니다. Window Move Motion·K Guide는 verification 태그로 숨겼고, Home Menu · 단독 사용 · 조합형은 개별 부품 예제로 사이드바에 표시합니다. 테스트·문서에서 쓰지 않던 Home Menu Figma 비교 Story 6개(sw-header--home-menu-figma, -capture-active, -capture-outline, -capture-recent, -variants, -recent-files)와 Home Menu의 Figma 스크린샷 비교 섹션은 제거했습니다. Figma 원본은 docs/sw/figma-additional/screenshots/에 있습니다. 상세: SW Storybook 간소화 보고. 아래는 작성 당시 기록입니다.

(2026-10-19 기록) 홈 메뉴 통합 후 SW / Header에는 27개 Story가 있습니다(사이드바 기본 표시 12개 + verification 태그 15개). 이전 시점의 Story 표는 1차 정리 보고 §2, 홈 메뉴 통합 후 목록은 홈 메뉴 통합·실측 More 보고를 봅니다. 아래 문단은 Story를 추가한 시점별 설명입니다.

2026-10-19 홈 메뉴 통합: 기존 SW / Home Menu 메뉴는 사이드바에서 제거하고 SW / Header로 옮겼습니다. 사이드바에는 Home Menu(sw-header--home-menu: 로고 클릭 열기/닫기·이벤트 기록, 헤더 폭 조절, Figma 26:10591 비교, Default/Hover/Active, 최근파일 하위 패널, 속성창·SWPopup·Escape 통합) 하나만 표시합니다. Figma 비교 캔버스·Variant·데이터 변경·단독 사용·속성Bar 비교 등은 sw-header--home-menu-* ID의 verification 태그 Story로 남아 직접 URL과 사이드바 태그 필터로 엽니다. Overview의 로고도 같은 홈 메뉴를 엽니다. Responsive More 첫 섹션 "실제 사용 가능한 공간 기준"은 헤더 폭 Range와 로고 오른쪽 버튼·추가 도구·홈 메뉴 On/Off로 실측 More의 이동·복귀를 확인합니다. 홈 메뉴는 SW 일반 모드 전용이며 SW / Viewer에는 없습니다.

(2026-10-16 기록 — 아래 이름 중 Default·Controls·Responsive·Composed Controls·Layer Dropdown 등 다수는 이후 제거·통합되어 현재 없습니다) SW / Header 아래 Story: Overview, Default, Controls, Interactive States(인쇄·팝업 가이드·최소화·종료 Default/Hover 비교와 Callback 기록 포함), Responsive, Composed Controls, Logo Slot(기본 임시 로고 / 사용자 지정 로고 / 로고 미전달), Document Dropdown(Default / Hover / Open / 선택 / 여러 문서 / 문서 없음 / 긴 파일명 / 좁은 화면), Layer Dropdown(Layer Default / Hover / Open(Figma 8행) / Row Hover / 전구·눈꽃·자물쇠 4상태 3배 확대 / 선택·Toggle Callback 기록과 상태표 / 여러 상태·현재 레이어 / 긴 레이어명 / 20개 스크롤 / 레이어 없음 / 레이어 상세 / 상태 표시 전용 / 좁은 화면 1440–390). Responsive Story는 헤더 폭 18개(1920–390, 각 단계 경계 전후 포함) 프레임을 나열합니다. 모든 단계가 헤더 폭 기준이므로 Viewport를 바꾸지 않아도 확인할 수 있습니다.

Window Move Motion은 GIF 주요 3상태와 속도 변경 후 약 0 / 205 / 410 / 444 / 683 / 888 / 1366 / 1400ms 프레임, 실제 Hover·Drag·최소화·종료 Callback 및 반복(motion cycle) 기록, 축소 폭 예제를 제공합니다. 내부 고정 시간은 원본 GIF 시간 × 1400/1640의 정확한 값입니다. 고정 프레임은 Storybook 전용 Hover attribute와 Web Animations API로 시간을 멈춥니다. Reduced Motion에서는 기본 아이콘·정적 Tooltip 정책을 따르므로 정상 타임라인 비교는 해당 설정을 끄고 Story를 다시 로드합니다.

2026-10-16 추가 Story: Color Dropdown, Line Style Dropdown, Text · Dimension Style Dropdown(Default / Hover / Expanded / 행 Hover / 독립 버튼 Hover 무변화 / Callback 기록 / 좁은 화면), Responsive More(대표 필드 전환 Callback 기록 1440·768·390 + 16개 폭), Text Stroke(Figma 원본 이미지 비교 · 3배 확대 · 말줄임), Layer Detail · Input(Figma 참조 이미지 · 레이어 상세 Default/Hover 무변화/Disabled/Callback · Input Default/Hover/ReadOnly/Disabled/편집/확정·취소·외부 값·IME), Dynamic Data(항목 추가·삭제·이름·값·순서 변경). Document Dropdown의 첫 예제는 현재 열린 문서 1개이며, 기본 figmaArgs도 그 문서 1개를 전달합니다(documents=[]는 예외 상태 예제로만 남김). 검증 결과는 Dropdown·More·Text Stroke·Input 검증 기록에 있습니다.

Composed Controls에는 기존 레이어·색상·상세보기와 공개 선/문자/치수 컨트롤, 5개 필드의 고정 Hover 비교가 있습니다. 실제 공용 컴포넌트를 렌더링하며 60 × 15px 치수 Hover SVG는 흰 foreground만 노란색으로 바꾸고 검정 mask 외곽선을 유지합니다. 기존 Story만 보완했으며 새 Story는 추가하지 않았습니다. 현재 통합 검증을 참고합니다.

속성창·K 가이드 추가 연결 (2026-10-16)

기존 공개 Props를 유지한 추가 API입니다. propertyPanel을 전달하면 기존 속성창 버튼에 패널이 연결됩니다. 미전달 시 기존 토글/Callback 계약만 유지하며 임의의 CAD 데이터/편집 창을 만들지 않습니다.

import { useState } from "react"
import { SWHeader, type SWHeaderProperty } from "@hm-design/sw"
import "@hm-design/sw/styles.css"

function DrawingHeader() {
  const [properties, setProperties] = useState<SWHeaderProperty[]>([
    { id: "layer", groupId: "common", name: "레이어", type: "text", value: "층_01" },
    { id: "weight", groupId: "common", name: "선가중치", type: "number", value: "0.25", min: 0, unit: "mm" },
  ])
  return <SWHeader
    documentTitle="도면"
    propertyPanel={{
      selection: { id: "selection-42", kind: "single", title: "선택된 객체 - 호스트 객체" },
      groups: [{ id: "common", collapsedCount: 6 }],
      properties,
      onPropertyChange: (property, value) => {
        // 실제 CAD 반영 및 계산은 호스트에서 수행한 뒤 결과를 Props로 돌려줍니다.
        setProperties(items => items.map(item => item.id === property.id ? { ...item, value } : item))
      },
      onToolActiveChange: active => console.info("호스트 선택 도구", active),
    }}
    onPropertyPanelOpenChange={open => console.info("속성창 열림", open)}
  />
}
  • Uncontrolled: propertyPanelOpen을 생략합니다. Controlled: propertyPanelOpen={open}, onPropertyPanelOpenChange={setOpen}을 함께 전달합니다. 기존 onPropertyPanelToggle은 버튼/More 클릭 시 그대로 호출됩니다. 이전 Controlled 호스트가 새 Open Callback 없이 기존 Toggle Callback만 사용한다면 X/Escape 닫기에서도 기존 Toggle로 알립니다.
  • 선택 대상이 바뀔 때 selection.id도 반드시 변경합니다. 같은 property id라도 이전 객체의 임시 편집값은 재사용하지 않습니다.
  • selection.kind: none/all/single. selection.title·counts·overviewTitle은 호스트 데이터입니다. counts의 선택적 row/column은 Figma 집계 배치를 표현할 수 있습니다. onOverviewClick도 선택 변경을 요청할 뿐 내부 CAD 동작은 없습니다. 2026-10-21: 전체 선택 하단의 객체 유형·개수 목록은 표시 전용이며, 각 항목에 마우스를 올리면 그 항목 글자만 #FF5C00으로 바뀝니다(클릭·선택·필터 동작 없음).
  • 항목 타입: text/number/select/color/slider/computed. id/name/value/type/groupId가 필수이고, order로 표시 순서를 지정합니다. 중복 id를 사용하지 마세요.
  • options는 select의 id/name/disabled 목록입니다. 선택은 Enter 또는 Focus 이동으로 확정하고 Escape로 취소합니다. 삭제되거나 disabled가 된 option은 새 값으로 확정하지 않습니다. 옵션 데이터가 바뀌면 임시 선택을 현재 호스트 값으로 돌립니다.
  • color는 기존 RGB/HEX parser를 재사용합니다. swatch로 별도 견본을 제공할 수 있습니다. linePreview/linePattern은 기존 선 미리보기를 재사용합니다. annotation은 (ByL) 같은 보조 정보, unit은 mm/% 같은 단위입니다.
  • number/slider는 min/max, slider는 step을 지원합니다. slider의 기본 범위는 0100이며 Story의 01 등은 호스트가 지정한 범위입니다. 수치를 계산하거나 Figma의 시각적 thumb 위치에 맞춰 임의 변환하지 않습니다.
  • validate(draft)가 false면 거부합니다. 항목의 onCommit(value, property)와 패널의 onPropertyChange(property, value)는 서로 다른 통지 지점입니다. 둘 다 연결하면 각각 통지됩니다.
  • 그룹은 layout=rows/circle/position, order/name/collapsedCount/positionLabel을 받습니다. circle은 원본 SVG 위 graphicSlot=circumference/diameter/radius/area의 항목을 배치합니다. 다른 항목은 그래픽 아래에 배치합니다. 계산값은 computed/readOnly로 전달합니다.
  • expandedGroupIds/onExpandedGroupsChange로 펼침을 제어하거나, Uncontrolled일 때 defaultExpandedGroupIds로 시작합니다. 펼침은 property Callback을 실행하지 않습니다.
  • toolActive/onToolActiveChange/toolLabel로 내부 아이콘을 연결합니다. 현재 의미는 호스트가 정하는 선택 도구 상태 통지이며 CAD 기능을 내장하지 않습니다.
  • 단독 사용: <SWTheme><SWHeaderPropertyPanel ... onClose={close} /></SWTheme>. 단독 배치는 호스트의 style로 지정할 수 있습니다.
  • 동적 패널 입력은 SWHeaderInput commitOnUnmount={false}를 사용합니다. 기존 Input의 기본 true 동작은 유지합니다.
  • 속성 필드 상태(2026-10-19, Figma 26:12388 ~ 26:12649): 공통 행은 200 × 28 필드(선가중치처럼 단위 없는 숫자와 투명도 값은 41px 내외 Hug)입니다. Default는 값만, Hover는 rgba(33,29,19,.4) 테두리, 숫자 편집 On은 #0F0D0A + 흰 단위, Dropdown On은 #211D13입니다. 하이퍼링크(text)·재료(computed)는 Figma에 On이 없어 Default/Hover만 적용합니다. 검증 기록.
  • 필드 Dropdown: property.picker("layer" | "lineStyle" | "color" | false)와 패널 menus(SWHeaderPropertyMenus: layers·toggle Callback, lineTypes·lineWeights·lineScale, colors·colorMode·onColorPick 등)를 전달합니다. type: "color"는 색상, type: "select" + linePreview는 선스타일 Dropdown이 기본이며 레이어는 picker: "layer"를 지정합니다. 목록 선택은 해당 속성의 onPropertyChange(값 = 행 id)로 확정되고, 레이어 Toggle·가중치·스케일·ByLayer/ByBlock·색상 추출·상세는 menus Callback만 호출합니다. menus가 없으면 options로 목록을 만듭니다. mixed는 다양함으로 표시하며 현재 행을 표시하지 않습니다.
  • 2026-10-21 디자인 검토: 속성창 선꼴 Dropdown은 선 종류 목록 1열만 표시합니다(menus.lineWeights·lineScale·가중치/스케일 Callback은 속성창 Dropdown에서 쓰이지 않으며 SWHeader 선스타일 필드는 3열 유지). 숫자 편집 Input은 값 영역 전체 너비를 채우고, 투명도는 값 + annotation이 하나의 편집 상자입니다. 수정 불가능한 값(readOnly·computed·Disabled)은 속성창에서 검정, 속성Bar에서 Bar 편집이 없는 값은 포커스 없는 텍스트(<output>)입니다. 상세는 packages/sw/ai/DESIGN_RULES.md 속성값 색상 항목.
  • decimals를 지정한 숫자는 확정값을 고정 소수 자릿수로 정규화합니다(예: 선축적 2 → 2.0000). 색상 입력은 기존 0~255 검증을 유지합니다.
  • K 가이드는 별도 데이터를 요구하지 않습니다. 기존 K 버튼 Hover/Focus로 원본 이미지가 Pan/Zoom 반복되고, 작은 화면의 More에서도 같은 미리보기가 보입니다. Reduced Motion·Escape·재진입 초기화가 적용됩니다. 클릭은 기존 onPopupGuide만 호출합니다.
  • 색상 모드의 null은 직접 색상 의미를 보존하고, 필수 모드 표시는 마지막 유효 모드(처음 ByLayer)를 유지합니다. 선택된 모드를 재클릭해도 onColorModeChange가 중복 호출되지 않습니다.
  • 신규 Story: Property Panel / Property Panel Variants / K Guide / Additional Hover. 기존 17개 Story를 유지합니다.

실행 결과와 제한은 추가 검증 기록을 따릅니다.

후속 5개 요구사항 연결 (2026-10-16)

속성Bar와 공유 데이터

import { SWHeader, SWHeaderPropertyBar, type SWHeaderProperty } from "@hm-design/sw"
import "@hm-design/sw/styles.css"

const fields: SWHeaderProperty[] = [
  { id: "length", groupId: "shape", name: "길이", type: "number", value: length, min: 0 },
]
const data = {
  selection: { id: objectId, title: objectName, kind: "single" as const },
  properties: fields,
  onPropertyChange: (property: SWHeaderProperty, next: string) => updateObject(property.id, next),
}
<SWHeader documentTitle={fileName}
  propertyBar={data} propertyBarOpen={barOpen} onPropertyBarOpenChange={setBarOpen}
  propertyPanel={{ ...data, groups: [{ id: "shape", name: "형상" }] }}
  propertyPanelOpen={panelOpen} onPropertyPanelOpenChange={setPanelOpen} />
  • propertyBar는 SWHeaderPropertyBarProps: selection/properties/onPropertyChange, 전체 readOnly/disabled 및 div HTML 속성입니다. 개별 property의 readOnly/disabled/validate/min/max도 적용합니다. 컴포넌트 밖에서 단독 사용하면 SWTheme로 감쌉니다.
  • propertyBar 생략 시 propertyPanel의 selection/properties/onPropertyChange만 공유합니다. 두 데이터를 모두 생략하면 토글 상태만 제공하며 임의 속성이나 CAD 수치를 만들지 않습니다.
  • propertyBarOpen 생략 시 내부 상태, defaultPropertyBarOpen으로 초기값을 정합니다. Controlled는 propertyBarOpen/onPropertyBarOpenChange를 함께 사용합니다. 기존 onPropertyBarToggle도 클릭마다 보존됩니다. 속성창 열림과 독립적입니다.
  • properties는 id로 식별하고 order 오름차순(같은 값은 배열 순서)으로 표시합니다. 객체 교체 시 selection.id를 바꿉니다. name/value/항목 추가·제거·외부 변경을 반영합니다.
  • 값 편집은 SWHeaderInput을 사용합니다. Enter/Blur 확정, Escape 취소, 유효하지 않은 입력 복원, 외부 value 반영, 편집 중 삭제 draft 폐기를 지원합니다. 바의 select 타입은 option id를 입력하고 해당 options로 검사합니다. slider 타입은 숫자로 편집합니다. computed는 읽기 전용입니다. 속성창의 select/range UI를 바에 복제하지 않습니다.
  • 헤더는 49px, 바는 별도 28px입니다. 폭은 최대 1222px이며 객체 수정 영역의 실제 위치부터 부모의 남은 폭을 사용합니다. x=377 고정값은 없습니다. 좁으면 focus 가능한 region 안에서 가로 스크롤하며, 편집 가능한 값은 input focus로 접근할 수 있습니다. 2026-10-21부터 바에서 편집하지 않는 값(읽기 전용·computed·Disabled)은 input 대신 포커스 없는 <output> 텍스트(빨강)로 표시하고 호스트 값을 그대로 따릅니다. 호스트 콘텐츠는 열림 상태에 따라 필요한 28px 공간을 예약하세요. 기존 Dropdown은 바 위에 표시됩니다.

메뉴 컴포넌트 자체 구성

<SWHeader documentTitle={fileName}
  controls={[{ id: "lineStyle", label: "선 종류" }, { id: "layer" }]}
  layers={layers} activeLayerId={layerId} onLayerSelect={selectLayer}
  lineTypes={lineTypes} lineStyle={lineStyle} onLineTypeSelect={selectLineType} />
  • controls?: readonly SWHeaderControlConfig[]는 전체 allowlist입니다. 미전달하면 승인된 기존 구성·계층·1920px 디자인을 유지합니다. []는 선택적 컨트롤 전부 제거입니다. 홈/로고·창 제어는 유지합니다.
  • id: document / drawingMode / layer / color / lineStyle / textStyle / dimensionStyle / details / propertyBar / propertyPanel / print / popupGuide. details는 상세보기 안내 caption이며 필수 액션이 아닙니다.
  • 배열에서 빼거나 visible:false로 숨기며, 배열 순서가 실제 DOM·키보드·배치 순서입니다. 중복 id는 첫 번째 표시 항목만 사용합니다. 숨김 해제/재추가할 수 있습니다. label은 필드 라벨·버튼 이름·More 이름을 바꿉니다. 문서는 실제 documentTitle을 유지하고 접근성 이름 접두사를 바꿉니다. K는 아이콘을 유지하고 접근성 이름을 바꿉니다.
  • 구성 모드는 각 컨트롤을 하나의 객체 수정 영역 안에서 재배치합니다. 기본 모드의 그룹 외곽 배치를 바꾸지 않습니다. 파일·색상 등을 제거한 공간은 고정 슬롯으로 남지 않습니다.
  • 기존 1800/1180/1080/900/640/520px 기준은 유지합니다(2026-10-21부터 헤더 폭 760px 미만에만 적용, 760px 이상은 More 없이 5개 메뉴 유지 — §7 반응형 More). 남은 필드 중 레이어(없으면 배열의 첫 필드)를 대표 슬롯으로 사용하고 More 선택으로 대체합니다. 대표 요소 삭제 시 남은 필드로 대체합니다. 표시 중인 대표는 More에서 제외되고, 구성에서 빠진 요소는 More에서도 사라집니다.
  • 값은 기존 Props/Callback으로 계속 호스트가 소유합니다. 재정렬은 keyed 컴포넌트를 유지하고, 제거된 Dropdown과 draft는 정리하며 데이터 Callback을 생성하지 않습니다. 내부 열림 상태는 제거 후 재추가 시 닫힘입니다. Controlled 열림은 호스트가 전달한 값이 우선합니다. 일반적인 Dropdown 닫힘 시 기존 입력 확정 계약은 유지합니다.
  • 구성요소 배열과 Dropdown 내부 옵션 배열은 별개입니다. layers/colors/userColors/lineTypes/lineWeights/textStyles/dimensionStyles의 동적 기능은 그대로 사용합니다.

Colorize와 CSS 경로

  • 색상 메뉴의 기존 색상 추출 위치에 Figma 26:11302/26:11305 원본 SVG를 연결했습니다. 버튼 24×24, 아이콘 18×18, Hover는 주황 경계와 검정 25% overlay입니다. Hover는 Callback을 실행하지 않으며 클릭/Enter는 기존 onColorPick을 호출하고 메뉴를 닫습니다.
  • 26:11338 color_돋보기 Overlay는 패키지에 구현되어 있지 않습니다. 호스트가 onColorPick에서 연결할 대상이며 임의 대체 색상창은 만들지 않았습니다.
  • CSS 편집 원본은 packages/sw/src/theme/sw.css입니다. SW build가 dist/styles.css를 만들고 Story가 공개 @hm-design/sw/styles.css를 읽습니다. dist만 편집하면 다음 빌드에서 원본이 다시 포함됩니다. 원본 상세 span의 Stroke 선언과 미사용 solid-stroke-width 변수를 제거했으므로 산출물 override는 필요하지 않습니다. 외부 소비 앱에는 SW를 다시 빌드한 뒤 산출물을 갱신하세요.
  • Text Stroke Story의 지정 원본 스크린샷·제목·전용 import를 제거했습니다. 이미지 파일과 나머지 비교 Story·text-shadow는 보존했습니다.
  • 기존 21개 Story에 Property Bar / Dynamic Controls / Colorize를 추가했습니다. 현재 실행 결과는 후속 검증 기록을 따릅니다.

구성요소 표시·메뉴 순서·속성창 4상태·형상 Slot (2026-10-16 3차)

이 절이 같은 주제의 이전 설명보다 우선합니다. 검증 결과는 3차 검증 기록에 별도로 기록했습니다.

로고 필수, 나머지 개별 제거 — hiddenControls

<SWHeader documentTitle={fileName} logoSrc={logoUrl} logoAlt="EasyBeam"
  hiddenControls={["document", "color", "textStyle", "print", "minimize"]}
  /* 값·목록·Callback Props는 기존 그대로 */ />
  • hiddenControls?: readonly SWHeaderHideableControlId[]. 대상 id: home, document, drawingMode, layer, color, lineStyle, textStyle, dimensionStyle, details, propertyBar, propertyPanel, print, popupGuide, windowMove, minimize, close. 로고는 대상이 아니며 항상 표시합니다(logo / logoSrc / logoAlt Props 유지).
  • home을 제거하면 홈 동작만 사라지고 로고는 같은 32×17 슬롯에 정적 이미지로 남습니다(Hover·onHomeClick 없음).
  • 전체 controls 목록 없이 쓸 수 있으며, 남은 요소는 기본 디자인의 크기·계층·Breakpoint를 유지합니다. controls와 함께 쓰면 두 조건을 모두 적용합니다(controls의 visible:false와 같은 의미).
  • 제거한 요소는 렌더링하지 않습니다: 공간 없음, Dropdown·속성창·속성Bar·K 가이드 표시 안 함, Callback 없음. 열린 상태에서 제거하면 내부 열림 상태만 조용히 닫힙니다(데이터 Callback 없음).
  • 배열에서 다시 빼면 재표시됩니다. 선택값은 호스트 Props이므로 그대로이고, 내부 Dropdown은 닫힌 상태로 돌아옵니다.
  • 미전달 또는 []이면 승인된 기본 Header와 동일합니다(1920~390px DOM 위치·크기·색 비교 차이 0).
  • 객체 영역 요소를 제거하면 More 이동은 CSS 대신 스크립트가 같은 Breakpoint(1800/1180/1080/900/640)로 결정합니다(2026-10-21부터 헤더 폭 760px 미만에서만; 760px 이상은 More로 옮기지 않음). 표시할 항목이 없는 폭에서는 More 버튼을 만들지 않습니다. 창 영역은 남은 요소 폭만 차지합니다.
  • 로고만 남겨도 Header 높이 49px와 녹색 객체 영역 배경은 유지합니다.

유사 메뉴 순서 — menuOrder

<SWHeader documentTitle={fileName}
  menuOrder={["color", "layer", "dimensionStyle", "lineStyle", "textStyle"]} />
  • 대상: layer, color, lineStyle, textStyle, dimensionStyle(SWHeaderMenuId). 메뉴는 메뉴가 이미 차지한 자리 안에서만 서로 바뀌며 홈·파일·로고·그리기모드·상세보기·창 제어는 움직이지 않습니다.
  • 미전달: 승인된 기본 순서 레이어 → 색상 → 선스타일 → 문자스타일 → 치수스타일(controls를 쓰면 그 배열 순서).
  • 누락 규칙: 지정한 id가 먼저 지정 순서로 오고, 빠진 id는 그 뒤에 기본 순서로 붙습니다. 중복 id는 첫 번째만, 알 수 없는 id는 무시합니다. 숨긴 메뉴가 들어 있어도 오류 없이 건너뜁니다.
  • 순서는 DOM·Tab 순서·More 목록 순서에 함께 반영됩니다. React key는 메뉴 id라 재정렬해도 값·Callback·Dropdown·Focus가 유지되며, 순서 변경만으로는 어떤 Callback도 호출하지 않습니다.
  • 대표 슬롯: 레이어가 표시 중이면 레이어, 아니면 순서상 첫 메뉴가 기본 대표입니다. More에서 고른 메뉴가 대표 자리를 대신하며, 그 메뉴가 제거되면 기본 대표로 돌아갑니다.

속성창 4가지 상태

새 팝업 없이 기존 SWHeaderPropertyPanel 하나를 호스트 데이터로 바꿉니다.

상태 Figma selection.kind 데이터
선택된 객체 없음 26:11886 · 314×256 none common 그룹만
전체 객체 선택 26:11823 · 314×361 all + counts common 그룹, 혼합값 mixed
원 1개 선택 26:11932 · 314×658 single + overviewTitle common(collapsedCount) + shape(circle) + position
모든 항목 펼침 26:12035 · 314×754 single 위와 같고 expandedGroupIds={["common"]}

Storybook: Property Panel · 선택 없음 / 전체 선택 / 원 1개 / 모두 펼침 개별 Story와 Property Panel · 4개 비교(디자인 검증용, 실측 크기 표시, 좁으면 줄바꿈). 실제 Header는 현재 선택에 맞는 패널 하나만 표시합니다.

형상 영역 Slot — shapeContent

<SWHeader documentTitle={fileName} propertyPanel={{
  selection, properties, groups,
  shapeContent: ({ selection, group, properties }) => <MyShapeViewer objectId={selection.id} />,
}} />
  • layout: "circle" 그룹의 208×208 영역(그룹 260px 안 39, 26)에 렌더링합니다. SWHeaderPropertyPanel과 SWHeader.propertyPanel 모두 같은 Prop입니다.
  • 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 메뉴입니다. 기본 숨김, 로고 클릭으로 열고 다시 클릭하면 닫힙니다.

<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={…} />(선택). panel이 없는 항목은 실행 버튼입니다(클릭 시 onSelect/onItemSelect, 클릭 후 ON 유지 없음). 최근파일만 목록이 열린 동안 Active이며, 유지형이 필요하면 toggle: true.
  • 이벤트 우선순위: homeMenu가 있으면 로고 클릭은 메뉴 토글만 하고 onHomeClick은 호출되지 않습니다. homeMenu가 없으면 기존과 같이 onHomeClick만 호출됩니다.
  • New / Open은 문서 Dropdown 안 그대로입니다(documentOpenPlacement 기본 "menu"). 메뉴가 열리면 필드·문서 Dropdown이 닫히고, 문서 Dropdown을 열면 메뉴가 닫힙니다.
  • 위치: 객체 수정 영역 −2px ~ 창 영역(DOM 측정, 1920에서 x 375 · 폭 1305). 좁은 폭에서는 가로 스크롤 없이 들어가는 섹션만 표시하고 나머지는 끝의 더보기(⋯) 섹션으로 옮깁니다(Dropdown은 섹션 제목 + 기능 텍스트 목록, 타일과 같은 Hover·Active·클릭, 폭이 넓어지면 자동 복귀, 배지 문구 homeMenu.moreLabel).
  • Escape: 하위 패널 → 메뉴 순서, 닫히면 로고로 Focus. 속성창·SWPopup과 같은 Escape 순서(Focus가 모두 밖이면 최근 사용 하나).
  • 상세: 패키지 카탈로그 5절, 통합 검증 기록.