feat: FoxButtonPanel 추가
Figma btn-page(404:5121)의 네 변형을 옮긴다. 자식은 버튼 그룹이고 여럿이면
양끝, 하나면 가운데다.
자식 수를 React에서 세지 않고 CSS `:has(> :only-child)`로 판단한다. 프래그먼트나
조건부 렌더로 자식이 갈려도 실제 DOM이 기준이라 어긋나지 않고, React를 안 쓰는
소비자도 표시할 클래스가 없다.
패널이 자식에게 강제하는 두 가지(사용자 승인):
- 패널 안 그룹 간격은 gap-3(8px). 단독으로 쓰는 그룹의 gap-2(4px)는 그대로다.
- 모바일에서는 패널도 그룹도 세로로 쌓이고 버튼이 폭을 채운다.
시안이 패널 안 버튼에만 주는 min-width 160px은 변수 바인딩이 없고 토큰 스케일에도
160이 없어 리터럴로 둔다(사용자 승인). 상단 80px 여백은 spacing-top-2xlg이며
offset={false}로 끌 수 있다.
Co-Authored-By: Claude Opus 5
@e440f2a8d2ffebf15f4e565db33c28a7b56be30a
--- @fox/README.md
+++ @fox/README.md
... | ... | @@ -95,6 +95,11 @@ |
| 95 | 95 |
<button class="fox-button fox-button--default fox-button--md">취소</button> |
| 96 | 96 |
<button class="fox-button fox-button--primary fox-button--md">확인</button> |
| 97 | 97 |
</div> |
| 98 |
+ |
|
| 99 |
+<div class="fox-button-panel fox-button-panel--offset"> |
|
| 100 |
+ <div class="fox-button-group fox-button-group--horizontal fox-button-group--start">…</div> |
|
| 101 |
+ <div class="fox-button-group fox-button-group--horizontal fox-button-group--end">…</div> |
|
| 102 |
+</div> |
|
| 98 | 103 |
``` |
| 99 | 104 |
|
| 100 | 105 |
- 비활성은 네이티브 `disabled` 속성으로 표현합니다(별도 클래스 없음). 앵커에는 `disabled`가 |
... | ... | @@ -104,6 +109,9 @@ |
| 104 | 109 |
- 버튼 묶음은 간격·방향·정렬만 담당합니다. 자식 버튼의 크기 클래스를 그룹 크기로 맞추는 건 |
| 105 | 110 |
작성자 몫입니다(React 래퍼는 `size` prop으로 자동화합니다). 세로(`--vertical`)는 자식이 |
| 106 | 111 |
폭을 채웁니다. |
| 112 |
+- 버튼 패널은 그룹이 여럿이면 양끝으로, 하나면 가운데로 둡니다 — 자식 수는 CSS `:only-child`가 |
|
| 113 |
+ 판단하므로 따로 표시할 게 없습니다. 패널 안에서는 그룹 간격이 넓어지고 버튼에 최소 폭이 |
|
| 114 |
+ 붙으며, 모바일에서는 패널·그룹이 모두 세로로 쌓입니다. |
|
| 107 | 115 |
- 아이콘 SVG는 `currentColor`로 그려야 계열별 색이 적용됩니다. 크기는 슬롯이 정합니다. |
| 108 | 116 |
|
| 109 | 117 |
`core/`의 React 컴포넌트는 이 클래스를 조립해주는 **얇은 래퍼**일 뿐입니다 — 쓰지 않아도 |
--- @fox/core/components/index.ts
+++ @fox/core/components/index.ts
... | ... | @@ -3,3 +3,4 @@ |
| 3 | 3 |
export * from "./fox-link-button"; |
| 4 | 4 |
export * from "./fox-icon-button"; |
| 5 | 5 |
export * from "./fox-button-group"; |
| 6 |
+export * from "./fox-button-panel"; |
--- @fox/dev-test/component-registry.tsx
+++ @fox/dev-test/component-registry.tsx
... | ... | @@ -6,6 +6,7 @@ |
| 6 | 6 |
import { FoxLinkButton } from "../core/components/fox-link-button";
|
| 7 | 7 |
import { FoxIconButton } from "../core/components/fox-icon-button";
|
| 8 | 8 |
import { FoxButtonGroup } from "../core/components/fox-button-group";
|
| 9 |
+import { FoxButtonPanel } from "../core/components/fox-button-panel";
|
|
| 9 | 10 |
import styles from "./dev-test.module.scss"; |
| 10 | 11 |
|
| 11 | 12 |
export interface ComponentExample {
|
... | ... | @@ -445,4 +446,71 @@ |
| 445 | 446 |
}, |
| 446 | 447 |
], |
| 447 | 448 |
}, |
| 449 |
+ {
|
|
| 450 |
+ id: "fox-button-panel", |
|
| 451 |
+ name: "FoxButtonPanel", |
|
| 452 |
+ description: |
|
| 453 |
+ "Figma 시안(btn-page, 컴포넌트 세트 404:5121)을 옮겼습니다. 자식은 버튼 그룹이고, 여럿이면 양끝으로 벌어지고 하나면 가운데로 갑니다 — 자식 수는 CSS :only-child가 판단합니다. 패널 안에서는 그룹 간격이 gap-3(8px)으로 덮어써지고(단독으로 쓰면 gap-2), 버튼에 최소 폭 160px이 붙습니다. 창을 768px 아래로 줄이면 패널도 그룹도 세로로 쌓이고 버튼이 폭을 채웁니다.", |
|
| 454 |
+ variants: [ |
|
| 455 |
+ {
|
|
| 456 |
+ label: "그룹 둘 — 양끝 (시안 type=flow)", |
|
| 457 |
+ node: ( |
|
| 458 |
+ <div className={styles.demoOutline}>
|
|
| 459 |
+ <FoxButtonPanel offset={false}>
|
|
| 460 |
+ <FoxButtonGroup size="lg"> |
|
| 461 |
+ <FoxButton size="lg" label="취소" /> |
|
| 462 |
+ </FoxButtonGroup> |
|
| 463 |
+ <FoxButtonGroup size="lg"> |
|
| 464 |
+ <FoxButton size="lg" type="secondary" label="임시 저장" /> |
|
| 465 |
+ <FoxButton size="lg" type="primary" label="다음" /> |
|
| 466 |
+ </FoxButtonGroup> |
|
| 467 |
+ </FoxButtonPanel> |
|
| 468 |
+ </div> |
|
| 469 |
+ ), |
|
| 470 |
+ }, |
|
| 471 |
+ {
|
|
| 472 |
+ label: "그룹 하나 — 가운데 (시안 type=default)", |
|
| 473 |
+ node: ( |
|
| 474 |
+ <div className={styles.demoOutline}>
|
|
| 475 |
+ <FoxButtonPanel offset={false}>
|
|
| 476 |
+ <FoxButtonGroup size="lg"> |
|
| 477 |
+ <FoxButton size="lg" label="취소" /> |
|
| 478 |
+ <FoxButton size="lg" type="primary" label="확인" /> |
|
| 479 |
+ </FoxButtonGroup> |
|
| 480 |
+ </FoxButtonPanel> |
|
| 481 |
+ </div> |
|
| 482 |
+ ), |
|
| 483 |
+ }, |
|
| 484 |
+ {
|
|
| 485 |
+ label: "상단 여백 (offset) — 기본 켜짐, 80px", |
|
| 486 |
+ node: ( |
|
| 487 |
+ <div className={styles.demoOutline}>
|
|
| 488 |
+ <FoxButtonPanel> |
|
| 489 |
+ <FoxButtonGroup size="lg"> |
|
| 490 |
+ <FoxButton size="lg" type="primary" label="확인" /> |
|
| 491 |
+ </FoxButtonGroup> |
|
| 492 |
+ </FoxButtonPanel> |
|
| 493 |
+ </div> |
|
| 494 |
+ ), |
|
| 495 |
+ }, |
|
| 496 |
+ {
|
|
| 497 |
+ label: "그룹 셋 이상도 양끝 기준으로 벌어진다", |
|
| 498 |
+ node: ( |
|
| 499 |
+ <div className={styles.demoOutline}>
|
|
| 500 |
+ <FoxButtonPanel offset={false}>
|
|
| 501 |
+ <FoxButtonGroup size="lg"> |
|
| 502 |
+ <FoxButton size="lg" label="삭제" /> |
|
| 503 |
+ </FoxButtonGroup> |
|
| 504 |
+ <FoxButtonGroup size="lg"> |
|
| 505 |
+ <FoxButton size="lg" label="미리보기" /> |
|
| 506 |
+ </FoxButtonGroup> |
|
| 507 |
+ <FoxButtonGroup size="lg"> |
|
| 508 |
+ <FoxButton size="lg" type="primary" label="저장" /> |
|
| 509 |
+ </FoxButtonGroup> |
|
| 510 |
+ </FoxButtonPanel> |
|
| 511 |
+ </div> |
|
| 512 |
+ ), |
|
| 513 |
+ }, |
|
| 514 |
+ ], |
|
| 515 |
+ }, |
|
| 448 | 516 |
]; |
--- @fox/styles/components.scss
+++ @fox/styles/components.scss
... | ... | @@ -6,3 +6,4 @@ |
| 6 | 6 |
@use "fox-link-button"; |
| 7 | 7 |
@use "fox-icon-button"; |
| 8 | 8 |
@use "fox-button-group"; |
| 9 |
+@use "fox-button-panel"; |
Add a comment
Delete comment
Once you delete this comment, you won't be able to recover it. Are you sure you want to delete this comment?