dydx-v4-web/src/components/Button.tsx
James Jia - Test 4b86068d8f
Initial commit
2023-09-08 13:52:13 -07:00

159 lines
4.4 KiB
TypeScript

import { forwardRef } from 'react';
import styled, {
css,
type FlattenInterpolation,
type FlattenSimpleInterpolation,
type ThemeProps,
} from 'styled-components';
import { ButtonAction, ButtonShape, ButtonSize, ButtonState } from '@/constants/buttons';
import { LoadingDots } from '@/components/Loading/LoadingDots';
import { BaseButton, BaseButtonProps } from './BaseButton';
export type ButtonStateConfig = {
isDisabled?: boolean;
isLoading?: boolean;
};
type ElementProps = {
children?: React.ReactNode;
href?: string;
onClick?: React.MouseEventHandler<HTMLButtonElement> | React.MouseEventHandler<HTMLAnchorElement>;
slotLeft?: React.ReactNode;
slotRight?: React.ReactNode;
state?: ButtonState | ButtonStateConfig;
};
type StyleProps = {
action?: ButtonAction;
state: Record<string, boolean | undefined>;
className?: string;
};
export type ButtonProps = BaseButtonProps & ElementProps & Omit<StyleProps, keyof ElementProps>;
export const Button = forwardRef<HTMLButtonElement | HTMLAnchorElement, ButtonProps>(
(
{
action = ButtonAction.Secondary,
size = ButtonSize.Base,
shape = ButtonShape.Rectangle,
state: stateConfig = ButtonState.Default,
children,
slotLeft = null,
slotRight = null,
...otherProps
},
ref
) => {
const state: Record<string, boolean | undefined> =
typeof stateConfig === 'string'
? { [stateConfig as ButtonState]: true }
: {
[ButtonState.Loading]: stateConfig.isLoading,
[ButtonState.Disabled]: stateConfig.isDisabled,
};
return (
<StyledBaseButton
disabled={state[ButtonState.Disabled]}
{...{ ref, action, size, shape, state, ...otherProps }}
>
{
<>
{state[ButtonState.Loading] ? (
<LoadingDots size={3} />
) : (
<>
{slotLeft}
{children}
{slotRight}
</>
)}
</>
}
</StyledBaseButton>
);
}
);
const buttonActionVariants = {
[ButtonAction.Base]: css`
--button-textColor: var(--color-text-1);
--button-backgroundColor: var(--color-layer-5);
--button-border: solid var(--border-width) var(--color-border);
`,
[ButtonAction.Primary]: css`
--button-textColor: var(--color-text-2);
--button-backgroundColor: var(--color-accent);
--button-border: solid var(--border-width) var(--color-border-white);
`,
[ButtonAction.Secondary]: css`
--button-textColor: var(--color-text-1);
--button-backgroundColor: var(--color-layer-3);
--button-border: solid var(--border-width) var(--color-border);
`,
[ButtonAction.Create]: css`
--button-textColor: var(--color-text-2);
--button-backgroundColor: var(--color-positive);
--button-border: solid var(--border-width) var(--color-border-white);
`,
[ButtonAction.Destroy]: css`
--button-textColor: var(--color-text-2);
--button-backgroundColor: var(--color-negative);
--button-border: solid var(--border-width) var(--color-border-white);
`,
[ButtonAction.Navigation]: css`
--button-textColor: var(--color-text-1);
--button-backgroundColor: transparent;
--button-border: none;
`,
[ButtonAction.Reset]: css`
--button-textColor: var(--color-negative);
--button-backgroundColor: var(--color-layer-3);
--button-border: solid var(--border-width) var(--color-border-red);
`,
};
const buttonStateVariants: Record<
ButtonState,
FlattenSimpleInterpolation | FlattenInterpolation<ThemeProps<any>>
> = {
[ButtonState.Default]: css``,
[ButtonState.Disabled]: css`
--button-textColor: var(--color-text-0);
--button-backgroundColor: var(--color-layer-2);
--button-border: solid var(--border-width) var(--color-layer-6);
--button-hover-filter: none;
--button-cursor: not-allowed;
`,
[ButtonState.Loading]: css`
${() => buttonStateVariants[ButtonState.Disabled]}
min-width: 4em;
`,
};
const StyledBaseButton = styled(BaseButton)<StyleProps>`
${({ action }) => action && buttonActionVariants[action]}
${({ state }) =>
state &&
css`
// Ordered from lowest to highest priority (ie. Disabled should overwrite Active and Loading states)
${state[ButtonState.Loading] && buttonStateVariants[ButtonState.Loading]}
${state[ButtonState.Disabled] && buttonStateVariants[ButtonState.Disabled]}
`}
`;