mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-17 01:56:27 +08:00
feat: add ui component Table & add initial workspace style
This commit is contained in:
@@ -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;
|
||||
@@ -0,0 +1 @@
|
||||
export * from './confirm';
|
||||
@@ -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',
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
@@ -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;
|
||||
@@ -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,
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
@@ -0,0 +1,8 @@
|
||||
import { CSSProperties } from 'react';
|
||||
|
||||
export type TableCellProps = {
|
||||
align?: 'left' | 'right' | 'center';
|
||||
ellipsis?: boolean;
|
||||
proportion?: number;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
@@ -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,
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -0,0 +1,8 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { StyledTableHead } from './styles';
|
||||
|
||||
export const TableHead = ({ children }: PropsWithChildren<{}>) => {
|
||||
return <StyledTableHead>{children}</StyledTableHead>;
|
||||
};
|
||||
|
||||
export default TableHead;
|
||||
@@ -0,0 +1,8 @@
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { StyledTableRow } from './styles';
|
||||
|
||||
export const TableRow = ({ children }: PropsWithChildren<{}>) => {
|
||||
return <StyledTableRow>{children}</StyledTableRow>;
|
||||
};
|
||||
|
||||
export default TableRow;
|
||||
@@ -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;
|
||||
Reference in New Issue
Block a user