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] 完成 S7:删除全部 HeroUI/NextUI 依赖,补齐 `@internationalized/date` 与 `@react-aria/i18n` 显式依赖
- [2026-02-19] 完成 S8:构建验收通过(`npm run build`),`package.json` 已无 `heroui/nextui` 依赖 - [2026-02-19] 完成 S8:构建验收通过(`npm run build`),`package.json` 已无 `heroui/nextui` 依赖
- [2026-02-19] 验证结果:业务代码中 `@heroui/*` 导入为 `0`,已统一替换为 `@/shadcn-bridge/heroui/*`(22 文件,106 处) - [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; asChild?: boolean;
}; };
function Button({ asChild = false, className, size, variant, ...props }: ButtonProps) { const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
const Comp = asChild ? Slot : "button"; ({ asChild = false, className, size, variant, ...props }, ref) => {
const Comp = asChild ? Slot : "button";
return ( return (
<Comp <Comp
className={cn(buttonVariants({ className, size, variant }))} className={cn(buttonVariants({ className, size, variant }))}
data-slot="button" data-slot="button"
{...props} ref={ref}
/> {...props}
); />
} );
},
);
Button.displayName = "Button";
export { Button, buttonVariants }; export { Button, buttonVariants };
@@ -57,49 +57,60 @@ function mapSize(size: HeroButtonSize, isIconOnly: boolean): "default" | "sm" |
return "default"; return "default";
} }
export function Button({ export const Button = React.forwardRef<
children, HTMLButtonElement,
className, ButtonProps & {
color = "default", isDisabled?: boolean;
disabled, }
endContent, >(
isIconOnly = false, (
isLoading = false, {
isDisabled, children,
onClick, className,
onPress, color = "default",
size = "md", disabled,
startContent, endContent,
type = "button", isIconOnly = false,
variant = "solid", isLoading = false,
...props isDisabled,
}: ButtonProps & { onClick,
isDisabled?: boolean; onPress,
}) { size = "md",
const resolvedVariant = mapVariant(color, variant); startContent,
const resolvedSize = mapSize(size, isIconOnly); type = "button",
const resolvedDisabled = Boolean(disabled || isDisabled || isLoading); 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>) => { const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
onClick?.(event); onClick?.(event);
onPress?.(event); onPress?.(event);
}; };
return ( return (
<BaseButton <BaseButton
className={cn(isIconOnly ? "p-0" : "", className)} className={cn(isIconOnly ? "p-0" : "", className)}
disabled={resolvedDisabled} disabled={resolvedDisabled}
size={resolvedSize} ref={ref}
type={type} size={resolvedSize}
variant={resolvedVariant} type={type}
onClick={handleClick} variant={resolvedVariant}
{...props} onClick={handleClick}
> {...props}
{isLoading ? <Loader2Icon className="mr-2 h-4 w-4 animate-spin" /> : null} >
{startContent} {isLoading ? <Loader2Icon className="mr-2 h-4 w-4 animate-spin" /> : null}
{isIconOnly ? null : children} {startContent}
{isIconOnly ? children : null} {isIconOnly ? null : children}
{endContent} {isIconOnly ? children : null}
</BaseButton> {endContent}
); </BaseButton>
} );
},
);
Button.displayName = "HeroBridgeButton";