From 2ca52c187b17e6a8a0debbe1553eca8a886fafaa Mon Sep 17 00:00:00 2001 From: QiShaoXuan Date: Mon, 5 Dec 2022 22:20:21 +0800 Subject: [PATCH] feat: refactor ui components --- packages/app/src/components/Header/header.tsx | 55 +++----- packages/app/src/components/Header/icons.tsx | 119 ------------------ packages/app/src/components/Header/styles.ts | 45 ------- packages/app/src/pages/all-page.tsx | 27 +++- packages/app/src/ui/button/icon-button.tsx | 85 +++++++++++++ packages/app/src/ui/button/index.ts | 1 + packages/app/src/ui/button/styles.ts | 55 ++++++++ packages/app/src/ui/menu/index.ts | 3 +- packages/app/src/ui/menu/menu-item.tsx | 35 ++++++ packages/app/src/ui/menu/styles.ts | 55 +++++--- packages/app/src/ui/table/styles.ts | 4 +- 11 files changed, 261 insertions(+), 223 deletions(-) delete mode 100644 packages/app/src/components/Header/icons.tsx create mode 100644 packages/app/src/ui/button/icon-button.tsx create mode 100644 packages/app/src/ui/button/index.ts create mode 100644 packages/app/src/ui/button/styles.ts create mode 100644 packages/app/src/ui/menu/menu-item.tsx diff --git a/packages/app/src/components/Header/header.tsx b/packages/app/src/components/Header/header.tsx index 3be73a1413..c4675ed222 100644 --- a/packages/app/src/components/Header/header.tsx +++ b/packages/app/src/components/Header/header.tsx @@ -1,29 +1,22 @@ -import React, { PropsWithChildren, useEffect, useState } from 'react'; -import { - LogoIcon, - MoreIcon, - ExportIcon, - Export2Markdown, - Export2HTML, - RightArrow, -} from './icons'; +import React, { PropsWithChildren, useState } from 'react'; import { StyledHeader, - StyledTitle, - StyledTitleWrapper, - StyledLogo, StyledHeaderRightSide, - IconButton, StyledHeaderContainer, StyledBrowserWarning, StyledCloseButton, - StyledMenuItemWrapper, } from './styles'; +import { + MiddleExportIcon, + EdgelessIcon, + PaperIcon, + MiddleExportToHtmlIcon, + MiddleExportToMarkdownIcon, + MoreVerticalIcon, +} from '@blocksuite/icons'; import { useEditor } from '@/providers/editor-provider'; -import EditorModeSwitch from '@/components/editor-mode-switch'; -import { EdgelessIcon, PaperIcon } from '../editor-mode-switch/icons'; import ThemeModeSwitch from '@/components/theme-mode-switch'; -import { useModal } from '@/providers/global-modal-provider'; +import { IconButton } from '@/ui/button'; import CloseIcon from '@mui/icons-material/Close'; import { getWarningMessage, shouldShowWarning } from './utils'; import { Menu, MenuItem } from '@/ui/menu'; @@ -33,14 +26,12 @@ const PopoverContent = () => { return ( <> : } onClick={() => { setMode(mode === 'page' ? 'edgeless' : 'page'); }} > - - {mode === 'page' ? : } - Convert to {mode === 'page' ? 'Edgeless' : 'Page'} - + Convert to {mode === 'page' ? 'Edgeless' : 'Page'} { onClick={() => { editor && editor.contentParser.onExportHtml(); }} + icon={} > - - - Export to HTML - + Export to HTML { editor && editor.contentParser.onExportMarkdown(); }} + icon={} > - - - Export to Markdown - + Export to Markdown } > - - - - Export - - + } isDir={true}> + Export @@ -115,7 +98,7 @@ export const Header = ({ children }: PropsWithChildren<{}>) => { } placement="bottom-end"> - + diff --git a/packages/app/src/components/Header/icons.tsx b/packages/app/src/components/Header/icons.tsx deleted file mode 100644 index 57a4134d6e..0000000000 --- a/packages/app/src/components/Header/icons.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import type { DOMAttributes, CSSProperties } from 'react'; -type IconProps = { - style?: CSSProperties; -} & DOMAttributes; - -export const RightArrow = ({ style = {}, ...props }: IconProps) => { - return ( - - - - ); -}; -export const Export2Markdown = ({ style = {}, ...props }: IconProps) => { - return ( - - - - - ); -}; -export const Export2HTML = ({ style = {}, ...props }: IconProps) => { - return ( - - - - - - - - ); -}; -export const LogoIcon = ({ style = {}, ...props }: IconProps) => { - return ( - - - - ); -}; - -export const MoreIcon = ({ style = {}, ...props }: IconProps) => { - return ( - - - - - - ); -}; -export const ExportIcon = ({ style = {}, ...props }: IconProps) => { - return ( - - - - ); -}; diff --git a/packages/app/src/components/Header/styles.ts b/packages/app/src/components/Header/styles.ts index b5ca7561da..399ac197a7 100644 --- a/packages/app/src/components/Header/styles.ts +++ b/packages/app/src/components/Header/styles.ts @@ -1,5 +1,4 @@ import { displayFlex, styled } from '@/styles'; -import { MenuItem } from '@/ui/menu'; export const StyledHeaderContainer = styled.div<{ hasWarning: boolean }>( ({ hasWarning }) => { @@ -47,56 +46,12 @@ export const StyledTitleWrapper = styled('div')({ position: 'relative', }); -export const StyledLogo = styled('div')(({ theme }) => ({ - color: theme.colors.primaryColor, - width: '60px', - height: '60px', - cursor: 'pointer', - marginLeft: '-22px', - ...displayFlex('center', 'center'), -})); - export const StyledHeaderRightSide = styled('div')({ height: '100%', display: 'flex', alignItems: 'center', }); -export const StyledMenuItemWrapper = styled.div(({ theme }) => { - return { - height: '32px', - position: 'relative', - cursor: 'pointer', - ...displayFlex('flex-start', 'center'), - svg: { - width: '16px', - height: '16px', - marginRight: '14px', - }, - 'svg:nth-child(2)': { - position: 'absolute', - right: 0, - top: 0, - bottom: 0, - margin: 'auto', - }, - }; -}); - -export const IconButton = styled('div')(({ theme }) => { - return { - width: '32px', - height: '32px', - ...displayFlex('center', 'center'), - color: theme.colors.iconColor, - borderRadius: '5px', - ':hover': { - color: theme.colors.primaryColor, - background: theme.colors.hoverBackground, - }, - }; -}); - export const StyledBrowserWarning = styled.div<{ show: boolean }>( ({ theme, show }) => { return { diff --git a/packages/app/src/pages/all-page.tsx b/packages/app/src/pages/all-page.tsx index dd40b8188f..447a271d18 100644 --- a/packages/app/src/pages/all-page.tsx +++ b/packages/app/src/pages/all-page.tsx @@ -3,6 +3,10 @@ import { Header } from '@/components/Header'; import { styled } from '@/styles'; import { Table, TableCell, TableHead, TableRow, TableBody } from '../ui/table'; import { useConfirm } from '@/providers/confirm-provider'; +import { IconButton } from '@/ui/button'; +import { MoreVerticalIcon } from '@blocksuite/icons'; +import { Menu, MenuItem } from '@/ui/menu'; + const StyledTableContainer = styled.div(() => { return { height: 'calc(100vh - 60px)', @@ -10,6 +14,17 @@ const StyledTableContainer = styled.div(() => { overflowY: 'auto', }; }); + +const OperationMenu = () => { + return ( + <> + Add to favourites + Open in new tab + Delete + + ); +}; + export const AllPage = () => { const { confirm } = useConfirm(); return ( @@ -47,7 +62,17 @@ export const AllPage = () => { 2022-11-02 18:30 2022-11-02 18:30 - ... + + } + placement="bottom-end" + disablePortal={true} + > + + + + + ); })} diff --git a/packages/app/src/ui/button/icon-button.tsx b/packages/app/src/ui/button/icon-button.tsx new file mode 100644 index 0000000000..922f838425 --- /dev/null +++ b/packages/app/src/ui/button/icon-button.tsx @@ -0,0 +1,85 @@ +import { + HTMLAttributes, + cloneElement, + ReactElement, + Children, + CSSProperties, + forwardRef, +} from 'react'; +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 +const SIZE_CONFIG = { + [SIZE_SMALL]: { + iconSize: 16, + areaSize: 24, + }, + [SIZE_MIDDLE]: { + iconSize: 20, + areaSize: 28, + }, + [SIZE_NORMAL]: { + iconSize: 24, + areaSize: 32, + }, +} as const; + +export type IconButtonProps = { + size?: + | typeof SIZE_SMALL + | typeof SIZE_MIDDLE + | typeof SIZE_NORMAL + | [number, number]; + iconSize?: + | typeof SIZE_SMALL + | typeof SIZE_MIDDLE + | typeof SIZE_NORMAL + | [number, number]; + disabled?: boolean; + hoverBackground?: string; + hoverColor?: string; + hoverStyle?: CSSProperties; + children: ReactElement, 'svg'>; +} & HTMLAttributes; + +export const IconButton = forwardRef( + ( + { + size = 'normal', + iconSize = 'normal', + disabled = false, + children, + ...props + }, + ref + ) => { + const [width, height] = Array.isArray(size) + ? size + : [SIZE_CONFIG[size]['areaSize'], SIZE_CONFIG[size]['areaSize']]; + const [iconWidth, iconHeight] = Array.isArray(iconSize) + ? iconSize + : [SIZE_CONFIG[iconSize]['iconSize'], SIZE_CONFIG[iconSize]['iconSize']]; + + return ( + + {cloneElement(Children.only(children), { + width: iconWidth, + height: iconHeight, + })} + + ); + } +); +IconButton.displayName = 'IconButton'; + +export default IconButton; diff --git a/packages/app/src/ui/button/index.ts b/packages/app/src/ui/button/index.ts new file mode 100644 index 0000000000..51688a7c80 --- /dev/null +++ b/packages/app/src/ui/button/index.ts @@ -0,0 +1 @@ +export * from './icon-button'; diff --git a/packages/app/src/ui/button/styles.ts b/packages/app/src/ui/button/styles.ts new file mode 100644 index 0000000000..90cd0a8f0a --- /dev/null +++ b/packages/app/src/ui/button/styles.ts @@ -0,0 +1,55 @@ +import { absoluteCenter, displayFlex, styled } from '@/styles'; +import { CSSProperties } from 'react'; + +export const StyledIconButton = styled.button<{ + width: number; + height: number; + borderRadius: number; + disabled?: boolean; + hoverBackground?: string; + hoverColor?: string; + hoverStyle?: CSSProperties; +}>( + ({ + theme, + width, + height, + disabled, + hoverBackground, + hoverColor, + hoverStyle, + }) => { + return { + width, + height, + color: theme.colors.iconColor, + ...displayFlex('center', 'center'), + position: 'relative', + ...(disabled ? { cursor: 'not-allowed', pointerEvents: 'none' } : {}), + transition: 'background .15s', + + // TODO: we need to add @emotion/babel-plugin + '::after': { + content: '""', + width, + height, + borderRadius: width / 5, + transition: 'background .15s', + ...absoluteCenter({ horizontal: true, vertical: true }), + }, + + svg: { + position: 'relative', + zIndex: 1, + }, + + ':hover': { + color: hoverColor ?? theme.colors.primaryColor, + '::after': { + background: hoverBackground ?? theme.colors.hoverBackground, + }, + ...(hoverStyle ?? {}), + }, + }; + } +); diff --git a/packages/app/src/ui/menu/index.ts b/packages/app/src/ui/menu/index.ts index 8dde85f113..d56e8d1489 100644 --- a/packages/app/src/ui/menu/index.ts +++ b/packages/app/src/ui/menu/index.ts @@ -1,2 +1,3 @@ export * from './menu'; -export { StyledMenuItem as MenuItem } from './styles'; +// export { StyledMenuItem as MenuItem } from './styles'; +export * from './menu-item'; diff --git a/packages/app/src/ui/menu/menu-item.tsx b/packages/app/src/ui/menu/menu-item.tsx new file mode 100644 index 0000000000..45e40fb649 --- /dev/null +++ b/packages/app/src/ui/menu/menu-item.tsx @@ -0,0 +1,35 @@ +import { + cloneElement, + forwardRef, + HTMLAttributes, + PropsWithChildren, + ReactElement, +} from 'react'; +import { StyledMenuItem, StyledArrow } from './styles'; + +export type IconMenuProps = PropsWithChildren<{ + isDir?: boolean; + icon?: ReactElement; +}> & + HTMLAttributes; + +export const MenuItem = forwardRef( + ({ isDir = false, icon, children, ...props }, ref) => { + return ( + + {icon && + cloneElement(icon, { + width: 16, + height: 16, + style: { + marginRight: 14, + }, + })} + {children} + {isDir ? : null} + + ); + } +); +MenuItem.displayName = 'MenuItem'; +export default MenuItem; diff --git a/packages/app/src/ui/menu/styles.ts b/packages/app/src/ui/menu/styles.ts index 1b426217d6..4321a17722 100644 --- a/packages/app/src/ui/menu/styles.ts +++ b/packages/app/src/ui/menu/styles.ts @@ -1,5 +1,6 @@ -import { styled } from '@/styles'; +import { displayFlex, styled } from '@/styles'; import StyledPopperContainer from '../shared/Container'; +import { MiddleArrowRightIcon } from '@blocksuite/icons'; export const StyledMenuWrapper = styled(StyledPopperContainer)(({ theme }) => { return { @@ -12,23 +13,39 @@ export const StyledMenuWrapper = styled(StyledPopperContainer)(({ theme }) => { }; }); -export const StyledMenuItem = styled('div')<{ popperVisible?: boolean }>( - ({ theme, popperVisible }) => { - return { - borderRadius: '5px', - padding: '0 14px', +export const StyledArrow = styled(MiddleArrowRightIcon)(({ theme }) => { + return { + position: 'absolute', + right: 0, + top: 0, + bottom: 0, + margin: 'auto', + }; +}); - color: popperVisible - ? theme.colors.primaryColor - : theme.colors.popoverColor, - backgroundColor: popperVisible - ? theme.colors.hoverBackground - : 'transparent', +export const StyledMenuItem = styled.button<{ + popperVisible?: boolean; + isDir?: boolean; +}>(({ theme, popperVisible, isDir = false }) => { + return { + width: '100%', + borderRadius: '5px', + padding: '0 14px', + fontSize: '14px', + height: '32px', + ...displayFlex('flex-start', 'center'), + cursor: isDir ? 'pointer' : '', + position: 'relative', + color: popperVisible + ? theme.colors.primaryColor + : theme.colors.popoverColor, + backgroundColor: popperVisible + ? theme.colors.hoverBackground + : 'transparent', - ':hover': { - color: theme.colors.primaryColor, - backgroundColor: theme.colors.hoverBackground, - }, - }; - } -); + ':hover': { + color: theme.colors.primaryColor, + backgroundColor: theme.colors.hoverBackground, + }, + }; +}); diff --git a/packages/app/src/ui/table/styles.ts b/packages/app/src/ui/table/styles.ts index 39d16aa5db..5c14236737 100644 --- a/packages/app/src/ui/table/styles.ts +++ b/packages/app/src/ui/table/styles.ts @@ -48,11 +48,11 @@ export const StyledTableRow = styled.tr(({ theme }) => { td: { transition: 'background .15s', }, - 'td:first-child': { + 'td:first-of-type': { borderTopLeftRadius: '10px', borderBottomLeftRadius: '10px', }, - 'td:last-child': { + 'td:last-of-type': { borderTopRightRadius: '10px', borderBottomRightRadius: '10px', },