Files
Endoleap/src/components/ui/icon-button.tsx
T

47 lines
2.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 画布等工具栏使用的图标按钮。统一按钮尺寸、图标与可访问名称,并按反馈Provider决定使用浮层提示还是原生title。
* 按钮的业务动作由onClick等原生属性传入;这里不维护工具选择状态。
*/
"use client";
import { FeedbackContext } from "./app-feedback";
import * as Tooltip from "@radix-ui/react-tooltip";
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<ButtonHTMLAttributes<HTMLButtonElement>, "children"> & {
icon: LucideIcon;
label: string;
};
/** 将ref交给真实button;type默认为button,避免放进表单时意外提交。
* 调用方仍可传aria-pressed、disabled等状态,组件不会自行判断命令是否可用。 */
export const IconButton = forwardRef<HTMLButtonElement, IconButtonProps>(
function IconButton({ icon: Icon, label, title = label, className = "", type = "button", ...props }, ref) {
// Provider存在时由Radix显示提示,移除原生title以免出现两层提示;图标自身对读屏隐藏。
// 后置 ...props 可以覆盖 aria-label 等同名属性;label 是默认名称,最终 DOM 语义由调用者共同负责。
const feedback = useContext(FeedbackContext);
const button = (
<button
ref={ref}
type={type}
aria-label={label}
title={feedback ? undefined : title}
className={`${styles.button} ${className}`}
{...props}
>
<Icon size={16} strokeWidth={1.6} aria-hidden="true" />
</button>
);
// 没有全局提示环境就返回原生按钮;空title也不创建Tooltip。提示通过Portal避免被工具栏裁切。
if (!feedback || !title) return button;
return <Tooltip.Root><Tooltip.Trigger asChild>{button}</Tooltip.Trigger><Tooltip.Portal>
<Tooltip.Content className={styles.tooltip} sideOffset={7} collisionPadding={8}>
{title}<Tooltip.Arrow className={styles.arrow} />
</Tooltip.Content>
</Tooltip.Portal></Tooltip.Root>;
},
);