From 0472ffe569c717ab7e09722ce801e7a75f02cba9 Mon Sep 17 00:00:00 2001 From: EYHN Date: Mon, 29 Jul 2024 09:57:33 +0000 Subject: [PATCH] feat(core): support sidebar collapse (#7630) --- .../app-sidebar/category-divider/index.css.ts | 15 ++++- .../app-sidebar/category-divider/index.tsx | 31 +++++++++- .../src/components/root-app-sidebar/index.tsx | 8 ++- .../views/sections/collections/index.tsx | 55 ++++++++++------ .../views/sections/collections/styles.css.ts | 2 +- .../views/sections/favorites/index.tsx | 62 ++++++++++++------- .../views/sections/favorites/styles.css.ts | 2 +- .../sections/migration-favorites/index.tsx | 39 ++++++++---- .../migration-favorites/styles.css.ts | 2 +- .../views/sections/old-favorites/index.tsx | 56 ++++++++++------- .../sections/old-favorites/styles.css.ts | 2 +- .../views/sections/organize/index.tsx | 53 +++++++++------- .../views/sections/organize/styles.css.ts | 2 +- .../explorer/views/sections/tags/index.tsx | 47 ++++++++------ .../views/sections/tags/styles.css.ts | 2 +- tests/affine-cloud/e2e/collaboration.spec.ts | 1 + .../e2e/local-first-collections-items.spec.ts | 7 +++ 17 files changed, 257 insertions(+), 129 deletions(-) diff --git a/packages/frontend/core/src/components/app-sidebar/category-divider/index.css.ts b/packages/frontend/core/src/components/app-sidebar/category-divider/index.css.ts index 1894851362..f56fb678e9 100644 --- a/packages/frontend/core/src/components/app-sidebar/category-divider/index.css.ts +++ b/packages/frontend/core/src/components/app-sidebar/category-divider/index.css.ts @@ -20,5 +20,18 @@ export const root = style({ }); export const label = style({ color: cssVar('black30'), - flex: '1', + flexGrow: '0', + display: 'flex', + alignItems: 'center', + justifyContent: 'start', + cursor: 'pointer', +}); + +export const collapseButton = style({ + selectors: { + [`${label} > &`]: { + color: cssVar('black30'), + transform: 'translateY(1px)', + }, + }, }); diff --git a/packages/frontend/core/src/components/app-sidebar/category-divider/index.tsx b/packages/frontend/core/src/components/app-sidebar/category-divider/index.tsx index 13a06697af..dfc85bf149 100644 --- a/packages/frontend/core/src/components/app-sidebar/category-divider/index.tsx +++ b/packages/frontend/core/src/components/app-sidebar/category-divider/index.tsx @@ -1,3 +1,5 @@ +import { IconButton } from '@affine/component'; +import { ToggleCollapseIcon, ToggleExpandIcon } from '@blocksuite/icons/rc'; import clsx from 'clsx'; import { type ForwardedRef, forwardRef, type PropsWithChildren } from 'react'; @@ -7,6 +9,8 @@ export type CategoryDividerProps = PropsWithChildren< { label: string; className?: string; + collapsed?: boolean; + setCollapsed?: (collapsed: boolean) => void; } & { [key: `data-${string}`]: unknown; } @@ -14,12 +18,35 @@ export type CategoryDividerProps = PropsWithChildren< export const CategoryDivider = forwardRef( ( - { label, children, className, ...otherProps }: CategoryDividerProps, + { + label, + children, + className, + collapsed, + setCollapsed, + ...otherProps + }: CategoryDividerProps, ref: ForwardedRef ) => { return (
-
{label}
+
setCollapsed?.(!collapsed)} + > + {label} + {collapsed !== undefined && ( + + {collapsed ? : } + + )} +
+
{children}
); diff --git a/packages/frontend/core/src/components/root-app-sidebar/index.tsx b/packages/frontend/core/src/components/root-app-sidebar/index.tsx index fa0b501bc7..4a2dcf6a1a 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/index.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/index.tsx @@ -166,9 +166,11 @@ export const RootAppSidebar = memo( {runtimeConfig.enableNewFavorite && } {runtimeConfig.enableOrganize && } {runtimeConfig.enableNewFavorite && } - {runtimeConfig.enableOldFavorite && } - - + {runtimeConfig.enableOldFavorite && ( + + )} + + {/* fixme: remove the following spacer */}
diff --git a/packages/frontend/core/src/modules/explorer/views/sections/collections/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/collections/index.tsx index c4da803f9c..68360a4219 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/collections/index.tsx +++ b/packages/frontend/core/src/modules/explorer/views/sections/collections/index.tsx @@ -7,20 +7,26 @@ import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree'; import { WorkbenchService } from '@affine/core/modules/workbench'; import { useI18n } from '@affine/i18n'; import { PlusIcon } from '@blocksuite/icons/rc'; +import * as Collapsible from '@radix-ui/react-collapsible'; import { useLiveData, useServices } from '@toeverything/infra'; import { nanoid } from 'nanoid'; -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; import { ExplorerCollectionNode } from '../../nodes/collection'; import { RootEmpty } from './empty'; import * as styles from './styles.css'; -export const ExplorerCollections = () => { +export const ExplorerCollections = ({ + defaultCollapsed = false, +}: { + defaultCollapsed?: boolean; +}) => { const t = useI18n(); const { collectionService, workbenchService } = useServices({ CollectionService, WorkbenchService, }); + const [collapsed, setCollapsed] = useState(defaultCollapsed); const collections = useLiveData(collectionService.collections$); const { node, open: openCreateCollectionModel } = useEditCollectionName({ title: t['com.affine.editCollection.createCollection'](), @@ -33,6 +39,7 @@ export const ExplorerCollections = () => { const id = nanoid(); collectionService.addCollection(createEmptyCollection(id, { name })); workbenchService.workbench.openCollection(id); + setCollapsed(false); }) .catch(err => { console.error(err); @@ -41,8 +48,16 @@ export const ExplorerCollections = () => { return ( <> -
- + + { - } - > - {collections.map(collection => ( - - ))} - -
+ + } + > + {collections.map(collection => ( + + ))} + + + {node} ); diff --git a/packages/frontend/core/src/modules/explorer/views/sections/collections/styles.css.ts b/packages/frontend/core/src/modules/explorer/views/sections/collections/styles.css.ts index c69d8ce569..702eb729e8 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/collections/styles.css.ts +++ b/packages/frontend/core/src/modules/explorer/views/sections/collections/styles.css.ts @@ -1,5 +1,5 @@ import { style } from '@vanilla-extract/css'; export const container = style({ - marginTop: '16px', + marginTop: '8px', }); diff --git a/packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx index c2184b210c..faa4395e97 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx +++ b/packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx @@ -19,8 +19,9 @@ import { WorkbenchService } from '@affine/core/modules/workbench'; import type { AffineDNDData } from '@affine/core/types/dnd'; import { useI18n } from '@affine/i18n'; import { PlusIcon } from '@blocksuite/icons/rc'; +import * as Collapsible from '@radix-ui/react-collapsible'; import { DocsService, useLiveData, useServices } from '@toeverything/infra'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { ExplorerCollectionNode } from '../../nodes/collection'; import { ExplorerDocNode } from '../../nodes/doc'; @@ -29,12 +30,17 @@ import { ExplorerTagNode } from '../../nodes/tag'; import { RootEmpty } from './empty'; import * as styles from './styles.css'; -export const ExplorerFavorites = () => { +export const ExplorerFavorites = ({ + defaultCollapsed = false, +}: { + defaultCollapsed?: boolean; +}) => { const { favoriteService, docsService, workbenchService } = useServices({ FavoriteService, DocsService, WorkbenchService, }); + const [collapsed, setCollapsed] = useState(defaultCollapsed); const favorites = useLiveData(favoriteService.favoriteList.sortedList$); @@ -51,6 +57,7 @@ export const ExplorerFavorites = () => { data.source.data.entity.id, favoriteService.favoriteList.indexAt('before') ); + setCollapsed(false); } }, [favoriteService] @@ -83,6 +90,7 @@ export const ExplorerFavorites = () => { favoriteService.favoriteList.indexAt('before') ); workbenchService.workbench.openDoc(newDoc.id); + setCollapsed(false); }, [docsService, favoriteService, workbenchService]); const handleOnChildrenDrop = useCallback( @@ -179,12 +187,18 @@ export const ExplorerFavorites = () => { ); return ( -
+ { /> )} - - } - > - {favorites.map(favorite => ( - - ))} - -
+ + + } + > + {favorites.map(favorite => ( + + ))} + + + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/sections/favorites/styles.css.ts b/packages/frontend/core/src/modules/explorer/views/sections/favorites/styles.css.ts index f7fa0fe231..00458330b3 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/favorites/styles.css.ts +++ b/packages/frontend/core/src/modules/explorer/views/sections/favorites/styles.css.ts @@ -2,7 +2,7 @@ import { cssVar } from '@toeverything/theme'; import { style } from '@vanilla-extract/css'; export const container = style({ - marginTop: '16px', + marginTop: '8px', }); export const draggedOverHighlight = style({ diff --git a/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/index.tsx index 048021d2c1..c94604aeb5 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/index.tsx +++ b/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/index.tsx @@ -4,14 +4,21 @@ import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree'; import { FavoriteItemsAdapter } from '@affine/core/modules/properties'; import { Trans, useI18n } from '@affine/i18n'; import { BroomIcon, HelpIcon } from '@blocksuite/icons/rc'; +import * as Collapsible from '@radix-ui/react-collapsible'; import { DocsService, useLiveData, useServices } from '@toeverything/infra'; -import { useCallback } from 'react'; +import { useCallback, useState } from 'react'; import { ExplorerCollectionNode } from '../../nodes/collection'; import { ExplorerDocNode } from '../../nodes/doc'; import * as styles from './styles.css'; -export const ExplorerMigrationFavorites = () => { +export const ExplorerMigrationFavorites = ({ + defaultCollapsed = false, +}: { + defaultCollapsed?: boolean; +}) => { + const [collapsed, setCollapsed] = useState(defaultCollapsed); + const t = useI18n(); const { favoriteItemsAdapter, docsService } = useServices({ @@ -89,8 +96,12 @@ export const ExplorerMigrationFavorites = () => { } return ( -
- + + { - - {favorites.map((favorite, i) => ( - - ))} - -
+ + + {favorites.map((favorite, i) => ( + + ))} + + + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/styles.css.ts b/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/styles.css.ts index 3da2d9e8e7..86a60389c6 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/styles.css.ts +++ b/packages/frontend/core/src/modules/explorer/views/sections/migration-favorites/styles.css.ts @@ -2,7 +2,7 @@ import { cssVar } from '@toeverything/theme'; import { style } from '@vanilla-extract/css'; export const container = style({ - marginTop: '16px', + marginTop: '8px', position: 'relative', selectors: { '&:after': { diff --git a/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/index.tsx index 4f8d60b96b..621bda2fe8 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/index.tsx +++ b/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/index.tsx @@ -13,8 +13,9 @@ import { WorkbenchService } from '@affine/core/modules/workbench'; import type { AffineDNDData } from '@affine/core/types/dnd'; import { useI18n } from '@affine/i18n'; import { PlusIcon } from '@blocksuite/icons/rc'; +import * as Collapsible from '@radix-ui/react-collapsible'; import { DocsService, useLiveData, useServices } from '@toeverything/infra'; -import { useCallback, useMemo } from 'react'; +import { useCallback, useMemo, useState } from 'react'; import { ExplorerCollectionNode } from '../../nodes/collection'; import { ExplorerDocNode } from '../../nodes/doc'; @@ -24,12 +25,17 @@ import * as styles from './styles.css'; /** * @deprecated remove this after 0.17 released */ -export const ExplorerOldFavorites = () => { +export const ExplorerOldFavorites = ({ + defaultCollapsed = false, +}: { + defaultCollapsed?: boolean; +}) => { const { favoriteItemsAdapter, docsService, workbenchService } = useServices({ FavoriteItemsAdapter, DocsService, WorkbenchService, }); + const [collapsed, setCollapsed] = useState(defaultCollapsed); const docs = useLiveData(docsService.list.docs$); const trashDocs = useLiveData(docsService.list.trashDocs$); @@ -174,7 +180,7 @@ export const ExplorerOldFavorites = () => { ); return ( -
+ { ? `${t['com.affine.rootAppSidebar.favorites']()} (OLD)` : t['com.affine.rootAppSidebar.favorites']() } + setCollapsed={setCollapsed} + collapsed={collapsed} > { - - } - > - {favorites.map((favorite, i) => ( - - ))} - -
+ + + } + > + {favorites.map((favorite, i) => ( + + ))} + + + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/styles.css.ts b/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/styles.css.ts index 574b2653a0..994b712e90 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/styles.css.ts +++ b/packages/frontend/core/src/modules/explorer/views/sections/old-favorites/styles.css.ts @@ -2,7 +2,7 @@ import { cssVar } from '@toeverything/theme'; import { style } from '@vanilla-extract/css'; export const container = style({ - marginTop: '16px', + marginTop: '8px', }); export const draggedOverHighlight = style({ diff --git a/packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx index 4db8782187..43c0151fd0 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx +++ b/packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx @@ -16,6 +16,7 @@ import { import type { AffineDNDData } from '@affine/core/types/dnd'; import { useI18n } from '@affine/i18n'; import { PlusIcon } from '@blocksuite/icons/rc'; +import * as Collapsible from '@radix-ui/react-collapsible'; import { useLiveData, useServices } from '@toeverything/infra'; import { useCallback, useMemo, useState } from 'react'; @@ -23,9 +24,14 @@ import { ExplorerFolderNode } from '../../nodes/folder'; import { RootEmpty } from './empty'; import * as styles from './styles.css'; -export const ExplorerOrganize = () => { +export const ExplorerOrganize = ({ + defaultCollapsed = false, +}: { + defaultCollapsed?: boolean; +}) => { const { organizeService } = useServices({ OrganizeService }); const [newFolderId, setNewFolderId] = useState(null); + const [collapsed, setCollapsed] = useState(defaultCollapsed); const t = useI18n(); @@ -39,6 +45,7 @@ export const ExplorerOrganize = () => { rootFolder.indexAt('before') ); setNewFolderId(newFolderId); + setCollapsed(false); }, [rootFolder]); const handleOnChildrenDrop = useCallback( @@ -89,10 +96,12 @@ export const ExplorerOrganize = () => { >(() => args => args.source.data.entity?.type === 'folder', []); return ( -
+ { - } - > - {folders.map(child => ( - - ))} - -
+ + } + > + {folders.map(child => ( + + ))} + + + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/sections/organize/styles.css.ts b/packages/frontend/core/src/modules/explorer/views/sections/organize/styles.css.ts index 574b2653a0..994b712e90 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/organize/styles.css.ts +++ b/packages/frontend/core/src/modules/explorer/views/sections/organize/styles.css.ts @@ -2,7 +2,7 @@ import { cssVar } from '@toeverything/theme'; import { style } from '@vanilla-extract/css'; export const container = style({ - marginTop: '16px', + marginTop: '8px', }); export const draggedOverHighlight = style({ diff --git a/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx b/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx index f8379daa45..fa6b1c6da1 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx +++ b/packages/frontend/core/src/modules/explorer/views/sections/tags/index.tsx @@ -5,6 +5,7 @@ import type { Tag } from '@affine/core/modules/tag'; import { TagService } from '@affine/core/modules/tag'; import { useI18n } from '@affine/i18n'; import { PlusIcon } from '@blocksuite/icons/rc'; +import * as Collapsible from '@radix-ui/react-collapsible'; import { useLiveData, useServices } from '@toeverything/infra'; import { useCallback, useState } from 'react'; @@ -12,12 +13,17 @@ import { ExplorerTagNode } from '../../nodes/tag'; import { RootEmpty } from './empty'; import * as styles from './styles.css'; -export const ExplorerTags = () => { +export const ExplorerTags = ({ + defaultCollapsed = false, +}: { + defaultCollapsed?: boolean; +}) => { const { tagService } = useServices({ TagService, }); const [createdTag, setCreatedTag] = useState(null); + const [collapsed, setCollapsed] = useState(defaultCollapsed); const tags = useLiveData(tagService.tagList.tags$); const t = useI18n(); @@ -28,13 +34,16 @@ export const ExplorerTags = () => { tagService.randomTagColor() ); setCreatedTag(newTags); + setCollapsed(false); }, [t, tagService]); return ( -
+ { - } - > - {tags.map(tag => ( - - ))} - -
+ + } + > + {tags.map(tag => ( + + ))} + + + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/sections/tags/styles.css.ts b/packages/frontend/core/src/modules/explorer/views/sections/tags/styles.css.ts index 574b2653a0..994b712e90 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/tags/styles.css.ts +++ b/packages/frontend/core/src/modules/explorer/views/sections/tags/styles.css.ts @@ -2,7 +2,7 @@ import { cssVar } from '@toeverything/theme'; import { style } from '@vanilla-extract/css'; export const container = style({ - marginTop: '16px', + marginTop: '8px', }); export const draggedOverHighlight = style({ diff --git a/tests/affine-cloud/e2e/collaboration.spec.ts b/tests/affine-cloud/e2e/collaboration.spec.ts index 3451a0607d..9230747393 100644 --- a/tests/affine-cloud/e2e/collaboration.spec.ts +++ b/tests/affine-cloud/e2e/collaboration.spec.ts @@ -131,6 +131,7 @@ test('can sync collections between different browser', async ({ await loginUser(page2, user.email); await page2.goto(page.url()); const collections = page2.getByTestId('explorer-collections'); + await collections.getByTestId('category-divider-collapse-button').click(); await expect(collections.getByText('test collection')).toBeVisible(); } }); diff --git a/tests/affine-local/e2e/local-first-collections-items.spec.ts b/tests/affine-local/e2e/local-first-collections-items.spec.ts index 2d4abaaeb8..b7bbd4e227 100644 --- a/tests/affine-local/e2e/local-first-collections-items.spec.ts +++ b/tests/affine-local/e2e/local-first-collections-items.spec.ts @@ -65,6 +65,7 @@ test('Show collections items in sidebar', async ({ page }) => { await removeOnboardingPages(page); await createAndPinCollection(page); const collections = page.getByTestId('explorer-collections'); + await collections.getByTestId('category-divider-collapse-button').click(); const items = collections.locator('[data-testid^="explorer-collection-"]'); expect(await items.count()).toBe(1); const first = items.first(); @@ -105,6 +106,7 @@ test('edit collection', async ({ page }) => { await removeOnboardingPages(page); await createAndPinCollection(page); const collections = page.getByTestId('explorer-collections'); + await collections.getByTestId('category-divider-collapse-button').click(); const items = collections.locator('[data-testid^="explorer-collection-"]'); expect(await items.count()).toBe(1); const first = items.first(); @@ -122,6 +124,7 @@ test('edit collection and change filter date', async ({ page }) => { await removeOnboardingPages(page); await createAndPinCollection(page); const collections = page.getByTestId('explorer-collections'); + await collections.getByTestId('category-divider-collapse-button').click(); const items = collections.locator('[data-testid^="explorer-collection-"]'); expect(await items.count()).toBe(1); const first = items.first(); @@ -143,6 +146,10 @@ test('add collection from sidebar', async ({ page }) => { await page.getByTestId('all-pages').click(); const cell = page.getByTestId('page-list-item-title').getByText('test page'); await expect(cell).toBeVisible(); + await page + .getByTestId('explorer-collections') + .getByTestId('category-divider-collapse-button') + .click(); const nullCollection = page.getByTestId( 'slider-bar-collection-empty-message' );