fix(core): add favorite folder in menu, adjust empty-page new page button (#7730)

close AF-1150, AF-1128, AF-1131
- Replace favorite migration related copy
- Adjust empty page's "New Page" button
  ![CleanShot 2024-08-05 at 15.16.06@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/1cf7d75a-a33a-4eec-9dc1-87d50d9526f1.png)
- Add toggle favorite to folder menu
  ![CleanShot 2024-08-05 at 15.17.50@2x.png](https://graphite-user-uploaded-assets-prod.s3.amazonaws.com/LakojjjzZNf6ogjOVwKE/af6116b5-47d1-49a6-9660-41c0d7cd8fd3.png)
- Adjust `Button`
  - add `withoutHover` state
  - remove cursor: not-allowed when disabled
This commit is contained in:
CatsJuice
2024-08-05 09:15:16 +00:00
parent 73a6723d15
commit 6d253c0600
9 changed files with 121 additions and 29 deletions

View File

@@ -79,6 +79,10 @@ export const button = style({
lineHeight: lineHeightVar,
selectors: {
// hover layer
'&[data-no-hover]:before, &[data-disabled]:before': {
display: 'none',
},
'&:hover:before': { opacity: 1 },
'&[data-block]': { display: 'flex' },
@@ -162,7 +166,6 @@ export const button = style({
// disabled
'&[data-disabled]': {
cursor: 'not-allowed',
opacity: 0.5,
},

View File

@@ -43,6 +43,9 @@ export interface ButtonProps
*/
loading?: boolean;
/** No hover state */
withoutHover?: boolean;
/**
* By default, it is considered as an icon with preset size and color,
* can be overridden by `prefixClassName` and `prefixStyle`.
@@ -103,6 +106,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
block,
loading,
className,
withoutHover,
prefix,
prefixClassName,
@@ -142,6 +146,7 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
data-disabled={disabled || undefined}
data-size={size}
data-variant={variant}
data-no-hover={withoutHover || undefined}
onClick={handleClick}
>
<IconSlot

View File

@@ -107,7 +107,8 @@ const NameWorkspaceContent = ({
confirmText={t['com.affine.nameWorkspace.button.create']()}
confirmButtonOptions={{
variant: 'primary',
disabled: !workspaceName || loading,
loading,
disabled: !workspaceName,
['data-testid' as string]: 'create-workspace-create-button',
}}
closeButtonOptions={{

View File

@@ -19,6 +19,7 @@ import {
type FolderNode,
OrganizeService,
} from '@affine/core/modules/organize';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
import { WorkbenchService } from '@affine/core/modules/workbench';
import type { AffineDNDData } from '@affine/core/types/dnd';
import { Unreachable } from '@affine/env/constant';
@@ -43,6 +44,7 @@ import { ExplorerDocNode } from '../doc';
import { ExplorerTagNode } from '../tag';
import type { GenericExplorerNode } from '../types';
import { FolderEmpty } from './empty';
import { FavoriteFolderOperation } from './operations';
export const ExplorerFolderNode = ({
nodeId,
@@ -164,6 +166,7 @@ export const ExplorerFolderNodeFolder = ({
const { docsService, workbenchService } = useServices({
DocsService,
WorkbenchService,
CompatibleFavoriteItemsAdapter,
});
const openDocsSelector = useSelectDoc();
const openTagsSelector = useSelectTag();
@@ -710,6 +713,17 @@ export const ExplorerFolderNodeFolder = ({
</MenuSub>
),
},
// favorite, only new favorite available
...(runtimeConfig.enableNewFavorite && node.id
? [
{
index: 200,
view: <FavoriteFolderOperation id={node.id} />,
},
]
: []),
{
index: 9999,
view: <MenuSeparator key="menu-separator" />,
@@ -731,7 +745,14 @@ export const ExplorerFolderNodeFolder = ({
),
},
];
}, [handleAddToFolder, handleCreateSubfolder, handleDelete, handleNewDoc, t]);
}, [
handleAddToFolder,
handleCreateSubfolder,
handleDelete,
handleNewDoc,
node.id,
t,
]);
const finalOperations = useMemo(() => {
if (additionalOperations) {

View File

@@ -0,0 +1,39 @@
import { MenuIcon, MenuItem } from '@affine/component';
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/properties';
import { useI18n } from '@affine/i18n';
import { FavoritedIcon, FavoriteIcon } from '@blocksuite/icons/rc';
import { useLiveData, useService } from '@toeverything/infra';
import { cssVar } from '@toeverything/theme';
import { useMemo } from 'react';
export const FavoriteFolderOperation = ({ id }: { id: string }) => {
const t = useI18n();
const compatibleFavoriteItemsAdapter = useService(
CompatibleFavoriteItemsAdapter
);
const favorite = useLiveData(
useMemo(() => {
return compatibleFavoriteItemsAdapter.isFavorite$(id, 'folder');
}, [compatibleFavoriteItemsAdapter, id])
);
return (
<MenuItem
preFix={
<MenuIcon>
{favorite ? (
<FavoritedIcon style={{ color: cssVar('primaryColor') }} />
) : (
<FavoriteIcon />
)}
</MenuIcon>
}
onClick={() => compatibleFavoriteItemsAdapter.toggle(id, 'folder')}
>
{favorite
? t['com.affine.rootAppSidebar.organize.folder-rm-favorite']()
: t['com.affine.rootAppSidebar.organize.folder-add-favorite']()}
</MenuItem>
);
};

View File

@@ -7,7 +7,7 @@ import { LiveData, Service } from '@toeverything/infra';
import { defaultsDeep } from 'lodash-es';
import { Observable } from 'rxjs';
import type { FavoriteService } from '../../favorite';
import type { FavoriteService, FavoriteSupportType } from '../../favorite';
import {
PagePropertyType,
PageSystemPropertyId,
@@ -305,6 +305,10 @@ export class FavoriteItemsAdapter extends Service {
}
}
type CompatibleFavoriteSupportType =
| WorkspaceFavoriteItem['type']
| FavoriteSupportType;
/**
* A service written for compatibility,with the same API as FavoriteItemsAdapter.
* When `runtimeConfig.enableNewFavorite` is false, it operates FavoriteItemsAdapter,
@@ -318,24 +322,29 @@ export class CompatibleFavoriteItemsAdapter extends Service {
super();
}
toggle(id: string, type: WorkspaceFavoriteItem['type']) {
if (runtimeConfig.enableNewFavorite) {
// old adapter only supports doc and collection
private isNewType(type: CompatibleFavoriteSupportType) {
return type !== 'doc' && type !== 'collection';
}
toggle(id: string, type: CompatibleFavoriteSupportType) {
if (runtimeConfig.enableNewFavorite || this.isNewType(type)) {
this.favoriteService.favoriteList.toggle(type, id);
} else {
this.favoriteItemsAdapter.toggle(id, type);
}
}
isFavorite$(id: string, type: WorkspaceFavoriteItem['type']) {
if (runtimeConfig.enableNewFavorite) {
isFavorite$(id: string, type: CompatibleFavoriteSupportType) {
if (runtimeConfig.enableNewFavorite || this.isNewType(type)) {
return this.favoriteService.favoriteList.isFavorite$(type, id);
} else {
return this.favoriteItemsAdapter.isFavorite$(id, type);
}
}
isFavorite(id: string, type: WorkspaceFavoriteItem['type']) {
if (runtimeConfig.enableNewFavorite) {
isFavorite(id: string, type: CompatibleFavoriteSupportType) {
if (runtimeConfig.enableNewFavorite || this.isNewType(type)) {
return this.favoriteService.favoriteList.isFavorite$(type, id).value;
} else {
return this.favoriteItemsAdapter.isFavorite(id, type);

View File

@@ -1,4 +1,5 @@
import { cssVar } from '@toeverything/theme';
import { cssVarV2 } from '@toeverything/theme/v2';
import { style } from '@vanilla-extract/css';
export const pageListEmptyStyle = style({
height: 'calc(100% - 52px)',
@@ -23,3 +24,15 @@ export const emptyDescKbd = style([
cursor: 'text',
},
]);
export const plusButton = style({
borderWidth: 1,
borderColor: cssVarV2('layer/border'),
boxShadow: 'none',
cursor: 'default',
});
export const descWrapper = style({
display: 'flex',
alignItems: 'center',
gap: 8,
});

View File

@@ -1,33 +1,30 @@
import { Empty } from '@affine/component';
import { Empty, IconButton } from '@affine/component';
import { Trans, useI18n } from '@affine/i18n';
import { PlusIcon } from '@blocksuite/icons/rc';
import type { DocCollection } from '@blocksuite/store';
import type { ReactNode } from 'react';
import { useCallback } from 'react';
import { usePageHelper } from '../../components/blocksuite/block-suite-page-list/utils';
import * as styles from './page-list-empty.css';
export const EmptyPageList = ({
type,
docCollection,
heading,
}: {
type: 'all' | 'trash' | 'shared' | 'public';
docCollection: DocCollection;
heading?: ReactNode;
}) => {
const { createPage } = usePageHelper(docCollection);
const t = useI18n();
const onCreatePage = useCallback(() => {
createPage?.();
}, [createPage]);
const getEmptyDescription = () => {
if (type === 'all') {
const createNewPageButton = (
<button className={styles.emptyDescButton} onClick={onCreatePage}>
{t['New Page']()}
</button>
<IconButton
withoutHover
className={styles.plusButton}
variant="solid"
icon={<PlusIcon />}
/>
);
if (environment.isDesktop) {
const shortcut = environment.isMacOs ? '⌘ + N' : 'Ctrl + N';
@@ -61,7 +58,9 @@ export const EmptyPageList = ({
{heading && <div>{heading}</div>}
<Empty
title={t['com.affine.emptyDesc']()}
description={getEmptyDescription()}
description={
<span className={styles.descWrapper}>{getEmptyDescription()}</span>
}
/>
</div>
);

View File

@@ -1118,12 +1118,12 @@
"com.affine.rootAppSidebar.collections": "Collections",
"com.affine.rootAppSidebar.favorites": "Favourites",
"com.affine.rootAppSidebar.migration-data": "Migration data",
"com.affine.rootAppSidebar.migration-data.help": "Migration your favorites data",
"com.affine.rootAppSidebar.migration-data.help.description": "This is the old Favourites data. Previously, Favourites were shared during collaboration. We have redesigned the Favourites feature, which requires you to take certain actions. You can drag this content or directly hide and delete this part of the data.",
"com.affine.rootAppSidebar.migration-data.help.clean-all": "Clear all migration data",
"com.affine.rootAppSidebar.migration-data.help": "The old \"Favorites\" will be replaced",
"com.affine.rootAppSidebar.migration-data.help.description": "Your documents are safe, yet you may need to re-pin your most-used ones. Previously, 'Favorites' were shared across the workspace. We've improved on this - now each person has a personal 'Favorites' section for their top documents, collections, and folders. We advise migrating your data.\nThe old 'Favorites' will disappear once emptied. Drag your items with ease from the old shared 'Favorites' into your new personal section, or opt to delete all old favorites by simply clicking 'Delete all the old data' now.",
"com.affine.rootAppSidebar.migration-data.help.clean-all": "Delete all the old data",
"com.affine.rootAppSidebar.migration-data.help.confirm": "OK",
"com.affine.rootAppSidebar.migration-data.clean-all": "Clean all migration data",
"com.affine.rootAppSidebar.migration-data.clean-all.description": "This will delete all Migration data, don't worry, <b>it won't delete any data entities</b>, it will just clean up the content on the sidebar. Once the content is cleared, the sidebar will no longer display this section until the next occurrence of conflicting data.",
"com.affine.rootAppSidebar.migration-data.clean-all": "Delete All Old Data",
"com.affine.rootAppSidebar.migration-data.clean-all.description": "This action will empty and delete the entire discontinued Favorites section. Don't worry, <b>all your documents will not be affected - this only removes the old Favorites from the sidebar to place the new one</b>. In the meantime, please verify that you've moved all your frequently accessed documents to the spanking new personal Favorites section first.",
"com.affine.rootAppSidebar.migration-data.clean-all.confirm": "OK",
"com.affine.rootAppSidebar.migration-data.clean-all.cancel": "Cancel",
"com.affine.rootAppSidebar.organize": "Organize",
@@ -1140,6 +1140,8 @@
"com.affine.rootAppSidebar.organize.delete": "Delete",
"com.affine.rootAppSidebar.organize.delete-from-folder": "Remove from folder",
"com.affine.rootAppSidebar.organize.root-folder-only": "Only folder can be placed on here",
"com.affine.rootAppSidebar.organize.folder-add-favorite": "Add to favorites",
"com.affine.rootAppSidebar.organize.folder-rm-favorite": "Remove from favorites",
"com.affine.rootAppSidebar.tags": "Tags",
"com.affine.rootAppSidebar.tags.empty": "No Tags",
"com.affine.rootAppSidebar.tags.new-tag": "New Tag",
@@ -1393,8 +1395,8 @@
"com.affine.syncing": "Syncing",
"core": "core",
"dark": "Dark",
"emptyAllPages": "Click on the <1>$t(New Doc)</1> button to create your first doc.",
"emptyAllPagesClient": "Click on the <1>$t(New Doc)</1> button Or press <3>{{shortcut}}</3> to create your first doc.",
"emptyAllPages": "Click on the <1></1> button to create your first doc.",
"emptyAllPagesClient": "Click on the <1></1> button Or press <3>{{shortcut}}</3> to create your first doc.",
"emptyFavorite": "Click Add to Favourites and the doc will appear here.",
"emptySharedPages": "Shared docs will appear here.",
"emptyTrash": "Click Add to Trash and the doc will appear here.",