From 57f19ee4a73ab9fc0229acd2d749073fbec67045 Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Fri, 9 Dec 2022 18:39:50 +0800 Subject: [PATCH] feat: add button component --- .../pages/{all-page.tsx => page-list/all.tsx} | 0 packages/app/src/ui/button/button.tsx | 60 ++++++++++ packages/app/src/ui/button/icon-button.tsx | 2 +- packages/app/src/ui/button/index.ts | 1 + packages/app/src/ui/button/interface.ts | 22 ++++ packages/app/src/ui/button/styles.ts | 63 +++++++++++ packages/app/src/ui/button/utils.ts | 104 ++++++++++++++++++ packages/app/src/ui/confirm/confirm.tsx | 16 +-- packages/app/src/ui/confirm/styles.ts | 18 --- 9 files changed, 260 insertions(+), 26 deletions(-) rename packages/app/src/pages/{all-page.tsx => page-list/all.tsx} (100%) create mode 100644 packages/app/src/ui/button/button.tsx create mode 100644 packages/app/src/ui/button/interface.ts create mode 100644 packages/app/src/ui/button/utils.ts diff --git a/packages/app/src/pages/all-page.tsx b/packages/app/src/pages/page-list/all.tsx similarity index 100% rename from packages/app/src/pages/all-page.tsx rename to packages/app/src/pages/page-list/all.tsx diff --git a/packages/app/src/ui/button/button.tsx b/packages/app/src/ui/button/button.tsx new file mode 100644 index 0000000000..0ed48ea044 --- /dev/null +++ b/packages/app/src/ui/button/button.tsx @@ -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( + ( + { + size = 'default', + disabled = false, + hoverBackground, + hoverColor, + hoverStyle, + shape = 'default', + icon, + type = 'default', + children, + ...props + }, + ref + ) => { + const { iconSize } = getSize(size); + console.log('type', type); + + return ( + + {icon && + cloneElement(Children.only(icon), { + width: iconSize, + height: iconSize, + className: `affine-button-icon ${icon.props.className ?? ''}`, + })} + {children && {children}} + + ); + } +); +Button.displayName = 'Button'; + +export default Button; diff --git a/packages/app/src/ui/button/icon-button.tsx b/packages/app/src/ui/button/icon-button.tsx index 922f838425..da40fbda57 100644 --- a/packages/app/src/ui/button/icon-button.tsx +++ b/packages/app/src/ui/button/icon-button.tsx @@ -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, diff --git a/packages/app/src/ui/button/index.ts b/packages/app/src/ui/button/index.ts index 51688a7c80..765395ea09 100644 --- a/packages/app/src/ui/button/index.ts +++ b/packages/app/src/ui/button/index.ts @@ -1 +1,2 @@ export * from './icon-button'; +export * from './button'; diff --git a/packages/app/src/ui/button/interface.ts b/packages/app/src/ui/button/interface.ts new file mode 100644 index 0000000000..431a03d4ed --- /dev/null +++ b/packages/app/src/ui/button/interface.ts @@ -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, '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'; + }; diff --git a/packages/app/src/ui/button/styles.ts b/packages/app/src/ui/button/styles.ts index abf791ada7..a39757ef62 100644 --- a/packages/app/src/ui/button/styles.ts +++ b/packages/app/src/ui/button/styles.ts @@ -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 ?? {}), + // }, + }; + } +); diff --git a/packages/app/src/ui/button/utils.ts b/packages/app/src/ui/button/utils.ts new file mode 100644 index 0000000000..fddfa5df6e --- /dev/null +++ b/packages/app/src/ui/button/utils.ts @@ -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, + }, + }, + }; + } +}; diff --git a/packages/app/src/ui/confirm/confirm.tsx b/packages/app/src/ui/confirm/confirm.tsx index c759ebfe98..06c2dc9b13 100644 --- a/packages/app/src/ui/confirm/confirm.tsx +++ b/packages/app/src/ui/confirm/confirm.tsx @@ -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 = ({ {content} - { setOpen(false); onCancel?.(); }} + style={{ marginRight: '24px' }} > Cancel - - + diff --git a/packages/app/src/ui/confirm/styles.ts b/packages/app/src/ui/confirm/styles.ts index a0ac9fa87b..03d1ef5bca 100644 --- a/packages/app/src/ui/confirm/styles.ts +++ b/packages/app/src/ui/confirm/styles.ts @@ -78,21 +78,3 @@ const getButtonColors = ( }; } }; - -export const StyledButton = styled.button>( - ({ 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', - }, - }; - } -);