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 3d35b34e15..4783c70af9 100644 --- a/packages/frontend/core/src/components/root-app-sidebar/index.tsx +++ b/packages/frontend/core/src/components/root-app-sidebar/index.tsx @@ -12,14 +12,6 @@ import { import { ExternalMenuLinkItem } from '@affine/core/modules/app-sidebar/views/menu-item/external-menu-link-item'; import { AuthService } from '@affine/core/modules/cloud'; import { WorkspaceDialogService } from '@affine/core/modules/dialogs'; -import { - CollapsibleSection, - ExplorerCollections, - ExplorerFavorites, - ExplorerMigrationFavorites, - ExplorerOrganize, -} from '@affine/core/modules/explorer'; -import { ExplorerTags } from '@affine/core/modules/explorer/views/sections/tags'; import { CMDKQuickSearchService } from '@affine/core/modules/quicksearch/services/cmdk'; import type { Workspace } from '@affine/core/modules/workspace'; import { useI18n } from '@affine/i18n'; @@ -35,6 +27,14 @@ import { useLiveData, useService, useServices } from '@toeverything/infra'; import type { ReactElement } from 'react'; import { memo, useCallback } from 'react'; +import { + CollapsibleSection, + NavigationPanelCollections, + NavigationPanelFavorites, + NavigationPanelMigrationFavorites, + NavigationPanelOrganize, + NavigationPanelTags, +} from '../../desktop/components/navigation-panel'; import { WorkbenchService } from '../../modules/workbench'; import { WorkspaceNavigator } from '../workspace-selector'; import { @@ -180,11 +180,11 @@ export const RootAppSidebar = memo((): ReactElement => { - - - - - + + + + + { - const section = useService(ExplorerService).sections[name]; + const section = useService(NavigationPanelService).sections[name]; const collapsed = useLiveData(section.collapsed$); diff --git a/packages/frontend/core/src/modules/explorer/views/layouts/empty-node-children.css.ts b/packages/frontend/core/src/desktop/components/navigation-panel/layouts/empty-node-children.css.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/layouts/empty-node-children.css.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/layouts/empty-node-children.css.ts diff --git a/packages/frontend/core/src/modules/explorer/views/layouts/empty-node-children.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/layouts/empty-node-children.tsx similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/layouts/empty-node-children.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/layouts/empty-node-children.tsx diff --git a/packages/frontend/core/src/modules/explorer/views/layouts/empty-section.css.ts b/packages/frontend/core/src/desktop/components/navigation-panel/layouts/empty-section.css.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/layouts/empty-section.css.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/layouts/empty-section.css.ts diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/layouts/empty-section.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/layouts/empty-section.tsx new file mode 100644 index 0000000000..92256516b0 --- /dev/null +++ b/packages/frontend/core/src/desktop/components/navigation-panel/layouts/empty-section.tsx @@ -0,0 +1,63 @@ +import { Button } from '@affine/component'; +import clsx from 'clsx'; +import { + cloneElement, + forwardRef, + type HTMLAttributes, + type JSX, + type ReactElement, + type Ref, + type SVGAttributes, + type SVGProps, +} from 'react'; + +import * as styles from './empty-section.css'; + +interface NavigationPanelEmptySectionProps + extends HTMLAttributes { + icon: + | ((props: SVGProps) => JSX.Element) + | ReactElement>; + message: string; + messageTestId?: string; + actionText?: string; + onActionClick?: () => void; +} + +export const NavigationPanelEmptySection = forwardRef( + function NavigationPanelEmptySection( + { + icon: Icon, + message, + messageTestId, + actionText, + children, + className, + onActionClick, + ...attrs + }: NavigationPanelEmptySectionProps, + ref: Ref + ) { + const icon = + typeof Icon === 'function' ? ( + + ) : ( + cloneElement(Icon, { className: styles.icon }) + ); + + return ( +
+
{icon}
+
+ {message} +
+ {actionText ? ( + + ) : null} + {children} +
+ ); + } +); diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/collection/empty.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/collection/empty.tsx similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/nodes/collection/empty.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/collection/empty.tsx diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/collection/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/collection/index.tsx similarity index 86% rename from packages/frontend/core/src/modules/explorer/views/nodes/collection/index.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/collection/index.tsx index 89732d8231..abc6edebba 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/collection/index.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/collection/index.tsx @@ -27,14 +27,17 @@ import { } from '@toeverything/infra'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { ExplorerTreeNode, type ExplorerTreeNodeDropEffect } from '../../tree'; -import type { ExplorerTreeNodeIcon } from '../../tree/node'; -import { ExplorerDocNode } from '../doc'; -import type { GenericExplorerNode } from '../types'; +import { + NavigationPanelTreeNode, + type NavigationPanelTreeNodeDropEffect, +} from '../../tree'; +import type { NavigationPanelTreeNodeIcon } from '../../tree/node'; +import { NavigationPanelDocNode } from '../doc'; +import type { GenericNavigationPanelNode } from '../types'; import { Empty } from './empty'; -import { useExplorerCollectionNodeOperations } from './operations'; +import { useNavigationPanelCollectionNodeOperations } from './operations'; -const CollectionIcon: ExplorerTreeNodeIcon = ({ +const CollectionIcon: NavigationPanelTreeNodeIcon = ({ className, draggedOver, treeInstruction, @@ -45,7 +48,7 @@ const CollectionIcon: ExplorerTreeNodeIcon = ({ /> ); -export const ExplorerCollectionNode = ({ +export const NavigationPanelCollectionNode = ({ collectionId, onDrop, location, @@ -55,7 +58,7 @@ export const ExplorerCollectionNode = ({ dropEffect, }: { collectionId: string; -} & GenericExplorerNode) => { +} & GenericNavigationPanelNode) => { const t = useI18n(); const { globalContextService, workspaceDialogService } = useServices({ GlobalContextService, @@ -79,7 +82,7 @@ export const ExplorerCollectionNode = ({ from: location, }, dropTarget: { - at: 'explorer:doc', + at: 'navigation-panel:doc', }, } satisfies AffineDNDData; }, [collectionId, location]); @@ -136,19 +139,20 @@ export const ExplorerCollectionNode = ({ [collection, onDrop, handleAddDocToCollection] ); - const handleDropEffectOnCollection = useCallback( - data => { - if (collection && data.treeInstruction?.type === 'make-child') { - if (data.source.data.entity?.type === 'doc') { - return 'link'; + const handleDropEffectOnCollection = + useCallback( + data => { + if (collection && data.treeInstruction?.type === 'make-child') { + if (data.source.data.entity?.type === 'doc') { + return 'link'; + } + } else { + return dropEffect?.(data); } - } else { - return dropEffect?.(data); - } - return; - }, - [collection, dropEffect] - ); + return; + }, + [collection, dropEffect] + ); const handleDropOnPlaceholder = useCallback( (data: DropTargetDropEvent) => { @@ -176,7 +180,7 @@ export const ExplorerCollectionNode = ({ }); }, [collection, workspaceDialogService]); - const collectionOperations = useExplorerCollectionNodeOperations( + const collectionOperations = useNavigationPanelCollectionNodeOperations( collectionId, handleOpenCollapsed, handleEditCollection @@ -204,7 +208,7 @@ export const ExplorerCollectionNode = ({ } return ( - } operations={finalOperations} dropEffect={handleDropEffectOnCollection} - data-testid={`explorer-collection-${collectionId}`} + data-testid={`navigation-panel-collection-${collectionId}`} > - - + + ); }; -const ExplorerCollectionNodeChildren = ({ +const NavigationPanelCollectionNodeChildren = ({ collection, }: { collection: Collection; @@ -294,12 +298,12 @@ const ExplorerCollectionNodeChildren = ({ }); return filtered.map(doc => ( - void, onOpenEdit: () => void diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/doc/empty.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/empty.tsx similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/nodes/doc/empty.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/empty.tsx diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/index.tsx similarity index 92% rename from packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/index.tsx index 136002d98e..36e50b9465 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/doc/index.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/index.tsx @@ -27,13 +27,16 @@ import { import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { NEVER } from 'rxjs'; -import { ExplorerTreeNode, type ExplorerTreeNodeDropEffect } from '../../tree'; -import type { GenericExplorerNode } from '../types'; +import { + NavigationPanelTreeNode, + type NavigationPanelTreeNodeDropEffect, +} from '../../tree'; +import type { GenericNavigationPanelNode } from '../types'; import { Empty } from './empty'; -import { useExplorerDocNodeOperations } from './operations'; +import { useNavigationPanelDocNodeOperations } from './operations'; import * as styles from './styles.css'; -export const ExplorerDocNode = ({ +export const NavigationPanelDocNode = ({ docId, onDrop, location, @@ -46,7 +49,7 @@ export const ExplorerDocNode = ({ docId: string; isLinked?: boolean; forwardKey?: string; -} & GenericExplorerNode) => { +} & GenericNavigationPanelNode) => { const t = useI18n(); const { docsSearchService, @@ -127,7 +130,7 @@ export const ExplorerDocNode = ({ from: location, }, dropTarget: { - at: 'explorer:doc', + at: 'navigation-panel:doc', }, } satisfies AffineDNDData; }, [docId, location]); @@ -166,7 +169,7 @@ export const ExplorerDocNode = ({ [docId, docsService, guardService, onDrop, t] ); - const handleDropEffectOnDoc = useCallback( + const handleDropEffectOnDoc = useCallback( data => { if (data.treeInstruction?.type === 'make-child') { if (data.source.data.entity?.type === 'doc') { @@ -214,7 +217,7 @@ export const ExplorerDocNode = ({ ); const workspaceDialogService = useService(WorkspaceDialogService); - const operations = useExplorerDocNodeOperations( + const operations = useNavigationPanelDocNodeOperations( docId, useMemo( () => ({ @@ -237,7 +240,7 @@ export const ExplorerDocNode = ({ } return ( - {canRead => canRead ? children?.map((child, index) => ( - - + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/operations.tsx similarity index 99% rename from packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/operations.tsx index e6163cc686..44142d6d69 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/doc/operations.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/operations.tsx @@ -31,7 +31,7 @@ import { useCallback, useMemo, useState } from 'react'; import type { NodeOperation } from '../../tree/types'; -export const useExplorerDocNodeOperations = ( +export const useNavigationPanelDocNodeOperations = ( docId: string, options: { openInfoModal: () => void; diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/doc/styles.css.ts b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/styles.css.ts similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/nodes/doc/styles.css.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/doc/styles.css.ts diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/folder/empty.css.ts b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/empty.css.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/nodes/folder/empty.css.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/empty.css.ts diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/folder/empty.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/empty.tsx similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/nodes/folder/empty.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/empty.tsx diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/folder/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/index.tsx similarity index 85% rename from packages/frontend/core/src/modules/explorer/views/nodes/folder/index.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/index.tsx index 1d5fc4bdb3..28190448ec 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/folder/index.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/index.tsx @@ -35,17 +35,20 @@ import { useLiveData, useServices } from '@toeverything/infra'; import { difference } from 'lodash-es'; import { useCallback, useMemo, useState } from 'react'; -import { ExplorerTreeNode, type ExplorerTreeNodeDropEffect } from '../../tree'; -import type { ExplorerTreeNodeIcon } from '../../tree/node'; +import { + NavigationPanelTreeNode, + type NavigationPanelTreeNodeDropEffect, +} from '../../tree'; +import type { NavigationPanelTreeNodeIcon } from '../../tree/node'; import type { NodeOperation } from '../../tree/types'; -import { ExplorerCollectionNode } from '../collection'; -import { ExplorerDocNode } from '../doc'; -import { ExplorerTagNode } from '../tag'; -import type { GenericExplorerNode } from '../types'; +import { NavigationPanelCollectionNode } from '../collection'; +import { NavigationPanelDocNode } from '../doc'; +import { NavigationPanelTagNode } from '../tag'; +import type { GenericNavigationPanelNode } from '../types'; import { FolderEmpty } from './empty'; import { FavoriteFolderOperation } from './operations'; -export const ExplorerFolderNode = ({ +export const NavigationPanelFolderNode = ({ nodeId, onDrop, defaultRenaming, @@ -61,7 +64,7 @@ export const ExplorerFolderNode = ({ operations?: | NodeOperation[] | ((type: string, node: FolderNode) => NodeOperation[]); -} & Omit) => { +} & Omit) => { const { organizeService } = useServices({ OrganizeService, }); @@ -93,7 +96,7 @@ export const ExplorerFolderNode = ({ if (type === 'folder') { return ( - ); -const ExplorerFolderNodeFolder = ({ +const NavigationPanelFolderNodeFolder = ({ node, onDrop, defaultRenaming, @@ -177,7 +180,7 @@ const ExplorerFolderNodeFolder = ({ }: { defaultRenaming?: boolean; node: FolderNode; -} & GenericExplorerNode) => { +} & GenericNavigationPanelNode) => { const t = useI18n(); const { workspaceService, featureFlagService, workspaceDialogService } = useServices({ @@ -224,7 +227,7 @@ const ExplorerFolderNodeFolder = ({ from: location, }, dropTarget: { - at: 'explorer:organize:folder', + at: 'navigation-panel:organize:folder', }, } satisfies AffineDNDData; }, [location, node.id]); @@ -258,7 +261,10 @@ const ExplorerFolderNodeFolder = ({ data.source.data.entity?.type === 'doc' || data.source.data.entity?.type === 'tag' ) { - if (data.source.data.from?.at === 'explorer:organize:folder-node') { + if ( + data.source.data.from?.at === + 'navigation-panel:organize:folder-node' + ) { node.moveHere(data.source.data.from.nodeId, node.indexAt('before')); track.$.navigationPanel.organize.moveOrganizeItem({ type: 'link', @@ -283,7 +289,7 @@ const ExplorerFolderNodeFolder = ({ [node, onDrop] ); - const handleDropEffect = useCallback( + const handleDropEffect = useCallback( data => { if (data.treeInstruction?.type === 'make-child') { if (data.source.data.entity?.type === 'folder') { @@ -295,7 +301,7 @@ const ExplorerFolderNodeFolder = ({ } return 'move'; } else if ( - data.source.data.from?.at === 'explorer:organize:folder-node' + data.source.data.from?.at === 'navigation-panel:organize:folder-node' ) { return 'move'; } else if ( @@ -334,7 +340,9 @@ const ExplorerFolderNodeFolder = ({ data.source.data.entity?.type === 'doc' || data.source.data.entity?.type === 'tag' ) { - if (data.source.data.from?.at === 'explorer:organize:folder-node') { + if ( + data.source.data.from?.at === 'navigation-panel:organize:folder-node' + ) { node.moveHere(data.source.data.from.nodeId, node.indexAt('before')); track.$.navigationPanel.organize.moveOrganizeItem({ type: data.source.data.entity?.type, @@ -388,7 +396,10 @@ const ExplorerFolderNodeFolder = ({ data.source.data.entity?.type === 'doc' || data.source.data.entity?.type === 'tag' ) { - if (data.source.data.from?.at === 'explorer:organize:folder-node') { + if ( + data.source.data.from?.at === + 'navigation-panel:organize:folder-node' + ) { node.moveHere( data.source.data.from.nodeId, node.indexAt(at, dropAtNode.id) @@ -437,58 +448,60 @@ const ExplorerFolderNodeFolder = ({ [node, onDrop] ); - const handleDropEffectOnChildren = useCallback( - data => { - if ( - data.treeInstruction?.type === 'reorder-above' || - data.treeInstruction?.type === 'reorder-below' - ) { - if (data.source.data.entity?.type === 'folder') { - if ( - node.id === data.source.data.entity.id || - node.beChildOf(data.source.data.entity.id) + const handleDropEffectOnChildren = + useCallback( + data => { + if ( + data.treeInstruction?.type === 'reorder-above' || + data.treeInstruction?.type === 'reorder-below' + ) { + if (data.source.data.entity?.type === 'folder') { + if ( + node.id === data.source.data.entity.id || + node.beChildOf(data.source.data.entity.id) + ) { + return; + } + return 'move'; + } else if ( + data.source.data.from?.at === + 'navigation-panel:organize:folder-node' ) { - return; + return 'move'; + } else if ( + data.source.data.entity?.type === 'collection' || + data.source.data.entity?.type === 'doc' || + data.source.data.entity?.type === 'tag' + ) { + return 'link'; + } + } else if (data.treeInstruction?.type === 'reparent') { + const currentLevel = data.treeInstruction.currentLevel; + const desiredLevel = data.treeInstruction.desiredLevel; + if (currentLevel === desiredLevel + 1) { + dropEffect?.({ + ...data, + treeInstruction: { + type: 'reorder-below', + currentLevel, + indentPerLevel: data.treeInstruction.indentPerLevel, + }, + }); + return; + } else { + dropEffect?.({ + ...data, + treeInstruction: { + ...data.treeInstruction, + currentLevel: currentLevel - 1, + }, + }); } - return 'move'; - } else if ( - data.source.data.from?.at === 'explorer:organize:folder-node' - ) { - return 'move'; - } else if ( - data.source.data.entity?.type === 'collection' || - data.source.data.entity?.type === 'doc' || - data.source.data.entity?.type === 'tag' - ) { - return 'link'; } - } else if (data.treeInstruction?.type === 'reparent') { - const currentLevel = data.treeInstruction.currentLevel; - const desiredLevel = data.treeInstruction.desiredLevel; - if (currentLevel === desiredLevel + 1) { - dropEffect?.({ - ...data, - treeInstruction: { - type: 'reorder-below', - currentLevel, - indentPerLevel: data.treeInstruction.indentPerLevel, - }, - }); - return; - } else { - dropEffect?.({ - ...data, - treeInstruction: { - ...data.treeInstruction, - currentLevel: currentLevel - 1, - }, - }); - } - } - return; - }, - [dropEffect, node] - ); + return; + }, + [dropEffect, node] + ); const handleCanDrop = useMemo['canDrop']>( () => args => { @@ -508,7 +521,7 @@ const ExplorerFolderNodeFolder = ({ } return true; } else if ( - args.source.data.from?.at === 'explorer:organize:folder-node' + args.source.data.from?.at === 'navigation-panel:organize:folder-node' ) { return true; } else if ( @@ -538,7 +551,7 @@ const ExplorerFolderNodeFolder = ({ } return true; } else if ( - args.source.data.from?.at === 'explorer:organize:folder-node' + args.source.data.from?.at === 'navigation-panel:organize:folder-node' ) { return true; } else if ( @@ -758,8 +771,8 @@ const ExplorerFolderNodeFolder = ({ }, []); return ( - } dropEffect={handleDropEffect} - data-testid={`explorer-folder-${node.id}`} + data-testid={`navigation-panel-folder-${node.id}`} > {children.map(child => ( - ))} - + ); }; diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/folder/operations.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/operations.tsx similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/nodes/folder/operations.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/folder/operations.tsx diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/tag/empty.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/empty.tsx similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/nodes/tag/empty.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/empty.tsx diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/tag/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/index.tsx similarity index 85% rename from packages/frontend/core/src/modules/explorer/views/nodes/tag/index.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/index.tsx index d97b689b23..6ad920674e 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/tag/index.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/index.tsx @@ -13,14 +13,17 @@ import { useLiveData, useServices } from '@toeverything/infra'; import clsx from 'clsx'; import { useCallback, useMemo, useState } from 'react'; -import { ExplorerTreeNode, type ExplorerTreeNodeDropEffect } from '../../tree'; -import { ExplorerDocNode } from '../doc'; -import type { GenericExplorerNode } from '../types'; +import { + NavigationPanelTreeNode, + type NavigationPanelTreeNodeDropEffect, +} from '../../tree'; +import { NavigationPanelDocNode } from '../doc'; +import type { GenericNavigationPanelNode } from '../types'; import { Empty } from './empty'; -import { useExplorerTagNodeOperations } from './operations'; +import { useNavigationPanelTagNodeOperations } from './operations'; import * as styles from './styles.css'; -export const ExplorerTagNode = ({ +export const NavigationPanelTagNode = ({ tagId, onDrop, location, @@ -30,7 +33,7 @@ export const ExplorerTagNode = ({ canDrop, }: { tagId: string; -} & GenericExplorerNode) => { +} & GenericNavigationPanelNode) => { const t = useI18n(); const { tagService, globalContextService } = useServices({ TagService, @@ -70,7 +73,7 @@ export const ExplorerTagNode = ({ from: location, }, dropTarget: { - at: 'explorer:tag', + at: 'navigation-panel:tag', }, } satisfies AffineDNDData; }, [location, tagId]); @@ -108,7 +111,7 @@ export const ExplorerTagNode = ({ [onDrop, t, tagRecord] ); - const handleDropEffectOnTag = useCallback( + const handleDropEffectOnTag = useCallback( data => { if (data.treeInstruction?.type === 'make-child') { if (data.source.data.entity?.type === 'doc') { @@ -145,7 +148,7 @@ export const ExplorerTagNode = ({ [canDrop] ); - const operations = useExplorerTagNodeOperations( + const operations = useNavigationPanelTagNodeOperations( tagId, useMemo( () => ({ @@ -167,7 +170,7 @@ export const ExplorerTagNode = ({ } return ( - } operations={finalOperations} dropEffect={handleDropEffectOnTag} - data-testid={`explorer-tag-${tagId}`} + data-testid={`navigation-panel-tag-${tagId}`} > - - + + ); }; @@ -195,16 +198,16 @@ export const ExplorerTagNode = ({ * so we split the tag node children into a separate component, * so it won't be rendered when the tag node is collapsed. */ -export const ExplorerTagNodeDocs = ({ tag }: { tag: Tag }) => { +export const NavigationPanelTagNodeDocs = ({ tag }: { tag: Tag }) => { const tagDocIds = useLiveData(tag.pageIds$); return tagDocIds.map(docId => ( - )); diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/tag/operations.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/operations.tsx similarity index 98% rename from packages/frontend/core/src/modules/explorer/views/nodes/tag/operations.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/operations.tsx index 89c3e2a420..8965bc7dd3 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/tag/operations.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/operations.tsx @@ -19,7 +19,7 @@ import { useCallback, useMemo } from 'react'; import type { NodeOperation } from '../../tree/types'; -export const useExplorerTagNodeOperations = ( +export const useNavigationPanelTagNodeOperations = ( tagId: string, { openNodeCollapsed, diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/tag/styles.css.ts b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/styles.css.ts similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/nodes/tag/styles.css.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/tag/styles.css.ts diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/types.ts b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/types.ts similarity index 87% rename from packages/frontend/core/src/modules/explorer/views/nodes/types.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/nodes/types.ts index 9a14d0141a..4ec9b5090e 100644 --- a/packages/frontend/core/src/modules/explorer/views/nodes/types.ts +++ b/packages/frontend/core/src/desktop/components/navigation-panel/nodes/types.ts @@ -1,11 +1,11 @@ import type { DropTargetDropEvent, DropTargetOptions } from '@affine/component'; import type { AffineDNDData } from '@affine/core/types/dnd'; -import type { ExplorerTreeNodeDropEffect } from '../tree'; +import type { NavigationPanelTreeNodeDropEffect } from '../tree'; import type { NodeOperation } from '../tree/types'; /** - * The interface for a generic explorer node. + * The interface for a generic navigation panel node. * * # Drop controlled area * @@ -21,7 +21,7 @@ import type { NodeOperation } from '../tree/types'; * * The controlled area can be distinguished by `data.treeInstruction.type` in the callback parameter. */ -export interface GenericExplorerNode { +export interface GenericNavigationPanelNode { /** * Tell the node and dropTarget where the node is located in the tree */ @@ -45,5 +45,5 @@ export interface GenericExplorerNode { /** * The drop effect to be used when an element is dropped over the node. */ - dropEffect?: ExplorerTreeNodeDropEffect; + dropEffect?: NavigationPanelTreeNodeDropEffect; } diff --git a/packages/frontend/core/src/modules/explorer/views/sections/collections/empty.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/collections/empty.tsx similarity index 82% rename from packages/frontend/core/src/modules/explorer/views/sections/collections/empty.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/sections/collections/empty.tsx index 2f292925b0..60ba950354 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/collections/empty.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/collections/empty.tsx @@ -1,7 +1,7 @@ import { useI18n } from '@affine/i18n'; import { ViewLayersIcon } from '@blocksuite/icons/rc'; -import { ExplorerEmptySection } from '../../layouts/empty-section'; +import { NavigationPanelEmptySection } from '../../layouts/empty-section'; export const RootEmpty = ({ onClickCreate, @@ -11,7 +11,7 @@ export const RootEmpty = ({ const t = useI18n(); return ( - { +export const NavigationPanelCollections = () => { const t = useI18n(); - const { collectionService, workbenchService, explorerService } = useServices({ - CollectionService, - WorkbenchService, - ExplorerService, - }); - const explorerSection = explorerService.sections.collections; + const { collectionService, workbenchService, navigationPanelService } = + useServices({ + CollectionService, + WorkbenchService, + NavigationPanelService, + }); + const navigationPanelSection = navigationPanelService.sections.collections; const collections = useLiveData(collectionService.collections$); const { openPromptModal } = usePromptModal(); @@ -51,12 +52,12 @@ export const ExplorerCollections = () => { type: 'collection', }); workbenchService.workbench.openCollection(id); - explorerSection.setCollapsed(false); + navigationPanelSection.setCollapsed(false); }, }); }, [ collectionService, - explorerSection, + navigationPanelSection, openPromptModal, t, workbenchService.workbench, @@ -65,11 +66,11 @@ export const ExplorerCollections = () => { return ( { } > - } > {collections.map(collection => ( - ))} - + ); }; diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/dnd.ts b/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/dnd.ts new file mode 100644 index 0000000000..58d346835b --- /dev/null +++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/dnd.ts @@ -0,0 +1,47 @@ +import type { DropTargetOptions } from '@affine/component'; +import { isFavoriteSupportType } from '@affine/core/modules/favorite'; +import type { AffineDNDData } from '@affine/core/types/dnd'; + +import type { NavigationPanelTreeNodeDropEffect } from '../../tree'; + +export const favoriteChildrenDropEffect: NavigationPanelTreeNodeDropEffect = + data => { + if ( + data.treeInstruction?.type === 'reorder-above' || + data.treeInstruction?.type === 'reorder-below' + ) { + if ( + data.source.data.from?.at === 'navigation-panel:favorite:list' && + data.source.data.entity?.type && + isFavoriteSupportType(data.source.data.entity.type) + ) { + return 'move'; + } else if ( + data.source.data.entity?.type && + isFavoriteSupportType(data.source.data.entity.type) + ) { + return 'link'; + } + } + return; // not supported + }; + +export const favoriteRootDropEffect: NavigationPanelTreeNodeDropEffect = + data => { + const sourceType = data.source.data.entity?.type; + if (sourceType && isFavoriteSupportType(sourceType)) { + return 'link'; + } + return; + }; + +export const favoriteRootCanDrop: DropTargetOptions['canDrop'] = + data => { + return data.source.data.entity?.type + ? isFavoriteSupportType(data.source.data.entity.type) + : false; + }; + +export const favoriteChildrenCanDrop: DropTargetOptions['canDrop'] = + // Same as favoriteRootCanDrop + data => favoriteRootCanDrop(data); diff --git a/packages/frontend/core/src/modules/explorer/views/sections/favorites/empty.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/empty.tsx similarity index 88% rename from packages/frontend/core/src/modules/explorer/views/sections/favorites/empty.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/empty.tsx index e7c91b8bfd..99ec9a7a1c 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/favorites/empty.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/empty.tsx @@ -7,7 +7,7 @@ import type { AffineDNDData } from '@affine/core/types/dnd'; import { useI18n } from '@affine/i18n'; import { FavoriteIcon } from '@blocksuite/icons/rc'; -import { ExplorerEmptySection } from '../../layouts/empty-section'; +import { NavigationPanelEmptySection } from '../../layouts/empty-section'; import { DropEffect } from '../../tree'; import { favoriteRootCanDrop, favoriteRootDropEffect } from './dnd'; @@ -26,7 +26,7 @@ const RootEmptyReady = ({ onDrop }: Omit) => { useDropTarget( () => ({ data: { - at: 'explorer:favorite:root', + at: 'navigation-panel:favorite:root', }, onDrop: onDrop, canDrop: favoriteRootCanDrop, @@ -36,7 +36,7 @@ const RootEmptyReady = ({ onDrop }: Omit) => { ); return ( - ) => { })} /> )} - + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/index.tsx similarity index 81% rename from packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/index.tsx index 5a8086f230..cb6bfcd6a1 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/favorites/index.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/favorites/index.tsx @@ -4,15 +4,12 @@ import { useDropTarget, } from '@affine/component'; import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils'; -import { - DropEffect, - ExplorerTreeRoot, -} from '@affine/core/modules/explorer/views/tree'; import type { FavoriteSupportTypeUnion } from '@affine/core/modules/favorite'; import { FavoriteService, isFavoriteSupportType, } from '@affine/core/modules/favorite'; +import { NavigationPanelService } from '@affine/core/modules/navigation-panel'; import { WorkspaceService } from '@affine/core/modules/workspace'; import type { AffineDNDData } from '@affine/core/types/dnd'; import { inferOpenMode } from '@affine/core/utils'; @@ -22,12 +19,12 @@ import { PlusIcon } from '@blocksuite/icons/rc'; import { useLiveData, useServices } from '@toeverything/infra'; import { type MouseEventHandler, useCallback } from 'react'; -import { ExplorerService } from '../../../services/explorer'; import { CollapsibleSection } from '../../layouts/collapsible-section'; -import { ExplorerCollectionNode } from '../../nodes/collection'; -import { ExplorerDocNode } from '../../nodes/doc'; -import { ExplorerFolderNode } from '../../nodes/folder'; -import { ExplorerTagNode } from '../../nodes/tag'; +import { NavigationPanelCollectionNode } from '../../nodes/collection'; +import { NavigationPanelDocNode } from '../../nodes/doc'; +import { NavigationPanelFolderNode } from '../../nodes/folder'; +import { NavigationPanelTagNode } from '../../nodes/tag'; +import { DropEffect, NavigationPanelTreeRoot } from '../../tree'; import { favoriteChildrenCanDrop, favoriteChildrenDropEffect, @@ -36,14 +33,15 @@ import { } from './dnd'; import { RootEmpty } from './empty'; -export const ExplorerFavorites = () => { - const { favoriteService, workspaceService, explorerService } = useServices({ - FavoriteService, - WorkspaceService, - ExplorerService, - }); +export const NavigationPanelFavorites = () => { + const { favoriteService, workspaceService, navigationPanelService } = + useServices({ + FavoriteService, + WorkspaceService, + NavigationPanelService, + }); - const explorerSection = explorerService.sections.favorites; + const navigationPanelSection = navigationPanelService.sections.favorites; const favorites = useLiveData(favoriteService.favoriteList.sortedList$); @@ -73,10 +71,10 @@ export const ExplorerFavorites = () => { track.$.navigationPanel.favorites.drop({ type: data.source.data.entity.type, }); - explorerSection.setCollapsed(false); + navigationPanelSection.setCollapsed(false); } }, - [explorerSection, favoriteService.favoriteList] + [navigationPanelSection, favoriteService.favoriteList] ); const handleCreateNewFavoriteDoc: MouseEventHandler = useCallback( @@ -87,9 +85,9 @@ export const ExplorerFavorites = () => { newDoc.id, favoriteService.favoriteList.indexAt('before') ); - explorerSection.setCollapsed(false); + navigationPanelSection.setCollapsed(false); }, - [createPage, explorerSection, favoriteService.favoriteList] + [createPage, navigationPanelSection, favoriteService.favoriteList] ); const handleOnChildrenDrop = useCallback( @@ -102,7 +100,7 @@ export const ExplorerFavorites = () => { data.treeInstruction?.type === 'reorder-below' ) { if ( - data.source.data.from?.at === 'explorer:favorite:list' && + data.source.data.from?.at === 'navigation-panel:favorite:list' && data.source.data.entity?.type && isFavoriteSupportType(data.source.data.entity.type) ) { @@ -153,7 +151,7 @@ export const ExplorerFavorites = () => { useDropTarget( () => ({ data: { - at: 'explorer:favorite:root', + at: 'navigation-panel:favorite:root', }, onDrop: handleDrop, canDrop: favoriteRootCanDrop, @@ -167,12 +165,12 @@ export const ExplorerFavorites = () => { name="favorites" title={t['com.affine.rootAppSidebar.favorites']()} headerRef={dropTargetRef} - testId="explorer-favorites" - headerTestId="explorer-favorite-category-divider" + testId="navigation-panel-favorites" + headerTestId="navigation-panel-favorite-category-divider" actions={ <> { } > - } > {favorites.map(favorite => ( - ))} - +
); }; const childLocation = { - at: 'explorer:favorite:list' as const, + at: 'navigation-panel:favorite:list' as const, }; -const ExplorerFavoriteNode = ({ +const NavigationPanelFavoriteNode = ({ favorite, onDrop, }: { @@ -237,7 +235,7 @@ const ExplorerFavoriteNode = ({ [favorite, onDrop] ); return favorite.type === 'doc' ? ( - ) : favorite.type === 'tag' ? ( - ) : favorite.type === 'folder' ? ( - ) : ( - { +export const NavigationPanelMigrationFavorites = () => { const t = useI18n(); const { migrationFavoriteItemsAdapter, docsService } = useServices({ @@ -105,14 +105,14 @@ export const ExplorerMigrationFavorites = () => { actions={ <> @@ -121,22 +121,22 @@ export const ExplorerMigrationFavorites = () => { } > - + {favorites.map((favorite, i) => ( - ))} - + ); }; const childLocation = { - at: 'explorer:migration-data:list' as const, + at: 'navigation-panel:migration-data:list' as const, }; -const ExplorerMigrationFavoriteNode = ({ +const NavigationPanelMigrationFavoriteNode = ({ favorite, }: { favorite: { @@ -145,7 +145,7 @@ const ExplorerMigrationFavoriteNode = ({ }; }) => { return favorite.type === 'doc' ? ( - ) : ( - { + if ( + data.treeInstruction?.type === 'reorder-above' || + data.treeInstruction?.type === 'reorder-below' + ) { + if (data.source.data.entity?.type === 'folder') { + return 'move'; + } + } else { + return; // not supported + } + return; + }; + +export const organizeEmptyDropEffect: NavigationPanelTreeNodeDropEffect = + data => { + const sourceType = data.source.data.entity?.type; + if (sourceType && isOrganizeSupportType(sourceType)) { + return 'link'; + } + return; + }; + +/** + * Check whether the data can be dropped on the empty state of the organize section + */ +export const organizeEmptyRootCanDrop: DropTargetOptions['canDrop'] = + data => { + const type = data.source.data.entity?.type; + return !!type && isOrganizeSupportType(type); + }; diff --git a/packages/frontend/core/src/modules/explorer/views/sections/organize/empty.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/organize/empty.tsx similarity index 89% rename from packages/frontend/core/src/modules/explorer/views/sections/organize/empty.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/sections/organize/empty.tsx index 9db448fa30..a599b0f87d 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/organize/empty.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/organize/empty.tsx @@ -7,7 +7,7 @@ import { import type { AffineDNDData } from '@affine/core/types/dnd'; import { useI18n } from '@affine/i18n'; -import { ExplorerEmptySection } from '../../layouts/empty-section'; +import { NavigationPanelEmptySection } from '../../layouts/empty-section'; import { DropEffect } from '../../tree'; import { organizeEmptyDropEffect, organizeEmptyRootCanDrop } from './dnd'; @@ -30,7 +30,7 @@ export const RootEmptyReady = ({ const { dropTargetRef, draggedOverDraggable, draggedOverPosition } = useDropTarget( () => ({ - data: { at: 'explorer:organize:root' }, + data: { at: 'navigation-panel:organize:root' }, onDrop, canDrop: organizeEmptyRootCanDrop, }), @@ -38,7 +38,7 @@ export const RootEmptyReady = ({ ); return ( - } message={t['com.affine.rootAppSidebar.organize.empty']()} @@ -57,7 +57,7 @@ export const RootEmptyReady = ({ })} /> )} - + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/organize/index.tsx similarity index 83% rename from packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/sections/organize/index.tsx index 694d39a3a8..f9c8dea06c 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/organize/index.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/organize/index.tsx @@ -4,7 +4,7 @@ import { IconButton, toast, } from '@affine/component'; -import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree'; +import { NavigationPanelService } from '@affine/core/modules/navigation-panel'; import { type FolderNode, OrganizeService, @@ -16,19 +16,19 @@ import { AddOrganizeIcon } from '@blocksuite/icons/rc'; import { useLiveData, useServices } from '@toeverything/infra'; import { useCallback, useEffect, useMemo, useState } from 'react'; -import { ExplorerService } from '../../../services/explorer'; import { CollapsibleSection } from '../../layouts/collapsible-section'; -import { ExplorerFolderNode } from '../../nodes/folder'; +import { NavigationPanelFolderNode } from '../../nodes/folder'; +import { NavigationPanelTreeRoot } from '../../tree'; import { organizeChildrenDropEffect } from './dnd'; import { RootEmpty } from './empty'; -export const ExplorerOrganize = () => { - const { organizeService, explorerService } = useServices({ +export const NavigationPanelOrganize = () => { + const { organizeService, navigationPanelService } = useServices({ OrganizeService, - ExplorerService, + NavigationPanelService, }); - const explorerSection = explorerService.sections.organize; - const collapsed = useLiveData(explorerSection.collapsed$); + const navigationPanelSection = navigationPanelService.sections.organize; + const collapsed = useLiveData(navigationPanelSection.collapsed$); const [newFolderId, setNewFolderId] = useState(null); const t = useI18n(); @@ -46,9 +46,9 @@ export const ExplorerOrganize = () => { ); track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' }); setNewFolderId(newFolderId); - explorerSection.setCollapsed(false); + navigationPanelSection.setCollapsed(false); return newFolderId; - }, [explorerSection, rootFolder]); + }, [navigationPanelSection, rootFolder]); const handleOnChildrenDrop = useCallback( (data: DropTargetDropEvent, node?: FolderNode) => { @@ -109,7 +109,7 @@ export const ExplorerOrganize = () => { title={t['com.affine.rootAppSidebar.organize']()} actions={ { } > - { } > {folders.map(child => ( - { dropEffect={organizeChildrenDropEffect} canDrop={handleChildrenCanDrop} location={{ - at: 'explorer:organize:folder-node', + at: 'navigation-panel:organize:folder-node', nodeId: child.id as string, }} /> ))} - + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/sections/tags/empty.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/sections/tags/empty.tsx similarity index 73% rename from packages/frontend/core/src/modules/explorer/views/sections/tags/empty.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/sections/tags/empty.tsx index a135b12e6a..e3065c2667 100644 --- a/packages/frontend/core/src/modules/explorer/views/sections/tags/empty.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/sections/tags/empty.tsx @@ -1,13 +1,13 @@ import { useI18n } from '@affine/i18n'; import { TagIcon } from '@blocksuite/icons/rc'; -import { ExplorerEmptySection } from '../../layouts/empty-section'; +import { NavigationPanelEmptySection } from '../../layouts/empty-section'; export const RootEmpty = () => { const t = useI18n(); return ( - { - const { tagService, explorerService } = useServices({ +export const NavigationPanelTags = () => { + const { tagService, navigationPanelService } = useServices({ TagService, - ExplorerService, + NavigationPanelService, }); - const explorerSection = explorerService.sections.tags; - const collapsed = useLiveData(explorerSection.collapsed$); + const navigationPanelSection = navigationPanelService.sections.tags; + const collapsed = useLiveData(navigationPanelSection.collapsed$); const [creating, setCreating] = useState(false); const tags = useLiveData(tagService.tagList.tags$); @@ -30,9 +30,9 @@ export const ExplorerTags = () => { (name: string) => { tagService.tagList.createTag(name, tagService.randomTagColor()); track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' }); - explorerSection.setCollapsed(false); + navigationPanelSection.setCollapsed(false); }, - [explorerSection, tagService] + [navigationPanelSection, tagService] ); useEffect(() => { @@ -46,13 +46,13 @@ export const ExplorerTags = () => { return ( { } > - }> + }> {tags.map(tag => ( - ))} - + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/sections/tags/styles.css.ts b/packages/frontend/core/src/desktop/components/navigation-panel/sections/tags/styles.css.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/sections/tags/styles.css.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/sections/tags/styles.css.ts diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/tree/context.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/tree/context.tsx new file mode 100644 index 0000000000..b52f5d132d --- /dev/null +++ b/packages/frontend/core/src/desktop/components/navigation-panel/tree/context.tsx @@ -0,0 +1,11 @@ +import React from 'react'; + +export interface NavigationPanelTreeContextData { + /** + * The level of the current tree node. + */ + level: number; +} + +export const NavigationPanelTreeContext = + React.createContext(null); diff --git a/packages/frontend/core/src/modules/explorer/views/tree/drop-effect.css.ts b/packages/frontend/core/src/desktop/components/navigation-panel/tree/drop-effect.css.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/tree/drop-effect.css.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/tree/drop-effect.css.ts diff --git a/packages/frontend/core/src/modules/explorer/views/tree/drop-effect.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/tree/drop-effect.tsx similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/tree/drop-effect.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/tree/drop-effect.tsx diff --git a/packages/frontend/core/src/desktop/components/navigation-panel/tree/index.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/tree/index.tsx new file mode 100644 index 0000000000..289906f24b --- /dev/null +++ b/packages/frontend/core/src/desktop/components/navigation-panel/tree/index.tsx @@ -0,0 +1,7 @@ +export { DropEffect } from './drop-effect'; +export type { + NavigationPanelTreeNodeDropEffect, + NavigationPanelTreeNodeDropEffectData, +} from './node'; +export { NavigationPanelTreeNode } from './node'; +export { NavigationPanelTreeRoot } from './root'; diff --git a/packages/frontend/core/src/modules/explorer/views/tree/node.css.ts b/packages/frontend/core/src/desktop/components/navigation-panel/tree/node.css.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/tree/node.css.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/tree/node.css.ts diff --git a/packages/frontend/core/src/modules/explorer/views/tree/node.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/tree/node.tsx similarity index 92% rename from packages/frontend/core/src/modules/explorer/views/tree/node.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/tree/node.tsx index b8c838883a..2fde7ca0fe 100644 --- a/packages/frontend/core/src/modules/explorer/views/tree/node.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/tree/node.tsx @@ -39,28 +39,28 @@ import { useState, } from 'react'; -import { ExplorerTreeContext } from './context'; +import { NavigationPanelTreeContext } from './context'; import { DropEffect } from './drop-effect'; import * as styles from './node.css'; import type { NodeOperation } from './types'; -export type ExplorerTreeNodeDropEffectData = { +export type NavigationPanelTreeNodeDropEffectData = { source: { data: AffineDNDData['draggable'] }; treeInstruction: DropTargetTreeInstruction | null; }; -export type ExplorerTreeNodeDropEffect = ( - data: ExplorerTreeNodeDropEffectData +export type NavigationPanelTreeNodeDropEffect = ( + data: NavigationPanelTreeNodeDropEffectData ) => 'copy' | 'move' | 'link' | undefined; -export type ExplorerTreeNodeIcon = React.ComponentType<{ +export type NavigationPanelTreeNodeIcon = React.ComponentType<{ className?: string; draggedOver?: boolean; treeInstruction?: DropTargetTreeInstruction | null; collapsed?: boolean; }>; -export interface BaseExplorerTreeNodeProps { +export interface BaseNavigationPanelTreeNodeProps { name?: string; - icon?: ExplorerTreeNodeIcon; + icon?: NavigationPanelTreeNodeIcon; children?: React.ReactNode; active?: boolean; extractEmojiAsIcon?: boolean; @@ -81,7 +81,8 @@ export interface BaseExplorerTreeNodeProps { [key: `data-${string}`]: any; } -interface WebExplorerTreeNodeProps extends BaseExplorerTreeNodeProps { +interface WebNavigationPanelTreeNodeProps + extends BaseNavigationPanelTreeNodeProps { renameable?: boolean; onRename?: (newName: string) => void; renameableGuard?: { docId: string; action: DocPermissionActions }; @@ -91,14 +92,14 @@ interface WebExplorerTreeNodeProps extends BaseExplorerTreeNodeProps { reorderable?: boolean; dndData?: AffineDNDData; onDrop?: (data: DropTargetDropEvent) => void; - dropEffect?: ExplorerTreeNodeDropEffect; + dropEffect?: NavigationPanelTreeNodeDropEffect; } /** - * specific rename modal for explorer tree node, + * specific rename modal for navigation panel tree node, * Separate it into a separate component to prevent re-rendering the entire component when width changes. */ -export const ExplorerTreeNodeRenameModal = ({ +export const NavigationPanelTreeNodeRenameModal = ({ setRenaming, handleRename, rawName, @@ -124,7 +125,7 @@ export const ExplorerTreeNodeRenameModal = ({ ); }; -export const ExplorerTreeNode = ({ +export const NavigationPanelTreeNode = ({ children, icon: Icon, name: rawName, @@ -150,10 +151,10 @@ export const ExplorerTreeNode = ({ onDrop, dropEffect, ...otherProps -}: WebExplorerTreeNodeProps) => { +}: WebNavigationPanelTreeNodeProps) => { const t = useI18n(); const cid = useId(); - const context = useContext(ExplorerTreeContext); + const context = useContext(NavigationPanelTreeContext); const level = context?.level ?? 0; // If no onClick or to is provided, clicking on the node will toggle the collapse state const clickForCollapse = !onClick && !to && !disabled; @@ -293,7 +294,7 @@ export const ExplorerTreeNode = ({ > {can => ( } onClick={() => setRenaming(true)} @@ -305,7 +306,7 @@ export const ExplorerTreeNode = ({ ) : ( } onClick={() => setRenaming(true)} @@ -381,7 +382,7 @@ export const ExplorerTreeNode = ({
@@ -432,7 +433,7 @@ export const ExplorerTreeNode = ({
{renameable && renaming && ( - {childCount === 0 && !collapsed ? childrenPlaceholder : null} - + {collapsed ? null : children} - + ); diff --git a/packages/frontend/core/src/modules/explorer/views/tree/root.css.ts b/packages/frontend/core/src/desktop/components/navigation-panel/tree/root.css.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/tree/root.css.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/tree/root.css.ts diff --git a/packages/frontend/core/src/modules/explorer/views/tree/root.tsx b/packages/frontend/core/src/desktop/components/navigation-panel/tree/root.tsx similarity index 81% rename from packages/frontend/core/src/modules/explorer/views/tree/root.tsx rename to packages/frontend/core/src/desktop/components/navigation-panel/tree/root.tsx index bf66771972..7dadabe2aa 100644 --- a/packages/frontend/core/src/modules/explorer/views/tree/root.tsx +++ b/packages/frontend/core/src/desktop/components/navigation-panel/tree/root.tsx @@ -1,10 +1,10 @@ import { useMemo, useState } from 'react'; -import { ExplorerTreeContext } from './context'; +import { NavigationPanelTreeContext } from './context'; import * as styles from './root.css'; import type { NodeOperation } from './types'; -export const ExplorerTreeRoot = ({ +export const NavigationPanelTreeRoot = ({ children, childrenOperations = [], placeholder, @@ -33,9 +33,9 @@ export const ExplorerTreeRoot = ({
{childCount === 0 && placeholder}
- + {children} - + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/tree/types.ts b/packages/frontend/core/src/desktop/components/navigation-panel/tree/types.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/tree/types.ts rename to packages/frontend/core/src/desktop/components/navigation-panel/tree/types.ts diff --git a/packages/frontend/core/src/mobile/components/explorer/index.ts b/packages/frontend/core/src/mobile/components/explorer/index.ts deleted file mode 100644 index fec3edc9c5..0000000000 --- a/packages/frontend/core/src/mobile/components/explorer/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export { CollapsibleSection } from './layouts/collapsible-section'; -export { ExplorerCollections } from './sections/collections'; -export { ExplorerFavorites } from './sections/favorites'; -export { ExplorerOrganize } from './sections/organize'; -export { ExplorerTags } from './sections/tags'; diff --git a/packages/frontend/core/src/mobile/components/navigation/index.ts b/packages/frontend/core/src/mobile/components/navigation/index.ts new file mode 100644 index 0000000000..1349d10c1e --- /dev/null +++ b/packages/frontend/core/src/mobile/components/navigation/index.ts @@ -0,0 +1,5 @@ +export { CollapsibleSection } from './layouts/collapsible-section'; +export { NavigationPanelCollections } from './sections/collections'; +export { NavigationPanelFavorites } from './sections/favorites'; +export { NavigationPanelOrganize } from './sections/organize'; +export { NavigationPanelTags } from './sections/tags'; diff --git a/packages/frontend/core/src/mobile/components/explorer/layouts/add-item-placeholder.css.ts b/packages/frontend/core/src/mobile/components/navigation/layouts/add-item-placeholder.css.ts similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/layouts/add-item-placeholder.css.ts rename to packages/frontend/core/src/mobile/components/navigation/layouts/add-item-placeholder.css.ts diff --git a/packages/frontend/core/src/mobile/components/explorer/layouts/add-item-placeholder.tsx b/packages/frontend/core/src/mobile/components/navigation/layouts/add-item-placeholder.tsx similarity index 87% rename from packages/frontend/core/src/mobile/components/explorer/layouts/add-item-placeholder.tsx rename to packages/frontend/core/src/mobile/components/navigation/layouts/add-item-placeholder.tsx index 98afe53c64..5fb3163802 100644 --- a/packages/frontend/core/src/mobile/components/explorer/layouts/add-item-placeholder.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/layouts/add-item-placeholder.tsx @@ -1,4 +1,4 @@ -import { ExplorerTreeContext } from '@affine/core/modules/explorer'; +import { NavigationPanelTreeContext } from '@affine/core/desktop/components/navigation-panel'; import { PlusIcon } from '@blocksuite/icons/rc'; import { assignInlineVars } from '@vanilla-extract/dynamic'; import clsx from 'clsx'; @@ -21,7 +21,7 @@ export const AddItemPlaceholder = ({ className, ...attrs }: AddItemPlaceholderProps) => { - const context = useContext(ExplorerTreeContext); + const context = useContext(NavigationPanelTreeContext); const level = context?.level ?? 0; return ( diff --git a/packages/frontend/core/src/mobile/components/explorer/layouts/collapsible-section.css.ts b/packages/frontend/core/src/mobile/components/navigation/layouts/collapsible-section.css.ts similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/layouts/collapsible-section.css.ts rename to packages/frontend/core/src/mobile/components/navigation/layouts/collapsible-section.css.ts diff --git a/packages/frontend/core/src/mobile/components/explorer/layouts/collapsible-section.tsx b/packages/frontend/core/src/mobile/components/navigation/layouts/collapsible-section.tsx similarity index 95% rename from packages/frontend/core/src/mobile/components/explorer/layouts/collapsible-section.tsx rename to packages/frontend/core/src/mobile/components/navigation/layouts/collapsible-section.tsx index cbcda7b3b9..2fd3fc9e66 100644 --- a/packages/frontend/core/src/mobile/components/explorer/layouts/collapsible-section.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/layouts/collapsible-section.tsx @@ -1,7 +1,7 @@ import { type CollapsibleSectionName, - ExplorerService, -} from '@affine/core/modules/explorer'; + NavigationPanelService, +} from '@affine/core/modules/navigation-panel'; import { ToggleRightIcon } from '@blocksuite/icons/rc'; import * as Collapsible from '@radix-ui/react-collapsible'; import { useLiveData, useService } from '@toeverything/infra'; @@ -86,7 +86,7 @@ export const CollapsibleSection = ({ children, ...attrs }: CollapsibleSectionProps) => { - const section = useService(ExplorerService).sections[name]; + const section = useService(NavigationPanelService).sections[name]; const collapsed = useLiveData(section.collapsed$); const setCollapsed = useCallback( diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/collection/dialog.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/collection/dialog.tsx similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/nodes/collection/dialog.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/collection/dialog.tsx diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/collection/index.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/collection/index.tsx similarity index 86% rename from packages/frontend/core/src/mobile/components/explorer/nodes/collection/index.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/collection/index.tsx index c89a7ca9ef..8fa84f246f 100644 --- a/packages/frontend/core/src/mobile/components/explorer/nodes/collection/index.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/nodes/collection/index.tsx @@ -1,9 +1,9 @@ import { MenuItem, notify } from '@affine/component'; import { filterPage } from '@affine/core/components/page-list'; +import type { NodeOperation } from '@affine/core/desktop/components/navigation-panel'; import { CollectionService } from '@affine/core/modules/collection'; import { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import { DocsService } from '@affine/core/modules/doc'; -import type { NodeOperation } from '@affine/core/modules/explorer'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite'; import { GlobalContextService } from '@affine/core/modules/global-context'; import { ShareDocsListService } from '@affine/core/modules/share-doc'; @@ -17,16 +17,16 @@ import { LiveData, useLiveData, useServices } from '@toeverything/infra'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { AddItemPlaceholder } from '../../layouts/add-item-placeholder'; -import { ExplorerTreeNode } from '../../tree/node'; -import { ExplorerDocNode } from '../doc'; +import { NavigationPanelTreeNode } from '../../tree/node'; +import { NavigationPanelDocNode } from '../doc'; import { - useExplorerCollectionNodeOperations, - useExplorerCollectionNodeOperationsMenu, + useNavigationPanelCollectionNodeOperations, + useNavigationPanelCollectionNodeOperationsMenu, } from './operations'; const CollectionIcon = () => ; -export const ExplorerCollectionNode = ({ +export const NavigationPanelCollectionNode = ({ collectionId, operations: additionalOperations, }: { @@ -60,16 +60,17 @@ export const ExplorerCollectionNode = ({ }); }, [collection, workspaceDialogService]); - const collectionOperations = useExplorerCollectionNodeOperationsMenu( - collectionId, - handleOpenCollapsed, - handleEditCollection - ); - const { handleAddDocToCollection } = useExplorerCollectionNodeOperations( + const collectionOperations = useNavigationPanelCollectionNodeOperationsMenu( collectionId, handleOpenCollapsed, handleEditCollection ); + const { handleAddDocToCollection } = + useNavigationPanelCollectionNodeOperations( + collectionId, + handleOpenCollapsed, + handleEditCollection + ); const finalOperations = useMemo(() => { if (additionalOperations) { @@ -83,7 +84,7 @@ export const ExplorerCollectionNode = ({ } return ( - - - + ); }; -const ExplorerCollectionNodeChildren = ({ +const NavigationPanelCollectionNodeChildren = ({ collection, onAddDoc, }: { @@ -174,7 +175,7 @@ const ExplorerCollectionNodeChildren = ({ return ( <> {filtered.map(doc => ( - void, onOpenEdit: () => void @@ -154,7 +154,7 @@ export const useExplorerCollectionNodeOperations = ( ); }; -export const useExplorerCollectionNodeOperationsMenu = ( +export const useNavigationPanelCollectionNodeOperationsMenu = ( collectionId: string, onOpenCollapsed: () => void, onOpenEdit: () => void @@ -170,7 +170,7 @@ export const useExplorerCollectionNodeOperationsMenu = ( handleShowEdit, handleToggleFavoriteCollection, handleRename, - } = useExplorerCollectionNodeOperations( + } = useNavigationPanelCollectionNodeOperations( collectionId, onOpenCollapsed, onOpenEdit diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/doc/dialog.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/doc/dialog.tsx similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/nodes/doc/dialog.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/doc/dialog.tsx diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/doc/index.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/doc/index.tsx similarity index 87% rename from packages/frontend/core/src/mobile/components/explorer/nodes/doc/index.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/doc/index.tsx index b010ef7266..9b8e777c26 100644 --- a/packages/frontend/core/src/mobile/components/explorer/nodes/doc/index.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/nodes/doc/index.tsx @@ -1,10 +1,10 @@ import { Loading } from '@affine/component'; import { Guard } from '@affine/core/components/guard'; +import type { NodeOperation } from '@affine/core/desktop/components/navigation-panel'; import { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import { DocsService } from '@affine/core/modules/doc'; import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta'; import { DocsSearchService } from '@affine/core/modules/docs-search'; -import type { NodeOperation } from '@affine/core/modules/explorer'; import { FeatureFlagService } from '@affine/core/modules/feature-flag'; import { GlobalContextService } from '@affine/core/modules/global-context'; import { useI18n } from '@affine/i18n'; @@ -18,14 +18,14 @@ import { import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { AddItemPlaceholder } from '../../layouts/add-item-placeholder'; -import { ExplorerTreeNode } from '../../tree/node'; +import { NavigationPanelTreeNode } from '../../tree/node'; import { - useExplorerDocNodeOperations, - useExplorerDocNodeOperationsMenu, + useNavigationPanelDocNodeOperations, + useNavigationPanelDocNodeOperationsMenu, } from './operations'; import * as styles from './styles.css'; -export const ExplorerDocNode = ({ +export const NavigationPanelDocNode = ({ docId, isLinked, operations: additionalOperations, @@ -105,8 +105,11 @@ export const ExplorerDocNode = ({ }), [docId, workspaceDialogService] ); - const operations = useExplorerDocNodeOperationsMenu(docId, option); - const { handleAddLinkedPage } = useExplorerDocNodeOperations(docId, option); + const operations = useNavigationPanelDocNodeOperationsMenu(docId, option); + const { handleAddLinkedPage } = useNavigationPanelDocNodeOperations( + docId, + option + ); const finalOperations = useMemo(() => { if (additionalOperations) { @@ -120,7 +123,7 @@ export const ExplorerDocNode = ({ } return ( - {canRead => canRead ? children?.map((child, index) => ( - - + ); }; diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/doc/operations.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/doc/operations.tsx similarity index 96% rename from packages/frontend/core/src/mobile/components/explorer/nodes/doc/operations.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/doc/operations.tsx index 38e31bf5e6..52b3ad0cee 100644 --- a/packages/frontend/core/src/mobile/components/explorer/nodes/doc/operations.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/nodes/doc/operations.tsx @@ -10,8 +10,8 @@ import { Guard } from '@affine/core/components/guard'; import { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/use-block-suite-meta-helper'; import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks'; import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; +import type { NodeOperation } from '@affine/core/desktop/components/navigation-panel'; import { DocsService } from '@affine/core/modules/doc'; -import type { NodeOperation } from '@affine/core/modules/explorer'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite'; import { WorkbenchService } from '@affine/core/modules/workbench'; import { WorkspaceService } from '@affine/core/modules/workspace'; @@ -31,7 +31,7 @@ import { useCallback, useMemo } from 'react'; import { DocFrameScope, DocInfoSheet } from '../../../doc-info'; import { DocRenameSubMenu } from './dialog'; -export const useExplorerDocNodeOperations = ( +export const useNavigationPanelDocNodeOperations = ( docId: string, options: { openNodeCollapsed: () => void; @@ -160,7 +160,7 @@ export const useExplorerDocNodeOperations = ( ); }; -export const useExplorerDocNodeOperationsMenu = ( +export const useNavigationPanelDocNodeOperationsMenu = ( docId: string, options: { openInfoModal: () => void; @@ -176,7 +176,7 @@ export const useExplorerDocNodeOperationsMenu = ( handleOpenInNewTab, handleMoveToTrash, handleRename, - } = useExplorerDocNodeOperations(docId, options); + } = useNavigationPanelDocNodeOperations(docId, options); const docService = useService(DocsService); const docRecord = useLiveData(docService.list.doc$(docId)); diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/doc/styles.css.ts b/packages/frontend/core/src/mobile/components/navigation/nodes/doc/styles.css.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/nodes/doc/styles.css.ts rename to packages/frontend/core/src/mobile/components/navigation/nodes/doc/styles.css.ts diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/folder/dialog.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/folder/dialog.tsx similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/nodes/folder/dialog.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/folder/dialog.tsx diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/folder/index.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/folder/index.tsx similarity index 91% rename from packages/frontend/core/src/mobile/components/explorer/nodes/folder/index.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/folder/index.tsx index cf27b0f855..778fc0b652 100644 --- a/packages/frontend/core/src/mobile/components/explorer/nodes/folder/index.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/nodes/folder/index.tsx @@ -7,11 +7,11 @@ import { notify, } from '@affine/component'; import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils'; -import { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import type { - ExplorerTreeNodeIcon, + NavigationPanelTreeNodeIcon, NodeOperation, -} from '@affine/core/modules/explorer'; +} from '@affine/core/desktop/components/navigation-panel'; +import { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite'; import { FeatureFlagService } from '@affine/core/modules/feature-flag'; import { @@ -36,14 +36,14 @@ import { difference } from 'lodash-es'; import { useCallback, useMemo, useState } from 'react'; import { AddItemPlaceholder } from '../../layouts/add-item-placeholder'; -import { ExplorerTreeNode } from '../../tree/node'; -import { ExplorerCollectionNode } from '../collection'; -import { ExplorerDocNode } from '../doc'; -import { ExplorerTagNode } from '../tag'; +import { NavigationPanelTreeNode } from '../../tree/node'; +import { NavigationPanelCollectionNode } from '../collection'; +import { NavigationPanelDocNode } from '../doc'; +import { NavigationPanelTagNode } from '../tag'; import { FolderCreateTip, FolderRenameSubMenu } from './dialog'; import { FavoriteFolderOperation } from './operations'; -export const ExplorerFolderNode = ({ +export const NavigationPanelFolderNode = ({ nodeId, operations, }: { @@ -75,27 +75,34 @@ export const ExplorerFolderNode = ({ if (type === 'folder') { return ( - + ); } if (!data) return null; if (type === 'doc') { - return ; + return ( + + ); } else if (type === 'collection') { return ( - ); } else if (type === 'tag') { - return ; + return ( + + ); } return; }; -const ExplorerFolderIcon: ExplorerTreeNodeIcon = ({ +const NavigationPanelFolderIcon: NavigationPanelTreeNodeIcon = ({ collapsed, className, draggedOver, @@ -109,7 +116,7 @@ const ExplorerFolderIcon: ExplorerTreeNodeIcon = ({ /> ); -const ExplorerFolderNodeFolder = ({ +const NavigationPanelFolderNodeFolder = ({ node, operations: additionalOperations, }: { @@ -390,19 +397,19 @@ const ExplorerFolderNodeFolder = ({ }, []); return ( - {children.map(child => ( - - + ); }; diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/folder/operations.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/folder/operations.tsx similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/nodes/folder/operations.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/folder/operations.tsx diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/tag/dialog.css.ts b/packages/frontend/core/src/mobile/components/navigation/nodes/tag/dialog.css.ts similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/nodes/tag/dialog.css.ts rename to packages/frontend/core/src/mobile/components/navigation/nodes/tag/dialog.css.ts diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/tag/dialog.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/tag/dialog.tsx similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/nodes/tag/dialog.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/tag/dialog.tsx diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/tag/index.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/tag/index.tsx similarity index 74% rename from packages/frontend/core/src/mobile/components/explorer/nodes/tag/index.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/tag/index.tsx index 404b0dae0e..c69443c945 100644 --- a/packages/frontend/core/src/mobile/components/explorer/nodes/tag/index.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/nodes/tag/index.tsx @@ -1,4 +1,4 @@ -import type { NodeOperation } from '@affine/core/modules/explorer'; +import type { NodeOperation } from '@affine/core/desktop/components/navigation-panel'; import { GlobalContextService } from '@affine/core/modules/global-context'; import type { Tag } from '@affine/core/modules/tag'; import { TagService } from '@affine/core/modules/tag'; @@ -8,15 +8,15 @@ import clsx from 'clsx'; import { useCallback, useMemo, useState } from 'react'; import { AddItemPlaceholder } from '../../layouts/add-item-placeholder'; -import { ExplorerTreeNode } from '../../tree/node'; -import { ExplorerDocNode } from '../doc'; +import { NavigationPanelTreeNode } from '../../tree/node'; +import { NavigationPanelDocNode } from '../doc'; import { - useExplorerTagNodeOperations, - useExplorerTagNodeOperationsMenu, + useNavigationPanelTagNodeOperations, + useNavigationPanelTagNodeOperationsMenu, } from './operations'; import * as styles from './styles.css'; -export const ExplorerTagNode = ({ +export const NavigationPanelTagNode = ({ tagId, operations: additionalOperations, }: { @@ -41,7 +41,7 @@ export const ExplorerTagNode = ({ return (
{ if (additionalOperations) { @@ -74,7 +74,7 @@ export const ExplorerTagNode = ({ } return ( - - - + + ); }; @@ -96,7 +96,7 @@ export const ExplorerTagNode = ({ * so we split the tag node children into a separate component, * so it won't be rendered when the tag node is collapsed. */ -export const ExplorerTagNodeDocs = ({ +export const NavigationPanelTagNodeDocs = ({ tag, onNewDoc, }: { @@ -109,7 +109,7 @@ export const ExplorerTagNodeDocs = ({ return ( <> {tagDocIds.map(docId => ( - + ))} diff --git a/packages/frontend/core/src/mobile/components/explorer/nodes/tag/operations.tsx b/packages/frontend/core/src/mobile/components/navigation/nodes/tag/operations.tsx similarity index 97% rename from packages/frontend/core/src/mobile/components/explorer/nodes/tag/operations.tsx rename to packages/frontend/core/src/mobile/components/navigation/nodes/tag/operations.tsx index 3bb335358c..dccb0790bc 100644 --- a/packages/frontend/core/src/mobile/components/explorer/nodes/tag/operations.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/nodes/tag/operations.tsx @@ -7,9 +7,9 @@ import { } from '@affine/component'; import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils'; import { IsFavoriteIcon } from '@affine/core/components/pure/icons'; +import type { NodeOperation } from '@affine/core/desktop/components/navigation-panel'; import { WorkspaceDialogService } from '@affine/core/modules/dialogs'; import { DocsService } from '@affine/core/modules/doc'; -import type { NodeOperation } from '@affine/core/modules/explorer'; import { FavoriteService } from '@affine/core/modules/favorite'; import { GlobalCacheService } from '@affine/core/modules/storage'; import { TagService } from '@affine/core/modules/tag'; @@ -28,7 +28,7 @@ import { useCallback, useMemo } from 'react'; import { TagRenameSubMenu } from './dialog'; -export const useExplorerTagNodeOperations = ( +export const useNavigationPanelTagNodeOperations = ( tagId: string, { openNodeCollapsed, @@ -211,7 +211,7 @@ export const useExplorerTagNodeOperations = ( ] ); }; -export const useExplorerTagNodeOperationsMenu = ( +export const useNavigationPanelTagNodeOperationsMenu = ( tagId: string, option: { openNodeCollapsed: () => void; @@ -226,7 +226,7 @@ export const useExplorerTagNodeOperationsMenu = ( handleToggleFavoriteTag, handleChangeNameOrColor, handleOpenDocSelector, - } = useExplorerTagNodeOperations(tagId, option); + } = useNavigationPanelTagNodeOperations(tagId, option); return useMemo( () => [ diff --git a/packages/frontend/core/src/modules/explorer/views/nodes/tag/styles.css.ts b/packages/frontend/core/src/mobile/components/navigation/nodes/tag/styles.css.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/nodes/tag/styles.css.ts rename to packages/frontend/core/src/mobile/components/navigation/nodes/tag/styles.css.ts diff --git a/packages/frontend/core/src/modules/explorer/views/sections/collections/index.css.ts b/packages/frontend/core/src/mobile/components/navigation/sections/collections/index.css.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/views/sections/collections/index.css.ts rename to packages/frontend/core/src/mobile/components/navigation/sections/collections/index.css.ts diff --git a/packages/frontend/core/src/mobile/components/explorer/sections/collections/index.tsx b/packages/frontend/core/src/mobile/components/navigation/sections/collections/index.tsx similarity index 74% rename from packages/frontend/core/src/mobile/components/explorer/sections/collections/index.tsx rename to packages/frontend/core/src/mobile/components/navigation/sections/collections/index.tsx index 349e0f3faa..d74fd01ecb 100644 --- a/packages/frontend/core/src/mobile/components/explorer/sections/collections/index.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/sections/collections/index.tsx @@ -1,8 +1,8 @@ import { usePromptModal } from '@affine/component'; import { createEmptyCollection } from '@affine/core/components/page-list/use-collection-manager'; +import { NavigationPanelTreeRoot } from '@affine/core/desktop/components/navigation-panel'; import { CollectionService } from '@affine/core/modules/collection'; -import { ExplorerService } from '@affine/core/modules/explorer'; -import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree'; +import { NavigationPanelService } from '@affine/core/modules/navigation-panel'; import { WorkbenchService } from '@affine/core/modules/workbench'; import { useI18n } from '@affine/i18n'; import { track } from '@affine/track'; @@ -13,17 +13,18 @@ import { useCallback } from 'react'; import { AddItemPlaceholder } from '../../layouts/add-item-placeholder'; import { CollapsibleSection } from '../../layouts/collapsible-section'; -import { ExplorerCollectionNode } from '../../nodes/collection'; +import { NavigationPanelCollectionNode } from '../../nodes/collection'; import * as styles from './index.css'; -export const ExplorerCollections = () => { +export const NavigationPanelCollections = () => { const t = useI18n(); - const { collectionService, workbenchService, explorerService } = useServices({ - CollectionService, - WorkbenchService, - ExplorerService, - }); - const explorerSection = explorerService.sections.collections; + const { collectionService, workbenchService, navigationPanelService } = + useServices({ + CollectionService, + WorkbenchService, + NavigationPanelService, + }); + const navigationPanelSection = navigationPanelService.sections.collections; const collections = useLiveData(collectionService.collections$); const { openPromptModal } = usePromptModal(); @@ -51,12 +52,12 @@ export const ExplorerCollections = () => { type: 'collection', }); workbenchService.workbench.openCollection(id); - explorerSection.setCollapsed(false); + navigationPanelSection.setCollapsed(false); }, }); }, [ collectionService, - explorerSection, + navigationPanelSection, openPromptModal, t, workbenchService.workbench, @@ -65,23 +66,23 @@ export const ExplorerCollections = () => { return ( - + {collections.map(collection => ( - ))} } - data-testid="explorer-bar-add-collection-button" + data-testid="navigation-panel-bar-add-collection-button" label={t['com.affine.rootAppSidebar.collection.new']()} onClick={() => handleCreateCollection()} /> - + ); }; diff --git a/packages/frontend/core/src/mobile/components/explorer/sections/favorites/index.tsx b/packages/frontend/core/src/mobile/components/navigation/sections/favorites/index.tsx similarity index 57% rename from packages/frontend/core/src/mobile/components/explorer/sections/favorites/index.tsx rename to packages/frontend/core/src/mobile/components/navigation/sections/favorites/index.tsx index 18e3a84a71..195f1d2e4b 100644 --- a/packages/frontend/core/src/mobile/components/explorer/sections/favorites/index.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/sections/favorites/index.tsx @@ -1,10 +1,8 @@ import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils'; -import { - ExplorerService, - ExplorerTreeRoot, -} from '@affine/core/modules/explorer'; +import { NavigationPanelTreeRoot } from '@affine/core/desktop/components/navigation-panel'; import type { FavoriteSupportTypeUnion } from '@affine/core/modules/favorite'; import { FavoriteService } from '@affine/core/modules/favorite'; +import { NavigationPanelService } from '@affine/core/modules/navigation-panel'; import { WorkspaceService } from '@affine/core/modules/workspace'; import { useI18n } from '@affine/i18n'; import { useLiveData, useServices } from '@toeverything/infra'; @@ -12,20 +10,21 @@ import { useCallback } from 'react'; import { AddItemPlaceholder } from '../../layouts/add-item-placeholder'; import { CollapsibleSection } from '../../layouts/collapsible-section'; -import { ExplorerCollectionNode } from '../../nodes/collection'; -import { ExplorerDocNode } from '../../nodes/doc'; -import { ExplorerFolderNode } from '../../nodes/folder'; -import { ExplorerTagNode } from '../../nodes/tag'; +import { NavigationPanelCollectionNode } from '../../nodes/collection'; +import { NavigationPanelDocNode } from '../../nodes/doc'; +import { NavigationPanelFolderNode } from '../../nodes/folder'; +import { NavigationPanelTagNode } from '../../nodes/tag'; -export const ExplorerFavorites = () => { - const { favoriteService, workspaceService, explorerService } = useServices({ - FavoriteService, - WorkspaceService, - ExplorerService, - }); +export const NavigationPanelFavorites = () => { + const { favoriteService, workspaceService, navigationPanelService } = + useServices({ + FavoriteService, + WorkspaceService, + NavigationPanelService, + }); const t = useI18n(); - const explorerSection = explorerService.sections.favorites; + const navigationPanelSection = navigationPanelService.sections.favorites; const favorites = useLiveData(favoriteService.favoriteList.sortedList$); const isLoading = useLiveData(favoriteService.favoriteList.isLoading$); const { createPage } = usePageHelper( @@ -39,28 +38,28 @@ export const ExplorerFavorites = () => { newDoc.id, favoriteService.favoriteList.indexAt('before') ); - explorerSection.setCollapsed(false); - }, [createPage, explorerSection, favoriteService.favoriteList]); + navigationPanelSection.setCollapsed(false); + }, [createPage, navigationPanelSection, favoriteService.favoriteList]); return ( - + {favorites.map(favorite => ( ))} - + ); }; @@ -74,12 +73,12 @@ export const FavoriteNode = ({ }; }) => { return favorite.type === 'doc' ? ( - + ) : favorite.type === 'tag' ? ( - + ) : favorite.type === 'folder' ? ( - + ) : ( - + ); }; diff --git a/packages/frontend/core/src/mobile/components/explorer/sections/favorites/loading.tsx b/packages/frontend/core/src/mobile/components/navigation/sections/favorites/loading.tsx similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/sections/favorites/loading.tsx rename to packages/frontend/core/src/mobile/components/navigation/sections/favorites/loading.tsx diff --git a/packages/frontend/core/src/mobile/components/explorer/sections/organize/index.tsx b/packages/frontend/core/src/mobile/components/navigation/sections/organize/index.tsx similarity index 68% rename from packages/frontend/core/src/mobile/components/explorer/sections/organize/index.tsx rename to packages/frontend/core/src/mobile/components/navigation/sections/organize/index.tsx index ebd60bc715..579581d227 100644 --- a/packages/frontend/core/src/mobile/components/explorer/sections/organize/index.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/sections/organize/index.tsx @@ -1,8 +1,6 @@ import { Skeleton } from '@affine/component'; -import { - ExplorerService, - ExplorerTreeRoot, -} from '@affine/core/modules/explorer'; +import { NavigationPanelTreeRoot } from '@affine/core/desktop/components/navigation-panel'; +import { NavigationPanelService } from '@affine/core/modules/navigation-panel'; import { OrganizeService } from '@affine/core/modules/organize'; import { useI18n } from '@affine/i18n'; import track from '@affine/track'; @@ -12,15 +10,15 @@ import { useCallback, useState } from 'react'; import { AddItemPlaceholder } from '../../layouts/add-item-placeholder'; import { CollapsibleSection } from '../../layouts/collapsible-section'; -import { ExplorerFolderNode } from '../../nodes/folder'; +import { NavigationPanelFolderNode } from '../../nodes/folder'; import { FolderCreateTip, FolderRenameDialog } from '../../nodes/folder/dialog'; -export const ExplorerOrganize = () => { - const { organizeService, explorerService } = useServices({ +export const NavigationPanelOrganize = () => { + const { organizeService, navigationPanelService } = useServices({ OrganizeService, - ExplorerService, + NavigationPanelService, }); - const explorerSection = explorerService.sections.organize; + const navigationPanelSection = navigationPanelService.sections.organize; const [openNewFolderDialog, setOpenNewFolderDialog] = useState(false); const t = useI18n(); @@ -38,10 +36,10 @@ export const ExplorerOrganize = () => { rootFolder.indexAt('before') ); track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' }); - explorerSection.setCollapsed(false); + navigationPanelSection.setCollapsed(false); return newFolderId; }, - [explorerSection, rootFolder] + [navigationPanelSection, rootFolder] ); return ( @@ -50,17 +48,20 @@ export const ExplorerOrganize = () => { title={t['com.affine.rootAppSidebar.organize']()} > {/* TODO(@CatsJuice): Organize loading UI */} - : null}> + : null}> {folders.map(child => ( - + ))} } - data-testid="explorer-bar-add-organize-button" + data-testid="navigation-panel-bar-add-organize-button" label={t['com.affine.rootAppSidebar.organize.add-folder']()} onClick={() => setOpenNewFolderDialog(true)} /> - + { @@ -20,12 +20,12 @@ export const TagDesc = ({ input }: { input: string }) => { : t['com.affine.m.explorer.tag.new-tip-empty'](); }; -export const ExplorerTags = () => { - const { tagService, explorerService } = useServices({ +export const NavigationPanelTags = () => { + const { tagService, navigationPanelService } = useServices({ TagService, - ExplorerService, + NavigationPanelService, }); - const explorerSection = explorerService.sections.tags; + const navigationPanelSection = navigationPanelService.sections.tags; const tags = useLiveData(tagService.tagList.tags$); const [showNewTagDialog, setShowNewTagDialog] = useState(false); @@ -36,9 +36,9 @@ export const ExplorerTags = () => { setShowNewTagDialog(false); tagService.tagList.createTag(name, color); track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' }); - explorerSection.setCollapsed(false); + navigationPanelSection.setCollapsed(false); }, - [explorerSection, tagService] + [navigationPanelSection, tagService] ); return ( @@ -46,13 +46,13 @@ export const ExplorerTags = () => { name="tags" title={t['com.affine.rootAppSidebar.tags']()} > - + {tags.map(tag => ( - + ))} } - data-testid="explorer-add-tag-button" + data-testid="navigation-panel-add-tag-button" onClick={() => setShowNewTagDialog(true)} label={t[ 'com.affine.rootAppSidebar.explorer.tag-section-add-tooltip' @@ -65,7 +65,7 @@ export const ExplorerTags = () => { enableAnimation descRenderer={TagDesc} /> - + ); }; diff --git a/packages/frontend/core/src/mobile/components/explorer/tree/node.css.ts b/packages/frontend/core/src/mobile/components/navigation/tree/node.css.ts similarity index 100% rename from packages/frontend/core/src/mobile/components/explorer/tree/node.css.ts rename to packages/frontend/core/src/mobile/components/navigation/tree/node.css.ts diff --git a/packages/frontend/core/src/mobile/components/explorer/tree/node.tsx b/packages/frontend/core/src/mobile/components/navigation/tree/node.tsx similarity index 90% rename from packages/frontend/core/src/mobile/components/explorer/tree/node.tsx rename to packages/frontend/core/src/mobile/components/navigation/tree/node.tsx index c77287461b..eb433052f3 100644 --- a/packages/frontend/core/src/mobile/components/explorer/tree/node.tsx +++ b/packages/frontend/core/src/mobile/components/navigation/tree/node.tsx @@ -1,6 +1,8 @@ import { MobileMenu } from '@affine/component'; -import type { BaseExplorerTreeNodeProps } from '@affine/core/modules/explorer'; -import { ExplorerTreeContext } from '@affine/core/modules/explorer'; +import { + type BaseNavigationPanelTreeNodeProps, + NavigationPanelTreeContext, +} from '@affine/core/desktop/components/navigation-panel'; import { WorkbenchLink } from '@affine/core/modules/workbench'; import { extractEmojiIcon } from '@affine/core/utils'; import { ArrowDownSmallIcon, MoreHorizontalIcon } from '@blocksuite/icons/rc'; @@ -18,9 +20,10 @@ import { import { SwipeMenu } from '../../swipe-menu'; import * as styles from './node.css'; -interface ExplorerTreeNodeProps extends BaseExplorerTreeNodeProps {} +interface NavigationPanelTreeNodeProps + extends BaseNavigationPanelTreeNodeProps {} -export const ExplorerTreeNode = ({ +export const NavigationPanelTreeNode = ({ children, icon: Icon, name: rawName, @@ -37,8 +40,8 @@ export const ExplorerTreeNode = ({ childrenPlaceholder, linkComponent: LinkComponent = WorkbenchLink, ...otherProps -}: ExplorerTreeNodeProps) => { - const context = useContext(ExplorerTreeContext); +}: NavigationPanelTreeNodeProps) => { + const context = useContext(NavigationPanelTreeContext); const level = context?.level ?? 0; // If no onClick or to is provided, clicking on the node will toggle the collapse state const clickForCollapse = !onClick && !to && !disabled; @@ -141,7 +144,7 @@ export const ExplorerTreeNode = ({
{childCount === 0 && !collapsed && childrenPlaceholder}
- + {collapsed ? null : children} - + ); diff --git a/packages/frontend/core/src/mobile/components/navigation/tree/root.css.ts b/packages/frontend/core/src/mobile/components/navigation/tree/root.css.ts new file mode 100644 index 0000000000..2db3cdf978 --- /dev/null +++ b/packages/frontend/core/src/mobile/components/navigation/tree/root.css.ts @@ -0,0 +1,10 @@ +import { style } from '@vanilla-extract/css'; + +export const placeholder = style({ + display: 'none', + selectors: { + '&:only-child': { + display: 'initial', + }, + }, +}); diff --git a/packages/frontend/core/src/mobile/components/navigation/tree/root.tsx b/packages/frontend/core/src/mobile/components/navigation/tree/root.tsx new file mode 100644 index 0000000000..4fe0020789 --- /dev/null +++ b/packages/frontend/core/src/mobile/components/navigation/tree/root.tsx @@ -0,0 +1,43 @@ +import { + NavigationPanelTreeContext, + type NodeOperation, +} from '@affine/core/desktop/components/navigation-panel'; +import { useMemo, useState } from 'react'; + +import * as styles from './root.css'; + +export const NavigationPanelTreeRoot = ({ + children, + childrenOperations = [], + placeholder, +}: { + children?: React.ReactNode; + childrenOperations?: NodeOperation[]; + className?: string; + placeholder?: React.ReactNode; +}) => { + const [childCount, setChildCount] = useState(0); + const contextValue = useMemo(() => { + return { + operations: childrenOperations, + level: 0, + registerChild: () => { + setChildCount(c => c + 1); + return () => setChildCount(c => c - 1); + }, + }; + }, [childrenOperations]); + + return ( + //
is for placeholder:last-child selector +
+ {/* For lastInGroup check, the placeholder must be placed above all children in the dom */} +
+ {childCount === 0 && placeholder} +
+ + {children} + +
+ ); +}; diff --git a/packages/frontend/core/src/mobile/pages/workspace/home.tsx b/packages/frontend/core/src/mobile/pages/workspace/home.tsx index 9fffda9a4b..531e6d7bae 100644 --- a/packages/frontend/core/src/mobile/pages/workspace/home.tsx +++ b/packages/frontend/core/src/mobile/pages/workspace/home.tsx @@ -2,11 +2,11 @@ import { SafeArea, useThemeColorV2 } from '@affine/component'; import { AppTabs } from '../../components'; import { - ExplorerCollections, - ExplorerFavorites, - ExplorerOrganize, - ExplorerTags, -} from '../../components/explorer'; + NavigationPanelCollections, + NavigationPanelFavorites, + NavigationPanelOrganize, + NavigationPanelTags, +} from '../../components/navigation'; import { HomeHeader, RecentDocs } from '../../views'; export const Component = () => { @@ -25,10 +25,10 @@ export const Component = () => { padding: '0 8px 32px 8px', }} > - - - - + + + +
diff --git a/packages/frontend/core/src/mobile/views/recent-docs/index.tsx b/packages/frontend/core/src/mobile/views/recent-docs/index.tsx index 64d56e191e..b2eb46bbb9 100644 --- a/packages/frontend/core/src/mobile/views/recent-docs/index.tsx +++ b/packages/frontend/core/src/mobile/views/recent-docs/index.tsx @@ -4,7 +4,7 @@ import { useService } from '@toeverything/infra'; import { useMemo } from 'react'; import { DocCard } from '../../components/doc-card'; -import { CollapsibleSection } from '../../components/explorer'; +import { CollapsibleSection } from '../../components/navigation'; import * as styles from './styles.css'; export const RecentDocs = ({ max = 5 }: { max?: number }) => { diff --git a/packages/frontend/core/src/modules/explorer/README.md b/packages/frontend/core/src/modules/explorer/README.md deleted file mode 100644 index 6c26343127..0000000000 --- a/packages/frontend/core/src/modules/explorer/README.md +++ /dev/null @@ -1,3 +0,0 @@ -# explorer - -file manager in app left sidebar diff --git a/packages/frontend/core/src/modules/explorer/index.ts b/packages/frontend/core/src/modules/explorer/index.ts deleted file mode 100644 index 3d1e72a8c0..0000000000 --- a/packages/frontend/core/src/modules/explorer/index.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { type Framework } from '@toeverything/infra'; - -import { GlobalCache } from '../storage'; -import { WorkspaceScope } from '../workspace'; -import { ExplorerSection } from './entities/explore-section'; -import { ExplorerService } from './services/explorer'; -export { ExplorerService } from './services/explorer'; -export type { CollapsibleSectionName } from './types'; -export { CollapsibleSection } from './views/layouts/collapsible-section'; -export { ExplorerCollections } from './views/sections/collections'; -export { ExplorerFavorites } from './views/sections/favorites'; -export { ExplorerMigrationFavorites } from './views/sections/migration-favorites'; -export { ExplorerOrganize } from './views/sections/organize'; -// for mobile -export { ExplorerTreeRoot } from './views/tree'; -export { ExplorerTreeContext } from './views/tree/context'; -export type { - BaseExplorerTreeNodeProps, - ExplorerTreeNodeIcon, -} from './views/tree/node'; -export type { NodeOperation } from './views/tree/types'; - -export function configureExplorerModule(framework: Framework) { - framework - .scope(WorkspaceScope) - .service(ExplorerService) - .entity(ExplorerSection, [GlobalCache]); -} diff --git a/packages/frontend/core/src/modules/explorer/views/layouts/empty-section.tsx b/packages/frontend/core/src/modules/explorer/views/layouts/empty-section.tsx deleted file mode 100644 index e664be8c56..0000000000 --- a/packages/frontend/core/src/modules/explorer/views/layouts/empty-section.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { Button } from '@affine/component'; -import clsx from 'clsx'; -import { - cloneElement, - forwardRef, - type HTMLAttributes, - type JSX, - type ReactElement, - type Ref, - type SVGAttributes, - type SVGProps, -} from 'react'; - -import * as styles from './empty-section.css'; - -interface ExplorerEmptySectionProps extends HTMLAttributes { - icon: - | ((props: SVGProps) => JSX.Element) - | ReactElement>; - message: string; - messageTestId?: string; - actionText?: string; - onActionClick?: () => void; -} - -export const ExplorerEmptySection = forwardRef(function ExplorerEmptySection( - { - icon: Icon, - message, - messageTestId, - actionText, - children, - className, - onActionClick, - ...attrs - }: ExplorerEmptySectionProps, - ref: Ref -) { - const icon = - typeof Icon === 'function' ? ( - - ) : ( - cloneElement(Icon, { className: styles.icon }) - ); - - return ( -
-
{icon}
-
- {message} -
- {actionText ? ( - - ) : null} - {children} -
- ); -}); diff --git a/packages/frontend/core/src/modules/explorer/views/sections/favorites/dnd.ts b/packages/frontend/core/src/modules/explorer/views/sections/favorites/dnd.ts deleted file mode 100644 index 32aede8269..0000000000 --- a/packages/frontend/core/src/modules/explorer/views/sections/favorites/dnd.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { DropTargetOptions } from '@affine/component'; -import { isFavoriteSupportType } from '@affine/core/modules/favorite'; -import type { AffineDNDData } from '@affine/core/types/dnd'; - -import type { ExplorerTreeNodeDropEffect } from '../../tree'; - -export const favoriteChildrenDropEffect: ExplorerTreeNodeDropEffect = data => { - if ( - data.treeInstruction?.type === 'reorder-above' || - data.treeInstruction?.type === 'reorder-below' - ) { - if ( - data.source.data.from?.at === 'explorer:favorite:list' && - data.source.data.entity?.type && - isFavoriteSupportType(data.source.data.entity.type) - ) { - return 'move'; - } else if ( - data.source.data.entity?.type && - isFavoriteSupportType(data.source.data.entity.type) - ) { - return 'link'; - } - } - return; // not supported -}; - -export const favoriteRootDropEffect: ExplorerTreeNodeDropEffect = data => { - const sourceType = data.source.data.entity?.type; - if (sourceType && isFavoriteSupportType(sourceType)) { - return 'link'; - } - return; -}; - -export const favoriteRootCanDrop: DropTargetOptions['canDrop'] = - data => { - return data.source.data.entity?.type - ? isFavoriteSupportType(data.source.data.entity.type) - : false; - }; - -export const favoriteChildrenCanDrop: DropTargetOptions['canDrop'] = - // Same as favoriteRootCanDrop - data => favoriteRootCanDrop(data); diff --git a/packages/frontend/core/src/modules/explorer/views/sections/organize/dnd.ts b/packages/frontend/core/src/modules/explorer/views/sections/organize/dnd.ts deleted file mode 100644 index e713ac1a25..0000000000 --- a/packages/frontend/core/src/modules/explorer/views/sections/organize/dnd.ts +++ /dev/null @@ -1,36 +0,0 @@ -import type { DropTargetOptions } from '@affine/component'; -import { isOrganizeSupportType } from '@affine/core/modules/organize/constants'; -import type { AffineDNDData } from '@affine/core/types/dnd'; - -import type { ExplorerTreeNodeDropEffect } from '../../tree'; - -export const organizeChildrenDropEffect: ExplorerTreeNodeDropEffect = data => { - if ( - data.treeInstruction?.type === 'reorder-above' || - data.treeInstruction?.type === 'reorder-below' - ) { - if (data.source.data.entity?.type === 'folder') { - return 'move'; - } - } else { - return; // not supported - } - return; -}; - -export const organizeEmptyDropEffect: ExplorerTreeNodeDropEffect = data => { - const sourceType = data.source.data.entity?.type; - if (sourceType && isOrganizeSupportType(sourceType)) { - return 'link'; - } - return; -}; - -/** - * Check whether the data can be dropped on the empty state of the organize section - */ -export const organizeEmptyRootCanDrop: DropTargetOptions['canDrop'] = - data => { - const type = data.source.data.entity?.type; - return !!type && isOrganizeSupportType(type); - }; diff --git a/packages/frontend/core/src/modules/explorer/views/tree/context.tsx b/packages/frontend/core/src/modules/explorer/views/tree/context.tsx deleted file mode 100644 index 584cf16a84..0000000000 --- a/packages/frontend/core/src/modules/explorer/views/tree/context.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from 'react'; - -export interface ExplorerTreeContextData { - /** - * The level of the current tree node. - */ - level: number; -} - -export const ExplorerTreeContext = - React.createContext(null); diff --git a/packages/frontend/core/src/modules/explorer/views/tree/index.tsx b/packages/frontend/core/src/modules/explorer/views/tree/index.tsx deleted file mode 100644 index a31d02c0c0..0000000000 --- a/packages/frontend/core/src/modules/explorer/views/tree/index.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export { DropEffect } from './drop-effect'; -export type { - ExplorerTreeNodeDropEffect, - ExplorerTreeNodeDropEffectData, -} from './node'; -export { ExplorerTreeNode } from './node'; -export { ExplorerTreeRoot } from './root'; diff --git a/packages/frontend/core/src/modules/index.ts b/packages/frontend/core/src/modules/index.ts index 2ed33dafd3..888366faeb 100644 --- a/packages/frontend/core/src/modules/index.ts +++ b/packages/frontend/core/src/modules/index.ts @@ -20,7 +20,6 @@ import { configureDocLinksModule } from './doc-link'; import { configureDocsSearchModule } from './docs-search'; import { configureEditorModule } from './editor'; import { configureEditorSettingModule } from './editor-setting'; -import { configureExplorerModule } from './explorer'; import { configureFavoriteModule } from './favorite'; import { configureFeatureFlagModule } from './feature-flag'; import { configureGlobalContextModule } from './global-context'; @@ -32,6 +31,7 @@ import { configureJournalModule } from './journal'; import { configureLifecycleModule } from './lifecycle'; import { configureMediaModule } from './media'; import { configureNavigationModule } from './navigation'; +import { configureNavigationPanelModule } from './navigation-panel'; import { configureNotificationModule } from './notification'; import { configureOpenInApp } from './open-in-app'; import { configureOrganizeModule } from './organize'; @@ -82,7 +82,7 @@ export function configureCommonModules(framework: Framework) { configureDocLinksModule(framework); configureOrganizeModule(framework); configureFavoriteModule(framework); - configureExplorerModule(framework); + configureNavigationPanelModule(framework); configureThemeEditorModule(framework); configureEditorModule(framework); configureSystemFontFamilyModule(framework); diff --git a/packages/frontend/core/src/modules/explorer/entities/explore-section.ts b/packages/frontend/core/src/modules/navigation-panel/entities/navigation-panel-section.ts similarity index 90% rename from packages/frontend/core/src/modules/explorer/entities/explore-section.ts rename to packages/frontend/core/src/modules/navigation-panel/entities/navigation-panel-section.ts index 0b46349a5c..c219a7fc94 100644 --- a/packages/frontend/core/src/modules/explorer/entities/explore-section.ts +++ b/packages/frontend/core/src/modules/navigation-panel/entities/navigation-panel-section.ts @@ -15,7 +15,9 @@ const DEFAULT_COLLAPSABLE_STATE: Record = { others: false, }; -export class ExplorerSection extends Entity<{ name: CollapsibleSectionName }> { +export class NavigationPanelSection extends Entity<{ + name: CollapsibleSectionName; +}> { name: CollapsibleSectionName = this.props.name; key = `explorer.section.${this.name}`; defaultValue = DEFAULT_COLLAPSABLE_STATE[this.name]; diff --git a/packages/frontend/core/src/modules/navigation-panel/index.ts b/packages/frontend/core/src/modules/navigation-panel/index.ts new file mode 100644 index 0000000000..aabe764034 --- /dev/null +++ b/packages/frontend/core/src/modules/navigation-panel/index.ts @@ -0,0 +1,15 @@ +import { type Framework } from '@toeverything/infra'; + +import { GlobalCache } from '../storage'; +import { WorkspaceScope } from '../workspace'; +import { NavigationPanelSection } from './entities/navigation-panel-section'; +import { NavigationPanelService } from './services/navigation-panel'; +export { NavigationPanelService } from './services/navigation-panel'; +export type { CollapsibleSectionName } from './types'; + +export function configureNavigationPanelModule(framework: Framework) { + framework + .scope(WorkspaceScope) + .service(NavigationPanelService) + .entity(NavigationPanelSection, [GlobalCache]); +} diff --git a/packages/frontend/core/src/modules/explorer/services/explorer.ts b/packages/frontend/core/src/modules/navigation-panel/services/navigation-panel.ts similarity index 59% rename from packages/frontend/core/src/modules/explorer/services/explorer.ts rename to packages/frontend/core/src/modules/navigation-panel/services/navigation-panel.ts index ce7b961654..b7f3f1c126 100644 --- a/packages/frontend/core/src/modules/explorer/services/explorer.ts +++ b/packages/frontend/core/src/modules/navigation-panel/services/navigation-panel.ts @@ -1,6 +1,6 @@ import { Service } from '@toeverything/infra'; -import { ExplorerSection } from '../entities/explore-section'; +import { NavigationPanelSection } from '../entities/navigation-panel-section'; import type { CollapsibleSectionName } from '../types'; const allSectionName: Array = [ @@ -14,12 +14,12 @@ const allSectionName: Array = [ 'others', ]; -export class ExplorerService extends Service { +export class NavigationPanelService extends Service { readonly sections = allSectionName.reduce( (prev, name) => Object.assign(prev, { - [name]: this.framework.createEntity(ExplorerSection, { name }), + [name]: this.framework.createEntity(NavigationPanelSection, { name }), }), - {} as Record + {} as Record ); } diff --git a/packages/frontend/core/src/modules/explorer/types.ts b/packages/frontend/core/src/modules/navigation-panel/types.ts similarity index 100% rename from packages/frontend/core/src/modules/explorer/types.ts rename to packages/frontend/core/src/modules/navigation-panel/types.ts diff --git a/packages/frontend/core/src/types/dnd.ts b/packages/frontend/core/src/types/dnd.ts index 5508f73994..7afb1420bd 100644 --- a/packages/frontend/core/src/types/dnd.ts +++ b/packages/frontend/core/src/types/dnd.ts @@ -27,28 +27,28 @@ export interface AffineDNDData extends DNDData { entity?: AffineDNDEntity; from?: | { - at: 'explorer:organize:folder-node'; + at: 'navigation-panel:organize:folder-node'; nodeId: string; } | { - at: 'explorer:collection:list'; + at: 'navigation-panel:collection:list'; } | { - at: 'explorer:doc:linked-docs'; + at: 'navigation-panel:doc:linked-docs'; docId: string; } | { - at: 'explorer:collection:filtered-docs'; + at: 'navigation-panel:collection:filtered-docs'; collectionId: string; } | { - at: 'explorer:favorite:list'; + at: 'navigation-panel:favorite:list'; } | { - at: 'explorer:old-favorite:list'; + at: 'navigation-panel:old-favorite:list'; } | { - at: 'explorer:migration-data:list'; + at: 'navigation-panel:migration-data:list'; } | { at: 'all-docs:list'; @@ -60,10 +60,10 @@ export interface AffineDNDData extends DNDData { at: 'all-collections:list'; } | { - at: 'explorer:tags:list'; + at: 'navigation-panel:tags:list'; } | { - at: 'explorer:tags:docs'; + at: 'navigation-panel:tags:docs'; } | { at: 'app-header:tabs'; @@ -103,28 +103,28 @@ export interface AffineDNDData extends DNDData { }; dropTarget: | { - at: 'explorer:organize:root'; + at: 'navigation-panel:organize:root'; } | { - at: 'explorer:favorite:root'; + at: 'navigation-panel:favorite:root'; } | { - at: 'explorer:organize:folder'; + at: 'navigation-panel:organize:folder'; } | { - at: 'explorer:favorite:root'; + at: 'navigation-panel:favorite:root'; } | { - at: 'explorer:old-favorite:root'; + at: 'navigation-panel:old-favorite:root'; } | { - at: 'explorer:doc'; + at: 'navigation-panel:doc'; } | { at: 'app-sidebar:trash'; } | { - at: 'explorer:tag'; + at: 'navigation-panel:tag'; } | { at: 'app-header:tabs'; diff --git a/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts b/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts index c3a5bda722..87807adacb 100644 --- a/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts +++ b/tests/affine-cloud-copilot/e2e/utils/editor-utils.ts @@ -241,8 +241,8 @@ export class EditorUtils { public static async clearAllCollections(page: Page) { while (true) { const collection = await page - .getByTestId('explorer-collections') - .locator('[data-testid^="explorer-collection-"]') + .getByTestId('navigation-panel-collections') + .locator('[data-testid^="navigation-panel-collection-"]') .first(); if (!(await collection.isVisible())) { @@ -252,7 +252,7 @@ export class EditorUtils { const collectionContent = await collection.locator('div').first(); await collectionContent.hover(); const more = await collectionContent.getByTestId( - 'explorer-tree-node-operation-button' + 'navigation-panel-tree-node-operation-button' ); await more.click(); await page.getByTestId('collection-delete-button').click(); @@ -263,8 +263,8 @@ export class EditorUtils { public static async clearAllTags(page: Page) { while (true) { const tag = await page - .getByTestId('explorer-tags') - .locator('[data-testid^="explorer-tag-"]') + .getByTestId('navigation-panel-tags') + .locator('[data-testid^="navigation-panel-tag-"]') .first(); if (!(await tag.isVisible())) { @@ -274,7 +274,7 @@ export class EditorUtils { const tagContent = await tag.locator('div').first(); await tagContent.hover(); const more = await tagContent.getByTestId( - 'explorer-tree-node-operation-button' + 'navigation-panel-tree-node-operation-button' ); await more.click(); await page.getByTestId('tag-delete-button').click(); @@ -288,7 +288,9 @@ export class EditorUtils { docContent: string ) { // Create collection - await page.getByTestId('explorer-bar-add-collection-button').click(); + await page + .getByTestId('navigation-panel-bar-add-collection-button') + .click(); const input = await page.getByTestId('prompt-modal-input'); await input.focus(); await input.pressSequentially(collectionName); @@ -318,9 +320,9 @@ export class EditorUtils { docContent: string ) { // Create tag - const tags = await page.getByTestId('explorer-tags'); + const tags = await page.getByTestId('navigation-panel-tags'); await tags.hover(); - await tags.getByTestId('explorer-bar-add-tag-button').click(); + await tags.getByTestId('navigation-panel-bar-add-tag-button').click(); const input = await page.getByTestId('rename-modal-input'); await input.focus(); await input.pressSequentially(tagName); diff --git a/tests/affine-cloud/e2e/collaboration.spec.ts b/tests/affine-cloud/e2e/collaboration.spec.ts index e84d7d7364..8868218588 100644 --- a/tests/affine-cloud/e2e/collaboration.spec.ts +++ b/tests/affine-cloud/e2e/collaboration.spec.ts @@ -110,7 +110,7 @@ test('can sync collections between different browser', async ({ page ); await enableCloudWorkspace(page); - await page.getByTestId('explorer-bar-add-collection-button').click(); + await page.getByTestId('navigation-panel-bar-add-collection-button').click(); const title = page.getByTestId('prompt-modal-input'); await title.isVisible(); await title.fill('test collection'); @@ -122,7 +122,7 @@ test('can sync collections between different browser', async ({ const page2 = await context.newPage(); await loginUser(page2, user); await page2.goto(page.url()); - const collections = page2.getByTestId('explorer-collections'); + const collections = page2.getByTestId('navigation-panel-collections'); await collections.getByTestId('category-divider-collapse-button').click(); await expect(collections.getByText('test collection')).toBeVisible(); } diff --git a/tests/affine-cloud/e2e/workspace.spec.ts b/tests/affine-cloud/e2e/workspace.spec.ts index 7d77e42245..1a774f2c6f 100644 --- a/tests/affine-cloud/e2e/workspace.spec.ts +++ b/tests/affine-cloud/e2e/workspace.spec.ts @@ -109,21 +109,24 @@ test('should transform local favorites data', async ({ page }) => { }, page ); - await page.getByTestId('explorer-bar-add-favorite-button').first().click(); + await page + .getByTestId('navigation-panel-bar-add-favorite-button') + .first() + .click(); await clickPageModeButton(page); await waitForEmptyEditor(page); await getBlockSuiteEditorTitle(page).fill('this is a new fav page'); await expect( page - .getByTestId('explorer-favorites') + .getByTestId('navigation-panel-favorites') .locator('[draggable] >> text=this is a new fav page') ).toBeVisible(); await enableCloudWorkspace(page); await expect( page - .getByTestId('explorer-favorites') + .getByTestId('navigation-panel-favorites') .locator('[draggable] >> text=this is a new fav page') ).toBeVisible(); }); diff --git a/tests/affine-local/e2e/doc-info-modal.spec.ts b/tests/affine-local/e2e/doc-info-modal.spec.ts index 86e44d0eb8..aed46abccd 100644 --- a/tests/affine-local/e2e/doc-info-modal.spec.ts +++ b/tests/affine-local/e2e/doc-info-modal.spec.ts @@ -85,14 +85,14 @@ test('New a page and add to favourites, then open info modal from sidebar', asyn await page.getByTestId('all-pages').click(); const favoriteListItemInSidebar = page.locator( - `[data-testid="explorer-favorites"] [data-testid="explorer-doc-${newPageId}"]` + `[data-testid="navigation-panel-favorites"] [data-testid="navigation-panel-doc-${newPageId}"]` ); expect(await favoriteListItemInSidebar.textContent()).toBe( 'this is a new page' ); await favoriteListItemInSidebar.hover(); await favoriteListItemInSidebar - .getByTestId('explorer-tree-node-operation-button') + .getByTestId('navigation-panel-tree-node-operation-button') .click(); const infoBtn = page.getByText('View Info'); await infoBtn.click(); diff --git a/tests/affine-local/e2e/drag-page.spec.ts b/tests/affine-local/e2e/drag-page.spec.ts index 384d011d96..4a89625e0b 100644 --- a/tests/affine-local/e2e/drag-page.spec.ts +++ b/tests/affine-local/e2e/drag-page.spec.ts @@ -27,23 +27,27 @@ const dragToFavourites = async ( id: string, type: 'doc' | 'collection' | 'tag' | 'folder' = 'doc' ) => { - const favourites = page.getByTestId('explorer-favorite-category-divider'); + const favourites = page.getByTestId( + 'navigation-panel-favorite-category-divider' + ); await dragTo(page, dragItem, favourites); const item = page - .getByTestId(`explorer-favorites`) - .locator(`[data-testid="explorer-${type}-${id}"]`); + .getByTestId(`navigation-panel-favorites`) + .locator(`[data-testid="navigation-panel-${type}-${id}"]`); await expect(item).toBeVisible(); return item; }; const createCollection = async (page: Page, name: string) => { - await page.getByTestId('explorer-bar-add-collection-button').click(); + await page.getByTestId('navigation-panel-bar-add-collection-button').click(); const input = page.getByTestId('prompt-modal-input'); await expect(input).toBeVisible(); await input.fill(name); await page.getByTestId('prompt-modal-confirm').click(); const newCollectionId = getCurrentCollectionIdFromUrl(page); - const collection = page.getByTestId(`explorer-collection-${newCollectionId}`); + const collection = page.getByTestId( + `navigation-panel-collection-${newCollectionId}` + ); await expect(collection).toBeVisible(); return collection; }; @@ -57,7 +61,9 @@ const dragToCollection = async (page: Page, dragItem: Locator) => { await clickSideBarAllPageButton(page); await dragTo(page, dragItem, collection); await page.waitForTimeout(500); - const collectionPage = collection.locator('[data-testid^="explorer-doc-"]'); + const collectionPage = collection.locator( + '[data-testid^="navigation-panel-doc-"]' + ); await expect(collectionPage).toBeVisible(); return collectionPage; }; @@ -182,39 +188,45 @@ test('items in favourites can be reordered by dragging', async ({ page }) => { // assert the order of the items in favourites await expect( - page.getByTestId('explorer-favorites').locator('[draggable]') + page.getByTestId('navigation-panel-favorites').locator('[draggable]') ).toHaveCount(3); await expect( - page.getByTestId('explorer-favorites').locator('[draggable]').first() + page + .getByTestId('navigation-panel-favorites') + .locator('[draggable]') + .first() ).toHaveText('test collection'); await expect( - page.getByTestId('explorer-favorites').locator('[draggable]').last() + page.getByTestId('navigation-panel-favorites').locator('[draggable]').last() ).toHaveText(title0); // drag the first item to the last const firstItem = page - .getByTestId('explorer-favorites') + .getByTestId('navigation-panel-favorites') .locator('[draggable]') .first(); const lastItem = page - .getByTestId('explorer-favorites') + .getByTestId('navigation-panel-favorites') .locator('[draggable]') .last(); await dragTo(page, firstItem, lastItem, 'bottom'); // now check the order again await expect( - page.getByTestId('explorer-favorites').locator('[draggable]') + page.getByTestId('navigation-panel-favorites').locator('[draggable]') ).toHaveCount(3); await expect( - page.getByTestId('explorer-favorites').locator('[draggable]').first() + page + .getByTestId('navigation-panel-favorites') + .locator('[draggable]') + .first() ).toHaveText(title1); await expect( - page.getByTestId('explorer-favorites').locator('[draggable]').last() + page.getByTestId('navigation-panel-favorites').locator('[draggable]').last() ).toHaveText('test collection'); }); @@ -304,8 +316,8 @@ test('drag a favourite page into note on page mode', async ({ page }) => { await page.getByTestId('pin-button').click(); const pageId = getCurrentDocIdFromUrl(page); const item = page - .getByTestId(`explorer-favorites`) - .locator(`[data-testid="explorer-doc-${pageId}"]`); + .getByTestId(`navigation-panel-favorites`) + .locator(`[data-testid="navigation-panel-doc-${pageId}"]`); await expect(item).toBeVisible(); // drag item into blocksuite editor @@ -327,8 +339,8 @@ test('drag a favourite page into canvas on edgeless mode', async ({ page }) => { await page.getByTestId('pin-button').click(); const pageId = getCurrentDocIdFromUrl(page); const item = page - .getByTestId(`explorer-favorites`) - .locator(`[data-testid="explorer-doc-${pageId}"]`); + .getByTestId(`navigation-panel-favorites`) + .locator(`[data-testid="navigation-panel-doc-${pageId}"]`); await expect(item).toBeVisible(); await clickNewPageButton(page, 'edgeless page'); @@ -373,8 +385,8 @@ test('drag a favourite page into note on edgeless mode', async ({ page }) => { await page.getByTestId('pin-button').click(); const pageId = getCurrentDocIdFromUrl(page); const item = page - .getByTestId(`explorer-favorites`) - .locator(`[data-testid="explorer-doc-${pageId}"]`); + .getByTestId(`navigation-panel-favorites`) + .locator(`[data-testid="navigation-panel-doc-${pageId}"]`); await expect(item).toBeVisible(); await clickNewPageButton(page, 'edgeless page'); 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 9c47d86978..37a50d2209 100644 --- a/tests/affine-local/e2e/local-first-collections-items.spec.ts +++ b/tests/affine-local/e2e/local-first-collections-items.spec.ts @@ -67,26 +67,34 @@ const createAndPinCollection = async ( test('Show collections items in sidebar', async ({ page }) => { await removeOnboardingPages(page); await createAndPinCollection(page); - const collections = page.getByTestId('explorer-collections'); + const collections = page.getByTestId('navigation-panel-collections'); await collections.getByTestId('category-divider-collapse-button').click(); - const items = collections.locator('[data-testid^="explorer-collection-"]'); + const items = collections.locator( + '[data-testid^="navigation-panel-collection-"]' + ); await expect(items).toHaveCount(1); const first = items.first(); expect(await first.textContent()).toBe('test collection'); - await first.getByTestId('explorer-collapsed-button').click(); - const collectionPage = first.locator('[data-testid^="explorer-doc-"]').nth(0); + await first.getByTestId('navigation-panel-collapsed-button').click(); + const collectionPage = first + .locator('[data-testid^="navigation-panel-doc-"]') + .nth(0); expect(await collectionPage.textContent()).toBe('test page'); await collectionPage.hover(); await collectionPage - .getByTestId('explorer-tree-node-operation-button') + .getByTestId('navigation-panel-tree-node-operation-button') .click(); const deletePage = page.getByText('Move to trash'); await deletePage.click(); await page.getByTestId('confirm-modal-confirm').click(); - expect(await first.locator('[data-testid^="explorer-doc-"]').count()).toBe(0); + expect( + await first.locator('[data-testid^="navigation-panel-doc-"]').count() + ).toBe(0); // position is a workaround for the hover issue when empty collection status's height > 26px (will cause scroll) await first.hover({ position: { x: 10, y: 10 } }); - await first.getByTestId('explorer-tree-node-operation-button').click(); + await first + .getByTestId('navigation-panel-tree-node-operation-button') + .click(); const deleteCollection = page.getByText('Delete'); await deleteCollection.click(); await page.waitForTimeout(50); @@ -109,13 +117,17 @@ test('Show collections items in sidebar', async ({ page }) => { test('edit collection', async ({ page }) => { await removeOnboardingPages(page); await createAndPinCollection(page); - const collections = page.getByTestId('explorer-collections'); + const collections = page.getByTestId('navigation-panel-collections'); await collections.getByTestId('category-divider-collapse-button').click(); - const items = collections.locator('[data-testid^="explorer-collection-"]'); + const items = collections.locator( + '[data-testid^="navigation-panel-collection-"]' + ); expect(await items.count()).toBe(1); const first = items.first(); await first.hover(); - await first.getByTestId('explorer-tree-node-operation-button').click(); + await first + .getByTestId('navigation-panel-tree-node-operation-button') + .click(); const editCollection = page.getByText('Rename'); await editCollection.click(); await page.getByTestId('rename-modal-input').fill('123'); @@ -127,13 +139,17 @@ test('edit collection', async ({ page }) => { test('edit collection and change filter date', async ({ page }) => { await removeOnboardingPages(page); await createAndPinCollection(page); - const collections = page.getByTestId('explorer-collections'); + const collections = page.getByTestId('navigation-panel-collections'); await collections.getByTestId('category-divider-collapse-button').click(); - const items = collections.locator('[data-testid^="explorer-collection-"]'); + const items = collections.locator( + '[data-testid^="navigation-panel-collection-"]' + ); expect(await items.count()).toBe(1); const first = items.first(); await first.hover(); - await first.getByTestId('explorer-tree-node-operation-button').click(); + await first + .getByTestId('navigation-panel-tree-node-operation-button') + .click(); const editCollection = page.getByText('Rename'); await editCollection.click(); await page.getByTestId('rename-modal-input').fill('123'); @@ -151,21 +167,23 @@ test('add collection from sidebar', async ({ page }) => { const cell = page.getByTestId('page-list-item-title').getByText('test page'); await expect(cell).toBeVisible(); await page - .getByTestId('explorer-collections') + .getByTestId('navigation-panel-collections') .getByTestId('category-divider-collapse-button') .click(); const nullCollection = page.getByTestId( 'slider-bar-collection-empty-message' ); await expect(nullCollection).toBeVisible(); - await page.getByTestId('explorer-bar-add-collection-button').click(); + await page.getByTestId('navigation-panel-bar-add-collection-button').click(); const title = page.getByTestId('prompt-modal-input'); await expect(title).toBeVisible(); await title.fill('test collection'); await page.getByTestId('prompt-modal-confirm').click(); await page.waitForTimeout(100); - const collections = page.getByTestId('explorer-collections'); - const items = collections.locator('[data-testid^="explorer-collection-"]'); + const collections = page.getByTestId('navigation-panel-collections'); + const items = collections.locator( + '[data-testid^="navigation-panel-collection-"]' + ); expect(await items.count()).toBe(1); await expect(nullCollection).not.toBeVisible(); }); diff --git a/tests/affine-local/e2e/local-first-favorites-items.spec.ts b/tests/affine-local/e2e/local-first-favorites-items.spec.ts index ae82eaae59..ab4e2f7435 100644 --- a/tests/affine-local/e2e/local-first-favorites-items.spec.ts +++ b/tests/affine-local/e2e/local-first-favorites-items.spec.ts @@ -29,7 +29,7 @@ test('Show favorite items in sidebar', async ({ page, workspace }) => { const favoriteBtn = page.getByTestId('editor-option-menu-favorite'); await favoriteBtn.click(); const favoriteListItemInSidebar = page.getByTestId( - 'explorer-doc-' + newPageId + 'navigation-panel-doc-' + newPageId ); expect(await favoriteListItemInSidebar.textContent()).toBe( 'this is a new page to favorite' @@ -58,7 +58,7 @@ test('Show favorite reference in sidebar', async ({ page, workspace }) => { const favoriteBtn = page.getByTestId('editor-option-menu-favorite'); await favoriteBtn.click(); - const favItemTestId = 'explorer-doc-' + newPageId; + const favItemTestId = 'navigation-panel-doc-' + newPageId; const favoriteListItemInSidebar = page.getByTestId(favItemTestId); expect(await favoriteListItemInSidebar.textContent()).toBe( @@ -66,14 +66,14 @@ test('Show favorite reference in sidebar', async ({ page, workspace }) => { ); const collapseButton = favoriteListItemInSidebar.getByTestId( - 'explorer-collapsed-button' + 'navigation-panel-collapsed-button' ); await expect(collapseButton).toBeVisible(); await collapseButton.click(); await expect( favoriteListItemInSidebar.locator( - '[data-testid^="explorer-doc-"]:has-text("Another page")' + '[data-testid^="navigation-panel-doc-"]:has-text("Another page")' ) ).toBeVisible(); const currentWorkspace = await workspace.current(); @@ -111,7 +111,7 @@ test("Deleted page's reference will not be shown in sidebar", async ({ const anotherPageId = getCurrentDocIdFromUrl(page); - const favItemTestId = 'explorer-doc-' + newPageId; + const favItemTestId = 'navigation-panel-doc-' + newPageId; await expect(page.getByTestId(favItemTestId)).toHaveText( 'this is a new page to favorite' @@ -119,10 +119,10 @@ test("Deleted page's reference will not be shown in sidebar", async ({ await page .getByTestId(favItemTestId) - .getByTestId('explorer-collapsed-button') + .getByTestId('navigation-panel-collapsed-button') .click(); - const favItemAnotherPageTestId = 'explorer-doc-' + anotherPageId; + const favItemAnotherPageTestId = 'navigation-panel-doc-' + anotherPageId; await expect( page.getByTestId(favItemTestId).getByTestId(favItemAnotherPageTestId) @@ -146,7 +146,10 @@ test('Add new favorite page via sidebar', async ({ page }) => { await openHomePage(page); await waitForEditorLoad(page); - await page.getByTestId('explorer-bar-add-favorite-button').first().click(); + await page + .getByTestId('navigation-panel-bar-add-favorite-button') + .first() + .click(); await clickPageModeButton(page); await waitForEmptyEditor(page); @@ -154,7 +157,7 @@ test('Add new favorite page via sidebar', async ({ page }) => { await getBlockSuiteEditorTitle(page).fill('this is a new fav page'); // check if the page title is shown in the favorite list const favItem = page - .getByTestId('explorer-favorites') + .getByTestId('navigation-panel-favorites') .locator('[draggable] >> text=this is a new fav page'); await expect(favItem).toBeVisible(); }); diff --git a/tests/affine-mobile/e2e/explorer-favorite.spec.ts b/tests/affine-mobile/e2e/explorer-favorite.spec.ts index 096937e1d8..54e86a2004 100644 --- a/tests/affine-mobile/e2e/explorer-favorite.spec.ts +++ b/tests/affine-mobile/e2e/explorer-favorite.spec.ts @@ -7,7 +7,9 @@ import { expandCollapsibleSection, pageBack } from './utils'; test('Create new doc in favorites', async ({ page }) => { const section = await expandCollapsibleSection(page, 'favorites'); - const newButton = section.getByTestId('explorer-bar-add-favorite-button'); + const newButton = section.getByTestId( + 'navigation-panel-bar-add-favorite-button' + ); await newButton.tap(); // const testTitleText = 'Test Favorited Doc'; @@ -19,7 +21,7 @@ test('Create new doc in favorites', async ({ page }) => { await pageBack(page); const section2 = await expandCollapsibleSection(page, 'favorites'); - const node = section2.getByTestId(`explorer-doc-${docId}`); + const node = section2.getByTestId(`navigation-panel-doc-${docId}`); await expect(node).toBeVisible(); // const label = node.getByText(testTitleText); diff --git a/tests/affine-mobile/e2e/explorer-folder.spec.ts b/tests/affine-mobile/e2e/explorer-folder.spec.ts index defb3a1655..a6ec112701 100644 --- a/tests/affine-mobile/e2e/explorer-folder.spec.ts +++ b/tests/affine-mobile/e2e/explorer-folder.spec.ts @@ -4,11 +4,13 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { expandCollapsibleSection, getAttrOfActiveElement, - openExplorerNodeMenu, + openNavigationPanelNodeMenu, } from './utils'; const locateFolder = async (scope: Page | Locator, name: string) => { - return scope.locator(`[data-role="explorer-folder"][aria-label="${name}"]`); + return scope.locator( + `[data-role="navigation-panel-folder"][aria-label="${name}"]` + ); }; /** @@ -21,7 +23,7 @@ const isRenameInputFocused = async (page: Page) => { const createRootFolder = async (page: Page, name: string) => { const section = await expandCollapsibleSection(page, 'organize'); - await section.getByTestId('explorer-bar-add-organize-button').tap(); + await section.getByTestId('navigation-panel-bar-add-organize-button').tap(); const dialog = page.getByRole('dialog'); await expect(dialog).toBeVisible(); await isRenameInputFocused(page); @@ -33,7 +35,7 @@ const createRootFolder = async (page: Page, name: string) => { }; const createSubFolder = async (page: Page, parent: Locator, name: string) => { - const menu = await openExplorerNodeMenu(page, parent); + const menu = await openNavigationPanelNodeMenu(page, parent); await menu.getByTestId('create-subfolder').tap(); const dialog = page.getByRole('dialog'); @@ -64,7 +66,7 @@ test('create a folder and rename it', async ({ page }) => { const appendedName = ' Renamed'; const folder = await createRootFolder(page, originalName); - const menu = await openExplorerNodeMenu(page, folder); + const menu = await openNavigationPanelNodeMenu(page, folder); await menu.getByTestId('rename-folder').tap(); await isRenameInputFocused(page); await page.keyboard.type(appendedName); diff --git a/tests/affine-mobile/e2e/explorer-tag.spec.ts b/tests/affine-mobile/e2e/explorer-tag.spec.ts index 788b178303..0914715024 100644 --- a/tests/affine-mobile/e2e/explorer-tag.spec.ts +++ b/tests/affine-mobile/e2e/explorer-tag.spec.ts @@ -4,15 +4,17 @@ import { expect, type Locator, type Page } from '@playwright/test'; import { expandCollapsibleSection, getAttrOfActiveElement, - openExplorerNodeMenu, + openNavigationPanelNodeMenu, } from './utils'; async function locateTag(scope: Page | Locator, name: string) { - return scope.locator(`[data-role="explorer-tag"][aria-label="${name}"]`); + return scope.locator( + `[data-role="navigation-panel-tag"][aria-label="${name}"]` + ); } -async function getExplorerTagColor(tagNode: Locator) { - const icon = tagNode.getByTestId('explorer-tag-icon-dot'); +async function getNavigationPanelTagColor(tagNode: Locator) { + const icon = tagNode.getByTestId('navigation-panel-tag-icon-dot'); await expect(icon).toBeVisible(); const color = await icon.evaluate(el => el.style.backgroundColor); return color; @@ -33,7 +35,7 @@ async function createRootTag( color = 'var(--affine-palette-line-red)' ) { const section = await expandCollapsibleSection(page, 'tags'); - await section.getByTestId('explorer-add-tag-button').tap(); + await section.getByTestId('navigation-panel-add-tag-button').tap(); const dialog = page.getByRole('dialog'); await expect(dialog).toBeVisible(); // input name @@ -47,21 +49,21 @@ async function createRootTag( const tag = await locateTag(section, name); await expect(tag).toBeVisible(); // check tag color - const fill = await getExplorerTagColor(tag); + const fill = await getNavigationPanelTagColor(tag); expect(fill).toEqual(color); return tag; } -test('create a tag from explorer', async ({ page }) => { +test('create a tag from navigation panel', async ({ page }) => { await createRootTag(page, 'Test Tag'); }); -test('rename a tag from explorer', async ({ page }) => { +test('rename a tag from navigation panel', async ({ page }) => { const originalName = 'Test Tag'; const appendedName = ' Renamed'; const tag = await createRootTag(page, originalName); - const menu = await openExplorerNodeMenu(page, tag); + const menu = await openNavigationPanelNodeMenu(page, tag); await menu.getByTestId('rename-tag').tap(); const focusedTestid = await getAttrOfActiveElement(page); expect(focusedTestid).toEqual('rename-input'); @@ -72,16 +74,16 @@ test('rename a tag from explorer', async ({ page }) => { await expect(renamedTag).toBeVisible(); }); -test('change tag color from explorer', async ({ page }) => { +test('change tag color from navigation panel', async ({ page }) => { const newColor = 'var(--affine-palette-line-green)'; const tagName = 'Test Tag'; const tag = await createRootTag(page, tagName); - const menu = await openExplorerNodeMenu(page, tag); + const menu = await openNavigationPanelNodeMenu(page, tag); await menu.getByTestId('rename-tag').tap(); await changeTagColor(menu, newColor); await menu.getByTestId('rename-confirm').tap(); const updatedTag = await locateTag(page, tagName); - const fill = await getExplorerTagColor(updatedTag); + const fill = await getNavigationPanelTagColor(updatedTag); expect(fill).toEqual(newColor); }); diff --git a/tests/affine-mobile/e2e/utils.ts b/tests/affine-mobile/e2e/utils.ts index fa4f8bfe79..2575ff3c56 100644 --- a/tests/affine-mobile/e2e/utils.ts +++ b/tests/affine-mobile/e2e/utils.ts @@ -1,8 +1,8 @@ -/* eslint-disable unicorn/prefer-dom-node-dataset */ import { expect, type Locator, type Page } from '@playwright/test'; export async function expandCollapsibleSection(page: Page, name: string) { const divider = page.locator(`[data-collapsible]:has-text("${name}")`); + // oxlint-disable-next-line prefer-dom-node-dataset if ((await divider.getAttribute('data-collapsed')) === 'true') { await divider.click(); } @@ -32,10 +32,10 @@ export async function getAttrOfActiveElement( } /** - * Open the context menu of an explorer node + * Open the context menu of an navigation panel node * @returns Menu Locator */ -export async function openExplorerNodeMenu(page: Page, node: Locator) { +export async function openNavigationPanelNodeMenu(page: Page, node: Locator) { await node.getByTestId('menu-trigger').tap(); const menu = page.getByRole('dialog'); await expect(menu).toBeVisible(); @@ -49,7 +49,8 @@ export async function openTab( const tab = page.locator('#app-tabs').getByRole('tab', { name }); await expect(tab).toBeVisible(); await tab.click(); - // eslint-disable-next-line unicorn/prefer-dom-node-dataset + + // oxlint-disable-next-line prefer-dom-node-dataset const isActive = await tab.getAttribute('data-active'); expect(isActive).toBe('true'); }