fix(sw): correct footer auxiliary button state colors
This commit is contained in:
1 parent
6cf885412e
commit
40c5b89814
14 files changed
+118
-41
No files matched your search
@@ -30,6 +30,8 @@
|
||||
| 34 | 26:10056 | command (펼침) | 명령창 | `SWFooterCommandLine expanded` | 270px | 〃 |
|
||||
| 35 | 26:10097 | 레이아웃 분리 | 전체 | `SWFooter`(+ `SWFooterLayoutTabs`) | 하단 72px 전체 | 기본 Footer, Figma Variant · Footer 전체 |
|
||||
|
||||
보조 참고(35개 목록 밖, 2026-10-20 상태 수정 때 확인): 26:10104의 보조기능 버튼은 Component Set btn_객체스냅 26:9248 · btn_선택효과 26:9267 · btn_직각교차 26:9298 · btn_격자스냅 26:9320 · btn_격자배경 26:9381 · btn_선가중치 26:9412(Default / Variant2 / Variant3)의 인스턴스입니다. 기준 이미지 `src/sw/assets/figma-reference/footer/aux-buttons/`, Story = Hover / Active / Selected / Disabled.
|
||||
|
||||
26:10097 안의 보조기능 버튼(btn_객체스냅 …, 26:10104)·측면 버튼(26:10189 / 26:10192)·구분선·bg_vacant·menu_bg도 같은 노드에서 읽어 `SWFooterAuxGroup`·`SWFooterSideButton`·`SWFooterMenu` 면으로 구현했습니다.
|
||||
|
||||
## 2. Storybook `SW/Footer`
|
||||
@@ -56,8 +58,10 @@
|
||||
| Model 선택 | Figma On = 갈색 / 실제 화면 = 주황 | Figma(갈색) | Figma 우선 |
|
||||
| Layout 01 단독 | 26:10097에는 Model 없이 Layout 01만 | `modelLabel={false}` 지원, 마지막 탭은 항상 사선 | 두 구성 모두 표현 |
|
||||
| Variant5 / 6 의미 | 이름 없음(검정 면 + 빨강 글자) | 이름 편집 상태로 해석(Header 입력 편집 `#0F0D0A`·`#E60403`과 같은 값) | 해석 — 검토 필요 |
|
||||
| 보조기능 켜짐 | Figma 없음 / 실제 화면 주황 테두리·글자 | 실제 화면 적용(아이콘은 자산 색 유지) | 주황 아이콘 자산 없음 |
|
||||
| Hover(보조기능·측면 버튼), Disabled | Figma 없음 | 글자 `#FF5C00`, 40% | AI 구성 값 — 디자인 확인 필요 |
|
||||
| 보조기능 Selected | Figma Variant2 = Variant3: 주황 그라데이션 테두리 + 아이콘·글자 `#FF5C00` | Figma 그대로(2026-10-20 수정: 아이콘을 `currentColor` 인라인 SVG로 바꿔 아이콘까지 주황) | — |
|
||||
| 보조기능 Hover | 버튼 세트에 Hover 없음 | 노랑 테두리만(SW Header Figma Hover 토큰), 아이콘·글자 기본색 | 사용자 확정 2026-10-20 |
|
||||
| 보조기능 Default 테두리 | Figma 갈색 그라데이션 | 그라데이션(2026-10-20 수정 전에는 단색 `#886D35`) | Figma 일치 |
|
||||
| Hover(측면 버튼), Disabled | Figma 없음 | 글자 `#FF5C00`, 40% | AI 구성 값 — 디자인 확인 필요 |
|
||||
| 좁은 폭 | Figma 없음 / 실제 화면 | 빈 칸 축소 → ▲ 패널, 분류 ◀ ▶, 보조기능 아이콘만(1279px 이하), 명령창 최소 280 | 전환 폭·최소 폭은 AI 구성 값 |
|
||||
| 명령창 줄 수 | Figma 3줄(줄 14px) / 실제 화면 접힘 5줄 | Figma 3줄 | Figma 우선 |
|
||||
| 가이드 영상 | Figma는 6프레임 스토리보드(래스터) | `media`로 프로그램이 제공, Story만 프레임 순환 | 영상 자산은 프로그램 소유 |
|
||||
|
||||
@@ -162,7 +162,7 @@ SW **뷰어 모드** 전용 Export입니다(2026-10-19 추가, 참고 DWG/DXF
|
||||
| `SWFooter` | 72px 하단 막대: 보조기능 버튼 → 3 × 2 보조기능 → (바 안 명령창) → 명령 칸(68 × 47) + 분류 행(24) → 명령어 전체보기, 위 오른쪽 끝 Layout 탭 | `categories: SWFooterCategory[]`(필수), `categoryId`/`defaultCategoryId`/`onCategoryChange`, `activeToolId`, `onToolSelect(toolId, categoryId, tool)`, `slotCount`(17), `onSlotAdd(slotIndex, categoryId)`, `slotGuide`(`SWFooterGuideContent \| false`), `onCategoryAdd`, `auxItems`, `onAuxPressedChange`, `onAuxItemClick`, `auxAll`/`commandAll`(`SWFooterSideAction \| false`), `command`(바 안 명령창), `status`(바 위 왼쪽 좌표), `layoutTabs`, `trailing`, `label`, `<footer>` 속성·`ref`. 좁은 폭: 빈 칸 축소 → ▲ 패널, 분류 ◀ ▶, 보조기능 아이콘만 | ◻ |
|
||||
| `SWFooterSideButton` | 양끝 74 × 71 버튼(보조기능 / 명령어 전체보기) | `title`(필수), `subtitle`, `side`(`start`/`end`), `tooltip`, `button` 속성 | ◻ |
|
||||
| `SWFooterAuxGroup` | 보조기능 토글 격자(Figma 3 × 2) | `items: SWFooterAuxItem[]`(필수), `onPressedChange`, `onItemClick`, `columns`(3), `label` | ◻ |
|
||||
| `SWFooterAuxButton` | 보조기능 104 × 33 버튼 | `label`(필수), `icon`, `pressed`, `button` 속성 | ◻ |
|
||||
| `SWFooterAuxButton` | 보조기능 104 × 33 버튼(Hover 노랑 테두리, `pressed` = 주황 테두리·아이콘·글자) | `label`(필수), `icon`(`currentColor` 아이콘이면 상태 색 적용), `pressed`, `button` 속성 | ◻ |
|
||||
| `SWFooterToolButton` | 명령 칸 68 × 47 | `label`(필수), `icon`, `activeIcon`, `pressed`, `button` 속성 | ◻ |
|
||||
| `SWFooterAddSlot` | 빈 칸(+), Hover 가이드 | `guide`(기본 Figma / `false`), `guideOpen`, `onGuideOpenChange`, `button` 속성 | ◻ |
|
||||
| `SWFooterGuide` | 빈 칸 안내 카드 208 × 149 | `title`(`명령어 등록`), `caption`(`빠른사용`), `media`, `div` 속성 | ◻ |
|
||||
@@ -170,6 +170,6 @@ SW **뷰어 모드** 전용 Export입니다(2026-10-19 추가, 참고 DWG/DXF
|
||||
| `SWFooterLayoutTabs` | 도면 공간 탭(+ · Layout · Model) | `layouts: SWFooterLayout[]`·`value`(필수), `onValueChange`, `onAdd`, `onRemove`, `onRename`, `editingId`/`onEditingChange`, `modelLabel`(`"Model"` / `false`), `label`, `addLabel`, `removeLabel` | ◻ |
|
||||
| `SWFooterCommandLine` | 명령창 566 × 71(접힘) / 270(펼침) | `history: ReactNode[]`(필수), `value`/`onValueChange`, `onSubmit`, `prompt`(`Command:`), `expanded`/`defaultExpanded`/`onExpandedChange`, `inputProps`, `historyLabel`, `expandLabel`, `collapseLabel` | ◻ |
|
||||
|
||||
- **상수·아이콘:** `SW_FOOTER_MODEL`(`"model"`, Model 탭의 `value`), `swFooterToolIcons`(`line`·`pline`·`arc`·`circle`·`ellipse`·`rect`·`poly`·`point`·`hatch`·`ncopy` → `{ icon, activeIcon }`), `swFooterAuxIcons`(`objectSnap`·`selectionEffect`·`orthogonal`·`gridSnap`·`gridBackground`·`lineWeight`).
|
||||
- **상수·아이콘:** `SW_FOOTER_MODEL`(`"model"`, Model 탭의 `value`), `swFooterToolIcons`(`line`·`pline`·`arc`·`circle`·`ellipse`·`rect`·`poly`·`point`·`hatch`·`ncopy` → `{ icon, activeIcon }`), `swFooterAuxIcons`(`objectSnap`·`selectionEffect`·`orthogonal`·`gridSnap`·`gridBackground`·`lineWeight`, 인라인 SVG·채움 `currentColor`).
|
||||
- **공개 타입:** `SWFooterProps`, `SWFooterSideAction`, `SWFooterSideButtonProps`, `SWFooterAuxGroupProps`, `SWFooterAuxButtonProps`, `SWFooterToolButtonProps`, `SWFooterAddSlotProps`, `SWFooterGuideProps`, `SWFooterGuideContent`, `SWFooterMenuProps`, `SWFooterMenuItem`, `SWFooterLayoutTabsProps`, `SWFooterCommandLineProps`, `SWFooterTool`(`id`, `label`, `icon`, `activeIcon`, `title`, `ariaLabel`, `disabled`), `SWFooterCategory`(`id`, `label`, `tools`, `disabled`), `SWFooterAuxItem`(`id`, `label`, `icon`, `pressed`, `title`, `disabled`), `SWFooterLayout`(`id`, `label`, `locked`, `shape`), `SWFooterToolIconName`, `SWFooterAuxIconName`.
|
||||
- **검증 표시:** Footer Export는 Storybook(`SW/Footer`)과 저장소 안 Import·Build 검증만 있고 독립 소비 프로젝트(Tarball) 브라우저 검증 기록이 없어 ◻입니다.
|
||||
@@ -183,7 +183,8 @@ Container `hm-sw-header` 기준 Breakpoint: **< 1800** 선/문자/치수스타
|
||||
| 바 | 높이 72, 배경 `#060606` | `--hm-sw-footer-height`, `-background` |
|
||||
| 배치(1920, Figma) | 보조기능 버튼 0~74 → 2 → 보조기능 격자 76~392(위 2) → 4 → 명령 그룹 396~(위 1) → 명령어 전체보기(명령 칸 바로 뒤, 1552~1626) → 나머지 bg_vacant | — |
|
||||
| 측면 버튼 | 74 × 71, 자산 면(그라데이션 + 그라데이션 테두리), 그림자 8px 60% 검정(왼쪽 버튼 → 오른쪽 5px, 오른쪽 버튼 → 왼쪽 5px). 제목 14px 1.3px 외곽선, 부제 14px 1px 외곽선, 위 18 · 줄 간격 2 | `--hm-sw-footer-side-*` |
|
||||
| 보조기능 | 104 × 33, gap 2, 테두리 1px `#886D35` radius 3, 면 `linear-gradient(0deg, #262218 8%, #4A3C2B 34%, #594C31 55%, #312A1B 90%)`, 아이콘 중심 (19.5, 16.5), 라벨 13px 왼쪽 36 폭 57 가운데 | `--hm-sw-footer-aux-*` |
|
||||
| 보조기능 | 104 × 33, gap 2, radius 3, 면 `linear-gradient(0deg, #262218 8%, #4A3C2B 34%, #594C31 55%, #312A1B 90%)`, 아이콘 중심 (19.5, 16.5), 라벨 13px 왼쪽 36 폭 57 가운데. 테두리 1px 그라데이션(위 → 아래): Default `#886D35 0%, #423625 8.85%, #F3DBA8 26.17%, #0E0B06 83.79%, #574B30 100%`(Figma btn_* 26:9248 …) | `--hm-sw-footer-aux-*`, `--hm-sw-footer-aux-border` |
|
||||
| 보조기능 상태 | Hover(선택 아님) = 테두리만 노랑 `--hm-sw-header-hover-border-gradient`(`#A67D03 → #F2CC0F → #FADD4F → #F2CC0F → #926E03`, SW Header Figma Hover 재사용 — 버튼 세트에는 Hover 없음, 사용자 확정 2026-10-20). Selected(`aria-pressed="true"`, Figma Variant2 = Variant3) = 테두리 `#DA6813 8.52%, #FFBB89 37.93%, #E5690D 71.85%, #AE3607 92.77%` + 아이콘 채움·글자 `#FF5C00`, Hover보다 우선. 아이콘 외곽선 검정 90% 1px는 모든 상태 동일 | `--hm-sw-footer-aux-hover-border`, `--hm-sw-footer-aux-pressed-border`, `--hm-sw-footer-aux-icon-color`(`#F1EDE5`) |
|
||||
| 명령 그룹 면 | 테두리 위·왼쪽·아래 1px `#886D35`, 왼쪽 위 radius 4, 면 `linear-gradient(0deg, #2F281A 0%, #312A1B 5%, #332B1C 36%, #463C29 58%, #38301F 79%, #19160E 98%)`. 마지막 명령 뒤(빈 칸·나머지)는 bg_vacant `linear-gradient(0deg, #18150E 28%, #262015 41%, #332B1C 58%, #38301F 81%, #18150E 100%)` | `--hm-sw-footer-group-*`, `--hm-sw-footer-vacant-background` |
|
||||
| 명령 칸 | 68 × 47, 라벨 14px `#F6F4F2` 위 6, 아이콘 18 × 18 위 26 가운데. 칸 사이 구분선 1 × 37(위 5) `linear-gradient(0deg, #20190E, #8A7A5B 45%, #F3DBA8 70%, #423625)` + `-1px 0 1px #000` | `--hm-sw-footer-slot-*`, `--hm-sw-footer-divider*` |
|
||||
| 명령 상태 | Hover = 선택(Figma Variant2 = Variant3): 글자 `#FF5C00` + 주황 아이콘. Disabled 40%(Figma 없음) | `--hm-sw-footer-accent` |
|
||||
@@ -195,8 +196,7 @@ Container `hm-sw-header` 기준 Breakpoint: **< 1800** 선/문자/치수스타
|
||||
| 명령창 | 566 × 71 / 270, 위 radius 4, 위 71px `linear-gradient(180deg, #635537, #262218 5%, #281F16 10%, #282216)` + 아래 `#282216`. 12px 자간 10% 줄 14, 1px 외곽선. 입력 칸 23(좌우 4) `#16130D` + `0 -1px 2px rgba(255,255,255,.25)`, `inset 0 1px 4px #000`. 화살표 18 × 18 검정 radius 2(4, 4 오른쪽 위) | `--hm-sw-footer-command-*` |
|
||||
| 바 안 명령창(실제 화면) | 보조기능 뒤 4px, 남는 폭(최소 280), 바 아래에 붙어 펼치면 위로 270 | `--hm-sw-footer-command-min-width` |
|
||||
| 좌표(실제 화면) | 바 위 왼쪽, 12px `#F6F4F2` 1px 외곽선, 아래 3px | — |
|
||||
| 보조기능 pressed(실제 화면) | 테두리 `#DA6813` + 글자 `#FF5C00` | `--hm-sw-footer-aux-pressed-border-color` |
|
||||
| 좁은 폭(실제 화면) | 빈 칸이 먼저 줄고, 넘치는 명령은 마지막 칸 ▲(ico_arrow 18 × 18, 오른쪽 위 4) → 명령 칸 위 패널(같은 칸, 오른쪽 맞춤, 테두리 1px `#886D35`·아래 없음·위 radius 4, 명령 칸 면). 분류 탭 넘침 = 탭 영역 잘림 + ◀ ▶(13 × 16, 6 × 8 삼각형 `#F6F4F2`, Hover `#FF5C00`) + +. 바 1279px 이하 보조기능 아이콘만 45 × 33 | `--hm-sw-footer-aux-compact-width` |
|
||||
|
||||
- Figma에 없는 값: 실제 화면에서 가져온 값(바 안 명령창 배치, 좌표, 보조기능 pressed, ▲ 패널, 분류 ◀ ▶, 보조기능 아이콘만)과 AI 구성 값 — 디자인 확인 필요(보조기능·측면 버튼 Hover 글자 `#FF5C00`, Disabled 40%, 명령창 최소 280px, 보조기능 아이콘 전환 1279px, Layout 탭 이름 편집 입력의 동작).
|
||||
- Figma에 없는 값: 실제 화면에서 가져온 값(바 안 명령창 배치, 좌표, ▲ 패널, 분류 ◀ ▶, 보조기능 아이콘만)과 AI 구성 값 — 디자인 확인 필요(측면 버튼 Hover 글자 `#FF5C00`, Disabled 40%, 명령창 최소 280px, 보조기능 아이콘 전환 1279px, Layout 탭 이름 편집 입력의 동작).
|
||||
- 새 하단 UI는 위 토큰을 `var(--…)`로 읽고 `hm-sw-footer*` class를 쓰거나 덮어쓰지 않습니다.
|
||||
@@ -52,7 +52,7 @@ import "@hm-design/sw/styles.css"
|
||||
| `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`.
|
||||
`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>` 안)
|
||||
|
||||
@@ -84,8 +84,8 @@ import "@hm-design/sw/styles.css"
|
||||
| Model 탭 | Default / Hover 주황 / On 갈색 | `value === SW_FOOTER_MODEL` |
|
||||
| 명령창 화살표 | Default ↑ / Hover 주황 ↑ / On ↓ / On hover 주황 ↓ | `expanded` |
|
||||
| Disabled | Figma 없음 | SW 공통 40%(명령·빈 칸·탭·보조기능) |
|
||||
| 보조기능 pressed | Figma는 Default만 | 실제 화면: 주황 테두리 `#DA6813` + 글자 `#FF5C00`(아이콘 색은 자산 그대로) |
|
||||
| 보조기능·측면 버튼 Hover | Figma 없음 | Footer Hover 규칙 재사용: 글자 `#FF5C00` (디자인 확인 필요) |
|
||||
| 보조기능 | 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,4 +1,4 @@
|
||||
import type { ReactElement } from "react"
|
||||
import { useId, type ReactElement } from "react"
|
||||
import line from "./assets/tool-line.svg"
|
||||
import lineOn from "./assets/tool-line-on.svg"
|
||||
import pline from "./assets/tool-pline.svg"
|
||||
@@ -19,12 +19,6 @@ import hatch from "./assets/tool-hatch.svg"
|
||||
import hatchOn from "./assets/tool-hatch-on.svg"
|
||||
import ncopy from "./assets/tool-ncopy.svg"
|
||||
import ncopyOn from "./assets/tool-ncopy-on.svg"
|
||||
import objectSnap from "./assets/aux-object-snap.svg"
|
||||
import selectionEffect from "./assets/aux-selection.svg"
|
||||
import orthogonal from "./assets/aux-orthogonal.svg"
|
||||
import gridSnap from "./assets/aux-grid-snap.svg"
|
||||
import gridBackground from "./assets/aux-grid.svg"
|
||||
import lineWeight from "./assets/aux-lineweight.svg"
|
||||
|
||||
const img = (src: string, width: number, height = width): ReactElement => <img src={src} width={width} height={height} alt="" draggable={false} />
|
||||
|
||||
@@ -49,12 +43,41 @@ export const swFooterToolIcons: Record<SWFooterToolIconName, { icon: ReactElemen
|
||||
|
||||
export type SWFooterAuxIconName = "objectSnap" | "selectionEffect" | "orthogonal" | "gridSnap" | "gridBackground" | "lineWeight"
|
||||
|
||||
/** Figma auxiliary toggle icons (26:10104: 객체스냅, 선택효과, 직각교차, 격자스냅, 격자배경, 선가중치). */
|
||||
export const swFooterAuxIcons: Record<SWFooterAuxIconName, ReactElement> = {
|
||||
objectSnap: img(objectSnap, 17),
|
||||
selectionEffect: img(selectionEffect, 16),
|
||||
orthogonal: img(orthogonal, 18),
|
||||
gridSnap: img(gridSnap, 16),
|
||||
gridBackground: img(gridBackground, 17),
|
||||
lineWeight: img(lineWeight, 16, 15),
|
||||
/**
|
||||
* Auxiliary toggle icon, drawn inline so the button state reaches it: the fill is `currentColor` (Figma Default
|
||||
* #F1EDE5, Variant2 / Variant3 #FF5C00 — the only difference between the variants), the 1px OUTSIDE outline stays
|
||||
* black 90% (a 2px stroke masked by the shape). Paths are the Figma exports of btn_객체스냅 … (26:9248 – 26:9412).
|
||||
*/
|
||||
function AuxIcon({ d, width, height }: { d: string; width: number; height: number }) {
|
||||
const mask = `${useId()}aux-outline`
|
||||
return (
|
||||
<svg width={width} height={height} viewBox={`0 0 ${width} ${height}`} fill="none" aria-hidden="true" focusable="false">
|
||||
<path d={d} fillRule="evenodd" clipRule="evenodd" fill="currentColor" />
|
||||
<mask id={mask} maskUnits="userSpaceOnUse" x="0" y="0" width={width} height={height} fill="black">
|
||||
<rect fill="white" width={width} height={height} />
|
||||
<path d={d} fillRule="evenodd" clipRule="evenodd" />
|
||||
</mask>
|
||||
<path d={d} stroke="black" strokeOpacity={0.9} strokeWidth={2} mask={`url(#${mask})`} />
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
const AUX_PATHS: Record<SWFooterAuxIconName, [string, number, number]> = {
|
||||
objectSnap: ["M5 1H1V5H2V16H16V2H5V1ZM3 5H5V3H15V15H3V5Z", 17, 17],
|
||||
selectionEffect: ["M15 15V1H1V15H15ZM14 14H2V2.70718L9.29282 10H7V11H10H11V10V7H10V9.29297L2.70703 2H14V14Z", 16, 16],
|
||||
orthogonal: ["M1 1H2V12H6V16H17V17H6H2H1V16V12V1Z", 18, 18],
|
||||
gridSnap: ["M3 1H1V3H3V1ZM3 5H1V7H3V5ZM1 9H3V11H1V9ZM3 13H1V15H3V13ZM5 1H7V3H5V1ZM7 5H5V7H7V5ZM5 9H7V11H5V9ZM7 13H5V15H7V13ZM9 1H11V3H9V1ZM11 5H9V7H11V5ZM9 9H11V11H9V9ZM11 13H9V15H11V13ZM13 1H15V3H13V1ZM15 5H13V7H15V5ZM13 9H15V11H13V9ZM15 13H13V15H15V13Z", 16, 16],
|
||||
gridBackground: ["M4 1H3V3L1 3V4H3V8H1V9H3V13H1V14H3V16H4V14H8V16H9V14H13V16H14V14H16V13H14V9H16V8H14V4H16V3L14 3V1H13V3L9 3V1H8V3L4 3V1ZM13 13V9H9V13H13ZM8 13V9H4V13H8ZM8 8H4V4H8V8ZM13 8H9V4H13V8Z", 17, 17],
|
||||
lineWeight: ["M1 1H15V3H1V1ZM1 5H15V8H1V5ZM15 10H1V14H15V10Z", 16, 15],
|
||||
}
|
||||
|
||||
/**
|
||||
* Figma auxiliary toggle icons (26:10104: 객체스냅, 선택효과, 직각교차, 격자스냅, 격자배경, 선가중치). Inline SVG with
|
||||
* a `currentColor` fill: `SWFooterAuxButton` colors them per state (Default #F1EDE5, pressed #FF5C00).
|
||||
*/
|
||||
export const swFooterAuxIcons: Record<SWFooterAuxIconName, ReactElement> = Object.fromEntries(
|
||||
(Object.keys(AUX_PATHS) as SWFooterAuxIconName[]).map(name => {
|
||||
const [d, width, height] = AUX_PATHS[name]
|
||||
return [name, <AuxIcon key={name} d={d} width={width} height={height} />]
|
||||
}),
|
||||
) as Record<SWFooterAuxIconName, ReactElement>
|
||||
@@ -25,11 +25,16 @@
|
||||
--hm-sw-footer-aux-height: 33px;
|
||||
--hm-sw-footer-aux-gap: 2px;
|
||||
--hm-sw-footer-aux-background: linear-gradient(0deg, #262218 8%, #4a3c2b 34%, #594c31 55%, #312a1b 90%);
|
||||
--hm-sw-footer-aux-border-color: #886d35;
|
||||
--hm-sw-footer-aux-radius: 3px;
|
||||
--hm-sw-footer-aux-font-size: 13px;
|
||||
/* Not in Figma (Default only): the reference screen (2026-10-20) draws an ON toggle with an orange border and text. */
|
||||
--hm-sw-footer-aux-pressed-border-color: #da6813;
|
||||
/* 1px borders (Plugin API, btn_객체스냅 26:9248 … btn_선가중치 26:9412): Default = brown gradient, Variant2 / Variant3
|
||||
(selected) = the footer orange ring gradient. Hover is not drawn on these sets: it reuses the SW header Figma
|
||||
hover ring (yellow gradient, 1px), user decision 2026-10-20. */
|
||||
--hm-sw-footer-aux-border: linear-gradient(180deg, #886d35 0%, #423625 8.85%, #f3dba8 26.17%, #0e0b06 83.79%, #574b30 100%);
|
||||
--hm-sw-footer-aux-hover-border: var(--hm-sw-header-hover-border-gradient);
|
||||
--hm-sw-footer-aux-pressed-border: linear-gradient(180deg, #da6813 8.52%, #ffbb89 37.93%, #e5690d 71.85%, #ae3607 92.77%);
|
||||
/* Icon fill (Default #F1EDE5, selected = --hm-sw-footer-accent #FF5C00); the black 90% outline never changes. */
|
||||
--hm-sw-footer-aux-icon-color: #f1ede5;
|
||||
|
||||
/* Command group (26:10111 groop명령어): 68 × 47 slots over a 24px category row, 71px high */
|
||||
--hm-sw-footer-slot-width: 68px;
|
||||
@@ -181,15 +186,16 @@
|
||||
position: relative;
|
||||
width: var(--hm-sw-footer-aux-width);
|
||||
height: var(--hm-sw-footer-aux-height);
|
||||
border: 1px solid var(--hm-sw-footer-aux-border-color);
|
||||
/* Gradient border: the face on the padding box, the border gradient on the border box (size and radius unchanged). */
|
||||
border: 1px solid transparent;
|
||||
border-radius: var(--hm-sw-footer-aux-radius);
|
||||
background: var(--hm-sw-footer-aux-background);
|
||||
background: var(--hm-sw-footer-aux-background) padding-box, var(--hm-sw-footer-aux-border) border-box;
|
||||
color: var(--hm-sw-footer-text);
|
||||
font-size: var(--hm-sw-footer-aux-font-size);
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
/* Icon box: Figma icons sit at 12px, 8–10px (14–16px boxes); a 17px box at 10, 7 inside the 1px border centers them. */
|
||||
.hm-sw-footer-aux__icon { position: absolute; left: 10px; top: 7px; display: grid; place-items: center; width: 17px; height: 17px; }
|
||||
.hm-sw-footer-aux__icon { position: absolute; left: 10px; top: 7px; display: grid; place-items: center; width: 17px; height: 17px; color: var(--hm-sw-footer-aux-icon-color); }
|
||||
.hm-sw-footer-aux__icon :is(img, svg) { display: block; flex: none; }
|
||||
.hm-sw-footer-aux__label {
|
||||
position: absolute;
|
||||
@@ -203,11 +209,16 @@
|
||||
white-space: nowrap;
|
||||
text-shadow: var(--hm-sw-header-text-outline);
|
||||
}
|
||||
/* Not in Figma (only Default is drawn): hover = the footer accent text; pressed = accent text + orange border
|
||||
(reference screen 2026-10-20). */
|
||||
.hm-sw-footer-aux__button:hover:not(:disabled) .hm-sw-footer-aux__label,
|
||||
.hm-sw-footer-aux__button[aria-pressed="true"] .hm-sw-footer-aux__label { color: var(--hm-sw-footer-accent); }
|
||||
.hm-sw-footer-aux__button[aria-pressed="true"] { border-color: var(--hm-sw-footer-aux-pressed-border-color); }
|
||||
/* Hover (not selected): yellow ring only; icon and text keep their Default colors. */
|
||||
.hm-sw-footer-aux__button:hover:not(:disabled):not([aria-pressed="true"]) {
|
||||
background: var(--hm-sw-footer-aux-background) padding-box, var(--hm-sw-footer-aux-hover-border) border-box;
|
||||
}
|
||||
/* Selected (Figma Variant2 / Variant3): orange border, icon and text together; it stays while hovered and after
|
||||
the pointer leaves. Icons follow through currentColor (swFooterAuxIcons, or any currentColor icon). */
|
||||
.hm-sw-footer-aux__button[aria-pressed="true"] {
|
||||
background: var(--hm-sw-footer-aux-background) padding-box, var(--hm-sw-footer-aux-pressed-border) border-box;
|
||||
}
|
||||
.hm-sw-footer-aux__button[aria-pressed="true"] :is(.hm-sw-footer-aux__icon, .hm-sw-footer-aux__label) { color: var(--hm-sw-footer-accent); }
|
||||
|
||||
/* ---------- Command slots (Line, PLine … / 빈 칸) ---------- */
|
||||
.hm-sw-footer-slot {
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
@@ -28,10 +28,16 @@
|
||||
.sw-footer-story-states { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px; }
|
||||
.sw-footer-story-state { margin: 0; }
|
||||
.sw-footer-story-state figcaption { margin-bottom: 6px; font-size: 12px; color: #3d4a45; }
|
||||
.sw-footer-story-lucide { display: grid; place-items: center; color: #d4cbbd; }
|
||||
.sw-footer-story-lucide { display: grid; place-items: center; }
|
||||
.sw-footer-story-status { padding: 0 12px; font-size: 12px; color: #d4cbbd; font-family: ui-monospace, monospace; }
|
||||
.sw-footer-story-row { display: flex; align-items: flex-start; gap: 16px; }
|
||||
.sw-footer-story-row--tight { gap: 0; }
|
||||
.sw-footer-story-inline { display: inline-flex; flex-direction: column; padding: 0; }
|
||||
.sw-footer-story-width + .sw-footer-story-width { margin-top: 16px; }
|
||||
.sw-footer-story-reference-narrow { display: block; width: 952px; max-width: 100%; }
|
||||
.sw-footer-story-aux-compare { display: grid; grid-template-columns: repeat(auto-fill, minmax(560px, 1fr)); gap: 16px; }
|
||||
.sw-footer-story-aux-row { display: flex; align-items: center; gap: 12px; margin: 0; }
|
||||
.sw-footer-story-aux-row figcaption { flex: 0 0 120px; font-size: 12px; color: #3d4a45; }
|
||||
.sw-footer-story-aux-row img { display: block; flex: none; }
|
||||
.sw-footer-story-aux-states { display: flex; flex-direction: column; gap: 9px; padding: 8px; }
|
||||
.sw-footer-story-aux-demo { margin-top: 12px; }
|
||||
@@ -199,6 +199,24 @@ const MENU_NODES: [string, string][] = [
|
||||
]
|
||||
const toolProps = (tool: SWFooterTool) => ({ label: tool.label, icon: tool.icon, activeIcon: tool.activeIcon, "aria-label": tool.ariaLabel })
|
||||
const AUX0 = { label: AUX_ITEMS[0].label, icon: AUX_ITEMS[0].icon }
|
||||
/** Figma component sets of the six auxiliary toggles, in AUX_ITEMS order. */
|
||||
const AUX_SETS = ["26:9248", "26:9267", "26:9298", "26:9320", "26:9381", "26:9412"]
|
||||
|
||||
/** Live toggles: click to select / deselect; "하나만" makes the selection move between buttons. */
|
||||
function AuxToggleDemo() {
|
||||
const [items, setItems] = useState(AUX_ITEMS)
|
||||
const [single, setSingle] = useState(false)
|
||||
return (
|
||||
<div className="sw-footer-story-aux-demo">
|
||||
<label className="sw-story-log"><input type="checkbox" checked={single} onChange={event => setSingle(event.target.checked)} /> 하나만 선택(다른 버튼을 누르면 선택 이동)</label>
|
||||
<SWTheme className="sw-footer-story-group-surface sw-footer-story-inline">
|
||||
<SWFooterAuxGroup items={items} onPressedChange={(id, pressed) => setItems(list => list.map(item => (
|
||||
item.id === id ? { ...item, pressed } : single && pressed ? { ...item, pressed: false } : item)))} />
|
||||
</SWTheme>
|
||||
<p className="sw-story-log" data-aux-state="">선택: {items.filter(item => item.pressed).map(item => item.label).join(", ") || "없음"}</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const SMALL_COMMAND = { width: 120, ["--hm-sw-footer-command-expanded-height" as string]: "71px" }
|
||||
|
||||
export const FigmaVariants: Story = {
|
||||
@@ -336,11 +354,26 @@ export const States: Story = {
|
||||
</div>
|
||||
<p className="sw-story-log">분류 Hover 고정: 두 번째 탭(수정)에 data-sb-hover.</p>
|
||||
</StorySection>
|
||||
<StorySection title="보조기능 · 측면 버튼 (Figma Default만 있음)" description="Hover·pressed는 Footer의 #FF5C00 글자 규칙을 재사용(Figma 미정의).">
|
||||
<StorySection title="보조기능 버튼 (Figma btn_객체스냅 26:9248 … btn_선가중치 26:9412)" description="Default = 갈색 그라데이션 테두리 · #F1EDE5 아이콘 · #F6F4F2 글자. Hover = 노랑 테두리(SW Header Hover 토큰)만, 아이콘·글자 기본색. Selected(Figma Variant2 = Variant3) = 주황 그라데이션 테두리 + #FF5C00 아이콘 + #FF5C00 글자, Hover 중에도 유지.">
|
||||
<div className="sw-footer-story-aux-compare">
|
||||
{AUX_ITEMS.map((item, index) => (
|
||||
<figure key={item.id} className="sw-footer-story-aux-row">
|
||||
<figcaption>{AUX_SETS[index]} {item.label}</figcaption>
|
||||
<img src={new URL(`./assets/figma-reference/footer/aux-buttons/${AUX_SETS[index].replace(":", "-")}.png`, import.meta.url).href} alt={`Figma ${AUX_SETS[index]} ${item.label}`} />
|
||||
<SWTheme className="sw-footer-story-group-surface sw-footer-story-aux-states">
|
||||
<SWFooterAuxButton label={item.label} icon={item.icon} pressed={false} />
|
||||
<Pin hover=".hm-sw-footer-aux__button"><SWFooterAuxButton label={item.label} icon={item.icon} pressed={false} /></Pin>
|
||||
<SWFooterAuxButton label={item.label} icon={item.icon} pressed />
|
||||
<Pin hover=".hm-sw-footer-aux__button"><SWFooterAuxButton label={item.label} icon={item.icon} pressed /></Pin>
|
||||
</SWTheme>
|
||||
</figure>
|
||||
))}
|
||||
</div>
|
||||
<p className="sw-story-log">구현 열: Default · Hover · Selected · Selected + Hover (Hover는 data-sb-hover 고정)</p>
|
||||
<AuxToggleDemo />
|
||||
</StorySection>
|
||||
<StorySection title="측면 버튼 (Figma Default만 있음)" description="Hover·pressed는 Footer의 #FF5C00 글자 규칙을 재사용(Figma 미정의).">
|
||||
<div className="sw-footer-story-states">
|
||||
<StateCell title="보조 Default"><SWFooterAuxButton {...AUX0} /></StateCell>
|
||||
<StateCell title="보조 Hover"><Pin hover=".hm-sw-footer-aux__button"><SWFooterAuxButton {...AUX0} /></Pin></StateCell>
|
||||
<StateCell title="보조 pressed"><SWFooterAuxButton {...AUX0} pressed /></StateCell>
|
||||
<StateCell title="보조 Disabled"><SWFooterAuxButton {...AUX0} disabled /></StateCell>
|
||||
<StateCell title="측면 Default"><SWFooterSideButton title="보조기능" subtitle="전체보기" /></StateCell>
|
||||
<StateCell title="측면 Hover"><Pin hover=".hm-sw-footer-side"><SWFooterSideButton title="명령어" subtitle="전체보기" side="end" /></Pin></StateCell>
|
||||
|
||||
Reference in new issue
Block a user