Update landing page (#387)
* feat: update landing page * feat(code): improve code editing experience * feat: update landing page * fix: react warning * fix: update logo and i18n Co-authored-by: tzhangchi <terry.zhangchi@outlook.com> Co-authored-by: Yifeng Wang <doodlewind@qq.com>
@ -10,6 +10,7 @@
|
||||
"keywords": [],
|
||||
"author": "AFFiNE <developer@affine.pro>",
|
||||
"dependencies": {
|
||||
"@emotion/babel-plugin": "^11.10.2",
|
||||
"@mui/icons-material": "^5.8.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -7,6 +7,7 @@
|
||||
"keywords": [],
|
||||
"author": "DarkSky <darksky2048@gmail.com>",
|
||||
"dependencies": {
|
||||
"@emotion/babel-plugin": "^11.10.2",
|
||||
"@emotion/react": "^11.10.0",
|
||||
"@emotion/styled": "^11.10.0",
|
||||
"@mui/joy": "^5.0.0-alpha.42",
|
||||
|
@ -10,7 +10,7 @@ export const AboutUs = () => {
|
||||
return (
|
||||
<>
|
||||
<AFFiNEHeader />
|
||||
<Grid xs={12} sx={{ display: 'flex', marginTop: '4vh!important' }}>
|
||||
<Grid xs={12} sx={{ display: 'flex', marginTop: '12vh!important' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
|
@ -1,600 +0,0 @@
|
||||
/* eslint-disable max-lines */
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
import { Box, Button, Grid, Typography } from '@mui/joy';
|
||||
import { styled } from '@mui/joy/styles';
|
||||
import { LogoIcon } from '@toeverything/components/icons';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useMediaQuery } from '@mui/material';
|
||||
|
||||
import CollaborationImage from './collaboration.png';
|
||||
import { AFFiNEFooter, AFFiNEHeader, AFFiNEImage } from './common';
|
||||
import { GitHub } from './Icons';
|
||||
import PageImage from './page.png';
|
||||
import ShapeImage from './shape.png';
|
||||
import TaskImage from './task.png';
|
||||
|
||||
const Alternatives = styled(Box)<{ width: string }>(({ width }) => ({
|
||||
position: 'relative',
|
||||
width: '24em',
|
||||
height: '128px',
|
||||
transform: 'translateY(-8px)',
|
||||
overflowY: 'hidden',
|
||||
'@media (max-width: 1024px)': {
|
||||
width,
|
||||
height: '48px',
|
||||
transform: 'translateY(0)',
|
||||
},
|
||||
'& .scroll-element': {
|
||||
width: 'inherit',
|
||||
height: 'inherit',
|
||||
position: 'absolute',
|
||||
left: '0%',
|
||||
top: '0%',
|
||||
lineHeight: '96px',
|
||||
'@media (max-width: 1024px)': {
|
||||
lineHeight: '32px',
|
||||
},
|
||||
},
|
||||
'& .scroll-element.active': {
|
||||
animation: 'primary 500ms linear infinite',
|
||||
},
|
||||
'.primary.active': {
|
||||
animation: 'primary 500ms linear infinite',
|
||||
},
|
||||
'.secondary.active': {
|
||||
animation: 'secondary 500ms linear infinite',
|
||||
},
|
||||
'@keyframes primary': {
|
||||
from: {
|
||||
top: '0%',
|
||||
},
|
||||
to: {
|
||||
top: '-100%',
|
||||
},
|
||||
},
|
||||
'@keyframes secondary': {
|
||||
from: {
|
||||
top: '100%',
|
||||
},
|
||||
to: {
|
||||
top: '0%',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const _alternatives = ['Notion', 'Miro', 'Monday'];
|
||||
const _alternativesSize = [8, 6, 10];
|
||||
|
||||
const Product = () => {
|
||||
const [idx, setIdx] = useState(0);
|
||||
const [last, current] = useMemo(
|
||||
() => [
|
||||
_alternatives[idx],
|
||||
_alternatives[idx + 1] ? _alternatives[idx + 1] : _alternatives[0],
|
||||
],
|
||||
[idx]
|
||||
);
|
||||
const maxWidth = useMemo(() => _alternativesSize[idx], [idx]);
|
||||
const [active, setActive] = useState(false);
|
||||
const matches = useMediaQuery('(max-width: 1024px)');
|
||||
|
||||
useEffect(() => {
|
||||
const handle = setInterval(() => {
|
||||
setActive(true);
|
||||
setTimeout(
|
||||
() => {
|
||||
setIdx(idx => (_alternatives[idx + 1] ? idx + 1 : 0));
|
||||
setActive(false);
|
||||
},
|
||||
matches ? 450 : 380
|
||||
);
|
||||
}, 2000);
|
||||
return () => clearInterval(handle);
|
||||
}, [matches]);
|
||||
|
||||
return (
|
||||
<Alternatives
|
||||
width={`${maxWidth}em`}
|
||||
sx={{
|
||||
margin: 'auto',
|
||||
marginRight: '1em',
|
||||
transition: 'width .5s',
|
||||
'@media (max-width: 1024px)': {
|
||||
width: '8em',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className={clsx(
|
||||
'scroll-element',
|
||||
'primary',
|
||||
active && 'active'
|
||||
)}
|
||||
>
|
||||
<Typography
|
||||
fontSize="96px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
color: '#06449d',
|
||||
textAlign: 'right',
|
||||
overflow: 'hidden',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{last}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
className={clsx(
|
||||
'scroll-element',
|
||||
'primary',
|
||||
active && 'active'
|
||||
)}
|
||||
sx={{
|
||||
marginTop: '96px',
|
||||
textAlign: 'right',
|
||||
overflow: 'hidden',
|
||||
'@media (max-width: 1024px)': {
|
||||
marginTop: '48px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
fontSize="96px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
color: '#06449d',
|
||||
overflow: 'hidden',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{current}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Alternatives>
|
||||
);
|
||||
};
|
||||
|
||||
const AFFiNEOnline = (props: { center?: boolean; flat?: boolean }) => {
|
||||
const matches = useMediaQuery('(max-width: 1024px)');
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.open('https://livedemo.affine.pro/');
|
||||
}}
|
||||
{...(props.flat ? { variant: 'plain' } : {})}
|
||||
{...{
|
||||
sx: {
|
||||
margin: 'auto 1em',
|
||||
fontSize: '24px',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '16px',
|
||||
},
|
||||
...(props.flat
|
||||
? {
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
':hover': { backgroundColor: 'unset' },
|
||||
}
|
||||
: {}),
|
||||
...(props.center
|
||||
? {
|
||||
padding: '0.5em 1em',
|
||||
fontSize: '2em',
|
||||
backgroundColor: '#000',
|
||||
':hover': {
|
||||
backgroundColor: '#0c60d9',
|
||||
boxShadow: '2px 2px 20px #08f4',
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
}}
|
||||
startIcon={<LogoIcon />}
|
||||
size="lg"
|
||||
>
|
||||
{t('Try it Online')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export function App() {
|
||||
const matches = useMediaQuery('(max-width: 1024px)');
|
||||
const navigate = useNavigate();
|
||||
const { t, i18n } = useTranslation();
|
||||
|
||||
const changeLanguage = (event: any) => {
|
||||
i18n.changeLanguage(event);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<AFFiNEHeader />
|
||||
<Grid xs={12} sx={{ display: 'flex', marginTop: '12vh!important' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
fontSize="96px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
marginRight: '0.25em',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '32px',
|
||||
marginRight: 0,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t('Open Source')},
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="96px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t('Privacy First')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexFlow: 'wrap',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
flexFlow: 'wrap',
|
||||
margin: 'auto',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Product />
|
||||
<Typography
|
||||
fontSize="96px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
color: '#06449d',
|
||||
margin: 'auto',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t('Alternative')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h3"
|
||||
fontWeight={'400'}
|
||||
sx={{ color: '#888' }}
|
||||
>
|
||||
{t('description1.part1')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
marginTop: '1.5em',
|
||||
marginBottom: '12vh!important',
|
||||
rawGap: '1em',
|
||||
}}
|
||||
>
|
||||
<GitHub center />
|
||||
<AFFiNEOnline center />
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{ display: 'flex', maxWidth: '1200px', margin: 'auto' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
transition: 'all .5s',
|
||||
transform: 'scale(0.98)',
|
||||
boxShadow: '2px 2px 40px #0002',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
boxShadow: '2px 2px 40px #0004',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AFFiNEImage src={PageImage} alt="AFFiNE main ui" />
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
margin: 'auto',
|
||||
marginTop: '12em',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level={matches ? 'h2' : 'h1'}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
{t('description1.part2')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
margin: 'auto',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center',
|
||||
marginBottom: '12em',
|
||||
}}
|
||||
>
|
||||
<Typography fontSize="1.2em">
|
||||
{t('description1.part3')}
|
||||
</Typography>
|
||||
<Typography fontSize="1.2em">
|
||||
{t('description1.part4')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: matches ? 'column' : 'row',
|
||||
marginBottom: '12em',
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={matches ? 12 : 3}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
...(matches
|
||||
? {}
|
||||
: { marginLeft: '4em', marginRight: '2em' }),
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
alignSelf: 'center',
|
||||
textAlign: 'left',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h2"
|
||||
fontWeight={'bold'}
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
>
|
||||
{t('description2.part1')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description2.part2')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description2.part3')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={matches ? 12 : 9}
|
||||
sx={{ display: 'flex', width: '100%' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
textAlign: 'left',
|
||||
transition: 'all .5s',
|
||||
transform: 'scale(0.98)',
|
||||
boxShadow: '2px 2px 40px #0002',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
boxShadow: '2px 2px 40px #0004',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AFFiNEImage
|
||||
src={ShapeImage}
|
||||
alt="AFFiNE Shape Your Page"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: matches ? 'column' : 'row-reverse',
|
||||
marginBottom: '12em',
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={matches ? 12 : 6}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
...(matches ? {} : { marginLeft: '4em' }),
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
alignSelf: 'center',
|
||||
textAlign: 'left',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h2"
|
||||
fontWeight={'bold'}
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
>
|
||||
{t('description3.part1')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description3.part2')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description3.part3')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description3.part4')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={matches ? 12 : 6}
|
||||
sx={{ display: 'flex', width: '100%' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
textAlign: 'left',
|
||||
transition: 'all .5s',
|
||||
transform: 'scale(0.98)',
|
||||
boxShadow: '2px 2px 40px #0002',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
boxShadow: '2px 2px 40px #0004',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AFFiNEImage
|
||||
src={TaskImage}
|
||||
alt="AFFiNE Plan Your Task"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
marginBottom: '4em',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h2"
|
||||
fontWeight={'bold'}
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
>
|
||||
{t('description4.part1')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description4.part2')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description4.part3')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex', marginBottom: '12em' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
transition: 'all .5s',
|
||||
transform: 'scale(0.98)',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AFFiNEImage
|
||||
src={CollaborationImage}
|
||||
alt="AFFiNE Privacy-first, and collaborative"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<AFFiNEFooter />
|
||||
</>
|
||||
);
|
||||
}
|
153
apps/venus/src/app/IndexPage/Alternatives.tsx
Normal file
@ -0,0 +1,153 @@
|
||||
import { Box, Typography } from '@mui/joy';
|
||||
import { styled } from '@mui/joy/styles';
|
||||
import clsx from 'clsx';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useMediaQuery } from '@mui/material';
|
||||
|
||||
const Alternatives = styled(Box)<{ width: string }>(({ width }) => ({
|
||||
position: 'relative',
|
||||
width: '20em',
|
||||
height: '128px',
|
||||
transform: 'translateY(-8px)',
|
||||
overflowY: 'hidden',
|
||||
'@media (max-width: 1024px)': {
|
||||
width,
|
||||
height: '48px',
|
||||
transform: 'translateY(0)',
|
||||
},
|
||||
'& .scroll-element': {
|
||||
width: 'inherit',
|
||||
height: 'inherit',
|
||||
position: 'absolute',
|
||||
left: '0',
|
||||
top: '0',
|
||||
paddingTop: '22px',
|
||||
lineHeight: '96px',
|
||||
'@media (max-width: 1024px)': {
|
||||
lineHeight: '32px',
|
||||
},
|
||||
},
|
||||
'& .scroll-element.active': {
|
||||
animation: 'primary 500ms linear infinite',
|
||||
},
|
||||
'.primary.active': {
|
||||
animation: 'primary 500ms linear infinite',
|
||||
},
|
||||
'.secondary.active': {
|
||||
animation: 'secondary 500ms linear infinite',
|
||||
},
|
||||
'@keyframes primary': {
|
||||
from: {
|
||||
top: '0%',
|
||||
},
|
||||
to: {
|
||||
top: '-100%',
|
||||
},
|
||||
},
|
||||
'@keyframes secondary': {
|
||||
from: {
|
||||
top: '100%',
|
||||
},
|
||||
to: {
|
||||
top: '0%',
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const _alternatives = ['Notion', 'Miro', 'Monday'];
|
||||
const _alternativesSize = [8, 6, 10];
|
||||
|
||||
export const AlternativesProduct = () => {
|
||||
const [idx, setIdx] = useState(0);
|
||||
const [last, current] = useMemo(
|
||||
() => [
|
||||
_alternatives[idx],
|
||||
_alternatives[idx + 1] ? _alternatives[idx + 1] : _alternatives[0],
|
||||
],
|
||||
[idx]
|
||||
);
|
||||
const maxWidth = useMemo(() => _alternativesSize[idx], [idx]);
|
||||
const [active, setActive] = useState(false);
|
||||
const matches = useMediaQuery('(max-width: 1024px)');
|
||||
|
||||
useEffect(() => {
|
||||
const handle = setInterval(() => {
|
||||
setActive(true);
|
||||
setTimeout(
|
||||
() => {
|
||||
setIdx(idx => (_alternatives[idx + 1] ? idx + 1 : 0));
|
||||
setActive(false);
|
||||
},
|
||||
matches ? 450 : 380
|
||||
);
|
||||
}, 2000);
|
||||
return () => clearInterval(handle);
|
||||
}, [matches]);
|
||||
|
||||
return (
|
||||
<Alternatives
|
||||
width={`${maxWidth}em`}
|
||||
sx={{
|
||||
margin: 'auto',
|
||||
marginRight: '1em',
|
||||
transition: 'width .5s',
|
||||
'@media (max-width: 1024px)': {
|
||||
width: '8em',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
className={clsx(
|
||||
'scroll-element',
|
||||
'primary',
|
||||
active && 'active'
|
||||
)}
|
||||
>
|
||||
<Typography
|
||||
fontSize="64px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
color: '#06449d',
|
||||
textAlign: 'right',
|
||||
overflow: 'hidden',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{last}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
className={clsx(
|
||||
'scroll-element',
|
||||
'primary',
|
||||
active && 'active'
|
||||
)}
|
||||
sx={{
|
||||
marginTop: '96px',
|
||||
textAlign: 'right',
|
||||
overflow: 'hidden',
|
||||
'@media (max-width: 1024px)': {
|
||||
marginTop: '48px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
fontSize="64px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
color: '#06449d',
|
||||
overflow: 'hidden',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{current}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Alternatives>
|
||||
);
|
||||
};
|
200
apps/venus/src/app/IndexPage/FunctionTabs.tsx
Normal file
@ -0,0 +1,200 @@
|
||||
import { Box, Grid, Typography } from '@mui/joy';
|
||||
import { useState } from 'react';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { Paper, Slide, Tab, Tabs, useMediaQuery } from '@mui/material';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { AFFiNEImage } from '../common';
|
||||
import ShapeImage from './shape.png';
|
||||
import TaskImage from './task.png';
|
||||
|
||||
export const FunctionTabs = () => {
|
||||
const matches = useMediaQuery('(max-width: 1024px)');
|
||||
const { t } = useTranslation();
|
||||
const [tab, selectTab] = useState(0);
|
||||
return (
|
||||
<Paper
|
||||
sx={{
|
||||
// backgroundColor: 'rgba(54, 100, 214, 0.05)',
|
||||
padding: '10px',
|
||||
position: 'relative',
|
||||
height: '700px',
|
||||
}}
|
||||
elevation={0}
|
||||
>
|
||||
<Tabs
|
||||
value={tab}
|
||||
onChange={(_, value) => selectTab(value)}
|
||||
centered
|
||||
>
|
||||
<Tab label={t('description2.part1')} value={0} />
|
||||
<Tab label={t('description3.part1')} value={1} />
|
||||
</Tabs>
|
||||
<Slide direction="left" in={tab === 0} mountOnEnter unmountOnExit>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: matches ? 'column' : 'row',
|
||||
marginBottom: '12em',
|
||||
position: 'absolute',
|
||||
top: '100px',
|
||||
left: 0,
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={matches ? 12 : 3}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
paddingLeft: '20px',
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
alignSelf: 'center',
|
||||
textAlign: 'left',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h2"
|
||||
fontWeight={'bold'}
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
>
|
||||
{t('description2.part1')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
>
|
||||
{t('description2.part2')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description2.part3')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={matches ? 12 : 9}
|
||||
sx={{ display: 'flex', width: '100%', flex: 2 }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
textAlign: 'left',
|
||||
transition: 'all .5s',
|
||||
transform: 'scale(0.98)',
|
||||
boxShadow: '2px 2px 40px #0002',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
boxShadow: '2px 2px 40px #0004',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AFFiNEImage
|
||||
src={ShapeImage}
|
||||
alt="AFFiNE Shape Your Page"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Slide>
|
||||
<Slide direction="left" in={tab === 1} mountOnEnter unmountOnExit>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: matches ? 'column' : 'row-reverse',
|
||||
position: 'absolute',
|
||||
top: '100px',
|
||||
left: 0,
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Grid
|
||||
xs={matches ? 12 : 6}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
...(matches ? {} : { marginLeft: '4em' }),
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
alignSelf: 'center',
|
||||
textAlign: 'left',
|
||||
width: '100%',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h2"
|
||||
fontWeight={'bold'}
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
>
|
||||
{t('description3.part1')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description3.part2')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description3.part3')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description3.part4')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={matches ? 12 : 6}
|
||||
sx={{ display: 'flex', width: '100%' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'left',
|
||||
textAlign: 'left',
|
||||
transition: 'all .5s',
|
||||
transform: 'scale(0.98)',
|
||||
boxShadow: '2px 2px 40px #0002',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
boxShadow: '2px 2px 40px #0004',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AFFiNEImage
|
||||
src={TaskImage}
|
||||
alt="AFFiNE Plan Your Task"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Slide>
|
||||
</Paper>
|
||||
);
|
||||
};
|
292
apps/venus/src/app/IndexPage/IndexPage.tsx
Normal file
@ -0,0 +1,292 @@
|
||||
/* eslint-disable max-lines */
|
||||
/* eslint-disable @typescript-eslint/naming-convention */
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { Box, Button, Grid, Typography } from '@mui/joy';
|
||||
import { LogoIcon } from '@toeverything/components/icons';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useMediaQuery } from '@mui/material';
|
||||
|
||||
import { AFFiNEFooter, AFFiNEHeader, AFFiNEImage } from '../common';
|
||||
import { GitHub } from '../Icons';
|
||||
import { AlternativesProduct } from './Alternatives';
|
||||
import CollaborationImage from './collaboration.png';
|
||||
import { FunctionTabs } from './FunctionTabs';
|
||||
import PageImage from './page.png';
|
||||
|
||||
const AFFiNEOnline = (props: { center?: boolean; flat?: boolean }) => {
|
||||
const matches = useMediaQuery('(max-width: 1024px)');
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.open('https://livedemo.affine.pro/');
|
||||
}}
|
||||
{...(props.flat ? { variant: 'plain' } : {})}
|
||||
{...{
|
||||
sx: {
|
||||
margin: 'auto 1em',
|
||||
fontSize: '24px',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '16px',
|
||||
},
|
||||
...(props.flat
|
||||
? {
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
':hover': { backgroundColor: 'unset' },
|
||||
}
|
||||
: {}),
|
||||
...(props.center
|
||||
? {
|
||||
padding: '0.5em 1em',
|
||||
fontSize: '2em',
|
||||
backgroundColor: '#000',
|
||||
':hover': {
|
||||
backgroundColor: '#0c60d9',
|
||||
boxShadow: '2px 2px 20px #08f4',
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
}}
|
||||
startIcon={<LogoIcon />}
|
||||
size="lg"
|
||||
>
|
||||
{t('Try it Online')}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export function IndexPage() {
|
||||
const matches = useMediaQuery('(max-width: 1024px)');
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<>
|
||||
<AFFiNEHeader />
|
||||
<Grid xs={12} sx={{ display: 'flex', marginTop: '12vh!important' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
fontSize="64px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
marginRight: '0.25em',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '32px',
|
||||
marginRight: 0,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t('Open Source')},
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="64px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t('Privacy First')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexFlow: 'wrap',
|
||||
overflow: 'auto',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'inline-flex',
|
||||
flexFlow: 'wrap',
|
||||
margin: 'auto',
|
||||
fontWeight: 'bold',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<AlternativesProduct />
|
||||
<Typography
|
||||
fontSize="64px"
|
||||
fontWeight={900}
|
||||
sx={{
|
||||
color: '#06449d',
|
||||
margin: 'auto',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '32px',
|
||||
},
|
||||
}}
|
||||
>
|
||||
{t('Alternative')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h3"
|
||||
fontWeight={'400'}
|
||||
sx={{ color: '#888' }}
|
||||
>
|
||||
{t('description1.part1')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
marginTop: '1.5em',
|
||||
marginBottom: '12vh!important',
|
||||
rawGap: '1em',
|
||||
}}
|
||||
>
|
||||
<GitHub center />
|
||||
<AFFiNEOnline center />
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{ display: 'flex', maxWidth: '1200px', margin: 'auto' }}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
transition: 'all .5s',
|
||||
transform: 'scale(0.98)',
|
||||
boxShadow: '2px 2px 40px #0002',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
boxShadow: '2px 2px 40px #0004',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AFFiNEImage src={PageImage} alt="AFFiNE main ui" />
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
margin: 'auto',
|
||||
marginTop: '12em',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level={matches ? 'h2' : 'h1'}
|
||||
fontWeight={'bold'}
|
||||
>
|
||||
{t('description1.part2')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
margin: 'auto',
|
||||
justifyContent: 'center',
|
||||
textAlign: 'center',
|
||||
marginBottom: '12em',
|
||||
}}
|
||||
>
|
||||
<Typography fontSize="1.2em">
|
||||
{t('description1.part3')}
|
||||
</Typography>
|
||||
<Typography fontSize="1.2em">
|
||||
{t('description1.part4')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<FunctionTabs />
|
||||
<Grid
|
||||
xs={12}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
marginTop: '12em !important',
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
flexWrap: 'wrap',
|
||||
margin: 'auto',
|
||||
textAlign: 'center',
|
||||
marginBottom: '4em',
|
||||
}}
|
||||
>
|
||||
<Typography
|
||||
level="h2"
|
||||
fontWeight={'bold'}
|
||||
style={{ marginBottom: '0.5em' }}
|
||||
>
|
||||
{t('description4.part1')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description4.part2')}
|
||||
</Typography>
|
||||
<Typography
|
||||
fontSize="1.2em"
|
||||
style={{ marginBottom: '0.25em' }}
|
||||
>
|
||||
{t('description4.part3')}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Grid>
|
||||
<Grid xs={12} sx={{ display: 'flex', marginBottom: '12em' }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
margin: 'auto',
|
||||
transition: 'all .5s',
|
||||
transform: 'scale(0.98)',
|
||||
':hover': {
|
||||
transform: 'scale(1)',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<AFFiNEImage
|
||||
src={CollaborationImage}
|
||||
alt="AFFiNE Privacy-first, and collaborative"
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
<AFFiNEFooter />
|
||||
</>
|
||||
);
|
||||
}
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
1
apps/venus/src/app/IndexPage/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { IndexPage } from './IndexPage';
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
@ -6,82 +6,141 @@ import { Button, Grid } from '@mui/joy';
|
||||
import Option from '@mui/joy/Option';
|
||||
import Select from '@mui/joy/Select';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { useMediaQuery } from '@mui/material';
|
||||
import GitHubIcon from '@mui/icons-material/GitHub';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { styled, useMediaQuery } from '@mui/material';
|
||||
import AFFiNETextLogo from './affine-text-logo.png';
|
||||
import { HoverMenu } from './HoverMenu';
|
||||
|
||||
import { options } from '../i18n';
|
||||
import { GitHub } from '../Icons';
|
||||
|
||||
export const AFFiNEHeader = () => {
|
||||
const matches = useMediaQuery('(max-width: 1024px)');
|
||||
const navigate = useNavigate();
|
||||
const { i18n } = useTranslation();
|
||||
const { i18n, t } = useTranslation();
|
||||
|
||||
const changeLanguage = (event: any) => {
|
||||
i18n.changeLanguage(event);
|
||||
};
|
||||
const matchesIPAD = useMediaQuery('(max-width: 768px)');
|
||||
return (
|
||||
<Grid
|
||||
container
|
||||
spacing={2}
|
||||
sx={{
|
||||
maxWidth: '1280px',
|
||||
margin: 'auto',
|
||||
}}
|
||||
>
|
||||
<Grid xs={6}>
|
||||
<Button
|
||||
size="lg"
|
||||
variant="plain"
|
||||
sx={{
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
':hover': { backgroundColor: 'unset' },
|
||||
fontSize: '24px',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '16px',
|
||||
},
|
||||
}}
|
||||
<Container container spacing={2}>
|
||||
<Grid xs={6} sx={{ display: 'flex', alignItems: 'center' }}>
|
||||
<StyledImage
|
||||
src={AFFiNETextLogo}
|
||||
alt="affine"
|
||||
onClick={() => navigate('/')}
|
||||
>
|
||||
AFFiNE
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid xs={6} sx={{ display: 'flex', justifyContent: 'right' }}>
|
||||
<GitHub flat />
|
||||
<Button
|
||||
onClick={() => window.open('https://blog.affine.pro')}
|
||||
variant="plain"
|
||||
sx={{
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
':hover': { backgroundColor: 'unset' },
|
||||
fontSize: '24px',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '16px',
|
||||
},
|
||||
}}
|
||||
size="lg"
|
||||
>
|
||||
Blog
|
||||
</Button>
|
||||
/>
|
||||
<Button
|
||||
onClick={() => navigate('/aboutus')}
|
||||
variant="plain"
|
||||
color="neutral"
|
||||
sx={{
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
':hover': { backgroundColor: 'unset' },
|
||||
fontSize: '24px',
|
||||
'@media (max-width: 1024px)': {
|
||||
fontSize: '16px',
|
||||
},
|
||||
fontSize: '16px',
|
||||
}}
|
||||
size="lg"
|
||||
size="md"
|
||||
>
|
||||
About Us
|
||||
{t('AboutUs')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => window.open('https://blog.affine.pro')}
|
||||
variant="plain"
|
||||
color="neutral"
|
||||
sx={{
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
size="md"
|
||||
>
|
||||
{t('Blog')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => window.open('https://docs.affine.pro/')}
|
||||
variant="plain"
|
||||
color="neutral"
|
||||
sx={{
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
size="md"
|
||||
>
|
||||
{t('Docs')}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => window.open('https://feedback.affine.pro/')}
|
||||
variant="plain"
|
||||
color="neutral"
|
||||
sx={{
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
size="md"
|
||||
>
|
||||
{t('Feedback')}
|
||||
</Button>
|
||||
</Grid>
|
||||
<Grid xs={6} sx={{ display: 'flex', justifyContent: 'right' }}>
|
||||
<Button
|
||||
variant="plain"
|
||||
color="neutral"
|
||||
onClick={() =>
|
||||
window.open('https://github.com/toeverything/AFFiNE')
|
||||
}
|
||||
sx={{
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
size="md"
|
||||
>
|
||||
<GitHubIcon />
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => window.open('https://livedemo.affine.pro/')}
|
||||
variant="plain"
|
||||
color="neutral"
|
||||
sx={{
|
||||
padding: matches ? '0' : '0 0.5em',
|
||||
fontSize: '16px',
|
||||
}}
|
||||
size="md"
|
||||
>
|
||||
{t('Try it Online')}
|
||||
</Button>
|
||||
<HoverMenu
|
||||
title={t('ContactUs')}
|
||||
options={[
|
||||
{
|
||||
title: 'Discord',
|
||||
value: 'https://discord.gg/Arn7TqJBvG',
|
||||
},
|
||||
{
|
||||
title: 'Telegram',
|
||||
value: 'https://t.me/affineworkos',
|
||||
},
|
||||
{
|
||||
title: 'Reddit',
|
||||
value: 'https://www.reddit.com/r/Affine/',
|
||||
},
|
||||
{
|
||||
title: 'Medium',
|
||||
value: 'https://medium.com/@affineworkos',
|
||||
},
|
||||
{
|
||||
title: 'Email',
|
||||
value: 'mailto:contact@toeverything.info',
|
||||
},
|
||||
]}
|
||||
onSelect={href => {
|
||||
window.open(href);
|
||||
}}
|
||||
/>
|
||||
<Select
|
||||
defaultValue="en"
|
||||
sx={{ display: matchesIPAD ? 'none' : 'intial' }}
|
||||
onChange={changeLanguage}
|
||||
size="md"
|
||||
variant="plain"
|
||||
>
|
||||
{options.map(option => (
|
||||
<Option key={option.value} value={option.value}>
|
||||
@ -90,6 +149,26 @@ export const AFFiNEHeader = () => {
|
||||
))}
|
||||
</Select>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Container>
|
||||
);
|
||||
};
|
||||
|
||||
const Container = styled(Grid)({
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: '50%',
|
||||
transform: 'translateX(-50%)',
|
||||
width: '100%',
|
||||
paddingTop: '1em',
|
||||
backgroundColor: '#fff',
|
||||
zIndex: 1500,
|
||||
maxWidth: '1440px',
|
||||
margin: 'auto',
|
||||
marginTop: '0 !important',
|
||||
});
|
||||
|
||||
const StyledImage = styled('img')({
|
||||
height: '24px',
|
||||
marginRight: '16px',
|
||||
cursor: 'pointer',
|
||||
});
|
||||
|
73
apps/venus/src/app/common/HoverMenu.tsx
Normal file
@ -0,0 +1,73 @@
|
||||
import Button from '@mui/joy/Button';
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { styled, Tooltip, type TooltipProps } from '@mui/material';
|
||||
|
||||
interface HoverMenuProps {
|
||||
title: string;
|
||||
options: Array<{ title: string; value: string }>;
|
||||
onSelect: (value: string) => void;
|
||||
}
|
||||
|
||||
export function HoverMenu({ title, options, onSelect }: HoverMenuProps) {
|
||||
return (
|
||||
<StyledTooltip
|
||||
title={
|
||||
<>
|
||||
{options.map(option => {
|
||||
return (
|
||||
<ListItem
|
||||
key={option.value}
|
||||
href={option.value}
|
||||
target="_blank"
|
||||
title={option.value}
|
||||
onClick={() => {
|
||||
onSelect(option.value);
|
||||
}}
|
||||
>
|
||||
{option.title}
|
||||
</ListItem>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
}
|
||||
>
|
||||
<Button
|
||||
variant="plain"
|
||||
color="neutral"
|
||||
sx={{
|
||||
fontSize: '16px',
|
||||
}}
|
||||
size="md"
|
||||
>
|
||||
{title}
|
||||
</Button>
|
||||
</StyledTooltip>
|
||||
);
|
||||
}
|
||||
|
||||
const StyledTooltip = styled(({ className, ...props }: TooltipProps) => (
|
||||
<Tooltip {...props} classes={{ popper: className }} />
|
||||
))(({ theme }) => ({
|
||||
'& .MuiTooltip-tooltip': {
|
||||
backgroundColor: 'white',
|
||||
boxShadow: theme.shadows[4],
|
||||
color: '#272930',
|
||||
zIndex: '1500',
|
||||
},
|
||||
}));
|
||||
|
||||
const ListItem = styled('a')({
|
||||
display: 'block',
|
||||
fontSize: '16px',
|
||||
lineHeight: '32px',
|
||||
padding: '5px',
|
||||
cursor: 'pointer',
|
||||
borderRadius: '4px',
|
||||
color: '#272930',
|
||||
textDecoration: 'none',
|
||||
|
||||
'&:hover': {
|
||||
color: '#131418',
|
||||
backgroundColor: '#eeeff0',
|
||||
},
|
||||
});
|
BIN
apps/venus/src/app/common/affine-text-logo.png
Normal file
After Width: | Height: | Size: 20 KiB |
@ -1,8 +1,11 @@
|
||||
{
|
||||
"translation": {
|
||||
"Blog": "Blog",
|
||||
"AboutUs": "About AboutUs",
|
||||
"AboutUs": "About Us",
|
||||
"Open Source": "Open Source",
|
||||
"Docs": "Docs",
|
||||
"Feedback": "Feedback",
|
||||
"ContactUs": "Contact Us",
|
||||
"Privacy First": "Privacy First",
|
||||
"Alternative": "Alternative",
|
||||
"Check GitHub": "Check GitHub",
|
||||
|
@ -4,9 +4,12 @@
|
||||
"AboutUs": "关于我们",
|
||||
"Open Source": "开源",
|
||||
"Privacy First": "隐私第一",
|
||||
"Docs": "文档",
|
||||
"Feedback": "反馈",
|
||||
"ContactUs": "联系我们",
|
||||
"Alternative": "的另一种选择",
|
||||
"Check GitHub": "GitHub中查看",
|
||||
"Try it Online": "在线上试试",
|
||||
"Try it Online": "在线试用",
|
||||
"description1": {
|
||||
"part1": "Affine是面向专业人士的下一代协同知识库",
|
||||
"part2": "它不仅仅是一个文档、白板和表格的集合。",
|
||||
|
@ -4,7 +4,7 @@ import { Container } from '@mui/joy';
|
||||
import { CssVarsProvider } from '@mui/joy/styles';
|
||||
|
||||
import { AboutUs } from './AboutUs';
|
||||
import { App } from './App';
|
||||
import { IndexPage } from './IndexPage';
|
||||
|
||||
const VenusContainer = () => {
|
||||
return (
|
||||
@ -30,7 +30,7 @@ export function VenusRoutes() {
|
||||
<Routes>
|
||||
<Route element={<VenusContainer />}>
|
||||
<Route path="/aboutus" element={<AboutUs />} />
|
||||
<Route path="/" element={<App />} />
|
||||
<Route path="/" element={<IndexPage />} />
|
||||
</Route>
|
||||
</Routes>
|
||||
);
|
||||
|
@ -25,9 +25,9 @@ const GoogleIcon = () => (
|
||||
<g
|
||||
id="Page-1"
|
||||
stroke="none"
|
||||
stroke-width="1"
|
||||
strokeWidth="1"
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
fillRule="evenodd"
|
||||
>
|
||||
<g id="Artboard-1" transform="translate(-332.000000, -639.000000)">
|
||||
<g
|
||||
|
@ -99,7 +99,7 @@ const langs: Record<string, any> = {
|
||||
dockerfile: () => StreamLanguage.define(dockerFile),
|
||||
r: () => StreamLanguage.define(r),
|
||||
};
|
||||
const DEFAULT_LANG = 'javascript';
|
||||
const DEFAULT_LANG = 'markdown';
|
||||
const CodeBlock = styled('div')(({ theme }) => ({
|
||||
backgroundColor: '#F2F5F9',
|
||||
padding: '8px 24px',
|
||||
@ -142,10 +142,13 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
|
||||
const langType: string = block.getProperty('lang');
|
||||
const [extensions, setExtensions] = useState<Extension[]>();
|
||||
const codeMirror = useRef<ReactCodeMirrorRef>();
|
||||
useOnSelect(block.id, (_is_select: boolean) => {
|
||||
const focusCode = () => {
|
||||
if (codeMirror.current) {
|
||||
codeMirror?.current?.view?.focus();
|
||||
}
|
||||
};
|
||||
useOnSelect(block.id, (_is_select: boolean) => {
|
||||
focusCode();
|
||||
});
|
||||
const onChange = (value: string) => {
|
||||
block.setProperty('text', {
|
||||
@ -158,6 +161,9 @@ export const CodeView = ({ block, editor }: CreateCodeView) => {
|
||||
};
|
||||
useEffect(() => {
|
||||
handleLangChange(langType ? langType : DEFAULT_LANG);
|
||||
setTimeout(() => {
|
||||
focusCode();
|
||||
}, 100);
|
||||
}, []);
|
||||
|
||||
const copyCode = () => {
|
||||
|
18
pnpm-lock.yaml
generated
@ -194,11 +194,13 @@ importers:
|
||||
|
||||
apps/ligo-virgo:
|
||||
specifiers:
|
||||
'@emotion/babel-plugin': ^11.10.2
|
||||
'@mui/icons-material': ^5.8.4
|
||||
firebase: ^9.9.3
|
||||
mini-css-extract-plugin: ^2.6.1
|
||||
webpack: ^5.74.0
|
||||
dependencies:
|
||||
'@emotion/babel-plugin': 11.10.2
|
||||
'@mui/icons-material': 5.8.4
|
||||
devDependencies:
|
||||
firebase: 9.9.3
|
||||
@ -213,6 +215,7 @@ importers:
|
||||
|
||||
apps/venus:
|
||||
specifiers:
|
||||
'@emotion/babel-plugin': ^11.10.2
|
||||
'@emotion/react': ^11.10.0
|
||||
'@emotion/styled': ^11.10.0
|
||||
'@mdx-js/loader': ^2.1.3
|
||||
@ -227,6 +230,7 @@ importers:
|
||||
react-i18next: ^11.18.4
|
||||
webpack: ^5.74.0
|
||||
dependencies:
|
||||
'@emotion/babel-plugin': 11.10.2
|
||||
'@emotion/react': 11.10.0
|
||||
'@emotion/styled': 11.10.0_@emotion+react@11.10.0
|
||||
'@mui/joy': 5.0.0-alpha.42_72v32ofbtgpmxm7mhvtx474vfu
|
||||
@ -2681,8 +2685,8 @@ packages:
|
||||
tslib: 2.4.0
|
||||
dev: false
|
||||
|
||||
/@emotion/babel-plugin/11.10.0:
|
||||
resolution: {integrity: sha512-xVnpDAAbtxL1dsuSelU5A7BnY/lftws0wUexNJZTPsvX/1tM4GZJbclgODhvW4E+NH7E5VFcH0bBn30NvniPJA==}
|
||||
/@emotion/babel-plugin/11.10.2:
|
||||
resolution: {integrity: sha512-xNQ57njWTFVfPAc3cjfuaPdsgLp5QOSuRsj9MA6ndEhH/AzuZM86qIQzt6rq+aGBwj3n5/TkLmU5lhAfdRmogA==}
|
||||
peerDependencies:
|
||||
'@babel/core': ^7.0.0
|
||||
peerDependenciesMeta:
|
||||
@ -2811,7 +2815,7 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/babel-plugin': 11.10.0
|
||||
'@emotion/babel-plugin': 11.10.2
|
||||
'@emotion/cache': 11.10.1
|
||||
'@emotion/serialize': 1.1.0
|
||||
'@emotion/utils': 1.2.0
|
||||
@ -2938,7 +2942,7 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.18.6
|
||||
'@emotion/babel-plugin': 11.10.0
|
||||
'@emotion/babel-plugin': 11.10.2
|
||||
'@emotion/is-prop-valid': 1.2.0
|
||||
'@emotion/react': 11.10.0
|
||||
'@emotion/serialize': 1.1.0
|
||||
@ -7417,10 +7421,8 @@ packages:
|
||||
indent-string: 4.0.0
|
||||
dev: true
|
||||
|
||||
/ajv-formats/2.1.1_ajv@8.11.0:
|
||||
/ajv-formats/2.1.1:
|
||||
resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==}
|
||||
peerDependencies:
|
||||
ajv: ^8.0.0
|
||||
peerDependenciesMeta:
|
||||
ajv:
|
||||
optional: true
|
||||
@ -16987,7 +16989,7 @@ packages:
|
||||
dependencies:
|
||||
'@types/json-schema': 7.0.11
|
||||
ajv: 8.11.0
|
||||
ajv-formats: 2.1.1_ajv@8.11.0
|
||||
ajv-formats: 2.1.1
|
||||
ajv-keywords: 5.1.0_ajv@8.11.0
|
||||
dev: true
|
||||
|
||||
|