"use client";
import type { ReactNode, Ref } from "react";
import { cx } from "../../utils";
import { FoxSpinner } from "../fox-spinner";
export type FoxButtonType = "primary" | "secondary" | "default" | "error";
export type FoxButtonSize = "lg" | "md" | "sm" | "xsm";
export interface FoxButtonProps {
type?: FoxButtonType;
size: FoxButtonSize;
/** 네이티브 `type` — prop의 `type`이 시각 계열을 쓰므로 분리했다. */
htmlType?: "button" | "submit" | "reset";
/** 배치 조정용. 모양이 달라야 하면 여기 말고 `type`을 추가한다. */
className?: string;
/** 아이콘 폰트를 넘긴다 — ``. 크기·색은 슬롯이 정한다. */
leadingIcon?: ReactNode;
trailingIcon?: ReactNode;
label: string;
disabled?: boolean;
/** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
hidden?: boolean;
/** 호출부가 소유한다 — 버튼이 스스로 켜지 않는다. */
loading?: boolean;
fullWidth?: boolean;
onAction?: () => void;
/** 포인터가 올라오면 `true`, 벗어나면 `false`. */
onHoverChange?: (hovered: boolean) => void;
ref?: Ref;
}
/** `Record`로 고정해 계열을 추가하면 항목 누락이 타입 에러가 되게 한다. */
const TYPE_CLASS: Record = {
primary: "fox-button--primary",
secondary: "fox-button--secondary",
default: "fox-button--default",
error: "fox-button--error",
};
const SIZE_CLASS: Record = {
lg: "fox-button--lg",
md: "fox-button--md",
sm: "fox-button--sm",
xsm: "fox-button--xsm",
};
/**
* @fox 공용 버튼. 상태를 갖지 않는다.
*
* ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
* (또는 개별 파티셜)로 한 번 불러와야 한다.
*/
export function FoxButton({
type = "default",
size,
htmlType = "button",
className,
leadingIcon,
trailingIcon,
label,
disabled = false,
hidden = false,
loading = false,
fullWidth = false,
onAction,
onHoverChange,
ref,
}: FoxButtonProps) {
if (hidden) {
return null;
}
// 같은 요청이 두 번 나가지 않게 로딩 중에도 막는다.
const inactive = disabled || loading;
return (
);
}