mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-01 06:10:16 +08:00
feat: migrate workspace setting with new design to setting modal (#2900)
Co-authored-by: Alex Yang <himself65@outlook.com>
This commit is contained in:
@@ -1,14 +0,0 @@
|
||||
import type { FC } from 'react';
|
||||
|
||||
import { settingHeader } from './share.css';
|
||||
export const SettingHeader: FC<{ title: string; subtitle?: string }> = ({
|
||||
title,
|
||||
subtitle,
|
||||
}) => {
|
||||
return (
|
||||
<div className={settingHeader}>
|
||||
<div className="title">{title}</div>
|
||||
<div className="subtitle">{subtitle}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
import type { CSSProperties, FC, PropsWithChildren, ReactElement } from 'react';
|
||||
|
||||
import { settingRow } from './share.css';
|
||||
|
||||
export const SettingRow: FC<
|
||||
PropsWithChildren<{
|
||||
name: string;
|
||||
desc: string | ReactElement;
|
||||
style?: CSSProperties;
|
||||
onClick?: () => void;
|
||||
}>
|
||||
> = ({ name, desc, children, onClick, style }) => {
|
||||
return (
|
||||
<div className={settingRow} style={style} onClick={onClick}>
|
||||
<div className="left-col">
|
||||
<div className="name">{name}</div>
|
||||
<div className="desc">{desc}</div>
|
||||
</div>
|
||||
<div className="right-col">{children}</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,69 +0,0 @@
|
||||
import { globalStyle, style } from '@vanilla-extract/css';
|
||||
|
||||
export const settingHeader = style({
|
||||
height: '68px',
|
||||
borderBottom: '1px solid var(--affine-border-color)',
|
||||
marginBottom: '24px',
|
||||
});
|
||||
|
||||
globalStyle(`${settingHeader} .title`, {
|
||||
fontSize: 'var(--affine-font-base)',
|
||||
fontWeight: 600,
|
||||
lineHeight: '24px',
|
||||
marginBottom: '4px',
|
||||
});
|
||||
|
||||
globalStyle(`${settingHeader} .subtitle`, {
|
||||
fontSize: 'var(--affine-font-xs)',
|
||||
lineHeight: '16px',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
});
|
||||
|
||||
export const wrapper = style({
|
||||
borderBottom: '1px solid var(--affine-border-color)',
|
||||
paddingBottom: '24px',
|
||||
marginBottom: '24px',
|
||||
selectors: {
|
||||
'&:last-of-type': {
|
||||
borderBottom: 'none',
|
||||
paddingBottom: '0',
|
||||
marginBottom: '0',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
globalStyle(`${wrapper} .title`, {
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
fontWeight: 600,
|
||||
lineHeight: '18px',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
marginBottom: '16px',
|
||||
});
|
||||
|
||||
export const settingRow = style({
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
marginBottom: '25px',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
});
|
||||
|
||||
globalStyle(`${settingRow} .left-col`, {
|
||||
flexShrink: 0,
|
||||
maxWidth: '80%',
|
||||
});
|
||||
globalStyle(`${settingRow} .name`, {
|
||||
marginBottom: '2px',
|
||||
fontSize: 'var(--affine-font-sm)',
|
||||
fontWeight: 600,
|
||||
});
|
||||
globalStyle(`${settingRow} .desc`, {
|
||||
fontSize: 'var(--affine-font-xs)',
|
||||
color: 'var(--affine-text-secondary-color)',
|
||||
});
|
||||
globalStyle(`${settingRow} .right-col`, {
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
paddingLeft: '15px',
|
||||
});
|
||||
@@ -1,14 +0,0 @@
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
|
||||
import { wrapper } from './share.css';
|
||||
export const Wrapper: FC<PropsWithChildren<{ title?: string }>> = ({
|
||||
title,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<div className={wrapper}>
|
||||
{title ? <div className="title">{title}</div> : null}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,2 +1,2 @@
|
||||
// Some settings are not implemented yet, but need to show in the setting modal when boss is watching.
|
||||
export const IS_EXHIBITION = false;
|
||||
export const IS_EXHIBITION = true;
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { Switch } from '@affine/component';
|
||||
import { relatedLinks } from '@affine/component/contact-modal';
|
||||
import { isDesktop } from '@affine/env/constant';
|
||||
import { SettingHeader } from '@affine/component/setting-components';
|
||||
import { SettingRow } from '@affine/component/setting-components';
|
||||
import { SettingWrapper } from '@affine/component/setting-components';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { ArrowRightSmallIcon, OpenInNewIcon } from '@blocksuite/icons';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { type AppSetting, useAppSetting } from '../../../../../atoms/settings';
|
||||
import { SettingHeader } from '../../common/setting-header';
|
||||
import { SettingRow } from '../../common/setting-row';
|
||||
import { Wrapper } from '../../common/wrapper';
|
||||
import { IS_EXHIBITION } from '../../config';
|
||||
import { communityItem, communityWrapper, link } from './style.css';
|
||||
|
||||
@@ -24,8 +23,8 @@ export const AboutAffine = () => {
|
||||
return (
|
||||
<>
|
||||
<SettingHeader title={t['About AFFiNE']()} subtitle={t['None yet']()} />
|
||||
{IS_EXHIBITION && isDesktop ? (
|
||||
<Wrapper title={t['Version']()}>
|
||||
{IS_EXHIBITION && environment.isDesktop ? (
|
||||
<SettingWrapper title={t['Version']()}>
|
||||
<SettingRow
|
||||
name={t['Check for updates']()}
|
||||
desc={t['New version is ready']()}
|
||||
@@ -65,9 +64,9 @@ export const AboutAffine = () => {
|
||||
>
|
||||
<ArrowRightSmallIcon />
|
||||
</SettingRow>
|
||||
</Wrapper>
|
||||
</SettingWrapper>
|
||||
) : null}
|
||||
<Wrapper title={t['Contact with us']()}>
|
||||
<SettingWrapper title={t['Contact with us']()}>
|
||||
<a className={link} href="https://affine.pro" target="_blank">
|
||||
{t['Official Website']()}
|
||||
<OpenInNewIcon className="icon" />
|
||||
@@ -76,8 +75,8 @@ export const AboutAffine = () => {
|
||||
{t['AFFiNE Community']()}
|
||||
<OpenInNewIcon className="icon" />
|
||||
</a>
|
||||
</Wrapper>
|
||||
<Wrapper title={t['Communities']()}>
|
||||
</SettingWrapper>
|
||||
<SettingWrapper title={t['Communities']()}>
|
||||
<div className={communityWrapper}>
|
||||
{relatedLinks.map(({ icon, title, link }) => {
|
||||
return (
|
||||
@@ -94,8 +93,8 @@ export const AboutAffine = () => {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</Wrapper>
|
||||
<Wrapper title={t['Info of legal']()}>
|
||||
</SettingWrapper>
|
||||
<SettingWrapper title={t['Info of legal']()}>
|
||||
<a className={link} href="https://affine.pro/privacy" target="_blank">
|
||||
{t['Privacy']()}
|
||||
<OpenInNewIcon className="icon" />
|
||||
@@ -104,7 +103,7 @@ export const AboutAffine = () => {
|
||||
{t['Terms of Use']()}
|
||||
<OpenInNewIcon className="icon" />
|
||||
</a>
|
||||
</Wrapper>
|
||||
</SettingWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { RadioButton, RadioButtonGroup, Switch } from '@affine/component';
|
||||
import { isDesktop } from '@affine/env/constant';
|
||||
import { SettingHeader } from '@affine/component/setting-components';
|
||||
import { SettingRow } from '@affine/component/setting-components';
|
||||
import { SettingWrapper } from '@affine/component/setting-components';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { useTheme } from 'next-themes';
|
||||
import { useCallback } from 'react';
|
||||
@@ -10,9 +12,6 @@ import {
|
||||
windowFrameStyleOptions,
|
||||
} from '../../../../../atoms/settings';
|
||||
import { LanguageMenu } from '../../../language-menu';
|
||||
import { SettingHeader } from '../../common/setting-header';
|
||||
import { SettingRow } from '../../common/setting-row';
|
||||
import { Wrapper } from '../../common/wrapper';
|
||||
import { IS_EXHIBITION } from '../../config';
|
||||
import { DateFormatSetting } from './date-format-setting';
|
||||
import { settingWrapper } from './style.css';
|
||||
@@ -56,7 +55,7 @@ export const AppearanceSettings = () => {
|
||||
subtitle={t['Customize your AFFiNE Appearance']()}
|
||||
/>
|
||||
|
||||
<Wrapper title={t['Theme']()}>
|
||||
<SettingWrapper title={t['Theme']()}>
|
||||
<SettingRow
|
||||
name={t['Color Scheme']()}
|
||||
desc={t['Choose your color scheme']()}
|
||||
@@ -71,7 +70,7 @@ export const AppearanceSettings = () => {
|
||||
<LanguageMenu />
|
||||
</div>
|
||||
</SettingRow>
|
||||
{IS_EXHIBITION && isDesktop ? (
|
||||
{IS_EXHIBITION && environment.isDesktop ? (
|
||||
<SettingRow
|
||||
name={t['Client Border Style']()}
|
||||
desc={t['Customize the appearance of the client.']()}
|
||||
@@ -92,7 +91,7 @@ export const AppearanceSettings = () => {
|
||||
onChange={checked => changeSwitch('fullWidthLayout', checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
{IS_EXHIBITION && isDesktop ? (
|
||||
{IS_EXHIBITION && environment.isDesktop ? (
|
||||
<SettingRow
|
||||
name={t['Window frame style']()}
|
||||
desc={t['Customize appearance of Windows Client.']()}
|
||||
@@ -114,9 +113,9 @@ export const AppearanceSettings = () => {
|
||||
</RadioButtonGroup>
|
||||
</SettingRow>
|
||||
) : null}
|
||||
</Wrapper>
|
||||
</SettingWrapper>
|
||||
{IS_EXHIBITION ? (
|
||||
<Wrapper title={t['Date']()}>
|
||||
<SettingWrapper title={t['Date']()}>
|
||||
<SettingRow
|
||||
name={t['Date Format']()}
|
||||
desc={t['Customize your date style.']()}
|
||||
@@ -134,11 +133,11 @@ export const AppearanceSettings = () => {
|
||||
onChange={checked => changeSwitch('startWeekOnMonday', checked)}
|
||||
/>
|
||||
</SettingRow>
|
||||
</Wrapper>
|
||||
</SettingWrapper>
|
||||
) : null}
|
||||
|
||||
{isDesktop ? (
|
||||
<Wrapper title={t['Sidebar']()}>
|
||||
{environment.isDesktop ? (
|
||||
<SettingWrapper title={t['Sidebar']()}>
|
||||
<SettingRow
|
||||
name={t['Disable the noise background on the sidebar']()}
|
||||
desc={t['None yet']()}
|
||||
@@ -161,7 +160,7 @@ export const AppearanceSettings = () => {
|
||||
}
|
||||
/>
|
||||
</SettingRow>
|
||||
</Wrapper>
|
||||
</SettingWrapper>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { style } from '@vanilla-extract/css';
|
||||
export const settingWrapper = style({
|
||||
flexGrow: 1,
|
||||
display: 'flex',
|
||||
width: '50%',
|
||||
justifyContent: 'flex-end',
|
||||
minWidth: '150px',
|
||||
maxWidth: '250px',
|
||||
});
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { SettingHeader } from '@affine/component/setting-components';
|
||||
import { SettingWrapper } from '@affine/component/setting-components';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
|
||||
import {
|
||||
@@ -6,8 +8,6 @@ import {
|
||||
useMarkdownShortcuts,
|
||||
usePageShortcuts,
|
||||
} from '../../../../../hooks/affine/use-shortcuts';
|
||||
import { SettingHeader } from '../../common/setting-header';
|
||||
import { Wrapper } from '../../common/wrapper';
|
||||
import { shortcutRow } from './style.css';
|
||||
|
||||
export const Shortcuts = () => {
|
||||
@@ -24,7 +24,7 @@ export const Shortcuts = () => {
|
||||
title={t['Keyboard Shortcuts']()}
|
||||
subtitle={t['Check Keyboard Shortcuts quickly']()}
|
||||
/>
|
||||
<Wrapper title={t['General']()}>
|
||||
<SettingWrapper title={t['General']()}>
|
||||
{Object.entries(generalShortcuts).map(([title, shortcuts]) => {
|
||||
return (
|
||||
<div key={title} className={shortcutRow}>
|
||||
@@ -33,8 +33,8 @@ export const Shortcuts = () => {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Wrapper>
|
||||
<Wrapper title={t['Page']()}>
|
||||
</SettingWrapper>
|
||||
<SettingWrapper title={t['Page']()}>
|
||||
{Object.entries(pageShortcuts).map(([title, shortcuts]) => {
|
||||
return (
|
||||
<div key={title} className={shortcutRow}>
|
||||
@@ -43,8 +43,8 @@ export const Shortcuts = () => {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Wrapper>
|
||||
<Wrapper title={t['Edgeless']()}>
|
||||
</SettingWrapper>
|
||||
<SettingWrapper title={t['Edgeless']()}>
|
||||
{Object.entries(edgelessShortcuts).map(([title, shortcuts]) => {
|
||||
return (
|
||||
<div key={title} className={shortcutRow}>
|
||||
@@ -53,8 +53,8 @@ export const Shortcuts = () => {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Wrapper>
|
||||
<Wrapper title={t['Markdown Syntax']()}>
|
||||
</SettingWrapper>
|
||||
<SettingWrapper title={t['Markdown Syntax']()}>
|
||||
{Object.entries(markdownShortcuts).map(([title, shortcuts]) => {
|
||||
return (
|
||||
<div key={title} className={shortcutRow}>
|
||||
@@ -63,7 +63,7 @@ export const Shortcuts = () => {
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</Wrapper>
|
||||
</SettingWrapper>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { Modal, ModalCloseButton, ModalWrapper } from '@affine/component';
|
||||
import {
|
||||
SettingModal as SettingModalBase,
|
||||
type SettingModalProps,
|
||||
} from '@affine/component/setting-components';
|
||||
import type {
|
||||
AffineLegacyCloudWorkspace,
|
||||
LocalWorkspace,
|
||||
@@ -6,13 +9,11 @@ import type {
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import { ContactWithUsIcon } from '@blocksuite/icons';
|
||||
import type { NextRouter } from 'next/router';
|
||||
import type React from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
|
||||
import { useCurrentWorkspace } from '../../../hooks/current/use-current-workspace';
|
||||
import { useWorkspaces } from '../../../hooks/use-workspaces';
|
||||
import type { BlockSuiteWorkspace } from '../../../shared';
|
||||
import { AccountSetting } from './account-setting';
|
||||
import {
|
||||
GeneralSetting,
|
||||
@@ -24,15 +25,7 @@ import { settingContent } from './style.css';
|
||||
import type { Workspace } from './type';
|
||||
import { WorkSpaceSetting } from './workspace-setting';
|
||||
|
||||
export type QuickSearchModalProps = {
|
||||
currentWorkspace?: BlockSuiteWorkspace;
|
||||
workspaceList?: BlockSuiteWorkspace[];
|
||||
open: boolean;
|
||||
setOpen: (value: boolean) => void;
|
||||
router: NextRouter;
|
||||
};
|
||||
|
||||
export const SettingModal: React.FC<QuickSearchModalProps> = ({
|
||||
export const SettingModal: React.FC<SettingModalProps> = ({
|
||||
open,
|
||||
setOpen,
|
||||
}) => {
|
||||
@@ -55,9 +48,6 @@ export const SettingModal: React.FC<QuickSearchModalProps> = ({
|
||||
generalKey: generalSettingList[0].key,
|
||||
isAccount: false,
|
||||
});
|
||||
const handleClose = useCallback(() => {
|
||||
setOpen(false);
|
||||
}, [setOpen]);
|
||||
|
||||
const onGeneralSettingClick = useCallback((key: GeneralSettingKeys) => {
|
||||
setCurrentRef({
|
||||
@@ -82,59 +72,41 @@ export const SettingModal: React.FC<QuickSearchModalProps> = ({
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
wrapperPosition={['center', 'center']}
|
||||
data-testid="setting-modal"
|
||||
>
|
||||
<ModalWrapper
|
||||
width={1080}
|
||||
height={760}
|
||||
style={{
|
||||
maxHeight: '85vh',
|
||||
maxWidth: '70vw',
|
||||
overflow: 'hidden',
|
||||
display: 'flex',
|
||||
}}
|
||||
>
|
||||
<ModalCloseButton top={16} right={20} onClick={handleClose} />
|
||||
<SettingModalBase open={open} setOpen={setOpen}>
|
||||
<SettingSidebar
|
||||
generalSettingList={generalSettingList}
|
||||
onGeneralSettingClick={onGeneralSettingClick}
|
||||
currentWorkspace={
|
||||
currentWorkspace as AffineLegacyCloudWorkspace | LocalWorkspace
|
||||
}
|
||||
workspaceList={workspaceList}
|
||||
onWorkspaceSettingClick={onWorkspaceSettingClick}
|
||||
selectedGeneralKey={currentRef.generalKey}
|
||||
selectedWorkspace={currentRef.workspace}
|
||||
onAccountSettingClick={onAccountSettingClick}
|
||||
/>
|
||||
|
||||
<SettingSidebar
|
||||
generalSettingList={generalSettingList}
|
||||
onGeneralSettingClick={onGeneralSettingClick}
|
||||
currentWorkspace={
|
||||
currentWorkspace as AffineLegacyCloudWorkspace | LocalWorkspace
|
||||
}
|
||||
workspaceList={workspaceList}
|
||||
onWorkspaceSettingClick={onWorkspaceSettingClick}
|
||||
selectedGeneralKey={currentRef.generalKey}
|
||||
selectedWorkspace={currentRef.workspace}
|
||||
onAccountSettingClick={onAccountSettingClick}
|
||||
/>
|
||||
|
||||
<div className={settingContent}>
|
||||
<div className="wrapper">
|
||||
<div className="content">
|
||||
{currentRef.workspace ? (
|
||||
<WorkSpaceSetting workspace={currentRef.workspace} />
|
||||
) : null}
|
||||
{currentRef.generalKey ? (
|
||||
<GeneralSetting generalKey={currentRef.generalKey} />
|
||||
) : null}
|
||||
{currentRef.isAccount ? <AccountSetting /> : null}
|
||||
</div>
|
||||
<div className="footer">
|
||||
<ContactWithUsIcon />
|
||||
<a href="https://community.affine.pro/home" target="_blank">
|
||||
{t[
|
||||
'Need more customization options? You can suggest them to us in the community.'
|
||||
]()}
|
||||
</a>
|
||||
</div>
|
||||
<div className={settingContent}>
|
||||
<div className="wrapper">
|
||||
<div className="content">
|
||||
{currentRef.workspace ? (
|
||||
<WorkSpaceSetting workspace={currentRef.workspace} />
|
||||
) : null}
|
||||
{currentRef.generalKey ? (
|
||||
<GeneralSetting generalKey={currentRef.generalKey} />
|
||||
) : null}
|
||||
{currentRef.isAccount ? <AccountSetting /> : null}
|
||||
</div>
|
||||
<div className="footer">
|
||||
<ContactWithUsIcon />
|
||||
<a href="https://community.affine.pro/home" target="_blank">
|
||||
{t[
|
||||
'Need more customization options? You can suggest them to us in the community.'
|
||||
]()}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</ModalWrapper>
|
||||
</Modal>
|
||||
</div>
|
||||
</SettingModalBase>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { UserAvatar } from '@affine/component/user-avatar';
|
||||
import { WorkspaceAvatar } from '@affine/component/workspace-avatar';
|
||||
import type {
|
||||
AffineLegacyCloudWorkspace,
|
||||
@@ -5,6 +6,7 @@ import type {
|
||||
} from '@affine/env/workspace';
|
||||
import { useBlockSuiteWorkspaceName } from '@toeverything/hooks/use-block-suite-workspace-name';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import type {
|
||||
GeneralSettingKeys,
|
||||
@@ -84,7 +86,13 @@ export const SettingSidebar = ({
|
||||
</div>
|
||||
|
||||
<div className={accountButton} onClick={onAccountSettingClick}>
|
||||
<div className="avatar"></div>
|
||||
<UserAvatar
|
||||
size={28}
|
||||
name="Account NameAccount Name"
|
||||
url={''}
|
||||
className="avatar"
|
||||
/>
|
||||
|
||||
<div className="content">
|
||||
<div className="name" title="xxx">
|
||||
Account NameAccount Name
|
||||
|
||||
@@ -3,9 +3,7 @@ import { globalStyle, style } from '@vanilla-extract/css';
|
||||
export const settingSlideBar = style({
|
||||
width: '25%',
|
||||
maxWidth: '242px',
|
||||
// TODO: use color variable
|
||||
// background: 'var(--affine-background-secondary-color)',
|
||||
backgroundColor: '#F4F4F5',
|
||||
background: 'var(--affine-background-secondary-color)',
|
||||
padding: '20px 16px',
|
||||
height: '100%',
|
||||
flexShrink: 0,
|
||||
@@ -107,14 +105,9 @@ export const accountButton = style({
|
||||
});
|
||||
|
||||
globalStyle(`${accountButton} .avatar`, {
|
||||
width: '28px',
|
||||
height: '28px',
|
||||
border: '1px solid',
|
||||
borderColor: 'var(--affine-white)',
|
||||
borderRadius: '14px',
|
||||
flexShrink: '0',
|
||||
marginRight: '10px',
|
||||
background: 'red',
|
||||
});
|
||||
globalStyle(`${accountButton} .content`, {
|
||||
flexGrow: '1',
|
||||
|
||||
Reference in New Issue
Block a user