SW package 0.2.0 integrated improvement (Task A, steps 1-8). All new props are
optional; existing SW Stories are pixel-identical to the previous build (28/28).
- Build reproducibility: dist text normalized to LF, dist/build-info.json
(commit, dirty, tool versions, no timestamp), packages/sw/.gitattributes
(eol=lf). LF checkouts give identical dist and tarball.
- SWHeader: documentOpenPlacement ("menu" default | "header": Open right of
the logo in the existing document-button look, reusing onDocumentOpen and
documentOpenLabel, no duplicate Open in the dropdown), leading slot,
hiddenControls sub-items documentNew / documentOpen / layerFreeze /
layerLock. Open moves into More on measured overflow (with hysteresis).
- showNew / showOpen (SWHeaderDocumentTitle), showFreeze / showLock (layer
menu, field, property window list).
- SWToolButton (text / pill / document) and SWToolGroup (radio selection),
reusing the existing toggle, pill and file-name looks.
- SWPopup / SWPopupSection / SWPopupRow on the shared property window frame;
SWHeaderPropertyPanel gains children, title, autoFocus and optional
selection / properties / groups. Escape stack for several popups.
- Field dropdowns inside a popup use the shared fixed overlay
(useSWFixedDropdownPosition), so the scrolling body no longer clips them.
- AI guide, examples E/F, README, Storybook (SW/Header/Options, SW/Tool,
SW/Popup) and verification record docs/sw/sw-integrated-improvement-2026-10-19.md.
- Regression scripts rescoped for the intended changes (unused-removal 81/81,
overview 52/54 with the two pre-existing historical assertions).
Known limitation (unresolved): tools / leading content (incl. SWToolGroup)
does not move into More. With a wide tool bar the configured fields shrink
instead; in Example F the document name and layer field reach 0px at 390px.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
126 lines
8.4 KiB
Markdown
126 lines
8.4 KiB
Markdown
# SW 조합 패턴
|
|
|
|
모든 패턴은 공개 Export만 사용합니다. 전체 코드는 [examples/](./examples)에 있으며, 설치된 패키지에서 꺼낸 그대로 독립 React 19 + TypeScript + Vite 프로젝트의 `tsc`·Production Build·브라우저 검증을 통과했습니다(2026-10-19).
|
|
|
|
## 0. 어떤 패턴을 고를까
|
|
|
|
| 요청 | 패턴 |
|
|
|---|---|
|
|
| 기존 SW Header 전체(일부 부품 제거·메뉴 순서 변경 포함) | 1. 완성형 |
|
|
| 기존 컨트롤 일부만, 다른 배치·순서 | 2. 조합 Header |
|
|
| 기존 Dropdown을 툴바·패널 등 다른 곳에 | 3. 개별 컨트롤 |
|
|
| 패키지에 없는 기능 | 4. 확장(소비 프로젝트 신규 컴포넌트) |
|
|
| SW Popup 디자인의 설정창·정보창·사용자 정의 폼 | 5. Popup(외형 고정, 콘텐츠 자유) |
|
|
| 로고 옆 파일 열기, 뷰어 도구 막대(Model/Layout, Fit, 2.5D) | 6. 파일 열기 + 도구 막대 |
|
|
|
|
## 1. 완성형 SW Header — [ExampleA-DefaultHeader.tsx](./examples/ExampleA-DefaultHeader.tsx)
|
|
|
|
```tsx
|
|
<SWHeader documentTitle={name} documents={documents} layers={layers} colors={colors} onClose={close} />
|
|
```
|
|
|
|
- 부품 제거: `hiddenControls={["print", "popupGuide"]}`(로고는 항상 표시, `"home"`은 정적 로고).
|
|
- 메뉴 순서: `menuOrder={["color", "layer"]}`(5개 메뉴끼리, 누락분은 기본 순서로 뒤에).
|
|
- 객체 영역 전체를 직접 나열: `controls={[{ id: "document" }, { id: "layer" }, { id: "print" }]}`.
|
|
- 추가 도구: `tools={<MyTool />}`(객체 영역 메뉴 뒤). 새 도구는 아래 4의 규칙을 따른다.
|
|
- 이 경우에만 반응형 More가 동작합니다.
|
|
|
|
## 2. 조합 Header (간소화·기능 중심)
|
|
|
|
`<SWTheme>`을 Header 막대로 쓰고, 막대·면 배치 CSS는 소비 프로젝트가 작성합니다([example-layout.css](./examples/example-layout.css)). 이 CSS는 `--hm-sw-header-*` 토큰만 읽고 `hm-sw-*` class를 건드리지 않습니다.
|
|
|
|
```tsx
|
|
<SWTheme className="app-sw-bar"> {/* 49px, #060606, flex, gap 2px */}
|
|
<SWHeaderHome logoAlt="뷰어" onClick={goHome} /> {/* 80px, 다음 파일 면과 겹침 */}
|
|
<SWHeaderDocumentTitle title={name} documents={documents} onDocumentSelect={open} />
|
|
<div className="app-sw-surface"> {/* 객체 수정 면 토큰 */}
|
|
<SWHeaderLayerMenu value={layer} layers={layers} onLayerSelect={selectLayer} />
|
|
<SWHeaderColorMenu value={color} colors={colors} onColorSelect={selectColor} />
|
|
</div>
|
|
<div className="app-sw-window"> {/* 창 영역 면 토큰 */}
|
|
<SWHeaderActions showPrint={false} onPopupGuide={showGuide} />
|
|
</div>
|
|
</SWTheme>
|
|
```
|
|
|
|
- 간소화 Viewer: [ExampleB-ViewerHeader.tsx](./examples/ExampleB-ViewerHeader.tsx) — 로고 → 파일 → 레이어 → 색상 → K.
|
|
- 기능 중심: [ExampleC-FeatureHeader.tsx](./examples/ExampleC-FeatureHeader.tsx) — 파일 → 선 → 문자 → 치수스타일 → 인쇄.
|
|
- 순서는 JSX 순서입니다(DOM·Tab 순서 동일). CSS `order`로 바꾸지 않습니다.
|
|
- 로고 없이 파일 면이 처음이면 왼쪽 테두리가 없으므로 wrapper에 1px 테두리를 둡니다(예제 C `app-sw-file-edge`).
|
|
- K만/인쇄만: `SWHeaderActions`의 `showPrint={false}` / `showPopupGuide={false}`.
|
|
- 창 제어: `<SWHeaderWindowControls onWindowMoveStart={startDrag} onMinimize={min} onClose={close} />`(창 영역 면 안).
|
|
- 조합 Header에는 반응형 More가 없습니다. 좁은 화면이 필요하면 완성형을 쓰거나 표시할 컨트롤을 줄입니다.
|
|
|
|
## 3. 기존 Dropdown을 다른 레이아웃에서
|
|
|
|
```tsx
|
|
<SWTheme className="app-toolbar">
|
|
<SWHeaderLineStyleMenu value={lineStyle} lineTypes={lineTypes} lineWeights={lineWeights} onLineTypeSelect={setType} />
|
|
</SWTheme>
|
|
```
|
|
|
|
- Dropdown은 필드 바로 아래에 열리고 뷰포트 안으로 좌우 보정됩니다. 부모에 `overflow: hidden`을 두지 않습니다.
|
|
- Controlled가 필요하면 `open` / `onOpenChange`를 함께 줍니다.
|
|
- Dropdown이 필요 없고 호스트가 자체 팝업을 열면 `SWHeaderLayerControl` 등 필드 버튼만 씁니다.
|
|
|
|
## 4. 확장 — 없는 컨트롤 새로 만들기 — [SWSearchControl.tsx](./examples/SWSearchControl.tsx), [ExampleD-SearchExtension.tsx](./examples/ExampleD-SearchExtension.tsx)
|
|
|
|
요청 예: "레이어와 색상은 기존 SW 컴포넌트, 검색 버튼은 SW 디자인으로 새로."
|
|
|
|
1. 카탈로그 검색: 검색 컴포넌트 없음(MISSING).
|
|
2. 조합 검토: 텍스트 입력·확정·취소·IME는 `SWHeaderInput`으로 충족 → 재사용. 필드 면과 검색 아이콘 버튼은 공개 컴포넌트가 없음 → 신규.
|
|
3. 신규 부분은 **소비 프로젝트**에 작성: 38px 속성 필드 면, 12px 라벨, gradient Hover 링, 아이콘 버튼은 글자색 Hover `#FFC600`. 모든 값은 토큰([sw-search-control.css](./examples/sw-search-control.css)).
|
|
4. 기존 컨트롤과 같은 면(`app-sw-surface`)에 나란히 배치.
|
|
5. 검증: 레이어 필드와 높이·위치·테두리·radius·배경·그림자·라벨 크기 일치, Hover 링, Enter 확정 → `onSearch`, Escape 복원.
|
|
|
|
새 컨트롤은 패키지 공식 컴포넌트로 자동 등록하지 않습니다. 여러 프로젝트에서 반복되면 SW 패키지 승격을 별도로 검토합니다.
|
|
|
|
## 5. Popup — 외형 고정, 콘텐츠 자유 — [ExampleE-CustomPopup.tsx](./examples/ExampleE-CustomPopup.tsx)
|
|
|
|
요청 예: "HMDesign SW Popup 디자인을 사용해서 내부에 새로운 설정 폼을 만들어 줘."
|
|
|
|
1. 카탈로그 3절에서 `SWPopup`을 찾는다(프레임 = 속성창 외형·닫기·Escape·스크롤).
|
|
2. 내용은 `children`으로 구성한다. 행 모양은 `SWPopupSection` + `SWPopupRow`, 값 입력은 `SWHeaderInput`.
|
|
3. SW에 없는 컨트롤(Switch, Slider, Select, Button 등)은 `@hm-design/ui`를 `children` 안에 그대로 둔다. 구조 제한은 없다.
|
|
4. 프레임(폭, 제목줄, 닫기, 표면색)을 다시 만들지 않고, `hm-sw-property*` class를 쓰지 않는다.
|
|
|
|
```tsx
|
|
<SWTheme>
|
|
<SWPopup title="도면 설정" onClose={close}>
|
|
<SWPopupSection title="도면">
|
|
<SWPopupRow label="이름" htmlFor={id}><SWHeaderInput id={id} value={name} onCommit={setName} /></SWPopupRow>
|
|
<SWPopupRow label="축척" unit="%" numeric><SWHeaderInput aria-label="축척" valueType="number" value={scale} onCommit={setScale} /></SWPopupRow>
|
|
</SWPopupSection>
|
|
<MyProjectFields />
|
|
</SWPopup>
|
|
</SWTheme>
|
|
```
|
|
|
|
- 데이터 기반 속성창이 필요하면 `SWHeaderPropertyPanel`(`properties` / `groups`)을 쓰고, 추가 내용은 같은 컴포넌트의 `children`으로 붙인다.
|
|
- 위치는 호스트가 정한다: 문서 흐름(`style`) 또는 `anchorRef`(기준 요소 아래 고정 Overlay). 열고 닫는 상태도 호스트가 가진다(조건부 렌더링).
|
|
- 여러 Popup을 동시에 띄워도 Escape는 하나씩 닫힌다(카탈로그 3절).
|
|
|
|
## 6. 파일 열기 + 도구 막대 — [ExampleF-ViewerToolbar.tsx](./examples/ExampleF-ViewerToolbar.tsx)
|
|
|
|
```tsx
|
|
<SWHeader documentTitle={name} documents={documents} documentOpenPlacement="header" documentOpenLabel="파일 열기" onDocumentOpen={open}
|
|
controls={[{ id: "document" }, { id: "layer" }, { id: "color" }, { id: "popupGuide" }]}
|
|
hiddenControls={["documentNew", "layerFreeze", "layerLock"]}
|
|
tools={<SWToolGroup label="보기" selection="single">
|
|
<SWToolButton pressed={view === "model"} onClick={showModel}>Model</SWToolButton>
|
|
<SWToolButton pressed={view === "layout"} onClick={showLayout}>Layout</SWToolButton>
|
|
</SWToolGroup>} />
|
|
```
|
|
|
|
- 파일 열기는 `tools`에 직접 만들지 않는다. `documentOpenPlacement="header"`가 로고 바로 오른쪽에 파일명과 같은 면으로 그리고 Dropdown의 Open은 숨긴다(중복 없음). 공간이 부족하면 More로 이동한다.
|
|
- 지원하지 않는 기능은 `hiddenControls`로 뺀다(`documentNew`, `documentOpen`, `layerFreeze`, `layerLock`). 가짜 버튼을 두지 않는다.
|
|
- `SWHeader` 없이 같은 순서가 필요하면: `SWHeaderHome` → `SWToolButton appearance="document"` → `SWHeaderDocumentTitle showOpen={false}`(사선 접점·12px 여백은 패키지가 처리). 이 경우 More는 없다.
|
|
|
|
## 7. 하지 말 것
|
|
|
|
- `hm-sw-*` class를 소비 코드에 쓰거나 CSS로 덮어쓰기
|
|
- `dist` 구현·`styles.css` 블록 복사로 같은 컴포넌트 재작성
|
|
- `@hm-design/sw/dist/...`·`src/...` 내부 경로 Import
|
|
- 문서에 없는 Props 추측, 새 색·크기 값 도입
|
|
- 같은 화면에 `SWHeader`와 조합 Header를 겹쳐 두 개의 Header 만들기
|