fix(frontend): forward refs through shadcn bridge buttons

This commit is contained in:
Sagit
2026-02-19 05:55:54 +00:00
parent b6c2159614
commit 2308b25bcf
3 changed files with 71 additions and 54 deletions
@@ -46,3 +46,4 @@
- [2026-02-19] 完成 S7:删除全部 HeroUI/NextUI 依赖,补齐 `@internationalized/date` 与 `@react-aria/i18n` 显式依赖
- [2026-02-19] 完成 S8:构建验收通过(`npm run build`),`package.json` 已无 `heroui/nextui` 依赖
- [2026-02-19] 验证结果:业务代码中 `@heroui/*` 导入为 `0`,已统一替换为 `@/shadcn-bridge/heroui/*`(22 文件,106 处)
- [2026-02-19] 后续修复:`src/components/ui/button.tsx` 与 `src/shadcn-bridge/heroui/button.tsx` 改为 `forwardRef`,消除 `DropdownMenuTrigger asChild` 场景 ref 警告;复构建通过
+15 -10
View File
@@ -37,16 +37,21 @@ type ButtonProps = React.ComponentProps<"button"> &
asChild?: boolean;
};
function Button({ asChild = false, className, size, variant, ...props }: ButtonProps) {
const Comp = asChild ? Slot : "button";
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ asChild = false, className, size, variant, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return (
<Comp
className={cn(buttonVariants({ className, size, variant }))}
data-slot="button"
{...props}
/>
);
}
return (
<Comp
className={cn(buttonVariants({ className, size, variant }))}
data-slot="button"
ref={ref}
{...props}
/>
);
},
);
Button.displayName = "Button";
export { Button, buttonVariants };
@@ -57,49 +57,60 @@ function mapSize(size: HeroButtonSize, isIconOnly: boolean): "default" | "sm" |
return "default";
}
export function Button({
children,
className,
color = "default",
disabled,
endContent,
isIconOnly = false,
isLoading = false,
isDisabled,
onClick,
onPress,
size = "md",
startContent,
type = "button",
variant = "solid",
...props
}: ButtonProps & {
isDisabled?: boolean;
}) {
const resolvedVariant = mapVariant(color, variant);
const resolvedSize = mapSize(size, isIconOnly);
const resolvedDisabled = Boolean(disabled || isDisabled || isLoading);
export const Button = React.forwardRef<
HTMLButtonElement,
ButtonProps & {
isDisabled?: boolean;
}
>(
(
{
children,
className,
color = "default",
disabled,
endContent,
isIconOnly = false,
isLoading = false,
isDisabled,
onClick,
onPress,
size = "md",
startContent,
type = "button",
variant = "solid",
...props
},
ref,
) => {
const resolvedVariant = mapVariant(color, variant);
const resolvedSize = mapSize(size, isIconOnly);
const resolvedDisabled = Boolean(disabled || isDisabled || isLoading);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(event);
onPress?.(event);
};
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(event);
onPress?.(event);
};
return (
<BaseButton
className={cn(isIconOnly ? "p-0" : "", className)}
disabled={resolvedDisabled}
size={resolvedSize}
type={type}
variant={resolvedVariant}
onClick={handleClick}
{...props}
>
{isLoading ? <Loader2Icon className="mr-2 h-4 w-4 animate-spin" /> : null}
{startContent}
{isIconOnly ? null : children}
{isIconOnly ? children : null}
{endContent}
</BaseButton>
);
}
return (
<BaseButton
className={cn(isIconOnly ? "p-0" : "", className)}
disabled={resolvedDisabled}
ref={ref}
size={resolvedSize}
type={type}
variant={resolvedVariant}
onClick={handleClick}
{...props}
>
{isLoading ? <Loader2Icon className="mr-2 h-4 w-4 animate-spin" /> : null}
{startContent}
{isIconOnly ? null : children}
{isIconOnly ? children : null}
{endContent}
</BaseButton>
);
},
);
Button.displayName = "HeroBridgeButton";