-
Notifications
You must be signed in to change notification settings - Fork 2
/
Button.tsx
131 lines (123 loc) · 4.63 KB
/
Button.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
* This program has been developed by students from the bachelor Computer Science at Utrecht University within the Software Project course.
* © Copyright Utrecht University (Department of Information and Computing Sciences)
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* This component uses the `class-variance-authority` package to generate dynamic class names based on the provided props.
* Inspired by https://ui.shadcn.com/docs/primitives/button.
*/
import React, { ReactNode } from 'react';
import { cn } from '@/src/lib/utils';
import { cva, VariantProps } from 'class-variance-authority';
import { IconType } from 'react-icons/lib';
/**
* The generated variants for the `Button` component, based on the `class-variance-authority` package.
*/
const buttonVariants = cva(
'active:scale-95 leading-4 inline-flex w-fit transition-colors duration-200 items-center justify-center rounded-md text-sm font-medium focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-background disabled:opacity-50 disabled:cursor-default disabled:pointer-events-none',
{
variants: {
variant: {
default:
'bg-primary text-primary-foreground hover:bg-primary/90 focus:ring-primary-highlight',
destructive:
'bg-destructive-background text-destructive-foreground hover:bg-destructive-background/80 focus:ring-destructive',
outline:
'bg-transparent text-background-foreground border border-border hover:bg-accent focus:ring-primary-highlight/20',
subtle:
'bg-accent text-accent-foreground hover:bg-accent/70 focus:ring-primary-highlight/50',
ghost:
'bg-transparent text-background-foreground hover:bg-accent focus:ring-primary-highlight/20',
},
size: {
default: 'h-10 py-2 px-4 ',
xs: 'h-8 px-3 rounded-md',
sm: 'h-10 px-3 rounded-md',
lg: 'h-11 px-8 rounded-md',
},
},
defaultVariants: {
variant: 'default',
size: 'default',
},
}
);
const iconVariants = cva('transition-all duration-200 shrink-0', {
variants: {
size: {
default: 'h-5 w-5',
xs: 'h-4 w-4',
sm: 'h-4 w-4',
lg: 'h-6 w-6',
},
},
defaultVariants: {
size: 'default',
},
});
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
VariantProps<typeof buttonVariants> {
label?: string;
icon?: IconType | null;
iconNode?: ReactNode | null;
}
/**
* A component that renders a button with customizable styling and an optional icon.
* @param props - Props for the `Button` component.
* @param [props.className] - Additional CSS classes to apply to the button.
* @param [props.icon] - The icon to display in the button. If `iconNode` is also provided, this icon will be ignored.
* @param [props.iconNode] - A custom icon node to display in the button. If provided, `icon` will be ignored.
* @param [props.variant="default"] - The variant of the button to use.
* @param [props.size="default"] - The size of the button to use.
* @param [props.label] - The label to display in the button, which will be read by screen readers.
* @param [props.children] - The content to display inside the button.
* @returns - A React element representing the `Button` component.
* @example
* <Button
* icon={AiFillCheckCircle}
* label="Submit"
* variant="outline"
* size="lg"
* onClick={() => console.log('Button clicked!')}
* >
* Submit
* </Button>
* @remark - You can pass either a label or children to the button, if you pass both, the label will be used.
*/
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ className, iconNode, variant, size, label, children, ...props }, ref) => {
const { icon, ...divProps } = props;
return (
<button
className={cn(buttonVariants({ variant, className, size }))}
ref={ref}
{...divProps}
>
<span className="sr-only">{label}</span>
{props.icon ? (
<div className="flex flex-row items-center gap-x-2 leading-4">
<props.icon className={cn(iconVariants({ size }))} />
{(label || children) && <>{label || children}</>}
</div>
) : (
<>
{iconNode ? (
<div className="flex flex-row items-center gap-x-2 leading-4">
{iconNode}
{label || children}
</div>
) : (
<>{label || children}</>
)}
</>
)}
</button>
);
}
);
Button.displayName = 'Button';
export { Button, buttonVariants, iconVariants };