286 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			286 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import React from 'react'
 | 
						|
import { styled } from '../stitches.config'
 | 
						|
import Flex from './Flex'
 | 
						|
import Spinner from './Spinner'
 | 
						|
 | 
						|
export const StyledButton = styled('button', {
 | 
						|
  // Reset
 | 
						|
  all: 'unset',
 | 
						|
  position: 'relative',
 | 
						|
  appereance: 'none',
 | 
						|
  fontFamily: '$body',
 | 
						|
  alignItems: 'center',
 | 
						|
  boxSizing: 'border-box',
 | 
						|
  userSelect: 'none',
 | 
						|
  '&::before': {
 | 
						|
    boxSizing: 'border-box'
 | 
						|
  },
 | 
						|
  '&::after': {
 | 
						|
    boxSizing: 'border-box'
 | 
						|
  },
 | 
						|
  // Custom reset?
 | 
						|
  display: 'inline-flex',
 | 
						|
  flexShrink: 0,
 | 
						|
  justifyContent: 'center',
 | 
						|
  lineHeight: '1',
 | 
						|
  gap: '5px',
 | 
						|
  WebkitTapHighlightColor: 'rgba(0,0,0,0)',
 | 
						|
  // Custom
 | 
						|
  height: '$6',
 | 
						|
  px: '$2',
 | 
						|
  fontSize: '$2',
 | 
						|
  fontWeight: 500,
 | 
						|
  fontVariantNumeric: 'tabular-nums',
 | 
						|
  cursor: 'pointer',
 | 
						|
  width: 'max-content',
 | 
						|
  '&:disabled': {
 | 
						|
    opacity: 0.6,
 | 
						|
    pointerEvents: 'none',
 | 
						|
    cursor: 'not-allowed'
 | 
						|
  },
 | 
						|
  variants: {
 | 
						|
    size: {
 | 
						|
      xs: {
 | 
						|
        borderRadius: '$sm',
 | 
						|
        height: '$5',
 | 
						|
        px: '$2',
 | 
						|
        fontSize: '$xs'
 | 
						|
      },
 | 
						|
      sm: {
 | 
						|
        borderRadius: '$sm',
 | 
						|
        height: '$7',
 | 
						|
        px: '$3',
 | 
						|
        fontSize: '$xs'
 | 
						|
      },
 | 
						|
      md: {
 | 
						|
        borderRadius: '$sm',
 | 
						|
        height: '$8',
 | 
						|
        px: '$3',
 | 
						|
        fontSize: '$xs'
 | 
						|
      },
 | 
						|
      lg: {
 | 
						|
        borderRadius: '$sm',
 | 
						|
        height: '$10',
 | 
						|
        px: '$4',
 | 
						|
        fontSize: '$xs'
 | 
						|
      }
 | 
						|
    },
 | 
						|
    variant: {
 | 
						|
      link: {
 | 
						|
        textDecoration: 'underline',
 | 
						|
        fontSize: 'inherit',
 | 
						|
        color: '$textMuted',
 | 
						|
        textUnderlineOffset: '2px'
 | 
						|
      },
 | 
						|
      default: {
 | 
						|
        backgroundColor: '$mauve12',
 | 
						|
        boxShadow: 'inset 0 0 0 1px $colors$mauve12',
 | 
						|
        color: '$mauve1',
 | 
						|
        '@hover': {
 | 
						|
          '&:hover': {
 | 
						|
            backgroundColor: '$mauve12',
 | 
						|
            boxShadow: 'inset 0 0 0 1px $colors$mauve12'
 | 
						|
          }
 | 
						|
        },
 | 
						|
        '&:active': {
 | 
						|
          backgroundColor: '$mauve10',
 | 
						|
          boxShadow: 'inset 0 0 0 1px $colors$mauve11'
 | 
						|
        },
 | 
						|
        '&:focus': {
 | 
						|
          boxShadow: 'inset 0 0 0 1px $colors$mauve12, inset 0 0 0 2px $colors$mauve12'
 | 
						|
        },
 | 
						|
        '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
 | 
						|
          {
 | 
						|
            backgroundColor: '$mauve4',
 | 
						|
            boxShadow: 'inset 0 0 0 1px $colors$mauve8'
 | 
						|
          }
 | 
						|
      },
 | 
						|
      primary: {
 | 
						|
        backgroundColor: `$accent`,
 | 
						|
        boxShadow: 'inset 0 0 0 1px $colors$purple9',
 | 
						|
        color: '$white',
 | 
						|
        '@hover': {
 | 
						|
          '&:hover': {
 | 
						|
            backgroundColor: '$purple10',
 | 
						|
            boxShadow: 'inset 0 0 0 1px $colors$purple11'
 | 
						|
          }
 | 
						|
        },
 | 
						|
        '&:active': {
 | 
						|
          backgroundColor: '$purple8',
 | 
						|
          boxShadow: 'inset 0 0 0 1px $colors$purple8'
 | 
						|
        },
 | 
						|
        '&:focus': {
 | 
						|
          boxShadow: 'inset 0 0 0 2px $colors$purple12'
 | 
						|
        },
 | 
						|
        '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
 | 
						|
          {
 | 
						|
            backgroundColor: '$mauve4',
 | 
						|
            boxShadow: 'inset 0 0 0 1px $colors$purple8'
 | 
						|
          }
 | 
						|
      },
 | 
						|
      secondary: {
 | 
						|
        backgroundColor: `$purple9`,
 | 
						|
        boxShadow: 'inset 0 0 0 1px $colors$purple9',
 | 
						|
        color: '$white',
 | 
						|
        '@hover': {
 | 
						|
          '&:hover': {
 | 
						|
            backgroundColor: '$purple10',
 | 
						|
            boxShadow: 'inset 0 0 0 1px $colors$purple11'
 | 
						|
          }
 | 
						|
        },
 | 
						|
        '&:active': {
 | 
						|
          backgroundColor: '$purple8',
 | 
						|
          boxShadow: 'inset 0 0 0 1px $colors$purple8'
 | 
						|
        },
 | 
						|
        '&:focus': {
 | 
						|
          boxShadow: 'inset 0 0 0 2px $colors$purple12'
 | 
						|
        },
 | 
						|
        '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
 | 
						|
          {
 | 
						|
            backgroundColor: '$mauve4',
 | 
						|
            boxShadow: 'inset 0 0 0 1px $colors$purple8'
 | 
						|
          }
 | 
						|
      },
 | 
						|
      destroy: {
 | 
						|
        backgroundColor: `$red9`,
 | 
						|
        boxShadow: 'inset 0 0 0 1px $colors$red9',
 | 
						|
        color: '$white',
 | 
						|
        '@hover': {
 | 
						|
          '&:hover': {
 | 
						|
            backgroundColor: '$red10',
 | 
						|
            boxShadow: 'inset 0 0 0 1px $colors$red11'
 | 
						|
          }
 | 
						|
        },
 | 
						|
        '&:active': {
 | 
						|
          backgroundColor: '$red8',
 | 
						|
          boxShadow: 'inset 0 0 0 1px $colors$red8'
 | 
						|
        },
 | 
						|
        '&:focus': {
 | 
						|
          boxShadow: 'inset 0 0 0 2px $colors$red12'
 | 
						|
        },
 | 
						|
        '&[data-radix-popover-trigger][data-state="open"], &[data-radix-dropdown-menu-trigger][data-state="open"]':
 | 
						|
          {
 | 
						|
            backgroundColor: '$mauve4',
 | 
						|
            boxShadow: 'inset 0 0 0 1px $colors$red8'
 | 
						|
          }
 | 
						|
      }
 | 
						|
    },
 | 
						|
    muted: {
 | 
						|
      true: {
 | 
						|
        color: '$textMuted'
 | 
						|
      }
 | 
						|
    },
 | 
						|
    isDisabled: {
 | 
						|
      true: {
 | 
						|
        opacity: 0.6,
 | 
						|
        // pointerEvents: "none",
 | 
						|
        cursor: 'auto',
 | 
						|
        '&:hover': {
 | 
						|
          boxShadow: 'inherit'
 | 
						|
        }
 | 
						|
      }
 | 
						|
    },
 | 
						|
    outline: {
 | 
						|
      true: {
 | 
						|
        backgroundColor: 'transparent'
 | 
						|
      }
 | 
						|
    },
 | 
						|
    uppercase: {
 | 
						|
      true: {
 | 
						|
        textTransform: 'uppercase'
 | 
						|
      }
 | 
						|
    },
 | 
						|
    fullWidth: {
 | 
						|
      true: {
 | 
						|
        width: '100%'
 | 
						|
      }
 | 
						|
    },
 | 
						|
    ghost: {
 | 
						|
      true: {
 | 
						|
        boxShadow: 'none',
 | 
						|
        background: 'transparent',
 | 
						|
        color: '$mauve12',
 | 
						|
        '@hover': {
 | 
						|
          '&:hover': {
 | 
						|
            backgroundColor: '$mauve6',
 | 
						|
            boxShadow: 'none'
 | 
						|
          }
 | 
						|
        },
 | 
						|
        '&:active': {
 | 
						|
          backgroundColor: '$mauve8',
 | 
						|
          boxShadow: 'none'
 | 
						|
        },
 | 
						|
        '&:focus': {
 | 
						|
          boxShadow: 'none'
 | 
						|
        }
 | 
						|
      }
 | 
						|
    },
 | 
						|
    isLoading: {
 | 
						|
      true: {
 | 
						|
        '& .button-content': {
 | 
						|
          visibility: 'hidden'
 | 
						|
        },
 | 
						|
        pointerEvents: 'none'
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
  compoundVariants: [
 | 
						|
    {
 | 
						|
      outline: true,
 | 
						|
      variant: 'default',
 | 
						|
      css: {
 | 
						|
        background: 'transparent',
 | 
						|
        color: '$mauve12',
 | 
						|
        boxShadow: 'inset 0 0 0 1px $colors$mauve10',
 | 
						|
        '&:hover': {
 | 
						|
          color: '$mauve12',
 | 
						|
          background: '$mauve5'
 | 
						|
        }
 | 
						|
      }
 | 
						|
    },
 | 
						|
    {
 | 
						|
      outline: true,
 | 
						|
      variant: 'primary',
 | 
						|
      css: {
 | 
						|
        background: 'transparent',
 | 
						|
        color: '$mauve12',
 | 
						|
        '&:hover': {
 | 
						|
          color: '$mauve12',
 | 
						|
          background: '$mauve5'
 | 
						|
        }
 | 
						|
      }
 | 
						|
    },
 | 
						|
    {
 | 
						|
      outline: true,
 | 
						|
      variant: 'secondary',
 | 
						|
      css: {
 | 
						|
        background: 'transparent',
 | 
						|
        color: '$mauve12',
 | 
						|
        '&:hover': {
 | 
						|
          color: '$mauve12',
 | 
						|
          background: '$mauve5'
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  ],
 | 
						|
  defaultVariants: {
 | 
						|
    size: 'md',
 | 
						|
    variant: 'default'
 | 
						|
  }
 | 
						|
})
 | 
						|
 | 
						|
const CustomButton: React.FC<React.ComponentProps<typeof StyledButton> & { as?: string }> =
 | 
						|
  React.forwardRef(({ children, as = 'button', ...rest }, ref) => (
 | 
						|
    // @ts-expect-error
 | 
						|
    <StyledButton {...rest} ref={ref} as={as}>
 | 
						|
      <Flex as="span" css={{ gap: '$2', alignItems: 'center' }} className="button-content">
 | 
						|
        {children}
 | 
						|
      </Flex>
 | 
						|
      {rest.isLoading && <Spinner css={{ position: 'absolute' }} />}
 | 
						|
    </StyledButton>
 | 
						|
  ))
 | 
						|
 | 
						|
CustomButton.displayName = 'CustomButton'
 | 
						|
 | 
						|
export default CustomButton
 |