feat(core): add responsive styles to registration page (#5044)

The responsive style of the login and registration page has been adjusted, with special treatment given to the input.
work for #4843
This commit is contained in:
JimmFly
2023-12-06 10:43:13 +00:00
parent 3e92942bb5
commit 7ec8e49b3b
18 changed files with 338 additions and 185 deletions

View File

@@ -18,6 +18,7 @@ export const runtimeFlagsSchema = z.object({
enableBroadcastChannelProvider: z.boolean(),
enableDebugPage: z.boolean(),
changelogUrl: z.string(),
downloadUrl: z.string(),
// see: tools/workers
imageProxyUrl: z.string(),
enablePreloading: z.boolean(),

View File

@@ -40,6 +40,11 @@ export const tipsContainer = style({
position: 'sticky',
gap: '16px',
containerType: 'inline-size',
'@media': {
'screen and (max-width: 520px)': {
flexWrap: 'wrap',
},
},
});
export const tipsMessage = style({
@@ -54,4 +59,9 @@ export const tipsRightItem = style({
justifyContent: 'space-between',
alignItems: 'center',
gap: '16px',
'@media': {
'screen and (max-width: 520px)': {
width: '100%',
},
},
});

View File

@@ -0,0 +1,24 @@
import * as styles from './index.css';
import { useNavConfig } from './use-nav-config';
export const DesktopNavbar = () => {
const config = useNavConfig();
return (
<div className={styles.topNavLinks}>
{config.map(item => {
return (
<a
key={item.title}
href={item.path}
target="_blank"
rel="noreferrer"
className={styles.topNavLink}
>
{item.title}
</a>
);
})}
</div>
);
};

View File

@@ -0,0 +1,89 @@
import { style } from '@vanilla-extract/css';
export const root = style({
height: '100vh',
width: '100vw',
display: 'flex',
flexDirection: 'column',
fontSize: 'var(--affine-font-base)',
position: 'relative',
background: 'var(--affine-background-primary-color)',
});
export const affineLogo = style({
color: 'inherit',
});
export const topNav = style({
top: 0,
left: 0,
right: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '16px 120px',
selectors: {
'&.mobile': {
padding: '16px 20px',
},
},
});
export const topNavLinks = style({
display: 'flex',
columnGap: 4,
});
export const topNavLink = style({
color: 'var(--affine-text-primary-color)',
fontSize: 'var(--affine-font-sm)',
fontWeight: 500,
textDecoration: 'none',
padding: '4px 18px',
});
export const iconButton = style({
fontSize: '24px',
pointerEvents: 'auto',
selectors: {
'&.plain': {
color: 'var(--affine-text-primary-color)',
},
},
});
export const menu = style({
width: '100vw',
height: '100vh',
padding: '0',
background: 'var(--affine-background-primary-color)',
borderRadius: '0',
border: 'none',
boxShadow: 'none',
});
export const menuItem = style({
color: 'var(--affine-text-primary-color)',
fontSize: 'var(--affine-font-sm)',
fontWeight: 500,
textDecoration: 'none',
padding: '12px 20px',
maxWidth: '100%',
position: 'relative',
borderRadius: '0',
transition: 'background 0.3s ease',
selectors: {
'&:after': {
position: 'absolute',
content: '""',
bottom: 0,
display: 'block',
width: 'calc(100% - 40px)',
height: '0.5px',
background: 'var(--affine-black-10)',
},
'&:not(:last-of-type)': {
marginBottom: '0',
},
},
});

View File

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

View File

@@ -0,0 +1,49 @@
import { Button } from '@affine/component/ui/button';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Logo1Icon } from '@blocksuite/icons';
import clsx from 'clsx';
import { useCallback } from 'react';
import { DesktopNavbar } from './desktop-navbar';
import * as styles from './index.css';
import { MobileNavbar } from './mobile-navbar';
export const AffineOtherPageLayout = ({
isSmallScreen,
children,
}: {
isSmallScreen: boolean;
children: React.ReactNode;
}) => {
const t = useAFFiNEI18N();
const openDownloadLink = useCallback(() => {
open(runtimeConfig.downloadUrl, '_blank');
}, []);
return (
<div className={styles.root}>
<div
className={clsx(styles.topNav, {
mobile: isSmallScreen,
})}
>
<a href="/" rel="noreferrer" className={styles.affineLogo}>
<Logo1Icon width={24} height={24} />
</a>
{isSmallScreen ? (
<MobileNavbar />
) : (
<>
<DesktopNavbar />
<Button onClick={openDownloadLink}>
{t['com.affine.auth.open.affine.download-app']()}
</Button>
</>
)}
</div>
{children}
</div>
);
};

View File

@@ -0,0 +1,50 @@
import { IconButton } from '@affine/component/ui/button';
import { Menu, MenuItem } from '@affine/component/ui/menu';
import { CloseIcon, PropertyIcon } from '@blocksuite/icons';
import { useState } from 'react';
import * as styles from './index.css';
import { useNavConfig } from './use-nav-config';
export const MobileNavbar = () => {
const [openMenu, setOpenMenu] = useState(false);
const navConfig = useNavConfig();
const menuItems = (
<>
{navConfig.map(item => {
return (
<MenuItem
key={item.title}
onClick={() => {
open(item.path, '_blank');
}}
className={styles.menuItem}
>
{item.title}
</MenuItem>
);
})}
</>
);
return (
<div>
<Menu
items={menuItems}
contentOptions={{
className: styles.menu,
sideOffset: 20,
}}
rootOptions={{
open: openMenu,
onOpenChange: setOpenMenu,
}}
>
<IconButton type="plain" className={styles.iconButton}>
{openMenu ? <CloseIcon /> : <PropertyIcon />}
</IconButton>
</Menu>
</div>
);
};

View File

@@ -0,0 +1,27 @@
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { useMemo } from 'react';
export const useNavConfig = () => {
const t = useAFFiNEI18N();
return useMemo(
() => [
{
title: t['com.affine.other-page.nav.official-website'](),
path: 'https://affine.pro',
},
{
title: t['com.affine.other-page.nav.affine-community'](),
path: 'https://community.affine.pro/home',
},
{
title: t['com.affine.other-page.nav.blog'](),
path: 'https://affine.pro/blog',
},
{
title: t['com.affine.other-page.nav.contact-us'](),
path: 'https://affine.pro/about-us',
},
],
[t]
);
};

View File

@@ -2,7 +2,7 @@ import clsx from 'clsx';
import type { FC, HTMLAttributes } from 'react';
import { Input, type InputProps } from '../../ui/input';
import { authInputWrapper, formHint } from './share.css';
import * as styles from './share.css';
export type AuthInputProps = InputProps & {
label?: string;
error?: boolean;
@@ -22,13 +22,14 @@ export const AuthInput: FC<AuthInputProps> = ({
}) => {
return (
<div
className={clsx(authInputWrapper, className, {
className={clsx(styles.authInputWrapper, className, {
'without-hint': withoutHint,
})}
{...otherWrapperProps}
>
{label ? <label>{label}</label> : null}
<Input
className={styles.input}
size="extraLarge"
status={error ? 'error' : 'default'}
onKeyDown={e => {
@@ -40,7 +41,7 @@ export const AuthInput: FC<AuthInputProps> = ({
/>
{error && errorHint && !withoutHint ? (
<div
className={clsx(formHint, {
className={clsx(styles.formHint, {
error: error,
})}
>

View File

@@ -1,32 +1,41 @@
import type { FC, PropsWithChildren, ReactNode } from 'react';
import {
type FC,
type PropsWithChildren,
type ReactNode,
useEffect,
useState,
} from 'react';
import { Empty } from '../../ui/empty';
import { Wrapper } from '../../ui/layout';
import { Logo } from './logo';
import { AffineOtherPageLayout } from '../affine-other-page-layout';
import { authPageContainer } from './share.css';
export const AuthPageContainer: FC<
PropsWithChildren<{ title?: ReactNode; subtitle?: ReactNode }>
> = ({ children, title, subtitle }) => {
const [isSmallScreen, setIsSmallScreen] = useState(false);
useEffect(() => {
const checkScreenSize = () => {
setIsSmallScreen(window.innerWidth <= 1024);
};
checkScreenSize();
window.addEventListener('resize', checkScreenSize);
return () => window.removeEventListener('resize', checkScreenSize);
}, []);
return (
<div className={authPageContainer}>
<Wrapper
style={{
position: 'absolute',
top: 25,
left: 20,
}}
>
<Logo />
</Wrapper>
<div className="wrapper">
<div className="content">
<p className="title">{title}</p>
<p className="subtitle">{subtitle}</p>
{children}
<AffineOtherPageLayout isSmallScreen={isSmallScreen}>
<div className={authPageContainer}>
<div className="wrapper">
<div className="content">
<p className="title">{title}</p>
<p className="subtitle">{subtitle}</p>
{children}
</div>
{isSmallScreen ? null : <Empty />}
</div>
<Empty />
</div>
</div>
</AffineOtherPageLayout>
);
};

View File

@@ -44,7 +44,6 @@ export const ChangeEmailPage = ({
>
<>
<AuthInput
width={320}
label={t['com.affine.settings.email']()}
placeholder={t['com.affine.auth.sign.email.placeholder']()}
value={email}

View File

@@ -4,6 +4,7 @@ import { type FC, useEffect } from 'react';
import { useCallback, useState } from 'react';
import { Input, type InputProps } from '../../../ui/input';
import * as styles from '../share.css';
import { ErrorIcon } from './error';
import { SuccessIcon } from './success';
import { Tag } from './tag';
@@ -74,6 +75,7 @@ export const PasswordInput: FC<
return (
<>
<Input
className={styles.input}
type="password"
size="extraLarge"
style={{ marginBottom: 20 }}
@@ -83,6 +85,7 @@ export const PasswordInput: FC<
{...inputProps}
/>
<Input
className={styles.input}
type="password"
size="extraLarge"
placeholder={t['com.affine.auth.set.password.placeholder.confirm']()}

View File

@@ -19,7 +19,6 @@ export const SetPassword: FC<{
<>
<Wrapper marginTop={30} marginBottom={42}>
<PasswordInput
width={320}
onPass={useCallback(password => {
setPasswordPass(true);
passwordRef.current = password;

View File

@@ -151,14 +151,25 @@ export const authPageContainer = style({
justifyContent: 'center',
alignItems: 'center',
fontSize: 'var(--affine-font-base)',
'@media': {
'screen and (max-width: 1024px)': {
flexDirection: 'column',
padding: '100px 20px',
justifyContent: 'flex-start',
},
},
});
globalStyle(`${authPageContainer} .wrapper`, {
display: 'flex',
alignItems: 'center',
'@media': {
'screen and (max-width: 1024px)': {
flexDirection: 'column',
},
},
});
globalStyle(`${authPageContainer} .content`, {
maxWidth: '700px',
minWidth: '550px',
});
globalStyle(`${authPageContainer} .title`, {
@@ -178,3 +189,12 @@ export const signInPageContainer = style({
width: '400px',
margin: '205px auto 0',
});
export const input = style({
width: '330px',
'@media': {
'screen and (max-width: 520px)': {
width: '100%',
},
},
});

View File

@@ -8,6 +8,7 @@ export const notFoundPageContainer = style({
alignItems: 'center',
justifyContent: 'center',
width: '100vw',
padding: '0 20px',
});
export const wrapper = style({

View File

@@ -20,6 +20,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
enableBroadcastChannelProvider: true,
enableDebugPage: true,
changelogUrl: 'https://affine.pro/what-is-new',
downloadUrl: 'https://affine.pro/download',
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
enablePreloading: true,
enableNewSettingModal: true,
@@ -67,6 +68,7 @@ export function getRuntimeConfig(buildFlags: BuildFlags): RuntimeConfig {
enableBroadcastChannelProvider: true,
enableDebugPage: true,
changelogUrl: 'https://github.com/toeverything/AFFiNE/releases',
downloadUrl: 'https://affine.pro/download',
imageProxyUrl: 'https://workers.toeverything.workers.dev/proxy/image',
enablePreloading: true,
enableNewSettingModal: true,

View File

@@ -1,87 +1,9 @@
import { style } from '@vanilla-extract/css';
export const root = style({
height: '100vh',
width: '100vw',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
fontSize: 'var(--affine-font-base)',
position: 'relative',
});
export const affineLogo = style({
color: 'inherit',
});
export const topNav = style({
position: 'absolute',
top: 0,
left: 0,
right: 0,
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
padding: '16px 120px',
});
export const topNavLinks = style({
display: 'flex',
columnGap: 4,
});
export const topNavLink = style({
color: 'var(--affine-text-primary-color)',
fontSize: 'var(--affine-font-sm)',
fontWeight: 500,
textDecoration: 'none',
padding: '4px 18px',
});
export const tryAgainLink = style({
color: 'var(--affine-link-color)',
fontWeight: 500,
textDecoration: 'none',
fontSize: 'var(--affine-font-sm)',
});
export const centerContent = style({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
marginTop: 40,
});
export const prompt = style({
marginTop: 20,
marginBottom: 12,
});
export const body = style({
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: '100%',
flexWrap: 'wrap',
gap: '48px',
padding: '0 20px',
});
export const leftContainer = style({
display: 'flex',
flexDirection: 'column',
width: '548px',
gap: '28px',
});
export const leftContentTitle = style({
fontSize: 'var(--affine-font-title)',
fontWeight: 700,
minHeight: '44px',
});
export const leftContentText = style({
fontSize: 'var(--affine-font-base)',
fontWeight: 400,
lineHeight: '1.6',
maxWidth: '548px',
});
export const mail = style({

View File

@@ -1,8 +1,7 @@
import { Empty } from '@affine/component';
import { AuthPageContainer } from '@affine/component/auth-components';
import { Button } from '@affine/component/ui/button';
import { Trans } from '@affine/i18n';
import { useAFFiNEI18N } from '@affine/i18n/hooks';
import { Logo1Icon } from '@blocksuite/icons';
import { useCallback } from 'react';
import { useNavigateHelper } from '../hooks/use-navigate-helper';
@@ -11,93 +10,40 @@ import * as styles from './upgrade-success.css';
export const UpgradeSuccess = () => {
const t = useAFFiNEI18N();
const openDownloadLink = useCallback(() => {
const url = `https://affine.pro/download`;
open(url, '_blank');
}, []);
const { jumpToIndex } = useNavigateHelper();
const openAffine = useCallback(() => {
jumpToIndex();
}, [jumpToIndex]);
return (
<div className={styles.root}>
<div className={styles.topNav}>
<a href="/" rel="noreferrer" className={styles.affineLogo}>
<Logo1Icon width={24} height={24} />
</a>
<div className={styles.topNavLinks}>
<a
href="https://affine.pro"
target="_blank"
rel="noreferrer"
className={styles.topNavLink}
>
{t['com.affine.other-page.nav.official-website']()}
</a>
<a
href="https://community.affine.pro/home"
target="_blank"
rel="noreferrer"
className={styles.topNavLink}
>
{t['com.affine.other-page.nav.affine-community']()}
</a>
<a
href="https://affine.pro/blog"
target="_blank"
rel="noreferrer"
className={styles.topNavLink}
>
{t['com.affine.other-page.nav.blog']()}
</a>
<a
href="https://affine.pro/about-us"
target="_blank"
rel="noreferrer"
className={styles.topNavLink}
>
{t['com.affine.other-page.nav.contact-us']()}
</a>
</div>
<Button onClick={openDownloadLink}>
{t['com.affine.auth.open.affine.download-app']()}
</Button>
</div>
<div className={styles.body}>
<div className={styles.leftContainer}>
<div className={styles.leftContentTitle}>
{t['com.affine.payment.upgrade-success-page.title']()}
</div>
<div className={styles.leftContentText}>
{t['com.affine.payment.upgrade-success-page.text']()}
<div>
<Trans
i18nKey={'com.affine.payment.upgrade-success-page.support'}
components={{
1: (
<a
href="mailto:support@toeverything.info"
className={styles.mail}
/>
),
}}
const subtitle = (
<div className={styles.leftContentText}>
{t['com.affine.payment.upgrade-success-page.text']()}
<div>
<Trans
i18nKey={'com.affine.payment.upgrade-success-page.support'}
components={{
1: (
<a
href="mailto:support@toeverything.info"
className={styles.mail}
/>
</div>
</div>
<div>
<Button type="primary" size="extraLarge" onClick={openAffine}>
{t['com.affine.other-page.nav.open-affine']()}
</Button>
</div>
</div>
<Empty />
),
}}
/>
</div>
</div>
);
return (
<AuthPageContainer
title={t['com.affine.payment.upgrade-success-page.title']()}
subtitle={subtitle}
>
<Button type="primary" size="extraLarge" onClick={openAffine}>
{t['com.affine.other-page.nav.open-affine']()}
</Button>
</AuthPageContainer>
);
};
export const Component = () => {