Merge branch 'master' into develop

This commit is contained in:
DarkSky
2022-09-08 20:20:52 +08:00
21 changed files with 875 additions and 663 deletions
+1
View File
@@ -10,6 +10,7 @@
"keywords": [],
"author": "AFFiNE <developer@affine.pro>",
"dependencies": {
"@emotion/babel-plugin": "^11.10.2",
"@mui/icons-material": "^5.8.4"
},
"devDependencies": {
+1
View File
@@ -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",
+1 -1
View File
@@ -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',
-600
View File
@@ -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 />
</>
);
}
@@ -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>
);
};
@@ -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
View 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
View 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

+131 -52
View File
@@ -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
View 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',
},
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

+4 -1
View File
@@ -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 -1
View File
@@ -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": "它不仅仅是一个文档、白板和表格的集合。",
+2 -2
View File
@@ -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
@@ -45,9 +45,11 @@ import { DuplicateIcon } from '@toeverything/components/icons';
import { Option, Select, styled } from '@toeverything/components/ui';
import { CreateView } from '@toeverything/framework/virgo';
import { copyToClipboard } from '@toeverything/utils';
import { elixir } from 'codemirror-lang-elixir';
import { useEffect, useRef, useState } from 'react';
import { StyleWithAtRules } from 'style9';
import CodeMirror, { ReactCodeMirrorRef } from './CodeMirror';
interface CreateCodeView extends CreateView {
+8 -4
View File
@@ -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
@@ -2813,8 +2817,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:
@@ -2920,7 +2924,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
@@ -3022,7 +3026,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