feat(core): add starAFFiNE and issueFeedback modal (#5718)

close TOV-482

https://github.com/toeverything/AFFiNE/assets/102217452/da1f74bc-4b8d-4d7f-987d-f53da98d92fe
This commit is contained in:
JimmFly
2024-02-20 12:50:51 +00:00
parent 6fad241350
commit 4068e7aeff
28 changed files with 338 additions and 522 deletions

View File

@@ -1,192 +0,0 @@
import { cssVar } from '@toeverything/theme';
import { keyframes, style } from '@vanilla-extract/css';
export const modalStyle = style({
width: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
backgroundColor: cssVar('backgroundSecondaryColor'),
borderRadius: '16px',
overflow: 'hidden',
});
export const titleContainerStyle = style({
width: 'calc(100% - 72px)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
position: 'relative',
height: '60px',
overflow: 'hidden',
});
export const titleStyle = style({
fontSize: cssVar('fontH6'),
fontWeight: '600',
marginTop: '12px',
position: 'absolute',
marginBottom: '12px',
});
const slideToLeft = keyframes({
'0%': {
transform: 'translateX(0)',
opacity: 1,
},
'100%': {
transform: 'translateX(-300px)',
opacity: 0,
},
});
const slideToRight = keyframes({
'0%': {
transform: 'translateX(0)',
opacity: 1,
},
'100%': {
transform: 'translateX(300px)',
opacity: 0,
},
});
const slideFormLeft = keyframes({
'0%': {
transform: 'translateX(300px)',
opacity: 0,
},
'100%': {
transform: 'translateX(0)',
opacity: 1,
},
});
const slideFormRight = keyframes({
'0%': {
transform: 'translateX(-300px)',
opacity: 0,
},
'100%': {
transform: 'translateX(0)',
opacity: 1,
},
});
export const formSlideToLeftStyle = style({
animation: `${slideFormLeft} 0.3s ease-in-out forwards`,
});
export const formSlideToRightStyle = style({
animation: `${slideFormRight} 0.3s ease-in-out forwards`,
});
export const slideToLeftStyle = style({
animation: `${slideToLeft} 0.3s ease-in-out forwards`,
});
export const slideToRightStyle = style({
animation: `${slideToRight} 0.3s ease-in-out forwards`,
});
export const containerStyle = style({
width: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
});
export const videoContainerStyle = style({
height: '300px',
width: 'calc(100% - 72px)',
display: 'flex',
alignItems: 'center',
flexGrow: 1,
justifyContent: 'space-between',
position: 'relative',
overflow: 'hidden',
});
export const videoSlideStyle = style({
width: '100%',
position: 'absolute',
top: 0,
display: 'flex',
justifyContent: 'center',
});
export const videoStyle = style({
position: 'absolute',
objectFit: 'fill',
height: '300px',
border: `1px solid ${cssVar('borderColor')}`,
transition: 'opacity 0.5s ease-in-out',
});
const fadeIn = keyframes({
'0%': {
transform: 'translateX(300px)',
},
'100%': {
transform: 'translateX(0)',
},
});
export const videoActiveStyle = style({
animation: `${fadeIn} 0.5s ease-in-out forwards`,
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 descriptionContainerStyle = style({
width: 'calc(100% - 112px)',
height: '100px',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
position: 'relative',
overflow: 'hidden',
});
export const descriptionStyle = style({
marginTop: '15px',
width: '100%',
display: 'flex',
fontSize: cssVar('fontSm'),
lineHeight: '18px',
position: 'absolute',
});
export const tabStyle = style({
width: '40px',
height: '40px',
content: '""',
margin: '40px 10px 40px 0',
transition: 'all 0.15s ease-in-out',
position: 'relative',
cursor: 'pointer',
':hover': {
opacity: 1,
},
'::after': {
content: '""',
position: 'absolute',
bottom: '20px',
left: '0',
width: '100%',
height: '2px',
background: cssVar('textPrimaryColor'),
transition: 'all 0.15s ease-in-out',
opacity: 0.2,
cursor: 'pointer',
},
});
export const tabActiveStyle = style({
'::after': {
opacity: 1,
},
});
export const tabContainerStyle = style({
width: '100%',
marginTop: '20px',
position: 'relative',
height: '2px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
});
export const buttonDisableStyle = style({
cursor: 'not-allowed',
color: cssVar('textDisableColor'),
});

View File

@@ -1 +0,0 @@
export * from './tour-modal';

View File

@@ -1,160 +0,0 @@
/// <reference types="../../type.d.ts" />
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { ArrowLeftSmallIcon, ArrowRightSmallIcon } from '@blocksuite/icons';
import clsx from 'clsx';
import { useState } from 'react';
import { Modal, type ModalProps } from '../../ui/modal';
import editingVideo from './editingVideo.mp4';
import {
arrowStyle,
buttonDisableStyle,
containerStyle,
descriptionContainerStyle,
descriptionStyle,
formSlideToLeftStyle,
formSlideToRightStyle,
modalStyle,
slideToLeftStyle,
slideToRightStyle,
tabActiveStyle,
tabContainerStyle,
tabStyle,
titleContainerStyle,
titleStyle,
videoContainerStyle,
videoSlideStyle,
videoStyle,
} from './index.css';
import switchVideo from './switchVideo.mp4';
export const TourModal = (props: ModalProps) => {
const t = useAFFiNEI18N();
const [step, setStep] = useState(-1);
return (
<Modal
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}
>
<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={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} type="video/mp4" />
</video>
)}
<video
autoPlay
muted
loop
className={clsx(videoStyle, {
[slideToLeftStyle]: step === 1,
[formSlideToRightStyle]: step === 0,
})}
data-testid="onboarding-modal-switch-video"
>
<source src={switchVideo} type="video/mp4" />
</video>
</div>
</div>
<div
className={clsx(arrowStyle, { [buttonDisableStyle]: step === 1 })}
onClick={() => setStep(1)}
data-testid="onboarding-modal-next-button"
>
<ArrowRightSmallIcon />
</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>
</div>
</Modal>
);
};
export default TourModal;