From f518365cf084e69347d2cecaa86900c1b378d88b Mon Sep 17 00:00:00 2001 From: Fangdun Tsai Date: Tue, 7 Jan 2025 19:52:42 +0800 Subject: [PATCH] feat(editor): add toolbar registry extension --- .../block-attachment/src/attachment-spec.ts | 16 ++++- .../affine/block-image/src/image-spec.ts | 13 +++- .../affine/shared/src/services/index.ts | 1 + .../src/services/toolbar-service/index.ts | 2 + .../src/services/toolbar-service/module.ts | 5 ++ .../src/services/toolbar-service/registry.ts | 59 +++++++++++++++++++ .../affine/widget-toolbar/src/toolbar.ts | 15 ++++- .../root-block/edgeless/edgeless-root-spec.ts | 2 + .../src/root-block/page/page-root-spec.ts | 2 + .../block-suite-editor/specs/common.ts | 2 + 10 files changed, 110 insertions(+), 7 deletions(-) create mode 100644 blocksuite/affine/shared/src/services/toolbar-service/index.ts create mode 100644 blocksuite/affine/shared/src/services/toolbar-service/module.ts create mode 100644 blocksuite/affine/shared/src/services/toolbar-service/registry.ts diff --git a/blocksuite/affine/block-attachment/src/attachment-spec.ts b/blocksuite/affine/block-attachment/src/attachment-spec.ts index fed3db5d084eb..a02bee0fe2b65 100644 --- a/blocksuite/affine/block-attachment/src/attachment-spec.ts +++ b/blocksuite/affine/block-attachment/src/attachment-spec.ts @@ -1,4 +1,9 @@ -import { BlockViewExtension, FlavourExtension } from '@blocksuite/block-std'; +import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services'; +import { + BlockFlavourIdentifier, + BlockViewExtension, + FlavourExtension, +} from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { literal } from 'lit/static-html.js'; @@ -12,10 +17,12 @@ import { AttachmentEmbedService, } from './embed.js'; +const Flavour = 'affine:attachment'; + export const AttachmentBlockSpec: ExtensionType[] = [ - FlavourExtension('affine:attachment'), + FlavourExtension(Flavour), AttachmentBlockService, - BlockViewExtension('affine:attachment', model => { + BlockViewExtension(Flavour, model => { return model.parent?.flavour === 'affine:surface' ? literal`affine-edgeless-attachment` : literal`affine-attachment`; @@ -24,4 +31,7 @@ export const AttachmentBlockSpec: ExtensionType[] = [ AttachmentEmbedConfigExtension(), AttachmentEmbedService, AttachmentBlockNotionHtmlAdapterExtension, + ToolbarModuleExtension({ + id: BlockFlavourIdentifier(Flavour), + }), ]; diff --git a/blocksuite/affine/block-image/src/image-spec.ts b/blocksuite/affine/block-image/src/image-spec.ts index 92068953a3460..60102114a5930 100644 --- a/blocksuite/affine/block-image/src/image-spec.ts +++ b/blocksuite/affine/block-image/src/image-spec.ts @@ -1,5 +1,7 @@ import { ImageSelectionExtension } from '@blocksuite/affine-shared/selection'; +import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services'; import { + BlockFlavourIdentifier, BlockViewExtension, CommandExtension, FlavourExtension, @@ -12,11 +14,13 @@ import { ImageBlockAdapterExtensions } from './adapters/extension.js'; import { commands } from './commands/index.js'; import { ImageBlockService, ImageDropOption } from './image-service.js'; +const Flavour = 'affine:image'; + export const ImageBlockSpec: ExtensionType[] = [ - FlavourExtension('affine:image'), + FlavourExtension(Flavour), ImageBlockService, CommandExtension(commands), - BlockViewExtension('affine:image', model => { + BlockViewExtension(Flavour, model => { const parent = model.doc.getParent(model.id); if (parent?.flavour === 'affine:surface') { @@ -25,10 +29,13 @@ export const ImageBlockSpec: ExtensionType[] = [ return literal`affine-image`; }), - WidgetViewMapExtension('affine:image', { + WidgetViewMapExtension(Flavour, { imageToolbar: literal`affine-image-toolbar-widget`, }), ImageDropOption, ImageSelectionExtension, ImageBlockAdapterExtensions, + ToolbarModuleExtension({ + id: BlockFlavourIdentifier(Flavour), + }), ].flat(); diff --git a/blocksuite/affine/shared/src/services/index.ts b/blocksuite/affine/shared/src/services/index.ts index ae5a0d51f8ddb..1e1a2d333d2f2 100644 --- a/blocksuite/affine/shared/src/services/index.ts +++ b/blocksuite/affine/shared/src/services/index.ts @@ -13,3 +13,4 @@ export * from './parse-url-service'; export * from './quick-search-service'; export * from './telemetry-service'; export * from './theme-service'; +export * from './toolbar-service'; diff --git a/blocksuite/affine/shared/src/services/toolbar-service/index.ts b/blocksuite/affine/shared/src/services/toolbar-service/index.ts new file mode 100644 index 0000000000000..e6d4e119aa4e6 --- /dev/null +++ b/blocksuite/affine/shared/src/services/toolbar-service/index.ts @@ -0,0 +1,2 @@ +export * from './module'; +export * from './registry'; diff --git a/blocksuite/affine/shared/src/services/toolbar-service/module.ts b/blocksuite/affine/shared/src/services/toolbar-service/module.ts new file mode 100644 index 0000000000000..d753961fb5474 --- /dev/null +++ b/blocksuite/affine/shared/src/services/toolbar-service/module.ts @@ -0,0 +1,5 @@ +import type { BlockFlavourIdentifier } from '@blocksuite/block-std'; + +export abstract class ToolbarModule { + constructor(public readonly id: ReturnType) {} +} diff --git a/blocksuite/affine/shared/src/services/toolbar-service/registry.ts b/blocksuite/affine/shared/src/services/toolbar-service/registry.ts new file mode 100644 index 0000000000000..62599ea62a118 --- /dev/null +++ b/blocksuite/affine/shared/src/services/toolbar-service/registry.ts @@ -0,0 +1,59 @@ +import { + type BlockStdScope, + LifeCycleWatcher, + StdIdentifier, +} from '@blocksuite/block-std'; +import { + type Container, + createIdentifier, + createScope, +} from '@blocksuite/global/di'; +import type { ExtensionType } from '@blocksuite/store'; + +import type { ToolbarModule } from './module'; + +export const ToolbarModuleIdentifier = createIdentifier( + 'AffineToolbarModuleIdentifier' +); + +export const ToolbarModulesIdentifier = createIdentifier< + Map +>('AffineToolbarModulesIdentifier'); + +export const ToolbarRegistryScope = createScope('AffineToolbarRegistryScope'); + +export const ToolbarRegistryIdentifier = + createIdentifier('AffineToolbarRegistryIdentifier'); + +export function ToolbarModuleExtension(module: ToolbarModule): ExtensionType { + return { + setup: di => { + di.scope(ToolbarRegistryScope).addImpl( + ToolbarModuleIdentifier(module.id.variant), + module + ); + }, + }; +} + +export class ToolbarRegistryExtension extends LifeCycleWatcher { + constructor( + std: BlockStdScope, + readonly modules: Map + ) { + super(std); + } + + static override readonly key = 'toolbar-registry'; + + static override setup(di: Container) { + di.scope(ToolbarRegistryScope) + .addImpl(ToolbarModulesIdentifier, provider => + provider.getAll(ToolbarModuleIdentifier) + ) + .addImpl(ToolbarRegistryIdentifier, this, [ + StdIdentifier, + ToolbarModulesIdentifier, + ]); + } +} diff --git a/blocksuite/affine/widget-toolbar/src/toolbar.ts b/blocksuite/affine/widget-toolbar/src/toolbar.ts index b6e8472d11df2..998fcaf1e181c 100644 --- a/blocksuite/affine/widget-toolbar/src/toolbar.ts +++ b/blocksuite/affine/widget-toolbar/src/toolbar.ts @@ -1,5 +1,18 @@ +import { + ToolbarRegistryIdentifier, + ToolbarRegistryScope, +} from '@blocksuite/affine-shared/services'; import { WidgetComponent } from '@blocksuite/block-std'; export const AFFINE_TOOLBAR_WIDGET = 'affine-toolbar-widget'; -export class AffineToolbarWidget extends WidgetComponent {} +export class AffineToolbarWidget extends WidgetComponent { + override connectedCallback() { + super.connectedCallback(); + + const toolbarRegistry = this.std.container + .provider(ToolbarRegistryScope, this.std.provider) + .get(ToolbarRegistryIdentifier); + console.log(toolbarRegistry); + } +} diff --git a/blocksuite/blocks/src/root-block/edgeless/edgeless-root-spec.ts b/blocksuite/blocks/src/root-block/edgeless/edgeless-root-spec.ts index 461a54eaf0a89..984fa03618fd0 100644 --- a/blocksuite/blocks/src/root-block/edgeless/edgeless-root-spec.ts +++ b/blocksuite/blocks/src/root-block/edgeless/edgeless-root-spec.ts @@ -5,6 +5,7 @@ import { EmbedOptionService, PageViewportServiceExtension, ThemeService, + ToolbarRegistryExtension, } from '@blocksuite/affine-shared/services'; import { AFFINE_DRAG_HANDLE_WIDGET } from '@blocksuite/affine-widget-drag-handle'; import { AFFINE_FRAME_TITLE_WIDGET } from '@blocksuite/affine-widget-frame-title'; @@ -102,6 +103,7 @@ const EdgelessCommonExtension: ExtensionType[] = [ PageViewportServiceExtension, RootBlockAdapterExtensions, FileDropExtension, + ToolbarRegistryExtension, ].flat(); export const EdgelessRootBlockSpec: ExtensionType[] = [ diff --git a/blocksuite/blocks/src/root-block/page/page-root-spec.ts b/blocksuite/blocks/src/root-block/page/page-root-spec.ts index c7ae87d84ed08..95f6039b73afe 100644 --- a/blocksuite/blocks/src/root-block/page/page-root-spec.ts +++ b/blocksuite/blocks/src/root-block/page/page-root-spec.ts @@ -5,6 +5,7 @@ import { EmbedOptionService, PageViewportServiceExtension, ThemeService, + ToolbarRegistryExtension, } from '@blocksuite/affine-shared/services'; import { AFFINE_DRAG_HANDLE_WIDGET } from '@blocksuite/affine-widget-drag-handle'; import { AFFINE_DOC_REMOTE_SELECTION_WIDGET } from '@blocksuite/affine-widget-remote-selection'; @@ -83,6 +84,7 @@ export const PageRootBlockSpec: ExtensionType[] = [ DNDAPIExtension, RootBlockAdapterExtensions, FileDropExtension, + ToolbarRegistryExtension, ].flat(); export const PreviewPageRootBlockSpec: ExtensionType[] = [ diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/common.ts b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/common.ts index 75bce26ab9a08..500436a7bd2f3 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/common.ts +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/common.ts @@ -21,6 +21,7 @@ import { ParagraphBlockSpec, RefNodeSlotsExtension, RichTextExtensions, + ToolbarRegistryExtension, } from '@blocksuite/affine/blocks'; import type { ExtensionType } from '@blocksuite/affine/store'; @@ -38,6 +39,7 @@ const CommonBlockSpecs: ExtensionType[] = [ AttachmentBlockSpec, AdapterFactoryExtensions, FontLoaderService, + ToolbarRegistryExtension, ].flat(); export const DefaultBlockSpecs: ExtensionType[] = [