File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
08-14
File name
Commit message
Commit date
"use client";
import {
Children,
Fragment,
cloneElement,
isValidElement,
type ReactElement,
type ReactNode,
} from "react";
import { cx } from "../../utils";
export type FoxButtonGroupOrientation = "horizontal" | "vertical";
export type FoxButtonGroupAlign = "start" | "center" | "end";
export type FoxButtonGroupSize = "lg" | "md" | "sm" | "xsm";
export interface FoxButtonGroupProps {
orientation?: FoxButtonGroupOrientation;
/** 주축 정렬. 세로에서는 그룹 높이가 정해져 있을 때만 눈에 띈다. */
align?: FoxButtonGroupAlign;
/** 자식 버튼의 `size`를 전부 이 값으로 덮어쓴다. 자식이 따로 준 값은 무시된다. */
size: FoxButtonGroupSize;
wrap?: boolean;
/** 배치 조정용. */
className?: string;
children?: ReactNode;
}
const ORIENTATION_CLASS: Record<FoxButtonGroupOrientation, string> = {
horizontal: "fox-button-group--horizontal",
vertical: "fox-button-group--vertical",
};
const ALIGN_CLASS: Record<FoxButtonGroupAlign, string> = {
start: "fox-button-group--start",
center: "fox-button-group--center",
end: "fox-button-group--end",
};
type Overrides = { size: FoxButtonGroupSize; fullWidth?: true };
/**
* 자식 버튼에 그룹 값을 강제로 덮어쓴다.
*
* - 호스트 요소(`div` 등)는 건드리지 않는다 — DOM에 없는 속성이 넘어가면 React가 경고한다.
* - `Children.map`은 프래그먼트 안으로 들어가지 않아 직접 내려간다. 그러지 않으면
* `<>…</>`로 감싼 버튼들이 덮어쓰기를 피해 가고 Fragment에 `size`가 붙어 경고가 난다.
*/
function overrideChildren(children: ReactNode, overrides: Overrides): ReactNode {
return Children.map(children, (child) => {
if (!isValidElement(child)) {
return child;
}
if (child.type === Fragment) {
const fragment = child as ReactElement<{ children?: ReactNode }>;
return overrideChildren(fragment.props.children, overrides);
}
if (typeof child.type === "string") {
return child;
}
return cloneElement(child as ReactElement<Record<string, unknown>>, overrides);
});
}
/**
* @fox 버튼 묶음. 간격·방향·정렬만 책임지고 상태를 갖지 않는다.
*
* 크기는 **그룹이 단일 진실 공급원**이다 — 자식이 저마다 다른 `size`를 줘도 그룹 값으로
* 통일된다. 세로일 때는 자식이 항상 폭을 채운다.
*
* ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
* (또는 개별 파티셜)로 한 번 불러와야 한다.
*/
export function FoxButtonGroup({
orientation = "horizontal",
align = "start",
size,
wrap = false,
className,
children,
}: FoxButtonGroupProps) {
const overrides: Overrides =
orientation === "vertical" ? { size, fullWidth: true } : { size };
return (
<div
className={cx(
"fox-button-group",
ORIENTATION_CLASS[orientation],
ALIGN_CLASS[align],
wrap && "fox-button-group--wrap",
className
)}
>
{overrideChildren(children, overrides)}
</div>
);
}