- SW Header: the five field previews hide and return together in one measured step; from 760 to 1920px the groups from 레이어 to 치수스타일 share the room left before 속성보기, above 1920px they keep the 1920 widths (controls bars and < 760px unchanged) - SW Viewer Header Model / Layout: long names ellipsize in equal buttons (minmax(0, 1fr), name counted at --hm-sw-viewer-tab-name-basis) so they never change the box or the measured steps; green outer outline and its tokens removed; 378px / 42px, text-shadow and state unchanged - SWViewerStatus: the label keeps the width of its widest real state text (idle keeps its icon slot), so a state change never moves the header; with too little room it ellipsizes after the file name gives way - Popups: body inset shadow removed, title SVG drop shadow removed, viewer panel title icons use --hm-sw-viewer-panel-icon-color (#c7bda9) - Storybook: SW/Viewer › Model / Layout story removed - AI guide (docs/ai + package copies): design-only changes for every component, content limits for Submodule consumers (Header excluded), Submodule usage rules; VIEWER / DESIGN_RULES synced - viewer.browser.mjs: explicit 1920 / 1440 labels and approved 1280 icon-only expectations
33 KiB
@hm-design/sw — 뷰어 모드 (Viewer Header · Popup) 사용 지침
기준: 2026-10-19, 참고 DWG/DXF 뷰어(dwg-dxf-viewer-hm) 상단 메뉴·팝업을 같은 해상도(1440 × 900)에서 실측해 패키지 컴포넌트로 옮긴 결과입니다. 아래 이름은 모두 from "@hm-design/sw" 공개 Export이고 CSS는 @hm-design/sw/styles.css 한 개입니다. 예제: examples/ExampleH-ViewerMode.tsx.
모드 선택 먼저: 이 문서는 SW 뷰어 모드 전용입니다. 화면이 뷰어 모드인지는 AI_USAGE.md §0(설정
HMDesign 사용 유형: SW 뷰어→ 요청 문구 → 불명확하면 질문)으로 정합니다. 전체 Export 목록은 COMPONENT_CATALOG.md §9, 토큰·상태 요약은 DESIGN_RULES.md §11, 조합은 COMPOSITION_PATTERNS.md 7입니다.일반 모드 금지 사항(상호 참조): 뷰어 화면에
SWHeader·SWPopup·SWPopupSection·SWPopupRow·SWToolButton·SWToolGroup·SWHeaderPropertyPanel을 쓰거나 재스타일하지 않고, 일반 모드 화면에SWViewer*와--hm-sw-viewer-*토큰을 쓰지 않습니다.SWViewerPanel이 내부에서SWPopup프레임을 렌더링하는 것과 두 Header가--hm-sw-header-document-*·object-*·property-*·외곽선 토큰을 공유하는 것은 패키지 구현이며 모드 혼합이 아닙니다(AI_USAGE.md §0.3). 유일한 예외는 창 최소화·닫기 버전입니다:SWHeaderWindowControls(창 이동 없이 최소화·닫기)를trailing오른쪽 끝에 그대로 두는 구성은 2026-10-21 승인된 구성입니다(§2.1). 패키지에 없는 뷰어 UI는 AI_USAGE.md §0.4 뷰어 열을 따릅니다.
1. 언제 무엇을 쓰나
| 화면 | 사용 | 쓰지 않음 |
|---|---|---|
| SW 일반 모드 Header | SWHeader (49px, Figma 26:8944) |
SWViewerHeader |
| SW 뷰어 모드 Header | SWViewerHeader (52px, 참고 뷰어) |
SWHeader에 뷰어 메뉴를 억지로 넣기, 직접 만든 툴바 |
| 뷰어 메뉴 Dropdown·서브메뉴·우클릭 메뉴 | type: "menu" 항목의 entries, 단독은 SWViewerMenu |
@hm-design/ui DropdownMenu를 Header 안에 직접 배치 |
| 뷰어 팝업·설정창·도구 패널·입력 팝업 | SWViewerPanel (+ modal) |
새 프레임 작성, SWPopup(일반 SW 속성창 프레임)을 뷰어 디자인으로 재스타일 |
| 일반 SW 팝업·속성창 | SWPopup, SWHeaderPropertyPanel (변경 없음) |
SWViewerPanel |
- 한 화면에는 Header 하나만 둡니다. 모드 전환은 프로그램이 Header 컴포넌트를 바꾸는 방식입니다(Storybook
SW/Viewer › SWHeader vs Viewer Header). 파일·패널 상태는 프로그램 상태라 Header를 바꿔도 유지됩니다. - 뷰어 모드 진입·종료, 파일 처리, DWG/DXF 렌더링, 메뉴 명령 실행은 프로그램 담당입니다. 패키지는 UI와 공통 인터랙션(열기/닫기, Focus, 키보드, 위치·크기)만 담당합니다.
- 기능이 없는 메뉴는 만들지 않습니다. 연결할 명령이 없으면 항목을 빼거나
disabled로 두고 “미연결”로 보고합니다.
2. SWViewerHeader
로고 면(사선) → 파일명 면 → 객체 수정 면(메뉴 그룹) → 오른쪽 끝 영역(placement: "end" 그룹 + trailing).
| Props | 설명 |
|---|---|
groups: SWViewerHeaderGroup[] |
메뉴 데이터(순서대로 배치). 그룹: id, label(접근성 이름·More 구획 제목), moreLabel?, surface?: "plain" | "field"(field = 사용자 확정 속성 입력 면), placement?: "start" | "end", hidden?, items |
항목 SWViewerHeaderItem |
공통: id, label, icon?(16px, 프로그램 제공), title?, disabled?, hidden?, collapseBelow?(공간이 부족할 때 More로 옮기는 대상·순서 — 값이 큰 것부터, px 기준 아님), iconOnlyBelow?(공간이 부족할 때 아이콘만으로 줄이는 대상, icon 필요 — 값의 크기는 무관), iconOnly?, moreLabel? |
type: "button" |
onSelect |
type: "toggle" |
pressed, onPressedChange (패널 열기/닫기 등) |
type: "menu" |
entries: SWViewerMenuEntry[], menuWidth?, menuAlign?: "start" | "end" |
type: "tabs" |
value, options: { value, label, count?, title?, disabled? }[], onValueChange (Model / Layout). 폭(2026-10-23): 두 버튼은 항상 같은 폭(가장 긴 이름 + 개수 이상이라 겹치지 않음). tabs만 있는 그룹은 Header에 남는 공간만큼 넓어져 field 면 최대 378px(--hm-sw-viewer-tabs-max-width), 공간이 적으면 그만큼만 넓어지고 기본 폭보다 부족하면 기존 측정 단계대로 More로 이동합니다. 모양(2026-10-23, Figma 7652:40344): tabs만 있는 그룹은 field 면 없이 옆 field 그룹과 같은 42px(--hm-sw-viewer-control-height + 2px) 어두운 컨테이너(검정 여백 위·좌우 1px·아래 1.5px, 안쪽 그림자; 녹색 외곽선은 2026-10-23 제거)가 면이 되고, 버튼(컨테이너를 채워 37.5px)은 같은 Gradient 면과 1.428px Gradient 테두리(선택 금색 #a67d03→#fadd4f→#926e03 + 글자 #ffc600, 비선택 #189b6e→#287a5e + 흰 글자 + 상단 초록 광택), 이름 14px 700·개수 14px 400, 간격 14px, 긴 이름은 버튼 안에서 이름부터 …로 줄고 개수는 그대로(두 버튼 같은 폭·최대 378px 유지; 공간 계산에서 이름은 --hm-sw-viewer-tab-name-basis(3em)까지만 세므로 긴 이름이 탭 폭을 늘리거나 다른 메뉴의 아이콘화·More 이동을 바꾸지 않음; 전체 이름은 접근 가능한 이름으로 남고 툴팁이 필요하면 option title)(버튼 좌우 여백 5px), 객체 면 첫 그룹이 이 탭만 가진 경우에만 그룹 오른쪽 여백 1px(조건부, 다른 Header 그룹에는 없음 — 다음 그룹까지 1180px 이상 5px, 미만 3px; 최대 378px·높이 42px·More 이동 단계 그대로), 글자 외곽선은 기존 그대로입니다. 색은 --hm-sw-viewer-tabs-*·--hm-sw-viewer-tab-* 토큰으로만 바꿉니다(목록·기본값·Override: DESIGN_RULES.md §11) |
type: "switch" |
checked, onCheckedChange (2.5D 등, 위 11px 캡션) |
type: "custom" |
render(), menuEntries?(More에 넣을 항목) |
onCommand(command) |
모든 명령을 한 곳에서 받습니다: { id, itemId, value? }. 항목 자체 Callback 다음에 호출 |
documentTitle, documentPlaceholder, documentTooltip, document? |
파일명 면 |
documents?: SWHeaderDocument[], activeDocumentId?, onDocumentSelect?(document) |
파일명 클릭 시 아래 문서 목록(샘플 도면 등). SWHeader 파일명 목록(SWHeaderDocumentTitle)과 같은 모양·키보드(ArrowDown 열기, Escape, 바깥 클릭)이며 Open·+ New는 표시하지 않음. 목록 외곽 너비는 파일명 버튼 외곽 너비와 SWHeader 목록 너비(312px) 이상이며 긴 항목이면 넓어집니다. 열려도 로고가 위에 표시됨. 선택 시 프로그램이 파일을 불러오고 documentTitle·activeDocumentId를 갱신. document가 있으면 document가 우선 |
onDocumentOpen?(), documentOpenLabel? |
로고 오른쪽 파일 열기 버튼(SWHeader documentOpenPlacement="header"와 같은 파일명 면 SWToolButton appearance="document" + 폴더 아이콘(다른 Header 아이콘과 같은 검정 외곽선 SWViewerIcon, 테두리·면 없음) + documentOpenLabel, 기본 Open). 아이콘 영역 16 × 16(--hm-sw-viewer-icon-size, 여백 0), 버튼 padding 0 12px 0 8px(로고 다음) · 로고가 없으면 0 12px(패키지 CSS 고정값, 2026-10-23). 공간이 부족하면 아이콘만(라벨은 Tooltip·접근성 이름) 표시하고 More로 옮기지 않습니다. 실행 후 onCommand({ id: "open", itemId: "document" }) |
logoMenu?: { title?, items } |
(2026-10-21 이후 뷰어 화면에서 사용하지 않음, 호환용) 로고 클릭 메뉴. 파일 열기는 onDocumentOpen 버튼을 씁니다. 항목 { id, label, icon?, value?, checked?, disabled?, hidden?, onSelect? } |
logo?, logoSrc?, logoAlt?, onHomeClick? |
SWHeader와 같은 로고 Props. 주지 않으면 아무것도 그리지 않습니다(“HM” 문자·기본 아이콘 없음. 2026-10-23부터 로고 면도 그리지 않아 폭·여백이 0이고 파일 열기 또는 파일명이 Header 왼쪽 끝에서 12px 안쪽으로 시작합니다. 이때 onHomeClick·logoMenu는 누를 곳이 없습니다). 로고를 주면 SWHeader와 같은 내부 로고 부품과 같은 사선 배경(Figma 홈 에셋)·폭 계산을 씁니다: 높이 17px·원본 비율, 왼쪽 12 + 로고 + 36(다음 부품과 17px 겹침, 예시 로고 79px), 로고는 가로·세로 가운데, 다음 부품 시작 여백 8px. Hover 효과는 Viewer 기존 그대로(없음)입니다(2026-10-23) |
trailing?: ReactNode |
오른쪽 끝 자유 영역: SWViewerStatus, SWViewerHeaderButton, 창 최소화·닫기 버전이면 맨 끝에 SWHeaderWindowControls(§2.1). 이 세 부품은 SWHeader 창 영역처럼 전체 높이(48px)의 칸이 이어진 띠로 그려집니다: 같은 면, 칸 앞 1px 구분선(라이선스 버튼 앞뒤에는 없음), 위·아래 경계는 객체 면 테두리 하나라 모든 칸의 위·아래가 같음, 칸 사이 여백 없음, 객체 면 오른쪽 끝까지(2026-10-21). 메뉴 면은 띠 앞에서 SWHeader 객체 영역처럼 오른쪽 라운드(--hm-sw-header-object-radius 6px, 같은 면·테두리)로 끝나고 2px 간격 뒤에 띠가 옵니다. 상태 칸은 표시 전용, 아이콘 버튼(정보·라이선스) 칸은 테두리·선 없음 |
moreLabel?, toolbarLabel?, <header> 속성, ref |
- 반응형(2026-10-21, 실제 가용 폭 기준 3단계): 고정 Breakpoint를 쓰지 않고 Header 행의 실제 남는 폭(로고·파일명·메뉴·Gap·Padding·More 버튼 포함)을 측정합니다. 파일명 면은 글자 수가 아니라 Header 폭으로 정한 기본 폭으로 계산하므로, 같은 폭에서는 짧은 파일명과 긴 파일명의 단계·파일 열기 버튼·파일명 면 너비가 같고 긴 이름은
…입니다(2026-10-21). ① 모두 들어가면 기본 형태(아이콘+텍스트), ② 부족하면icon과iconOnlyBelow가 있는 항목을 모두 아이콘만(라벨은 Tooltip·접근성 이름), ③ 그래도 부족하면collapseBelow항목을 하나씩 More(⋯, 베이지 260px Dropdown)로 옮깁니다(값이 큰 것부터, 같으면 오른쪽 먼저). 넓어지면 More → 아이콘 → 기본 순서로 복귀하고, 상단과 More에 같은 항목이 동시에 나오지 않습니다.collapseBelow·iconOnlyBelow의 숫자는 px Breakpoint가 아니며 순서만 정합니다(iconOnlyBelow는 지정 여부만,collapseBelow는 값이 큰 것부터; Props 형식 동일). 로고 옆 파일 열기 버튼(onDocumentOpen)은 ② 단계에서 아이콘만으로 줄고 More로 옮기지 않으며, 샘플은documents. 레이어·설정·속성은collapseBelow없이 아이콘까지만. 1180px 미만 간격 2px, 560px 미만 상태 글자 숨김(CSS container)은 그대로입니다. 가로 스크롤은 만들지 않습니다. - More는 그룹/항목 단위 구획으로 자동 구성됩니다: menu·tabs는 자기 구획(
moreLabel), button·toggle·switch는 그룹 구획(group.moreLabel ?? group.label). toggle·switch는 체크 항목, tabs는 라디오 항목이 됩니다. - Dropdown 색(2026-10-21): Header 안의 Dropdown — 메뉴 버튼(
type: "menu"), More(⋯), 로고 메뉴 — 은 크기·간격·글자는SWViewerMenu그대로, 색은 일반SWHeader의...메뉴와 같습니다(면 #0e3d2f, Border--hm-sw-header-object-border-color, 그림자 0 4px 12px rgba(0,0,0,.5), 글자--hm-sw-header-text-color, 보조 글자는 같은 색 80%, Hover--hm-sw-header-hover, 구분선--hm-sw-header-document-menu-divider-color). 파일명 목록(documents)은SWHeader파일명 목록 모양을 유지하고, 단독SWViewerMenu(우클릭 메뉴 등)는 기존 색 그대로입니다. - 상태 표현: Hover·Active·Pressed·열림·선택 탭 = 글자·아이콘 #FFC600(면·선택 박스 없음, Hover 전후 크기·위치 동일 — 2026-10-21 Hover 면 #266651 제거), Disabled = 60% 불투명, Focus = 2px #FFC600 외곽선.
- 글자·아이콘 외곽선(SWHeader와 동일): 14px 글자
--hm-sw-header-text-outline(검정 1.3px), 11px 캡션·개수·상태--hm-sw-header-label-outline(검정 1px), 아이콘SWViewerIcon(SWHeader 아이콘 자산의 1px 검정 바깥 Stroke를 SVG stroke로 재현: 아이콘 뒤 복사본의 모든 도형을 검정, 원래 선 두께 + 화면 기준 양쪽 1px(--hm-sw-viewer-icon-outline-width)로 그림. 위 아이콘은 그대로라 색·모양·선 두께 유지. CSSfilter미사용, glow는 생략). Header 항목·SWViewerHeaderButton의icon은 자동 적용,custom/trailing의 직접 그린 아이콘은<SWViewerIcon>으로 감쌉니다. 색·크기·굵기·간격은 그대로이고 Hover/Active는 채움 색만 바뀝니다. Dropdown·패널 안에는 외곽선이 없습니다(SWHeader More 메뉴와 같음). 프로그램 아이콘은currentColor로 그리면 상태 색과 외곽선이 자동 적용됩니다.
2.1 두 가지 구성 (2026-10-21)
| 버전 | 구성 | 사용 |
|---|---|---|
| 기본 | trailing에 SWViewerStatus·SWViewerHeaderButton 등만 |
창 버튼이 없는 화면(웹 뷰어, 프로그램이 창 틀을 따로 가짐) |
| 최소화·닫기 | 기존 trailing 항목 뒤 맨 끝에 <SWHeaderWindowControls showWindowMove={false} onMinimize={…} onClose={…} /> |
설치형 프로그램처럼 Header 오른쪽 끝에 창 최소화·닫기가 필요한 화면 |
<SWViewerHeader groups={groups} onCommand={run} trailing={<>
<SWViewerStatus … />
<SWHeaderWindowControls showWindowMove={false} onMinimize={minimizeWindow} onClose={closeWindow} />
</>} />
- 새 API는 없습니다(
trailing슬롯 사용). 버튼 아이콘·크기·Hover는SWHeader와 같고, 맨 끝에 두면 오른쪽 여백 없이 Header 끝에 붙습니다(패키지 CSS가 처리). 창 이동(showWindowMove)은 뷰어 버전에 넣지 않습니다. - 실제 창 최소화·닫기는 프로그램 담당입니다(
onMinimize·onClose). Storybook:SW/Viewer › Viewer Header의 두 번째 섹션. - 이 버튼 외의
SWHeader부품(필드·Dropdown·SWHeaderActions등)은 뷰어 화면에 넣지 않습니다.
2.2 상태 Label(SWViewerStatus) 너비 (2026-10-23)
- 원칙: 상태에 따라 텍스트가 변경되는 Label은 표시 가능한 텍스트의 최대 렌더링 너비를 기준으로 영역을 확보한다. 상태값 변경만으로 Label 너비와 주변 메뉴 위치가 불필요하게 변하지 않도록 한다. 반응형 환경에서 가용 공간이 부족하면 Label 영역을 축소하고 긴 텍스트는 말줄임표(
…)로 처리한다. 화면 공간이 다시 확보되면 전체 텍스트를 정상 표시한다. - 구현(패키지가 처리): 후보는 상태별 기본 Label(
idle준비 ·loading불러오는 중 ·ready도면 열림 ·error불러오기 실패)과 전달한label입니다. 모두 같은 Grid 칸에 두고 현재 것만 보이므로 칸 너비 = 실제 폰트로 그린 가장 긴 후보(기본 폰트 11px 400에서 "불러오기 실패" 약 64px)이며, 글자 수로 계산하지 않습니다.idle도 아이콘 자리를 비워 두어 상태가 바뀌어도 상태 칸 전체 너비가 같습니다. 상태별 기본 Label이 추가되면 자동으로 후보가 됩니다. - 반응형: 측정 단계(아이콘화 → More)와 파일명 축소(최소 56px)가 먼저 적용되고, 그래도 부족할 때만 Label이 줄며
…가 됩니다. 넓어지면 전체 텍스트로 돌아오고, 560px 미만에서는 기존대로 Label을 숨깁니다. 소비 프로젝트는 Label 폭을 따로 지정하거나 상태마다 다른 길이의 임의 문구를 넣지 않습니다(필요한 문구는 실제 상태 데이터로label에 전달).
3. SWViewerMenu / SWViewerMenuEntry
entries: item(icon?, shortcut?, onSelect), checkbox(checked, onCheckedChange), radio(checked, onSelect), submenu(entries — 다단계), label, separator. 공통 disabled?, hidden?, keepOpen?.
- 테마:
SWViewerMenu에는styleProp이 없습니다. 토큰은 메뉴를 감싼 바깥 SW 범위(SWViewerHeader·SWViewerPanel·<SWTheme>)에서 상속됩니다. 단독 우클릭 메뉴의 값을 바꾸려면 메뉴를<SWTheme style={theme}>안에 둡니다(AI_USAGE.md §3.1). - Header의
type: "menu"가 이 Dropdown을 엽니다. 다른 위치(도면 우클릭 등)는SWViewerMenu를open/onOpenChange+anchorRef(버튼 아래 4px) 또는anchorPoint(포인터 위치)로 씁니다.surface="beige"는 More 면,initialFocus는first(키보드 열기) /menu(클릭 열기). - 키보드: Enter·Space·ArrowDown 열기, ↑↓ Home End 이동, → 서브메뉴 열기, ← 한 단계 닫기, Escape 닫고 Trigger로 Focus 복귀, Tab·바깥 클릭 닫기. 화면 끝에서는 위/왼쪽으로 뒤집혀 화면 안에 남습니다.
4. SWViewerPanel — 팝업 프레임 + 자유 콘텐츠
프레임은 공통 SW 팝업 SWPopup과 같은 컴포넌트·DOM입니다(30px 제목 막대·Figma 제목 아트, #FFC600 700 제목 + 검정 1px 외곽선, SW 닫기 버튼, 1px #A67D03 테두리, 반경 4, #E4DBC9 면). 뷰어는 여기에 드래그·모서리 크기 조절·접기·경계 유지·앞으로 가져오기·모달을 더합니다. 314px가 아닌 폭에서도 제목 아트 곡선은 닫기 버튼에 붙어 있고 왼쪽 평면이 늘어납니다. 내용은 children·footer가 담당합니다. 일반 SW 팝업은 SWPopup(titleActions?, footer? 추가)을 그대로 씁니다.
| Props | 설명 |
|---|---|
title, open, onClose, closeLabel? |
필수는 title·open·onClose. 제목 아이콘은 없습니다(SW 팝업 디자인) |
children |
스크롤 본문. SWViewerSection(카드/plain, 12px 제목), SWViewerField(라벨 + 컨트롤, inline / stacked + below), SWViewerSwitch, 게이지 SWSlider appearance="property"(옵션 패널 기준, 아래 옵션 패널 항목), SWViewerInput, SWViewerButton, SWViewerBadge, SWViewerHint, 프로그램 목록·표 등 무엇이든 |
footer? |
하단 버튼 영역(본문 스크롤과 무관하게 고정). SWViewerButton variant="outline" | "primary". tone="green" = 기본은 그대로, Hover는 primary(확인) 버튼 Hover와 같은 토큰 --hm-sw-viewer-hover, 누르는 동안 primary 면 --hm-sw-viewer-panel-primary (레이어 전체 표시는 footer 버튼이 아니라 §4.1의 전체 레이어 전구) |
headerActions? |
제목 막대 추가 버튼: SWViewerButton variant="ghost" (26px, 기본 아이콘색 --hm-sw-viewer-panel-icon-color #c7bda9 — 접기 버튼과 같음, 글자색만 Hover #FFC600 — SW 제목 막대 방식) |
width?(314 = SWPopup), height?(420, "auto" 가능), minWidth?(250), minHeight?(140) |
메뉴별 크기. 최대 90vw × 90vh |
defaultPosition? |
{ left, top } 또는 "top-end"(기본) / "top-start" / "center"(modal 기본), inset?: { top?, side? }(기본 56 / 24 = 52px Header 아래) |
strategy? |
fixed(기본, 화면) / absolute(가장 가까운 positioned 조상 안 — 미리보기·임베드) |
draggable?, resizable?, collapsible?, defaultCollapsed?, collapsed?, onCollapsedChange?, onPositionChange?, onSizeChange? |
제목 막대 드래그, 오른쪽 아래 모서리 크기 조절, 접기(버튼·제목 더블클릭). 이동·크기는 경계 10px 안 |
modal? |
10% 검정 배경, Focus Trap, 가운데 배치(입력·확인 팝업) |
autoFocus? |
열 때 패널로 Focus, 닫을 때 원래 위치로 복귀(기본 true) |
- Escape는 Focus가 패널 안에 있을 때 그 패널만 닫습니다. 비모달 패널은 바깥 클릭으로 닫히지 않습니다(참고 뷰어와 동일, 도구 창). 마지막으로 누른 패널이 맨 앞으로 옵니다.
- 콘텐츠 우선순위:
children·footer는 타입상ReactNode이고 소스 주석도 자유 콘텐츠(“any component”)라고 적습니다. 그래도 기본은SWViewerSection·SWViewerField·SWViewerSwitch·게이지SWSlider appearance="property"·SWViewerInput·SWViewerButton·SWViewerBadge·SWViewerHint와 선택 목록SWSelect(축척 지정 단위, 뷰어 패널 토큰으로 표시) 우선입니다. 이것으로 안 되는 컨트롤(예: Select·Checkbox·표)만@hm-design/ui컴포넌트나 소비 프로젝트 요소를 넣을 수 있습니다.@hm-design/ui는 SW 패키지 dependency가 아니어서 별도 설치(사용 유형 혼합)가 필요하고,SWPopup과 달리 뷰어 패널 안@hm-design/ui외형 정합성은 문서화·검증 기록이 없으므로 결과 보고에 “디자인 확인 필요”로 적습니다. Header 안에는@hm-design/uiDropdownMenu 등을 직접 두지 않습니다(§1 표). - 콘텐츠 교체: 같은 프레임에 다른
children을 넣습니다(레이어 목록 / 옵션 / 속성 / 입력 폼 — StorybookSW/Viewer › Settings Popup). 프레임을 복사하거나hm-sw-viewer-*class로 새 팝업을 만들지 않습니다. - 본문 하단 경계(2026-10-22): 뷰어 패널 본문은 아래 7px 여백 대신 스크롤 영역 밖의 8px 투명 경계를 둡니다(패키지 CSS,
padding-bottom: 0+border-bottom: 8px solid transparent). 스크롤된 목록 행은 테두리 8px 위에서 잘리고, 맨 아래로 스크롤하면 마지막 행이 다 보입니다. 소비 코드에서 하단 여백·padding을 덧대지 않습니다. 일반SWPopup은 7px 그대로입니다.
4.1 레이어 Popup — 개별·전체 표시·숨김 모두 전구 아이콘 버튼 (필수, 2026-10-22)
규칙: SW Viewer 레이어 Popup에서 레이어별 표시·숨김은 SW Header 레이어 메뉴와 같은 전구 아이콘(
SWLayerVisibilityIcon) 버튼을 사용한다(2026-10-21 눈Eye/EyeOff규정 대체). 전체 레이어 켜기·끄기도 Popup 상단의 같은 전구 버튼(SWLayerVisibilityIcon) 하나로 제어한다(2026-10-22SWViewerSwitch규정 대체). 개별·전체 표시·숨김을 Switch·Checkbox로 대체하지 않는다.
- 패키지에는 레이어 목록 전용 컴포넌트가 없습니다. 목록은
SWViewerPanel>SWViewerSection surface="plain"안에 프로그램이 두고, 위에서부터 검색(선택) · 전체 레이어 전구 · 목록 순서입니다. 검색과 전체 레이어 행은 고정이고 목록만 세로 스크롤합니다: 바깥 Section(app-…)에flex: 1 1 auto; min-height: 0, 목록에flex: 1 1 auto; min-height: 0; overflow-y: auto; scrollbar-gutter: stable, 나머지 자식은flex-shrink: 0(소비 앱 class, 예제example-layout.css). 패널 본문 자체는 스크롤되지 않으며 팝업 크기(기본 314 × 420)는 그대로입니다. 검색과 전체 레이어 사이에는 구분선이 없고, 전체 레이어 행 아래에만 연한 1px 선#d2c8b4(--hm-sw-property-table,padding-bottom: 8px)를 둡니다. 행은 색 점 · 레이어 이름 · 개수(SWViewerBadge) · 전구 아이콘 버튼 순서입니다(StorybookSW/Viewer › Viewer Header의 레이어 Popup, 예제 ExampleH-ViewerMode.tsx). - 전구 아이콘 버튼: 프로그램의
<button type="button">24 × 24, radius 4, Hover 면rgba(154, 146, 132, .3),cursor: pointer안에<SWLayerVisibilityIcon on={visible} />(20 × 20, 장식용). 표시 중이면 색 있는 전구, 숨김이면 회색 전구(Header 레이어 메뉴와 같은 그림). 숨긴 행은 불투명도 0.45. 아이콘을 직접 그리거나hm-sw-header__*class를 붙이지 않습니다. - 접근성 이름에 다음 동작을 넣습니다: 표시 중
"<레이어> 숨기기", 숨김"<레이어> 표시하기"(title도 같은 동사). - 클릭하면 그 레이어 하나의 가시성만 바꾸고 프로그램의 가시성 Callback·상태를 그대로 호출합니다. 아이콘만 그리고 기능을 빼지 않습니다.
- 전체 레이어 전구(2026-10-22):
<SWViewerSection surface="plain" title="전체 레이어" className="app-…"><button type="button" role="switch" aria-checked={allVisible} aria-label="전체 레이어" className="app-…-bulb" onClick={() => setAll(!allVisible)}><SWLayerVisibilityIcon on={allVisible} /></button></SWViewerSection>. 버튼은 행의 전구 버튼과 같은 class·크기(24 × 24)이고 전구를 따로 그리지 않습니다.role="switch"·aria-checked·이름전체 레이어로 켜짐 상태를 알리며 Enter·Space는 기본 button 동작입니다. 행 전구와 세로로 맞추려면 이 행에 목록과 같은padding-right: 6px+overflow-y: hidden; scrollbar-gutter: stable을 주고 포커스 링은outline-offset: -2px로 버튼 안에 둡니다. 제목은 기존 Section 제목(.hm-sw-viewer-section__title, 12px 700)을 그대로 쓰고 별도 제목 CSS·Prop을 만들지 않습니다. 제목과 전구를 한 줄에 두는 것은 소비 앱 자기 class(예:section.app-viewer-all-layers { flex-direction: row; align-items: center; justify-content: space-between; min-height: 20px }, 예제example-layout.css)로 하고hm-sw-viewer-*class는 덮지 않습니다. 켜짐(aria-checked, 색 있는 전구)은 모든 레이어가 표시 중일 때만true이고, 일부만 표시 중이면false(회색 전구)로 보이며 누르면 전체 표시가 됩니다. 켜진 전구를 누르면 전체 숨김. 별도 상태를 두지 않고 레이어 가시성 상태에서 계산하므로 개별 전환 시 자동으로 맞춰집니다. 이전의전체 켜기·전체 끄기버튼 두 개는 쓰지 않습니다. - 그 밖의
SWViewerSwitch는 옵션 패널의 ON/OFF 설정(그리드·2.5D 등)에 씁니다. - 옵션(설정) 패널(2026-10-22): 하단
취소·적용버튼이 없습니다(footer미사용 → Footer 영역·구분선도 없음). 토글·Input·Slider는 바뀌는 즉시 프로그램 상태에 반영하고, 기본값 복원은 제목 막대의 초기화(headerActions) 버튼입니다. 확인이 필요한 입력 팝업(축척 지정 등)의footer는 그대로 씁니다. - 옵션 패널 게이지(필수, 2026-10-22 확정): 선가중치·표시·2.5D 등 옵션 패널의 게이지는
SWSlider appearance="property"(스타일 B: 베이지 트랙 · 빨강 채움 · Thumb 14px, 높이 16px) 하나만 씁니다. Card(SWViewerSection)마다 필요한 값 하나에 게이지 하나를SWViewerField layout="stacked"의below에 두고, 숫자 입력(SWViewerInput)과 같은 값·min·max·step·disabled를 공유합니다. 녹색 panel 모양(스타일 A)인SWViewerSlider·SWSlider기본값(appearance="panel")은 옵션 패널에 쓰지 않습니다(컴포넌트는 호환용으로 그대로 공개). StorybookSW/Viewer › Settings Popup, 예제 H.
4.2 패널 콘텐츠 글자 기준 (2026-10-22)
- 일반 콘텐츠(레이어 이름, 속성 행 Label)·
SWViewerFieldLabel: 14px 500. 읽기 전용 속성값은 14px, Weight는 패널 기본(400), 색--hm-sw-viewer-panel-text(검정 계열). 속성 행 Label 색은--hm-sw-viewer-panel-label. - 설명·안내(
SWViewerHint, 빈 상태 문구)·Input 값·SWSelect선택값·Placeholder·Badge: 12px(기존 Weight 유지). Section 제목 12px 700, 패널 제목 14px 700은 그대로입니다. - 글꼴은 패널이 상속하는
--hm-sw-header-font-family(Noto Sans KR)만 씁니다. 목록·값에ui-monospace등 다른font-family를 따로 지정하지 않습니다. - 속성 패널의 그룹(예: 일반 정보 / 기하 속성)을 더 떨어뜨릴 때는 두 번째 Section에 자기 class로
margin-top: var(--hm-sw-viewer-panel-spacing)(12px, 본문 간격 8px과 합쳐 20px)을 줍니다. 패널 본문 공통 간격은 바꾸지 않습니다.
5. 올바른 사용
import { SWViewerHeader, SWViewerPanel, SWViewerSection, SWViewerField, SWViewerSwitch, type SWViewerHeaderGroup } from "@hm-design/sw"
import "@hm-design/sw/styles.css"
const groups: SWViewerHeaderGroup[] = [
{ id: "view", label: "화면 보기", surface: "field", items: [{ type: "button", id: "fit", label: "전체 보기", icon: <FitIcon />, onSelect: fit }] },
{ id: "panels", label: "패널", placement: "end", items: [{ type: "toggle", id: "options", label: "설정", icon: <OptionsIcon />, pressed: open, onPressedChange: setOpen, iconOnlyBelow: 900 }] },
]
<SWViewerHeader documentTitle={fileName} groups={groups} onCommand={runCommand}
onDocumentOpen={openFile} documentOpenLabel="파일 열기"
documents={samples} activeDocumentId={sampleId} onDocumentSelect={loadSample} />
<SWViewerPanel title="옵션" open={open} onClose={() => setOpen(false)}>
<SWViewerSection title="표시">
<SWViewerField label="그리드">{id => <SWViewerSwitch id={id} checked={grid} onCheckedChange={setGrid} />}</SWViewerField>
</SWViewerSection>
</SWViewerPanel>
6. 잘못된 사용
| 하지 말 것 | 이유 / 대신 |
|---|---|
<SWViewerHeader logo="HM" />처럼 로고가 없다고 임의 문자·아이콘 넣기 |
로고는 프로그램이 정합니다. 없으면 생략 |
뷰어 화면에 로고(홈) 메뉴(SWHomeMenu·SWRecentFiles, SWHeader homeMenu) 넣기 또는 비슷한 2단 메뉴 직접 만들기 |
홈 메뉴는 SW 일반 모드 전용입니다. 뷰어의 파일 열기는 로고 옆 onDocumentOpen 버튼, 샘플 목록은 documents(직접 만든 Dropdown 금지) |
뷰어 Header를 div + button으로 다시 만들기, hm-sw-viewer-* class를 소비 코드에 붙이기 |
데이터(groups)와 custom / trailing으로 구성 |
Header 버튼 글자에 -webkit-text-stroke·border로 외곽선 만들기 |
외곽선은 패키지 토큰이 이미 적용합니다 |
메뉴 색·간격을 #123456, margin 등 임의 값으로 덮기 |
토큰 --hm-sw-viewer-* / --hm-sw-header-*만 가장 바깥 SW 범위(<SWTheme> 또는 단독 컴포넌트 루트)의 style로 조정(기본값 유지 권장, AI_USAGE.md §3.1) |
Header·Dropdown·More·Panel의 글꼴을 각각 따로 지정, .hm-sw-viewer-menu 등 class로 덮기 |
화면을 <SWTheme style={theme}> 하나로 감싸면 Header·Dropdown·More·Panel이 같은 값을 상속합니다. 글꼴 기본값은 Noto Sans KR이며 Web UI 글꼴 토큰을 복사하지 않습니다 |
레이어 Popup의 개별·전체 표시·숨김을 SWViewerSwitch·Checkbox·ON/OFF 스위치·눈 아이콘으로 만들기 |
§4.1: 전구(SWLayerVisibilityIcon) 아이콘 버튼(전체 레이어 포함). Switch는 옵션 설정에만 |
존재하지 않는 Props 추측(items, content, renderBody …) |
위 표와 dist/index.d.ts가 전부 |
일반 모드 화면에서 SWViewerHeader 사용, 뷰어에서 SWHeader 재스타일 |
모드별 Header를 바꿔 끼웁니다(창 최소화·닫기 SWHeaderWindowControls를 trailing 끝에 두는 §2.1 구성은 허용) |
| 기능 없는 메뉴를 가짜 동작으로 연결 | 항목 제외 또는 disabled + 미연결 보고 |
7. 디자인 변경 시 주의
- 크기(52px Header, 34px 버튼, 40px 토글·스위치 칸, 41~42px field 면)와 색은 참고 뷰어 실측값이고(Model / Layout 탭은 2026-10-23 Figma 7652:40344 모양, 높이는 field 그룹과 같은 42px·버튼 37.5px), 로고 면 64px는 2026-10-23
SWHeader로고 기준입니다. 파일명·객체 수정·속성 면 배경은 SWHeader와 같은 사용자 확정 토큰(--hm-sw-header-document-*,object-*,property-*)을 공유하므로 바꾸면 두 Header가 함께 바뀝니다. - 높이: 패키지는 뷰어(도면) 영역 높이를 정하지 않습니다. Header는 52px 한 줄, 팝업만 최대 90vh입니다. 화면 높이는 앱의 상위 레이아웃이 정하고(예:
html, body, #root { height: 100% }→ 세로 flex, Header 다음 도면 영역flex: 1; min-height: 0), 도면 영역 크기 변화에 맞춰 렌더러 크기를 바꾸는 것은 프로그램 담당입니다(ResizeObserver 권장). Storybook 예시의 고정 높이는 미리보기 프레임 값일 뿐 컴포넌트 값이 아닙니다. - Light/Dark 분기는 없습니다. 참고 뷰어와의 알려진 차이: 도면 공간(tabs) field 면 높이 42px(참고 41px — 참고 Tabs 루트가 39px인 구현 부산물), 크기 조절은 네이티브
resize대신 같은 위치의 모서리 Grip(모든 브라우저 동작), 패널 Escape 닫기 추가, 로고 기본 표시 없음(사용자 결정).