返回 presentation-ai
button.tsx
根目录 / src / components / ui / button.tsx
1 import {
2 Tooltip,
3 TooltipContent,
4 TooltipProvider,
5 TooltipTrigger,
6 } from "@/components/ui/tooltip";
7 import { cn } from "@/lib/utils";
8 import { Slot } from "@radix-ui/react-slot";
9 import { Arrow } from "@radix-ui/react-tooltip";
10 import { cva, type VariantProps } from "class-variance-authority";
11 import * as React from "react";
12 import { Spinner } from "./spinner";
13
14 const buttonVariants = cva(
15 "inline-flex items-center justify-center rounded-md text-sm font-medium whitespace-nowrap ring-offset-background transition-all duration-150 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:outline-hidden active:scale-95 disabled:pointer-events-none disabled:opacity-50",
16 {
17 variants: {
18 variant: {
19 default: "bg-primary text-primary-foreground hover:bg-primary/90",
20 success: "bg-green-500 text-primary-foreground hover:bg-green-400/90",
21 destructive:
22 "bg-destructive text-destructive-foreground hover:bg-destructive/90",
23 outline:
24 "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
25 secondary:
26 "bg-secondary text-secondary-foreground hover:bg-secondary/80",
27 ghost: "hover:bg-accent hover:text-accent-foreground",
28 link: "m-0 p-0 text-primary underline underline-offset-4",
29 loading:
30 "disabled flex items-center justify-center bg-primary text-primary-foreground",
31 noBackground: "m-0 bg-transparent p-0 text-sm text-inherit",
32 outlineLoading:
33 "disabled flex items-center justify-center border border-input bg-background hover:bg-accent hover:text-accent-foreground",
34 noBackgroundLoading:
35 "disabled m-0 flex items-center justify-center bg-transparent p-0 text-sm text-inherit",
36 },
37 size: {
38 default: "h-10 px-4 py-2",
39 sm: "h-9 rounded-md px-3",
40 lg: "h-11 rounded-md px-8",
41 icon: "h-8 w-8 md:h-10 md:w-10",
42 xs: "h-5 rounded-md p-1",
43 },
44 },
45 defaultVariants: {
46 variant: "default",
47 size: "default",
48 },
49 },
50 );
51
52 export interface ButtonProps
53 extends
54 React.ButtonHTMLAttributes<HTMLButtonElement>,
55 VariantProps<typeof buttonVariants> {
56 asChild?: boolean;
57 }
58
59 const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
60 ({ className, variant, size, asChild = false, children, ...props }, ref) => {
61 const Comp = asChild ? Slot : "button";
62 const ChildComponent = () => {
63 if (variant !== "loading") {
64 return children;
65 }
66 if (
67 variant === "loading" ||
68 variant === "outlineLoading" ||
69 variant === "noBackgroundLoading"
70 ) {
71 return <Spinner className="h-4 w-4"></Spinner>;
72 }
73 return children;
74 };
75 return (
76 <Comp
77 className={cn(buttonVariants({ variant, size, className }))}
78 ref={ref}
79 {...props}
80 >
81 {ChildComponent()}
82 </Comp>
83 );
84 },
85 );
86 Button.displayName = "Button";
87 // TooltipButton should have exactly the same type as Button, with a couple tooltip props
88 export interface TooltipButtonProps
89 extends React.ComponentProps<"button">, VariantProps<typeof buttonVariants> {
90 tooltipText?: string;
91 tooltipSide?: "top" | "right" | "bottom" | "left";
92 tooltipAlign?: "start" | "center" | "end";
93 tooltipOffset?: number;
94 tooltipAlignOffset?: number;
95 delayDuration?: number;
96 arrow?: boolean;
97 asChild?: boolean;
98 }
99
100 const TooltipButton = React.forwardRef<HTMLButtonElement, TooltipButtonProps>(
101 (
102 {
103 tooltipText,
104 tooltipSide = "top",
105 tooltipAlign = "center",
106 tooltipOffset = 4,
107 tooltipAlignOffset = 0,
108 delayDuration = 0,
109 arrow = true,
110 children,
111 ...props
112 },
113 ref,
114 ) => {
115 // No tooltip: just Button
116 if (!tooltipText) {
117 return (
118 <Button ref={ref} {...props}>
119 {children}
120 </Button>
121 );
122 }
123 // With tooltip
124 return (
125 <TooltipProvider>
126 <Tooltip delayDuration={delayDuration}>
127 <TooltipTrigger asChild suppressHydrationWarning>
128 <Button ref={ref} {...props}>
129 {children}
130 </Button>
131 </TooltipTrigger>
132 <TooltipContent
133 side={tooltipSide}
134 align={tooltipAlign}
135 sideOffset={tooltipOffset}
136 alignOffset={tooltipAlignOffset}
137 >
138 {arrow && <Arrow className="fill-primary" />}
139 <p>{tooltipText}</p>
140 </TooltipContent>
141 </Tooltip>
142 </TooltipProvider>
143 );
144 },
145 );
146
147 TooltipButton.displayName = "TooltipButton";
148
149 export { Button, buttonVariants, TooltipButton };
150
150 lines Plain Text