# @hm-design/sw 컴포넌트 카탈로그 기준: `@hm-design/sw` 0.2.0, `dist/index.d.ts` 공개 Export(2026-10-19 통합 개선 반영). 출처 Commit·빌드 도구 버전은 `dist/build-info.json`에 있습니다. 모든 Import는 `from "@hm-design/sw"`이며 CSS는 `@hm-design/sw/styles.css` 한 개입니다. 이 표에 없는 이름은 공개 API가 아닙니다. - **단독 사용:** `SWHeader` 밖에서는 `` 안에 둡니다(토큰·글꼴·버튼 초기화). - **상태 소유:** 값은 Props, 사용자 행동은 Callback입니다. 컴포넌트는 CAD 데이터를 바꾸지 않습니다. 호스트가 Callback을 받아 Props를 갱신합니다. - **열림 상태:** Dropdown은 `open` / `onOpenChange`(Controlled)를 주지 않으면 내부 상태로 동작합니다. - **공통 키보드:** Click·Enter·Space 열기/닫기(Hover로는 열리지 않음), ArrowDown 열고 첫(현재) 항목 Focus, Arrow 이동, Escape·바깥 클릭·Tab 이탈 닫기(Escape는 Trigger로 Focus 복귀). - **검증 표시:** ✅ = 2026-10-19 독립 소비 프로젝트(Tarball 설치)에서 브라우저 검증, ◻ = 소비 검증 범위 밖(Storybook 검증 기록만 있음). - **먼저 모드:** SW 일반 모드(편집) / SW 뷰어 모드(도면 보기)를 [AI_USAGE.md](./AI_USAGE.md) §0으로 정합니다. **뷰어 모드는 §9(`SWViewer*`)만** 쓰고(예외: 창 최소화·닫기 버전의 `trailing` 끝 `SWHeaderWindowControls`, [VIEWER.md](./VIEWER.md) §2.1), 아래 §1~8은 일반 모드용입니다. 두 모드를 한 화면에 섞지 않습니다. - **무엇을 쓸까(일반 모드):** Header 전체 → `SWHeader`(1). Header 부품 조합 → 개별 컨트롤(2). 팝업·설정창·정보창 → `SWPopup`(3). 도구 막대·탭·토글 → `SWToolButton` / `SWToolGroup`(4). 로고 클릭 2단 메뉴·최근파일 → `SWHeader homeMenu` / `SWHomeMenu`(5). 게이지바(투명도 등)는 `SWSlider`, 선택 목록(단위 등)은 `SWSelect`(3). Switch·Badge·Progress·Dialog(모달)·Label·FilterBar는 SW에 없습니다 → `@hm-design/ui`를 그대로 씁니다(필요하면 `SWPopup` children 안에). - **무엇을 쓸까(일반 모드 하단):** 하단 명령 막대(보조기능·명령 칸·분류 탭·명령어 전체보기) → `SWFooter`, 도면 공간 탭(Model/Layout) → `SWFooterLayoutTabs`, 명령창 → `SWFooterCommandLine`(10, [FOOTER.md](./FOOTER.md)). - **무엇을 쓸까(뷰어 모드):** Header → `SWViewerHeader`(`groups` + `trailing`), Dropdown·우클릭 → `SWViewerMenu`, 팝업·설정창·모달 → `SWViewerPanel` + `SWViewerSection`/`SWViewerField`/`SWViewerSwitch`/`SWViewerInput`/`SWViewerButton`, 옵션 패널 게이지 → `SWSlider appearance="property"`, 상태 → `SWViewerStatus`(9). ## 1. 완성형 템플릿 ### `SWHeader` ✅ SW 공식 기준 Header(Figma 26:8944, 1920 × 49). 로고 · 파일 · 객체 수정 영역(그리기모드, 레이어/색상/선/문자/치수스타일, 상세보기) · 창 영역(인쇄·K, 창 이동, 최소화, 종료)과 반응형 More, 속성Bar, 속성창을 포함합니다. | 구분 | Props / Callback | |---|---| | 필수 | `documentTitle: string` | | 구성 | `hiddenControls?: SWHeaderHideableControlId[]`(로고 외 부품 제거, `home` 제거 시 정적 로고. 하위 항목 `documentNew`·`documentOpen`·`layerFreeze`·`layerLock`도 제거 — 레이아웃 변화 없음), `menuOrder?: SWHeaderMenuId[]`(5개 메뉴끼리 순서), `controls?: SWHeaderControlConfig[]`(객체 영역 전체 allowlist·순서·label) | | 로고 오른쪽 | `documentOpenPlacement?: "menu" \| "header"`(기본 `menu` = 기존 Dropdown 하단. `header` = 로고 바로 오른쪽에 파일명과 같은 면의 Open 버튼, Dropdown의 Open은 숨김. 둘 다 `onDocumentOpen`·`documentOpenLabel`), `leading?: ReactNode`(Open 뒤·파일명 앞 추가 부품, 예: `SWToolButton appearance="document"`) | | 로고 | `logo?: ReactNode`, `logoSrc?: string`, `logoAlt?: string`, `onHomeClick`(`homeMenu`가 없을 때만 호출). 로고는 높이 17px·원본 비율이고 홈과 사선 배경이 로고 폭을 따라 늘거나 줍니다. 로고가 없어도 홈과 사선 배경, `onHomeClick`·`homeMenu` 동작은 그대로입니다(빈 로고 자리 32px을 유지해 80 × 48, 2026-10-23). `SWViewerHeader`는 로고가 없으면 로고 면 전체를 그리지 않습니다(이때 `onHomeClick`·`logoMenu`는 누를 곳이 없음) | | 로고 클릭 메뉴 | `homeMenu?: SWHeaderHomeMenu`(= `SWHomeMenu` Props에서 열림 상태 제외, 5절), `homeMenuOpen`, `defaultHomeMenuOpen`(기본 false), `onHomeMenuOpenChange(open, reason: SWHeaderHomeMenuChangeReason)` | | 파일 | `documents`, `activeDocumentId`, `documentMenuOpen`, `onDocumentMenuOpenChange`, `onDocumentSelect`, `onDocumentClose`, `onDocumentNew`, `onDocumentOpen`, `onDocumentClick`, `documentIcon`, `documentNewLabel`, `documentOpenLabel` | | 레이어 | `layer`, `layers`, `activeLayerId`, `layerMenuOpen`, `onLayerMenuOpenChange`, `onLayerSelect`, `onLayerVisibilityChange`, `onLayerFreezeChange`, `onLayerLockChange`, `onLayerDetail`, `onLayerOpen` | | 색상 | `color`, `colors`, `userColors`, `colorMode`, `colorMenuOpen`, `onColorMenuOpenChange`, `onColorSelect`, `onColorModeChange`, `onColorInput`, `onColorPick`, `onColorDetail`, `onColorOpen` | | 선스타일 | `lineStyle`, `lineTypes`, `lineWeights`, `activeLineTypeId`, `activeLineWeightId`, `lineStyleMenuOpen`, `onLineStyleMenuOpenChange`, `onLineTypeSelect`, `onLineWeightSelect`, `onLineWeightValueChange`, `onLineScaleChange`, `onLineStyleDetail`, `onLineStyleOpen` | | 문자/치수 | `textStyle`, `textStyles`, `activeTextStyleId`, `textStyleMenuOpen`, `onTextStyleMenuOpenChange`, `onTextStyleSelect`, `onTextStyleDetail`, `onTextStyleOpen` / `dimensionStyle`, `dimensionStyles`, `activeDimensionStyleId`, `dimensionStyleMenuOpen`, `onDimensionStyleMenuOpenChange`, `onDimensionStyleSelect`, `onDimensionStyleDetail`, `onDimensionStyleOpen` | | 상세 | `propertyBar`, `propertyBarOpen`, `defaultPropertyBarOpen`, `onPropertyBarOpenChange`, `onPropertyBarToggle`, `propertyPanel`, `propertyPanelOpen`, `onPropertyPanelOpenChange`, `onPropertyPanelToggle` | | 창 | `onPrint`, `onPopupGuide`, `onWindowMoveStart(PointerEvent)`, `onMinimize`, `onClose` | | 기타 | `activeMenu`, `disabled`(창 버튼 제외), `drawingModeLabel`, `tools?: ReactNode`(객체 영역 메뉴 뒤), `
` HTML 속성·`ref` | - 데이터 배열(`layers`, `colors`, `lineTypes`/`lineWeights`, `textStyles`, `dimensionStyles`, `documents`)을 주지 않으면 해당 필드는 Dropdown 없는 버튼입니다. - 반응형(2026-10-21): Header 폭 760px 이상에서는 More로 옮기지 않습니다. 레이어·색상·선스타일·문자스타일·치수스타일 5개 메뉴와 타이틀은 항상 보이고, 좁아지면 그리기모드 텍스트 숨김 → 미리보기 5종(레이어 상태 아이콘·색상 칩·선 그림·글꼴명·치수 그림)을 한 번에 함께 숨김(2026-10-23, 모두 들어갈 공간이 생기면 함께 복원) → 파일명 축소(최소 96px, `…`) → 메뉴 값 `…` 순으로 줄입니다. 미리보기가 숨으면 이름·값 텍스트만 남고, 900px 이하는 선스타일 필드에서 가중치·스케일을 숨깁니다. 모든 값과 타이틀이 잘리지 않는 폭은 약 1,200px 이상입니다. 760px 미만은 기존 More 동작을 유지합니다. - 반응형(More·대표 필드 전환·창 영역 축소)은 **`SWHeader` 안에서만** 동작합니다(CSS container `hm-sw-header`). 조합형 구성이 반응형이어야 하면 `controls` + `documentOpenPlacement` + `leading` + `tools`로 `SWHeader` 안에서 구성합니다(예제 F). - (760px 미만) 로고 옆 Open은 실제 공간을 측정해 넘칠 때만 More로 이동하고, 그 시점보다 Open 폭만큼 넓어지면 돌아옵니다. Open이 보이는 동안 파일명이 먼저 줄고 레이어 필드는 Figma 폭을 유지합니다. - (760px 미만) 메뉴도 고정 폭만이 아니라 실제 남은 공간을 기준으로 More에 옮기고 공간이 생기면 원래 자리·순서로 돌려놓습니다(Breakpoint는 그대로, 추가 Prop 없음). 메뉴를 열고 닫아도 Header 요소의 위치·폭은 바뀌지 않습니다. ```tsx setId(l.id)} onClose={closeWindow} /> ``` ## 2. 개별 컨트롤 (조합용) | 컴포넌트 | 용도 | 주요 Props / Callback | 크기(Figma) | 상태 | 검증 | |---|---|---|---|---|---| | `SWTheme` | 단독 사용 범위(토큰·글꼴·초기화). 배치는 정하지 않음 | `div` HTML 속성, `className`, `style`(토큰 override — 가장 바깥 SW 범위에서 지정하면 안쪽 Header·Dropdown·More·Panel·명령창이 상속, [AI_USAGE.md](./AI_USAGE.md) §3.1) | — | — | ✅ | | `SWHeaderHome` | 로고 슬롯 + 홈 버튼 | `logo`, `logoSrc`, `logoAlt`, `onClick`, `interactive`(false = 정적 로고) | 12 + 로고 폭 + 36 × 48(예시 로고 32 × 17에서 80 × 48, 로고 없음도 80 × 48), 오른쪽 −17px로 다음 파일 면과 겹침 | Hover 배경 교체, Focus | ✅ | | `SWHeaderDocumentTitle` | 파일명 + 문서 Dropdown(+ New / Open) | `title`(필수), `documents`, `activeDocumentId`, `menuOpen`, `onMenuOpenChange`, `onDocumentSelect`, `onDocumentClose`, `onNew`, `onOpen`, `onClick`, `documentIcon`, `newLabel`, `openLabel`, `label`, `showNew` / `showOpen`(기본 true, 둘 다 false면 하단 없음) | 높이 48, 폭 190–310(반응형 clamp), 메뉴 310 | 텍스트만 Hover #FFC600, 열림 화살표 회전 | ✅ | | `SWHeaderLayerMenu` | 레이어 필드 + Dropdown | `value`(필수), `layers`(필수), `activeLayerId`, `open`, `onOpenChange`, `onClick`, `selected`, `disabled`, `onLayerSelect`, `onVisibilityChange`, `onFreezeChange`, `onLockChange`, `onDetail`, `label`, `showFreeze` / `showLock`(기본 true. false = 그 상태를 그리지 않고 20px 자리만 유지) | 필드 234 × 38, 메뉴 234 | Hover 링, Selected, Expanded, 행 Hover | ✅ | | `SWHeaderColorMenu` | 색상 필드 + Dropdown(ByLayer/ByBlock, 팔레트, 사용자 색, 추출, RGB 입력) | `value`, `colors`(필수), `userColors`, `mode`, `open`, `onOpenChange`, `onClick`, `selected`, `disabled`, `onColorSelect`, `onModeChange`, `onColorInput`, `onPick`, `onDetail`, `label` | 필드 192 × 38 | 칩 선택 시 열림 유지 | ✅ | | `SWHeaderLineStyleMenu` | 선스타일 필드 + 3열 Dropdown | `value`, `lineTypes`, `lineWeights`, `activeLineTypeId`, `activeLineWeightId`, `open`, `onOpenChange`, `onClick`, `selected`, `disabled`, `onLineTypeSelect`, `onLineWeightSelect`, `onLineWeightValueChange`, `onScaleChange`, `onDetail`, `label` | 필드 284 × 38, 열 182·142·142(필드 중앙 정렬) | 열별 선택, 열림 유지 | ✅ | | `SWHeaderTextStyleMenu` | 문자스타일 필드 + 목록 | `value`, `textStyles`(필수), `activeTextStyleId`, `onTextStyleSelect`, `open`, `onOpenChange`, `onClick`, `selected`, `disabled`, `onDetail`, `label` | 필드 164 × 38 | 선택 시 닫힘 | ✅ | | `SWHeaderDimensionStyleMenu` | 치수스타일 필드 + 목록 | `value`, `dimensionStyles`(필수), `activeDimensionStyleId`, `onDimensionStyleSelect`, 열림·`onDetail`·`label` 동일 | 필드 164 × 38 | 선택 시 닫힘 | ✅ | | `SWHeaderLayerControl` · `SWHeaderColorControl` · `SWHeaderLineStyle` · `SWHeaderTextStyle` · `SWHeaderDimensionStyle` | Dropdown 없는 필드 버튼(호스트가 자체 팝업을 열 때) | `value`(필수), `label`, `selected`, `disabled`, `onClick`, `expanded`, `controls`, `buttonRef` / `SWHeaderLayerControl`은 `state`(표시·동결·잠금), `showFreeze` / `showLock` | 위 필드 크기와 같음 | Hover 링, Selected | ◻ | | `SWHeaderActions` | 인쇄·K 버튼 pill | `onPrint`, `onPopupGuide`, `disabled`, `showPrint`, `showPopupGuide`(하나만 남기면 pill이 축소) | pill 85 × 42(버튼 40 × 38) | 아이콘 Hover, Hover 링, K 가이드 미리보기(반복 Motion, Reduced Motion 정적) | ✅ | | `SWHeaderWindowControls` | 창 이동·최소화·종료 | `onWindowMoveStart`, `onMinimize`, `onClose`, `showWindowMove`, `showMinimize`, `showClose` | 이동 88 × 48, 버튼 32 × 48 | 창 이동 Hover Motion 1.4s, 종료 Hover 자산 | ◻ | | `SWHeaderProperties` | 상세보기 / 속성Bar · 속성창 토글 | `barOpen`, `panelOpen`, `panelId`, `disabled`, `onBarToggle`, `onPanelToggle`, `showCaption`, `showBar`, `showPanel` | 157 × 38 | 텍스트 Hover #FFC600 | ◻ | | `SWHeaderPropertyBar` | 28px 속성 편집 띠 | `selection`, `properties`, `onPropertyChange`, `readOnly`, `disabled`, `div` 속성 | 높이 28(폭이 부족하면 항목이 다음 줄로 넘어가며 27px씩 늘어남, 가로 스크롤 없음, 2026-10-23), 콘텐츠 폭(`SWHeader` 안에서는 오른쪽 끝이 최소 속성Bar·속성창 구분선까지, 토글이 More에 있으면 콘텐츠 폭, 2026-10-23) | Input 상태 | ◻ | | `SWHeaderPropertyPanel` | 데이터 기반 속성창(필드 타입 6종 `text`·`number`·`select`·`color`·`slider`·`computed` + picker, 그룹 레이아웃 `rows`·`circle`·`position`, 형상 Slot) | `selection`·`properties`·`groups`(0.2.0부터 선택), `title`, `children`(그룹 뒤 자유 콘텐츠), `autoFocus`, `onPropertyChange`, `menus`(`showLayerFreeze`/`showLayerLock` 포함), `expandedGroupIds`/`defaultExpandedGroupIds`/`onExpandedGroupsChange`, `toolActive`/`onToolActiveChange`, `toolLabel`, `onOverviewClick`, `shapeContent`, `onClose`, `id`, `anchorRef`, `style` | 314 폭, 높이는 콘텐츠(최대 화면, 본문 스크롤). `anchorRef`가 있으면 고정 위치 Overlay, 없으면 문서 흐름(위치는 `style`) | 4가지 선택 상태 | ✅ | | `SWHeaderInput` | 수정 가능한 SW Input | `value`(필수), `onValueChange`, `onCommit`(Enter·Blur, 유효 + 변경 시), `onCancel`(Escape 복원), `valueType`(`text`/`number`), `parse`, `readOnly`, `disabled`, `commitOnUnmount`, `input` 속성 | 58 × 20 기준, Hover/편집 24px | Default/Hover/편집/ReadOnly/Disabled, IME 조합 중 Enter 무시 | ✅ | | `parseSWHeaderColor` · `parseSWHeaderNumber` | Input 검증 도우미 | `r,g,b` / `#rrggbb`, 숫자(`min`, `max`) | — | — | ◻ | | `SWHeaderLinePreview` · `swHeaderLinePatterns` | 70 × 2 선 미리보기, CENTER/HIDDEN 패턴 | `pattern?: number[]` | 70 × 2 | — | ◻(패턴 상수만 예제 데이터에서 사용) | ## 3. Popup — 외형 고정, 콘텐츠 자유 속성창(Figma 26:11822)의 외형과 공통 동작을 그대로 쓰는 Popup입니다. Header 없이 `` 안에서 씁니다. 내용은 `children`으로 자유롭게 구성합니다(SW 컨트롤, `@hm-design/ui` 컴포넌트, 소비 프로젝트 요소 모두 가능, 구조 제한 없음). | 컴포넌트 | 용도 | 주요 Props | 크기·동작 | 검증 | |---|---|---|---|---| | `SWPopup` | 설정·정보·사용자 정의 Popup 프레임 | `title`(필수), `children`, `onClose`(닫기 버튼·Escape), `closeLabel`(기본 `닫기`), `titleActions`(제목 막대 닫기 버튼 앞 추가 버튼), `footer`(본문 뒤 고정 영역, 본문이 스크롤해도 보임), `autoFocus`(열릴 때 Focus, 안에서 닫히면 원래 위치로 복귀. 기본: `anchorRef`가 있으면 true, 없으면 false), `anchorRef`(기준 요소 아래 고정 Overlay), `style`·`className`·`div` 속성 | 폭 314, 제목줄 30, 높이는 콘텐츠(최대 화면 − 8px, `style.maxHeight`로 더 줄일 수 있음) → 본문 스크롤. Non-modal(Dim·Portal·Focus trap 없음) | ✅ | | `SWPopupSection` | 속성창 그룹 면(제목 + 내용) | `title`, `children`, `section` 속성 | 그룹 제목 700·70% 검정, 내용 면 `--hm-sw-property-content` | ✅ | | `SWPopupRow` | 속성창 한 줄(라벨 + 값) | `label`(필수), `htmlFor`, `children`(값 컨트롤, SW 필드는 `SWHeaderInput`), `unit`, `field`(`box` 기본 · `compact` · `none`), `numeric`(숫자 편집 On), `readOnly`, `disabled` | 행 최소 32, 라벨 72, 값 상자 28(Default·Hover 테두리, 숫자 편집 시 #0F0D0A). `compact` = 41px hug 숫자 필드 | ✅ | | `SWSlider` | 게이지바(native range, 2026-10-20). `SWPopupRow field="none"` 안이나 `SWTheme` 안 어디든 | `value`(필수), `min`(0)·`max`(100)·`step`(1), `onValueChange`(드래그·키보드마다), `onValueCommit`(값 확정 = native `change`), `appearance`(`"panel"` 기본 · `"property"`), `disabled`, `aria-label`, `input` 속성 | 모양만 다르고 값·이벤트·키보드는 같음. `panel` = 지금까지 Popup에서 쓰던 게이지바(6px 트랙 · 18px 흰 Thumb + 2px 녹색 테두리, `SWViewerSlider`와 같은 모양), `property` = 속성창 게이지바(베이지 트랙 · 빨강 채움 · Figma Thumb 14px). 폭은 행을 채움(호스트가 `style.width`로 지정 가능). Disabled 40%. 뷰어 옵션 패널 게이지는 `property`만(2026-10-22, VIEWER.md §4) | ◻ | | `SWPopupPropertyRow` | **Popup 안 레이어·색상·선스타일 선택 행의 공식 컴포넌트**(2026-10-20). 속성창 한 줄 | `property`(`SWHeaderProperty`: `name`·`value`·`type`. 레이어 = `type: "select"` + `picker: "layer"` + `options`, 색상 = `type: "color"`(picker 자동), 선스타일 = `type: "select"` + `linePreview: true` 또는 `picker: "lineStyle"`), `menus`(`SWHeaderPropertyMenus`: `layers`·`colors`·`lineTypes`·…), `onPropertyChange(property, value)`(선택한 행 id 또는 입력값, 호스트가 `property`를 갱신), `className` | `SWHeaderPropertyPanel` 행과 같은 DOM·모양·동작(속성창 `picker` 필드, 목록은 고정 Overlay라 스크롤 본문에 잘리지 않음). **SWHeader 필드 Dropdown(`SWHeaderLayerMenu` 등)을 Popup에 넣지 않습니다** | ◻ | | `SWSelect` | 선택 목록(native select, 2026-10-20). `SWPopupRow field="none"`, `SWViewerField`, `SWTheme` 안 어디든 | `value`/`defaultValue`, `options`(`SWSelectOption[]` = `{ value, label?, disabled? }`) 또는 `