feat: add button component

This commit is contained in:
QiShaoXuan
2022-12-09 18:39:50 +08:00
parent 101847503c
commit 57f19ee4a7
9 changed files with 260 additions and 26 deletions
+60
View File
@@ -0,0 +1,60 @@
import {
HTMLAttributes,
cloneElement,
ReactElement,
Children,
CSSProperties,
forwardRef,
PropsWithChildren,
} from 'react';
import { StyledButton } from './styles';
import { ButtonProps } from './interface';
import { getSize } from './utils';
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(
{
size = 'default',
disabled = false,
hoverBackground,
hoverColor,
hoverStyle,
shape = 'default',
icon,
type = 'default',
children,
...props
},
ref
) => {
const { iconSize } = getSize(size);
console.log('type', type);
return (
<StyledButton
ref={ref}
disabled={disabled}
size={size}
shape={shape}
hoverBackground={hoverBackground}
hoverColor={hoverColor}
hoverStyle={hoverStyle}
// @ts-ignore
type={type}
{...props}
>
{icon &&
cloneElement(Children.only(icon), {
width: iconSize,
height: iconSize,
className: `affine-button-icon ${icon.props.className ?? ''}`,
})}
{children && <span>{children}</span>}
</StyledButton>
);
}
);
Button.displayName = 'Button';
export default Button;
+1 -1
View File
@@ -11,7 +11,7 @@ import { StyledIconButton } from './styles';
const SIZE_SMALL = 'small' as const;
const SIZE_MIDDLE = 'middle' as const;
const SIZE_NORMAL = 'normal' as const;
// TODO: Designer is not sure about the size of the icon button
// TODO: IconButton should merge into Button, but it has not been designed yet
const SIZE_CONFIG = {
[SIZE_SMALL]: {
iconSize: 16,
+1
View File
@@ -1 +1,2 @@
export * from './icon-button';
export * from './button';
+22
View File
@@ -0,0 +1,22 @@
import {
CSSProperties,
HTMLAttributes,
PropsWithChildren,
ReactElement,
} from 'react';
export const SIZE_SMALL = 'small' as const;
export const SIZE_MIDDLE = 'middle' as const;
export const SIZE_DEFAULT = 'default' as const;
export type ButtonProps = PropsWithChildren &
Omit<HTMLAttributes<HTMLButtonElement>, 'type'> & {
size?: typeof SIZE_SMALL | typeof SIZE_MIDDLE | typeof SIZE_DEFAULT;
disabled?: boolean;
hoverBackground?: CSSProperties['background'];
hoverColor?: CSSProperties['color'];
hoverStyle?: CSSProperties;
icon?: ReactElement;
shape?: 'default' | 'round' | 'circle';
type?: 'primary' | 'warning' | 'danger' | 'default';
};
+63
View File
@@ -1,5 +1,7 @@
import { absoluteCenter, displayInlineFlex, styled } from '@/styles';
import { CSSProperties } from 'react';
import { ButtonProps } from '@/ui/button/interface';
import { getSize, getButtonColors } from './utils';
export const StyledIconButton = styled.button<{
width: number;
@@ -53,3 +55,64 @@ export const StyledIconButton = styled.button<{
};
}
);
export const StyledButton = styled.button<
Pick<
ButtonProps,
| 'size'
| 'disabled'
| 'hoverBackground'
| 'hoverColor'
| 'hoverStyle'
| 'shape'
| 'type'
>
>(
({
theme,
size = 'default',
disabled,
hoverBackground,
hoverColor,
hoverStyle,
shape = 'default',
type = 'default',
}) => {
const { fontSize, borderRadius, padding, height } = getSize(size);
return {
height,
paddingLeft: padding,
paddingRight: padding,
border: '1px solid',
...displayInlineFlex('flex-start', 'center'),
position: 'relative',
...(disabled ? { cursor: 'not-allowed', pointerEvents: 'none' } : {}),
transition: 'background .15s',
// TODO: Implement circle shape
borderRadius: shape === 'default' ? borderRadius : height / 2,
fontSize,
'.affine-button-icon': {
color: theme.colors.iconColor,
},
'>span': {
marginLeft: '5px',
},
// @ts-ignore
...getButtonColors(theme, type, {
hoverBackground,
hoverColor,
hoverStyle,
}),
//
// ':hover': {
// color: hoverColor ?? theme.colors.primaryColor,
// background: hoverBackground ?? theme.colors.hoverBackground,
// '.affine-button-icon':{
//
// }
// ...(hoverStyle ?? {}),
// },
};
}
);
+104
View File
@@ -0,0 +1,104 @@
import { AffineTheme } from '@/styles';
import {
SIZE_SMALL,
SIZE_MIDDLE,
SIZE_DEFAULT,
ButtonProps,
} from './interface';
// TODO: Designer is not sure about the size, Now, is just use default size
export const SIZE_CONFIG = {
[SIZE_SMALL]: {
iconSize: 16,
fontSize: 16,
borderRadius: 6,
height: 26,
padding: 24,
},
[SIZE_MIDDLE]: {
iconSize: 20,
fontSize: 16,
borderRadius: 6,
height: 32,
padding: 24,
},
[SIZE_DEFAULT]: {
iconSize: 24,
fontSize: 16,
height: 38,
padding: 24,
borderRadius: 6,
},
} as const;
export const getSize = (
size: typeof SIZE_SMALL | typeof SIZE_MIDDLE | typeof SIZE_DEFAULT
) => {
return SIZE_CONFIG[size];
};
export const getButtonColors = (
theme: AffineTheme,
type: ButtonProps['type'],
extend?: {
hoverBackground: ButtonProps['hoverBackground'];
hoverColor: ButtonProps['hoverColor'];
hoverStyle: ButtonProps['hoverStyle'];
}
) => {
switch (type) {
case 'primary':
return {
background: theme.colors.primaryColor,
color: '#fff',
borderColor: theme.colors.primaryColor,
'.affine-button-icon': {
color: '#fff',
},
};
case 'warning':
return {
background: theme.colors.warningBackground,
color: theme.colors.warningColor,
borderColor: theme.colors.warningBackground,
'.affine-button-icon': {
color: theme.colors.warningColor,
},
':hover': {
borderColor: theme.colors.warningColor,
color: extend?.hoverColor,
background: extend?.hoverBackground,
...extend?.hoverStyle,
},
};
case 'danger':
return {
background: theme.colors.errorBackground,
color: theme.colors.errorColor,
borderColor: theme.colors.errorBackground,
'.affine-button-icon': {
color: theme.colors.errorColor,
},
':hover': {
borderColor: theme.colors.errorColor,
color: extend?.hoverColor,
background: extend?.hoverBackground,
...extend?.hoverStyle,
},
};
default:
return {
color: theme.colors.popoverColor,
borderColor: theme.colors.borderColor,
':hover': {
borderColor: theme.colors.primaryColor,
color: extend?.hoverColor ?? theme.colors.primaryColor,
'.affine-button-icon': {
color: extend?.hoverColor ?? theme.colors.primaryColor,
background: extend?.hoverBackground,
...extend?.hoverStyle,
},
},
};
}
};
+9 -7
View File
@@ -5,9 +5,8 @@ import {
StyledConfirmContent,
StyledConfirmTitle,
StyledModalWrapper,
StyledButton,
} from '@/ui/confirm/styles';
import { Button } from '@/ui/button';
export type ConfirmProps = {
title?: string;
content?: string;
@@ -40,23 +39,26 @@ export const Confirm = ({
<StyledConfirmContent>{content}</StyledConfirmContent>
<StyledButtonWrapper>
<StyledButton
<Button
shape="round"
onClick={() => {
setOpen(false);
onCancel?.();
}}
style={{ marginRight: '24px' }}
>
Cancel
</StyledButton>
<StyledButton
confirmType={confirmType}
</Button>
<Button
type={confirmType}
shape="round"
onClick={() => {
setOpen(false);
onConfirm?.();
}}
>
{confirmText}
</StyledButton>
</Button>
</StyledButtonWrapper>
</StyledModalWrapper>
</Modal>
-18
View File
@@ -78,21 +78,3 @@ const getButtonColors = (
};
}
};
export const StyledButton = styled.button<Pick<ConfirmProps, 'confirmType'>>(
({ theme, confirmType }) => {
return {
width: '100px',
height: '38px',
borderRadius: '19px',
border: '1px solid',
...getButtonColors(theme, confirmType),
fontSize: theme.font.sm,
fontWeight: 500,
'&:first-of-type': {
marginRight: '24px',
},
};
}
);