fix(core): adjust sidebar explorer empty status (#7657)

Close AF-1126,AF-1132
This commit is contained in:
CatsJuice
2024-07-31 02:41:53 +00:00
parent 083123cdfb
commit ede576061d
15 changed files with 116 additions and 238 deletions

View File

@@ -1,4 +1,5 @@
import { cssVar } from '@toeverything/theme'; import { cssVar } from '@toeverything/theme';
import { cssVarV2 } from '@toeverything/theme/v2';
import { globalStyle, style } from '@vanilla-extract/css'; import { globalStyle, style } from '@vanilla-extract/css';
export const dropdownBtn = style({ export const dropdownBtn = style({
display: 'inline-flex', display: 'inline-flex',
@@ -124,8 +125,8 @@ export const button = style({
// changeable // changeable
height: '28px', height: '28px',
background: cssVar('white'), background: cssVar('white'),
borderColor: cssVar('borderColor'), borderColor: cssVarV2('layer/border'),
color: cssVar('textPrimaryColor'), color: cssVarV2('text/primary'),
selectors: { selectors: {
'&.text-bold': { '&.text-bold': {
fontWeight: 600, fontWeight: 600,

View File

@@ -1,4 +1,5 @@
import { cssVar } from '@toeverything/theme'; import { cssVar } from '@toeverything/theme';
import { cssVarV2 } from '@toeverything/theme/v2';
import { style } from '@vanilla-extract/css'; import { style } from '@vanilla-extract/css';
export const content = style({ export const content = style({
@@ -6,8 +7,8 @@ export const content = style({
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
gap: 6, gap: 4,
padding: '9px 20px 25px 21px', padding: '12px 0px',
}); });
export const iconWrapper = style({ export const iconWrapper = style({
width: 36, width: 36,
@@ -16,21 +17,24 @@ export const iconWrapper = style({
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
borderRadius: '50%', borderRadius: '50%',
backgroundColor: cssVar('hoverColor'), backgroundColor: cssVarV2('button/emptyIconBackground'),
}); });
export const icon = style({ export const icon = style({
fontSize: 20, fontSize: 20,
color: cssVar('iconSecondary'), color: cssVarV2('icon/secondary'),
}); });
export const message = style({ export const message = style({
fontSize: cssVar('fontSm'), fontSize: cssVar('fontSm'),
textAlign: 'center', textAlign: 'center',
color: cssVar('black30'), color: cssVarV2('text/tertiary'),
userSelect: 'none', userSelect: 'none',
fontWeight: 400,
lineHeight: '22px',
}); });
export const newButton = style({ export const newButton = style({
padding: '0 8px', marginTop: 8,
height: '28px', padding: '4px 8px',
fontSize: cssVar('fontXs'), height: '30px',
fontSize: cssVar('fontSm'),
}); });

View File

@@ -0,0 +1,49 @@
import { Button } from '@affine/component';
import clsx from 'clsx';
import {
forwardRef,
type HTMLAttributes,
type Ref,
type SVGProps,
} from 'react';
import * as styles from './empty-layout.css';
interface ExplorerGroupEmptyProps extends HTMLAttributes<HTMLDivElement> {
icon: (props: SVGProps<SVGSVGElement>) => JSX.Element;
message: string;
messageTestId?: string;
actionText?: string;
onActionClick?: () => void;
}
export const ExplorerGroupEmpty = forwardRef(function ExplorerGroupEmpty(
{
icon: Icon,
message,
messageTestId,
actionText,
children,
className,
onActionClick,
...attrs
}: ExplorerGroupEmptyProps,
ref: Ref<HTMLDivElement>
) {
return (
<div className={clsx(styles.content, className)} ref={ref} {...attrs}>
<div className={styles.iconWrapper}>
<Icon className={styles.icon} />
</div>
<div data-testid={messageTestId} className={styles.message}>
{message}
</div>
{actionText ? (
<Button className={styles.newButton} onClick={onActionClick}>
{actionText}
</Button>
) : null}
{children}
</div>
);
});

View File

@@ -1,39 +1,6 @@
import { cssVar } from '@toeverything/theme'; import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css'; import { style } from '@vanilla-extract/css';
export const content = style({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 6,
padding: '9px 20px 25px 21px',
});
export const iconWrapper = style({
width: 36,
height: 36,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '50%',
backgroundColor: cssVar('hoverColor'),
});
export const icon = style({
fontSize: 20,
color: cssVar('iconSecondary'),
});
export const message = style({
fontSize: cssVar('fontSm'),
textAlign: 'center',
color: cssVar('black30'),
userSelect: 'none',
});
export const newButton = style({
padding: '0 8px',
height: '28px',
fontSize: cssVar('fontXs'),
});
export const draggedOverHighlight = style({ export const draggedOverHighlight = style({
selectors: { selectors: {
'&[data-dragged-over="true"]': { '&[data-dragged-over="true"]': {

View File

@@ -1,5 +1,4 @@
import { import {
Button,
type DropTargetDropEvent, type DropTargetDropEvent,
type DropTargetOptions, type DropTargetOptions,
useDropTarget, useDropTarget,
@@ -9,6 +8,7 @@ import { useI18n } from '@affine/i18n';
import { FolderIcon } from '@blocksuite/icons/rc'; import { FolderIcon } from '@blocksuite/icons/rc';
import clsx from 'clsx'; import clsx from 'clsx';
import { ExplorerGroupEmpty } from '../../layouts/empty-layout';
import * as styles from './empty.css'; import * as styles from './empty.css';
export const FolderEmpty = ({ export const FolderEmpty = ({
@@ -32,22 +32,16 @@ export const FolderEmpty = ({
const t = useI18n(); const t = useI18n();
return ( return (
<div <ExplorerGroupEmpty
className={clsx(styles.content, styles.draggedOverHighlight, className)} className={clsx(styles.draggedOverHighlight, className)}
ref={dropTargetRef} ref={dropTargetRef}
> icon={FolderIcon}
<div className={styles.iconWrapper}> message={t['com.affine.rootAppSidebar.organize.empty-folder']()}
<FolderIcon className={styles.icon} /> messageTestId="slider-bar-organize-empty-message"
</div> actionText={t[
<div 'com.affine.rootAppSidebar.organize.empty-folder.add-pages'
data-testid="slider-bar-organize-empty-message" ]()}
className={styles.message} onActionClick={onClickCreate}
> />
{t['com.affine.rootAppSidebar.organize.empty-folder']()}
</div>
<Button className={styles.newButton} onClick={onClickCreate}>
{t['com.affine.rootAppSidebar.organize.empty-folder.add-pages']()}
</Button>
</div>
); );
}; };

View File

@@ -814,7 +814,11 @@ export const ExplorerFolderNodeFolder = ({
operations={finalOperations} operations={finalOperations}
canDrop={handleCanDrop} canDrop={handleCanDrop}
childrenPlaceholder={ childrenPlaceholder={
<FolderEmpty canDrop={handleCanDrop} onDrop={handleDropOnPlaceholder} /> <FolderEmpty
canDrop={handleCanDrop}
onDrop={handleDropOnPlaceholder}
onClickCreate={() => handleAddToFolder('doc')}
/>
} }
dropEffect={handleDropEffect} dropEffect={handleDropEffect}
data-testid={`explorer-folder-${node.id}`} data-testid={`explorer-folder-${node.id}`}

View File

@@ -1,35 +0,0 @@
import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';
export const content = style({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 6,
padding: '9px 20px 25px 21px',
});
export const iconWrapper = style({
width: 36,
height: 36,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '50%',
backgroundColor: cssVar('hoverColor'),
});
export const icon = style({
fontSize: 20,
color: cssVar('iconSecondary'),
});
export const message = style({
fontSize: cssVar('fontSm'),
textAlign: 'center',
color: cssVar('black30'),
userSelect: 'none',
});
export const newButton = style({
padding: '0 8px',
height: '28px',
fontSize: cssVar('fontXs'),
});

View File

@@ -1,8 +1,7 @@
import { Button } from '@affine/component';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { ViewLayersIcon } from '@blocksuite/icons/rc'; import { ViewLayersIcon } from '@blocksuite/icons/rc';
import * as styles from './empty.css'; import { ExplorerGroupEmpty } from '../../layouts/empty-layout';
export const RootEmpty = ({ export const RootEmpty = ({
onClickCreate, onClickCreate,
@@ -12,19 +11,12 @@ export const RootEmpty = ({
const t = useI18n(); const t = useI18n();
return ( return (
<div className={styles.content}> <ExplorerGroupEmpty
<div className={styles.iconWrapper}> icon={ViewLayersIcon}
<ViewLayersIcon className={styles.icon} /> message={t['com.affine.collections.empty.message']()}
</div> messageTestId="slider-bar-collection-empty-message"
<div actionText={t['com.affine.collections.empty.new-collection-button']()}
data-testid="slider-bar-collection-empty-message" onActionClick={onClickCreate}
className={styles.message} />
>
{t['com.affine.collections.empty.message']()}
</div>
<Button className={styles.newButton} onClick={onClickCreate}>
{t['com.affine.collections.empty.new-collection-button']()}
</Button>
</div>
); );
}; };

View File

@@ -5,10 +5,10 @@ import {
} from '@affine/component'; } from '@affine/component';
import type { AffineDNDData } from '@affine/core/types/dnd'; import type { AffineDNDData } from '@affine/core/types/dnd';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { FolderIcon } from '@blocksuite/icons/rc'; import { FavoriteIcon } from '@blocksuite/icons/rc';
import { ExplorerGroupEmpty } from '../../layouts/empty-layout';
import { DropEffect, type ExplorerTreeNodeDropEffect } from '../../tree'; import { DropEffect, type ExplorerTreeNodeDropEffect } from '../../tree';
import * as styles from './empty.css';
export const RootEmpty = ({ export const RootEmpty = ({
onDrop, onDrop,
@@ -34,16 +34,12 @@ export const RootEmpty = ({
); );
return ( return (
<div className={styles.content} ref={dropTargetRef}> <ExplorerGroupEmpty
<div className={styles.iconWrapper}> ref={dropTargetRef}
<FolderIcon className={styles.icon} /> icon={FavoriteIcon}
</div> message={t['com.affine.rootAppSidebar.favorites.empty']()}
<div messageTestId="slider-bar-favorites-empty-message"
data-testid="slider-bar-favorites-empty-message"
className={styles.message}
> >
{t['com.affine.rootAppSidebar.favorites.empty']()}
</div>
{dropEffect && draggedOverDraggable && ( {dropEffect && draggedOverDraggable && (
<DropEffect <DropEffect
position={{ position={{
@@ -56,6 +52,6 @@ export const RootEmpty = ({
})} })}
/> />
)} )}
</div> </ExplorerGroupEmpty>
); );
}; };

View File

@@ -1,36 +0,0 @@
import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';
export const content = style({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 6,
padding: '9px 20px 25px 21px',
position: 'relative',
});
export const iconWrapper = style({
width: 36,
height: 36,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '50%',
backgroundColor: cssVar('hoverColor'),
});
export const icon = style({
fontSize: 20,
color: cssVar('iconSecondary'),
});
export const message = style({
fontSize: cssVar('fontSm'),
textAlign: 'center',
color: cssVar('black30'),
userSelect: 'none',
});
export const newButton = style({
padding: '0 8px',
height: '28px',
fontSize: cssVar('fontXs'),
});

View File

@@ -1,8 +1,7 @@
import { Button } from '@affine/component';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { FolderIcon } from '@blocksuite/icons/rc'; import { FolderIcon } from '@blocksuite/icons/rc';
import * as styles from './empty.css'; import { ExplorerGroupEmpty } from '../../layouts/empty-layout';
export const RootEmpty = ({ export const RootEmpty = ({
onClickCreate, onClickCreate,
@@ -12,19 +11,14 @@ export const RootEmpty = ({
const t = useI18n(); const t = useI18n();
return ( return (
<div className={styles.content}> <ExplorerGroupEmpty
<div className={styles.iconWrapper}> icon={FolderIcon}
<FolderIcon className={styles.icon} /> message={t['com.affine.rootAppSidebar.organize.empty']()}
</div> messageTestId="slider-bar-organize-empty-message"
<div actionText={t[
data-testid="slider-bar-organize-empty-message" 'com.affine.rootAppSidebar.organize.empty.new-folders-button'
className={styles.message} ]()}
> onActionClick={onClickCreate}
{t['com.affine.rootAppSidebar.organize.empty']()} />
</div>
<Button className={styles.newButton} onClick={onClickCreate}>
{t['com.affine.rootAppSidebar.organize.empty.new-folders-button']()}
</Button>
</div>
); );
}; };

View File

@@ -1,36 +0,0 @@
import { cssVar } from '@toeverything/theme';
import { style } from '@vanilla-extract/css';
export const content = style({
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: 6,
padding: '9px 20px 25px 21px',
position: 'relative',
});
export const iconWrapper = style({
width: 36,
height: 36,
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '50%',
backgroundColor: cssVar('hoverColor'),
});
export const icon = style({
fontSize: 20,
color: cssVar('iconSecondary'),
});
export const message = style({
fontSize: cssVar('fontSm'),
textAlign: 'center',
color: cssVar('black30'),
userSelect: 'none',
});
export const newButton = style({
padding: '0 8px',
height: '28px',
fontSize: cssVar('fontXs'),
});

View File

@@ -1,30 +1,16 @@
import { Button } from '@affine/component';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { TagIcon } from '@blocksuite/icons/rc'; import { TagIcon } from '@blocksuite/icons/rc';
import * as styles from './empty.css'; import { ExplorerGroupEmpty } from '../../layouts/empty-layout';
export const RootEmpty = ({ export const RootEmpty = () => {
onClickCreate,
}: {
onClickCreate?: () => void;
}) => {
const t = useI18n(); const t = useI18n();
return ( return (
<div className={styles.content}> <ExplorerGroupEmpty
<div className={styles.iconWrapper}> icon={TagIcon}
<TagIcon className={styles.icon} /> message={t['com.affine.rootAppSidebar.tags.empty']()}
</div> messageTestId="slider-bar-tags-empty-message"
<div />
data-testid="slider-bar-tags-empty-message"
className={styles.message}
>
{t['com.affine.rootAppSidebar.tags.empty']()}
</div>
<Button className={styles.newButton} onClick={onClickCreate}>
{t['com.affine.rootAppSidebar.tags.empty.new-tag-button']()}
</Button>
</div>
); );
}; };

View File

@@ -69,9 +69,7 @@ export const ExplorerTags = ({
</IconButton> </IconButton>
</CategoryDivider> </CategoryDivider>
<Collapsible.Content> <Collapsible.Content>
<ExplorerTreeRoot <ExplorerTreeRoot placeholder={<RootEmpty />}>
placeholder={<RootEmpty onClickCreate={handleCreateNewFavoriteDoc} />}
>
{tags.map(tag => ( {tags.map(tag => (
<ExplorerTagNode <ExplorerTagNode
key={tag.id} key={tag.id}

View File

@@ -1149,7 +1149,7 @@
"com.affine.rootAppSidebar.explorer.drop-effect.move": "Move", "com.affine.rootAppSidebar.explorer.drop-effect.move": "Move",
"com.affine.rootAppSidebar.explorer.drop-effect.copy": "Copy", "com.affine.rootAppSidebar.explorer.drop-effect.copy": "Copy",
"com.affine.rootAppSidebar.docs.no-subdoc": "No linked docs", "com.affine.rootAppSidebar.docs.no-subdoc": "No linked docs",
"com.affine.rootAppSidebar.favorites.empty": "You can add documents to your favourites", "com.affine.rootAppSidebar.favorites.empty": "No Favorites",
"com.affine.rootAppSidebar.others": "Others", "com.affine.rootAppSidebar.others": "Others",
"com.affine.rootAppSidebar.docs.references-loading": "Loading linked docs...", "com.affine.rootAppSidebar.docs.references-loading": "Loading linked docs...",
"com.affine.search-tags.placeholder": "Type here ...", "com.affine.search-tags.placeholder": "Type here ...",