mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00: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:
@@ -0,0 +1,4 @@
|
||||
export { SettingModal, type SettingModalProps } from './modal';
|
||||
export { SettingHeader } from './setting-header';
|
||||
export { SettingRow } from './setting-row';
|
||||
export { SettingWrapper } from './wrapper';
|
||||
@@ -0,0 +1,42 @@
|
||||
import { Modal, ModalCloseButton, ModalWrapper } from '@affine/component';
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
export type SettingModalProps = {
|
||||
open: boolean;
|
||||
setOpen: (value: boolean) => void;
|
||||
};
|
||||
|
||||
export const SettingModal: FC<PropsWithChildren<SettingModalProps>> = ({
|
||||
children,
|
||||
open,
|
||||
setOpen,
|
||||
}) => {
|
||||
const handleClose = useCallback(() => {
|
||||
setOpen(false);
|
||||
}, [setOpen]);
|
||||
|
||||
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',
|
||||
backgroundColor: 'var(--affine-white)',
|
||||
}}
|
||||
>
|
||||
<ModalCloseButton top={16} right={20} onClick={handleClose} />
|
||||
{children}
|
||||
</ModalWrapper>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
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>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,30 @@
|
||||
import clsx from 'clsx';
|
||||
import type { CSSProperties, FC, PropsWithChildren, ReactElement } from 'react';
|
||||
|
||||
import { settingRow } from './share.css';
|
||||
|
||||
export const SettingRow: FC<
|
||||
PropsWithChildren<{
|
||||
name: string | ReactElement;
|
||||
desc: string | ReactElement;
|
||||
style?: CSSProperties;
|
||||
onClick?: () => void;
|
||||
spreadCol?: boolean;
|
||||
}>
|
||||
> = ({ name, desc, children, onClick, style, spreadCol = true }) => {
|
||||
return (
|
||||
<div
|
||||
className={clsx(settingRow, {
|
||||
'two-col': spreadCol,
|
||||
})}
|
||||
style={style}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="left-col">
|
||||
<div className="name">{name}</div>
|
||||
<div className="desc">{desc}</div>
|
||||
</div>
|
||||
{spreadCol ? <div className="right-col">{children}</div> : children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,82 @@
|
||||
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({
|
||||
marginBottom: '25px',
|
||||
color: 'var(--affine-text-primary-color)',
|
||||
borderRadius: '8px',
|
||||
selectors: {
|
||||
'&.two-col': {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
},
|
||||
'&:last-of-type': {
|
||||
marginBottom: '0',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
globalStyle(`${settingRow} .left-col`, {
|
||||
flexShrink: 0,
|
||||
maxWidth: '100%',
|
||||
});
|
||||
globalStyle(`${settingRow}.two-col .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',
|
||||
flexShrink: 0,
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { FC, PropsWithChildren } from 'react';
|
||||
|
||||
import { wrapper } from './share.css';
|
||||
export const SettingWrapper: FC<
|
||||
PropsWithChildren<{
|
||||
title?: string;
|
||||
}>
|
||||
> = ({ title, children }) => {
|
||||
return (
|
||||
<div className={wrapper}>
|
||||
{title ? <div className="title">{title}</div> : null}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
37
packages/component/src/components/user-avatar/index.tsx
Normal file
37
packages/component/src/components/user-avatar/index.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import * as Avatar from '@radix-ui/react-avatar';
|
||||
import clsx from 'clsx';
|
||||
import type { CSSProperties, FC } from 'react';
|
||||
|
||||
import * as style from './style.css';
|
||||
|
||||
export type UserAvatar = {
|
||||
size?: number;
|
||||
url?: string;
|
||||
name?: string;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
export const UserAvatar: FC<UserAvatar> = ({
|
||||
size = 20,
|
||||
style: propsStyles = {},
|
||||
url,
|
||||
name,
|
||||
className,
|
||||
}) => {
|
||||
return (
|
||||
<Avatar.Root
|
||||
style={{
|
||||
width: size,
|
||||
height: size,
|
||||
...propsStyles,
|
||||
}}
|
||||
className={clsx(style.avatarRoot, className)}
|
||||
>
|
||||
<Avatar.Image className={style.avatarImage} src={url} alt={name} />
|
||||
<Avatar.Fallback className={style.avatarFallback} delayMs={600}>
|
||||
{name?.slice(0, 1) || 'A'}
|
||||
</Avatar.Fallback>
|
||||
</Avatar.Root>
|
||||
);
|
||||
};
|
||||
31
packages/component/src/components/user-avatar/style.css.ts
Normal file
31
packages/component/src/components/user-avatar/style.css.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { style } from '@vanilla-extract/css';
|
||||
|
||||
export const avatarRoot = style({
|
||||
display: 'inline-flex',
|
||||
flexShrink: 0,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
verticalAlign: 'middle',
|
||||
overflow: 'hidden',
|
||||
userSelect: 'none',
|
||||
borderRadius: '100%',
|
||||
});
|
||||
|
||||
export const avatarImage = style({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
borderRadius: 'inherit',
|
||||
});
|
||||
export const avatarFallback = style({
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'var(--affine-primary-color)',
|
||||
color: 'var(--affine-white)',
|
||||
fontSize: 'var(--affine-font-base)',
|
||||
lineHeight: '1',
|
||||
fontWeight: '500',
|
||||
});
|
||||
@@ -10,14 +10,14 @@ export const SIZE_CONFIG = {
|
||||
fontSize: 16,
|
||||
borderRadius: 4,
|
||||
height: 26,
|
||||
padding: 24,
|
||||
padding: 6,
|
||||
},
|
||||
[SIZE_MIDDLE]: {
|
||||
iconSize: 20,
|
||||
fontSize: 16,
|
||||
borderRadius: 4,
|
||||
height: 32,
|
||||
padding: 24,
|
||||
padding: 12,
|
||||
},
|
||||
[SIZE_DEFAULT]: {
|
||||
iconSize: 24,
|
||||
|
||||
@@ -144,6 +144,7 @@
|
||||
"Publishing": "Publishing to web requires AFFiNE Cloud service.",
|
||||
"Share with link": "Share with link",
|
||||
"Copy Link": "Copy Link",
|
||||
"Copy": "Copy",
|
||||
"Publishing Description": "After publishing to the web, everyone can view the content of this workspace through the link.",
|
||||
"Stop publishing": "Stop publishing",
|
||||
"Publish to web": "Publish to web",
|
||||
@@ -245,7 +246,7 @@
|
||||
"Sync across devices with AFFiNE Cloud": "Sync across devices with AFFiNE Cloud",
|
||||
"Update workspace name success": "Update workspace name success",
|
||||
"Create your own workspace": "Create your own workspace",
|
||||
"Storage Folder Hint": "Check or change storage location.",
|
||||
"Storage Folder Hint": "Check or change storage location. Click path to edit location.",
|
||||
"Save": "Save",
|
||||
"Customize": "Customize",
|
||||
"Move folder success": "Move folder success",
|
||||
@@ -359,5 +360,17 @@
|
||||
"Discover what's new": "Discover what's new",
|
||||
"View the AFFiNE Changelog.": "View the AFFiNE Changelog.",
|
||||
"Privacy": "Privacy",
|
||||
"Terms of Use": "Terms of Use"
|
||||
"Terms of Use": "Terms of Use",
|
||||
"Workspace Settings with name": "{{name}}'s Settings",
|
||||
"You can customize your workspace here.": "You can customize your workspace here.",
|
||||
"Info": "Info",
|
||||
"Storage and Export": "Storage and Export",
|
||||
"Workspace Profile": "Workspace Profile",
|
||||
"Only an owner can edit the the Workspace avatar and name.Changes will be shown for everyone.": "Only an owner can edit the the Workspace avatar and name.Changes will be shown for everyone.",
|
||||
"Storage": "Storage",
|
||||
"Workspace saved locally": "{{name}} is saved locally",
|
||||
"Enable cloud hint": "The following functions rely on AFFiNE Cloud. All data is stored on the current device. You can enable AFFiNE Cloud for this workspace to keep data in sync with the cloud.",
|
||||
"Unpublished hint": "Once published to the web, visitors can view the contents through the provided link.",
|
||||
"Published hint": "Visitors can view the contents through the provided link.",
|
||||
"Members hint": "Manage members here, invite new member by email."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user