feat(core): add doc/tag/collection to folders via menu (#7631)
close AF-993,AF-995,AF-997,AF-998
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
AnimatedCollectionsIcon,
|
||||||
AnimatedFolderIcon,
|
AnimatedFolderIcon,
|
||||||
type DropTargetDropEvent,
|
type DropTargetDropEvent,
|
||||||
type DropTargetOptions,
|
type DropTargetOptions,
|
||||||
@@ -6,7 +7,13 @@ import {
|
|||||||
MenuIcon,
|
MenuIcon,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
MenuSeparator,
|
MenuSeparator,
|
||||||
|
MenuSub,
|
||||||
} from '@affine/component';
|
} from '@affine/component';
|
||||||
|
import {
|
||||||
|
useSelectCollection,
|
||||||
|
useSelectDoc,
|
||||||
|
useSelectTag,
|
||||||
|
} from '@affine/core/components/page-list/selector';
|
||||||
import {
|
import {
|
||||||
type FolderNode,
|
type FolderNode,
|
||||||
OrganizeService,
|
OrganizeService,
|
||||||
@@ -18,10 +25,14 @@ import { useI18n } from '@affine/i18n';
|
|||||||
import {
|
import {
|
||||||
DeleteIcon,
|
DeleteIcon,
|
||||||
FolderIcon,
|
FolderIcon,
|
||||||
|
PageIcon,
|
||||||
PlusIcon,
|
PlusIcon,
|
||||||
|
PlusThickIcon,
|
||||||
RemoveFolderIcon,
|
RemoveFolderIcon,
|
||||||
|
TagsIcon,
|
||||||
} from '@blocksuite/icons/rc';
|
} from '@blocksuite/icons/rc';
|
||||||
import { DocsService, useLiveData, useServices } from '@toeverything/infra';
|
import { DocsService, useLiveData, useServices } from '@toeverything/infra';
|
||||||
|
import { difference } from 'lodash-es';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useCallback, useMemo, useState } from 'react';
|
||||||
|
|
||||||
import { ExplorerTreeNode, type ExplorerTreeNodeDropEffect } from '../../tree';
|
import { ExplorerTreeNode, type ExplorerTreeNodeDropEffect } from '../../tree';
|
||||||
@@ -153,6 +164,9 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
DocsService,
|
DocsService,
|
||||||
WorkbenchService,
|
WorkbenchService,
|
||||||
});
|
});
|
||||||
|
const openDocsSelector = useSelectDoc();
|
||||||
|
const openTagsSelector = useSelectTag();
|
||||||
|
const openCollectionsSelector = useSelectCollection();
|
||||||
const name = useLiveData(node.name$);
|
const name = useLiveData(node.name$);
|
||||||
const [collapsed, setCollapsed] = useState(true);
|
const [collapsed, setCollapsed] = useState(true);
|
||||||
const [newFolderId, setNewFolderId] = useState<string | null>(null);
|
const [newFolderId, setNewFolderId] = useState<string | null>(null);
|
||||||
@@ -479,6 +493,47 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
setNewFolderId(newFolderId);
|
setNewFolderId(newFolderId);
|
||||||
}, [node, t]);
|
}, [node, t]);
|
||||||
|
|
||||||
|
const handleAddToFolder = useCallback(
|
||||||
|
(type: 'doc' | 'collection' | 'tag') => {
|
||||||
|
const initialIds = children
|
||||||
|
.filter(node => node.type$.value === type)
|
||||||
|
.map(node => node.data$.value)
|
||||||
|
.filter(Boolean) as string[];
|
||||||
|
const selector =
|
||||||
|
type === 'doc'
|
||||||
|
? openDocsSelector
|
||||||
|
: type === 'collection'
|
||||||
|
? openCollectionsSelector
|
||||||
|
: openTagsSelector;
|
||||||
|
selector(initialIds)
|
||||||
|
.then(selectedIds => {
|
||||||
|
const newItemIds = difference(selectedIds, initialIds);
|
||||||
|
const removedItemIds = difference(initialIds, selectedIds);
|
||||||
|
const removedItems = children.filter(
|
||||||
|
node =>
|
||||||
|
!!node.data$.value && removedItemIds.includes(node.data$.value)
|
||||||
|
);
|
||||||
|
|
||||||
|
newItemIds.forEach(id =>
|
||||||
|
node.createLink(type, id, node.indexAt('after'))
|
||||||
|
);
|
||||||
|
removedItems.forEach(node => node.delete());
|
||||||
|
const updated = newItemIds.length + removedItems.length;
|
||||||
|
updated && setCollapsed(false);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.error(`Unexpected error while selecting ${type}`, err);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
[
|
||||||
|
children,
|
||||||
|
node,
|
||||||
|
openCollectionsSelector,
|
||||||
|
openDocsSelector,
|
||||||
|
openTagsSelector,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
const folderOperations = useMemo(() => {
|
const folderOperations = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -505,6 +560,63 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
</MenuItem>
|
</MenuItem>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
index: 101,
|
||||||
|
view: (
|
||||||
|
<MenuItem
|
||||||
|
preFix={
|
||||||
|
<MenuIcon>
|
||||||
|
<PageIcon />
|
||||||
|
</MenuIcon>
|
||||||
|
}
|
||||||
|
onClick={() => handleAddToFolder('doc')}
|
||||||
|
>
|
||||||
|
{t['com.affine.rootAppSidebar.organize.folder.add-docs']()}
|
||||||
|
</MenuItem>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
index: 102,
|
||||||
|
view: (
|
||||||
|
<MenuSub
|
||||||
|
triggerOptions={{
|
||||||
|
preFix: (
|
||||||
|
<MenuIcon>
|
||||||
|
<PlusThickIcon />
|
||||||
|
</MenuIcon>
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
items={
|
||||||
|
<>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => handleAddToFolder('tag')}
|
||||||
|
preFix={
|
||||||
|
<MenuIcon>
|
||||||
|
<TagsIcon />
|
||||||
|
</MenuIcon>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t['com.affine.rootAppSidebar.organize.folder.add-tags']()}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
onClick={() => handleAddToFolder('collection')}
|
||||||
|
preFix={
|
||||||
|
<MenuIcon>
|
||||||
|
<AnimatedCollectionsIcon closed={false} />
|
||||||
|
</MenuIcon>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t[
|
||||||
|
'com.affine.rootAppSidebar.organize.folder.add-collections'
|
||||||
|
]()}
|
||||||
|
</MenuItem>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t['com.affine.rootAppSidebar.organize.folder.add-others']()}
|
||||||
|
</MenuSub>
|
||||||
|
),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
index: 9999,
|
index: 9999,
|
||||||
view: <MenuSeparator key="menu-separator" />,
|
view: <MenuSeparator key="menu-separator" />,
|
||||||
@@ -526,7 +638,7 @@ export const ExplorerFolderNodeFolder = ({
|
|||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}, [handleCreateSubfolder, handleDelete, handleNewDoc, t]);
|
}, [handleAddToFolder, handleCreateSubfolder, handleDelete, handleNewDoc, t]);
|
||||||
|
|
||||||
const finalOperations = useMemo(() => {
|
const finalOperations = useMemo(() => {
|
||||||
if (additionalOperations) {
|
if (additionalOperations) {
|
||||||
|
|||||||
@@ -1128,6 +1128,10 @@
|
|||||||
"com.affine.rootAppSidebar.organize.empty": "No Folders",
|
"com.affine.rootAppSidebar.organize.empty": "No Folders",
|
||||||
"com.affine.rootAppSidebar.organize.empty-folder": "Empty Folder",
|
"com.affine.rootAppSidebar.organize.empty-folder": "Empty Folder",
|
||||||
"com.affine.rootAppSidebar.organize.folder.create-subfolder": "Create a subfolder",
|
"com.affine.rootAppSidebar.organize.folder.create-subfolder": "Create a subfolder",
|
||||||
|
"com.affine.rootAppSidebar.organize.folder.add-docs": "Add docs",
|
||||||
|
"com.affine.rootAppSidebar.organize.folder.add-others": "Add others",
|
||||||
|
"com.affine.rootAppSidebar.organize.folder.add-tags": "Add Tags",
|
||||||
|
"com.affine.rootAppSidebar.organize.folder.add-collections": "Add Collections",
|
||||||
"com.affine.rootAppSidebar.organize.empty-folder.add-pages": "Add pages",
|
"com.affine.rootAppSidebar.organize.empty-folder.add-pages": "Add pages",
|
||||||
"com.affine.rootAppSidebar.organize.empty.new-folders-button": "New Folder",
|
"com.affine.rootAppSidebar.organize.empty.new-folders-button": "New Folder",
|
||||||
"com.affine.rootAppSidebar.organize.new-folders": "New Folder",
|
"com.affine.rootAppSidebar.organize.new-folders": "New Folder",
|
||||||
|
|||||||
Reference in New Issue
Block a user