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
08-14
08-18
08-18
08-14
08-19
08-18
08-18
08-18
08-18
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>
);
}