refactor(core): rename explorer to navigation-panel (#11876)
This commit is contained in:
@@ -12,14 +12,6 @@ import {
|
|||||||
import { ExternalMenuLinkItem } from '@affine/core/modules/app-sidebar/views/menu-item/external-menu-link-item';
|
import { ExternalMenuLinkItem } from '@affine/core/modules/app-sidebar/views/menu-item/external-menu-link-item';
|
||||||
import { AuthService } from '@affine/core/modules/cloud';
|
import { AuthService } from '@affine/core/modules/cloud';
|
||||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
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 { CMDKQuickSearchService } from '@affine/core/modules/quicksearch/services/cmdk';
|
||||||
import type { Workspace } from '@affine/core/modules/workspace';
|
import type { Workspace } from '@affine/core/modules/workspace';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -35,6 +27,14 @@ import { useLiveData, useService, useServices } from '@toeverything/infra';
|
|||||||
import type { ReactElement } from 'react';
|
import type { ReactElement } from 'react';
|
||||||
import { memo, useCallback } 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 { WorkbenchService } from '../../modules/workbench';
|
||||||
import { WorkspaceNavigator } from '../workspace-selector';
|
import { WorkspaceNavigator } from '../workspace-selector';
|
||||||
import {
|
import {
|
||||||
@@ -180,11 +180,11 @@ export const RootAppSidebar = memo((): ReactElement => {
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
</SidebarContainer>
|
</SidebarContainer>
|
||||||
<SidebarScrollableContainer>
|
<SidebarScrollableContainer>
|
||||||
<ExplorerFavorites />
|
<NavigationPanelFavorites />
|
||||||
<ExplorerOrganize />
|
<NavigationPanelOrganize />
|
||||||
<ExplorerMigrationFavorites />
|
<NavigationPanelMigrationFavorites />
|
||||||
<ExplorerCollections />
|
<NavigationPanelCollections />
|
||||||
<ExplorerTags />
|
<NavigationPanelTags />
|
||||||
<CollapsibleSection
|
<CollapsibleSection
|
||||||
name="others"
|
name="others"
|
||||||
title={t['com.affine.rootAppSidebar.others']()}
|
title={t['com.affine.rootAppSidebar.others']()}
|
||||||
|
|||||||
@@ -0,0 +1,13 @@
|
|||||||
|
export { CollapsibleSection } from './layouts/collapsible-section';
|
||||||
|
export { NavigationPanelCollections } from './sections/collections';
|
||||||
|
export { NavigationPanelFavorites } from './sections/favorites';
|
||||||
|
export { NavigationPanelMigrationFavorites } from './sections/migration-favorites';
|
||||||
|
export { NavigationPanelOrganize } from './sections/organize';
|
||||||
|
export { NavigationPanelTags } from './sections/tags';
|
||||||
|
export { NavigationPanelTreeRoot } from './tree';
|
||||||
|
export { NavigationPanelTreeContext } from './tree/context';
|
||||||
|
export type {
|
||||||
|
BaseNavigationPanelTreeNodeProps,
|
||||||
|
NavigationPanelTreeNodeIcon,
|
||||||
|
} from './tree/node';
|
||||||
|
export type { NodeOperation } from './tree/types';
|
||||||
@@ -1,4 +1,8 @@
|
|||||||
import { CategoryDivider } from '@affine/core/modules/app-sidebar/views';
|
import { CategoryDivider } from '@affine/core/modules/app-sidebar/views';
|
||||||
|
import {
|
||||||
|
type CollapsibleSectionName,
|
||||||
|
NavigationPanelService,
|
||||||
|
} from '@affine/core/modules/navigation-panel';
|
||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
@@ -10,8 +14,6 @@ import {
|
|||||||
useCallback,
|
useCallback,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import { ExplorerService } from '../../services/explorer';
|
|
||||||
import type { CollapsibleSectionName } from '../../types';
|
|
||||||
import { content, header, root } from './collapsible-section.css';
|
import { content, header, root } from './collapsible-section.css';
|
||||||
|
|
||||||
interface CollapsibleSectionProps extends PropsWithChildren {
|
interface CollapsibleSectionProps extends PropsWithChildren {
|
||||||
@@ -46,7 +48,7 @@ export const CollapsibleSection = ({
|
|||||||
contentClassName,
|
contentClassName,
|
||||||
contentStyle,
|
contentStyle,
|
||||||
}: CollapsibleSectionProps) => {
|
}: CollapsibleSectionProps) => {
|
||||||
const section = useService(ExplorerService).sections[name];
|
const section = useService(NavigationPanelService).sections[name];
|
||||||
|
|
||||||
const collapsed = useLiveData(section.collapsed$);
|
const collapsed = useLiveData(section.collapsed$);
|
||||||
|
|
||||||
@@ -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<HTMLDivElement> {
|
||||||
|
icon:
|
||||||
|
| ((props: SVGProps<SVGSVGElement>) => JSX.Element)
|
||||||
|
| ReactElement<SVGAttributes<SVGElement>>;
|
||||||
|
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<HTMLDivElement>
|
||||||
|
) {
|
||||||
|
const icon =
|
||||||
|
typeof Icon === 'function' ? (
|
||||||
|
<Icon className={styles.icon} />
|
||||||
|
) : (
|
||||||
|
cloneElement(Icon, { className: styles.icon })
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={clsx(styles.content, className)} ref={ref} {...attrs}>
|
||||||
|
<div className={styles.iconWrapper}>{icon}</div>
|
||||||
|
<div data-testid={messageTestId} className={styles.message}>
|
||||||
|
{message}
|
||||||
|
</div>
|
||||||
|
{actionText ? (
|
||||||
|
<Button className={styles.newButton} onClick={onActionClick}>
|
||||||
|
{actionText}
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
@@ -27,14 +27,17 @@ import {
|
|||||||
} from '@toeverything/infra';
|
} from '@toeverything/infra';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ExplorerTreeNode, type ExplorerTreeNodeDropEffect } from '../../tree';
|
import {
|
||||||
import type { ExplorerTreeNodeIcon } from '../../tree/node';
|
NavigationPanelTreeNode,
|
||||||
import { ExplorerDocNode } from '../doc';
|
type NavigationPanelTreeNodeDropEffect,
|
||||||
import type { GenericExplorerNode } from '../types';
|
} from '../../tree';
|
||||||
|
import type { NavigationPanelTreeNodeIcon } from '../../tree/node';
|
||||||
|
import { NavigationPanelDocNode } from '../doc';
|
||||||
|
import type { GenericNavigationPanelNode } from '../types';
|
||||||
import { Empty } from './empty';
|
import { Empty } from './empty';
|
||||||
import { useExplorerCollectionNodeOperations } from './operations';
|
import { useNavigationPanelCollectionNodeOperations } from './operations';
|
||||||
|
|
||||||
const CollectionIcon: ExplorerTreeNodeIcon = ({
|
const CollectionIcon: NavigationPanelTreeNodeIcon = ({
|
||||||
className,
|
className,
|
||||||
draggedOver,
|
draggedOver,
|
||||||
treeInstruction,
|
treeInstruction,
|
||||||
@@ -45,7 +48,7 @@ const CollectionIcon: ExplorerTreeNodeIcon = ({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const ExplorerCollectionNode = ({
|
export const NavigationPanelCollectionNode = ({
|
||||||
collectionId,
|
collectionId,
|
||||||
onDrop,
|
onDrop,
|
||||||
location,
|
location,
|
||||||
@@ -55,7 +58,7 @@ export const ExplorerCollectionNode = ({
|
|||||||
dropEffect,
|
dropEffect,
|
||||||
}: {
|
}: {
|
||||||
collectionId: string;
|
collectionId: string;
|
||||||
} & GenericExplorerNode) => {
|
} & GenericNavigationPanelNode) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const { globalContextService, workspaceDialogService } = useServices({
|
const { globalContextService, workspaceDialogService } = useServices({
|
||||||
GlobalContextService,
|
GlobalContextService,
|
||||||
@@ -79,7 +82,7 @@ export const ExplorerCollectionNode = ({
|
|||||||
from: location,
|
from: location,
|
||||||
},
|
},
|
||||||
dropTarget: {
|
dropTarget: {
|
||||||
at: 'explorer:doc',
|
at: 'navigation-panel:doc',
|
||||||
},
|
},
|
||||||
} satisfies AffineDNDData;
|
} satisfies AffineDNDData;
|
||||||
}, [collectionId, location]);
|
}, [collectionId, location]);
|
||||||
@@ -136,7 +139,8 @@ export const ExplorerCollectionNode = ({
|
|||||||
[collection, onDrop, handleAddDocToCollection]
|
[collection, onDrop, handleAddDocToCollection]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDropEffectOnCollection = useCallback<ExplorerTreeNodeDropEffect>(
|
const handleDropEffectOnCollection =
|
||||||
|
useCallback<NavigationPanelTreeNodeDropEffect>(
|
||||||
data => {
|
data => {
|
||||||
if (collection && data.treeInstruction?.type === 'make-child') {
|
if (collection && data.treeInstruction?.type === 'make-child') {
|
||||||
if (data.source.data.entity?.type === 'doc') {
|
if (data.source.data.entity?.type === 'doc') {
|
||||||
@@ -176,7 +180,7 @@ export const ExplorerCollectionNode = ({
|
|||||||
});
|
});
|
||||||
}, [collection, workspaceDialogService]);
|
}, [collection, workspaceDialogService]);
|
||||||
|
|
||||||
const collectionOperations = useExplorerCollectionNodeOperations(
|
const collectionOperations = useNavigationPanelCollectionNodeOperations(
|
||||||
collectionId,
|
collectionId,
|
||||||
handleOpenCollapsed,
|
handleOpenCollapsed,
|
||||||
handleEditCollection
|
handleEditCollection
|
||||||
@@ -204,7 +208,7 @@ export const ExplorerCollectionNode = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerTreeNode
|
<NavigationPanelTreeNode
|
||||||
icon={CollectionIcon}
|
icon={CollectionIcon}
|
||||||
name={collection.name || t['Untitled']()}
|
name={collection.name || t['Untitled']()}
|
||||||
dndData={dndData}
|
dndData={dndData}
|
||||||
@@ -220,14 +224,14 @@ export const ExplorerCollectionNode = ({
|
|||||||
childrenPlaceholder={<Empty onDrop={handleDropOnPlaceholder} />}
|
childrenPlaceholder={<Empty onDrop={handleDropOnPlaceholder} />}
|
||||||
operations={finalOperations}
|
operations={finalOperations}
|
||||||
dropEffect={handleDropEffectOnCollection}
|
dropEffect={handleDropEffectOnCollection}
|
||||||
data-testid={`explorer-collection-${collectionId}`}
|
data-testid={`navigation-panel-collection-${collectionId}`}
|
||||||
>
|
>
|
||||||
<ExplorerCollectionNodeChildren collection={collection} />
|
<NavigationPanelCollectionNodeChildren collection={collection} />
|
||||||
</ExplorerTreeNode>
|
</NavigationPanelTreeNode>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ExplorerCollectionNodeChildren = ({
|
const NavigationPanelCollectionNodeChildren = ({
|
||||||
collection,
|
collection,
|
||||||
}: {
|
}: {
|
||||||
collection: Collection;
|
collection: Collection;
|
||||||
@@ -294,12 +298,12 @@ const ExplorerCollectionNodeChildren = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return filtered.map(doc => (
|
return filtered.map(doc => (
|
||||||
<ExplorerDocNode
|
<NavigationPanelDocNode
|
||||||
key={doc.id}
|
key={doc.id}
|
||||||
docId={doc.id}
|
docId={doc.id}
|
||||||
reorderable={false}
|
reorderable={false}
|
||||||
location={{
|
location={{
|
||||||
at: 'explorer:collection:filtered-docs',
|
at: 'navigation-panel:collection:filtered-docs',
|
||||||
collectionId: collection.id,
|
collectionId: collection.id,
|
||||||
}}
|
}}
|
||||||
operations={
|
operations={
|
||||||
@@ -25,7 +25,7 @@ import { useCallback, useMemo } from 'react';
|
|||||||
|
|
||||||
import type { NodeOperation } from '../../tree/types';
|
import type { NodeOperation } from '../../tree/types';
|
||||||
|
|
||||||
export const useExplorerCollectionNodeOperations = (
|
export const useNavigationPanelCollectionNodeOperations = (
|
||||||
collectionId: string,
|
collectionId: string,
|
||||||
onOpenCollapsed: () => void,
|
onOpenCollapsed: () => void,
|
||||||
onOpenEdit: () => void
|
onOpenEdit: () => void
|
||||||
@@ -27,13 +27,16 @@ import {
|
|||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { NEVER } from 'rxjs';
|
import { NEVER } from 'rxjs';
|
||||||
|
|
||||||
import { ExplorerTreeNode, type ExplorerTreeNodeDropEffect } from '../../tree';
|
import {
|
||||||
import type { GenericExplorerNode } from '../types';
|
NavigationPanelTreeNode,
|
||||||
|
type NavigationPanelTreeNodeDropEffect,
|
||||||
|
} from '../../tree';
|
||||||
|
import type { GenericNavigationPanelNode } from '../types';
|
||||||
import { Empty } from './empty';
|
import { Empty } from './empty';
|
||||||
import { useExplorerDocNodeOperations } from './operations';
|
import { useNavigationPanelDocNodeOperations } from './operations';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
export const ExplorerDocNode = ({
|
export const NavigationPanelDocNode = ({
|
||||||
docId,
|
docId,
|
||||||
onDrop,
|
onDrop,
|
||||||
location,
|
location,
|
||||||
@@ -46,7 +49,7 @@ export const ExplorerDocNode = ({
|
|||||||
docId: string;
|
docId: string;
|
||||||
isLinked?: boolean;
|
isLinked?: boolean;
|
||||||
forwardKey?: string;
|
forwardKey?: string;
|
||||||
} & GenericExplorerNode) => {
|
} & GenericNavigationPanelNode) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const {
|
const {
|
||||||
docsSearchService,
|
docsSearchService,
|
||||||
@@ -127,7 +130,7 @@ export const ExplorerDocNode = ({
|
|||||||
from: location,
|
from: location,
|
||||||
},
|
},
|
||||||
dropTarget: {
|
dropTarget: {
|
||||||
at: 'explorer:doc',
|
at: 'navigation-panel:doc',
|
||||||
},
|
},
|
||||||
} satisfies AffineDNDData;
|
} satisfies AffineDNDData;
|
||||||
}, [docId, location]);
|
}, [docId, location]);
|
||||||
@@ -166,7 +169,7 @@ export const ExplorerDocNode = ({
|
|||||||
[docId, docsService, guardService, onDrop, t]
|
[docId, docsService, guardService, onDrop, t]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDropEffectOnDoc = useCallback<ExplorerTreeNodeDropEffect>(
|
const handleDropEffectOnDoc = useCallback<NavigationPanelTreeNodeDropEffect>(
|
||||||
data => {
|
data => {
|
||||||
if (data.treeInstruction?.type === 'make-child') {
|
if (data.treeInstruction?.type === 'make-child') {
|
||||||
if (data.source.data.entity?.type === 'doc') {
|
if (data.source.data.entity?.type === 'doc') {
|
||||||
@@ -214,7 +217,7 @@ export const ExplorerDocNode = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const workspaceDialogService = useService(WorkspaceDialogService);
|
const workspaceDialogService = useService(WorkspaceDialogService);
|
||||||
const operations = useExplorerDocNodeOperations(
|
const operations = useNavigationPanelDocNodeOperations(
|
||||||
docId,
|
docId,
|
||||||
useMemo(
|
useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -237,7 +240,7 @@ export const ExplorerDocNode = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerTreeNode
|
<NavigationPanelTreeNode
|
||||||
icon={Icon}
|
icon={Icon}
|
||||||
name={t.t(docTitle)}
|
name={t.t(docTitle)}
|
||||||
dndData={dndData}
|
dndData={dndData}
|
||||||
@@ -277,18 +280,18 @@ export const ExplorerDocNode = ({
|
|||||||
}
|
}
|
||||||
operations={finalOperations}
|
operations={finalOperations}
|
||||||
dropEffect={handleDropEffectOnDoc}
|
dropEffect={handleDropEffectOnDoc}
|
||||||
data-testid={`explorer-doc-${docId}`}
|
data-testid={`navigation-panel-doc-${docId}`}
|
||||||
>
|
>
|
||||||
<Guard docId={docId} permission="Doc_Read">
|
<Guard docId={docId} permission="Doc_Read">
|
||||||
{canRead =>
|
{canRead =>
|
||||||
canRead
|
canRead
|
||||||
? children?.map((child, index) => (
|
? children?.map((child, index) => (
|
||||||
<ExplorerDocNode
|
<NavigationPanelDocNode
|
||||||
key={`${child.docId}-${index}`}
|
key={`${child.docId}-${index}`}
|
||||||
docId={child.docId}
|
docId={child.docId}
|
||||||
reorderable={false}
|
reorderable={false}
|
||||||
location={{
|
location={{
|
||||||
at: 'explorer:doc:linked-docs',
|
at: 'navigation-panel:doc:linked-docs',
|
||||||
docId,
|
docId,
|
||||||
}}
|
}}
|
||||||
isLinked
|
isLinked
|
||||||
@@ -297,6 +300,6 @@ export const ExplorerDocNode = ({
|
|||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
</Guard>
|
</Guard>
|
||||||
</ExplorerTreeNode>
|
</NavigationPanelTreeNode>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -31,7 +31,7 @@ import { useCallback, useMemo, useState } from 'react';
|
|||||||
|
|
||||||
import type { NodeOperation } from '../../tree/types';
|
import type { NodeOperation } from '../../tree/types';
|
||||||
|
|
||||||
export const useExplorerDocNodeOperations = (
|
export const useNavigationPanelDocNodeOperations = (
|
||||||
docId: string,
|
docId: string,
|
||||||
options: {
|
options: {
|
||||||
openInfoModal: () => void;
|
openInfoModal: () => void;
|
||||||
@@ -35,17 +35,20 @@ import { useLiveData, useServices } from '@toeverything/infra';
|
|||||||
import { difference } from 'lodash-es';
|
import { difference } from 'lodash-es';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ExplorerTreeNode, type ExplorerTreeNodeDropEffect } from '../../tree';
|
import {
|
||||||
import type { ExplorerTreeNodeIcon } from '../../tree/node';
|
NavigationPanelTreeNode,
|
||||||
|
type NavigationPanelTreeNodeDropEffect,
|
||||||
|
} from '../../tree';
|
||||||
|
import type { NavigationPanelTreeNodeIcon } from '../../tree/node';
|
||||||
import type { NodeOperation } from '../../tree/types';
|
import type { NodeOperation } from '../../tree/types';
|
||||||
import { ExplorerCollectionNode } from '../collection';
|
import { NavigationPanelCollectionNode } from '../collection';
|
||||||
import { ExplorerDocNode } from '../doc';
|
import { NavigationPanelDocNode } from '../doc';
|
||||||
import { ExplorerTagNode } from '../tag';
|
import { NavigationPanelTagNode } from '../tag';
|
||||||
import type { GenericExplorerNode } from '../types';
|
import type { GenericNavigationPanelNode } from '../types';
|
||||||
import { FolderEmpty } from './empty';
|
import { FolderEmpty } from './empty';
|
||||||
import { FavoriteFolderOperation } from './operations';
|
import { FavoriteFolderOperation } from './operations';
|
||||||
|
|
||||||
export const ExplorerFolderNode = ({
|
export const NavigationPanelFolderNode = ({
|
||||||
nodeId,
|
nodeId,
|
||||||
onDrop,
|
onDrop,
|
||||||
defaultRenaming,
|
defaultRenaming,
|
||||||
@@ -61,7 +64,7 @@ export const ExplorerFolderNode = ({
|
|||||||
operations?:
|
operations?:
|
||||||
| NodeOperation[]
|
| NodeOperation[]
|
||||||
| ((type: string, node: FolderNode) => NodeOperation[]);
|
| ((type: string, node: FolderNode) => NodeOperation[]);
|
||||||
} & Omit<GenericExplorerNode, 'operations'>) => {
|
} & Omit<GenericNavigationPanelNode, 'operations'>) => {
|
||||||
const { organizeService } = useServices({
|
const { organizeService } = useServices({
|
||||||
OrganizeService,
|
OrganizeService,
|
||||||
});
|
});
|
||||||
@@ -93,7 +96,7 @@ export const ExplorerFolderNode = ({
|
|||||||
|
|
||||||
if (type === 'folder') {
|
if (type === 'folder') {
|
||||||
return (
|
return (
|
||||||
<ExplorerFolderNodeFolder
|
<NavigationPanelFolderNodeFolder
|
||||||
node={node}
|
node={node}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
defaultRenaming={defaultRenaming}
|
defaultRenaming={defaultRenaming}
|
||||||
@@ -106,7 +109,7 @@ export const ExplorerFolderNode = ({
|
|||||||
} else if (type === 'doc') {
|
} else if (type === 'doc') {
|
||||||
return (
|
return (
|
||||||
data && (
|
data && (
|
||||||
<ExplorerDocNode
|
<NavigationPanelDocNode
|
||||||
docId={data}
|
docId={data}
|
||||||
location={location}
|
location={location}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
@@ -120,7 +123,7 @@ export const ExplorerFolderNode = ({
|
|||||||
} else if (type === 'collection') {
|
} else if (type === 'collection') {
|
||||||
return (
|
return (
|
||||||
data && (
|
data && (
|
||||||
<ExplorerCollectionNode
|
<NavigationPanelCollectionNode
|
||||||
collectionId={data}
|
collectionId={data}
|
||||||
location={location}
|
location={location}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
@@ -134,7 +137,7 @@ export const ExplorerFolderNode = ({
|
|||||||
} else if (type === 'tag') {
|
} else if (type === 'tag') {
|
||||||
return (
|
return (
|
||||||
data && (
|
data && (
|
||||||
<ExplorerTagNode
|
<NavigationPanelTagNode
|
||||||
tagId={data}
|
tagId={data}
|
||||||
location={location}
|
location={location}
|
||||||
onDrop={handleDrop}
|
onDrop={handleDrop}
|
||||||
@@ -150,8 +153,8 @@ export const ExplorerFolderNode = ({
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define outside the `ExplorerFolderNodeFolder` to avoid re-render(the close animation won't play)
|
// Define outside the `NavigationPanelFolderNodeFolder` to avoid re-render(the close animation won't play)
|
||||||
const ExplorerFolderIcon: ExplorerTreeNodeIcon = ({
|
const NavigationPanelFolderIcon: NavigationPanelTreeNodeIcon = ({
|
||||||
collapsed,
|
collapsed,
|
||||||
className,
|
className,
|
||||||
draggedOver,
|
draggedOver,
|
||||||
@@ -165,7 +168,7 @@ const ExplorerFolderIcon: ExplorerTreeNodeIcon = ({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const ExplorerFolderNodeFolder = ({
|
const NavigationPanelFolderNodeFolder = ({
|
||||||
node,
|
node,
|
||||||
onDrop,
|
onDrop,
|
||||||
defaultRenaming,
|
defaultRenaming,
|
||||||
@@ -177,7 +180,7 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
}: {
|
}: {
|
||||||
defaultRenaming?: boolean;
|
defaultRenaming?: boolean;
|
||||||
node: FolderNode;
|
node: FolderNode;
|
||||||
} & GenericExplorerNode) => {
|
} & GenericNavigationPanelNode) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const { workspaceService, featureFlagService, workspaceDialogService } =
|
const { workspaceService, featureFlagService, workspaceDialogService } =
|
||||||
useServices({
|
useServices({
|
||||||
@@ -224,7 +227,7 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
from: location,
|
from: location,
|
||||||
},
|
},
|
||||||
dropTarget: {
|
dropTarget: {
|
||||||
at: 'explorer:organize:folder',
|
at: 'navigation-panel:organize:folder',
|
||||||
},
|
},
|
||||||
} satisfies AffineDNDData;
|
} satisfies AffineDNDData;
|
||||||
}, [location, node.id]);
|
}, [location, node.id]);
|
||||||
@@ -258,7 +261,10 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
data.source.data.entity?.type === 'doc' ||
|
data.source.data.entity?.type === 'doc' ||
|
||||||
data.source.data.entity?.type === 'tag'
|
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'));
|
node.moveHere(data.source.data.from.nodeId, node.indexAt('before'));
|
||||||
track.$.navigationPanel.organize.moveOrganizeItem({
|
track.$.navigationPanel.organize.moveOrganizeItem({
|
||||||
type: 'link',
|
type: 'link',
|
||||||
@@ -283,7 +289,7 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
[node, onDrop]
|
[node, onDrop]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDropEffect = useCallback<ExplorerTreeNodeDropEffect>(
|
const handleDropEffect = useCallback<NavigationPanelTreeNodeDropEffect>(
|
||||||
data => {
|
data => {
|
||||||
if (data.treeInstruction?.type === 'make-child') {
|
if (data.treeInstruction?.type === 'make-child') {
|
||||||
if (data.source.data.entity?.type === 'folder') {
|
if (data.source.data.entity?.type === 'folder') {
|
||||||
@@ -295,7 +301,7 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
}
|
}
|
||||||
return 'move';
|
return 'move';
|
||||||
} else if (
|
} else if (
|
||||||
data.source.data.from?.at === 'explorer:organize:folder-node'
|
data.source.data.from?.at === 'navigation-panel:organize:folder-node'
|
||||||
) {
|
) {
|
||||||
return 'move';
|
return 'move';
|
||||||
} else if (
|
} else if (
|
||||||
@@ -334,7 +340,9 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
data.source.data.entity?.type === 'doc' ||
|
data.source.data.entity?.type === 'doc' ||
|
||||||
data.source.data.entity?.type === 'tag'
|
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'));
|
node.moveHere(data.source.data.from.nodeId, node.indexAt('before'));
|
||||||
track.$.navigationPanel.organize.moveOrganizeItem({
|
track.$.navigationPanel.organize.moveOrganizeItem({
|
||||||
type: data.source.data.entity?.type,
|
type: data.source.data.entity?.type,
|
||||||
@@ -388,7 +396,10 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
data.source.data.entity?.type === 'doc' ||
|
data.source.data.entity?.type === 'doc' ||
|
||||||
data.source.data.entity?.type === 'tag'
|
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(
|
node.moveHere(
|
||||||
data.source.data.from.nodeId,
|
data.source.data.from.nodeId,
|
||||||
node.indexAt(at, dropAtNode.id)
|
node.indexAt(at, dropAtNode.id)
|
||||||
@@ -437,7 +448,8 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
[node, onDrop]
|
[node, onDrop]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDropEffectOnChildren = useCallback<ExplorerTreeNodeDropEffect>(
|
const handleDropEffectOnChildren =
|
||||||
|
useCallback<NavigationPanelTreeNodeDropEffect>(
|
||||||
data => {
|
data => {
|
||||||
if (
|
if (
|
||||||
data.treeInstruction?.type === 'reorder-above' ||
|
data.treeInstruction?.type === 'reorder-above' ||
|
||||||
@@ -452,7 +464,8 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
}
|
}
|
||||||
return 'move';
|
return 'move';
|
||||||
} else if (
|
} else if (
|
||||||
data.source.data.from?.at === 'explorer:organize:folder-node'
|
data.source.data.from?.at ===
|
||||||
|
'navigation-panel:organize:folder-node'
|
||||||
) {
|
) {
|
||||||
return 'move';
|
return 'move';
|
||||||
} else if (
|
} else if (
|
||||||
@@ -508,7 +521,7 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (
|
} else if (
|
||||||
args.source.data.from?.at === 'explorer:organize:folder-node'
|
args.source.data.from?.at === 'navigation-panel:organize:folder-node'
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
} else if (
|
} else if (
|
||||||
@@ -538,7 +551,7 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else if (
|
} else if (
|
||||||
args.source.data.from?.at === 'explorer:organize:folder-node'
|
args.source.data.from?.at === 'navigation-panel:organize:folder-node'
|
||||||
) {
|
) {
|
||||||
return true;
|
return true;
|
||||||
} else if (
|
} else if (
|
||||||
@@ -758,8 +771,8 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerTreeNode
|
<NavigationPanelTreeNode
|
||||||
icon={ExplorerFolderIcon}
|
icon={NavigationPanelFolderIcon}
|
||||||
name={name}
|
name={name}
|
||||||
dndData={dndData}
|
dndData={dndData}
|
||||||
onDrop={handleDropOnFolder}
|
onDrop={handleDropOnFolder}
|
||||||
@@ -776,10 +789,10 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
<FolderEmpty canDrop={handleCanDrop} onDrop={handleDropOnPlaceholder} />
|
<FolderEmpty canDrop={handleCanDrop} onDrop={handleDropOnPlaceholder} />
|
||||||
}
|
}
|
||||||
dropEffect={handleDropEffect}
|
dropEffect={handleDropEffect}
|
||||||
data-testid={`explorer-folder-${node.id}`}
|
data-testid={`navigation-panel-folder-${node.id}`}
|
||||||
>
|
>
|
||||||
{children.map(child => (
|
{children.map(child => (
|
||||||
<ExplorerFolderNode
|
<NavigationPanelFolderNode
|
||||||
key={child.id}
|
key={child.id}
|
||||||
nodeId={child.id as string}
|
nodeId={child.id as string}
|
||||||
defaultRenaming={child.id === newFolderId}
|
defaultRenaming={child.id === newFolderId}
|
||||||
@@ -788,11 +801,11 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
dropEffect={handleDropEffectOnChildren}
|
dropEffect={handleDropEffectOnChildren}
|
||||||
canDrop={handleChildrenCanDrop}
|
canDrop={handleChildrenCanDrop}
|
||||||
location={{
|
location={{
|
||||||
at: 'explorer:organize:folder-node',
|
at: 'navigation-panel:organize:folder-node',
|
||||||
nodeId: child.id as string,
|
nodeId: child.id as string,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ExplorerTreeNode>
|
</NavigationPanelTreeNode>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -13,14 +13,17 @@ import { useLiveData, useServices } from '@toeverything/infra';
|
|||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ExplorerTreeNode, type ExplorerTreeNodeDropEffect } from '../../tree';
|
import {
|
||||||
import { ExplorerDocNode } from '../doc';
|
NavigationPanelTreeNode,
|
||||||
import type { GenericExplorerNode } from '../types';
|
type NavigationPanelTreeNodeDropEffect,
|
||||||
|
} from '../../tree';
|
||||||
|
import { NavigationPanelDocNode } from '../doc';
|
||||||
|
import type { GenericNavigationPanelNode } from '../types';
|
||||||
import { Empty } from './empty';
|
import { Empty } from './empty';
|
||||||
import { useExplorerTagNodeOperations } from './operations';
|
import { useNavigationPanelTagNodeOperations } from './operations';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
export const ExplorerTagNode = ({
|
export const NavigationPanelTagNode = ({
|
||||||
tagId,
|
tagId,
|
||||||
onDrop,
|
onDrop,
|
||||||
location,
|
location,
|
||||||
@@ -30,7 +33,7 @@ export const ExplorerTagNode = ({
|
|||||||
canDrop,
|
canDrop,
|
||||||
}: {
|
}: {
|
||||||
tagId: string;
|
tagId: string;
|
||||||
} & GenericExplorerNode) => {
|
} & GenericNavigationPanelNode) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const { tagService, globalContextService } = useServices({
|
const { tagService, globalContextService } = useServices({
|
||||||
TagService,
|
TagService,
|
||||||
@@ -70,7 +73,7 @@ export const ExplorerTagNode = ({
|
|||||||
from: location,
|
from: location,
|
||||||
},
|
},
|
||||||
dropTarget: {
|
dropTarget: {
|
||||||
at: 'explorer:tag',
|
at: 'navigation-panel:tag',
|
||||||
},
|
},
|
||||||
} satisfies AffineDNDData;
|
} satisfies AffineDNDData;
|
||||||
}, [location, tagId]);
|
}, [location, tagId]);
|
||||||
@@ -108,7 +111,7 @@ export const ExplorerTagNode = ({
|
|||||||
[onDrop, t, tagRecord]
|
[onDrop, t, tagRecord]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDropEffectOnTag = useCallback<ExplorerTreeNodeDropEffect>(
|
const handleDropEffectOnTag = useCallback<NavigationPanelTreeNodeDropEffect>(
|
||||||
data => {
|
data => {
|
||||||
if (data.treeInstruction?.type === 'make-child') {
|
if (data.treeInstruction?.type === 'make-child') {
|
||||||
if (data.source.data.entity?.type === 'doc') {
|
if (data.source.data.entity?.type === 'doc') {
|
||||||
@@ -145,7 +148,7 @@ export const ExplorerTagNode = ({
|
|||||||
[canDrop]
|
[canDrop]
|
||||||
);
|
);
|
||||||
|
|
||||||
const operations = useExplorerTagNodeOperations(
|
const operations = useNavigationPanelTagNodeOperations(
|
||||||
tagId,
|
tagId,
|
||||||
useMemo(
|
useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
@@ -167,7 +170,7 @@ export const ExplorerTagNode = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerTreeNode
|
<NavigationPanelTreeNode
|
||||||
icon={Icon}
|
icon={Icon}
|
||||||
name={tagName || t['Untitled']()}
|
name={tagName || t['Untitled']()}
|
||||||
dndData={dndData}
|
dndData={dndData}
|
||||||
@@ -183,10 +186,10 @@ export const ExplorerTagNode = ({
|
|||||||
childrenPlaceholder={<Empty onDrop={handleDropOnPlaceholder} />}
|
childrenPlaceholder={<Empty onDrop={handleDropOnPlaceholder} />}
|
||||||
operations={finalOperations}
|
operations={finalOperations}
|
||||||
dropEffect={handleDropEffectOnTag}
|
dropEffect={handleDropEffectOnTag}
|
||||||
data-testid={`explorer-tag-${tagId}`}
|
data-testid={`navigation-panel-tag-${tagId}`}
|
||||||
>
|
>
|
||||||
<ExplorerTagNodeDocs tag={tagRecord} />
|
<NavigationPanelTagNodeDocs tag={tagRecord} />
|
||||||
</ExplorerTreeNode>
|
</NavigationPanelTreeNode>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -195,16 +198,16 @@ export const ExplorerTagNode = ({
|
|||||||
* so we split the tag node children into a separate component,
|
* so we split the tag node children into a separate component,
|
||||||
* so it won't be rendered when the tag node is collapsed.
|
* 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$);
|
const tagDocIds = useLiveData(tag.pageIds$);
|
||||||
|
|
||||||
return tagDocIds.map(docId => (
|
return tagDocIds.map(docId => (
|
||||||
<ExplorerDocNode
|
<NavigationPanelDocNode
|
||||||
key={docId}
|
key={docId}
|
||||||
docId={docId}
|
docId={docId}
|
||||||
reorderable={false}
|
reorderable={false}
|
||||||
location={{
|
location={{
|
||||||
at: 'explorer:tags:docs',
|
at: 'navigation-panel:tags:docs',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
@@ -19,7 +19,7 @@ import { useCallback, useMemo } from 'react';
|
|||||||
|
|
||||||
import type { NodeOperation } from '../../tree/types';
|
import type { NodeOperation } from '../../tree/types';
|
||||||
|
|
||||||
export const useExplorerTagNodeOperations = (
|
export const useNavigationPanelTagNodeOperations = (
|
||||||
tagId: string,
|
tagId: string,
|
||||||
{
|
{
|
||||||
openNodeCollapsed,
|
openNodeCollapsed,
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import type { DropTargetDropEvent, DropTargetOptions } from '@affine/component';
|
import type { DropTargetDropEvent, DropTargetOptions } from '@affine/component';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
|
|
||||||
import type { ExplorerTreeNodeDropEffect } from '../tree';
|
import type { NavigationPanelTreeNodeDropEffect } from '../tree';
|
||||||
import type { NodeOperation } from '../tree/types';
|
import type { NodeOperation } from '../tree/types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The interface for a generic explorer node.
|
* The interface for a generic navigation panel node.
|
||||||
*
|
*
|
||||||
* # Drop controlled area
|
* # 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.
|
* 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
|
* 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.
|
* The drop effect to be used when an element is dropped over the node.
|
||||||
*/
|
*/
|
||||||
dropEffect?: ExplorerTreeNodeDropEffect;
|
dropEffect?: NavigationPanelTreeNodeDropEffect;
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { ViewLayersIcon } from '@blocksuite/icons/rc';
|
import { ViewLayersIcon } from '@blocksuite/icons/rc';
|
||||||
|
|
||||||
import { ExplorerEmptySection } from '../../layouts/empty-section';
|
import { NavigationPanelEmptySection } from '../../layouts/empty-section';
|
||||||
|
|
||||||
export const RootEmpty = ({
|
export const RootEmpty = ({
|
||||||
onClickCreate,
|
onClickCreate,
|
||||||
@@ -11,7 +11,7 @@ export const RootEmpty = ({
|
|||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerEmptySection
|
<NavigationPanelEmptySection
|
||||||
icon={ViewLayersIcon}
|
icon={ViewLayersIcon}
|
||||||
message={t['com.affine.collections.empty.message']()}
|
message={t['com.affine.collections.empty.message']()}
|
||||||
messageTestId="slider-bar-collection-empty-message"
|
messageTestId="slider-bar-collection-empty-message"
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { IconButton, usePromptModal } from '@affine/component';
|
import { IconButton, usePromptModal } from '@affine/component';
|
||||||
import { createEmptyCollection } from '@affine/core/components/page-list/use-collection-manager';
|
import { createEmptyCollection } from '@affine/core/components/page-list/use-collection-manager';
|
||||||
import { CollectionService } from '@affine/core/modules/collection';
|
import { CollectionService } from '@affine/core/modules/collection';
|
||||||
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 { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
@@ -10,20 +10,21 @@ import { useLiveData, useServices } from '@toeverything/infra';
|
|||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { ExplorerService } from '../../../services/explorer';
|
|
||||||
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
||||||
import { ExplorerCollectionNode } from '../../nodes/collection';
|
import { NavigationPanelCollectionNode } from '../../nodes/collection';
|
||||||
|
import { NavigationPanelTreeRoot } from '../../tree';
|
||||||
import { RootEmpty } from './empty';
|
import { RootEmpty } from './empty';
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
|
|
||||||
export const ExplorerCollections = () => {
|
export const NavigationPanelCollections = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const { collectionService, workbenchService, explorerService } = useServices({
|
const { collectionService, workbenchService, navigationPanelService } =
|
||||||
|
useServices({
|
||||||
CollectionService,
|
CollectionService,
|
||||||
WorkbenchService,
|
WorkbenchService,
|
||||||
ExplorerService,
|
NavigationPanelService,
|
||||||
});
|
});
|
||||||
const explorerSection = explorerService.sections.collections;
|
const navigationPanelSection = navigationPanelService.sections.collections;
|
||||||
const collections = useLiveData(collectionService.collections$);
|
const collections = useLiveData(collectionService.collections$);
|
||||||
const { openPromptModal } = usePromptModal();
|
const { openPromptModal } = usePromptModal();
|
||||||
|
|
||||||
@@ -51,12 +52,12 @@ export const ExplorerCollections = () => {
|
|||||||
type: 'collection',
|
type: 'collection',
|
||||||
});
|
});
|
||||||
workbenchService.workbench.openCollection(id);
|
workbenchService.workbench.openCollection(id);
|
||||||
explorerSection.setCollapsed(false);
|
navigationPanelSection.setCollapsed(false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
collectionService,
|
collectionService,
|
||||||
explorerSection,
|
navigationPanelSection,
|
||||||
openPromptModal,
|
openPromptModal,
|
||||||
t,
|
t,
|
||||||
workbenchService.workbench,
|
workbenchService.workbench,
|
||||||
@@ -65,11 +66,11 @@ export const ExplorerCollections = () => {
|
|||||||
return (
|
return (
|
||||||
<CollapsibleSection
|
<CollapsibleSection
|
||||||
name="collections"
|
name="collections"
|
||||||
testId="explorer-collections"
|
testId="navigation-panel-collections"
|
||||||
title={t['com.affine.rootAppSidebar.collections']()}
|
title={t['com.affine.rootAppSidebar.collections']()}
|
||||||
actions={
|
actions={
|
||||||
<IconButton
|
<IconButton
|
||||||
data-testid="explorer-bar-add-collection-button"
|
data-testid="navigation-panel-bar-add-collection-button"
|
||||||
onClick={handleCreateCollection}
|
onClick={handleCreateCollection}
|
||||||
size="16"
|
size="16"
|
||||||
tooltip={t[
|
tooltip={t[
|
||||||
@@ -80,20 +81,20 @@ export const ExplorerCollections = () => {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ExplorerTreeRoot
|
<NavigationPanelTreeRoot
|
||||||
placeholder={<RootEmpty onClickCreate={handleCreateCollection} />}
|
placeholder={<RootEmpty onClickCreate={handleCreateCollection} />}
|
||||||
>
|
>
|
||||||
{collections.map(collection => (
|
{collections.map(collection => (
|
||||||
<ExplorerCollectionNode
|
<NavigationPanelCollectionNode
|
||||||
key={collection.id}
|
key={collection.id}
|
||||||
collectionId={collection.id}
|
collectionId={collection.id}
|
||||||
reorderable={false}
|
reorderable={false}
|
||||||
location={{
|
location={{
|
||||||
at: 'explorer:collection:list',
|
at: 'navigation-panel:collection:list',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ExplorerTreeRoot>
|
</NavigationPanelTreeRoot>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -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<AffineDNDData>['canDrop'] =
|
||||||
|
data => {
|
||||||
|
return data.source.data.entity?.type
|
||||||
|
? isFavoriteSupportType(data.source.data.entity.type)
|
||||||
|
: false;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const favoriteChildrenCanDrop: DropTargetOptions<AffineDNDData>['canDrop'] =
|
||||||
|
// Same as favoriteRootCanDrop
|
||||||
|
data => favoriteRootCanDrop(data);
|
||||||
@@ -7,7 +7,7 @@ import type { AffineDNDData } from '@affine/core/types/dnd';
|
|||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { FavoriteIcon } from '@blocksuite/icons/rc';
|
import { FavoriteIcon } from '@blocksuite/icons/rc';
|
||||||
|
|
||||||
import { ExplorerEmptySection } from '../../layouts/empty-section';
|
import { NavigationPanelEmptySection } from '../../layouts/empty-section';
|
||||||
import { DropEffect } from '../../tree';
|
import { DropEffect } from '../../tree';
|
||||||
import { favoriteRootCanDrop, favoriteRootDropEffect } from './dnd';
|
import { favoriteRootCanDrop, favoriteRootDropEffect } from './dnd';
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ const RootEmptyReady = ({ onDrop }: Omit<RootEmptyProps, 'isLoading'>) => {
|
|||||||
useDropTarget<AffineDNDData>(
|
useDropTarget<AffineDNDData>(
|
||||||
() => ({
|
() => ({
|
||||||
data: {
|
data: {
|
||||||
at: 'explorer:favorite:root',
|
at: 'navigation-panel:favorite:root',
|
||||||
},
|
},
|
||||||
onDrop: onDrop,
|
onDrop: onDrop,
|
||||||
canDrop: favoriteRootCanDrop,
|
canDrop: favoriteRootCanDrop,
|
||||||
@@ -36,7 +36,7 @@ const RootEmptyReady = ({ onDrop }: Omit<RootEmptyProps, 'isLoading'>) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerEmptySection
|
<NavigationPanelEmptySection
|
||||||
ref={dropTargetRef}
|
ref={dropTargetRef}
|
||||||
icon={FavoriteIcon}
|
icon={FavoriteIcon}
|
||||||
message={t['com.affine.rootAppSidebar.favorites.empty']()}
|
message={t['com.affine.rootAppSidebar.favorites.empty']()}
|
||||||
@@ -51,7 +51,7 @@ const RootEmptyReady = ({ onDrop }: Omit<RootEmptyProps, 'isLoading'>) => {
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ExplorerEmptySection>
|
</NavigationPanelEmptySection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -4,15 +4,12 @@ import {
|
|||||||
useDropTarget,
|
useDropTarget,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
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 type { FavoriteSupportTypeUnion } from '@affine/core/modules/favorite';
|
||||||
import {
|
import {
|
||||||
FavoriteService,
|
FavoriteService,
|
||||||
isFavoriteSupportType,
|
isFavoriteSupportType,
|
||||||
} from '@affine/core/modules/favorite';
|
} from '@affine/core/modules/favorite';
|
||||||
|
import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
import { inferOpenMode } from '@affine/core/utils';
|
import { inferOpenMode } from '@affine/core/utils';
|
||||||
@@ -22,12 +19,12 @@ import { PlusIcon } from '@blocksuite/icons/rc';
|
|||||||
import { useLiveData, useServices } from '@toeverything/infra';
|
import { useLiveData, useServices } from '@toeverything/infra';
|
||||||
import { type MouseEventHandler, useCallback } from 'react';
|
import { type MouseEventHandler, useCallback } from 'react';
|
||||||
|
|
||||||
import { ExplorerService } from '../../../services/explorer';
|
|
||||||
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
||||||
import { ExplorerCollectionNode } from '../../nodes/collection';
|
import { NavigationPanelCollectionNode } from '../../nodes/collection';
|
||||||
import { ExplorerDocNode } from '../../nodes/doc';
|
import { NavigationPanelDocNode } from '../../nodes/doc';
|
||||||
import { ExplorerFolderNode } from '../../nodes/folder';
|
import { NavigationPanelFolderNode } from '../../nodes/folder';
|
||||||
import { ExplorerTagNode } from '../../nodes/tag';
|
import { NavigationPanelTagNode } from '../../nodes/tag';
|
||||||
|
import { DropEffect, NavigationPanelTreeRoot } from '../../tree';
|
||||||
import {
|
import {
|
||||||
favoriteChildrenCanDrop,
|
favoriteChildrenCanDrop,
|
||||||
favoriteChildrenDropEffect,
|
favoriteChildrenDropEffect,
|
||||||
@@ -36,14 +33,15 @@ import {
|
|||||||
} from './dnd';
|
} from './dnd';
|
||||||
import { RootEmpty } from './empty';
|
import { RootEmpty } from './empty';
|
||||||
|
|
||||||
export const ExplorerFavorites = () => {
|
export const NavigationPanelFavorites = () => {
|
||||||
const { favoriteService, workspaceService, explorerService } = useServices({
|
const { favoriteService, workspaceService, navigationPanelService } =
|
||||||
|
useServices({
|
||||||
FavoriteService,
|
FavoriteService,
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
ExplorerService,
|
NavigationPanelService,
|
||||||
});
|
});
|
||||||
|
|
||||||
const explorerSection = explorerService.sections.favorites;
|
const navigationPanelSection = navigationPanelService.sections.favorites;
|
||||||
|
|
||||||
const favorites = useLiveData(favoriteService.favoriteList.sortedList$);
|
const favorites = useLiveData(favoriteService.favoriteList.sortedList$);
|
||||||
|
|
||||||
@@ -73,10 +71,10 @@ export const ExplorerFavorites = () => {
|
|||||||
track.$.navigationPanel.favorites.drop({
|
track.$.navigationPanel.favorites.drop({
|
||||||
type: data.source.data.entity.type,
|
type: data.source.data.entity.type,
|
||||||
});
|
});
|
||||||
explorerSection.setCollapsed(false);
|
navigationPanelSection.setCollapsed(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[explorerSection, favoriteService.favoriteList]
|
[navigationPanelSection, favoriteService.favoriteList]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCreateNewFavoriteDoc: MouseEventHandler = useCallback(
|
const handleCreateNewFavoriteDoc: MouseEventHandler = useCallback(
|
||||||
@@ -87,9 +85,9 @@ export const ExplorerFavorites = () => {
|
|||||||
newDoc.id,
|
newDoc.id,
|
||||||
favoriteService.favoriteList.indexAt('before')
|
favoriteService.favoriteList.indexAt('before')
|
||||||
);
|
);
|
||||||
explorerSection.setCollapsed(false);
|
navigationPanelSection.setCollapsed(false);
|
||||||
},
|
},
|
||||||
[createPage, explorerSection, favoriteService.favoriteList]
|
[createPage, navigationPanelSection, favoriteService.favoriteList]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleOnChildrenDrop = useCallback(
|
const handleOnChildrenDrop = useCallback(
|
||||||
@@ -102,7 +100,7 @@ export const ExplorerFavorites = () => {
|
|||||||
data.treeInstruction?.type === 'reorder-below'
|
data.treeInstruction?.type === 'reorder-below'
|
||||||
) {
|
) {
|
||||||
if (
|
if (
|
||||||
data.source.data.from?.at === 'explorer:favorite:list' &&
|
data.source.data.from?.at === 'navigation-panel:favorite:list' &&
|
||||||
data.source.data.entity?.type &&
|
data.source.data.entity?.type &&
|
||||||
isFavoriteSupportType(data.source.data.entity.type)
|
isFavoriteSupportType(data.source.data.entity.type)
|
||||||
) {
|
) {
|
||||||
@@ -153,7 +151,7 @@ export const ExplorerFavorites = () => {
|
|||||||
useDropTarget<AffineDNDData>(
|
useDropTarget<AffineDNDData>(
|
||||||
() => ({
|
() => ({
|
||||||
data: {
|
data: {
|
||||||
at: 'explorer:favorite:root',
|
at: 'navigation-panel:favorite:root',
|
||||||
},
|
},
|
||||||
onDrop: handleDrop,
|
onDrop: handleDrop,
|
||||||
canDrop: favoriteRootCanDrop,
|
canDrop: favoriteRootCanDrop,
|
||||||
@@ -167,12 +165,12 @@ export const ExplorerFavorites = () => {
|
|||||||
name="favorites"
|
name="favorites"
|
||||||
title={t['com.affine.rootAppSidebar.favorites']()}
|
title={t['com.affine.rootAppSidebar.favorites']()}
|
||||||
headerRef={dropTargetRef}
|
headerRef={dropTargetRef}
|
||||||
testId="explorer-favorites"
|
testId="navigation-panel-favorites"
|
||||||
headerTestId="explorer-favorite-category-divider"
|
headerTestId="navigation-panel-favorite-category-divider"
|
||||||
actions={
|
actions={
|
||||||
<>
|
<>
|
||||||
<IconButton
|
<IconButton
|
||||||
data-testid="explorer-bar-add-favorite-button"
|
data-testid="navigation-panel-bar-add-favorite-button"
|
||||||
data-event-props="$.navigationPanel.favorites.createDoc"
|
data-event-props="$.navigationPanel.favorites.createDoc"
|
||||||
data-event-args-control="addFavorite"
|
data-event-args-control="addFavorite"
|
||||||
onClick={handleCreateNewFavoriteDoc}
|
onClick={handleCreateNewFavoriteDoc}
|
||||||
@@ -196,25 +194,25 @@ export const ExplorerFavorites = () => {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ExplorerTreeRoot
|
<NavigationPanelTreeRoot
|
||||||
placeholder={<RootEmpty onDrop={handleDrop} isLoading={isLoading} />}
|
placeholder={<RootEmpty onDrop={handleDrop} isLoading={isLoading} />}
|
||||||
>
|
>
|
||||||
{favorites.map(favorite => (
|
{favorites.map(favorite => (
|
||||||
<ExplorerFavoriteNode
|
<NavigationPanelFavoriteNode
|
||||||
key={favorite.id}
|
key={favorite.id}
|
||||||
favorite={favorite}
|
favorite={favorite}
|
||||||
onDrop={handleOnChildrenDrop}
|
onDrop={handleOnChildrenDrop}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ExplorerTreeRoot>
|
</NavigationPanelTreeRoot>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const childLocation = {
|
const childLocation = {
|
||||||
at: 'explorer:favorite:list' as const,
|
at: 'navigation-panel:favorite:list' as const,
|
||||||
};
|
};
|
||||||
const ExplorerFavoriteNode = ({
|
const NavigationPanelFavoriteNode = ({
|
||||||
favorite,
|
favorite,
|
||||||
onDrop,
|
onDrop,
|
||||||
}: {
|
}: {
|
||||||
@@ -237,7 +235,7 @@ const ExplorerFavoriteNode = ({
|
|||||||
[favorite, onDrop]
|
[favorite, onDrop]
|
||||||
);
|
);
|
||||||
return favorite.type === 'doc' ? (
|
return favorite.type === 'doc' ? (
|
||||||
<ExplorerDocNode
|
<NavigationPanelDocNode
|
||||||
key={favorite.id}
|
key={favorite.id}
|
||||||
docId={favorite.id}
|
docId={favorite.id}
|
||||||
location={childLocation}
|
location={childLocation}
|
||||||
@@ -246,7 +244,7 @@ const ExplorerFavoriteNode = ({
|
|||||||
canDrop={favoriteChildrenCanDrop}
|
canDrop={favoriteChildrenCanDrop}
|
||||||
/>
|
/>
|
||||||
) : favorite.type === 'tag' ? (
|
) : favorite.type === 'tag' ? (
|
||||||
<ExplorerTagNode
|
<NavigationPanelTagNode
|
||||||
key={favorite.id}
|
key={favorite.id}
|
||||||
tagId={favorite.id}
|
tagId={favorite.id}
|
||||||
location={childLocation}
|
location={childLocation}
|
||||||
@@ -255,7 +253,7 @@ const ExplorerFavoriteNode = ({
|
|||||||
canDrop={favoriteChildrenCanDrop}
|
canDrop={favoriteChildrenCanDrop}
|
||||||
/>
|
/>
|
||||||
) : favorite.type === 'folder' ? (
|
) : favorite.type === 'folder' ? (
|
||||||
<ExplorerFolderNode
|
<NavigationPanelFolderNode
|
||||||
key={favorite.id}
|
key={favorite.id}
|
||||||
nodeId={favorite.id}
|
nodeId={favorite.id}
|
||||||
location={childLocation}
|
location={childLocation}
|
||||||
@@ -264,7 +262,7 @@ const ExplorerFavoriteNode = ({
|
|||||||
canDrop={favoriteChildrenCanDrop}
|
canDrop={favoriteChildrenCanDrop}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ExplorerCollectionNode
|
<NavigationPanelCollectionNode
|
||||||
key={favorite.id}
|
key={favorite.id}
|
||||||
collectionId={favorite.id}
|
collectionId={favorite.id}
|
||||||
location={childLocation}
|
location={childLocation}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { IconButton, useConfirmModal } from '@affine/component';
|
import { IconButton, useConfirmModal } from '@affine/component';
|
||||||
import { DocsService } from '@affine/core/modules/doc';
|
import { DocsService } from '@affine/core/modules/doc';
|
||||||
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
|
|
||||||
import { MigrationFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
import { MigrationFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
||||||
import { Trans, useI18n } from '@affine/i18n';
|
import { Trans, useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
@@ -9,11 +8,12 @@ import { useLiveData, useServices } from '@toeverything/infra';
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
||||||
import { ExplorerCollectionNode } from '../../nodes/collection';
|
import { NavigationPanelCollectionNode } from '../../nodes/collection';
|
||||||
import { ExplorerDocNode } from '../../nodes/doc';
|
import { NavigationPanelDocNode } from '../../nodes/doc';
|
||||||
|
import { NavigationPanelTreeRoot } from '../../tree';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
export const ExplorerMigrationFavorites = () => {
|
export const NavigationPanelMigrationFavorites = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
const { migrationFavoriteItemsAdapter, docsService } = useServices({
|
const { migrationFavoriteItemsAdapter, docsService } = useServices({
|
||||||
@@ -105,14 +105,14 @@ export const ExplorerMigrationFavorites = () => {
|
|||||||
actions={
|
actions={
|
||||||
<>
|
<>
|
||||||
<IconButton
|
<IconButton
|
||||||
data-testid="explorer-bar-favorite-migration-clear-button"
|
data-testid="navigation-panel-bar-favorite-migration-clear-button"
|
||||||
onClick={handleClickClear}
|
onClick={handleClickClear}
|
||||||
size="16"
|
size="16"
|
||||||
>
|
>
|
||||||
<BroomIcon />
|
<BroomIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<IconButton
|
<IconButton
|
||||||
data-testid="explorer-bar-favorite-migration-help-button"
|
data-testid="navigation-panel-bar-favorite-migration-help-button"
|
||||||
size="16"
|
size="16"
|
||||||
onClick={handleClickHelp}
|
onClick={handleClickHelp}
|
||||||
>
|
>
|
||||||
@@ -121,22 +121,22 @@ export const ExplorerMigrationFavorites = () => {
|
|||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ExplorerTreeRoot>
|
<NavigationPanelTreeRoot>
|
||||||
{favorites.map((favorite, i) => (
|
{favorites.map((favorite, i) => (
|
||||||
<ExplorerMigrationFavoriteNode
|
<NavigationPanelMigrationFavoriteNode
|
||||||
key={favorite.id + ':' + i}
|
key={favorite.id + ':' + i}
|
||||||
favorite={favorite}
|
favorite={favorite}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ExplorerTreeRoot>
|
</NavigationPanelTreeRoot>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const childLocation = {
|
const childLocation = {
|
||||||
at: 'explorer:migration-data:list' as const,
|
at: 'navigation-panel:migration-data:list' as const,
|
||||||
};
|
};
|
||||||
const ExplorerMigrationFavoriteNode = ({
|
const NavigationPanelMigrationFavoriteNode = ({
|
||||||
favorite,
|
favorite,
|
||||||
}: {
|
}: {
|
||||||
favorite: {
|
favorite: {
|
||||||
@@ -145,7 +145,7 @@ const ExplorerMigrationFavoriteNode = ({
|
|||||||
};
|
};
|
||||||
}) => {
|
}) => {
|
||||||
return favorite.type === 'doc' ? (
|
return favorite.type === 'doc' ? (
|
||||||
<ExplorerDocNode
|
<NavigationPanelDocNode
|
||||||
key={favorite.id}
|
key={favorite.id}
|
||||||
docId={favorite.id}
|
docId={favorite.id}
|
||||||
location={childLocation}
|
location={childLocation}
|
||||||
@@ -153,7 +153,7 @@ const ExplorerMigrationFavoriteNode = ({
|
|||||||
canDrop={false}
|
canDrop={false}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<ExplorerCollectionNode
|
<NavigationPanelCollectionNode
|
||||||
key={favorite.id}
|
key={favorite.id}
|
||||||
collectionId={favorite.id}
|
collectionId={favorite.id}
|
||||||
location={childLocation}
|
location={childLocation}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import type { DropTargetOptions } from '@affine/component';
|
||||||
|
import { isOrganizeSupportType } from '@affine/core/modules/organize/constants';
|
||||||
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
|
|
||||||
|
import type { NavigationPanelTreeNodeDropEffect } from '../../tree';
|
||||||
|
|
||||||
|
export const organizeChildrenDropEffect: NavigationPanelTreeNodeDropEffect =
|
||||||
|
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: 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<AffineDNDData>['canDrop'] =
|
||||||
|
data => {
|
||||||
|
const type = data.source.data.entity?.type;
|
||||||
|
return !!type && isOrganizeSupportType(type);
|
||||||
|
};
|
||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
import type { AffineDNDData } from '@affine/core/types/dnd';
|
import type { AffineDNDData } from '@affine/core/types/dnd';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
|
|
||||||
import { ExplorerEmptySection } from '../../layouts/empty-section';
|
import { NavigationPanelEmptySection } from '../../layouts/empty-section';
|
||||||
import { DropEffect } from '../../tree';
|
import { DropEffect } from '../../tree';
|
||||||
import { organizeEmptyDropEffect, organizeEmptyRootCanDrop } from './dnd';
|
import { organizeEmptyDropEffect, organizeEmptyRootCanDrop } from './dnd';
|
||||||
|
|
||||||
@@ -30,7 +30,7 @@ export const RootEmptyReady = ({
|
|||||||
const { dropTargetRef, draggedOverDraggable, draggedOverPosition } =
|
const { dropTargetRef, draggedOverDraggable, draggedOverPosition } =
|
||||||
useDropTarget<AffineDNDData>(
|
useDropTarget<AffineDNDData>(
|
||||||
() => ({
|
() => ({
|
||||||
data: { at: 'explorer:organize:root' },
|
data: { at: 'navigation-panel:organize:root' },
|
||||||
onDrop,
|
onDrop,
|
||||||
canDrop: organizeEmptyRootCanDrop,
|
canDrop: organizeEmptyRootCanDrop,
|
||||||
}),
|
}),
|
||||||
@@ -38,7 +38,7 @@ export const RootEmptyReady = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerEmptySection
|
<NavigationPanelEmptySection
|
||||||
ref={dropTargetRef}
|
ref={dropTargetRef}
|
||||||
icon={<AnimatedFolderIcon open={!!draggedOverDraggable} />}
|
icon={<AnimatedFolderIcon open={!!draggedOverDraggable} />}
|
||||||
message={t['com.affine.rootAppSidebar.organize.empty']()}
|
message={t['com.affine.rootAppSidebar.organize.empty']()}
|
||||||
@@ -57,7 +57,7 @@ export const RootEmptyReady = ({
|
|||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ExplorerEmptySection>
|
</NavigationPanelEmptySection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
IconButton,
|
IconButton,
|
||||||
toast,
|
toast,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
|
import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
|
||||||
import {
|
import {
|
||||||
type FolderNode,
|
type FolderNode,
|
||||||
OrganizeService,
|
OrganizeService,
|
||||||
@@ -16,19 +16,19 @@ import { AddOrganizeIcon } from '@blocksuite/icons/rc';
|
|||||||
import { useLiveData, useServices } from '@toeverything/infra';
|
import { useLiveData, useServices } from '@toeverything/infra';
|
||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ExplorerService } from '../../../services/explorer';
|
|
||||||
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
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 { organizeChildrenDropEffect } from './dnd';
|
||||||
import { RootEmpty } from './empty';
|
import { RootEmpty } from './empty';
|
||||||
|
|
||||||
export const ExplorerOrganize = () => {
|
export const NavigationPanelOrganize = () => {
|
||||||
const { organizeService, explorerService } = useServices({
|
const { organizeService, navigationPanelService } = useServices({
|
||||||
OrganizeService,
|
OrganizeService,
|
||||||
ExplorerService,
|
NavigationPanelService,
|
||||||
});
|
});
|
||||||
const explorerSection = explorerService.sections.organize;
|
const navigationPanelSection = navigationPanelService.sections.organize;
|
||||||
const collapsed = useLiveData(explorerSection.collapsed$);
|
const collapsed = useLiveData(navigationPanelSection.collapsed$);
|
||||||
const [newFolderId, setNewFolderId] = useState<string | null>(null);
|
const [newFolderId, setNewFolderId] = useState<string | null>(null);
|
||||||
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
@@ -46,9 +46,9 @@ export const ExplorerOrganize = () => {
|
|||||||
);
|
);
|
||||||
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
|
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
|
||||||
setNewFolderId(newFolderId);
|
setNewFolderId(newFolderId);
|
||||||
explorerSection.setCollapsed(false);
|
navigationPanelSection.setCollapsed(false);
|
||||||
return newFolderId;
|
return newFolderId;
|
||||||
}, [explorerSection, rootFolder]);
|
}, [navigationPanelSection, rootFolder]);
|
||||||
|
|
||||||
const handleOnChildrenDrop = useCallback(
|
const handleOnChildrenDrop = useCallback(
|
||||||
(data: DropTargetDropEvent<AffineDNDData>, node?: FolderNode) => {
|
(data: DropTargetDropEvent<AffineDNDData>, node?: FolderNode) => {
|
||||||
@@ -109,7 +109,7 @@ export const ExplorerOrganize = () => {
|
|||||||
title={t['com.affine.rootAppSidebar.organize']()}
|
title={t['com.affine.rootAppSidebar.organize']()}
|
||||||
actions={
|
actions={
|
||||||
<IconButton
|
<IconButton
|
||||||
data-testid="explorer-bar-add-organize-button"
|
data-testid="navigation-panel-bar-add-organize-button"
|
||||||
onClick={handleCreateFolder}
|
onClick={handleCreateFolder}
|
||||||
size="16"
|
size="16"
|
||||||
tooltip={t[
|
tooltip={t[
|
||||||
@@ -120,7 +120,7 @@ export const ExplorerOrganize = () => {
|
|||||||
</IconButton>
|
</IconButton>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ExplorerTreeRoot
|
<NavigationPanelTreeRoot
|
||||||
placeholder={
|
placeholder={
|
||||||
<RootEmpty
|
<RootEmpty
|
||||||
onClickCreate={handleCreateFolder}
|
onClickCreate={handleCreateFolder}
|
||||||
@@ -130,7 +130,7 @@ export const ExplorerOrganize = () => {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{folders.map(child => (
|
{folders.map(child => (
|
||||||
<ExplorerFolderNode
|
<NavigationPanelFolderNode
|
||||||
key={child.id}
|
key={child.id}
|
||||||
nodeId={child.id as string}
|
nodeId={child.id as string}
|
||||||
defaultRenaming={child.id === newFolderId}
|
defaultRenaming={child.id === newFolderId}
|
||||||
@@ -138,12 +138,12 @@ export const ExplorerOrganize = () => {
|
|||||||
dropEffect={organizeChildrenDropEffect}
|
dropEffect={organizeChildrenDropEffect}
|
||||||
canDrop={handleChildrenCanDrop}
|
canDrop={handleChildrenCanDrop}
|
||||||
location={{
|
location={{
|
||||||
at: 'explorer:organize:folder-node',
|
at: 'navigation-panel:organize:folder-node',
|
||||||
nodeId: child.id as string,
|
nodeId: child.id as string,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ExplorerTreeRoot>
|
</NavigationPanelTreeRoot>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { TagIcon } from '@blocksuite/icons/rc';
|
import { TagIcon } from '@blocksuite/icons/rc';
|
||||||
|
|
||||||
import { ExplorerEmptySection } from '../../layouts/empty-section';
|
import { NavigationPanelEmptySection } from '../../layouts/empty-section';
|
||||||
|
|
||||||
export const RootEmpty = () => {
|
export const RootEmpty = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerEmptySection
|
<NavigationPanelEmptySection
|
||||||
icon={TagIcon}
|
icon={TagIcon}
|
||||||
message={t['com.affine.rootAppSidebar.tags.empty']()}
|
message={t['com.affine.rootAppSidebar.tags.empty']()}
|
||||||
messageTestId="slider-bar-tags-empty-message"
|
messageTestId="slider-bar-tags-empty-message"
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { IconButton } from '@affine/component';
|
import { IconButton } from '@affine/component';
|
||||||
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
|
import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
|
||||||
import { TagService } from '@affine/core/modules/tag';
|
import { TagService } from '@affine/core/modules/tag';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
@@ -7,20 +7,20 @@ import { AddTagIcon } from '@blocksuite/icons/rc';
|
|||||||
import { useLiveData, useServices } from '@toeverything/infra';
|
import { useLiveData, useServices } from '@toeverything/infra';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
|
|
||||||
import { ExplorerService } from '../../../services/explorer';
|
|
||||||
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
||||||
import { ExplorerTagNode } from '../../nodes/tag';
|
import { NavigationPanelTagNode } from '../../nodes/tag';
|
||||||
import { ExplorerTreeNodeRenameModal as CreateTagModal } from '../../tree/node';
|
import { NavigationPanelTreeRoot } from '../../tree';
|
||||||
|
import { NavigationPanelTreeNodeRenameModal as CreateTagModal } from '../../tree/node';
|
||||||
import { RootEmpty } from './empty';
|
import { RootEmpty } from './empty';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
export const ExplorerTags = () => {
|
export const NavigationPanelTags = () => {
|
||||||
const { tagService, explorerService } = useServices({
|
const { tagService, navigationPanelService } = useServices({
|
||||||
TagService,
|
TagService,
|
||||||
ExplorerService,
|
NavigationPanelService,
|
||||||
});
|
});
|
||||||
const explorerSection = explorerService.sections.tags;
|
const navigationPanelSection = navigationPanelService.sections.tags;
|
||||||
const collapsed = useLiveData(explorerSection.collapsed$);
|
const collapsed = useLiveData(navigationPanelSection.collapsed$);
|
||||||
const [creating, setCreating] = useState(false);
|
const [creating, setCreating] = useState(false);
|
||||||
const tags = useLiveData(tagService.tagList.tags$);
|
const tags = useLiveData(tagService.tagList.tags$);
|
||||||
|
|
||||||
@@ -30,9 +30,9 @@ export const ExplorerTags = () => {
|
|||||||
(name: string) => {
|
(name: string) => {
|
||||||
tagService.tagList.createTag(name, tagService.randomTagColor());
|
tagService.tagList.createTag(name, tagService.randomTagColor());
|
||||||
track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' });
|
track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' });
|
||||||
explorerSection.setCollapsed(false);
|
navigationPanelSection.setCollapsed(false);
|
||||||
},
|
},
|
||||||
[explorerSection, tagService]
|
[navigationPanelSection, tagService]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -46,13 +46,13 @@ export const ExplorerTags = () => {
|
|||||||
return (
|
return (
|
||||||
<CollapsibleSection
|
<CollapsibleSection
|
||||||
name="tags"
|
name="tags"
|
||||||
testId="explorer-tags"
|
testId="navigation-panel-tags"
|
||||||
headerClassName={styles.draggedOverHighlight}
|
headerClassName={styles.draggedOverHighlight}
|
||||||
title={t['com.affine.rootAppSidebar.tags']()}
|
title={t['com.affine.rootAppSidebar.tags']()}
|
||||||
actions={
|
actions={
|
||||||
<div className={styles.iconContainer}>
|
<div className={styles.iconContainer}>
|
||||||
<IconButton
|
<IconButton
|
||||||
data-testid="explorer-bar-add-tag-button"
|
data-testid="navigation-panel-bar-add-tag-button"
|
||||||
onClick={handleOpenCreateModal}
|
onClick={handleOpenCreateModal}
|
||||||
size="16"
|
size="16"
|
||||||
tooltip={t[
|
tooltip={t[
|
||||||
@@ -72,18 +72,18 @@ export const ExplorerTags = () => {
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<ExplorerTreeRoot placeholder={<RootEmpty />}>
|
<NavigationPanelTreeRoot placeholder={<RootEmpty />}>
|
||||||
{tags.map(tag => (
|
{tags.map(tag => (
|
||||||
<ExplorerTagNode
|
<NavigationPanelTagNode
|
||||||
key={tag.id}
|
key={tag.id}
|
||||||
tagId={tag.id}
|
tagId={tag.id}
|
||||||
reorderable={false}
|
reorderable={false}
|
||||||
location={{
|
location={{
|
||||||
at: 'explorer:tags:list',
|
at: 'navigation-panel:tags:list',
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</ExplorerTreeRoot>
|
</NavigationPanelTreeRoot>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -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<NavigationPanelTreeContextData | null>(null);
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export { DropEffect } from './drop-effect';
|
||||||
|
export type {
|
||||||
|
NavigationPanelTreeNodeDropEffect,
|
||||||
|
NavigationPanelTreeNodeDropEffectData,
|
||||||
|
} from './node';
|
||||||
|
export { NavigationPanelTreeNode } from './node';
|
||||||
|
export { NavigationPanelTreeRoot } from './root';
|
||||||
@@ -39,28 +39,28 @@ import {
|
|||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
|
|
||||||
import { ExplorerTreeContext } from './context';
|
import { NavigationPanelTreeContext } from './context';
|
||||||
import { DropEffect } from './drop-effect';
|
import { DropEffect } from './drop-effect';
|
||||||
import * as styles from './node.css';
|
import * as styles from './node.css';
|
||||||
import type { NodeOperation } from './types';
|
import type { NodeOperation } from './types';
|
||||||
|
|
||||||
export type ExplorerTreeNodeDropEffectData = {
|
export type NavigationPanelTreeNodeDropEffectData = {
|
||||||
source: { data: AffineDNDData['draggable'] };
|
source: { data: AffineDNDData['draggable'] };
|
||||||
treeInstruction: DropTargetTreeInstruction | null;
|
treeInstruction: DropTargetTreeInstruction | null;
|
||||||
};
|
};
|
||||||
export type ExplorerTreeNodeDropEffect = (
|
export type NavigationPanelTreeNodeDropEffect = (
|
||||||
data: ExplorerTreeNodeDropEffectData
|
data: NavigationPanelTreeNodeDropEffectData
|
||||||
) => 'copy' | 'move' | 'link' | undefined;
|
) => 'copy' | 'move' | 'link' | undefined;
|
||||||
export type ExplorerTreeNodeIcon = React.ComponentType<{
|
export type NavigationPanelTreeNodeIcon = React.ComponentType<{
|
||||||
className?: string;
|
className?: string;
|
||||||
draggedOver?: boolean;
|
draggedOver?: boolean;
|
||||||
treeInstruction?: DropTargetTreeInstruction | null;
|
treeInstruction?: DropTargetTreeInstruction | null;
|
||||||
collapsed?: boolean;
|
collapsed?: boolean;
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
export interface BaseExplorerTreeNodeProps {
|
export interface BaseNavigationPanelTreeNodeProps {
|
||||||
name?: string;
|
name?: string;
|
||||||
icon?: ExplorerTreeNodeIcon;
|
icon?: NavigationPanelTreeNodeIcon;
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
active?: boolean;
|
active?: boolean;
|
||||||
extractEmojiAsIcon?: boolean;
|
extractEmojiAsIcon?: boolean;
|
||||||
@@ -81,7 +81,8 @@ export interface BaseExplorerTreeNodeProps {
|
|||||||
[key: `data-${string}`]: any;
|
[key: `data-${string}`]: any;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface WebExplorerTreeNodeProps extends BaseExplorerTreeNodeProps {
|
interface WebNavigationPanelTreeNodeProps
|
||||||
|
extends BaseNavigationPanelTreeNodeProps {
|
||||||
renameable?: boolean;
|
renameable?: boolean;
|
||||||
onRename?: (newName: string) => void;
|
onRename?: (newName: string) => void;
|
||||||
renameableGuard?: { docId: string; action: DocPermissionActions };
|
renameableGuard?: { docId: string; action: DocPermissionActions };
|
||||||
@@ -91,14 +92,14 @@ interface WebExplorerTreeNodeProps extends BaseExplorerTreeNodeProps {
|
|||||||
reorderable?: boolean;
|
reorderable?: boolean;
|
||||||
dndData?: AffineDNDData;
|
dndData?: AffineDNDData;
|
||||||
onDrop?: (data: DropTargetDropEvent<AffineDNDData>) => void;
|
onDrop?: (data: DropTargetDropEvent<AffineDNDData>) => 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.
|
* Separate it into a separate component to prevent re-rendering the entire component when width changes.
|
||||||
*/
|
*/
|
||||||
export const ExplorerTreeNodeRenameModal = ({
|
export const NavigationPanelTreeNodeRenameModal = ({
|
||||||
setRenaming,
|
setRenaming,
|
||||||
handleRename,
|
handleRename,
|
||||||
rawName,
|
rawName,
|
||||||
@@ -124,7 +125,7 @@ export const ExplorerTreeNodeRenameModal = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ExplorerTreeNode = ({
|
export const NavigationPanelTreeNode = ({
|
||||||
children,
|
children,
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
name: rawName,
|
name: rawName,
|
||||||
@@ -150,10 +151,10 @@ export const ExplorerTreeNode = ({
|
|||||||
onDrop,
|
onDrop,
|
||||||
dropEffect,
|
dropEffect,
|
||||||
...otherProps
|
...otherProps
|
||||||
}: WebExplorerTreeNodeProps) => {
|
}: WebNavigationPanelTreeNodeProps) => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const cid = useId();
|
const cid = useId();
|
||||||
const context = useContext(ExplorerTreeContext);
|
const context = useContext(NavigationPanelTreeContext);
|
||||||
const level = context?.level ?? 0;
|
const level = context?.level ?? 0;
|
||||||
// If no onClick or to is provided, clicking on the node will toggle the collapse state
|
// If no onClick or to is provided, clicking on the node will toggle the collapse state
|
||||||
const clickForCollapse = !onClick && !to && !disabled;
|
const clickForCollapse = !onClick && !to && !disabled;
|
||||||
@@ -293,7 +294,7 @@ export const ExplorerTreeNode = ({
|
|||||||
>
|
>
|
||||||
{can => (
|
{can => (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={'explorer-tree-rename'}
|
key={'navigation-panel-tree-rename'}
|
||||||
type={'default'}
|
type={'default'}
|
||||||
prefixIcon={<EditIcon />}
|
prefixIcon={<EditIcon />}
|
||||||
onClick={() => setRenaming(true)}
|
onClick={() => setRenaming(true)}
|
||||||
@@ -305,7 +306,7 @@ export const ExplorerTreeNode = ({
|
|||||||
</Guard>
|
</Guard>
|
||||||
) : (
|
) : (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
key={'explorer-tree-rename'}
|
key={'navigation-panel-tree-rename'}
|
||||||
type={'default'}
|
type={'default'}
|
||||||
prefixIcon={<EditIcon />}
|
prefixIcon={<EditIcon />}
|
||||||
onClick={() => setRenaming(true)}
|
onClick={() => setRenaming(true)}
|
||||||
@@ -381,7 +382,7 @@ export const ExplorerTreeNode = ({
|
|||||||
<div
|
<div
|
||||||
data-disabled={disabled}
|
data-disabled={disabled}
|
||||||
onClick={handleCollapsedChange}
|
onClick={handleCollapsedChange}
|
||||||
data-testid="explorer-collapsed-button"
|
data-testid="navigation-panel-collapsed-button"
|
||||||
className={styles.collapsedIconContainer}
|
className={styles.collapsedIconContainer}
|
||||||
>
|
>
|
||||||
<ArrowDownSmallIcon
|
<ArrowDownSmallIcon
|
||||||
@@ -421,7 +422,7 @@ export const ExplorerTreeNode = ({
|
|||||||
>
|
>
|
||||||
<IconButton
|
<IconButton
|
||||||
size="16"
|
size="16"
|
||||||
data-testid="explorer-tree-node-operation-button"
|
data-testid="navigation-panel-tree-node-operation-button"
|
||||||
style={{ marginLeft: 4 }}
|
style={{ marginLeft: 4 }}
|
||||||
>
|
>
|
||||||
<MoreHorizontalIcon />
|
<MoreHorizontalIcon />
|
||||||
@@ -432,7 +433,7 @@ export const ExplorerTreeNode = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{renameable && renaming && (
|
{renameable && renaming && (
|
||||||
<ExplorerTreeNodeRenameModal
|
<NavigationPanelTreeNodeRenameModal
|
||||||
setRenaming={setRenaming}
|
setRenaming={setRenaming}
|
||||||
handleRename={handleRename}
|
handleRename={handleRename}
|
||||||
rawName={rawName}
|
rawName={rawName}
|
||||||
@@ -497,9 +498,9 @@ export const ExplorerTreeNode = ({
|
|||||||
<div className={styles.collapseContentPlaceholder}>
|
<div className={styles.collapseContentPlaceholder}>
|
||||||
{childCount === 0 && !collapsed ? childrenPlaceholder : null}
|
{childCount === 0 && !collapsed ? childrenPlaceholder : null}
|
||||||
</div>
|
</div>
|
||||||
<ExplorerTreeContext.Provider value={contextValue}>
|
<NavigationPanelTreeContext.Provider value={contextValue}>
|
||||||
{collapsed ? null : children}
|
{collapsed ? null : children}
|
||||||
</ExplorerTreeContext.Provider>
|
</NavigationPanelTreeContext.Provider>
|
||||||
</Collapsible.Content>
|
</Collapsible.Content>
|
||||||
</Collapsible.Root>
|
</Collapsible.Root>
|
||||||
);
|
);
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ExplorerTreeContext } from './context';
|
import { NavigationPanelTreeContext } from './context';
|
||||||
import * as styles from './root.css';
|
import * as styles from './root.css';
|
||||||
import type { NodeOperation } from './types';
|
import type { NodeOperation } from './types';
|
||||||
|
|
||||||
export const ExplorerTreeRoot = ({
|
export const NavigationPanelTreeRoot = ({
|
||||||
children,
|
children,
|
||||||
childrenOperations = [],
|
childrenOperations = [],
|
||||||
placeholder,
|
placeholder,
|
||||||
@@ -33,9 +33,9 @@ export const ExplorerTreeRoot = ({
|
|||||||
<div className={styles.placeholder}>
|
<div className={styles.placeholder}>
|
||||||
{childCount === 0 && placeholder}
|
{childCount === 0 && placeholder}
|
||||||
</div>
|
</div>
|
||||||
<ExplorerTreeContext.Provider value={contextValue}>
|
<NavigationPanelTreeContext.Provider value={contextValue}>
|
||||||
{children}
|
{children}
|
||||||
</ExplorerTreeContext.Provider>
|
</NavigationPanelTreeContext.Provider>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -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';
|
|
||||||
@@ -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';
|
||||||
@@ -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 { PlusIcon } from '@blocksuite/icons/rc';
|
||||||
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
import { assignInlineVars } from '@vanilla-extract/dynamic';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
@@ -21,7 +21,7 @@ export const AddItemPlaceholder = ({
|
|||||||
className,
|
className,
|
||||||
...attrs
|
...attrs
|
||||||
}: AddItemPlaceholderProps) => {
|
}: AddItemPlaceholderProps) => {
|
||||||
const context = useContext(ExplorerTreeContext);
|
const context = useContext(NavigationPanelTreeContext);
|
||||||
const level = context?.level ?? 0;
|
const level = context?.level ?? 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
type CollapsibleSectionName,
|
type CollapsibleSectionName,
|
||||||
ExplorerService,
|
NavigationPanelService,
|
||||||
} from '@affine/core/modules/explorer';
|
} from '@affine/core/modules/navigation-panel';
|
||||||
import { ToggleRightIcon } from '@blocksuite/icons/rc';
|
import { ToggleRightIcon } from '@blocksuite/icons/rc';
|
||||||
import * as Collapsible from '@radix-ui/react-collapsible';
|
import * as Collapsible from '@radix-ui/react-collapsible';
|
||||||
import { useLiveData, useService } from '@toeverything/infra';
|
import { useLiveData, useService } from '@toeverything/infra';
|
||||||
@@ -86,7 +86,7 @@ export const CollapsibleSection = ({
|
|||||||
children,
|
children,
|
||||||
...attrs
|
...attrs
|
||||||
}: CollapsibleSectionProps) => {
|
}: CollapsibleSectionProps) => {
|
||||||
const section = useService(ExplorerService).sections[name];
|
const section = useService(NavigationPanelService).sections[name];
|
||||||
const collapsed = useLiveData(section.collapsed$);
|
const collapsed = useLiveData(section.collapsed$);
|
||||||
|
|
||||||
const setCollapsed = useCallback(
|
const setCollapsed = useCallback(
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { MenuItem, notify } from '@affine/component';
|
import { MenuItem, notify } from '@affine/component';
|
||||||
import { filterPage } from '@affine/core/components/page-list';
|
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 { CollectionService } from '@affine/core/modules/collection';
|
||||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import { DocsService } from '@affine/core/modules/doc';
|
import { DocsService } from '@affine/core/modules/doc';
|
||||||
import type { NodeOperation } from '@affine/core/modules/explorer';
|
|
||||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
||||||
import { GlobalContextService } from '@affine/core/modules/global-context';
|
import { GlobalContextService } from '@affine/core/modules/global-context';
|
||||||
import { ShareDocsListService } from '@affine/core/modules/share-doc';
|
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 { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
||||||
import { ExplorerTreeNode } from '../../tree/node';
|
import { NavigationPanelTreeNode } from '../../tree/node';
|
||||||
import { ExplorerDocNode } from '../doc';
|
import { NavigationPanelDocNode } from '../doc';
|
||||||
import {
|
import {
|
||||||
useExplorerCollectionNodeOperations,
|
useNavigationPanelCollectionNodeOperations,
|
||||||
useExplorerCollectionNodeOperationsMenu,
|
useNavigationPanelCollectionNodeOperationsMenu,
|
||||||
} from './operations';
|
} from './operations';
|
||||||
|
|
||||||
const CollectionIcon = () => <ViewLayersIcon />;
|
const CollectionIcon = () => <ViewLayersIcon />;
|
||||||
|
|
||||||
export const ExplorerCollectionNode = ({
|
export const NavigationPanelCollectionNode = ({
|
||||||
collectionId,
|
collectionId,
|
||||||
operations: additionalOperations,
|
operations: additionalOperations,
|
||||||
}: {
|
}: {
|
||||||
@@ -60,12 +60,13 @@ export const ExplorerCollectionNode = ({
|
|||||||
});
|
});
|
||||||
}, [collection, workspaceDialogService]);
|
}, [collection, workspaceDialogService]);
|
||||||
|
|
||||||
const collectionOperations = useExplorerCollectionNodeOperationsMenu(
|
const collectionOperations = useNavigationPanelCollectionNodeOperationsMenu(
|
||||||
collectionId,
|
collectionId,
|
||||||
handleOpenCollapsed,
|
handleOpenCollapsed,
|
||||||
handleEditCollection
|
handleEditCollection
|
||||||
);
|
);
|
||||||
const { handleAddDocToCollection } = useExplorerCollectionNodeOperations(
|
const { handleAddDocToCollection } =
|
||||||
|
useNavigationPanelCollectionNodeOperations(
|
||||||
collectionId,
|
collectionId,
|
||||||
handleOpenCollapsed,
|
handleOpenCollapsed,
|
||||||
handleEditCollection
|
handleEditCollection
|
||||||
@@ -83,7 +84,7 @@ export const ExplorerCollectionNode = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerTreeNode
|
<NavigationPanelTreeNode
|
||||||
icon={CollectionIcon}
|
icon={CollectionIcon}
|
||||||
name={collection.name || t['Untitled']()}
|
name={collection.name || t['Untitled']()}
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
@@ -91,17 +92,17 @@ export const ExplorerCollectionNode = ({
|
|||||||
to={`/collection/${collection.id}`}
|
to={`/collection/${collection.id}`}
|
||||||
active={active}
|
active={active}
|
||||||
operations={finalOperations}
|
operations={finalOperations}
|
||||||
data-testid={`explorer-collection-${collectionId}`}
|
data-testid={`navigation-panel-collection-${collectionId}`}
|
||||||
>
|
>
|
||||||
<ExplorerCollectionNodeChildren
|
<NavigationPanelCollectionNodeChildren
|
||||||
collection={collection}
|
collection={collection}
|
||||||
onAddDoc={handleAddDocToCollection}
|
onAddDoc={handleAddDocToCollection}
|
||||||
/>
|
/>
|
||||||
</ExplorerTreeNode>
|
</NavigationPanelTreeNode>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const ExplorerCollectionNodeChildren = ({
|
const NavigationPanelCollectionNodeChildren = ({
|
||||||
collection,
|
collection,
|
||||||
onAddDoc,
|
onAddDoc,
|
||||||
}: {
|
}: {
|
||||||
@@ -174,7 +175,7 @@ const ExplorerCollectionNodeChildren = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{filtered.map(doc => (
|
{filtered.map(doc => (
|
||||||
<ExplorerDocNode
|
<NavigationPanelDocNode
|
||||||
key={doc.id}
|
key={doc.id}
|
||||||
docId={doc.id}
|
docId={doc.id}
|
||||||
operations={
|
operations={
|
||||||
@@ -8,8 +8,8 @@ import {
|
|||||||
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
||||||
import { useDeleteCollectionInfo } from '@affine/core/components/hooks/affine/use-delete-collection-info';
|
import { useDeleteCollectionInfo } from '@affine/core/components/hooks/affine/use-delete-collection-info';
|
||||||
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
||||||
|
import type { NodeOperation } from '@affine/core/desktop/components/navigation-panel';
|
||||||
import { CollectionService } from '@affine/core/modules/collection';
|
import { CollectionService } from '@affine/core/modules/collection';
|
||||||
import type { NodeOperation } from '@affine/core/modules/explorer';
|
|
||||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
@@ -27,7 +27,7 @@ import { useCallback, useMemo } from 'react';
|
|||||||
|
|
||||||
import { CollectionRenameSubMenu } from './dialog';
|
import { CollectionRenameSubMenu } from './dialog';
|
||||||
|
|
||||||
export const useExplorerCollectionNodeOperations = (
|
export const useNavigationPanelCollectionNodeOperations = (
|
||||||
collectionId: string,
|
collectionId: string,
|
||||||
onOpenCollapsed: () => void,
|
onOpenCollapsed: () => void,
|
||||||
onOpenEdit: () => void
|
onOpenEdit: () => void
|
||||||
@@ -154,7 +154,7 @@ export const useExplorerCollectionNodeOperations = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useExplorerCollectionNodeOperationsMenu = (
|
export const useNavigationPanelCollectionNodeOperationsMenu = (
|
||||||
collectionId: string,
|
collectionId: string,
|
||||||
onOpenCollapsed: () => void,
|
onOpenCollapsed: () => void,
|
||||||
onOpenEdit: () => void
|
onOpenEdit: () => void
|
||||||
@@ -170,7 +170,7 @@ export const useExplorerCollectionNodeOperationsMenu = (
|
|||||||
handleShowEdit,
|
handleShowEdit,
|
||||||
handleToggleFavoriteCollection,
|
handleToggleFavoriteCollection,
|
||||||
handleRename,
|
handleRename,
|
||||||
} = useExplorerCollectionNodeOperations(
|
} = useNavigationPanelCollectionNodeOperations(
|
||||||
collectionId,
|
collectionId,
|
||||||
onOpenCollapsed,
|
onOpenCollapsed,
|
||||||
onOpenEdit
|
onOpenEdit
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { Loading } from '@affine/component';
|
import { Loading } from '@affine/component';
|
||||||
import { Guard } from '@affine/core/components/guard';
|
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 { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import { DocsService } from '@affine/core/modules/doc';
|
import { DocsService } from '@affine/core/modules/doc';
|
||||||
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
import { DocDisplayMetaService } from '@affine/core/modules/doc-display-meta';
|
||||||
import { DocsSearchService } from '@affine/core/modules/docs-search';
|
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 { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import { GlobalContextService } from '@affine/core/modules/global-context';
|
import { GlobalContextService } from '@affine/core/modules/global-context';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
@@ -18,14 +18,14 @@ import {
|
|||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
||||||
import { ExplorerTreeNode } from '../../tree/node';
|
import { NavigationPanelTreeNode } from '../../tree/node';
|
||||||
import {
|
import {
|
||||||
useExplorerDocNodeOperations,
|
useNavigationPanelDocNodeOperations,
|
||||||
useExplorerDocNodeOperationsMenu,
|
useNavigationPanelDocNodeOperationsMenu,
|
||||||
} from './operations';
|
} from './operations';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
export const ExplorerDocNode = ({
|
export const NavigationPanelDocNode = ({
|
||||||
docId,
|
docId,
|
||||||
isLinked,
|
isLinked,
|
||||||
operations: additionalOperations,
|
operations: additionalOperations,
|
||||||
@@ -105,8 +105,11 @@ export const ExplorerDocNode = ({
|
|||||||
}),
|
}),
|
||||||
[docId, workspaceDialogService]
|
[docId, workspaceDialogService]
|
||||||
);
|
);
|
||||||
const operations = useExplorerDocNodeOperationsMenu(docId, option);
|
const operations = useNavigationPanelDocNodeOperationsMenu(docId, option);
|
||||||
const { handleAddLinkedPage } = useExplorerDocNodeOperations(docId, option);
|
const { handleAddLinkedPage } = useNavigationPanelDocNodeOperations(
|
||||||
|
docId,
|
||||||
|
option
|
||||||
|
);
|
||||||
|
|
||||||
const finalOperations = useMemo(() => {
|
const finalOperations = useMemo(() => {
|
||||||
if (additionalOperations) {
|
if (additionalOperations) {
|
||||||
@@ -120,7 +123,7 @@ export const ExplorerDocNode = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerTreeNode
|
<NavigationPanelTreeNode
|
||||||
icon={Icon}
|
icon={Icon}
|
||||||
name={t.t(docTitle)}
|
name={t.t(docTitle)}
|
||||||
extractEmojiAsIcon={enableEmojiIcon}
|
extractEmojiAsIcon={enableEmojiIcon}
|
||||||
@@ -137,13 +140,13 @@ export const ExplorerDocNode = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
operations={finalOperations}
|
operations={finalOperations}
|
||||||
data-testid={`explorer-doc-${docId}`}
|
data-testid={`navigation-panel-doc-${docId}`}
|
||||||
>
|
>
|
||||||
<Guard docId={docId} permission="Doc_Read">
|
<Guard docId={docId} permission="Doc_Read">
|
||||||
{canRead =>
|
{canRead =>
|
||||||
canRead
|
canRead
|
||||||
? children?.map((child, index) => (
|
? children?.map((child, index) => (
|
||||||
<ExplorerDocNode
|
<NavigationPanelDocNode
|
||||||
key={`${child.docId}-${index}`}
|
key={`${child.docId}-${index}`}
|
||||||
docId={child.docId}
|
docId={child.docId}
|
||||||
isLinked
|
isLinked
|
||||||
@@ -162,6 +165,6 @@ export const ExplorerDocNode = ({
|
|||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
</Guard>
|
</Guard>
|
||||||
</ExplorerTreeNode>
|
</NavigationPanelTreeNode>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -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 { useBlockSuiteMetaHelper } from '@affine/core/components/hooks/affine/use-block-suite-meta-helper';
|
||||||
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
import { useAsyncCallback } from '@affine/core/components/hooks/affine-async-hooks';
|
||||||
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
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 { DocsService } from '@affine/core/modules/doc';
|
||||||
import type { NodeOperation } from '@affine/core/modules/explorer';
|
|
||||||
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
import { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { WorkspaceService } from '@affine/core/modules/workspace';
|
import { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
@@ -31,7 +31,7 @@ import { useCallback, useMemo } from 'react';
|
|||||||
import { DocFrameScope, DocInfoSheet } from '../../../doc-info';
|
import { DocFrameScope, DocInfoSheet } from '../../../doc-info';
|
||||||
import { DocRenameSubMenu } from './dialog';
|
import { DocRenameSubMenu } from './dialog';
|
||||||
|
|
||||||
export const useExplorerDocNodeOperations = (
|
export const useNavigationPanelDocNodeOperations = (
|
||||||
docId: string,
|
docId: string,
|
||||||
options: {
|
options: {
|
||||||
openNodeCollapsed: () => void;
|
openNodeCollapsed: () => void;
|
||||||
@@ -160,7 +160,7 @@ export const useExplorerDocNodeOperations = (
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useExplorerDocNodeOperationsMenu = (
|
export const useNavigationPanelDocNodeOperationsMenu = (
|
||||||
docId: string,
|
docId: string,
|
||||||
options: {
|
options: {
|
||||||
openInfoModal: () => void;
|
openInfoModal: () => void;
|
||||||
@@ -176,7 +176,7 @@ export const useExplorerDocNodeOperationsMenu = (
|
|||||||
handleOpenInNewTab,
|
handleOpenInNewTab,
|
||||||
handleMoveToTrash,
|
handleMoveToTrash,
|
||||||
handleRename,
|
handleRename,
|
||||||
} = useExplorerDocNodeOperations(docId, options);
|
} = useNavigationPanelDocNodeOperations(docId, options);
|
||||||
|
|
||||||
const docService = useService(DocsService);
|
const docService = useService(DocsService);
|
||||||
const docRecord = useLiveData(docService.list.doc$(docId));
|
const docRecord = useLiveData(docService.list.doc$(docId));
|
||||||
@@ -7,11 +7,11 @@ import {
|
|||||||
notify,
|
notify,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
||||||
import { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
|
||||||
import type {
|
import type {
|
||||||
ExplorerTreeNodeIcon,
|
NavigationPanelTreeNodeIcon,
|
||||||
NodeOperation,
|
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 { CompatibleFavoriteItemsAdapter } from '@affine/core/modules/favorite';
|
||||||
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
import { FeatureFlagService } from '@affine/core/modules/feature-flag';
|
||||||
import {
|
import {
|
||||||
@@ -36,14 +36,14 @@ import { difference } from 'lodash-es';
|
|||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
||||||
import { ExplorerTreeNode } from '../../tree/node';
|
import { NavigationPanelTreeNode } from '../../tree/node';
|
||||||
import { ExplorerCollectionNode } from '../collection';
|
import { NavigationPanelCollectionNode } from '../collection';
|
||||||
import { ExplorerDocNode } from '../doc';
|
import { NavigationPanelDocNode } from '../doc';
|
||||||
import { ExplorerTagNode } from '../tag';
|
import { NavigationPanelTagNode } from '../tag';
|
||||||
import { FolderCreateTip, FolderRenameSubMenu } from './dialog';
|
import { FolderCreateTip, FolderRenameSubMenu } from './dialog';
|
||||||
import { FavoriteFolderOperation } from './operations';
|
import { FavoriteFolderOperation } from './operations';
|
||||||
|
|
||||||
export const ExplorerFolderNode = ({
|
export const NavigationPanelFolderNode = ({
|
||||||
nodeId,
|
nodeId,
|
||||||
operations,
|
operations,
|
||||||
}: {
|
}: {
|
||||||
@@ -75,27 +75,34 @@ export const ExplorerFolderNode = ({
|
|||||||
|
|
||||||
if (type === 'folder') {
|
if (type === 'folder') {
|
||||||
return (
|
return (
|
||||||
<ExplorerFolderNodeFolder node={node} operations={additionalOperations} />
|
<NavigationPanelFolderNodeFolder
|
||||||
|
node={node}
|
||||||
|
operations={additionalOperations}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (!data) return null;
|
if (!data) return null;
|
||||||
if (type === 'doc') {
|
if (type === 'doc') {
|
||||||
return <ExplorerDocNode docId={data} operations={additionalOperations} />;
|
return (
|
||||||
|
<NavigationPanelDocNode docId={data} operations={additionalOperations} />
|
||||||
|
);
|
||||||
} else if (type === 'collection') {
|
} else if (type === 'collection') {
|
||||||
return (
|
return (
|
||||||
<ExplorerCollectionNode
|
<NavigationPanelCollectionNode
|
||||||
collectionId={data}
|
collectionId={data}
|
||||||
operations={additionalOperations}
|
operations={additionalOperations}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (type === 'tag') {
|
} else if (type === 'tag') {
|
||||||
return <ExplorerTagNode tagId={data} operations={additionalOperations} />;
|
return (
|
||||||
|
<NavigationPanelTagNode tagId={data} operations={additionalOperations} />
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ExplorerFolderIcon: ExplorerTreeNodeIcon = ({
|
const NavigationPanelFolderIcon: NavigationPanelTreeNodeIcon = ({
|
||||||
collapsed,
|
collapsed,
|
||||||
className,
|
className,
|
||||||
draggedOver,
|
draggedOver,
|
||||||
@@ -109,7 +116,7 @@ const ExplorerFolderIcon: ExplorerTreeNodeIcon = ({
|
|||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
const ExplorerFolderNodeFolder = ({
|
const NavigationPanelFolderNodeFolder = ({
|
||||||
node,
|
node,
|
||||||
operations: additionalOperations,
|
operations: additionalOperations,
|
||||||
}: {
|
}: {
|
||||||
@@ -390,19 +397,19 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerTreeNode
|
<NavigationPanelTreeNode
|
||||||
icon={ExplorerFolderIcon}
|
icon={NavigationPanelFolderIcon}
|
||||||
name={name}
|
name={name}
|
||||||
extractEmojiAsIcon={enableEmojiIcon}
|
extractEmojiAsIcon={enableEmojiIcon}
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
setCollapsed={handleCollapsedChange}
|
setCollapsed={handleCollapsedChange}
|
||||||
operations={finalOperations}
|
operations={finalOperations}
|
||||||
data-testid={`explorer-folder-${node.id}`}
|
data-testid={`navigation-panel-folder-${node.id}`}
|
||||||
aria-label={name}
|
aria-label={name}
|
||||||
data-role="explorer-folder"
|
data-role="navigation-panel-folder"
|
||||||
>
|
>
|
||||||
{children.map(child => (
|
{children.map(child => (
|
||||||
<ExplorerFolderNode
|
<NavigationPanelFolderNode
|
||||||
key={child.id}
|
key={child.id}
|
||||||
nodeId={child.id as string}
|
nodeId={child.id as string}
|
||||||
operations={childrenOperations}
|
operations={childrenOperations}
|
||||||
@@ -413,6 +420,6 @@ const ExplorerFolderNodeFolder = ({
|
|||||||
onClick={handleNewDoc}
|
onClick={handleNewDoc}
|
||||||
data-testid="new-folder-in-folder-button"
|
data-testid="new-folder-in-folder-button"
|
||||||
/>
|
/>
|
||||||
</ExplorerTreeNode>
|
</NavigationPanelTreeNode>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -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 { GlobalContextService } from '@affine/core/modules/global-context';
|
||||||
import type { Tag } from '@affine/core/modules/tag';
|
import type { Tag } from '@affine/core/modules/tag';
|
||||||
import { TagService } 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 { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
||||||
import { ExplorerTreeNode } from '../../tree/node';
|
import { NavigationPanelTreeNode } from '../../tree/node';
|
||||||
import { ExplorerDocNode } from '../doc';
|
import { NavigationPanelDocNode } from '../doc';
|
||||||
import {
|
import {
|
||||||
useExplorerTagNodeOperations,
|
useNavigationPanelTagNodeOperations,
|
||||||
useExplorerTagNodeOperationsMenu,
|
useNavigationPanelTagNodeOperationsMenu,
|
||||||
} from './operations';
|
} from './operations';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
export const ExplorerTagNode = ({
|
export const NavigationPanelTagNode = ({
|
||||||
tagId,
|
tagId,
|
||||||
operations: additionalOperations,
|
operations: additionalOperations,
|
||||||
}: {
|
}: {
|
||||||
@@ -41,7 +41,7 @@ export const ExplorerTagNode = ({
|
|||||||
return (
|
return (
|
||||||
<div className={clsx(styles.tagIconContainer, className)}>
|
<div className={clsx(styles.tagIconContainer, className)}>
|
||||||
<div
|
<div
|
||||||
data-testid="explorer-tag-icon-dot"
|
data-testid="navigation-panel-tag-icon-dot"
|
||||||
className={styles.tagIcon}
|
className={styles.tagIcon}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: tagColor,
|
backgroundColor: tagColor,
|
||||||
@@ -59,8 +59,8 @@ export const ExplorerTagNode = ({
|
|||||||
}),
|
}),
|
||||||
[]
|
[]
|
||||||
);
|
);
|
||||||
const operations = useExplorerTagNodeOperationsMenu(tagId, option);
|
const operations = useNavigationPanelTagNodeOperationsMenu(tagId, option);
|
||||||
const { handleNewDoc } = useExplorerTagNodeOperations(tagId, option);
|
const { handleNewDoc } = useNavigationPanelTagNodeOperations(tagId, option);
|
||||||
|
|
||||||
const finalOperations = useMemo(() => {
|
const finalOperations = useMemo(() => {
|
||||||
if (additionalOperations) {
|
if (additionalOperations) {
|
||||||
@@ -74,7 +74,7 @@ export const ExplorerTagNode = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ExplorerTreeNode
|
<NavigationPanelTreeNode
|
||||||
icon={Icon}
|
icon={Icon}
|
||||||
name={tagName || t['Untitled']()}
|
name={tagName || t['Untitled']()}
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
@@ -82,12 +82,12 @@ export const ExplorerTagNode = ({
|
|||||||
to={`/tag/${tagId}`}
|
to={`/tag/${tagId}`}
|
||||||
active={active}
|
active={active}
|
||||||
operations={finalOperations}
|
operations={finalOperations}
|
||||||
data-testid={`explorer-tag-${tagId}`}
|
data-testid={`navigation-panel-tag-${tagId}`}
|
||||||
aria-label={tagName}
|
aria-label={tagName}
|
||||||
data-role="explorer-tag"
|
data-role="navigation-panel-tag"
|
||||||
>
|
>
|
||||||
<ExplorerTagNodeDocs tag={tagRecord} onNewDoc={handleNewDoc} />
|
<NavigationPanelTagNodeDocs tag={tagRecord} onNewDoc={handleNewDoc} />
|
||||||
</ExplorerTreeNode>
|
</NavigationPanelTreeNode>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ export const ExplorerTagNode = ({
|
|||||||
* so we split the tag node children into a separate component,
|
* so we split the tag node children into a separate component,
|
||||||
* so it won't be rendered when the tag node is collapsed.
|
* so it won't be rendered when the tag node is collapsed.
|
||||||
*/
|
*/
|
||||||
export const ExplorerTagNodeDocs = ({
|
export const NavigationPanelTagNodeDocs = ({
|
||||||
tag,
|
tag,
|
||||||
onNewDoc,
|
onNewDoc,
|
||||||
}: {
|
}: {
|
||||||
@@ -109,7 +109,7 @@ export const ExplorerTagNodeDocs = ({
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{tagDocIds.map(docId => (
|
{tagDocIds.map(docId => (
|
||||||
<ExplorerDocNode key={docId} docId={docId} />
|
<NavigationPanelDocNode key={docId} docId={docId} />
|
||||||
))}
|
))}
|
||||||
<AddItemPlaceholder label={t['New Page']()} onClick={onNewDoc} />
|
<AddItemPlaceholder label={t['New Page']()} onClick={onNewDoc} />
|
||||||
</>
|
</>
|
||||||
@@ -7,9 +7,9 @@ import {
|
|||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
||||||
import { IsFavoriteIcon } from '@affine/core/components/pure/icons';
|
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 { WorkspaceDialogService } from '@affine/core/modules/dialogs';
|
||||||
import { DocsService } from '@affine/core/modules/doc';
|
import { DocsService } from '@affine/core/modules/doc';
|
||||||
import type { NodeOperation } from '@affine/core/modules/explorer';
|
|
||||||
import { FavoriteService } from '@affine/core/modules/favorite';
|
import { FavoriteService } from '@affine/core/modules/favorite';
|
||||||
import { GlobalCacheService } from '@affine/core/modules/storage';
|
import { GlobalCacheService } from '@affine/core/modules/storage';
|
||||||
import { TagService } from '@affine/core/modules/tag';
|
import { TagService } from '@affine/core/modules/tag';
|
||||||
@@ -28,7 +28,7 @@ import { useCallback, useMemo } from 'react';
|
|||||||
|
|
||||||
import { TagRenameSubMenu } from './dialog';
|
import { TagRenameSubMenu } from './dialog';
|
||||||
|
|
||||||
export const useExplorerTagNodeOperations = (
|
export const useNavigationPanelTagNodeOperations = (
|
||||||
tagId: string,
|
tagId: string,
|
||||||
{
|
{
|
||||||
openNodeCollapsed,
|
openNodeCollapsed,
|
||||||
@@ -211,7 +211,7 @@ export const useExplorerTagNodeOperations = (
|
|||||||
]
|
]
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
export const useExplorerTagNodeOperationsMenu = (
|
export const useNavigationPanelTagNodeOperationsMenu = (
|
||||||
tagId: string,
|
tagId: string,
|
||||||
option: {
|
option: {
|
||||||
openNodeCollapsed: () => void;
|
openNodeCollapsed: () => void;
|
||||||
@@ -226,7 +226,7 @@ export const useExplorerTagNodeOperationsMenu = (
|
|||||||
handleToggleFavoriteTag,
|
handleToggleFavoriteTag,
|
||||||
handleChangeNameOrColor,
|
handleChangeNameOrColor,
|
||||||
handleOpenDocSelector,
|
handleOpenDocSelector,
|
||||||
} = useExplorerTagNodeOperations(tagId, option);
|
} = useNavigationPanelTagNodeOperations(tagId, option);
|
||||||
|
|
||||||
return useMemo(
|
return useMemo(
|
||||||
() => [
|
() => [
|
||||||
@@ -1,8 +1,8 @@
|
|||||||
import { usePromptModal } from '@affine/component';
|
import { usePromptModal } from '@affine/component';
|
||||||
import { createEmptyCollection } from '@affine/core/components/page-list/use-collection-manager';
|
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 { CollectionService } from '@affine/core/modules/collection';
|
||||||
import { ExplorerService } from '@affine/core/modules/explorer';
|
import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
|
||||||
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
|
|
||||||
import { WorkbenchService } from '@affine/core/modules/workbench';
|
import { WorkbenchService } from '@affine/core/modules/workbench';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
@@ -13,17 +13,18 @@ import { useCallback } from 'react';
|
|||||||
|
|
||||||
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
||||||
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
||||||
import { ExplorerCollectionNode } from '../../nodes/collection';
|
import { NavigationPanelCollectionNode } from '../../nodes/collection';
|
||||||
import * as styles from './index.css';
|
import * as styles from './index.css';
|
||||||
|
|
||||||
export const ExplorerCollections = () => {
|
export const NavigationPanelCollections = () => {
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const { collectionService, workbenchService, explorerService } = useServices({
|
const { collectionService, workbenchService, navigationPanelService } =
|
||||||
|
useServices({
|
||||||
CollectionService,
|
CollectionService,
|
||||||
WorkbenchService,
|
WorkbenchService,
|
||||||
ExplorerService,
|
NavigationPanelService,
|
||||||
});
|
});
|
||||||
const explorerSection = explorerService.sections.collections;
|
const navigationPanelSection = navigationPanelService.sections.collections;
|
||||||
const collections = useLiveData(collectionService.collections$);
|
const collections = useLiveData(collectionService.collections$);
|
||||||
const { openPromptModal } = usePromptModal();
|
const { openPromptModal } = usePromptModal();
|
||||||
|
|
||||||
@@ -51,12 +52,12 @@ export const ExplorerCollections = () => {
|
|||||||
type: 'collection',
|
type: 'collection',
|
||||||
});
|
});
|
||||||
workbenchService.workbench.openCollection(id);
|
workbenchService.workbench.openCollection(id);
|
||||||
explorerSection.setCollapsed(false);
|
navigationPanelSection.setCollapsed(false);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [
|
}, [
|
||||||
collectionService,
|
collectionService,
|
||||||
explorerSection,
|
navigationPanelSection,
|
||||||
openPromptModal,
|
openPromptModal,
|
||||||
t,
|
t,
|
||||||
workbenchService.workbench,
|
workbenchService.workbench,
|
||||||
@@ -65,23 +66,23 @@ export const ExplorerCollections = () => {
|
|||||||
return (
|
return (
|
||||||
<CollapsibleSection
|
<CollapsibleSection
|
||||||
name="collections"
|
name="collections"
|
||||||
testId="explorer-collections"
|
testId="navigation-panel-collections"
|
||||||
title={t['com.affine.rootAppSidebar.collections']()}
|
title={t['com.affine.rootAppSidebar.collections']()}
|
||||||
>
|
>
|
||||||
<ExplorerTreeRoot>
|
<NavigationPanelTreeRoot>
|
||||||
{collections.map(collection => (
|
{collections.map(collection => (
|
||||||
<ExplorerCollectionNode
|
<NavigationPanelCollectionNode
|
||||||
key={collection.id}
|
key={collection.id}
|
||||||
collectionId={collection.id}
|
collectionId={collection.id}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<AddItemPlaceholder
|
<AddItemPlaceholder
|
||||||
icon={<AddCollectionIcon />}
|
icon={<AddCollectionIcon />}
|
||||||
data-testid="explorer-bar-add-collection-button"
|
data-testid="navigation-panel-bar-add-collection-button"
|
||||||
label={t['com.affine.rootAppSidebar.collection.new']()}
|
label={t['com.affine.rootAppSidebar.collection.new']()}
|
||||||
onClick={() => handleCreateCollection()}
|
onClick={() => handleCreateCollection()}
|
||||||
/>
|
/>
|
||||||
</ExplorerTreeRoot>
|
</NavigationPanelTreeRoot>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
import { usePageHelper } from '@affine/core/blocksuite/block-suite-page-list/utils';
|
||||||
import {
|
import { NavigationPanelTreeRoot } from '@affine/core/desktop/components/navigation-panel';
|
||||||
ExplorerService,
|
|
||||||
ExplorerTreeRoot,
|
|
||||||
} from '@affine/core/modules/explorer';
|
|
||||||
import type { FavoriteSupportTypeUnion } from '@affine/core/modules/favorite';
|
import type { FavoriteSupportTypeUnion } from '@affine/core/modules/favorite';
|
||||||
import { FavoriteService } 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 { WorkspaceService } from '@affine/core/modules/workspace';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { useLiveData, useServices } from '@toeverything/infra';
|
import { useLiveData, useServices } from '@toeverything/infra';
|
||||||
@@ -12,20 +10,21 @@ import { useCallback } from 'react';
|
|||||||
|
|
||||||
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
||||||
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
||||||
import { ExplorerCollectionNode } from '../../nodes/collection';
|
import { NavigationPanelCollectionNode } from '../../nodes/collection';
|
||||||
import { ExplorerDocNode } from '../../nodes/doc';
|
import { NavigationPanelDocNode } from '../../nodes/doc';
|
||||||
import { ExplorerFolderNode } from '../../nodes/folder';
|
import { NavigationPanelFolderNode } from '../../nodes/folder';
|
||||||
import { ExplorerTagNode } from '../../nodes/tag';
|
import { NavigationPanelTagNode } from '../../nodes/tag';
|
||||||
|
|
||||||
export const ExplorerFavorites = () => {
|
export const NavigationPanelFavorites = () => {
|
||||||
const { favoriteService, workspaceService, explorerService } = useServices({
|
const { favoriteService, workspaceService, navigationPanelService } =
|
||||||
|
useServices({
|
||||||
FavoriteService,
|
FavoriteService,
|
||||||
WorkspaceService,
|
WorkspaceService,
|
||||||
ExplorerService,
|
NavigationPanelService,
|
||||||
});
|
});
|
||||||
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
const explorerSection = explorerService.sections.favorites;
|
const navigationPanelSection = navigationPanelService.sections.favorites;
|
||||||
const favorites = useLiveData(favoriteService.favoriteList.sortedList$);
|
const favorites = useLiveData(favoriteService.favoriteList.sortedList$);
|
||||||
const isLoading = useLiveData(favoriteService.favoriteList.isLoading$);
|
const isLoading = useLiveData(favoriteService.favoriteList.isLoading$);
|
||||||
const { createPage } = usePageHelper(
|
const { createPage } = usePageHelper(
|
||||||
@@ -39,28 +38,28 @@ export const ExplorerFavorites = () => {
|
|||||||
newDoc.id,
|
newDoc.id,
|
||||||
favoriteService.favoriteList.indexAt('before')
|
favoriteService.favoriteList.indexAt('before')
|
||||||
);
|
);
|
||||||
explorerSection.setCollapsed(false);
|
navigationPanelSection.setCollapsed(false);
|
||||||
}, [createPage, explorerSection, favoriteService.favoriteList]);
|
}, [createPage, navigationPanelSection, favoriteService.favoriteList]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<CollapsibleSection
|
<CollapsibleSection
|
||||||
name="favorites"
|
name="favorites"
|
||||||
title={t['com.affine.rootAppSidebar.favorites']()}
|
title={t['com.affine.rootAppSidebar.favorites']()}
|
||||||
testId="explorer-favorites"
|
testId="navigation-panel-favorites"
|
||||||
headerTestId="explorer-favorite-category-divider"
|
headerTestId="navigation-panel-favorite-category-divider"
|
||||||
>
|
>
|
||||||
<ExplorerTreeRoot placeholder={isLoading ? 'Loading' : null}>
|
<NavigationPanelTreeRoot placeholder={isLoading ? 'Loading' : null}>
|
||||||
{favorites.map(favorite => (
|
{favorites.map(favorite => (
|
||||||
<FavoriteNode key={favorite.id} favorite={favorite} />
|
<FavoriteNode key={favorite.id} favorite={favorite} />
|
||||||
))}
|
))}
|
||||||
<AddItemPlaceholder
|
<AddItemPlaceholder
|
||||||
data-testid="explorer-bar-add-favorite-button"
|
data-testid="navigation-panel-bar-add-favorite-button"
|
||||||
data-event-props="$.navigationPanel.favorites.createDoc"
|
data-event-props="$.navigationPanel.favorites.createDoc"
|
||||||
data-event-args-control="addFavorite"
|
data-event-args-control="addFavorite"
|
||||||
onClick={handleCreateNewFavoriteDoc}
|
onClick={handleCreateNewFavoriteDoc}
|
||||||
label={t['New Page']()}
|
label={t['New Page']()}
|
||||||
/>
|
/>
|
||||||
</ExplorerTreeRoot>
|
</NavigationPanelTreeRoot>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -74,12 +73,12 @@ export const FavoriteNode = ({
|
|||||||
};
|
};
|
||||||
}) => {
|
}) => {
|
||||||
return favorite.type === 'doc' ? (
|
return favorite.type === 'doc' ? (
|
||||||
<ExplorerDocNode docId={favorite.id} />
|
<NavigationPanelDocNode docId={favorite.id} />
|
||||||
) : favorite.type === 'tag' ? (
|
) : favorite.type === 'tag' ? (
|
||||||
<ExplorerTagNode tagId={favorite.id} />
|
<NavigationPanelTagNode tagId={favorite.id} />
|
||||||
) : favorite.type === 'folder' ? (
|
) : favorite.type === 'folder' ? (
|
||||||
<ExplorerFolderNode nodeId={favorite.id} />
|
<NavigationPanelFolderNode nodeId={favorite.id} />
|
||||||
) : (
|
) : (
|
||||||
<ExplorerCollectionNode collectionId={favorite.id} />
|
<NavigationPanelCollectionNode collectionId={favorite.id} />
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1,8 +1,6 @@
|
|||||||
import { Skeleton } from '@affine/component';
|
import { Skeleton } from '@affine/component';
|
||||||
import {
|
import { NavigationPanelTreeRoot } from '@affine/core/desktop/components/navigation-panel';
|
||||||
ExplorerService,
|
import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
|
||||||
ExplorerTreeRoot,
|
|
||||||
} from '@affine/core/modules/explorer';
|
|
||||||
import { OrganizeService } from '@affine/core/modules/organize';
|
import { OrganizeService } from '@affine/core/modules/organize';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import track from '@affine/track';
|
import track from '@affine/track';
|
||||||
@@ -12,15 +10,15 @@ import { useCallback, useState } from 'react';
|
|||||||
|
|
||||||
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
||||||
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
||||||
import { ExplorerFolderNode } from '../../nodes/folder';
|
import { NavigationPanelFolderNode } from '../../nodes/folder';
|
||||||
import { FolderCreateTip, FolderRenameDialog } from '../../nodes/folder/dialog';
|
import { FolderCreateTip, FolderRenameDialog } from '../../nodes/folder/dialog';
|
||||||
|
|
||||||
export const ExplorerOrganize = () => {
|
export const NavigationPanelOrganize = () => {
|
||||||
const { organizeService, explorerService } = useServices({
|
const { organizeService, navigationPanelService } = useServices({
|
||||||
OrganizeService,
|
OrganizeService,
|
||||||
ExplorerService,
|
NavigationPanelService,
|
||||||
});
|
});
|
||||||
const explorerSection = explorerService.sections.organize;
|
const navigationPanelSection = navigationPanelService.sections.organize;
|
||||||
const [openNewFolderDialog, setOpenNewFolderDialog] = useState(false);
|
const [openNewFolderDialog, setOpenNewFolderDialog] = useState(false);
|
||||||
|
|
||||||
const t = useI18n();
|
const t = useI18n();
|
||||||
@@ -38,10 +36,10 @@ export const ExplorerOrganize = () => {
|
|||||||
rootFolder.indexAt('before')
|
rootFolder.indexAt('before')
|
||||||
);
|
);
|
||||||
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
|
track.$.navigationPanel.organize.createOrganizeItem({ type: 'folder' });
|
||||||
explorerSection.setCollapsed(false);
|
navigationPanelSection.setCollapsed(false);
|
||||||
return newFolderId;
|
return newFolderId;
|
||||||
},
|
},
|
||||||
[explorerSection, rootFolder]
|
[navigationPanelSection, rootFolder]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -50,17 +48,20 @@ export const ExplorerOrganize = () => {
|
|||||||
title={t['com.affine.rootAppSidebar.organize']()}
|
title={t['com.affine.rootAppSidebar.organize']()}
|
||||||
>
|
>
|
||||||
{/* TODO(@CatsJuice): Organize loading UI */}
|
{/* TODO(@CatsJuice): Organize loading UI */}
|
||||||
<ExplorerTreeRoot placeholder={isLoading ? <Skeleton /> : null}>
|
<NavigationPanelTreeRoot placeholder={isLoading ? <Skeleton /> : null}>
|
||||||
{folders.map(child => (
|
{folders.map(child => (
|
||||||
<ExplorerFolderNode key={child.id} nodeId={child.id as string} />
|
<NavigationPanelFolderNode
|
||||||
|
key={child.id}
|
||||||
|
nodeId={child.id as string}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
<AddItemPlaceholder
|
<AddItemPlaceholder
|
||||||
icon={<AddOrganizeIcon />}
|
icon={<AddOrganizeIcon />}
|
||||||
data-testid="explorer-bar-add-organize-button"
|
data-testid="navigation-panel-bar-add-organize-button"
|
||||||
label={t['com.affine.rootAppSidebar.organize.add-folder']()}
|
label={t['com.affine.rootAppSidebar.organize.add-folder']()}
|
||||||
onClick={() => setOpenNewFolderDialog(true)}
|
onClick={() => setOpenNewFolderDialog(true)}
|
||||||
/>
|
/>
|
||||||
</ExplorerTreeRoot>
|
</NavigationPanelTreeRoot>
|
||||||
<FolderRenameDialog
|
<FolderRenameDialog
|
||||||
open={openNewFolderDialog}
|
open={openNewFolderDialog}
|
||||||
onConfirm={handleCreateFolder}
|
onConfirm={handleCreateFolder}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ExplorerService } from '@affine/core/modules/explorer';
|
import { NavigationPanelTreeRoot } from '@affine/core/desktop/components/navigation-panel';
|
||||||
import { ExplorerTreeRoot } from '@affine/core/modules/explorer/views/tree';
|
import { NavigationPanelService } from '@affine/core/modules/navigation-panel';
|
||||||
import { TagService } from '@affine/core/modules/tag';
|
import { TagService } from '@affine/core/modules/tag';
|
||||||
import { useI18n } from '@affine/i18n';
|
import { useI18n } from '@affine/i18n';
|
||||||
import { track } from '@affine/track';
|
import { track } from '@affine/track';
|
||||||
@@ -9,7 +9,7 @@ import { useCallback, useState } from 'react';
|
|||||||
|
|
||||||
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
import { AddItemPlaceholder } from '../../layouts/add-item-placeholder';
|
||||||
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
import { CollapsibleSection } from '../../layouts/collapsible-section';
|
||||||
import { ExplorerTagNode } from '../../nodes/tag';
|
import { NavigationPanelTagNode } from '../../nodes/tag';
|
||||||
import { TagRenameDialog } from '../../nodes/tag/dialog';
|
import { TagRenameDialog } from '../../nodes/tag/dialog';
|
||||||
|
|
||||||
export const TagDesc = ({ input }: { input: string }) => {
|
export const TagDesc = ({ input }: { input: string }) => {
|
||||||
@@ -20,12 +20,12 @@ export const TagDesc = ({ input }: { input: string }) => {
|
|||||||
: t['com.affine.m.explorer.tag.new-tip-empty']();
|
: t['com.affine.m.explorer.tag.new-tip-empty']();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ExplorerTags = () => {
|
export const NavigationPanelTags = () => {
|
||||||
const { tagService, explorerService } = useServices({
|
const { tagService, navigationPanelService } = useServices({
|
||||||
TagService,
|
TagService,
|
||||||
ExplorerService,
|
NavigationPanelService,
|
||||||
});
|
});
|
||||||
const explorerSection = explorerService.sections.tags;
|
const navigationPanelSection = navigationPanelService.sections.tags;
|
||||||
const tags = useLiveData(tagService.tagList.tags$);
|
const tags = useLiveData(tagService.tagList.tags$);
|
||||||
const [showNewTagDialog, setShowNewTagDialog] = useState(false);
|
const [showNewTagDialog, setShowNewTagDialog] = useState(false);
|
||||||
|
|
||||||
@@ -36,9 +36,9 @@ export const ExplorerTags = () => {
|
|||||||
setShowNewTagDialog(false);
|
setShowNewTagDialog(false);
|
||||||
tagService.tagList.createTag(name, color);
|
tagService.tagList.createTag(name, color);
|
||||||
track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' });
|
track.$.navigationPanel.organize.createOrganizeItem({ type: 'tag' });
|
||||||
explorerSection.setCollapsed(false);
|
navigationPanelSection.setCollapsed(false);
|
||||||
},
|
},
|
||||||
[explorerSection, tagService]
|
[navigationPanelSection, tagService]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -46,13 +46,13 @@ export const ExplorerTags = () => {
|
|||||||
name="tags"
|
name="tags"
|
||||||
title={t['com.affine.rootAppSidebar.tags']()}
|
title={t['com.affine.rootAppSidebar.tags']()}
|
||||||
>
|
>
|
||||||
<ExplorerTreeRoot>
|
<NavigationPanelTreeRoot>
|
||||||
{tags.map(tag => (
|
{tags.map(tag => (
|
||||||
<ExplorerTagNode key={tag.id} tagId={tag.id} />
|
<NavigationPanelTagNode key={tag.id} tagId={tag.id} />
|
||||||
))}
|
))}
|
||||||
<AddItemPlaceholder
|
<AddItemPlaceholder
|
||||||
icon={<AddTagIcon />}
|
icon={<AddTagIcon />}
|
||||||
data-testid="explorer-add-tag-button"
|
data-testid="navigation-panel-add-tag-button"
|
||||||
onClick={() => setShowNewTagDialog(true)}
|
onClick={() => setShowNewTagDialog(true)}
|
||||||
label={t[
|
label={t[
|
||||||
'com.affine.rootAppSidebar.explorer.tag-section-add-tooltip'
|
'com.affine.rootAppSidebar.explorer.tag-section-add-tooltip'
|
||||||
@@ -65,7 +65,7 @@ export const ExplorerTags = () => {
|
|||||||
enableAnimation
|
enableAnimation
|
||||||
descRenderer={TagDesc}
|
descRenderer={TagDesc}
|
||||||
/>
|
/>
|
||||||
</ExplorerTreeRoot>
|
</NavigationPanelTreeRoot>
|
||||||
</CollapsibleSection>
|
</CollapsibleSection>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { MobileMenu } from '@affine/component';
|
import { MobileMenu } from '@affine/component';
|
||||||
import type { BaseExplorerTreeNodeProps } from '@affine/core/modules/explorer';
|
import {
|
||||||
import { ExplorerTreeContext } from '@affine/core/modules/explorer';
|
type BaseNavigationPanelTreeNodeProps,
|
||||||
|
NavigationPanelTreeContext,
|
||||||
|
} from '@affine/core/desktop/components/navigation-panel';
|
||||||
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
import { WorkbenchLink } from '@affine/core/modules/workbench';
|
||||||
import { extractEmojiIcon } from '@affine/core/utils';
|
import { extractEmojiIcon } from '@affine/core/utils';
|
||||||
import { ArrowDownSmallIcon, MoreHorizontalIcon } from '@blocksuite/icons/rc';
|
import { ArrowDownSmallIcon, MoreHorizontalIcon } from '@blocksuite/icons/rc';
|
||||||
@@ -18,9 +20,10 @@ import {
|
|||||||
import { SwipeMenu } from '../../swipe-menu';
|
import { SwipeMenu } from '../../swipe-menu';
|
||||||
import * as styles from './node.css';
|
import * as styles from './node.css';
|
||||||
|
|
||||||
interface ExplorerTreeNodeProps extends BaseExplorerTreeNodeProps {}
|
interface NavigationPanelTreeNodeProps
|
||||||
|
extends BaseNavigationPanelTreeNodeProps {}
|
||||||
|
|
||||||
export const ExplorerTreeNode = ({
|
export const NavigationPanelTreeNode = ({
|
||||||
children,
|
children,
|
||||||
icon: Icon,
|
icon: Icon,
|
||||||
name: rawName,
|
name: rawName,
|
||||||
@@ -37,8 +40,8 @@ export const ExplorerTreeNode = ({
|
|||||||
childrenPlaceholder,
|
childrenPlaceholder,
|
||||||
linkComponent: LinkComponent = WorkbenchLink,
|
linkComponent: LinkComponent = WorkbenchLink,
|
||||||
...otherProps
|
...otherProps
|
||||||
}: ExplorerTreeNodeProps) => {
|
}: NavigationPanelTreeNodeProps) => {
|
||||||
const context = useContext(ExplorerTreeContext);
|
const context = useContext(NavigationPanelTreeContext);
|
||||||
const level = context?.level ?? 0;
|
const level = context?.level ?? 0;
|
||||||
// If no onClick or to is provided, clicking on the node will toggle the collapse state
|
// If no onClick or to is provided, clicking on the node will toggle the collapse state
|
||||||
const clickForCollapse = !onClick && !to && !disabled;
|
const clickForCollapse = !onClick && !to && !disabled;
|
||||||
@@ -141,7 +144,7 @@ export const ExplorerTreeNode = ({
|
|||||||
<div
|
<div
|
||||||
data-disabled={disabled}
|
data-disabled={disabled}
|
||||||
onClick={handleCollapsedChange}
|
onClick={handleCollapsedChange}
|
||||||
data-testid="explorer-collapsed-button"
|
data-testid="navigation-panel-collapsed-button"
|
||||||
className={styles.collapsedIconContainer}
|
className={styles.collapsedIconContainer}
|
||||||
>
|
>
|
||||||
<ArrowDownSmallIcon
|
<ArrowDownSmallIcon
|
||||||
@@ -197,9 +200,9 @@ export const ExplorerTreeNode = ({
|
|||||||
<div className={styles.collapseContentPlaceholder}>
|
<div className={styles.collapseContentPlaceholder}>
|
||||||
{childCount === 0 && !collapsed && childrenPlaceholder}
|
{childCount === 0 && !collapsed && childrenPlaceholder}
|
||||||
</div>
|
</div>
|
||||||
<ExplorerTreeContext.Provider value={contextValue}>
|
<NavigationPanelTreeContext.Provider value={contextValue}>
|
||||||
{collapsed ? null : children}
|
{collapsed ? null : children}
|
||||||
</ExplorerTreeContext.Provider>
|
</NavigationPanelTreeContext.Provider>
|
||||||
</Collapsible.Content>
|
</Collapsible.Content>
|
||||||
</Collapsible.Root>
|
</Collapsible.Root>
|
||||||
);
|
);
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { style } from '@vanilla-extract/css';
|
||||||
|
|
||||||
|
export const placeholder = style({
|
||||||
|
display: 'none',
|
||||||
|
selectors: {
|
||||||
|
'&:only-child': {
|
||||||
|
display: 'initial',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -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 (
|
||||||
|
// <div> is for placeholder:last-child selector
|
||||||
|
<div>
|
||||||
|
{/* For lastInGroup check, the placeholder must be placed above all children in the dom */}
|
||||||
|
<div className={styles.placeholder}>
|
||||||
|
{childCount === 0 && placeholder}
|
||||||
|
</div>
|
||||||
|
<NavigationPanelTreeContext.Provider value={contextValue}>
|
||||||
|
{children}
|
||||||
|
</NavigationPanelTreeContext.Provider>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -2,11 +2,11 @@ import { SafeArea, useThemeColorV2 } from '@affine/component';
|
|||||||
|
|
||||||
import { AppTabs } from '../../components';
|
import { AppTabs } from '../../components';
|
||||||
import {
|
import {
|
||||||
ExplorerCollections,
|
NavigationPanelCollections,
|
||||||
ExplorerFavorites,
|
NavigationPanelFavorites,
|
||||||
ExplorerOrganize,
|
NavigationPanelOrganize,
|
||||||
ExplorerTags,
|
NavigationPanelTags,
|
||||||
} from '../../components/explorer';
|
} from '../../components/navigation';
|
||||||
import { HomeHeader, RecentDocs } from '../../views';
|
import { HomeHeader, RecentDocs } from '../../views';
|
||||||
|
|
||||||
export const Component = () => {
|
export const Component = () => {
|
||||||
@@ -25,10 +25,10 @@ export const Component = () => {
|
|||||||
padding: '0 8px 32px 8px',
|
padding: '0 8px 32px 8px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ExplorerFavorites />
|
<NavigationPanelFavorites />
|
||||||
<ExplorerOrganize />
|
<NavigationPanelOrganize />
|
||||||
<ExplorerCollections />
|
<NavigationPanelCollections />
|
||||||
<ExplorerTags />
|
<NavigationPanelTags />
|
||||||
</div>
|
</div>
|
||||||
</SafeArea>
|
</SafeArea>
|
||||||
<AppTabs />
|
<AppTabs />
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import { useService } from '@toeverything/infra';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { DocCard } from '../../components/doc-card';
|
import { DocCard } from '../../components/doc-card';
|
||||||
import { CollapsibleSection } from '../../components/explorer';
|
import { CollapsibleSection } from '../../components/navigation';
|
||||||
import * as styles from './styles.css';
|
import * as styles from './styles.css';
|
||||||
|
|
||||||
export const RecentDocs = ({ max = 5 }: { max?: number }) => {
|
export const RecentDocs = ({ max = 5 }: { max?: number }) => {
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
# explorer
|
|
||||||
|
|
||||||
file manager in app left sidebar
|
|
||||||
@@ -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]);
|
|
||||||
}
|
|
||||||
@@ -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<HTMLDivElement> {
|
|
||||||
icon:
|
|
||||||
| ((props: SVGProps<SVGSVGElement>) => JSX.Element)
|
|
||||||
| ReactElement<SVGAttributes<SVGElement>>;
|
|
||||||
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<HTMLDivElement>
|
|
||||||
) {
|
|
||||||
const icon =
|
|
||||||
typeof Icon === 'function' ? (
|
|
||||||
<Icon className={styles.icon} />
|
|
||||||
) : (
|
|
||||||
cloneElement(Icon, { className: styles.icon })
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={clsx(styles.content, className)} ref={ref} {...attrs}>
|
|
||||||
<div className={styles.iconWrapper}>{icon}</div>
|
|
||||||
<div data-testid={messageTestId} className={styles.message}>
|
|
||||||
{message}
|
|
||||||
</div>
|
|
||||||
{actionText ? (
|
|
||||||
<Button className={styles.newButton} onClick={onActionClick}>
|
|
||||||
{actionText}
|
|
||||||
</Button>
|
|
||||||
) : null}
|
|
||||||
{children}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@@ -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<AffineDNDData>['canDrop'] =
|
|
||||||
data => {
|
|
||||||
return data.source.data.entity?.type
|
|
||||||
? isFavoriteSupportType(data.source.data.entity.type)
|
|
||||||
: false;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const favoriteChildrenCanDrop: DropTargetOptions<AffineDNDData>['canDrop'] =
|
|
||||||
// Same as favoriteRootCanDrop
|
|
||||||
data => favoriteRootCanDrop(data);
|
|
||||||
@@ -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<AffineDNDData>['canDrop'] =
|
|
||||||
data => {
|
|
||||||
const type = data.source.data.entity?.type;
|
|
||||||
return !!type && isOrganizeSupportType(type);
|
|
||||||
};
|
|
||||||
@@ -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<ExplorerTreeContextData | null>(null);
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
export { DropEffect } from './drop-effect';
|
|
||||||
export type {
|
|
||||||
ExplorerTreeNodeDropEffect,
|
|
||||||
ExplorerTreeNodeDropEffectData,
|
|
||||||
} from './node';
|
|
||||||
export { ExplorerTreeNode } from './node';
|
|
||||||
export { ExplorerTreeRoot } from './root';
|
|
||||||
@@ -20,7 +20,6 @@ import { configureDocLinksModule } from './doc-link';
|
|||||||
import { configureDocsSearchModule } from './docs-search';
|
import { configureDocsSearchModule } from './docs-search';
|
||||||
import { configureEditorModule } from './editor';
|
import { configureEditorModule } from './editor';
|
||||||
import { configureEditorSettingModule } from './editor-setting';
|
import { configureEditorSettingModule } from './editor-setting';
|
||||||
import { configureExplorerModule } from './explorer';
|
|
||||||
import { configureFavoriteModule } from './favorite';
|
import { configureFavoriteModule } from './favorite';
|
||||||
import { configureFeatureFlagModule } from './feature-flag';
|
import { configureFeatureFlagModule } from './feature-flag';
|
||||||
import { configureGlobalContextModule } from './global-context';
|
import { configureGlobalContextModule } from './global-context';
|
||||||
@@ -32,6 +31,7 @@ import { configureJournalModule } from './journal';
|
|||||||
import { configureLifecycleModule } from './lifecycle';
|
import { configureLifecycleModule } from './lifecycle';
|
||||||
import { configureMediaModule } from './media';
|
import { configureMediaModule } from './media';
|
||||||
import { configureNavigationModule } from './navigation';
|
import { configureNavigationModule } from './navigation';
|
||||||
|
import { configureNavigationPanelModule } from './navigation-panel';
|
||||||
import { configureNotificationModule } from './notification';
|
import { configureNotificationModule } from './notification';
|
||||||
import { configureOpenInApp } from './open-in-app';
|
import { configureOpenInApp } from './open-in-app';
|
||||||
import { configureOrganizeModule } from './organize';
|
import { configureOrganizeModule } from './organize';
|
||||||
@@ -82,7 +82,7 @@ export function configureCommonModules(framework: Framework) {
|
|||||||
configureDocLinksModule(framework);
|
configureDocLinksModule(framework);
|
||||||
configureOrganizeModule(framework);
|
configureOrganizeModule(framework);
|
||||||
configureFavoriteModule(framework);
|
configureFavoriteModule(framework);
|
||||||
configureExplorerModule(framework);
|
configureNavigationPanelModule(framework);
|
||||||
configureThemeEditorModule(framework);
|
configureThemeEditorModule(framework);
|
||||||
configureEditorModule(framework);
|
configureEditorModule(framework);
|
||||||
configureSystemFontFamilyModule(framework);
|
configureSystemFontFamilyModule(framework);
|
||||||
|
|||||||
@@ -15,7 +15,9 @@ const DEFAULT_COLLAPSABLE_STATE: Record<CollapsibleSectionName, boolean> = {
|
|||||||
others: false,
|
others: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export class ExplorerSection extends Entity<{ name: CollapsibleSectionName }> {
|
export class NavigationPanelSection extends Entity<{
|
||||||
|
name: CollapsibleSectionName;
|
||||||
|
}> {
|
||||||
name: CollapsibleSectionName = this.props.name;
|
name: CollapsibleSectionName = this.props.name;
|
||||||
key = `explorer.section.${this.name}`;
|
key = `explorer.section.${this.name}`;
|
||||||
defaultValue = DEFAULT_COLLAPSABLE_STATE[this.name];
|
defaultValue = DEFAULT_COLLAPSABLE_STATE[this.name];
|
||||||
15
packages/frontend/core/src/modules/navigation-panel/index.ts
Normal file
15
packages/frontend/core/src/modules/navigation-panel/index.ts
Normal file
@@ -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]);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Service } from '@toeverything/infra';
|
import { Service } from '@toeverything/infra';
|
||||||
|
|
||||||
import { ExplorerSection } from '../entities/explore-section';
|
import { NavigationPanelSection } from '../entities/navigation-panel-section';
|
||||||
import type { CollapsibleSectionName } from '../types';
|
import type { CollapsibleSectionName } from '../types';
|
||||||
|
|
||||||
const allSectionName: Array<CollapsibleSectionName> = [
|
const allSectionName: Array<CollapsibleSectionName> = [
|
||||||
@@ -14,12 +14,12 @@ const allSectionName: Array<CollapsibleSectionName> = [
|
|||||||
'others',
|
'others',
|
||||||
];
|
];
|
||||||
|
|
||||||
export class ExplorerService extends Service {
|
export class NavigationPanelService extends Service {
|
||||||
readonly sections = allSectionName.reduce(
|
readonly sections = allSectionName.reduce(
|
||||||
(prev, name) =>
|
(prev, name) =>
|
||||||
Object.assign(prev, {
|
Object.assign(prev, {
|
||||||
[name]: this.framework.createEntity(ExplorerSection, { name }),
|
[name]: this.framework.createEntity(NavigationPanelSection, { name }),
|
||||||
}),
|
}),
|
||||||
{} as Record<CollapsibleSectionName, ExplorerSection>
|
{} as Record<CollapsibleSectionName, NavigationPanelSection>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -27,28 +27,28 @@ export interface AffineDNDData extends DNDData {
|
|||||||
entity?: AffineDNDEntity;
|
entity?: AffineDNDEntity;
|
||||||
from?:
|
from?:
|
||||||
| {
|
| {
|
||||||
at: 'explorer:organize:folder-node';
|
at: 'navigation-panel:organize:folder-node';
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
at: 'explorer:collection:list';
|
at: 'navigation-panel:collection:list';
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
at: 'explorer:doc:linked-docs';
|
at: 'navigation-panel:doc:linked-docs';
|
||||||
docId: string;
|
docId: string;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
at: 'explorer:collection:filtered-docs';
|
at: 'navigation-panel:collection:filtered-docs';
|
||||||
collectionId: string;
|
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';
|
at: 'all-docs:list';
|
||||||
@@ -60,10 +60,10 @@ export interface AffineDNDData extends DNDData {
|
|||||||
at: 'all-collections:list';
|
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';
|
at: 'app-header:tabs';
|
||||||
@@ -103,28 +103,28 @@ export interface AffineDNDData extends DNDData {
|
|||||||
};
|
};
|
||||||
dropTarget:
|
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: 'app-sidebar:trash';
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
at: 'explorer:tag';
|
at: 'navigation-panel:tag';
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
at: 'app-header:tabs';
|
at: 'app-header:tabs';
|
||||||
|
|||||||
@@ -241,8 +241,8 @@ export class EditorUtils {
|
|||||||
public static async clearAllCollections(page: Page) {
|
public static async clearAllCollections(page: Page) {
|
||||||
while (true) {
|
while (true) {
|
||||||
const collection = await page
|
const collection = await page
|
||||||
.getByTestId('explorer-collections')
|
.getByTestId('navigation-panel-collections')
|
||||||
.locator('[data-testid^="explorer-collection-"]')
|
.locator('[data-testid^="navigation-panel-collection-"]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (!(await collection.isVisible())) {
|
if (!(await collection.isVisible())) {
|
||||||
@@ -252,7 +252,7 @@ export class EditorUtils {
|
|||||||
const collectionContent = await collection.locator('div').first();
|
const collectionContent = await collection.locator('div').first();
|
||||||
await collectionContent.hover();
|
await collectionContent.hover();
|
||||||
const more = await collectionContent.getByTestId(
|
const more = await collectionContent.getByTestId(
|
||||||
'explorer-tree-node-operation-button'
|
'navigation-panel-tree-node-operation-button'
|
||||||
);
|
);
|
||||||
await more.click();
|
await more.click();
|
||||||
await page.getByTestId('collection-delete-button').click();
|
await page.getByTestId('collection-delete-button').click();
|
||||||
@@ -263,8 +263,8 @@ export class EditorUtils {
|
|||||||
public static async clearAllTags(page: Page) {
|
public static async clearAllTags(page: Page) {
|
||||||
while (true) {
|
while (true) {
|
||||||
const tag = await page
|
const tag = await page
|
||||||
.getByTestId('explorer-tags')
|
.getByTestId('navigation-panel-tags')
|
||||||
.locator('[data-testid^="explorer-tag-"]')
|
.locator('[data-testid^="navigation-panel-tag-"]')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
if (!(await tag.isVisible())) {
|
if (!(await tag.isVisible())) {
|
||||||
@@ -274,7 +274,7 @@ export class EditorUtils {
|
|||||||
const tagContent = await tag.locator('div').first();
|
const tagContent = await tag.locator('div').first();
|
||||||
await tagContent.hover();
|
await tagContent.hover();
|
||||||
const more = await tagContent.getByTestId(
|
const more = await tagContent.getByTestId(
|
||||||
'explorer-tree-node-operation-button'
|
'navigation-panel-tree-node-operation-button'
|
||||||
);
|
);
|
||||||
await more.click();
|
await more.click();
|
||||||
await page.getByTestId('tag-delete-button').click();
|
await page.getByTestId('tag-delete-button').click();
|
||||||
@@ -288,7 +288,9 @@ export class EditorUtils {
|
|||||||
docContent: string
|
docContent: string
|
||||||
) {
|
) {
|
||||||
// Create collection
|
// 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');
|
const input = await page.getByTestId('prompt-modal-input');
|
||||||
await input.focus();
|
await input.focus();
|
||||||
await input.pressSequentially(collectionName);
|
await input.pressSequentially(collectionName);
|
||||||
@@ -318,9 +320,9 @@ export class EditorUtils {
|
|||||||
docContent: string
|
docContent: string
|
||||||
) {
|
) {
|
||||||
// Create tag
|
// Create tag
|
||||||
const tags = await page.getByTestId('explorer-tags');
|
const tags = await page.getByTestId('navigation-panel-tags');
|
||||||
await tags.hover();
|
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');
|
const input = await page.getByTestId('rename-modal-input');
|
||||||
await input.focus();
|
await input.focus();
|
||||||
await input.pressSequentially(tagName);
|
await input.pressSequentially(tagName);
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ test('can sync collections between different browser', async ({
|
|||||||
page
|
page
|
||||||
);
|
);
|
||||||
await enableCloudWorkspace(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');
|
const title = page.getByTestId('prompt-modal-input');
|
||||||
await title.isVisible();
|
await title.isVisible();
|
||||||
await title.fill('test collection');
|
await title.fill('test collection');
|
||||||
@@ -122,7 +122,7 @@ test('can sync collections between different browser', async ({
|
|||||||
const page2 = await context.newPage();
|
const page2 = await context.newPage();
|
||||||
await loginUser(page2, user);
|
await loginUser(page2, user);
|
||||||
await page2.goto(page.url());
|
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 collections.getByTestId('category-divider-collapse-button').click();
|
||||||
await expect(collections.getByText('test collection')).toBeVisible();
|
await expect(collections.getByText('test collection')).toBeVisible();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,21 +109,24 @@ test('should transform local favorites data', async ({ page }) => {
|
|||||||
},
|
},
|
||||||
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 clickPageModeButton(page);
|
||||||
await waitForEmptyEditor(page);
|
await waitForEmptyEditor(page);
|
||||||
|
|
||||||
await getBlockSuiteEditorTitle(page).fill('this is a new fav page');
|
await getBlockSuiteEditorTitle(page).fill('this is a new fav page');
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
.getByTestId('explorer-favorites')
|
.getByTestId('navigation-panel-favorites')
|
||||||
.locator('[draggable] >> text=this is a new fav page')
|
.locator('[draggable] >> text=this is a new fav page')
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
|
|
||||||
await enableCloudWorkspace(page);
|
await enableCloudWorkspace(page);
|
||||||
await expect(
|
await expect(
|
||||||
page
|
page
|
||||||
.getByTestId('explorer-favorites')
|
.getByTestId('navigation-panel-favorites')
|
||||||
.locator('[draggable] >> text=this is a new fav page')
|
.locator('[draggable] >> text=this is a new fav page')
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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();
|
await page.getByTestId('all-pages').click();
|
||||||
const favoriteListItemInSidebar = page.locator(
|
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(
|
expect(await favoriteListItemInSidebar.textContent()).toBe(
|
||||||
'this is a new page'
|
'this is a new page'
|
||||||
);
|
);
|
||||||
await favoriteListItemInSidebar.hover();
|
await favoriteListItemInSidebar.hover();
|
||||||
await favoriteListItemInSidebar
|
await favoriteListItemInSidebar
|
||||||
.getByTestId('explorer-tree-node-operation-button')
|
.getByTestId('navigation-panel-tree-node-operation-button')
|
||||||
.click();
|
.click();
|
||||||
const infoBtn = page.getByText('View Info');
|
const infoBtn = page.getByText('View Info');
|
||||||
await infoBtn.click();
|
await infoBtn.click();
|
||||||
|
|||||||
@@ -27,23 +27,27 @@ const dragToFavourites = async (
|
|||||||
id: string,
|
id: string,
|
||||||
type: 'doc' | 'collection' | 'tag' | 'folder' = 'doc'
|
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);
|
await dragTo(page, dragItem, favourites);
|
||||||
const item = page
|
const item = page
|
||||||
.getByTestId(`explorer-favorites`)
|
.getByTestId(`navigation-panel-favorites`)
|
||||||
.locator(`[data-testid="explorer-${type}-${id}"]`);
|
.locator(`[data-testid="navigation-panel-${type}-${id}"]`);
|
||||||
await expect(item).toBeVisible();
|
await expect(item).toBeVisible();
|
||||||
return item;
|
return item;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createCollection = async (page: Page, name: string) => {
|
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');
|
const input = page.getByTestId('prompt-modal-input');
|
||||||
await expect(input).toBeVisible();
|
await expect(input).toBeVisible();
|
||||||
await input.fill(name);
|
await input.fill(name);
|
||||||
await page.getByTestId('prompt-modal-confirm').click();
|
await page.getByTestId('prompt-modal-confirm').click();
|
||||||
const newCollectionId = getCurrentCollectionIdFromUrl(page);
|
const newCollectionId = getCurrentCollectionIdFromUrl(page);
|
||||||
const collection = page.getByTestId(`explorer-collection-${newCollectionId}`);
|
const collection = page.getByTestId(
|
||||||
|
`navigation-panel-collection-${newCollectionId}`
|
||||||
|
);
|
||||||
await expect(collection).toBeVisible();
|
await expect(collection).toBeVisible();
|
||||||
return collection;
|
return collection;
|
||||||
};
|
};
|
||||||
@@ -57,7 +61,9 @@ const dragToCollection = async (page: Page, dragItem: Locator) => {
|
|||||||
await clickSideBarAllPageButton(page);
|
await clickSideBarAllPageButton(page);
|
||||||
await dragTo(page, dragItem, collection);
|
await dragTo(page, dragItem, collection);
|
||||||
await page.waitForTimeout(500);
|
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();
|
await expect(collectionPage).toBeVisible();
|
||||||
return collectionPage;
|
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
|
// assert the order of the items in favourites
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('explorer-favorites').locator('[draggable]')
|
page.getByTestId('navigation-panel-favorites').locator('[draggable]')
|
||||||
).toHaveCount(3);
|
).toHaveCount(3);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('explorer-favorites').locator('[draggable]').first()
|
page
|
||||||
|
.getByTestId('navigation-panel-favorites')
|
||||||
|
.locator('[draggable]')
|
||||||
|
.first()
|
||||||
).toHaveText('test collection');
|
).toHaveText('test collection');
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('explorer-favorites').locator('[draggable]').last()
|
page.getByTestId('navigation-panel-favorites').locator('[draggable]').last()
|
||||||
).toHaveText(title0);
|
).toHaveText(title0);
|
||||||
|
|
||||||
// drag the first item to the last
|
// drag the first item to the last
|
||||||
const firstItem = page
|
const firstItem = page
|
||||||
.getByTestId('explorer-favorites')
|
.getByTestId('navigation-panel-favorites')
|
||||||
.locator('[draggable]')
|
.locator('[draggable]')
|
||||||
.first();
|
.first();
|
||||||
const lastItem = page
|
const lastItem = page
|
||||||
.getByTestId('explorer-favorites')
|
.getByTestId('navigation-panel-favorites')
|
||||||
.locator('[draggable]')
|
.locator('[draggable]')
|
||||||
.last();
|
.last();
|
||||||
await dragTo(page, firstItem, lastItem, 'bottom');
|
await dragTo(page, firstItem, lastItem, 'bottom');
|
||||||
|
|
||||||
// now check the order again
|
// now check the order again
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('explorer-favorites').locator('[draggable]')
|
page.getByTestId('navigation-panel-favorites').locator('[draggable]')
|
||||||
).toHaveCount(3);
|
).toHaveCount(3);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('explorer-favorites').locator('[draggable]').first()
|
page
|
||||||
|
.getByTestId('navigation-panel-favorites')
|
||||||
|
.locator('[draggable]')
|
||||||
|
.first()
|
||||||
).toHaveText(title1);
|
).toHaveText(title1);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId('explorer-favorites').locator('[draggable]').last()
|
page.getByTestId('navigation-panel-favorites').locator('[draggable]').last()
|
||||||
).toHaveText('test collection');
|
).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();
|
await page.getByTestId('pin-button').click();
|
||||||
const pageId = getCurrentDocIdFromUrl(page);
|
const pageId = getCurrentDocIdFromUrl(page);
|
||||||
const item = page
|
const item = page
|
||||||
.getByTestId(`explorer-favorites`)
|
.getByTestId(`navigation-panel-favorites`)
|
||||||
.locator(`[data-testid="explorer-doc-${pageId}"]`);
|
.locator(`[data-testid="navigation-panel-doc-${pageId}"]`);
|
||||||
await expect(item).toBeVisible();
|
await expect(item).toBeVisible();
|
||||||
|
|
||||||
// drag item into blocksuite editor
|
// 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();
|
await page.getByTestId('pin-button').click();
|
||||||
const pageId = getCurrentDocIdFromUrl(page);
|
const pageId = getCurrentDocIdFromUrl(page);
|
||||||
const item = page
|
const item = page
|
||||||
.getByTestId(`explorer-favorites`)
|
.getByTestId(`navigation-panel-favorites`)
|
||||||
.locator(`[data-testid="explorer-doc-${pageId}"]`);
|
.locator(`[data-testid="navigation-panel-doc-${pageId}"]`);
|
||||||
await expect(item).toBeVisible();
|
await expect(item).toBeVisible();
|
||||||
|
|
||||||
await clickNewPageButton(page, 'edgeless page');
|
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();
|
await page.getByTestId('pin-button').click();
|
||||||
const pageId = getCurrentDocIdFromUrl(page);
|
const pageId = getCurrentDocIdFromUrl(page);
|
||||||
const item = page
|
const item = page
|
||||||
.getByTestId(`explorer-favorites`)
|
.getByTestId(`navigation-panel-favorites`)
|
||||||
.locator(`[data-testid="explorer-doc-${pageId}"]`);
|
.locator(`[data-testid="navigation-panel-doc-${pageId}"]`);
|
||||||
await expect(item).toBeVisible();
|
await expect(item).toBeVisible();
|
||||||
|
|
||||||
await clickNewPageButton(page, 'edgeless page');
|
await clickNewPageButton(page, 'edgeless page');
|
||||||
|
|||||||
@@ -67,26 +67,34 @@ const createAndPinCollection = async (
|
|||||||
test('Show collections items in sidebar', async ({ page }) => {
|
test('Show collections items in sidebar', async ({ page }) => {
|
||||||
await removeOnboardingPages(page);
|
await removeOnboardingPages(page);
|
||||||
await createAndPinCollection(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();
|
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);
|
await expect(items).toHaveCount(1);
|
||||||
const first = items.first();
|
const first = items.first();
|
||||||
expect(await first.textContent()).toBe('test collection');
|
expect(await first.textContent()).toBe('test collection');
|
||||||
await first.getByTestId('explorer-collapsed-button').click();
|
await first.getByTestId('navigation-panel-collapsed-button').click();
|
||||||
const collectionPage = first.locator('[data-testid^="explorer-doc-"]').nth(0);
|
const collectionPage = first
|
||||||
|
.locator('[data-testid^="navigation-panel-doc-"]')
|
||||||
|
.nth(0);
|
||||||
expect(await collectionPage.textContent()).toBe('test page');
|
expect(await collectionPage.textContent()).toBe('test page');
|
||||||
await collectionPage.hover();
|
await collectionPage.hover();
|
||||||
await collectionPage
|
await collectionPage
|
||||||
.getByTestId('explorer-tree-node-operation-button')
|
.getByTestId('navigation-panel-tree-node-operation-button')
|
||||||
.click();
|
.click();
|
||||||
const deletePage = page.getByText('Move to trash');
|
const deletePage = page.getByText('Move to trash');
|
||||||
await deletePage.click();
|
await deletePage.click();
|
||||||
await page.getByTestId('confirm-modal-confirm').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)
|
// 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.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');
|
const deleteCollection = page.getByText('Delete');
|
||||||
await deleteCollection.click();
|
await deleteCollection.click();
|
||||||
await page.waitForTimeout(50);
|
await page.waitForTimeout(50);
|
||||||
@@ -109,13 +117,17 @@ test('Show collections items in sidebar', async ({ page }) => {
|
|||||||
test('edit collection', async ({ page }) => {
|
test('edit collection', async ({ page }) => {
|
||||||
await removeOnboardingPages(page);
|
await removeOnboardingPages(page);
|
||||||
await createAndPinCollection(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();
|
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);
|
expect(await items.count()).toBe(1);
|
||||||
const first = items.first();
|
const first = items.first();
|
||||||
await first.hover();
|
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');
|
const editCollection = page.getByText('Rename');
|
||||||
await editCollection.click();
|
await editCollection.click();
|
||||||
await page.getByTestId('rename-modal-input').fill('123');
|
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 }) => {
|
test('edit collection and change filter date', async ({ page }) => {
|
||||||
await removeOnboardingPages(page);
|
await removeOnboardingPages(page);
|
||||||
await createAndPinCollection(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();
|
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);
|
expect(await items.count()).toBe(1);
|
||||||
const first = items.first();
|
const first = items.first();
|
||||||
await first.hover();
|
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');
|
const editCollection = page.getByText('Rename');
|
||||||
await editCollection.click();
|
await editCollection.click();
|
||||||
await page.getByTestId('rename-modal-input').fill('123');
|
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');
|
const cell = page.getByTestId('page-list-item-title').getByText('test page');
|
||||||
await expect(cell).toBeVisible();
|
await expect(cell).toBeVisible();
|
||||||
await page
|
await page
|
||||||
.getByTestId('explorer-collections')
|
.getByTestId('navigation-panel-collections')
|
||||||
.getByTestId('category-divider-collapse-button')
|
.getByTestId('category-divider-collapse-button')
|
||||||
.click();
|
.click();
|
||||||
const nullCollection = page.getByTestId(
|
const nullCollection = page.getByTestId(
|
||||||
'slider-bar-collection-empty-message'
|
'slider-bar-collection-empty-message'
|
||||||
);
|
);
|
||||||
await expect(nullCollection).toBeVisible();
|
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');
|
const title = page.getByTestId('prompt-modal-input');
|
||||||
await expect(title).toBeVisible();
|
await expect(title).toBeVisible();
|
||||||
await title.fill('test collection');
|
await title.fill('test collection');
|
||||||
await page.getByTestId('prompt-modal-confirm').click();
|
await page.getByTestId('prompt-modal-confirm').click();
|
||||||
await page.waitForTimeout(100);
|
await page.waitForTimeout(100);
|
||||||
const collections = page.getByTestId('explorer-collections');
|
const collections = page.getByTestId('navigation-panel-collections');
|
||||||
const items = collections.locator('[data-testid^="explorer-collection-"]');
|
const items = collections.locator(
|
||||||
|
'[data-testid^="navigation-panel-collection-"]'
|
||||||
|
);
|
||||||
expect(await items.count()).toBe(1);
|
expect(await items.count()).toBe(1);
|
||||||
await expect(nullCollection).not.toBeVisible();
|
await expect(nullCollection).not.toBeVisible();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ test('Show favorite items in sidebar', async ({ page, workspace }) => {
|
|||||||
const favoriteBtn = page.getByTestId('editor-option-menu-favorite');
|
const favoriteBtn = page.getByTestId('editor-option-menu-favorite');
|
||||||
await favoriteBtn.click();
|
await favoriteBtn.click();
|
||||||
const favoriteListItemInSidebar = page.getByTestId(
|
const favoriteListItemInSidebar = page.getByTestId(
|
||||||
'explorer-doc-' + newPageId
|
'navigation-panel-doc-' + newPageId
|
||||||
);
|
);
|
||||||
expect(await favoriteListItemInSidebar.textContent()).toBe(
|
expect(await favoriteListItemInSidebar.textContent()).toBe(
|
||||||
'this is a new page to favorite'
|
'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');
|
const favoriteBtn = page.getByTestId('editor-option-menu-favorite');
|
||||||
await favoriteBtn.click();
|
await favoriteBtn.click();
|
||||||
|
|
||||||
const favItemTestId = 'explorer-doc-' + newPageId;
|
const favItemTestId = 'navigation-panel-doc-' + newPageId;
|
||||||
|
|
||||||
const favoriteListItemInSidebar = page.getByTestId(favItemTestId);
|
const favoriteListItemInSidebar = page.getByTestId(favItemTestId);
|
||||||
expect(await favoriteListItemInSidebar.textContent()).toBe(
|
expect(await favoriteListItemInSidebar.textContent()).toBe(
|
||||||
@@ -66,14 +66,14 @@ test('Show favorite reference in sidebar', async ({ page, workspace }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const collapseButton = favoriteListItemInSidebar.getByTestId(
|
const collapseButton = favoriteListItemInSidebar.getByTestId(
|
||||||
'explorer-collapsed-button'
|
'navigation-panel-collapsed-button'
|
||||||
);
|
);
|
||||||
|
|
||||||
await expect(collapseButton).toBeVisible();
|
await expect(collapseButton).toBeVisible();
|
||||||
await collapseButton.click();
|
await collapseButton.click();
|
||||||
await expect(
|
await expect(
|
||||||
favoriteListItemInSidebar.locator(
|
favoriteListItemInSidebar.locator(
|
||||||
'[data-testid^="explorer-doc-"]:has-text("Another page")'
|
'[data-testid^="navigation-panel-doc-"]:has-text("Another page")'
|
||||||
)
|
)
|
||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
const currentWorkspace = await workspace.current();
|
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 anotherPageId = getCurrentDocIdFromUrl(page);
|
||||||
|
|
||||||
const favItemTestId = 'explorer-doc-' + newPageId;
|
const favItemTestId = 'navigation-panel-doc-' + newPageId;
|
||||||
|
|
||||||
await expect(page.getByTestId(favItemTestId)).toHaveText(
|
await expect(page.getByTestId(favItemTestId)).toHaveText(
|
||||||
'this is a new page to favorite'
|
'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
|
await page
|
||||||
.getByTestId(favItemTestId)
|
.getByTestId(favItemTestId)
|
||||||
.getByTestId('explorer-collapsed-button')
|
.getByTestId('navigation-panel-collapsed-button')
|
||||||
.click();
|
.click();
|
||||||
|
|
||||||
const favItemAnotherPageTestId = 'explorer-doc-' + anotherPageId;
|
const favItemAnotherPageTestId = 'navigation-panel-doc-' + anotherPageId;
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
page.getByTestId(favItemTestId).getByTestId(favItemAnotherPageTestId)
|
page.getByTestId(favItemTestId).getByTestId(favItemAnotherPageTestId)
|
||||||
@@ -146,7 +146,10 @@ test('Add new favorite page via sidebar', async ({ page }) => {
|
|||||||
await openHomePage(page);
|
await openHomePage(page);
|
||||||
await waitForEditorLoad(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 clickPageModeButton(page);
|
||||||
await waitForEmptyEditor(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');
|
await getBlockSuiteEditorTitle(page).fill('this is a new fav page');
|
||||||
// check if the page title is shown in the favorite list
|
// check if the page title is shown in the favorite list
|
||||||
const favItem = page
|
const favItem = page
|
||||||
.getByTestId('explorer-favorites')
|
.getByTestId('navigation-panel-favorites')
|
||||||
.locator('[draggable] >> text=this is a new fav page');
|
.locator('[draggable] >> text=this is a new fav page');
|
||||||
await expect(favItem).toBeVisible();
|
await expect(favItem).toBeVisible();
|
||||||
});
|
});
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user