feat:init

This commit is contained in:
zhongyidian committed 2025-12-29 19:19:10 +08:00
1 parent 53d0eeac6b
commit ae0c050010
494 files changed
+17701 -2318

No files matched your search

+75
View File
@@ -0,0 +1,75 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Button, ImageButton } from "./button";
const meta: Meta<typeof Button> = {
title: "Components/Button",
component: Button,
tags: ["autodocs"],
argTypes: {
variant: {
control: { type: "select" },
options: ["primary", "secondary", "ghost"],
},
},
};
export default meta;
type Story = StoryObj<typeof Button>;
export const Primary: Story = {
args: {
variant: "primary",
children: "按钮",
},
};
export const Secondary: Story = {
args: {
variant: "secondary",
children: "次要按钮",
},
};
export const Ghost: Story = {
args: {
variant: "ghost",
children: "幽灵按钮",
},
};
export const Disabled: Story = {
args: {
disabled: true,
children: "禁用状态",
},
};
export const Loading: Story = {
args: {
loading: true,
children: "加载中...",
},
};
export const WithImage: Story = {
render: () => (
<div className="flex gap-4">
<ImageButton
src="/path/to/button-image.png"
hoverSrc="/path/to/button-image-hover.png"
alt="图片按钮"
width={120}
height={40}
/>
</div>
),
};
export const Playground: Story = {
args: {
variant: "primary",
children: "点击我",
className: "px-8 py-3 text-lg",
},
};
+129
View File
@@ -0,0 +1,129 @@
import { forwardRef, useState, useEffect } from "react";
import { cn } from "@/lib/utils";
import type { BaseButtonProps, ImageButtonProps } from "./button.types";
/**
* 基础按钮组件
*/
const Button = forwardRef<HTMLButtonElement, BaseButtonProps>(
(
{
children,
className = "",
variant = "primary",
disabled = false,
loading = false,
type = "button",
...props
},
ref
) => {
const baseClasses =
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none";
const variantClasses = {
primary: "bg-primary text-primary-foreground hover:bg-primary/90",
secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
ghost: "hover:bg-accent hover:text-accent-foreground",
};
return (
<button
ref={ref}
type={type as "button" | "submit" | "reset" | undefined}
className={cn(
baseClasses,
variantClasses[variant],
"px-4 py-2",
className
)}
disabled={disabled || loading}
{...props}
>
{loading ? "加载中..." : children}
</button>
);
}
);
Button.displayName = "Button";
/**
* 图片按钮组件
*/
const ImageButton = forwardRef<HTMLButtonElement, ImageButtonProps>(
(
{
src,
alt,
hoverSrc,
width = "auto",
height = "auto",
className = "",
disabled = false,
loading = false,
type = "button",
onMouseEnter,
onMouseLeave,
...props
},
ref
) => {
const [isHovered, setIsHovered] = useState(false);
const [imageSrc, setImageSrc] = useState(src);
useEffect(() => {
if (hoverSrc) {
const img = new Image();
img.src = hoverSrc;
}
}, [hoverSrc]);
const handleMouseEnter = (e: React.MouseEvent<HTMLButtonElement>) => {
setIsHovered(true);
if (hoverSrc) {
setImageSrc(hoverSrc);
}
onMouseEnter?.(e);
};
const handleMouseLeave = (e: React.MouseEvent<HTMLButtonElement>) => {
setIsHovered(false);
setImageSrc(src);
onMouseLeave?.(e);
};
return (
<button
ref={ref}
type={type as "button" | "submit" | "reset" | undefined}
className={cn(
"transition-opacity hover:opacity-90 active:opacity-80",
"focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary",
"disabled:opacity-50 disabled:cursor-not-allowed",
className
)}
disabled={disabled || loading}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
{...props}
>
<img
src={imageSrc}
alt={alt}
width={width}
height={height}
className={cn(
"object-contain transition-transform",
isHovered && "transform scale-105"
)}
draggable="false"
/>
</button>
);
}
);
ImageButton.displayName = "ImageButton";
export { Button, ImageButton };
+30
View File
@@ -0,0 +1,30 @@
import type { ButtonHTMLAttributes, ReactNode } from "react";
/** 基础按钮属性 */
export interface BaseButtonProps
extends ButtonHTMLAttributes<HTMLButtonElement> {
/** 是否显示加载状态 */
loading?: boolean;
/** 按钮变体 */
variant?: "primary" | "secondary" | "ghost";
/** 自定义类名 */
className?: string;
/** 子元素 */
children: ReactNode;
}
/** 图片按钮属性 */
export interface ImageButtonProps extends Omit<BaseButtonProps, "children"> {
/** 图片URL */
src: string;
/** 图片替代文本 */
alt: string;
/** 图片宽度 */
width?: number | string;
/** 图片高度 */
height?: number | string;
/** 悬停时显示的图片URL */
hoverSrc?: string;
}
export type ButtonProps = BaseButtonProps | ImageButtonProps;
+2
View File
@@ -0,0 +1,2 @@
export * from "./button";
export * from "./button.types";