chore: button component (#81)

* chore: add button component with storybook

* chore: add stories button

* refactor: remove github custom logo

* fix: fix build

* chore: changes based on PR review
This commit is contained in:
Camila Sosa Morales 2023-01-23 15:01:02 -05:00 committed by GitHub
parent 1dd06c6baf
commit 75a6de5ac7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 903 additions and 185 deletions

View File

@ -13,6 +13,10 @@ export const parameters = {
date: /Date$/,
},
},
darkMode: {
dark: { ...themes.dark, backgroundColor: 'black' },
// light: { ...themes.normal, backgroundColor: 'white' },
},
};
const { darkTheme: darkThemeClassName } = dripStitches;

View File

@ -19,6 +19,7 @@
"@emotion/styled": "^11.10.5",
"@ethersproject/providers": "^5.7.2",
"@radix-ui/colors": "^0.1.8",
"@react-icons/all-files": "^4.1.0",
"@reduxjs/toolkit": "^1.9.1",
"@stitches/react": "^1.2.8",
"formik": "^2.2.9",
@ -57,7 +58,6 @@
"ethers": "^5.7.2",
"postcss": "^8.4.21",
"prettier": "^2.8.0",
"react-icons": "^4.7.1",
"react-query": "^3.39.2",
"storybook-dark-mode": "^2.0.5",
"tailwindcss": "^3.2.4",

View File

@ -0,0 +1,12 @@
import { dripStitches } from '../../../theme/stitches'; //TODO replace with absolute path
import { Flex, Grid } from '../../layout';
const { styled } = dripStitches;
export const StyledButtonContentGrid = styled(Grid, {
gap: '$0h',
});
export const StyledButtonContentFlex = styled(Flex, {
alignItems: 'center',
});

View File

@ -0,0 +1,57 @@
import React from 'react';
import { ButtonProps } from '.';
import {
StyledButtonContentFlex,
StyledButtonContentGrid,
} from './button-content.styled';
import { ButtonIcon } from './button-icon';
export type ButtonContentProps = Pick<
ButtonProps,
| 'leftIcon'
| 'rightIcon'
| 'topIcon'
| 'bottomIcon'
| 'children'
| 'iconSpacing'
>;
export const ButtonContent: React.FC<ButtonContentProps> = (props) => {
const {
leftIcon,
rightIcon,
topIcon,
bottomIcon,
children,
iconSpacing = '1h',
} = props;
const midNode = (
<>
{leftIcon && (
<ButtonIcon css={{ marginRight: `$${iconSpacing}` }}>
{leftIcon}
</ButtonIcon>
)}
{children}
{rightIcon && (
<ButtonIcon css={{ marginLeft: `$${iconSpacing}` }}>
{rightIcon}
</ButtonIcon>
)}
</>
);
if (!topIcon && !bottomIcon) {
return midNode;
}
return (
<StyledButtonContentGrid>
{topIcon && <ButtonIcon>{topIcon}</ButtonIcon>}
<StyledButtonContentFlex>{midNode}</StyledButtonContentFlex>
{bottomIcon && <ButtonIcon>{bottomIcon}</ButtonIcon>}
</StyledButtonContentGrid>
);
};

View File

@ -0,0 +1,17 @@
import { dripStitches } from '../../../theme/stitches'; //TODO replace with absolute path
const { styled } = dripStitches;
export const StyledButtonIconSpan = styled('span', {
all: 'unset',
display: 'inline-flex',
alignSelf: 'center',
alignItems: 'center',
justifyContent: 'center',
lineHeight: 1.3,
flexShrink: 0,
});
export type ButtonIconSpanProps = React.ComponentProps<
typeof StyledButtonIconSpan
>;

View File

@ -0,0 +1,23 @@
import * as React from 'react';
import {
ButtonIconSpanProps,
StyledButtonIconSpan,
} from './button-icon.styled';
export const ButtonIcon: React.FC<ButtonIconSpanProps> = (props) => {
const { children, className, ...rest } = props;
const _children = React.isValidElement(children)
? React.cloneElement(children, {
'aria-hidden': true,
focusable: false,
})
: children;
return (
<StyledButtonIconSpan {...rest} className={className}>
{_children}
</StyledButtonIconSpan>
);
};

View File

