diff --git a/packages/app/src/components/Header/header.tsx b/packages/app/src/components/Header/header.tsx new file mode 100644 index 0000000000..3be73a1413 --- /dev/null +++ b/packages/app/src/components/Header/header.tsx @@ -0,0 +1,127 @@ +import React, { PropsWithChildren, useEffect, useState } from 'react'; +import { + LogoIcon, + MoreIcon, + ExportIcon, + Export2Markdown, + Export2HTML, + RightArrow, +} from './icons'; +import { + StyledHeader, + StyledTitle, + StyledTitleWrapper, + StyledLogo, + StyledHeaderRightSide, + IconButton, + StyledHeaderContainer, + StyledBrowserWarning, + StyledCloseButton, + StyledMenuItemWrapper, +} from './styles'; +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 CloseIcon from '@mui/icons-material/Close'; +import { getWarningMessage, shouldShowWarning } from './utils'; +import { Menu, MenuItem } from '@/ui/menu'; + +const PopoverContent = () => { + const { editor, mode, setMode } = useEditor(); + return ( + <> + { + setMode(mode === 'page' ? 'edgeless' : 'page'); + }} + > + + {mode === 'page' ? : } + Convert to {mode === 'page' ? 'Edgeless' : 'Page'} + + + + { + editor && editor.contentParser.onExportHtml(); + }} + > + + + Export to HTML + + + { + editor && editor.contentParser.onExportMarkdown(); + }} + > + + + Export to Markdown + + + + } + > + + + + Export + + + + + + ); +}; + +const BrowserWarning = ({ + show, + onClose, +}: { + show: boolean; + onClose: () => void; +}) => { + return ( + + {getWarningMessage()} + + + + + ); +}; + +export const Header = ({ children }: PropsWithChildren<{}>) => { + const [showWarning, setShowWarning] = useState(shouldShowWarning()); + + return ( + + { + setShowWarning(false); + }} + /> + + {children} + + + } placement="bottom-end"> + + + + + + + + ); +}; + +export default Header; diff --git a/packages/app/src/components/Header/index.tsx b/packages/app/src/components/Header/index.tsx index 33c9d0495c..9ecc82e6c6 100644 --- a/packages/app/src/components/Header/index.tsx +++ b/packages/app/src/components/Header/index.tsx @@ -1,165 +1,2 @@ -import React, { useEffect, useState } from 'react'; -import { - LogoIcon, - MoreIcon, - ExportIcon, - Export2Markdown, - Export2HTML, - RightArrow, -} from './icons'; -import { - StyledHeader, - StyledTitle, - StyledTitleWrapper, - StyledLogo, - StyledHeaderRightSide, - IconButton, - StyledHeaderContainer, - StyledBrowserWarning, - StyledCloseButton, - StyledMenuItemWrapper, -} from './styles'; -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 CloseIcon from '@mui/icons-material/Close'; -import { getWarningMessage, shouldShowWarning } from './utils'; -import { Menu, MenuItem } from '@/ui/menu'; - -const PopoverContent = () => { - const { editor, mode, setMode } = useEditor(); - return ( - <> - { - setMode(mode === 'page' ? 'edgeless' : 'page'); - }} - > - - {mode === 'page' ? : } - Convert to {mode === 'page' ? 'Edgeless' : 'Page'} - - - - { - editor && editor.contentParser.onExportHtml(); - }} - > - - - Export to HTML - - - { - editor && editor.contentParser.onExportMarkdown(); - }} - > - - - Export to Markdown - - - - } - > - - - - Export - - - - - - ); -}; - -const BrowserWarning = ({ - show, - onClose, -}: { - show: boolean; - onClose: () => void; -}) => { - return ( - - {getWarningMessage()} - - - - - ); -}; - -export const Header = () => { - const [title, setTitle] = useState(''); - const [isHover, setIsHover] = useState(false); - const [showWarning, setShowWarning] = useState(shouldShowWarning()); - - const { contactModalHandler } = useModal(); - const { editor } = useEditor(); - - useEffect(() => { - console.log('header', editor); - - if (editor?.model) { - setTitle(editor.model.title ?? ''); - editor.model.propsUpdated.on(() => { - setTitle(editor.model.title); - }); - } - }, [editor]); - return ( - - { - setShowWarning(false); - }} - /> - - {/* {*/} - {/* contactModalHandler(true);*/} - {/* }}*/} - {/*>*/} - {/* */} - {/**/} - {title ? ( - { - setIsHover(true); - }} - onMouseLeave={() => { - setIsHover(false); - }} - > - - {title} - - ) : null} - - - - } placement="bottom-end"> - - - - - - - - ); -}; +export * from './header'; +export * from './page-header'; diff --git a/packages/app/src/components/Header/page-header.tsx b/packages/app/src/components/Header/page-header.tsx new file mode 100644 index 0000000000..65dfa8e04a --- /dev/null +++ b/packages/app/src/components/Header/page-header.tsx @@ -0,0 +1,47 @@ +import React, { useEffect, useState } from 'react'; +import { StyledTitle, StyledTitleWrapper } from './styles'; +import { useEditor } from '@/providers/editor-provider'; +import EditorModeSwitch from '@/components/editor-mode-switch'; + +import Header from './header'; + +export const PageHeader = () => { + const [title, setTitle] = useState(''); + const [isHover, setIsHover] = useState(false); + + const { editor } = useEditor(); + + useEffect(() => { + if (editor?.model) { + setTitle(editor.model.title ?? ''); + editor.model.propsUpdated.on(() => { + setTitle(editor.model.title); + }); + } + }, [editor]); + + return ( +
+ {title ? ( + { + setIsHover(true); + }} + onMouseLeave={() => { + setIsHover(false); + }} + > + + {title} + + ) : null} +
+ ); +}; + +export default PageHeader; diff --git a/packages/app/src/components/workspace-slider-bar/index.tsx b/packages/app/src/components/workspace-slider-bar/index.tsx index 7a4ef8361c..4cd6065462 100644 --- a/packages/app/src/components/workspace-slider-bar/index.tsx +++ b/packages/app/src/components/workspace-slider-bar/index.tsx @@ -1,22 +1,26 @@ import React, { useState } from 'react'; +import Router from 'next/router'; import { StyledArrowButton, StyledListItem, + StyledNewPageButton, StyledSliderBar, StyledSubListItem, - StyledWrapper, } from './style'; import { Arrow } from './icons'; - export const WorkSpaceSliderBar = () => { const [show, setShow] = useState(false); return ( <> - - Quick search - All pages + { + Router.push('/all-page'); + }} + > + All pages + Favourites document 1, this is a paper icondocument 1 @@ -25,6 +29,8 @@ export const WorkSpaceSliderBar = () => { document 4 Import Bin + + New Page ( zIndex: theme.zIndex.modal, padding: show ? '24px 12px' : '24px 0', overflowX: 'hidden', + flexShrink: 0, }; } ); -export const StyledWrapper = styled.div(() => { - return { - // padding: '24px 12px', - // height: '100%', - // overflowY: 'auto', - }; -}); + export const StyledArrowButton = styled.button<{ isShow: boolean }>( ({ theme, isShow }) => { return { @@ -54,6 +49,8 @@ export const StyledListItem = styled.button(({ theme }) => { marginTop: '12px', fontSize: theme.font.sm, color: theme.colors.popoverColor, + padding: '0 12px', + borderRadius: '5px', ...displayFlex('flex-start', 'center'), ':hover': { color: theme.colors.primaryColor, @@ -62,13 +59,22 @@ export const StyledListItem = styled.button(({ theme }) => { }; }); +export const StyledNewPageButton = styled(StyledListItem)(({ theme }) => { + return { + position: 'absolute', + bottom: '24px', + left: '0', + right: '0', + margin: 'auto', + }; +}); + export const StyledSubListItem = styled.button(({ theme }) => { return { width: '296px', height: '32px', marginTop: '4px', fontSize: theme.font.sm, - fontWeight: 400, color: theme.colors.popoverColor, paddingLeft: '45px', lineHeight: '32px', @@ -77,7 +83,6 @@ export const StyledSubListItem = styled.button(({ theme }) => { ':hover': { color: theme.colors.primaryColor, backgroundColor: theme.colors.hoverBackground, - fontWeight: 500, }, }; }); diff --git a/packages/app/src/pages/_app.tsx b/packages/app/src/pages/_app.tsx index 9b15465982..b64264e83c 100644 --- a/packages/app/src/pages/_app.tsx +++ b/packages/app/src/pages/_app.tsx @@ -11,9 +11,8 @@ import '@fontsource/space-mono'; import '@fontsource/poppins'; import '../utils/print-build-info'; import { styled } from '@/styles'; -import type { ReactNode, PropsWithChildren, FC } from 'react'; -import { cloneElement } from 'react'; - +import ProviderComposer from '@/components/provider-composer'; +import ConfirmProvider from '@/providers/confirm-provider'; const ThemeProvider = dynamic(() => import('@/providers/themeProvider'), { ssr: false, }); @@ -24,21 +23,15 @@ const StyledPage = styled('div')(({ theme }) => { backgroundColor: theme.colors.pageBackground, transition: 'background-color .5s', display: 'flex', + flexGrow: '1', }; }); -const ProviderComposer: FC< - PropsWithChildren<{ - contexts: any; - }> -> = ({ contexts, children }) => - contexts.reduceRight( - (kids: ReactNode, parent: any) => - cloneElement(parent, { - children: kids, - }), - children - ); +const StyledWrapper = styled('div')(({ theme }) => { + return { + flexGrow: 1, + }; +}); function MyApp({ Component, pageProps }: AppProps) { return ( @@ -49,11 +42,14 @@ function MyApp({ Component, pageProps }: AppProps) { , , , + , ]} > - + + + diff --git a/packages/app/src/pages/all-page.tsx b/packages/app/src/pages/all-page.tsx new file mode 100644 index 0000000000..dd40b8188f --- /dev/null +++ b/packages/app/src/pages/all-page.tsx @@ -0,0 +1,61 @@ +import React from 'react'; +import { Header } from '@/components/Header'; +import { styled } from '@/styles'; +import { Table, TableCell, TableHead, TableRow, TableBody } from '../ui/table'; +import { useConfirm } from '@/providers/confirm-provider'; +const StyledTableContainer = styled.div(() => { + return { + height: 'calc(100vh - 60px)', + padding: '78px 72px', + overflowY: 'auto', + }; +}); +export const AllPage = () => { + const { confirm } = useConfirm(); + return ( + <> +
+ + + + + + Documents + Created + Uploaded + + + + + {new Array(100).fill(0).map((_, index) => { + return ( + + + This is a long, very long, extremely long, incredibly long, + exceedingly long, very long document title + + 2022-11-02 18:30 + 2022-11-02 18:30 + ... + + ); + })} + +
+
+ + ); +}; + +export default AllPage; diff --git a/packages/app/src/pages/index.tsx b/packages/app/src/pages/index.tsx index 2ceee74813..33c87c954f 100644 --- a/packages/app/src/pages/index.tsx +++ b/packages/app/src/pages/index.tsx @@ -1,6 +1,6 @@ import type { NextPage } from 'next'; import { styled } from '@/styles'; -import { Header } from '@/components/Header'; +import { PageHeader } from '@/components/Header'; import { FAQ } from '@/components/faq'; import EdgelessToolbar from '@/components/edgeless-toolbar'; import MobileModal from '@/components/mobile-modal'; @@ -14,26 +14,17 @@ const StyledEditorContainer = styled('div')(({ theme }) => { }; }); -const StyledWrapper = styled('div')(({ theme }) => { - return { - height: '100vh', - backgroundColor: theme.colors.pageBackground, - transition: 'background-color .5s', - flexGrow: 1, - }; -}); - const Home: NextPage = () => { return ( - -
+ <> + - + ); }; diff --git a/packages/app/src/providers/confirm-provider.tsx b/packages/app/src/providers/confirm-provider.tsx new file mode 100644 index 0000000000..4b7b374d42 --- /dev/null +++ b/packages/app/src/providers/confirm-provider.tsx @@ -0,0 +1,52 @@ +import { createContext, useContext, useState, ReactNode } from 'react'; +import type { PropsWithChildren } from 'react'; +import { Confirm, ConfirmProps } from '@/ui/confirm'; + +type ConfirmContextValue = { + confirm: (props: ConfirmProps) => void; +}; +type ConfirmContextProps = PropsWithChildren<{}>; + +export const ConfirmContext = createContext({ + confirm: () => {}, +}); + +export const useConfirm = () => useContext(ConfirmContext); + +export const ConfirmProvider = ({ + children, +}: PropsWithChildren) => { + const [confirmRecord, setConfirmRecord] = useState>( + {} + ); + return ( + { + const confirmId = String(Date.now()); + setConfirmRecord(oldConfirmRecord => { + return { + ...oldConfirmRecord, + [confirmId]: ( + { + delete confirmRecord[confirmId]; + setConfirmRecord({ ...confirmRecord }); + }} + /> + ), + }; + }); + }, + }} + > + {children} + {Object.entries(confirmRecord).map(([confirmId, confirmNode]) => { + return
{confirmNode}
; + })} +
+ ); +}; + +export default ConfirmProvider; diff --git a/packages/app/src/styles/theme.ts b/packages/app/src/styles/theme.ts index e54ba4a4fd..18ca3b7163 100644 --- a/packages/app/src/styles/theme.ts +++ b/packages/app/src/styles/theme.ts @@ -20,6 +20,7 @@ export const getLightTheme = ( tooltipBackground: '#6880FF', codeBackground: '#f2f5f9', warningBackground: '#FFF9C7', + errorBackground: '#FFDED8', textColor: '#3A4C5C', edgelessTextColor: '#3A4C5C', @@ -36,11 +37,19 @@ export const getLightTheme = ( borderColor: '#D0D7E3', disableColor: '#C0C0C0', warningColor: '#906616', + errorColor: '#EB4335', }, font: { xs: '12px', sm: '16px', base: '18px', + h1: '30px', + h2: '28px', + h3: '26px', + h4: '24px', + h5: '22px', + h6: '20px', + family: `Avenir Next, Poppins, ${basicFontFamily}`, family2: `Space Mono, ${basicFontFamily}`, lineHeightBase: '26px', @@ -85,6 +94,7 @@ export const getDarkTheme = ( ? lightTheme.colors.codeBackground : '#505662', warningBackground: '#FFF9C7', + errorBackground: '#FFDED8', textColor: '#fff', edgelessTextColor: '#3A4C5C', @@ -102,6 +112,7 @@ export const getDarkTheme = ( borderColor: '#4D4C53', disableColor: '#4b4b4b', warningColor: '#906616', + errorColor: '#EB4335', }, shadow: { popover: diff --git a/packages/app/src/styles/types.ts b/packages/app/src/styles/types.ts index 155842eebc..0a69af4b47 100644 --- a/packages/app/src/styles/types.ts +++ b/packages/app/src/styles/types.ts @@ -25,6 +25,7 @@ export interface AffineTheme { hoverBackground: string; codeBackground: string; warningBackground: string; + errorBackground: string; // Use for the page`s text textColor: string; // Use for the editor`s text, because in edgeless mode text is different form other @@ -43,11 +44,18 @@ export interface AffineTheme { borderColor: string; disableColor: string; warningColor: string; + errorColor: string; }; font: { xs: string; // tiny sm: string; // small base: string; + h1: string; + h2: string; + h3: string; + h4: string; + h5: string; + h6: string; family: string; family2: string; diff --git a/packages/app/src/ui/confirm/confirm.tsx b/packages/app/src/ui/confirm/confirm.tsx new file mode 100644 index 0000000000..c759ebfe98 --- /dev/null +++ b/packages/app/src/ui/confirm/confirm.tsx @@ -0,0 +1,66 @@ +import { useState } from 'react'; +import { Modal, ModalCloseButton, ModalProps } from '../modal'; +import { + StyledButtonWrapper, + StyledConfirmContent, + StyledConfirmTitle, + StyledModalWrapper, + StyledButton, +} from '@/ui/confirm/styles'; + +export type ConfirmProps = { + title?: string; + content?: string; + confirmText?: string; + // TODO: Confirm button's color should depend on confirm type + confirmType?: 'primary' | 'warning' | 'danger'; + onConfirm?: () => void; + onCancel?: () => void; +} & Omit; + +export const Confirm = ({ + title, + content, + confirmText, + confirmType, + onConfirm, + onCancel, +}: ConfirmProps) => { + const [open, setOpen] = useState(true); + return ( + + + { + setOpen(false); + onCancel?.(); + }} + /> + {title} + {content} + + + { + setOpen(false); + onCancel?.(); + }} + > + Cancel + + { + setOpen(false); + onConfirm?.(); + }} + > + {confirmText} + + + + + ); +}; + +export default Confirm; diff --git a/packages/app/src/ui/confirm/index.ts b/packages/app/src/ui/confirm/index.ts new file mode 100644 index 0000000000..5d2064b5f1 --- /dev/null +++ b/packages/app/src/ui/confirm/index.ts @@ -0,0 +1 @@ +export * from './confirm'; diff --git a/packages/app/src/ui/confirm/styles.ts b/packages/app/src/ui/confirm/styles.ts new file mode 100644 index 0000000000..d285ecdad7 --- /dev/null +++ b/packages/app/src/ui/confirm/styles.ts @@ -0,0 +1,98 @@ +import { displayFlex, styled, AffineTheme } from '@/styles'; +import { ConfirmProps } from '@/ui/confirm/confirm'; + +export const StyledModalWrapper = styled.div(({ theme }) => { + return { + width: '460px', + height: '240px', + padding: '0 60px', + background: theme.colors.popoverBackground, + borderRadius: '28px', + position: 'relative', + }; +}); + +export const StyledConfirmTitle = styled.div(({ theme }) => { + return { + fontSize: theme.font.h6, + fontWeight: 600, + textAlign: 'center', + marginTop: '45px', + color: theme.colors.popoverColor, + }; +}); + +export const StyledConfirmContent = styled.div(({ theme }) => { + return { + fontSize: theme.font.base, + textAlign: 'center', + marginTop: '12px', + color: theme.colors.textColor, + }; +}); + +export const StyledButtonWrapper = styled.div(() => { + return { + ...displayFlex('center', 'center'), + marginTop: '32px', + }; +}); + +const getButtonColors = ( + theme: AffineTheme, + confirmType: ConfirmProps['confirmType'] +) => { + switch (confirmType) { + case 'primary': + return { + background: theme.colors.primaryColor, + color: '#fff', + borderColor: theme.colors.primaryColor, + }; + case 'warning': + return { + background: theme.colors.warningBackground, + color: theme.colors.warningColor, + borderColor: theme.colors.warningBackground, + ':hover': { + borderColor: theme.colors.warningColor, + }, + }; + case 'danger': + return { + background: theme.colors.errorBackground, + color: theme.colors.errorColor, + borderColor: theme.colors.errorBackground, + ':hover': { + borderColor: theme.colors.errorColor, + }, + }; + default: + return { + color: theme.colors.popoverColor, + borderColor: theme.colors.borderColor, + ':hover': { + borderColor: theme.colors.primaryColor, + color: theme.colors.primaryColor, + }, + }; + } +}; + +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', + }, + }; + } +); diff --git a/packages/app/src/ui/modal/index.tsx b/packages/app/src/ui/modal/index.tsx index 936be1b987..0052b129f1 100644 --- a/packages/app/src/ui/modal/index.tsx +++ b/packages/app/src/ui/modal/index.tsx @@ -1,4 +1,6 @@ import Modal from './modal'; +export * from './modal-close-button'; export * from './modal'; + export default Modal; diff --git a/packages/app/src/ui/modal/modal-close-button.tsx b/packages/app/src/ui/modal/modal-close-button.tsx new file mode 100644 index 0000000000..4b1d2bad91 --- /dev/null +++ b/packages/app/src/ui/modal/modal-close-button.tsx @@ -0,0 +1,25 @@ +import { HTMLAttributes } from 'react'; +import { StyledCloseButton } from './style'; +import { CloseIcon } from '@blocksuite/icons'; + +export type ModalCloseButtonProps = { + top?: number; + right?: number; + triggerSize?: [number, number]; + size?: [number, number]; + iconSize?: [number, number]; +} & HTMLAttributes; + +export const ModalCloseButton = ({ + iconSize = [24, 24], + ...props +}: ModalCloseButtonProps) => { + const [iconWidth, iconHeight] = iconSize; + return ( + + + + ); +}; + +export default ModalCloseButton; diff --git a/packages/app/src/ui/modal/style.ts b/packages/app/src/ui/modal/style.ts index 3b37e3c0f2..999f254030 100644 --- a/packages/app/src/ui/modal/style.ts +++ b/packages/app/src/ui/modal/style.ts @@ -1,5 +1,6 @@ -import { displayFlex, fixedCenter, styled } from '@/styles'; +import { absoluteCenter, displayFlex, fixedCenter, styled } from '@/styles'; import ModalUnstyled from '@mui/base/ModalUnstyled'; +import { ModalCloseButtonProps } from '@/ui/modal/modal-close-button'; export const StyledBackdrop = styled.div<{ open?: boolean }>(({ open }) => { return { @@ -28,3 +29,40 @@ export const StyledModal = styled(ModalUnstyled)(({ theme }) => { }, }; }); + +export const StyledCloseButton = styled.button< + Pick +>(({ theme, triggerSize = [], size = [32, 32], top, right }) => { + const [triggerWidth, triggerHeight] = triggerSize; + const [width, height] = size; + + return { + width: triggerWidth ?? width * 2, + height: triggerHeight ?? height * 2, + color: theme.colors.iconColor, + cursor: 'pointer', + ...displayFlex('center', 'center'), + position: 'absolute', + top: top ?? 0, + right: right ?? 0, + + // TODO: we need to add @emotion/babel-plugin + '::after': { + content: '""', + width, + height, + borderRadius: '6px', + ...absoluteCenter({ horizontal: true, vertical: true }), + }, + ':hover': { + color: theme.colors.primaryColor, + '::after': { + background: theme.colors.hoverBackground, + }, + }, + svg: { + position: 'relative', + zIndex: 1, + }, + }; +}); diff --git a/packages/app/src/ui/table/index.ts b/packages/app/src/ui/table/index.ts new file mode 100644 index 0000000000..f9c589659b --- /dev/null +++ b/packages/app/src/ui/table/index.ts @@ -0,0 +1,12 @@ +// import Table from '@mui/material/Table'; +// import TableBody from '@mui/material/TableBody'; +import TableCell from '@mui/material/TableCell'; +// import TableHead from '@mui/material/TableHead'; +// import TableRow from '@mui/material/TableRow'; +// + +export * from './table'; +export * from './table-body'; +export * from './table-cell'; +export * from './table-head'; +export * from './table-row'; diff --git a/packages/app/src/ui/table/interface.ts b/packages/app/src/ui/table/interface.ts new file mode 100644 index 0000000000..30e8795e50 --- /dev/null +++ b/packages/app/src/ui/table/interface.ts @@ -0,0 +1,8 @@ +import { CSSProperties } from 'react'; + +export type TableCellProps = { + align?: 'left' | 'right' | 'center'; + ellipsis?: boolean; + proportion?: number; + style?: CSSProperties; +}; diff --git a/packages/app/src/ui/table/styles.ts b/packages/app/src/ui/table/styles.ts new file mode 100644 index 0000000000..39d16aa5db --- /dev/null +++ b/packages/app/src/ui/table/styles.ts @@ -0,0 +1,66 @@ +import { styled, textEllipsis } from '@/styles'; +import { TableCellProps } from './interface'; + +export const StyledTable = styled.table<{ tableLayout: 'auto' | 'fixed' }>( + ({ theme, tableLayout }) => { + return { + fontSize: theme.font.base, + color: theme.colors.textColor, + tableLayout, + width: '100%', + borderCollapse: 'separate', + borderSpacing: '0 25px', + }; + } +); + +export const StyledTableBody = styled.tbody(({ theme }) => { + return { + fontWeight: 400, + }; +}); + +export const StyledTableCell = styled.td< + Pick +>(({ theme, align = 'left', ellipsis = false, proportion }) => { + const width = proportion ? `${proportion * 100}%` : 'auto'; + return { + width, + height: '54px', + lineHeight: '54px', + padding: '0 30px', + boxSizing: 'border-box', + textAlign: align, + ...(ellipsis ? textEllipsis(1) : {}), + overflowWrap: 'break-word', + }; +}); + +export const StyledTableHead = styled.thead(({ theme }) => { + return { + fontWeight: 500, + }; +}); + +export const StyledTableRow = styled.tr(({ theme }) => { + return { + marginBottom: '25px', + td: { + transition: 'background .15s', + }, + 'td:first-child': { + borderTopLeftRadius: '10px', + borderBottomLeftRadius: '10px', + }, + 'td:last-child': { + borderTopRightRadius: '10px', + borderBottomRightRadius: '10px', + }, + + ':hover': { + td: { + background: theme.colors.hoverBackground, + }, + }, + }; +}); diff --git a/packages/app/src/ui/table/table-body.tsx b/packages/app/src/ui/table/table-body.tsx new file mode 100644 index 0000000000..2954415bf7 --- /dev/null +++ b/packages/app/src/ui/table/table-body.tsx @@ -0,0 +1,8 @@ +import { PropsWithChildren } from 'react'; +import { StyledTableBody } from '@/ui/table/styles'; + +export const TableBody = ({ children }: PropsWithChildren<{}>) => { + return {children}; +}; + +export default TableBody; diff --git a/packages/app/src/ui/table/table-cell.tsx b/packages/app/src/ui/table/table-cell.tsx new file mode 100644 index 0000000000..5dfe025a5c --- /dev/null +++ b/packages/app/src/ui/table/table-cell.tsx @@ -0,0 +1,12 @@ +import { PropsWithChildren, useLayoutEffect } from 'react'; +import { TableCellProps } from './interface'; +import { StyledTableCell } from './styles'; + +export const TableCell = ({ + children, + ...props +}: PropsWithChildren) => { + return {children}; +}; + +export default TableCell; diff --git a/packages/app/src/ui/table/table-head.tsx b/packages/app/src/ui/table/table-head.tsx new file mode 100644 index 0000000000..33e95ce6e3 --- /dev/null +++ b/packages/app/src/ui/table/table-head.tsx @@ -0,0 +1,8 @@ +import { PropsWithChildren } from 'react'; +import { StyledTableHead } from './styles'; + +export const TableHead = ({ children }: PropsWithChildren<{}>) => { + return {children}; +}; + +export default TableHead; diff --git a/packages/app/src/ui/table/table-row.tsx b/packages/app/src/ui/table/table-row.tsx new file mode 100644 index 0000000000..c421f81868 --- /dev/null +++ b/packages/app/src/ui/table/table-row.tsx @@ -0,0 +1,8 @@ +import { PropsWithChildren } from 'react'; +import { StyledTableRow } from './styles'; + +export const TableRow = ({ children }: PropsWithChildren<{}>) => { + return {children}; +}; + +export default TableRow; diff --git a/packages/app/src/ui/table/table.tsx b/packages/app/src/ui/table/table.tsx new file mode 100644 index 0000000000..47bea8362e --- /dev/null +++ b/packages/app/src/ui/table/table.tsx @@ -0,0 +1,23 @@ +import { PropsWithChildren, Children, ReactNode } from 'react'; +import { StyledTable } from './styles'; + +const childrenHasEllipsis = (children: ReactNode | ReactNode[]): boolean => { + return Children.toArray(children).some(child => { + if (typeof child === 'object' && 'props' in child) { + if (!child.props.ellipsis && child.props.children) { + return childrenHasEllipsis(child.props.children); + } + return child.props.ellipsis ?? false; + } + + return false; + }); +}; + +export const Table = ({ children }: PropsWithChildren<{}>) => { + const tableLayout = childrenHasEllipsis(children) ? 'fixed' : 'auto'; + + return {children}; +}; + +export default Table;