mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-24 18:02:47 +08:00
feat(core): add block and element toolbar widget custom config (#7886)
Upstreams: https://github.com/toeverything/blocksuite/pull/8001 https://github.com/toeverything/blocksuite/pull/7964 * add block/element toolbar widget config * add `Copy link to block` to `more menu` on block/element toolbar <img width="376" alt="Screenshot 2024-08-16 at 16 20 08" src="https://github.com/user-attachments/assets/49b41de9-39d1-4f55-ac9b-445fe020187a">
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import { type DocMeta } from '@blocksuite/store';
|
||||
import type { DocMeta } from '@blocksuite/store';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { distinctUntilChanged, Observable } from 'rxjs';
|
||||
|
||||
|
||||
@@ -19,7 +19,8 @@ import {
|
||||
} from '@blocksuite/blocks';
|
||||
import { type FrameworkProvider } from '@toeverything/infra';
|
||||
|
||||
import { createLinkedWidgetConfig } from './linked-widget';
|
||||
import { createLinkedWidgetConfig } from './widgets/linked';
|
||||
import { createToolbarMoreMenuConfig } from './widgets/toolbar';
|
||||
|
||||
function customLoadFonts(service: RootService): void {
|
||||
if (runtimeConfig.isSelfHosted) {
|
||||
@@ -70,6 +71,7 @@ export function createPageRootBlockSpec(
|
||||
ConfigExtension('affine:page', {
|
||||
linkedWidget: createLinkedWidgetConfig(framework),
|
||||
editorSetting: editorSettingService.editorSetting.settingSignal,
|
||||
toolbarMoreMenu: createToolbarMoreMenuConfig(framework),
|
||||
}),
|
||||
];
|
||||
}
|
||||
@@ -92,6 +94,7 @@ export function createEdgelessRootBlockSpec(
|
||||
ConfigExtension('affine:page', {
|
||||
linkedWidget: createLinkedWidgetConfig(framework),
|
||||
editorSetting: editorSettingService.editorSetting.settingSignal,
|
||||
toolbarMoreMenu: createToolbarMoreMenuConfig(framework),
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
import { notify } from '@affine/component';
|
||||
import {
|
||||
generateUrl,
|
||||
type UseSharingUrl,
|
||||
} from '@affine/core/hooks/affine/use-share-url';
|
||||
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
|
||||
import { EditorService } from '@affine/core/modules/editor';
|
||||
import { I18n } from '@affine/i18n';
|
||||
import type { MenuItemGroup } from '@blocksuite/affine-components/toolbar';
|
||||
import type { MenuContext } from '@blocksuite/blocks';
|
||||
import { LinkIcon } from '@blocksuite/icons/lit';
|
||||
import type { FrameworkProvider } from '@toeverything/infra';
|
||||
|
||||
export function createToolbarMoreMenuConfig(framework: FrameworkProvider) {
|
||||
return {
|
||||
configure: <T extends MenuContext>(groups: MenuItemGroup<T>[]) => {
|
||||
const clipboardGroup = groups.find(group => group.type === 'clipboard');
|
||||
|
||||
if (clipboardGroup) {
|
||||
let copyIndex = clipboardGroup.items.findIndex(
|
||||
item => item.type === 'copy'
|
||||
);
|
||||
if (copyIndex === -1) {
|
||||
copyIndex = clipboardGroup.items.findIndex(
|
||||
item => item.type === 'duplicate'
|
||||
);
|
||||
if (copyIndex !== -1) {
|
||||
copyIndex -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
// after `copy` or before `duplicate`
|
||||
clipboardGroup.items.splice(
|
||||
copyIndex + 1,
|
||||
0,
|
||||
createCopyLinkToBlockMenuItem(framework)
|
||||
);
|
||||
}
|
||||
|
||||
return groups;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createCopyLinkToBlockMenuItem(
|
||||
framework: FrameworkProvider,
|
||||
item = {
|
||||
icon: LinkIcon({ width: '20', height: '20' }),
|
||||
label: 'Copy link to block',
|
||||
type: 'copy-link-to-block',
|
||||
when: (ctx: MenuContext) => {
|
||||
if (ctx.isEmpty()) return false;
|
||||
|
||||
const { editor } = framework.get(EditorService);
|
||||
const mode = editor.mode$.value;
|
||||
|
||||
if (mode === 'edgeless') {
|
||||
// linking blocks in notes is currently not supported in edgeless mode.
|
||||
if (ctx.selectedBlockModels.length > 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// linking single block/element in edgeless mode.
|
||||
if (ctx.isMultiple()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
}
|
||||
) {
|
||||
return {
|
||||
...item,
|
||||
action: (ctx: MenuContext) => {
|
||||
const baseUrl = getAffineCloudBaseUrl();
|
||||
if (!baseUrl) return;
|
||||
|
||||
const { editor } = framework.get(EditorService);
|
||||
const mode = editor.mode$.value;
|
||||
const pageId = editor.doc.id;
|
||||
const workspaceId = editor.doc.workspace.id;
|
||||
const options: UseSharingUrl = { workspaceId, pageId, shareMode: mode };
|
||||
|
||||
if (mode === 'page') {
|
||||
// maybe multiple blocks
|
||||
const blockIds = ctx.selectedBlockModels.map(model => model.id);
|
||||
options.blockIds = blockIds;
|
||||
} else if (mode === 'edgeless' && ctx.firstElement) {
|
||||
// single block/element
|
||||
const id = ctx.firstElement.id;
|
||||
const key = ctx.isElement() ? 'element' : 'block';
|
||||
options[`${key}Ids`] = [id];
|
||||
}
|
||||
|
||||
const str = generateUrl(options);
|
||||
if (!str) return;
|
||||
|
||||
navigator.clipboard
|
||||
.writeText(str)
|
||||
.then(() => {
|
||||
notify.success({
|
||||
title: I18n['Copied link to clipboard'](),
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import { useCallback } from 'react';
|
||||
|
||||
import { useActiveBlocksuiteEditor } from '../use-block-suite-editor';
|
||||
|
||||
type UseSharingUrl = {
|
||||
export type UseSharingUrl = {
|
||||
workspaceId: string;
|
||||
pageId: string;
|
||||
shareMode?: DocMode;
|
||||
@@ -20,7 +20,7 @@ type UseSharingUrl = {
|
||||
/**
|
||||
* to generate a url like https://app.affine.pro/workspace/workspaceId/docId?mode=DocMode?element=seletedBlockid#seletedBlockid
|
||||
*/
|
||||
const generateUrl = ({
|
||||
export const generateUrl = ({
|
||||
workspaceId,
|
||||
pageId,
|
||||
blockIds,
|
||||
|
||||
@@ -2,7 +2,7 @@ import { PageDetailSkeleton } from '@affine/component/page-detail-skeleton';
|
||||
import type { Editor } from '@affine/core/modules/editor';
|
||||
import { EditorsService } from '@affine/core/modules/editor';
|
||||
import { ViewService } from '@affine/core/modules/workbench/services/view';
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import { type DocMode, DocModes } from '@blocksuite/blocks';
|
||||
import type { Doc } from '@toeverything/infra';
|
||||
import {
|
||||
DocsService,
|
||||
@@ -35,7 +35,7 @@ const useLoadDoc = (pageId: string) => {
|
||||
);
|
||||
|
||||
const queryStringMode =
|
||||
queryString.mode && ['edgeless', 'page'].includes(queryString.mode)
|
||||
queryString.mode && DocModes.includes(queryString.mode)
|
||||
? (queryString.mode as DocMode)
|
||||
: null;
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ import { ShareReaderService } from '@affine/core/modules/share-doc';
|
||||
import { CloudBlobStorage } from '@affine/core/modules/workspace-engine';
|
||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { useI18n } from '@affine/i18n';
|
||||
import type { DocMode } from '@blocksuite/blocks';
|
||||
import { type DocMode, DocModes } from '@blocksuite/blocks';
|
||||
import { noop } from '@blocksuite/global/utils';
|
||||
import { Logo1Icon } from '@blocksuite/icons/rc';
|
||||
import type { AffineEditorContainer } from '@blocksuite/presets';
|
||||
@@ -59,7 +59,7 @@ export const SharePage = ({
|
||||
useEffect(() => {
|
||||
const searchParams = new URLSearchParams(location.search);
|
||||
const queryStringMode = searchParams.get('mode') as DocMode | null;
|
||||
if (queryStringMode && ['edgeless', 'page'].includes(queryStringMode)) {
|
||||
if (queryStringMode && DocModes.includes(queryStringMode)) {
|
||||
setMode(queryStringMode);
|
||||
}
|
||||
}, [location.search]);
|
||||
|
||||
Reference in New Issue
Block a user