This commit is contained in:
2026-06-14 23:29:30 +08:00
parent 542615a941
commit 5094d341fa
70 changed files with 313 additions and 914 deletions
@@ -0,0 +1,83 @@
import {
type ComponentPropsWithoutRef,
forwardRef,
type ReactNode,
} from "react";
import { Icon } from "../Icon/Icon";
import { SpinnerSvg } from "../../assets/svg/SpinnerSvg";
import { cpm } from "../../styles/utils/cpm";
import { itemSizeRecipe } from "../../recipes/itemSizeRecipe";
import { variantRecipe } from "../../recipes/variantRecipe";
import { brandRecipe } from "../../recipes/brandRecipe";
import { inlineSizeRecipe } from "../../recipes/inlineSizeRecipe";
export type ButtonSize = "xs" | "sm" | "md" | "lg";
export type ButtonProps = ComponentPropsWithoutRef<"button"> & {
size?: ButtonSize;
variant?: "filled" | "outline" | "subtle" | "ghost";
shape?: "rounded" | "square" | "circle";
brand?: "success" | "danger" | "info" | "warning" | "emphasize";
} & {
iconOnly?: boolean;
hideIcon?: boolean;
loading?: boolean;
} & {
iconSvg?: ReactNode;
loadingIconSvg?: ReactNode;
};
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(props, ref) => {
const {
size = "md",
variant = "filled",
shape = "rounded",
brand = "info",
// ===========================
iconOnly = false,
hideIcon = false,
loading,
disabled = false,
// =========================
className = "",
children,
// ==========================
iconSvg,
loadingIconSvg = <SpinnerSvg />,
...rest
} = props;
const mergedClass = cpm(
itemSizeRecipe({ size, shape, iconOnly }),
variantRecipe({ variant, disabled }),
brandRecipe({ brand }),
className,
);
const contentClass = loading
? cpm("visible--hidden", inlineSizeRecipe({ size }))
: inlineSizeRecipe({ size });
const loadingIconClass = loading ? "position--absolute" : "";
const icon = <Icon size={size}>{iconSvg}</Icon>;
const loadingIcon = (
<Icon className={loadingIconClass} size={size}>
{loadingIconSvg}
</Icon>
);
return (
<button ref={ref} className={mergedClass} disabled={disabled} {...rest}>
<span className={contentClass}>
{!hideIcon && iconSvg && icon}
{!iconOnly && children}
</span>
{loading && loadingIcon}
</button>
);
},
);
Button.displayName = "Button";