feat: add ui component Table & add initial workspace style

This commit is contained in:
QiShaoXuan
2022-12-02 23:50:21 +08:00
parent 94ef380d20
commit 09767c310a
25 changed files with 725 additions and 209 deletions
@@ -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 (
<>
<MenuItem
onClick={() => {
setMode(mode === 'page' ? 'edgeless' : 'page');
}}
>
<StyledMenuItemWrapper>
{mode === 'page' ? <EdgelessIcon /> : <PaperIcon />}
Convert to {mode === 'page' ? 'Edgeless' : 'Page'}
</StyledMenuItemWrapper>
</MenuItem>
<Menu
placement="left-start"
content={
<>
<MenuItem
onClick={() => {
editor && editor.contentParser.onExportHtml();
}}
>
<StyledMenuItemWrapper>
<Export2HTML />
Export to HTML
</StyledMenuItemWrapper>
</MenuItem>
<MenuItem
onClick={() => {
editor && editor.contentParser.onExportMarkdown();
}}
>
<StyledMenuItemWrapper>
<Export2Markdown />
Export to Markdown
</StyledMenuItemWrapper>
</MenuItem>
</>
}
>
<MenuItem>
<StyledMenuItemWrapper>
<ExportIcon />
Export
<RightArrow />
</StyledMenuItemWrapper>
</MenuItem>
</Menu>
</>
);
};
const BrowserWarning = ({
show,
onClose,
}: {
show: boolean;
onClose: () => void;
}) => {
return (
<StyledBrowserWarning show={show}>
{getWarningMessage()}
<StyledCloseButton onClick={onClose}>
<CloseIcon />
</StyledCloseButton>
</StyledBrowserWarning>
);
};
export const Header = ({ children }: PropsWithChildren<{}>) => {
const [showWarning, setShowWarning] = useState(shouldShowWarning());
return (
<StyledHeaderContainer hasWarning={showWarning}>
<BrowserWarning
show={showWarning}
onClose={() => {
setShowWarning(false);
}}
/>
<StyledHeader hasWarning={showWarning}>
{children}
<StyledHeaderRightSide>
<ThemeModeSwitch />
<Menu content={<PopoverContent />} placement="bottom-end">
<IconButton>
<MoreIcon />
</IconButton>
</Menu>
</StyledHeaderRightSide>
</StyledHeader>
</StyledHeaderContainer>
);
};
export default Header;
+2 -165
View File
@@ -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 (
<>
<MenuItem
onClick={() => {
setMode(mode === 'page' ? 'edgeless' : 'page');
}}
>
<StyledMenuItemWrapper>
{mode === 'page' ? <EdgelessIcon /> : <PaperIcon />}
Convert to {mode === 'page' ? 'Edgeless' : 'Page'}
</StyledMenuItemWrapper>
</MenuItem>
<Menu
placement="left-start"
content={
<>
<MenuItem
onClick={() => {
editor && editor.contentParser.onExportHtml();
}}
>
<StyledMenuItemWrapper>
<Export2HTML />
Export to HTML
</StyledMenuItemWrapper>
</MenuItem>
<MenuItem
onClick={() => {
editor && editor.contentParser.onExportMarkdown();
}}
>
<StyledMenuItemWrapper>
<Export2Markdown />
Export to Markdown
</StyledMenuItemWrapper>
</MenuItem>
</>
}
>
<MenuItem>
<StyledMenuItemWrapper>
<ExportIcon />
Export
<RightArrow />
</StyledMenuItemWrapper>
</MenuItem>
</Menu>
</>
);
};
const BrowserWarning = ({
show,
onClose,
}: {
show: boolean;
onClose: () => void;
}) => {
return (
<StyledBrowserWarning show={show}>
{getWarningMessage()}
<StyledCloseButton onClick={onClose}>
<CloseIcon />
</StyledCloseButton>
</StyledBrowserWarning>
);
};
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 (
<StyledHeaderContainer hasWarning={showWarning}>
<BrowserWarning
show={showWarning}
onClose={() => {
setShowWarning(false);
}}
/>
<StyledHeader hasWarning={showWarning}>
{/*<StyledLogo*/}
{/* onClick={() => {*/}
{/* contactModalHandler(true);*/}
{/* }}*/}
{/*>*/}
{/* <LogoIcon />*/}
{/*</StyledLogo>*/}
{title ? (
<StyledTitle
onMouseEnter={() => {
setIsHover(true);
}}
onMouseLeave={() => {
setIsHover(false);
}}
>
<EditorModeSwitch
isHover={isHover}
style={{
marginRight: '12px',
}}
/>
<StyledTitleWrapper>{title}</StyledTitleWrapper>
</StyledTitle>
) : null}
<StyledHeaderRightSide>
<ThemeModeSwitch />
<Menu content={<PopoverContent />} placement="bottom-end">
<IconButton>
<MoreIcon />
</IconButton>
</Menu>
</StyledHeaderRightSide>
</StyledHeader>
</StyledHeaderContainer>
);
};
export * from './header';
export * from './page-header';
@@ -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 (
<Header>
{title ? (
<StyledTitle
onMouseEnter={() => {
setIsHover(true);
}}
onMouseLeave={() => {
setIsHover(false);
}}
>
<EditorModeSwitch
isHover={isHover}
style={{
marginRight: '12px',
}}
/>
<StyledTitleWrapper>{title}</StyledTitleWrapper>
</StyledTitle>
) : null}
</Header>
);
};
export default PageHeader;
@@ -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 (
<>
<StyledSliderBar show={show}>
<StyledListItem>Quick search</StyledListItem>
<StyledListItem>All pages</StyledListItem>
<StyledListItem
onClick={() => {
Router.push('/all-page');
}}
>
All pages
</StyledListItem>
<StyledListItem>Favourites</StyledListItem>
<StyledSubListItem>
document 1, this is a paper icondocument 1
@@ -25,6 +29,8 @@ export const WorkSpaceSliderBar = () => {
<StyledSubListItem>document 4</StyledSubListItem>
<StyledListItem>Import</StyledListItem>
<StyledListItem>Bin</StyledListItem>
<StyledNewPageButton>New Page</StyledNewPageButton>
</StyledSliderBar>
<StyledArrowButton
isShow={show}
@@ -12,16 +12,11 @@ export const StyledSliderBar = styled.div<{ show: boolean }>(
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,
},
};
});
+12 -16
View File
@@ -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) {
<EditorProvider key="EditorProvider" />,
<ThemeProvider key="ThemeProvider" />,
<ModalProvider key="ModalProvider" />,
<ConfirmProvider key="ConfirmProvider" />,
]}
>
<StyledPage>
<WorkSpaceSliderBar />
<Component {...pageProps} />
<StyledWrapper>
<Component {...pageProps} />
</StyledWrapper>
</StyledPage>
</ProviderComposer>
</>
+61
View File
@@ -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 (
<>
<Header />
<button
onClick={() => {
confirm({
title: 'Permanently delete',
content: "Once deleted, you can't undo this action.Do you confirm?",
confirmText: 'Delete',
confirmType: 'danger',
});
}}
>
click to show confirm
</button>
<StyledTableContainer>
<Table>
<TableHead>
<TableRow>
<TableCell proportion={0.5}>Documents</TableCell>
<TableCell proportion={0.2}>Created</TableCell>
<TableCell proportion={0.2}>Uploaded</TableCell>
<TableCell proportion={0.1}></TableCell>
</TableRow>
</TableHead>
<TableBody>
{new Array(100).fill(0).map((_, index) => {
return (
<TableRow key={index}>
<TableCell ellipsis={true}>
This is a long, very long, extremely long, incredibly long,
exceedingly long, very long document title
</TableCell>
<TableCell ellipsis={true}>2022-11-02 18:30</TableCell>
<TableCell ellipsis={true}>2022-11-02 18:30</TableCell>
<TableCell>...</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
</StyledTableContainer>
</>
);
};
export default AllPage;
+4 -13
View File
@@ -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 (
<StyledWrapper>
<Header />
<>
<PageHeader />
<MobileModal />
<StyledEditorContainer>
<Editor />
</StyledEditorContainer>
<FAQ />
<EdgelessToolbar />
</StyledWrapper>
</>
);
};
@@ -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<ConfirmContextValue>({
confirm: () => {},
});
export const useConfirm = () => useContext(ConfirmContext);
export const ConfirmProvider = ({
children,
}: PropsWithChildren<ConfirmContextProps>) => {
const [confirmRecord, setConfirmRecord] = useState<Record<string, ReactNode>>(
{}
);
return (
<ConfirmContext.Provider
value={{
confirm: (props: ConfirmProps) => {
const confirmId = String(Date.now());
setConfirmRecord(oldConfirmRecord => {
return {
...oldConfirmRecord,
[confirmId]: (
<Confirm
{...props}
onClose={() => {
delete confirmRecord[confirmId];
setConfirmRecord({ ...confirmRecord });
}}
/>
),
};
});
},
}}
>
{children}
{Object.entries(confirmRecord).map(([confirmId, confirmNode]) => {
return <div key={confirmId}>{confirmNode}</div>;
})}
</ConfirmContext.Provider>
);
};
export default ConfirmProvider;
+11
View File
@@ -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:
+8
View File
@@ -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;
+66
View File
@@ -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<ModalProps, 'open' | 'children'>;
export const Confirm = ({
title,
content,
confirmText,
confirmType,
onConfirm,
onCancel,
}: ConfirmProps) => {
const [open, setOpen] = useState(true);
return (
<Modal open={open}>
<StyledModalWrapper>
<ModalCloseButton
onClick={() => {
setOpen(false);
onCancel?.();
}}
/>
<StyledConfirmTitle>{title}</StyledConfirmTitle>
<StyledConfirmContent>{content}</StyledConfirmContent>
<StyledButtonWrapper>
<StyledButton
onClick={() => {
setOpen(false);
onCancel?.();
}}
>
Cancel
</StyledButton>
<StyledButton
confirmType={confirmType}
onClick={() => {
setOpen(false);
onConfirm?.();
}}
>
{confirmText}
</StyledButton>
</StyledButtonWrapper>
</StyledModalWrapper>
</Modal>
);
};
export default Confirm;
+1
View File
@@ -0,0 +1 @@
export * from './confirm';
+98
View File
@@ -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<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',
},
};
}
);
+2
View File
@@ -1,4 +1,6 @@
import Modal from './modal';
export * from './modal-close-button';
export * from './modal';
export default Modal;
@@ -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<HTMLButtonElement>;
export const ModalCloseButton = ({
iconSize = [24, 24],
...props
}: ModalCloseButtonProps) => {
const [iconWidth, iconHeight] = iconSize;
return (
<StyledCloseButton {...props}>
<CloseIcon width={iconWidth} height={iconHeight} />
</StyledCloseButton>
);
};
export default ModalCloseButton;
+39 -1
View File
@@ -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<ModalCloseButtonProps, 'size' | 'triggerSize' | 'top' | 'right'>
>(({ 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,
},
};
});
+12
View File
@@ -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';
+8
View File
@@ -0,0 +1,8 @@
import { CSSProperties } from 'react';
export type TableCellProps = {
align?: 'left' | 'right' | 'center';
ellipsis?: boolean;
proportion?: number;
style?: CSSProperties;
};
+66
View File
@@ -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<TableCellProps, 'ellipsis' | 'align' | 'proportion'>
>(({ 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,
},
},
};
});
+8
View File
@@ -0,0 +1,8 @@
import { PropsWithChildren } from 'react';
import { StyledTableBody } from '@/ui/table/styles';
export const TableBody = ({ children }: PropsWithChildren<{}>) => {
return <StyledTableBody>{children}</StyledTableBody>;
};
export default TableBody;
+12
View File
@@ -0,0 +1,12 @@
import { PropsWithChildren, useLayoutEffect } from 'react';
import { TableCellProps } from './interface';
import { StyledTableCell } from './styles';
export const TableCell = ({
children,
...props
}: PropsWithChildren<TableCellProps>) => {
return <StyledTableCell {...props}>{children}</StyledTableCell>;
};
export default TableCell;
+8
View File
@@ -0,0 +1,8 @@
import { PropsWithChildren } from 'react';
import { StyledTableHead } from './styles';
export const TableHead = ({ children }: PropsWithChildren<{}>) => {
return <StyledTableHead>{children}</StyledTableHead>;
};
export default TableHead;
+8
View File
@@ -0,0 +1,8 @@
import { PropsWithChildren } from 'react';
import { StyledTableRow } from './styles';
export const TableRow = ({ children }: PropsWithChildren<{}>) => {
return <StyledTableRow>{children}</StyledTableRow>;
};
export default TableRow;
+23
View File
@@ -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 <StyledTable tableLayout={tableLayout}>{children}</StyledTable>;
};
export default Table;