File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
File name
Commit message
Commit date
"use client";
import type { ReactNode, Ref } from "react";
import { cx } from "../../utils";
export type FoxIconButtonType = "default" | "ghost" | "overlay";
export type FoxIconButtonSize = "lg" | "md" | "sm" | "xsm";
export interface FoxIconButtonProps {
type?: FoxIconButtonType;
size: FoxIconButtonSize;
/** 네이티브 `type` — prop의 `type`이 시각 계열을 쓰므로 분리했다. */
htmlType?: "button" | "submit" | "reset";
/** 배치 조정용. 모양이 달라야 하면 여기 말고 `type`을 추가한다. */
className?: string;
/** `currentColor`로 그린 SVG여야 계열별 색이 적용된다. 크기는 슬롯이 정한다. */
icon: ReactNode;
/** 글자가 보이지 않으므로 `aria-label`이 된다. 무엇을 하는 버튼인지 적는다. */
label: string;
/**
* 주면 토글 버튼이 된다(`aria-pressed`). 생략하면 일반 아이콘 버튼이다.
* 호출부가 소유한다 — 버튼이 스스로 뒤집지 않는다.
*/
active?: boolean;
disabled?: boolean;
/** 참이면 렌더하지 않는다(DOM에 남지 않는다). */
hidden?: boolean;
onAction?: () => void;
/** 포인터가 올라오면 `true`, 벗어나면 `false`. */
onHoverChange?: (hovered: boolean) => void;
ref?: Ref<HTMLButtonElement>;
}
const TYPE_CLASS: Record<FoxIconButtonType, string> = {
default: "fox-icon-button--default",
ghost: "fox-icon-button--ghost",
overlay: "fox-icon-button--overlay",
};
const SIZE_CLASS: Record<FoxIconButtonSize, string> = {
lg: "fox-icon-button--lg",
md: "fox-icon-button--md",
sm: "fox-icon-button--sm",
xsm: "fox-icon-button--xsm",
};
/**
* @fox 아이콘 버튼. 상태를 갖지 않는다.
*
* `active`를 주면 토글로 동작한다 — 시안의 btn-ico와 btn-ico-toggle이 계열·크기·모양이
* 같고 켜짐 상태만 다르므로 한 컴포넌트로 둔다.
*
* ⚠️ 스타일은 이 파일이 import하지 않는다 — 호스트 앱이 `@use "@fox/styles/components"`
* (또는 개별 파티셜)로 한 번 불러와야 한다.
*/
export function FoxIconButton({
type = "default",
size,
htmlType = "button",
className,
icon,
label,
active,
disabled = false,
hidden = false,
onAction,
onHoverChange,
ref,
}: FoxIconButtonProps) {
if (hidden) {
return null;
}
return (
<button
ref={ref}
type={htmlType}
className={cx("fox-icon-button", TYPE_CLASS[type], SIZE_CLASS[size], className)}
aria-label={label}
aria-pressed={active}
disabled={disabled}
onClick={disabled ? undefined : onAction}
onMouseEnter={onHoverChange ? () => onHoverChange(true) : undefined}
onMouseLeave={onHoverChange ? () => onHoverChange(false) : undefined}
>
<span className="fox-icon-button__icon" aria-hidden="true">
{icon}
</span>
</button>
);
}