feat: replace modal with new design (#4324)

Co-authored-by: Peng Xiao <pengxiao@outlook.com>
This commit is contained in:
Qi
2023-09-13 16:05:19 +08:00
committed by GitHub
parent 49e0172316
commit 0b1ba6bf43
58 changed files with 637 additions and 1404 deletions
@@ -1,110 +0,0 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button } from '@toeverything/components/button';
import { useCallback, useMemo } from 'react';
import type { ModalProps } from '../modal';
import { Modal, ModalCloseButton } from '../modal';
import {
StyledColumnButtonWrapper,
StyledConfirmContent,
StyledConfirmTitle,
StyledModalWrapper,
StyledRowButtonWrapper,
} from './styles';
export type ConfirmProps = {
title?: string;
content?: string;
confirmText?: string;
cancelText?: string;
// TODO: Confirm button's color should depend on confirm type
confirmType?: 'primary' | 'warning' | 'error';
buttonDirection?: 'row' | 'column';
onConfirm?: () => void;
onCancel?: () => void;
cancelButtonTestId?: string;
confirmButtonTestId?: string;
} & Omit<ModalProps, 'children'>;
export const Confirm = ({
title,
content,
confirmText,
confirmType,
onConfirm,
onCancel,
buttonDirection = 'row',
cancelText = 'Cancel',
open,
cancelButtonTestId = '',
confirmButtonTestId = '',
}: ConfirmProps) => {
const t = useAFFiNEI18N();
const cancelText_ = useMemo<string>(() => {
return cancelText === 'Cancel'
? t['com.affine.confirmModal.button.cancel']()
: cancelText;
}, [cancelText, t]);
const handleCancel = useCallback(() => {
onCancel?.();
}, [onCancel]);
const handleConfirm = useCallback(() => {
onConfirm?.();
}, [onConfirm]);
return (
<Modal open={open} disablePortal={false}>
<StyledModalWrapper>
<ModalCloseButton onClick={handleCancel} />
<StyledConfirmTitle>{title}</StyledConfirmTitle>
<StyledConfirmContent>{content}</StyledConfirmContent>
{buttonDirection === 'row' ? (
<StyledRowButtonWrapper>
<Button
onClick={handleCancel}
size="large"
style={{ marginRight: '24px' }}
data-testid={cancelButtonTestId}
>
{cancelText_}
</Button>
<Button
type={confirmType}
onClick={handleConfirm}
size="large"
data-testid={confirmButtonTestId}
>
{confirmText}
</Button>
</StyledRowButtonWrapper>
) : (
<StyledColumnButtonWrapper>
<Button
type={confirmType}
onClick={handleConfirm}
style={{ width: '284px', height: '38px', textAlign: 'center' }}
data-testid={confirmButtonTestId}
>
{confirmText}
</Button>
<Button
onClick={handleCancel}
style={{
marginTop: '16px',
width: '284px',
height: '38px',
textAlign: 'center',
}}
data-testid={cancelButtonTestId}
>
{cancelText_}
</Button>
</StyledColumnButtonWrapper>
)}
</StyledModalWrapper>
</Modal>
);
};
export default Confirm;
@@ -1 +0,0 @@
export * from './confirm';
@@ -1,46 +0,0 @@
import { displayFlex, styled } from '../../styles';
import { ModalWrapper } from '../modal';
export const StyledModalWrapper = styled(ModalWrapper)(() => {
return {
minWidth: '460px',
maxWidth: '560px',
maxHeight: '292px',
padding: '44px 84px 32px 84px',
overflow: 'auto',
};
});
export const StyledConfirmTitle = styled('div')(() => {
return {
fontSize: 'var(--affine-font-h6)',
fontWeight: 600,
textAlign: 'center',
lineHeight: '28px',
};
});
export const StyledConfirmContent = styled('div')(() => {
return {
fontSize: 'var(--affine-font-base)',
textAlign: 'center',
marginTop: '12px',
color: 'var(--affine-text-primary-color)',
lineHeight: '26px',
};
});
export const StyledColumnButtonWrapper = styled('div')(() => {
return {
...displayFlex('center', 'center'),
flexDirection: 'column',
marginTop: '32px',
};
});
export const StyledRowButtonWrapper = styled('div')(() => {
return {
...displayFlex('center', 'center'),
flexDirection: 'row',
marginTop: '32px',
};
});
@@ -1,72 +0,0 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button, type ButtonType } from '@toeverything/components/button';
import { useCallback } from 'react';
import { Modal, type ModalProps } from './modal';
import { ModalCloseButton } from './modal-close-button';
import { ModalWrapper } from './modal-wrapper';
import {
StyledModalContent,
StyledModalFooter,
StyledModalTitle,
} from './styles';
export interface BaseModalProps
extends Omit<ModalProps, 'onClose' | 'children'> {
title?: string;
content?: string;
confirmText?: string;
confirmType?: ButtonType;
onClose: () => void;
onCancel: () => void;
onConfirm: () => void;
}
export const ConfirmModal = ({
open,
onClose,
confirmText,
confirmType = 'primary',
onCancel,
onConfirm,
title,
content,
}: BaseModalProps) => {
const t = useAFFiNEI18N();
const handleClose = useCallback(() => {
onClose();
}, [onClose]);
return (
<Modal
open={open}
onClose={handleClose}
wrapperPosition={['center', 'center']}
data-testid="auth-modal"
>
<ModalWrapper
width={480}
minHeight={194}
style={{
overflow: 'hidden',
backgroundColor: 'var(--affine-white)',
boxShadow: 'var(--affine-popover-shadow)',
padding: '20px 24px 0',
}}
>
<ModalCloseButton top={22} right={20} onClick={handleClose} />
<StyledModalTitle>{title}</StyledModalTitle>
<StyledModalContent>{content}</StyledModalContent>
<StyledModalFooter>
<Button onClick={onCancel} style={{ marginRight: 20 }}>
{t['com.affine.confirmModal.button.cancel']()}
</Button>
<Button type={confirmType} onClick={onConfirm}>
{confirmText || t['Confirm']()}
</Button>
</StyledModalFooter>
</ModalWrapper>
</Modal>
);
};
@@ -1,8 +0,0 @@
import Modal from './modal';
export * from './confirm-modal';
export * from './modal';
export * from './modal-close-button';
export * from './modal-wrapper';
export default Modal;
@@ -1,39 +0,0 @@
import { CloseIcon } from '@blocksuite/icons';
import {
IconButton,
type IconButtonProps,
} from '@toeverything/components/button';
import type { HTMLAttributes } from 'react';
export type ModalCloseButtonProps = {
top?: number;
right?: number;
absolute?: boolean;
} & Omit<IconButtonProps, 'children'> &
HTMLAttributes<HTMLButtonElement>;
export const ModalCloseButton = ({
absolute = true,
right,
top,
...props
}: ModalCloseButtonProps) => {
return (
<IconButton
style={
absolute
? {
position: 'absolute',
top: top ?? 24,
right: right ?? 40,
zIndex: 1,
}
: {}
}
data-testid="modal-close-button"
{...props}
>
<CloseIcon />
</IconButton>
);
};
@@ -1,22 +0,0 @@
import type { CSSProperties } from 'react';
import { styled } from '../../styles';
export const ModalWrapper = styled('div')<{
width?: CSSProperties['width'];
height?: CSSProperties['height'];
minHeight?: CSSProperties['minHeight'];
}>(({ width, height, minHeight }) => {
return {
width,
height,
minHeight,
backgroundColor: 'var(--affine-background-overlay-panel-color)',
boxShadow: 'var(--affine-shadow-3)',
borderRadius: '12px',
position: 'relative',
maxHeight: 'calc(100vh - 32px)',
};
});
export default ModalWrapper;
-59
View File
@@ -1,59 +0,0 @@
import type { ModalProps as ModalUnstyledOwnProps } from '@mui/base/Modal';
import Fade from '@mui/material/Fade';
import { StyledBackdrop, StyledModal } from './styles';
const Backdrop = ({
open,
...other
}: {
open?: boolean;
className: string;
}) => {
return (
<Fade in={open}>
<StyledBackdrop {...other} />
</Fade>
);
};
export type ModalProps = {
wrapperPosition?: ['top' | 'bottom' | 'center', 'left' | 'right' | 'center'];
} & ModalUnstyledOwnProps;
const transformConfig = {
top: 'flex-start',
bottom: 'flex-end',
center: 'center',
left: 'flex-start',
right: 'flex-end',
};
export const Modal = (props: ModalProps) => {
const {
wrapperPosition = ['center', 'center'],
open,
children,
...otherProps
} = props;
const [vertical, horizontal] = wrapperPosition;
// Fixme: This is a workaround for Mui bug
// Refs: https://github.com/mui/material-ui/issues/33748
if (!open) {
return null;
}
return (
<StyledModal
{...otherProps}
open={open}
slots={{ backdrop: Backdrop }}
alignItems={transformConfig[vertical]}
justifyContent={transformConfig[horizontal]}
disableEnforceFocus
>
<Fade in={open}>{children}</Fade>
</StyledModal>
);
};
export default Modal;
-65
View File
@@ -1,65 +0,0 @@
import { Modal as ModalUnstyled } from '@mui/base/Modal';
import type { CSSProperties } from 'react';
import { styled } from '../../styles';
export const StyledBackdrop = styled('div')(() => {
return {
zIndex: '-1',
position: 'fixed',
right: '0',
bottom: '0',
top: '0',
left: '0',
backgroundColor: 'var(--affine-background-modal-color)',
};
});
export const StyledModal = styled(ModalUnstyled, {
shouldForwardProp: prop => {
return !['justifyContent', 'alignItems'].includes(prop as string);
},
})<{
alignItems: CSSProperties['alignItems'];
justifyContent: CSSProperties['justifyContent'];
}>(({ alignItems, justifyContent }) => {
return {
width: '100vw',
height: '100vh',
display: 'flex',
alignItems,
justifyContent,
position: 'fixed',
left: '0',
top: '0',
zIndex: 'var(--affine-z-index-modal)',
WebkitAppRegion: 'no-drag',
'*': {
WebkitTapHighlightColor: 'transparent',
outline: 'none',
},
};
});
export const StyledModalFooter = styled('div')(() => {
return {
marginTop: 40,
display: 'flex',
justifyContent: 'flex-end',
alignItems: 'center',
};
});
export const StyledModalTitle = styled('div')(() => {
return {
fontWeight: 600,
fontSize: 'var(--affine-font-h-6)',
};
});
export const StyledModalContent = styled('div')(() => {
return {
fontSize: 'var(--affine-font-base)',
lineHeight: '24px',
marginTop: '12px',
};
});