@ -0,0 +1,33 @@
import { dripStitches } from '../../../theme/stitches'; //TODO replace with absolute path
import { Flex, Grid } from '../../layout';
const { styled } = dripStitches;
export const StyledButtonSpinnerBox = styled(Flex, {
alignItems: 'center',
fontSize: '$md',
lineHeight: 'normal',
});
export const StyledButtonSpinnerDotsBox = styled(Grid, {
gap: '$1',
gridAutoFlow: 'column',
width: '100%',
height: '100%',
});
export const StyledButtonSpinnerDot = styled('div', {
all: 'unset',
width: '$2',
height: '$2',
borderRadius: '$full',
backgroundColor: '$slate1',
opacity: 0.2,
});
export interface ButtonSpinnerProps
extends React.HTMLAttributes<HTMLDivElement> {
label?: string;
spacing?: string;
placement?: 'start' | 'end';
}

View File

@ -0,0 +1,69 @@
import { dripStitches } from '../../../theme/stitches'; //TODO replace with absolute path
import React, { HTMLAttributes, useMemo } from 'react';
import {
ButtonSpinnerProps,
StyledButtonSpinnerBox,
StyledButtonSpinnerDot,
StyledButtonSpinnerDotsBox,
} from './button-spinner.styled';
export const ButtonSpinner: React.FC<ButtonSpinnerProps> = (props) => {
const {
label,
placement,
spacing = '$2',
children,
className,
...rest
} = props;
const marginProp = placement === 'start' ? 'marginRight' : 'marginLeft';
const spinnerStyles = useMemo(
() => ({
position: label ? 'relative' : 'absolute',
[marginProp]: label ? spacing : 0,
}),
[label, marginProp, spacing]
);
return (
<StyledButtonSpinnerBox className={className} css={spinnerStyles} {...rest}>
{children || <ButtonSpinnerDots />}
</StyledButtonSpinnerBox>
);
};
const { keyframes } = dripStitches;
const blink = keyframes({
'0%': { opacity: 0.2 },
'50%': { opacity: 1 },
'100%': { opacity: 0.2 },
});
const ButtonSpinnerDots: React.FC<HTMLAttributes<HTMLDivElement>> = (props) => {
return (
<StyledButtonSpinnerDotsBox {...props}>
<StyledButtonSpinnerDot
css={{
animation: `${blink} infinite linear 2s`,
animationDelay: '0ms',
}}
/>
<StyledButtonSpinnerDot
css={{
animation: `${blink} infinite linear 2s`,
animationDelay: '250ms',
}}
/>
<StyledButtonSpinnerDot
css={{
animation: `${blink} infinite linear 2s`,
animationDelay: '500ms',
}}
/>
</StyledButtonSpinnerDotsBox>
);
};

View File

@ -0,0 +1,94 @@
import { Flex } from '../../layout';
import { Button } from './button';
import { IconButton } from './icon-button';
import { Icon as IconComponent } from '../icon';
import { dripStitches } from '../../../theme/stitches';
export default {
title: 'Components/Button',
component: Button,
};
const { styled } = dripStitches;
const StoryFlex = styled(Flex, {
display: 'flex',
gap: '$2',
flexWrap: 'wrap',
});
export const Default = () => (
<StoryFlex>
<Button colorScheme="blue">Primary</Button>
<Button>Default</Button>
<Button
colorScheme="blue"
variant="outline"
css={{ py: '$1', borderRadius: '$md' }}
>
Use for NFA
</Button>
<Button
colorScheme="gray"
variant="outline"
css={{ py: '$1', borderRadius: '$md' }}
>
NFA Repo
</Button>
</StoryFlex>
);
export const Icon = () => (
<>
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<IconComponent name="info" />}
/>
<IconButton
aria-label="Add"
colorScheme="gray"
variant="link"
icon={<IconComponent name="back" />}
/>
</>
);
export const ConnectorButtons = () => (
<StoryFlex>
<Button
size="lg"
iconSpacing="40"
variant="ghost"
rightIcon={<IconComponent name="github" css={{ color: 'white' }} />}
>
GitHub
</Button>
<Button
disabled
size="lg"
iconSpacing="40"
variant="ghost"
rightIcon={<IconComponent name="github" css={{ color: 'white' }} />}
>
GitHub
</Button>
<Button
size="lg"
iconSpacing="40"
variant="ghost"
rightIcon={<IconComponent name="ethereum" />}
>
Connect Ethereum Wallet
</Button>
<Button
disabled
size="lg"
iconSpacing="40"
variant="ghost"
rightIcon={<IconComponent name="ethereum" />}
>
Connect Ethereum Wallet
</Button>
</StoryFlex>
);

