diff --git a/src/components/ui/app-feedback.tsx b/src/components/ui/app-feedback.tsx
index 64c3661..5bc937d 100644
--- a/src/components/ui/app-feedback.tsx
+++ b/src/components/ui/app-feedback.tsx
@@ -1,11 +1,19 @@
+/**
+ * 应用级反馈设施:提供共享Tooltip时间参数与通知挂载点。
+ * 这个模块只负责提示的呈现环境,任务进度、错误事实和通知触发仍由业务调用方管理。
+ */
"use client";
import { createContext, type ReactNode } from "react";
import * as Tooltip from "@radix-ui/react-tooltip";
import { Toaster } from "sonner";
+/** 默认false使独立挂载的IconButton退回原生title;只有AppFeedback子树启用统一浮层提示。 */
export const FeedbackContext = createContext(false);
+/** 包住页面一次即可共享提示延迟;400ms首次出现,200ms内切换提示可跳过等待。
+ * Toaster放在右下角,最多展示三条通知,默认4500ms消失并允许关闭;颜色读取主题变量。
+ * children正常透传,本组件不订阅文档、队列或服务端状态。 */
export function AppFeedback({ children }: { children: ReactNode }) {
return
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
index 775d380..b287e0c 100644
--- a/src/components/ui/button.tsx
+++ b/src/components/ui/button.tsx
@@ -1,9 +1,15 @@
+/**
+ * 通用按钮的样式与属性适配层。variant表达视觉语义,size控制尺寸;点击、禁用和表单行为由调用方传入。
+ * 可通过asChild复用子元素,避免链接外再套按钮;这里不保存业务状态,也不默认指定button的type。
+ */
import * as React from "react";
import { Slot } from "@radix-ui/react-slot";
import { cva, type VariantProps } from "class-variance-authority";
import { cn } from "@/lib/utils";
+/** 将基础交互样式、语义变体和尺寸变体组合成className;未指定时使用default/default。
+ * focus-visible提供键盘焦点反馈,disabled类限制指针交互并淡化外观,内嵌SVG统一尺寸。 */
const buttonVariants = cva(
"inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
{
@@ -34,6 +40,7 @@ const buttonVariants = cva(
},
);
+/** 在原生button属性上增加样式变体和asChild;ref仍按按钮声明,实际宿主由asChild决定。 */
export interface ButtonProps
extends
React.ButtonHTMLAttributes,
@@ -41,8 +48,11 @@ export interface ButtonProps
asChild?: boolean;
}
+/** 转发ref和剩余原生属性,供调用方设置事件、可访问名称、disabled和表单属性。
+ * Slot把属性合并到唯一子元素;使用链接等宿主时,其语义及行为由该子元素承担。 */
const Button = React.forwardRef(
({ className, variant, size, asChild = false, ...props }, ref) => {
+ // 只选择宿主,不另包一层DOM;cn合并变体和调用方样式,业务逻辑不进入这个封装。
const Comp = asChild ? Slot : "button";
return (
(
);
},
);
+// forwardRef生成包装组件后补显示名,便于React开发工具定位。
Button.displayName = "Button";
export { Button, buttonVariants };
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
index ac2b9bb..a0c2fec 100644
--- a/src/components/ui/dialog.tsx
+++ b/src/components/ui/dialog.tsx
@@ -1,3 +1,7 @@
+/**
+ * 基于Radix的对话框展示封装:保留其开关、焦点管理和可访问语义,补项目统一的遮罩、布局与动画。
+ * 调用方通过Root的open/onOpenChange控制业务流程;保存中是否允许关闭等规则不由本模块决定。
+ */
"use client";
import * as React from "react";
@@ -6,14 +10,19 @@ import { X } from "lucide-react";
import { cn } from "@/lib/utils";
+/** Root提供受控或非受控开关上下文,本层不再复制一份open状态。 */
const Dialog = DialogPrimitive.Root;
+/** Trigger接入同一Root,负责触发打开;可沿用Radix的asChild组合调用方控件。 */
const DialogTrigger = DialogPrimitive.Trigger;
+/** Portal把浮层移出页面普通布局,避免受父容器裁切;挂载方式沿用Radix。 */
const DialogPortal = DialogPrimitive.Portal;
+/** 暴露关闭原语,供业务在底部动作区等位置放额外关闭入口。 */
const DialogClose = DialogPrimitive.Close;
+/** 转发遮罩ref与属性,铺满视口;开关动画由Radix的data-state驱动,className允许调用方补样式。 */
const DialogOverlay = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
@@ -29,6 +38,9 @@ const DialogOverlay = React.forwardRef<
));
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
+/** 组合Portal、遮罩与居中内容面板,并在children之后固定附带右上角关闭按钮。
+ * 面板ref和剩余属性交给Radix Content,保留焦点及外部交互事件;关闭按钮有读屏文字。
+ * 本封装没有单独隐藏默认关闭按钮的参数,业务若要阻止关闭应在受控开关/事件层处理。 */
const DialogContent = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
@@ -53,6 +65,7 @@ const DialogContent = React.forwardRef<
));
DialogContent.displayName = DialogPrimitive.Content.displayName;
+/** 标题区只组织纵向间距与响应式文字对齐;原生div属性和children原样透传。 */
const DialogHeader = ({
className,
...props
@@ -67,6 +80,7 @@ const DialogHeader = ({
);
DialogHeader.displayName = "DialogHeader";
+/** 动作区在窄屏使用反向纵排,在sm及以上改为靠右横排;不绑定提交或取消动作。 */
const DialogFooter = ({
className,
...props
@@ -81,6 +95,7 @@ const DialogFooter = ({
);
DialogFooter.displayName = "DialogFooter";
+/** 转发Radix标题的ref/属性并统一字号;标题仍参与对话框的可访问命名关联。 */
const DialogTitle = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
@@ -96,6 +111,7 @@ const DialogTitle = React.forwardRef<
));
DialogTitle.displayName = DialogPrimitive.Title.displayName;
+/** 转发Radix说明文字的ref/属性并使用弱化文字样式;说明关联由Radix上下文维护。 */
const DialogDescription = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef
diff --git a/src/components/ui/icon-button.tsx b/src/components/ui/icon-button.tsx
index 5315ce8..1490afe 100644
--- a/src/components/ui/icon-button.tsx
+++ b/src/components/ui/icon-button.tsx
@@ -1,3 +1,7 @@
+/**
+ * 画布等工具栏使用的图标按钮。统一按钮尺寸、图标与可访问名称,并按反馈Provider决定使用浮层提示还是原生title。
+ * 按钮的业务动作由onClick等原生属性传入;这里不维护工具选择状态。
+ */
"use client";
import { FeedbackContext } from "./app-feedback";
@@ -6,13 +10,17 @@ import { useContext, forwardRef, type ButtonHTMLAttributes } from "react";
import type { LucideIcon } from "lucide-react";
import styles from "./icon-button.module.css";
+/** icon提供Lucide图标实现,label提供默认可访问名称与提示;不接受任意children替换内部图标。 */
export type IconButtonProps = Omit, "children"> & {
icon: LucideIcon;
label: string;
};
+/** 将ref交给真实button;type默认为button,避免放进表单时意外提交。
+ * 调用方仍可传aria-pressed、disabled等状态,组件不会自行判断命令是否可用。 */
export const IconButton = forwardRef(
function IconButton({ icon: Icon, label, title = label, className = "", type = "button", ...props }, ref) {
+ // Provider存在时由Radix显示提示,移除原生title以免出现两层提示;图标自身对读屏隐藏。
const feedback = useContext(FeedbackContext);
const button = (
);
+ // 没有全局提示环境就返回原生按钮;空title也不创建Tooltip。提示通过Portal避免被工具栏裁切。
if (!feedback || !title) return button;
return {button}