- 动效基元:ai-dot 波动(三点竖跳)、ai-stream 不确定进度条、ai-breathe 呼吸微光(均带 reduced-motion 回退) - ThinkingIndicator 升级为明显波动;新增 Spinner / StreamingBar / GenerationSkeleton 原子件;Button 加 loading 态 - 写作台:首 token 前正文区『AI 正在构思本章…』占位(补最大缺口,不再空白像卡死)+ 流式顶部进度条 - 整章重写流式进度条;续写/润色忙碌骨架 + 触发键转圈;生成器/角色/世界观改用 Button loading
45 lines
1006 B
TypeScript
45 lines
1006 B
TypeScript
import { forwardRef, type ButtonHTMLAttributes, type ReactNode } from "react";
|
||
|
||
import {
|
||
buttonClass,
|
||
type ButtonSize,
|
||
type ButtonVariant,
|
||
} from "@/lib/ui/variants";
|
||
import { Spinner } from "./Spinner";
|
||
|
||
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||
children: ReactNode;
|
||
variant?: ButtonVariant;
|
||
size?: ButtonSize;
|
||
// 忙碌态:显示转圈 + 禁用 + aria-busy,统一各处"处理中"的过程感。
|
||
loading?: boolean;
|
||
}
|
||
|
||
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(
|
||
{
|
||
children,
|
||
className,
|
||
variant,
|
||
size,
|
||
type = "button",
|
||
loading = false,
|
||
disabled,
|
||
...props
|
||
},
|
||
ref,
|
||
) {
|
||
return (
|
||
<button
|
||
ref={ref}
|
||
type={type}
|
||
className={buttonClass({ variant, size, className })}
|
||
disabled={disabled || loading}
|
||
aria-busy={loading || undefined}
|
||
{...props}
|
||
>
|
||
{loading ? <Spinner className="h-4 w-4" /> : null}
|
||
{children}
|
||
</button>
|
||
);
|
||
});
|