250 lines
6.0 KiB
TypeScript
250 lines
6.0 KiB
TypeScript
import { dripStitches } from '../../../theme';
|
|
import { CSS } from '@stitches/react';
|
|
|
|
type StyledButtonProps = React.ComponentProps<typeof StyledButton>;
|
|
export interface ButtonProps extends StyledButtonProps {
|
|
/**
|
|
* If `true`, the button will show a spinner.
|
|
*/
|
|
isLoading?: boolean;
|
|
/**
|
|
* If `true`, the button will be styled in its active state.
|
|
*/
|
|
isActive?: boolean;
|
|
/**
|
|
* If `true`, the button will be disabled.
|
|
*/
|
|
isDisabled?: boolean;
|
|
/**
|
|
* The label to show in the button when `isLoading` is true
|
|
* If no text is passed, it only shows the spinner
|
|
*/
|
|
loadingText?: string;
|
|
/**
|
|
* If `true`, the button will take up the full width of its container.
|
|
*/
|
|
isFullWidth?: boolean;
|
|
/**
|
|
* The html button type to use.
|
|
*/
|
|
type?: 'button' | 'reset' | 'submit';
|
|
/**
|
|
* If added, the button will show an icon before the button's label.
|
|
* @type React.ReactElement
|
|
*/
|
|
leftIcon?: React.ReactElement;
|
|
/**
|
|
* If added, the button will show an icon after the button's label.
|
|
* @type React.ReactElement
|
|
*/
|
|
rightIcon?: React.ReactElement;
|
|
/**
|
|
* If added, the button will show an icon on top side from the button's label.
|
|
* @type React.ReactElement
|
|
*/
|
|
topIcon?: React.ReactElement;
|
|
/**
|
|
* If added, the button will show an icon on bottom side from the button's label.
|
|
* @type React.ReactElement
|
|
*/
|
|
bottomIcon?: React.ReactElement;
|
|
/**
|
|
* The space between the button icon and label.
|
|
* @type SystemProps["marginRight"]
|
|
*/
|
|
iconSpacing?: string;
|
|
/**
|
|
* Replace the spinner component when `isLoading` is set to `true`
|
|
* @type React.ReactElement
|
|
*/
|
|
spinner?: React.ReactElement;
|
|
/**
|
|
* It determines the placement of the spinner when isLoading is true
|
|
* @default "start"
|
|
*/
|
|
spinnerPlacement?: 'start' | 'end';
|
|
}
|
|
|
|
export type ButtonColor = 'gray' | 'blue';
|
|
export type ButtonVariant = 'solid' | 'outline' | 'ghost' | 'link';
|
|
|
|
export type GetButtonCompoundVariantOptions = {
|
|
color?: ButtonColor;
|
|
variant?: ButtonVariant;
|
|
};
|
|
|
|
const getButtonCompoundVariant = ({
|
|
color = 'gray',
|
|
variant = 'solid',
|
|
}: GetButtonCompoundVariantOptions): CSS => {
|
|
switch (variant) {
|
|
case 'solid':
|
|
return {
|
|
color: 'white',
|
|
transition: '$all-200',
|
|
backgroundColor: `$${color}9`,
|
|
'&:focus, &:hover': {
|
|
backgroundColor: `$${color}10`,
|
|
},
|
|
'&:focus, &:active': {
|
|
backgroundColor: `$${color}11`,
|
|
},
|
|
};
|
|
case 'outline':
|
|
return {
|
|
color: `$${color}11`,
|
|
transition: '$all-200',
|
|
backgroundColor: `$${color}4`,
|
|
'&:hover': {
|
|
backgroundColor: `$${color}5`,
|
|
},
|
|
'&:focus, &:active': {
|
|
backgroundColor: `$${color}6`,
|
|
},
|
|
};
|
|
case 'link':
|
|
return {
|
|
color: `$${color}11`,
|
|
transition: '$all-200',
|
|
height: 'auto',
|
|
px: '0',
|
|
'&:hover, &:focus': {
|
|
textDecoration: 'underline',
|
|
color: `$${color}12`,
|
|
'&:disabled': {
|
|
textDecoration: 'none',
|
|
},
|
|
},
|
|
};
|
|
case 'ghost':
|
|
return {
|
|
color: `$slate11`,
|
|
transition: '$all-200',
|
|
|
|
'&:hover, &:focus, &:active': {
|
|
color: `$slate12`,
|
|
},
|
|
'&:hover': {
|
|
backgroundColor: `$${color}4`,
|
|
},
|
|
'&:focus, &:active': {
|
|
backgroundColor: `$${color}3`,
|
|
},
|
|
|
|
'&:disabled': {
|
|
backgroundColor: `initial`,
|
|
'&:hover': {
|
|
color: `$${color}11`,
|
|
backgroundColor: `initial`,
|
|
},
|
|
'& img, & svg': {
|
|
filter: 'grayscale(100%)',
|
|
},
|
|
},
|
|
};
|
|
|
|
default:
|
|
return {};
|
|
}
|
|
};
|
|
|
|
const { styled } = dripStitches;
|
|
|
|
export const StyledButton = styled('button', {
|
|
all: 'unset',
|
|
cursor: 'pointer',
|
|
position: 'relative',
|
|
display: 'inline-flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
whiteSpace: 'nowrap',
|
|
verticalAlign: 'middle',
|
|
userSelect: 'none',
|
|
fontWeight: '$medium',
|
|
'&:disabled': {
|
|
cursor: 'not-allowed',
|
|
opacity: '0.4',
|
|
},
|
|
|
|
variants: {
|
|
size: {
|
|
sm: {
|
|
borderRadius: '$md',
|
|
fontSize: '$xs',
|
|
p: '$1 $3',
|
|
},
|
|
md: {
|
|
borderRadius: '$lg',
|
|
fontSize: '$sm',
|
|
p: '$3 $3h',
|
|
},
|
|
lg: {
|
|
borderRadius: '$xl',
|
|
fontSize: '$md',
|
|
p: '$4 $5',
|
|
},
|
|
},
|
|
variant: {
|
|
solid: {
|
|
color: '$gray1',
|
|
},
|
|
ghost: {},
|
|
outline: {},
|
|
link: {},
|
|
unstyled: {},
|
|
},
|
|
colorScheme: {
|
|
gray: {},
|
|
blue: {},
|
|
red: {},
|
|
},
|
|
},
|
|
compoundVariants: [
|
|
{
|
|
colorScheme: 'gray',
|
|
variant: 'solid',
|
|
css: getButtonCompoundVariant({ color: 'gray', variant: 'solid' }),
|
|
},
|
|
{
|
|
colorScheme: 'blue',
|
|
variant: 'solid',
|
|
css: getButtonCompoundVariant({ color: 'blue', variant: 'solid' }),
|
|
},
|
|
{
|
|
colorScheme: 'gray',
|
|
variant: 'outline',
|
|
css: getButtonCompoundVariant({ color: 'gray', variant: 'outline' }),
|
|
},
|
|
{
|
|
colorScheme: 'blue',
|
|
variant: 'outline',
|
|
css: getButtonCompoundVariant({ color: 'blue', variant: 'outline' }),
|
|
},
|
|
{
|
|
colorScheme: 'gray',
|
|
variant: 'ghost',
|
|
css: getButtonCompoundVariant({ color: 'gray', variant: 'ghost' }),
|
|
},
|
|
{
|
|
colorScheme: 'blue',
|
|
variant: 'ghost',
|
|
css: getButtonCompoundVariant({ color: 'blue', variant: 'ghost' }),
|
|
},
|
|
{
|
|
colorScheme: 'gray',
|
|
variant: 'link',
|
|
css: getButtonCompoundVariant({ color: 'gray', variant: 'link' }),
|
|
},
|
|
{
|
|
colorScheme: 'blue',
|
|
variant: 'link',
|
|
css: getButtonCompoundVariant({ color: 'blue', variant: 'link' }),
|
|
},
|
|
],
|
|
defaultVariants: {
|
|
colorScheme: 'gray',
|
|
variant: 'solid',
|
|
size: 'md',
|
|
},
|
|
});
|