View File

@ -0,0 +1,249 @@
import { dripStitches } from '../../../theme/stitches';
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',
},
});

View File

@ -0,0 +1,75 @@
import { ButtonProps, StyledButton } from './button.styled';
import { ButtonContent } from './button-content';
import { ButtonSpinner } from './button-spinner';
import { forwardRef } from 'react';
export const Button = forwardRef<ButtonProps, 'button'>((props, ref) => {
const {
isActive,
isLoading,
isDisabled,
spinnerPlacement = 'start',
spinner,
loadingText,
iconSpacing,
topIcon,
bottomIcon,
rightIcon,
leftIcon,
isFullWidth,
children,
...ownProps
} = props;
const contentProps = {
rightIcon,
leftIcon,
bottomIcon,
topIcon,
iconSpacing,
children,
};
return (
<StyledButton
ref={ref}
disabled={isDisabled || isLoading}
data-active={isActive}
data-loading={isLoading}
css={{
width: isFullWidth ? '100%' : undefined,
}}
{...ownProps}
>
{isLoading && spinnerPlacement === 'start' && (
<ButtonSpinner
label={loadingText}
placement={spinnerPlacement}
spacing={iconSpacing}
>
{spinner}
</ButtonSpinner>
)}
{isLoading ? (
loadingText || (
<span style={{ opacity: 0 }}>
<ButtonContent {...contentProps} />
</span>
)
) : (
<ButtonContent {...contentProps} />
)}
{isLoading && spinnerPlacement === 'end' && (
<ButtonSpinner
label={loadingText}
placement={spinnerPlacement}
spacing={iconSpacing}
>
{spinner}
</ButtonSpinner>
)}
</StyledButton>
);
});

View File

@ -0,0 +1,91 @@
import React, { forwardRef, useMemo } from 'react';
import { Button } from './button';
import { ButtonProps } from './button.styled';
type OmittedProps =
| 'leftIcon'
| 'isFullWidth'
| 'rightIcon'
| 'loadingText'
| 'iconSpacing'
| 'spinnerPlacement';
type BaseButtonProps = Omit<ButtonProps, OmittedProps>;
export interface IconButtonProps extends BaseButtonProps {
/**
* The icon to be used in the button.
* @type React.ReactElement
*/
icon?: React.ReactElement;
/**
* If `true`, the button will be perfectly round. Else, it'll be slightly round
*/
isRound?: boolean;
/**
* A11y: A label that describes the button
*/
'aria-label': string;
}
export const IconButton = forwardRef<IconButtonProps, 'button'>(
function IconButton(props, ref) {
const {
icon,
children,
isRound,
'aria-label': ariaLabel,
size = 'md',
...rest
} = props;
/**
* Passing the icon as prop or children should work
*/
const element = icon || children;
const _children = React.isValidElement(element)
? React.cloneElement(element, {
'aria-hidden': true,
focusable: false,
})
: null;
const { minWidth, fontSize } = useMemo(() => {
const props = {
sm: {
minWidth: '$8',
fontSize: '$lg',
},
md: {
minWidth: '$11',
fontSize: '$xl',
},
lg: {
minWidth: '$14',
fontSize: '$2xl',
},
};
return props[size as 'sm' | 'md' | 'lg'];
}, [size]);
return (
<Button
ref={ref}
aria-label={ariaLabel}
size={size}
{...rest}
css={{
padding: 0,
minWidth,
fontSize,
borderRadius: isRound ? '$full' : undefined,
...(rest.css ?? {}),
}}
>
{_children}
</Button>
);
}
);

View File

@ -0,0 +1,2 @@
export * from './button.styled';
export * from './button';

View File

