refactor: tour modal (#2297)

This commit is contained in:
JimmFly
2023-05-10 16:11:42 +08:00
committed by himself65
parent 58fa9d1fb8
commit df60392c31
6 changed files with 155 additions and 77 deletions
+1 -1
View File
@@ -67,7 +67,7 @@ test('affine onboarding button', async ({ page }) => {
'[data-testid=onboarding-modal-editing-video]' '[data-testid=onboarding-modal-editing-video]'
); );
expect(await editingVideo.isVisible()).toEqual(true); expect(await editingVideo.isVisible()).toEqual(true);
await page.getByTestId('onboarding-modal-ok-button').click(); await page.getByTestId('onboarding-modal-close-button').click();
expect(await onboardingModal.isVisible()).toEqual(false); expect(await onboardingModal.isVisible()).toEqual(false);
}); });
@@ -7,38 +7,90 @@ export const modalStyle = style({
flexDirection: 'column', flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
position: 'relative', position: 'relative',
padding: '12px 36px', backgroundColor: 'var(--affine-background-secondary-color)',
backgroundColor: 'var(--affine-white)', borderRadius: '16px',
borderRadius: '12px',
boxShadow: 'var(--affine-popover-shadow)',
}); });
export const titleStyle = style({ export const titleStyle = style({
fontSize: 'var(--affine-font-h6)', fontSize: 'var(--affine-font-h6)',
fontWeight: '600', fontWeight: '600',
marginTop: '12px',
});
export const containerStyle = style({
paddingTop: '25px',
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
}); });
export const videoContainerStyle = style({ export const videoContainerStyle = style({
paddingTop: '15px', paddingTop: '25px',
height: '300px',
width: 'calc(100% - 72px)',
display: 'flex',
alignItems: 'center',
flexGrow: 1,
justifyContent: 'space-between',
position: 'relative',
});
export const videoSlideStyle = style({
width: '100%', width: '100%',
position: 'absolute',
top: 0,
display: 'flex',
justifyContent: 'center',
}); });
export const videoStyle = style({ export const videoStyle = style({
position: 'absolute',
objectFit: 'fill', objectFit: 'fill',
height: '300px', height: '300px',
width: '100%', border: '1px solid var(--affine-border-color)',
transition: 'opacity 0.5s ease-in-out',
}); });
export const buttonContainerStyle = style({ export const videoActiveStyle = style({
opacity: 0,
});
export const arrowStyle = style({
wordBreak: 'break-all',
wordWrap: 'break-word',
width: '36px',
fontSize: '32px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '240px',
flexGrow: 0.2,
cursor: 'pointer',
});
export const descriptionStyle = style({
marginTop: '15px', marginTop: '15px',
width: '100%', width: '100%',
display: 'flex', display: 'flex',
justifyContent: 'flex-end', padding: '0 56px',
fontSize: 'var(--affine-font-sm)',
lineHeight: '18px',
marginBottom: '20px',
}); });
export const buttonStyle = style({ export const tabStyle = style({
borderRadius: '8px', width: '40px',
backgroundColor: 'var(--affine-primary-color)', height: '20px',
color: 'var(--affine-white)', content: '""',
height: '32px', borderBottom: '2px solid var(--affine-text-primary-color)',
padding: '4 20px', opacity: 0.2,
margin: '0 10px 20px 0',
transition: 'all 0.15s ease-in-out',
':hover': { ':hover': {
backgroundColor: 'var(--affine-primary-color)', opacity: 1,
color: 'var(--affine-white)',
}, },
}); });
export const tabActiveStyle = style({
opacity: 1,
});
export const tabContainerStyle = style({
width: '100%',
marginTop: '20px',
position: 'relative',
height: '2px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
});
@@ -1,13 +1,22 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowLeftSmallIcon, ArrowRightSmallIcon } from '@blocksuite/icons';
import clsx from 'clsx';
import type { FC } from 'react'; import type { FC } from 'react';
import { useState } from 'react'; import { useState } from 'react';
import { Button, Modal, ModalCloseButton, ModalWrapper } from '../..'; import { Modal, ModalCloseButton, ModalWrapper } from '../..';
import { import {
buttonContainerStyle, arrowStyle,
buttonStyle, containerStyle,
descriptionStyle,
modalStyle, modalStyle,
tabActiveStyle,
tabContainerStyle,
tabStyle,
titleStyle, titleStyle,
videoActiveStyle,
videoContainerStyle, videoContainerStyle,
videoSlideStyle,
videoStyle, videoStyle,
} from './index.css'; } from './index.css';
@@ -17,6 +26,7 @@ type TourModalProps = {
}; };
export const TourModal: FC<TourModalProps> = ({ open, onClose }) => { export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
const t = useAFFiNEI18N();
const [step, setStep] = useState(0); const [step, setStep] = useState(0);
const handleClose = () => { const handleClose = () => {
setStep(0); setStep(0);
@@ -27,73 +37,85 @@ export const TourModal: FC<TourModalProps> = ({ open, onClose }) => {
open={open} open={open}
onClose={handleClose} onClose={handleClose}
wrapperPosition={['center', 'center']} wrapperPosition={['center', 'center']}
hideBackdrop
> >
<ModalWrapper width={545} height={442} data-testid="onboarding-modal"> <ModalWrapper
width={545}
style={{ minHeight: '480px' }}
data-testid="onboarding-modal"
>
<ModalCloseButton <ModalCloseButton
top={10} top={6}
right={10} right={10}
onClick={handleClose} onClick={handleClose}
data-testid="onboarding-modal-close-button" data-testid="onboarding-modal-close-button"
/> />
{step === 0 && ( <div className={modalStyle}>
<div className={modalStyle}> <div className={titleStyle}>
<div className={titleStyle}>Hyper merged whiteboard and docs</div> {step === 1
<div className={videoContainerStyle}> ? t['com.affine.onboarding.title2']()
<video : t['com.affine.onboarding.title1']()}
autoPlay
muted
loop
className={videoStyle}
data-testid="onboarding-modal-switch-video"
>
<source src="/switchVideo.mp4" type="video/mp4" />
<source src="/switchVideo.webm" type="video/webm" />
Easily switch between Page mode for structured document creation
and Whiteboard mode for the freeform visual expression of
creative ideas.
</video>
</div>
<div className={buttonContainerStyle}>
<Button
className={buttonStyle}
onClick={() => setStep(1)}
data-testid="onboarding-modal-next-button"
>
Next
</Button>
</div>
</div> </div>
)} <div className={containerStyle}>
{step === 1 && ( <div
<div className={modalStyle}> className={arrowStyle}
<div className={titleStyle}> onClick={() => setStep(0)}
Intuitive & robust block-based editing data-testid="onboarding-modal-pre-button"
>
<ArrowLeftSmallIcon />
</div> </div>
<div className={videoContainerStyle}> <div className={videoContainerStyle}>
<video <div className={videoSlideStyle}>
autoPlay <video
muted autoPlay
loop muted
className={videoStyle} loop
data-testid="onboarding-modal-editing-video" className={clsx(videoStyle, {
> [videoActiveStyle]: step === 0,
<source src="/editingVideo.mp4" type="video/mp4" /> })}
<source src="/editingVideo.webm" type="video/webm" /> data-testid="onboarding-modal-editing-video"
Create structured documents with ease, using a modular interface >
to drag and drop blocks of text, images, and other content. <source src="/editingVideo.mp4" type="video/mp4" />
</video> <source src="/editingVideo.webm" type="video/webm" />
</video>
<video
autoPlay
muted
loop
className={clsx(videoStyle, {
[videoActiveStyle]: step === 1,
})}
data-testid="onboarding-modal-switch-video"
>
<source src="/switchVideo.mp4" type="video/mp4" />
<source src="/switchVideo.webm" type="video/webm" />
</video>
</div>
</div> </div>
<div className={buttonContainerStyle}> <div
<Button className={arrowStyle}
className={buttonStyle} onClick={() => setStep(1)}
onClick={handleClose} data-testid="onboarding-modal-next-button"
data-testid="onboarding-modal-ok-button" >
> <ArrowRightSmallIcon />
Okay, I Like It !
</Button>
</div> </div>
</div> </div>
)} <ul className={tabContainerStyle}>
<li
className={clsx(tabStyle, { [tabActiveStyle]: step === 0 })}
onClick={() => setStep(0)}
></li>
<li
className={clsx(tabStyle, { [tabActiveStyle]: step === 1 })}
onClick={() => setStep(1)}
></li>
</ul>
<div className={descriptionStyle}>
{step === 1
? t['com.affine.onboarding.videoDescription2']()
: t['com.affine.onboarding.videoDescription1']()}
</div>
</div>
</ModalWrapper> </ModalWrapper>
</Modal> </Modal>
); );
@@ -12,7 +12,7 @@ export const ModalWrapper = styled('div')<{
height, height,
minHeight, minHeight,
backgroundColor: 'var(--affine-background-secondary-color)', backgroundColor: 'var(--affine-background-secondary-color)',
boxShadow: 'var(--affine-popover-shadow)', boxShadow: 'var(--affine-shadow-3)',
borderRadius: '16px', borderRadius: '16px',
position: 'relative', position: 'relative',
maxHeight: 'calc(100vh - 32px)', maxHeight: 'calc(100vh - 32px)',
+5 -1
View File
@@ -267,5 +267,9 @@
"dark": "dark", "dark": "dark",
"system": "system", "system": "system",
"com.affine.pageMode": "Page Mode", "com.affine.pageMode": "Page Mode",
"com.affine.edgelessMode": "Edgeless Mode" "com.affine.edgelessMode": "Edgeless Mode",
"com.affine.onboarding.title1": "Hyper merged whiteboard and docs",
"com.affine.onboarding.title2": "Intuitive & robust block-based editing",
"com.affine.onboarding.videoDescription1": "Easily switch between Page mode for structured document creation and Whiteboard mode for the freeform visual expression of creative ideas.",
"com.affine.onboarding.videoDescription2": "Create structured documents with ease, using a modular interface to drag and drop blocks of text, images, and other content."
} }