-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathButton.tsx
More file actions
123 lines (112 loc) · 3.05 KB
/
Button.tsx
File metadata and controls
123 lines (112 loc) · 3.05 KB
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
import classNames from 'classnames';
import React, { useCallback, forwardRef } from 'react';
import { Theme, Button as ThemeButton, ButtonProps as ThemeButtonProps } from 'theme-ui';
type ButtonClickType = React.MouseEvent< HTMLButtonElement, MouseEvent >;
interface ButtonTheme extends Theme {
outline?: Record< string, string >;
}
export enum ButtonVariant {
'danger', // will be deprecated in the future
'display',
'ghost',
'icon',
'primary',
'secondary',
'tertiary',
'text',
}
export interface ButtonProps extends ThemeButtonProps {
/** Whether the button is disabled. */
disabled?: boolean;
/** Uses `aria-disabled` instead of the native `disabled` attribute, keeping the button focusable. */
preferAriaDisabled?: boolean;
/** Click event handler. */
onClick?: ( event: ButtonClickType ) => void;
/** Stretches the button to full width of its container. */
full?: boolean;
/** Allows the button to grow within a flex container. */
grow?: boolean;
/**
* The visual style variant of the button.
* @default 'primary'
*/
variant?: keyof typeof ButtonVariant; // converts the enum to a string union type
/** Applies danger/destructive styling to the button. */
danger?: boolean;
}
/**
* A versatile button component with multiple style variants, danger state, and accessible disabled support.
*/
const Button = forwardRef< HTMLButtonElement, ButtonProps >(
(
{
className,
disabled,
preferAriaDisabled,
onClick,
sx,
full,
grow,
variant = 'primary',
danger = variant === 'danger', // fallback for danger variant used before the prop was added
...rest
},
ref
) => {
const disabledAttributes =
disabled && preferAriaDisabled === true ? { 'aria-disabled': true } : { disabled };
let disabledStyles = {};
const handleOnClick = useCallback(
( event: ButtonClickType ) => {
if ( preferAriaDisabled && disabled ) {
return event.preventDefault();
}
if ( onClick ) {
return onClick( event );
}
},
[ disabled, onClick ]
);
if (
disabled &&
! danger &&
variant !== 'text' &&
variant !== 'ghost' &&
variant !== 'tertiary'
) {
disabledStyles = {
opacity: 0.7,
backgroundColor: 'input.border.disabled',
color: 'texts.secondary',
};
}
return (
<ThemeButton
sx={ {
'&:focus': 'none',
'&:focus-visible': ( theme: ButtonTheme ) => theme.outline,
'&[disabled], &[aria-disabled="true"]': {
cursor: 'not-allowed',
pointerEvents: 'none',
...disabledStyles,
},
'&:hover, &:focus': {
textDecoration: 'none',
},
flexGrow: Boolean( grow ) === true ? '1' : undefined,
width: Boolean( full ) === true ? '100%' : undefined,
...sx,
} }
{ ...rest }
{ ...disabledAttributes }
variant={ variant === 'danger' ? 'primary' : variant } // fallback for danger variant used before the prop was added
onClick={ handleOnClick }
className={ classNames( 'vip-button-component', className ) }
data-danger={ danger }
ref={ ref }
/>
);
}
);
Button.displayName = 'Button';
export { Button };