@ -0,0 +1,29 @@
import { IconStyles as IS } from '../icon.styles';
export const EthereumIcon: React.FC<IS.CustomProps> = (props) => (
<IS.Custom
{...props}
viewBox="0 0 22 36"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.9728 0L10.7329 0.820393V24.6242L10.9728 24.8651L21.9453 18.3339L10.9728 0Z"
fill="#343434"
/>
<path d="M10.9727 0L0 18.3339L10.9727 24.8651V13.3114V0Z" fill="#8C8C8C" />
<path
d="M10.9727 26.9571L10.8376 27.1231V35.6024L10.9727 35.9997L21.952 20.4292L10.9727 26.9571Z"
fill="#3C3C3B"
/>
<path
d="M10.9727 35.9997V26.9571L0 20.4292L10.9727 35.9997Z"
fill="#8C8C8C"
/>
<path
d="M10.9727 24.8652L21.9452 18.3339L10.9727 13.3115V24.8652Z"
fill="#141414"
/>
<path d="M0 18.3339L10.9727 24.8652V13.3115L0 18.3339Z" fill="#393939" />
</IS.Custom>
);

View File

@ -0,0 +1,2 @@
export * from './metamask-icon';
export * from './ethereum-icon';

View File

@ -1,16 +1,12 @@
import { Icon, IconProps } from '@chakra-ui/react';
import { IconStyles as IS } from '../icon.styles';
export const MetamaskIcon: React.FC<IconProps> = (props) => {
const { width = '1.5em', height = '1.5em' } = props;
export const MetamaskIcon: React.FC<IS.CustomProps> = (props) => {
return (
<Icon
width={width}
height={height}
<IS.Custom
{...props}
viewBox="0 0 256 240"
version="1.1"
fill="none"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
>
<title>MetaMask</title>
<g>
@ -131,6 +127,6 @@ export const MetamaskIcon: React.FC<IconProps> = (props) => {
points="163.383898 33.1117385 147.744691 75.3505047 144.425852 132.411352 143.155934 150.295986 143.055195 195.983514 112.943788 195.983514 112.846176 150.381702 111.572114 132.395585 108.251786 75.3505047 92.6150854 33.1117385"
></polygon>
</g>
</Icon>
</IS.Custom>
);
};

View File

@ -0,0 +1,16 @@
import { IoLogoGithub } from '@react-icons/all-files/io5/IoLogoGithub';
import { IoArrowBackCircleSharp } from '@react-icons/all-files/io5/IoArrowBackCircleSharp';
import { IoInformationCircleSharp } from '@react-icons/all-files/io5/IoInformationCircleSharp';
import { MetamaskIcon, EthereumIcon } from './custom';
export const IconLibrary = Object.freeze({
back: IoArrowBackCircleSharp,
ethereum: EthereumIcon,
github: IoLogoGithub,
info: IoInformationCircleSharp,
metamask: MetamaskIcon, //remove if not used
});
export type IconName = keyof typeof IconLibrary;
export type IconType<Name extends IconName> = typeof IconLibrary[Name];

View File

@ -0,0 +1,23 @@
import { Icon } from './icon';
import { styled } from '@stitches/react';
import { Flex } from '../../layout';
export default {
title: 'Components/Icons',
component: Icon,
};
const StoryFlex = styled(Flex, {
display: 'flex',
gap: '$2',
flexWrap: 'wrap',
color: 'white',
});
export const ConnectorIcons = () => (
<StoryFlex>
<Icon name="metamask" />
<Icon name="github" />
<Icon name="ethereum" />
</StoryFlex>
);

View File

@ -0,0 +1,42 @@
import { dripStitches } from '../../../theme/stitches'; //TODO replace for absolute path
const { styled } = dripStitches;
export abstract class IconStyles {
static readonly Container = styled('span', {
transition: 'transform $default',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
variants: {
size: {
sm: {
fontSize: '$md',
},
md: {
fontSize: '$2xl',
},
lg: {
fontSize: '$3xl',
},
},
rotate: {
true: {
transform: 'rotate(-180deg)',
},
},
},
defaultVariants: {
rotate: false,
},
});
static readonly Custom = styled('svg');
}
export namespace IconStyles {
export type CustomProps = React.ComponentProps<typeof IconStyles.Custom>;
}

View File

@ -0,0 +1,22 @@
import { forwardRef } from 'react';
import { IconStyles } from './icon.styles';
import { IconLibrary, IconName, IconType } from './icon-library';
export type IconProps = {
name: IconName;
} & React.ComponentProps<typeof IconStyles.Container>;
export const Icon: React.FC<IconProps> = forwardRef<HTMLSpanElement, IconProps>(
(props, ref) => {
const { name, ...rest } = props;
const IconElement: IconType<typeof name> = IconLibrary[name];
return (
<IconStyles.Container {...rest} ref={ref}>
<IconElement style={{ width: '1em', height: '1em' }} />
</IconStyles.Container>
);
}
);
Icon.displayName = 'Icon';

View File

@ -0,0 +1,2 @@
export * from './icon';
export * from './icon-library';

View File

@ -0,0 +1 @@
export * from './button';

View File

@ -1 +0,0 @@
export * from './metamask-icon';

View File

@ -1,15 +0,0 @@
import { FaWallet } from 'react-icons/fa';
import { IoExitOutline } from 'react-icons/io5';
import { AiOutlineCopy } from 'react-icons/ai';
import { MetamaskIcon } from './custom';
export const IconLibrary = Object.freeze({
copy: AiOutlineCopy,
'log-out': IoExitOutline,
metamask: MetamaskIcon,
wallet: FaWallet,
});
export type IconName = keyof typeof IconLibrary;
export type IconType<Name extends IconName> = typeof IconLibrary[Name];

View File

@ -1,16 +0,0 @@
import {
forwardRef,
IconProps as IconPropsChakra,
Icon as IconChakra,
} from '@chakra-ui/react';
import { IconLibrary, IconName, IconType } from './icon-types';
export type IconComponentProps = IconPropsChakra & { name: IconName };
export const Icon = forwardRef<IconComponentProps, 'svg'>(
({ name, ...iconProps }, ref) => {
const IconElement: IconType<typeof name> = IconLibrary[name];
return <IconChakra as={IconElement} {...iconProps} ref={ref} />;
}
);

View File

@ -1 +0,0 @@
export * from './icon';

View File

@ -0,0 +1,10 @@
import { dripStitches } from '../../theme/stitches'; //TODO replace with absolute path
import React from 'react';
const { styled } = dripStitches;
export const Flex = styled('div', {
display: 'flex',
});
export type FlexProps = React.ComponentProps<typeof Flex>;

View File

@ -0,0 +1,9 @@
import { dripStitches } from '../../theme/stitches'; //TODO replace with absolute path
const { styled } = dripStitches;
export const Grid = styled('div', {
display: 'grid',
});
export type GridProps = React.ComponentProps<typeof Grid>;

View File

@ -0,0 +1,2 @@
export * from './grid.styled';
export * from './flex.styled';

View File

@ -9,7 +9,7 @@ import {
Button,
Flex,
} from '@chakra-ui/react';
import { Icon } from '../icon';
import { Icon } from '../core/icon';
import { WalletType } from './wallet.utils';
const WalletMenu: React.FC = () => {
@ -41,7 +41,6 @@ const WalletMenu: React.FC = () => {
_hover={{ bg: 'custom.gray.100' }}
bg={'custom.gray.200'}
onClick={handleCopyAccount}
icon={<Icon name="copy" />}
>
Copy Account
</MenuItem>
@ -49,7 +48,6 @@ const WalletMenu: React.FC = () => {
_hover={{ bg: 'custom.gray.100' }}
bg={'custom.gray.200'}
onClick={handleDisconnect}
icon={<Icon name="log-out" />}
>
Disconnect
</MenuItem>
@ -72,7 +70,6 @@ const ConnectionMenu: React.FC = () => {
<Button
borderRadius="50px"
as={MenuButton}
leftIcon={<Icon name="wallet" />}
isLoading={state === 'loading'}
disabled={state === 'loading'}
>
@ -83,7 +80,6 @@ const ConnectionMenu: React.FC = () => {
_hover={{ bg: 'custom.gray.100' }}
bg={'custom.gray.200'}
onClick={handleConnectWallet}
icon={<Icon name={WalletType.metamask} />}
>
Metamask
</MenuItem>

View File

@ -1,8 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import { App } from './app';
import { ChakraProvider } from '@chakra-ui/react';
import { theme } from './theme';
import { Provider as ReduxProvider } from 'react-redux';
import { store } from './store';
import { QueryClient, QueryClientProvider } from 'react-query';
@ -19,11 +17,9 @@ const root = ReactDOM.createRoot(
root.render(
<React.StrictMode>
<ReduxProvider store={store}>
<ChakraProvider theme={theme} resetCSS>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</ChakraProvider>
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
</ReduxProvider>
</React.StrictMode>
);

View File

@ -1,43 +0,0 @@
import React from 'react';
import { ComponentStory, ComponentMeta } from '@storybook/react';
import { Button } from './Button';
//TODO: remove example stories
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
export default {
title: 'Example/Button',
component: Button,
// More on argTypes: https://storybook.js.org/docs/react/api/argtypes
argTypes: {
backgroundColor: { control: 'color' },
},
} as ComponentMeta<typeof Button>;
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
const Template: ComponentStory<typeof Button> = (args) => <Button {...args} />;
export const Primary = Template.bind({});
// More on args: https://storybook.js.org/docs/react/writing-stories/args
Primary.args = {
primary: true,
label: 'Button',
};
export const Secondary = Template.bind({});
Secondary.args = {
label: 'Button',
};
export const Large = Template.bind({});
Large.args = {
size: 'large',
label: 'Button',
};
export const Small = Template.bind({});
Small.args = {
size: 'small',
label: 'Button',
};

View File

@ -1,52 +0,0 @@
import React from 'react';
import './button.css';
interface ButtonProps {
/**
* Is this the principal call to action on the page?
*/
primary?: boolean;
/**
* What background color to use
*/
backgroundColor?: string;
/**
* How large should the button be?
*/
size?: 'small' | 'medium' | 'large';
/**
* Button contents
*/
label: string;
/**
* Optional click handler
*/
onClick?: () => void;
}
/**
* Primary UI component for user interaction
*/
export const Button = ({
primary = false,
size = 'medium',
backgroundColor,
label,
...props
}: ButtonProps) => {
const mode = primary
? 'storybook-button--primary'
: 'storybook-button--secondary';
return (
<button
type="button"
className={['storybook-button', `storybook-button--${size}`, mode].join(
' '
)}
style={{ backgroundColor }}
{...props}
>
{label}
</button>
);
};

View File

@ -1,30 +0,0 @@
.storybook-button {
font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: 700;
border: 0;
border-radius: 3em;
cursor: pointer;
display: inline-block;
line-height: 1;
}
.storybook-button--primary {
color: white;
background-color: #1ea7fd;
}
.storybook-button--secondary {
color: #333;
background-color: transparent;
box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset;
}
.storybook-button--small {
font-size: 12px;
padding: 10px 16px;
}
.storybook-button--medium {
font-size: 14px;
padding: 11px 20px;
}
.storybook-button--large {
font-size: 16px;
padding: 12px 24px;
}

View File

@ -101,6 +101,10 @@ export const createDripStitches = <
...zIndices,
...(theme?.zIndices || {}),
},
transitions: {
'all-200': 'all 200ms',
...(theme?.transitions || {}),
},
...(theme || {}),
},
themeMap,

View File

@ -2933,6 +2933,11 @@
resolved "https://registry.yarnpkg.com/@radix-ui/colors/-/colors-0.1.8.tgz#b08c62536fc462a87632165fb28e9b18f9bd047e"
integrity sha512-jwRMXYwC0hUo0mv6wGpuw254Pd9p/R6Td5xsRpOmaWkUHlooNWqVcadgyzlRumMq3xfOTXwJReU0Jv+EIy4Jbw==
"@react-icons/all-files@^4.1.0":
version "4.1.0"
resolved "https://registry.yarnpkg.com/@react-icons/all-files/-/all-files-4.1.0.tgz#477284873a0821928224b6fc84c62d2534d6650b"
integrity sha512-hxBI2UOuVaI3O/BhQfhtb4kcGn9ft12RWAFVMUeNjqqhLsHvFtzIkFaptBJpFDANTKoDfdVoHTKZDlwKCACbMQ==
"@reduxjs/toolkit@^1.9.1":
version "1.9.1"
resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-1.9.1.tgz#4c34dc4ddcec161535288c60da5c19c3ef15180e"
@ -10808,11 +10813,6 @@ react-focus-lock@^2.9.1:
use-callback-ref "^1.3.0"
use-sidecar "^1.1.2"
react-icons@^4.7.1:
version "4.7.1"
resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.7.1.tgz#0f4b25a5694e6972677cb189d2a72eabea7a8345"
integrity sha512-yHd3oKGMgm7zxo3EA7H2n7vxSoiGmHk5t6Ou4bXsfcgWyhfDKMpyKfhHR6Bjnn63c+YXBLBPUql9H4wPJM6sXw==
react-inspector@^5.1.0:
version "5.1.1"
resolved "https://registry.yarnpkg.com/react-inspector/-/react-inspector-5.1.1.tgz#58476c78fde05d5055646ed8ec02030af42953c8"