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
@@ -6,7 +6,7 @@ import { Link } from 'react-router-dom';
import * as styles from './index.css';
export interface MenuItemProps extends React.HTMLAttributes<HTMLButtonElement> {
export interface MenuItemProps extends React.HTMLAttributes<HTMLDivElement> {
icon?: React.ReactElement;
active?: boolean;
disabled?: boolean;
@@ -25,7 +25,7 @@ const stopPropagation: React.MouseEventHandler = e => {
e.stopPropagation();
};
export const MenuItem = React.forwardRef<HTMLButtonElement, MenuItemProps>(
export const MenuItem = React.forwardRef<HTMLDivElement, MenuItemProps>(
(
{
onClick,
@@ -42,7 +42,7 @@ export const MenuItem = React.forwardRef<HTMLButtonElement, MenuItemProps>(
) => {
const collapsible = onCollapsedChange !== undefined;
return (
<button
<div
ref={ref}
{...props}
onClick={onClick}
@@ -82,22 +82,21 @@ export const MenuItem = React.forwardRef<HTMLButtonElement, MenuItemProps>(
{postfix}
</div>
) : null}
</button>
</div>
);
}
);
MenuItem.displayName = 'MenuItem';
export const MenuLinkItem = React.forwardRef<
HTMLButtonElement,
MenuLinkItemProps
>(({ to, ...props }, ref) => {
return (
<Link to={to} className={styles.linkItemRoot}>
{/* The <a> element rendered by Link does not generate display box due to `display: contents` style */}
{/* Thus ref is passed to MenuItem instead of Link */}
<MenuItem ref={ref} {...props}></MenuItem>
</Link>
);
});
export const MenuLinkItem = React.forwardRef<HTMLDivElement, MenuLinkItemProps>(
({ to, ...props }, ref) => {
return (
<Link to={to} className={styles.linkItemRoot}>
{/* The <a> element rendered by Link does not generate display box due to `display: contents` style */}
{/* Thus ref is passed to MenuItem instead of Link */}
<MenuItem ref={ref} {...props}></MenuItem>
</Link>
);
}
);
MenuLinkItem.displayName = 'MenuLinkItem';
@@ -1,5 +1,5 @@
import clsx from 'clsx';
import type { FC } from 'react';
import type { FC, HTMLAttributes } from 'react';
import { Input, type InputProps } from '../../ui/input';
import { authInputWrapper, formHint } from './share.css';
@@ -9,6 +9,7 @@ export type AuthInputProps = InputProps & {
errorHint?: string;
withoutHint?: boolean;
onEnter?: () => void;
wrapperProps?: HTMLAttributes<HTMLDivElement>;
};
export const AuthInput: FC<AuthInputProps> = ({
label,
@@ -16,13 +17,15 @@ export const AuthInput: FC<AuthInputProps> = ({
errorHint,
withoutHint = false,
onEnter,
wrapperProps: { className, ...otherWrapperProps } = {},
...inputProps
}) => {
return (
<div
className={clsx(authInputWrapper, {
className={clsx(authInputWrapper, className, {
'without-hint': withoutHint,
})}
{...otherWrapperProps}
>
{label ? <label>{label}</label> : null}
<Input
@@ -1,6 +1,5 @@
import { Modal, ModalCloseButton, ModalWrapper } from '@affine/component';
import { Modal } from '@toeverything/components/modal';
import type { FC, PropsWithChildren } from 'react';
import { useCallback } from 'react';
export type AuthModalProps = {
open: boolean;
@@ -12,32 +11,18 @@ export const AuthModal: FC<PropsWithChildren<AuthModalProps>> = ({
open,
setOpen,
}) => {
const handleClose = useCallback(() => {
setOpen(false);
}, [setOpen]);
return (
<Modal
open={open}
onClose={handleClose}
wrapperPosition={['center', 'center']}
data-testid="auth-modal"
onOpenChange={setOpen}
width={400}
height={468}
contentOptions={{
['data-testid' as string]: 'auth-modal',
style: { padding: '44px 40px 0' },
}}
>
<ModalWrapper
width={1080}
height={760}
style={{
height: '468px',
width: '400px',
overflow: 'hidden',
backgroundColor: 'var(--affine-white)',
boxShadow: 'var(--affine-popover-shadow)',
padding: '44px 40px 0',
}}
>
<ModalCloseButton top={20} right={20} onClick={handleClose} />
{children}
</ModalWrapper>
{children}
</Modal>
);
};
@@ -1,13 +1,14 @@
import {
CloseIcon,
ExportToHtmlIcon,
ExportToMarkdownIcon,
HelpIcon,
NewIcon,
NotionIcon,
} from '@blocksuite/icons';
import { IconButton } from '@toeverything/components/button';
import { Tooltip } from '@toeverything/components/tooltip';
import { ModalCloseButton } from '../../ui/modal';
import { BlockCard } from '../card/block-card';
import {
importPageBodyStyle,
@@ -27,7 +28,17 @@ export const ImportPage = ({
onClose: () => void;
}) => (
<div className={importPageContainerStyle}>
<ModalCloseButton top={6} right={6} onClick={onClose} />
<IconButton
style={{
position: 'absolute',
right: 6,
top: 6,
}}
onClick={() => {
onClose();
}}
icon={<CloseIcon />}
/>
<div className={importPageBodyStyle}>
<div className="title">Import</div>
<span>
@@ -1,12 +1,10 @@
import { Permission } from '@affine/graphql';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Button } from '@toeverything/components/button';
import { ConfirmModal } from '@toeverything/components/modal';
import { useCallback, useEffect, useState } from 'react';
import { Modal, ModalCloseButton, ModalWrapper } from '../../ui/modal';
import { AuthInput } from '..//auth-components';
import { emailRegex } from '..//auth-components/utils';
import * as styles from './styles.css';
export interface InviteModalProps {
open: boolean;
@@ -26,10 +24,6 @@ export const InviteModal = ({
const [permission] = useState(Permission.Write);
const [isValidEmail, setIsValidEmail] = useState(true);
const handleCancel = useCallback(() => {
setOpen(false);
}, [setOpen]);
const handleConfirm = useCallback(() => {
if (!emailRegex.test(inviteEmail)) {
setIsValidEmail(false);
@@ -51,47 +45,41 @@ export const InviteModal = ({
}, [open]);
return (
<Modal
wrapperPosition={['center', 'center']}
data-testid="invite-modal"
<ConfirmModal
open={open}
>
<ModalWrapper
width={480}
height={254}
style={{
onOpenChange={setOpen}
width={480}
title={t['Invite Members']()}
description={t['Invite Members Message']()}
cancelText={t['com.affine.inviteModal.button.cancel']()}
contentOptions={{
['data-testid' as string]: 'invite-modal',
style: {
padding: '20px 26px',
},
}}
confirmButtonOptions={{
loading: isMutating,
type: 'primary',
['data-testid' as string]: 'confirm-enable-affine-cloud-button',
children: t['Invite'](),
}}
onConfirm={handleConfirm}
>
{/*TODO: check email & add placeholder*/}
<AuthInput
disabled={isMutating}
placeholder="email@example.com"
value={inviteEmail}
onChange={setInviteEmail}
error={!isValidEmail}
errorHint={isValidEmail ? '' : t['com.affine.auth.sign.email.error']()}
onEnter={handleConfirm}
wrapperProps={{
style: { padding: 0 },
}}
>
<ModalCloseButton top={20} right={20} onClick={handleCancel} />
<div className={styles.inviteModalTitle}>{t['Invite Members']()}</div>
<div className={styles.inviteModalContent}>
{t['Invite Members Message']()}
{/*TODO: check email & add placeholder*/}
<AuthInput
disabled={isMutating}
placeholder="email@example.com"
value={inviteEmail}
onChange={setInviteEmail}
error={!isValidEmail}
errorHint={
isValidEmail ? '' : t['com.affine.auth.sign.email.error']()
}
onEnter={handleConfirm}
style={{ marginTop: 20 }}
size="large"
/>
</div>
<div className={styles.inviteModalButtonContainer}>
<Button style={{ marginRight: 20 }} onClick={handleCancel}>
{t['com.affine.inviteModal.button.cancel']()}
</Button>
<Button type="primary" onClick={handleConfirm} loading={isMutating}>
{t['Invite']()}
</Button>
</div>
</ModalWrapper>
</Modal>
size="large"
/>
</ConfirmModal>
);
};
@@ -10,10 +10,11 @@ import {
} from '@blocksuite/icons';
import { IconButton } from '@toeverything/components/button';
import { Menu, MenuIcon, MenuItem } from '@toeverything/components/menu';
import { ConfirmModal } from '@toeverything/components/modal';
import { Tooltip } from '@toeverything/components/tooltip';
import { useState } from 'react';
import { Confirm, FlexWrapper } from '../../..';
import { FlexWrapper } from '../../..';
import { DisablePublicSharing, MoveToTrash } from './operation-menu-items';
export interface OperationCellProps {
@@ -106,19 +107,12 @@ export const OperationCell = ({
onRemoveToTrash();
setOpen(false);
}}
onClose={() => {
setOpen(false);
}}
onCancel={() => {
setOpen(false);
}}
onOpenChange={setOpen}
/>
<DisablePublicSharing.DisablePublicSharingModal
onConfirmDisable={onDisablePublicSharing}
onConfirm={onDisablePublicSharing}
open={openDisableShared}
onClose={() => {
setOpenDisableShared(false);
}}
onOpenChange={setOpenDisableShared}
/>
</>
);
@@ -161,22 +155,20 @@ export const TrashOperationCell = ({
<DeletePermanentlyIcon />
</IconButton>
</Tooltip>
<Confirm
<ConfirmModal
title={`${t['com.affine.trashOperation.deletePermanently']()}?`}
content={t['com.affine.trashOperation.deleteDescription']()}
confirmText={t['com.affine.trashOperation.delete']()}
confirmType="error"
description={t['com.affine.trashOperation.deleteDescription']()}
cancelText={t['com.affine.confirmModal.button.cancel']()}
confirmButtonOptions={{
type: 'error',
children: t['com.affine.trashOperation.delete'](),
}}
open={open}
onOpenChange={setOpen}
onConfirm={() => {
onPermanentlyDeletePage();
setOpen(false);
}}
onClose={() => {
setOpen(false);
}}
onCancel={() => {
setOpen(false);
}}
/>
</FlexWrapper>
);
@@ -5,9 +5,10 @@ import {
MenuItem,
type MenuItemProps,
} from '@toeverything/components/menu';
import type { ConfirmProps } from '../../..';
import { Confirm } from '../../..';
import {
ConfirmModal,
type ConfirmModalProps,
} from '@toeverything/components/modal';
export const MoveToTrash = (props: MenuItemProps) => {
const t = useAFFiNEI18N();
@@ -27,26 +28,29 @@ export const MoveToTrash = (props: MenuItemProps) => {
);
};
const ConfirmModal = ({
const MoveToTrashConfirm = ({
title,
...confirmModalProps
}: {
title: string;
} & ConfirmProps) => {
} & ConfirmModalProps) => {
const t = useAFFiNEI18N();
return (
<Confirm
<ConfirmModal
title={t['com.affine.moveToTrash.confirmModal.title']()}
content={t['com.affine.moveToTrash.confirmModal.description']({
description={t['com.affine.moveToTrash.confirmModal.description']({
title: title || 'Untitled',
})}
confirmButtonTestId="confirm-delete-page"
confirmText={t.Delete()}
confirmType="error"
cancelText={t['com.affine.confirmModal.button.cancel']()}
confirmButtonOptions={{
['data-testid' as string]: 'confirm-delete-page',
type: 'error',
children: t.Delete(),
}}
{...confirmModalProps}
/>
);
};
MoveToTrash.ConfirmModal = ConfirmModal;
MoveToTrash.ConfirmModal = MoveToTrashConfirm;
@@ -5,14 +5,14 @@ import { ViewLayersIcon } from '@blocksuite/icons';
import { Button } from '@toeverything/components/button';
import { Tooltip } from '@toeverything/components/tooltip';
import clsx from 'clsx';
import { useCallback, useState } from 'react';
import { useState } from 'react';
import {
type CollectionsAtom,
useCollectionManager,
} from '../use-collection-manager';
import * as styles from './collection-bar.css';
import { EditCollectionModel } from './create-collection';
import { EditCollectionModal } from './create-collection';
import { useActions } from './use-action';
interface CollectionBarProps {
@@ -33,21 +33,20 @@ export const CollectionBar = (props: CollectionBarProps) => {
setting,
openEdit: () => setOpen(true),
});
const onClose = useCallback(() => setOpen(false), []);
return !setting.isDefault ? (
<tr style={{ userSelect: 'none' }}>
<td>
<div className={styles.view}>
<EditCollectionModel
<EditCollectionModal
propertiesMeta={propertiesMeta}
getPageInfo={getPageInfo}
init={collection}
open={open}
onClose={onClose}
onOpenChange={setOpen}
// eslint-disable-next-line @typescript-eslint/no-misused-promises
onConfirm={setting.updateCollection}
></EditCollectionModel>
/>
<ViewLayersIcon
style={{
height: 20,
@@ -14,7 +14,7 @@ import { useCallback, useRef, useState } from 'react';
import { CreateFilterMenu } from '../filter/vars';
import type { useCollectionManager } from '../use-collection-manager';
import * as styles from './collection-list.css';
import { EditCollectionModel } from './create-collection';
import { EditCollectionModal } from './create-collection';
import { useActions } from './use-action';
const CollectionOption = ({
@@ -126,14 +126,16 @@ export const CollectionList = ({
},
[setting]
);
const closeUpdateCollectionModal = useCallback(
() => setCollection(undefined),
[]
);
const closeUpdateCollectionModal = useCallback((open: boolean) => {
if (!open) {
setCollection(undefined);
}
}, []);
const onConfirm = useCallback(
async (view: Collection) => {
await setting.updateCollection(view);
closeUpdateCollectionModal();
closeUpdateCollectionModal(false);
},
[closeUpdateCollectionModal, setting]
);
@@ -214,14 +216,14 @@ export const CollectionList = ({
{t['com.affine.filter']()}
</Button>
</Menu>
<EditCollectionModel
<EditCollectionModal
propertiesMeta={propertiesMeta}
getPageInfo={getPageInfo}
init={collection}
open={!!collection}
onClose={closeUpdateCollectionModal}
onOpenChange={closeUpdateCollectionModal}
onConfirm={onConfirm}
></EditCollectionModel>
/>
</FlexWrapper>
);
};
@@ -10,72 +10,69 @@ import {
} from '@blocksuite/icons';
import { uuidv4 } from '@blocksuite/store';
import { Button } from '@toeverything/components/button';
import { Modal } from '@toeverything/components/modal';
import { useCallback, useMemo, useState } from 'react';
import {
Input,
Modal,
ModalCloseButton,
ModalWrapper,
ScrollableContainer,
} from '../../..';
import { Input, ScrollableContainer } from '../../..';
import { FilterList } from '../filter';
import * as styles from './collection-list.css';
interface EditCollectionModelProps {
interface EditCollectionModalProps {
init?: Collection;
title?: string;
open: boolean;
getPageInfo: GetPageInfoById;
propertiesMeta: PropertiesMeta;
onClose: () => void;
onOpenChange: (open: boolean) => void;
onConfirm: (view: Collection) => Promise<void>;
}
export const EditCollectionModel = ({
export const EditCollectionModal = ({
init,
onConfirm,
open,
onClose,
onOpenChange,
getPageInfo,
propertiesMeta,
title,
}: EditCollectionModelProps) => {
}: EditCollectionModalProps) => {
const t = useAFFiNEI18N();
const onConfirmOnCollection = useCallback(
(view: Collection) => {
onConfirm(view)
.then(() => {
onClose();
onOpenChange(false);
})
.catch(err => {
console.error(err);
});
},
[onClose, onConfirm]
[onConfirm, onOpenChange]
);
const onCancel = useCallback(() => {
onOpenChange(false);
}, [onOpenChange]);
return (
<Modal open={open} onClose={onClose}>
<ModalWrapper
width={600}
style={{
padding: '40px',
background: 'var(--affine-background-primary-color)',
}}
>
<ModalCloseButton top={12} right={12} onClick={onClose} />
{init ? (
<EditCollection
propertiesMeta={propertiesMeta}
title={title}
onConfirmText={t['com.affine.editCollection.save']()}
init={init}
getPageInfo={getPageInfo}
onCancel={onClose}
onConfirm={onConfirmOnCollection}
/>
) : null}
</ModalWrapper>
<Modal
open={open}
onOpenChange={onOpenChange}
width={600}
contentOptions={{
style: { padding: '40px' },
}}
>
{init ? (
<EditCollection
propertiesMeta={propertiesMeta}
title={title}
onConfirmText={t['com.affine.editCollection.save']()}
init={init}
getPageInfo={getPageInfo}
onCancel={onCancel}
onConfirm={onConfirmOnCollection}
/>
) : null}
</Modal>
);
};
@@ -301,14 +298,14 @@ export const SaveCollectionButton = ({
>
{t['com.affine.editCollection.saveCollection']()}
</Button>
<EditCollectionModel
<EditCollectionModal
title={t['com.affine.editCollection.saveCollection']()}
propertiesMeta={propertiesMeta}
init={init}
onConfirm={onConfirm}
open={show}
getPageInfo={getPageInfo}
onClose={() => changeShow(false)}
onOpenChange={changeShow}
/>
</>
);
@@ -1,4 +1,3 @@
export { SettingModal, type SettingModalProps } from './modal';
export { SettingHeader } from './setting-header';
export { SettingRow } from './setting-row';
export * from './storage-progess';
@@ -1,43 +0,0 @@
import { Modal, ModalCloseButton, ModalWrapper } from '@affine/component';
import type { PropsWithChildren } from 'react';
import { useCallback } from 'react';
export interface SettingModalProps {
open: boolean;
setOpen: (value: boolean) => void;
}
export const SettingModal = ({
children,
open,
setOpen,
}: PropsWithChildren<SettingModalProps>) => {
const handleClose = useCallback(() => {
setOpen(false);
}, [setOpen]);
return (
<Modal
open={open}
onClose={handleClose}
wrapperPosition={['center', 'center']}
data-testid="setting-modal"
>
<ModalWrapper
width={1080}
height={760}
style={{
maxHeight: '85vh',
maxWidth: '70vw',
overflow: 'hidden',
display: 'flex',
backgroundColor: 'var(--affine-background-overlay-panel-color)',
boxShadow: 'var(--affine-popover-shadow)',
}}
>
<ModalCloseButton top={16} right={20} onClick={handleClose} />
{children}
</ModalWrapper>
</Modal>
);
};
@@ -1,57 +1,24 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { CloseIcon } from '@blocksuite/icons';
import { Button, IconButton } from '@toeverything/components/button';
import {
ConfirmModal,
type ConfirmModalProps,
} from '@toeverything/components/modal';
import { Modal, ModalWrapper } from '../../..';
import { ButtonContainer, Content, Header, StyleTips, Title } from './style';
export type PublicLinkDisableProps = {
open: boolean;
onConfirmDisable: () => void;
onClose: () => void;
};
export const PublicLinkDisableModal = ({
open,
onConfirmDisable,
onClose,
}: PublicLinkDisableProps) => {
export const PublicLinkDisableModal = (props: ConfirmModalProps) => {
const t = useAFFiNEI18N();
return (
<Modal open={open} onClose={onClose}>
<ModalWrapper width={480}>
<Header>
<Title>{t['com.affine.publicLinkDisableModal.title']()}</Title>
<IconButton onClick={onClose}>
<CloseIcon />
</IconButton>
</Header>
<Content>
<StyleTips>
{t['com.affine.publicLinkDisableModal.description']()}
</StyleTips>
<ButtonContainer>
<div>
<Button onClick={onClose} block>
{t['com.affine.publicLinkDisableModal.button.cancel']()}
</Button>
</div>
<div>
<Button
data-testid="confirm-enable-affine-cloud-button"
type="error"
block
onClick={() => {
onConfirmDisable();
onClose();
}}
>
{t['com.affine.publicLinkDisableModal.button.disable']()}
</Button>
</div>
</ButtonContainer>
</Content>
</ModalWrapper>
</Modal>
<ConfirmModal
width={480}
title={t['com.affine.publicLinkDisableModal.title']()}
description={t['com.affine.publicLinkDisableModal.description']()}
cancelText={t['com.affine.publicLinkDisableModal.button.cancel']()}
confirmButtonOptions={{
type: 'error',
['data-testid' as string]: 'confirm-enable-affine-cloud-button',
children: t['com.affine.publicLinkDisableModal.button.disable'](),
}}
{...props}
/>
);
};
@@ -1,35 +0,0 @@
import { styled } from '../../..';
export const Header = styled('div')({
display: 'flex',
justifyContent: 'space-between',
paddingRight: '20px',
paddingTop: '20px',
paddingLeft: '24px',
alignItems: 'center',
});
export const Content = styled('div')({
padding: '12px 24px 20px 24px',
});
export const Title = styled('div')({
fontSize: 'var(--affine-font-h6)',
lineHeight: '26px',
fontWeight: 600,
});
export const StyleTips = styled('div')(() => {
return {
userSelect: 'none',
marginBottom: '20px',
};
});
export const ButtonContainer = styled('div')(() => {
return {
display: 'flex',
justifyContent: 'flex-end',
gap: '20px',
paddingTop: '20px',
};
});
@@ -208,7 +208,10 @@ export const AffineSharePage = (props: ShareMenuProps) => {
block
type="danger"
className={styles.menuItemStyle}
onClick={() => setShowDisable(true)}
onSelect={e => {
e.preventDefault();
setShowDisable(true);
}}
>
<div className={styles.disableSharePage}>
{t['Disable Public Link']()}
@@ -216,10 +219,8 @@ export const AffineSharePage = (props: ShareMenuProps) => {
</MenuItem>
<PublicLinkDisableModal
open={showDisable}
onConfirmDisable={onDisablePublic}
onClose={() => {
setShowDisable(false);
}}
onConfirm={onDisablePublic}
onOpenChange={setShowDisable}
/>
</>
) : null}
@@ -231,7 +232,11 @@ export const SharePage = (props: ShareMenuProps) => {
if (props.workspace.flavour === WorkspaceFlavour.LOCAL) {
return <LocalSharePage {...props} />;
} else if (props.workspace.flavour === WorkspaceFlavour.AFFINE_CLOUD) {
return <AffineSharePage {...props} />;
return (
<>
<AffineSharePage {...props} />
</>
);
}
throw new Error('Unreachable');
};
@@ -5,7 +5,6 @@ export const modalStyle = style({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
position: 'relative',
backgroundColor: 'var(--affine-background-secondary-color)',
borderRadius: '16px',
overflow: 'hidden',
@@ -1,9 +1,9 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowLeftSmallIcon, ArrowRightSmallIcon } from '@blocksuite/icons';
import { Modal, type ModalProps } from '@toeverything/components/modal';
import clsx from 'clsx';
import { useState } from 'react';
import { Modal, ModalCloseButton, ModalWrapper } from '../..';
import {
arrowStyle,
buttonDisableStyle,
@@ -25,140 +25,133 @@ import {
videoStyle,
} from './index.css';
export interface TourModalProps {
open: boolean;
onClose: () => void;
}
export const TourModal = ({ open, onClose }: TourModalProps) => {
export const TourModal = (props: ModalProps) => {
const t = useAFFiNEI18N();
const [step, setStep] = useState(-1);
const handleClose = () => {
setStep(-1);
onClose();
};
return (
<Modal
open={open}
onClose={handleClose}
wrapperPosition={['center', 'center']}
hideBackdrop
width={545}
contentOptions={{
['data-testid' as string]: 'onboarding-modal',
style: {
minHeight: '480px',
padding: 0,
},
}}
overlayOptions={{
style: {
background: 'transparent',
},
}}
closeButtonOptions={{
// @ts-expect-error - fix upstream type
'data-testid': 'onboarding-modal-close-button',
}}
{...props}
>
<ModalWrapper
width={545}
style={{ minHeight: '480px' }}
data-testid="onboarding-modal"
>
<ModalCloseButton
top={6}
right={10}
onClick={handleClose}
data-testid="onboarding-modal-close-button"
/>
<div className={modalStyle}>
<div className={titleContainerStyle}>
{step !== -1 && (
<div
className={clsx(titleStyle, {
[slideToRightStyle]: step === 0,
[formSlideToLeftStyle]: step === 1,
})}
>
{t['com.affine.onboarding.title2']()}
</div>
)}
<div className={modalStyle}>
<div className={titleContainerStyle}>
{step !== -1 && (
<div
className={clsx(titleStyle, {
[slideToLeftStyle]: step === 1,
[formSlideToRightStyle]: step === 0,
[slideToRightStyle]: step === 0,
[formSlideToLeftStyle]: step === 1,
})}
>
{t['com.affine.onboarding.title1']()}
{t['com.affine.onboarding.title2']()}
</div>
)}
<div
className={clsx(titleStyle, {
[slideToLeftStyle]: step === 1,
[formSlideToRightStyle]: step === 0,
})}
>
{t['com.affine.onboarding.title1']()}
</div>
</div>
<div className={containerStyle}>
<div
className={clsx(arrowStyle, { [buttonDisableStyle]: step !== 1 })}
onClick={() => step === 1 && setStep(0)}
data-testid="onboarding-modal-pre-button"
>
<ArrowLeftSmallIcon />
</div>
<div className={videoContainerStyle}>
<div className={videoSlideStyle}>
{step !== -1 && (
<video
autoPlay
muted
loop
className={clsx(videoStyle, {
[slideToRightStyle]: step === 0,
[formSlideToLeftStyle]: step === 1,
})}
data-testid="onboarding-modal-editing-video"
>
<source src="/editingVideo.mp4" type="video/mp4" />
<source src="/editingVideo.webm" type="video/webm" />
</video>
)}
<div className={containerStyle}>
<div
className={clsx(arrowStyle, { [buttonDisableStyle]: step !== 1 })}
onClick={() => step === 1 && setStep(0)}
data-testid="onboarding-modal-pre-button"
>
<ArrowLeftSmallIcon />
</div>
<div className={videoContainerStyle}>
<div className={videoSlideStyle}>
{step !== -1 && (
<video
autoPlay
muted
loop
className={clsx(videoStyle, {
[slideToLeftStyle]: step === 1,
[formSlideToRightStyle]: step === 0,
[slideToRightStyle]: step === 0,
[formSlideToLeftStyle]: step === 1,
})}
data-testid="onboarding-modal-switch-video"
data-testid="onboarding-modal-editing-video"
>
<source src="/switchVideo.mp4" type="video/mp4" />
<source src="/switchVideo.webm" type="video/webm" />
<source src="/editingVideo.mp4" type="video/mp4" />
<source src="/editingVideo.webm" type="video/webm" />
</video>
</div>
</div>
<div
className={clsx(arrowStyle, { [buttonDisableStyle]: step === 1 })}
onClick={() => setStep(1)}
data-testid="onboarding-modal-next-button"
>
<ArrowRightSmallIcon />
)}
<video
autoPlay
muted
loop
className={clsx(videoStyle, {
[slideToLeftStyle]: step === 1,
[formSlideToRightStyle]: step === 0,
})}
data-testid="onboarding-modal-switch-video"
>
<source src="/switchVideo.mp4" type="video/mp4" />
<source src="/switchVideo.webm" type="video/webm" />
</video>
</div>
</div>
<ul className={tabContainerStyle}>
<li
className={clsx(tabStyle, {
[tabActiveStyle]: step !== 1,
})}
onClick={() => setStep(0)}
></li>
<li
className={clsx(tabStyle, { [tabActiveStyle]: step === 1 })}
onClick={() => setStep(1)}
></li>
</ul>
<div className={descriptionContainerStyle}>
{step !== -1 && (
<div
className={clsx(descriptionStyle, {
[slideToRightStyle]: step === 0,
[formSlideToLeftStyle]: step === 1,
})}
>
{t['com.affine.onboarding.videoDescription2']()}
</div>
)}
<div
className={clsx(descriptionStyle, {
[slideToLeftStyle]: step === 1,
[formSlideToRightStyle]: step === 0,
})}
>
{t['com.affine.onboarding.videoDescription1']()}
</div>
<div
className={clsx(arrowStyle, { [buttonDisableStyle]: step === 1 })}
onClick={() => setStep(1)}
data-testid="onboarding-modal-next-button"
>
<ArrowRightSmallIcon />
</div>
</div>
</ModalWrapper>
<ul className={tabContainerStyle}>
<li
className={clsx(tabStyle, {
[tabActiveStyle]: step !== 1,
})}
onClick={() => setStep(0)}
></li>
<li
className={clsx(tabStyle, { [tabActiveStyle]: step === 1 })}
onClick={() => setStep(1)}
></li>
</ul>
<div className={descriptionContainerStyle}>
{step !== -1 && (
<div
className={clsx(descriptionStyle, {
[slideToRightStyle]: step === 0,
[formSlideToLeftStyle]: step === 1,
})}
>
{t['com.affine.onboarding.videoDescription2']()}
</div>
)}
<div
className={clsx(descriptionStyle, {
[slideToLeftStyle]: step === 1,
[formSlideToRightStyle]: step === 0,
})}
>
{t['com.affine.onboarding.videoDescription1']()}
</div>
</div>
</div>
</Modal>
);
};