From 8e08b9000d83a4dc870033238d353929d5d35cdb Mon Sep 17 00:00:00 2001 From: Saul-Mirone Date: Mon, 24 Mar 2025 03:14:22 +0000 Subject: [PATCH] feat(editor): create gfx mindmap package (#11100) --- blocksuite/affine/all/package.json | 2 + blocksuite/affine/all/src/gfx/mindmap.ts | 1 + blocksuite/affine/all/tsconfig.json | 1 + .../src/edgelss-toolbar/config.ts | 11 ++- .../affine/blocks/block-root/package.json | 1 + .../src/edgeless/configs/toolbar/index.ts | 35 +++------ .../src/edgeless/configs/toolbar/shape.ts | 73 ------------------- .../affine/blocks/block-root/src/effects.ts | 16 +--- .../affine/blocks/block-root/tsconfig.json | 1 + .../gfx/connector/src/toolbar/config.ts | 15 +++- blocksuite/affine/gfx/mindmap/package.json | 50 +++++++++++++ blocksuite/affine/gfx/mindmap/src/effects.ts | 20 +++++ blocksuite/affine/gfx/mindmap/src/index.ts | 1 + .../mindmap/src/toolbar}/assets.ts | 0 .../mindmap/src/toolbar}/basket-elements.ts | 0 .../mindmap/src/toolbar/config.ts} | 15 +++- .../mindmap/src/toolbar}/icons.ts | 0 .../toolbar}/mindmap-importing-placeholder.ts | 0 .../mindmap/src/toolbar}/mindmap-menu.ts | 16 ++-- .../src/toolbar}/mindmap-tool-button.ts | 6 ++ .../src/toolbar}/utils/import-mindmap.ts | 0 blocksuite/affine/gfx/mindmap/tsconfig.json | 24 ++++++ blocksuite/affine/gfx/shape/package.json | 1 + .../affine/gfx/shape/src/toolbar/config.ts | 67 ++++++++++++++++- blocksuite/affine/gfx/shape/tsconfig.json | 1 + .../affine/gfx/text/src/toolbar/config.ts | 11 ++- tools/utils/src/workspace.gen.ts | 23 ++++++ tsconfig.json | 1 + yarn.lock | 35 +++++++++ 29 files changed, 292 insertions(+), 135 deletions(-) create mode 100644 blocksuite/affine/all/src/gfx/mindmap.ts delete mode 100644 blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/shape.ts create mode 100644 blocksuite/affine/gfx/mindmap/package.json create mode 100644 blocksuite/affine/gfx/mindmap/src/effects.ts create mode 100644 blocksuite/affine/gfx/mindmap/src/index.ts rename blocksuite/affine/{blocks/block-root/src/edgeless/components/toolbar/mindmap => gfx/mindmap/src/toolbar}/assets.ts (100%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/toolbar/mindmap => gfx/mindmap/src/toolbar}/basket-elements.ts (100%) rename blocksuite/affine/{blocks/block-root/src/edgeless/configs/toolbar/mindmap.ts => gfx/mindmap/src/toolbar/config.ts} (89%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/toolbar/mindmap => gfx/mindmap/src/toolbar}/icons.ts (100%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/toolbar/mindmap => gfx/mindmap/src/toolbar}/mindmap-importing-placeholder.ts (100%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/toolbar/mindmap => gfx/mindmap/src/toolbar}/mindmap-menu.ts (96%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/toolbar/mindmap => gfx/mindmap/src/toolbar}/mindmap-tool-button.ts (97%) rename blocksuite/affine/{blocks/block-root/src/edgeless/components/toolbar/mindmap => gfx/mindmap/src/toolbar}/utils/import-mindmap.ts (100%) create mode 100644 blocksuite/affine/gfx/mindmap/tsconfig.json diff --git a/blocksuite/affine/all/package.json b/blocksuite/affine/all/package.json index c4c7c8278..4d1f9236d 100644 --- a/blocksuite/affine/all/package.json +++ b/blocksuite/affine/all/package.json @@ -34,6 +34,7 @@ "@blocksuite/affine-fragment-frame-panel": "workspace:*", "@blocksuite/affine-fragment-outline": "workspace:*", "@blocksuite/affine-gfx-connector": "workspace:*", + "@blocksuite/affine-gfx-mindmap": "workspace:*", "@blocksuite/affine-gfx-note": "workspace:*", "@blocksuite/affine-gfx-shape": "workspace:*", "@blocksuite/affine-gfx-text": "workspace:*", @@ -119,6 +120,7 @@ "./gfx/text": "./src/gfx/text.ts", "./gfx/shape": "./src/gfx/shape.ts", "./gfx/note": "./src/gfx/note.ts", + "./gfx/mindmap": "./src/gfx/mindmap.ts", "./gfx/connector": "./src/gfx/connector.ts", "./gfx/turbo-renderer": "./src/gfx/turbo-renderer.ts", "./components/block-selection": "./src/components/block-selection.ts", diff --git a/blocksuite/affine/all/src/gfx/mindmap.ts b/blocksuite/affine/all/src/gfx/mindmap.ts new file mode 100644 index 000000000..edfe2f733 --- /dev/null +++ b/blocksuite/affine/all/src/gfx/mindmap.ts @@ -0,0 +1 @@ +export * from '@blocksuite/affine-gfx-mindmap'; diff --git a/blocksuite/affine/all/tsconfig.json b/blocksuite/affine/all/tsconfig.json index 03984338a..dd3ddee2e 100644 --- a/blocksuite/affine/all/tsconfig.json +++ b/blocksuite/affine/all/tsconfig.json @@ -31,6 +31,7 @@ { "path": "../fragments/fragment-frame-panel" }, { "path": "../fragments/fragment-outline" }, { "path": "../gfx/connector" }, + { "path": "../gfx/mindmap" }, { "path": "../gfx/note" }, { "path": "../gfx/shape" }, { "path": "../gfx/text" }, diff --git a/blocksuite/affine/blocks/block-edgeless-text/src/edgelss-toolbar/config.ts b/blocksuite/affine/blocks/block-edgeless-text/src/edgelss-toolbar/config.ts index aba36f45f..c2367624f 100644 --- a/blocksuite/affine/blocks/block-edgeless-text/src/edgelss-toolbar/config.ts +++ b/blocksuite/affine/blocks/block-edgeless-text/src/edgelss-toolbar/config.ts @@ -1,6 +1,10 @@ import { EdgelessTextBlockModel } from '@blocksuite/affine-model'; -import { type ToolbarModuleConfig } from '@blocksuite/affine-shared/services'; +import { + type ToolbarModuleConfig, + ToolbarModuleExtension, +} from '@blocksuite/affine-shared/services'; import { createTextActions } from '@blocksuite/affine-widget-edgeless-toolbar'; +import { BlockFlavourIdentifier } from '@blocksuite/block-std'; export const edgelessTextToolbarConfig = { // No need to adjust element bounds, which updates itself using ResizeObserver @@ -8,3 +12,8 @@ export const edgelessTextToolbarConfig = { when: ctx => ctx.getSurfaceModelsByType(EdgelessTextBlockModel).length > 0, } as const satisfies ToolbarModuleConfig; + +export const edgelessTextToolbarExtension = ToolbarModuleExtension({ + id: BlockFlavourIdentifier('affine:surface:edgeless-text'), + config: edgelessTextToolbarConfig, +}); diff --git a/blocksuite/affine/blocks/block-root/package.json b/blocksuite/affine/blocks/block-root/package.json index ee3591868..b2db2bb5b 100644 --- a/blocksuite/affine/blocks/block-root/package.json +++ b/blocksuite/affine/blocks/block-root/package.json @@ -28,6 +28,7 @@ "@blocksuite/affine-components": "workspace:*", "@blocksuite/affine-fragment-doc-title": "workspace:*", "@blocksuite/affine-gfx-connector": "workspace:*", + "@blocksuite/affine-gfx-mindmap": "workspace:*", "@blocksuite/affine-gfx-note": "workspace:*", "@blocksuite/affine-gfx-shape": "workspace:*", "@blocksuite/affine-gfx-text": "workspace:*", diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts b/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts index 6d14f1b7e..26afc93a7 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts +++ b/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/index.ts @@ -1,16 +1,16 @@ -import { edgelessTextToolbarConfig } from '@blocksuite/affine-block-edgeless-text'; +import { edgelessTextToolbarExtension } from '@blocksuite/affine-block-edgeless-text'; import { frameToolbarExtension } from '@blocksuite/affine-block-frame'; -import { connectorToolbarConfig } from '@blocksuite/affine-gfx-connector'; -import { textToolbarConfig } from '@blocksuite/affine-gfx-text'; +import { connectorToolbarExtension } from '@blocksuite/affine-gfx-connector'; +import { mindmapToolbarExtension } from '@blocksuite/affine-gfx-mindmap'; +import { shapeToolbarExtension } from '@blocksuite/affine-gfx-shape'; +import { textToolbarExtension } from '@blocksuite/affine-gfx-text'; import { ToolbarModuleExtension } from '@blocksuite/affine-shared/services'; import { BlockFlavourIdentifier } from '@blocksuite/block-std'; import type { ExtensionType } from '@blocksuite/store'; import { builtinBrushToolbarConfig } from './brush'; import { builtinGroupToolbarConfig } from './group'; -import { builtinMindmapToolbarConfig } from './mindmap'; import { builtinLockedToolbarConfig, builtinMiscToolbarConfig } from './misc'; -import { builtinShapeToolbarConfig } from './shape'; export const EdgelessElementToolbarExtension: ExtensionType[] = [ frameToolbarExtension, @@ -25,30 +25,15 @@ export const EdgelessElementToolbarExtension: ExtensionType[] = [ config: builtinBrushToolbarConfig, }), - ToolbarModuleExtension({ - id: BlockFlavourIdentifier('affine:surface:connector'), - config: connectorToolbarConfig, - }), + connectorToolbarExtension, - ToolbarModuleExtension({ - id: BlockFlavourIdentifier('affine:surface:mindmap'), - config: builtinMindmapToolbarConfig, - }), + mindmapToolbarExtension, - ToolbarModuleExtension({ - id: BlockFlavourIdentifier('affine:surface:text'), - config: textToolbarConfig, - }), + textToolbarExtension, - ToolbarModuleExtension({ - id: BlockFlavourIdentifier('affine:surface:edgeless-text'), - config: edgelessTextToolbarConfig, - }), + edgelessTextToolbarExtension, - ToolbarModuleExtension({ - id: BlockFlavourIdentifier('affine:surface:shape'), - config: builtinShapeToolbarConfig, - }), + shapeToolbarExtension, ToolbarModuleExtension({ id: BlockFlavourIdentifier('affine:surface:*'), diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/shape.ts b/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/shape.ts deleted file mode 100644 index 0d06907a3..000000000 --- a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/shape.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { shapeToolbarConfig } from '@blocksuite/affine-gfx-shape'; -import { - MindmapElementModel, - ShapeElementModel, -} from '@blocksuite/affine-model'; -import type { ToolbarActions } from '@blocksuite/affine-shared/services'; - -import { - createMindmapLayoutActionMenu, - createMindmapStyleActionMenu, -} from './mindmap'; - -const mindmapActions = [ - { - id: 'a.mindmap-style', - when(ctx) { - const models = ctx.getSurfaceModelsByType(ShapeElementModel); - return models.some(hasGrouped); - }, - content(ctx) { - const models = ctx.getSurfaceModelsByType(ShapeElementModel); - if (!models.length) return null; - - let mindmaps = models - .map(model => model.group) - .filter(model => ctx.matchModel(model, MindmapElementModel)); - if (!mindmaps.length) return null; - - // Not displayed when there is both a normal shape and a mindmap shape. - if (models.length !== mindmaps.length) return null; - - mindmaps = Array.from(new Set(mindmaps)); - - return createMindmapStyleActionMenu(ctx, mindmaps); - }, - }, - { - id: 'b.mindmap-layout', - when(ctx) { - const models = ctx.getSurfaceModelsByType(ShapeElementModel); - return models.some(hasGrouped); - }, - content(ctx) { - const models = ctx.getSurfaceModelsByType(ShapeElementModel); - if (!models.length) return null; - - let mindmaps = models - .map(model => model.group) - .filter(model => ctx.matchModel(model, MindmapElementModel)); - if (!mindmaps.length) return null; - - // Not displayed when there is both a normal shape and a mindmap shape. - if (models.length !== mindmaps.length) return null; - - mindmaps = Array.from(new Set(mindmaps)); - - // It's a sub node. - if (models.length === 1 && mindmaps[0].tree.element !== models[0]) - return null; - - return createMindmapLayoutActionMenu(ctx, mindmaps); - }, - }, -] as const satisfies ToolbarActions; - -export const builtinShapeToolbarConfig = { - ...shapeToolbarConfig, - actions: [...mindmapActions, ...shapeToolbarConfig.actions], -}; - -function hasGrouped(model: ShapeElementModel) { - return model.group instanceof MindmapElementModel; -} diff --git a/blocksuite/affine/blocks/block-root/src/effects.ts b/blocksuite/affine/blocks/block-root/src/effects.ts index f7f045ff3..434347a17 100644 --- a/blocksuite/affine/blocks/block-root/src/effects.ts +++ b/blocksuite/affine/blocks/block-root/src/effects.ts @@ -1,4 +1,5 @@ import { effects as gfxConnectorEffects } from '@blocksuite/affine-gfx-connector/effects'; +import { effects as gfxMindmapEffects } from '@blocksuite/affine-gfx-mindmap/effects'; import { effects as gfxNoteEffects } from '@blocksuite/affine-gfx-note/effects'; import { effects as gfxShapeEffects } from '@blocksuite/affine-gfx-shape/effects'; import { effects as gfxCanvasTextEffects } from '@blocksuite/affine-gfx-text/effects'; @@ -32,9 +33,6 @@ import { EdgelessDefaultToolButton } from './edgeless/components/toolbar/default import { EdgelessEraserToolButton } from './edgeless/components/toolbar/eraser/eraser-tool-button.js'; import { EdgelessLassoToolButton } from './edgeless/components/toolbar/lasso/lasso-tool-button.js'; import { EdgelessLinkToolButton } from './edgeless/components/toolbar/link/link-tool-button.js'; -import { MindMapPlaceholder } from './edgeless/components/toolbar/mindmap/mindmap-importing-placeholder.js'; -import { EdgelessMindmapMenu } from './edgeless/components/toolbar/mindmap/mindmap-menu.js'; -import { EdgelessMindmapToolButton } from './edgeless/components/toolbar/mindmap/mindmap-tool-button.js'; import { OverlayScrollbar } from './edgeless/components/toolbar/template/overlay-scrollbar.js'; import { AffineTemplateLoading } from './edgeless/components/toolbar/template/template-loading.js'; import { EdgelessTemplatePanel } from './edgeless/components/toolbar/template/template-panel.js'; @@ -108,6 +106,7 @@ function registerGfxEffects() { gfxShapeEffects(); gfxNoteEffects(); gfxConnectorEffects(); + gfxMindmapEffects(); } function registerWidgets() { @@ -141,15 +140,10 @@ function registerEdgelessToolbarComponents() { ); customElements.define('edgeless-link-tool-button', EdgelessLinkToolButton); customElements.define('edgeless-lasso-tool-button', EdgelessLassoToolButton); - customElements.define( - 'edgeless-mindmap-tool-button', - EdgelessMindmapToolButton - ); customElements.define('edgeless-template-button', EdgelessTemplateButton); // Menus customElements.define('edgeless-brush-menu', EdgelessBrushMenu); - customElements.define('edgeless-mindmap-menu', EdgelessMindmapMenu); customElements.define('edgeless-slide-menu', EdgelessSlideMenu); // Toolbar components @@ -214,9 +208,6 @@ function registerMiscComponents() { EDGELESS_SELECTED_RECT_WIDGET, EdgelessSelectedRectWidget ); - - // Mindmap components - customElements.define('mindmap-import-placeholder', MindMapPlaceholder); } declare global { @@ -243,9 +234,6 @@ declare global { 'edgeless-eraser-tool-button': EdgelessEraserToolButton; 'edgeless-lasso-tool-button': EdgelessLassoToolButton; 'edgeless-link-tool-button': EdgelessLinkToolButton; - 'mindmap-import-placeholder': MindMapPlaceholder; - 'edgeless-mindmap-menu': EdgelessMindmapMenu; - 'edgeless-mindmap-tool-button': EdgelessMindmapToolButton; 'overlay-scrollbar': OverlayScrollbar; 'affine-template-loading': AffineTemplateLoading; 'edgeless-templates-panel': EdgelessTemplatePanel; diff --git a/blocksuite/affine/blocks/block-root/tsconfig.json b/blocksuite/affine/blocks/block-root/tsconfig.json index c7945fe85..fcb5db3fe 100644 --- a/blocksuite/affine/blocks/block-root/tsconfig.json +++ b/blocksuite/affine/blocks/block-root/tsconfig.json @@ -25,6 +25,7 @@ { "path": "../../components" }, { "path": "../../fragments/fragment-doc-title" }, { "path": "../../gfx/connector" }, + { "path": "../../gfx/mindmap" }, { "path": "../../gfx/note" }, { "path": "../../gfx/shape" }, { "path": "../../gfx/text" }, diff --git a/blocksuite/affine/gfx/connector/src/toolbar/config.ts b/blocksuite/affine/gfx/connector/src/toolbar/config.ts index 0c7c58b90..1c4c2399e 100644 --- a/blocksuite/affine/gfx/connector/src/toolbar/config.ts +++ b/blocksuite/affine/gfx/connector/src/toolbar/config.ts @@ -21,10 +21,11 @@ import { resolveColor, StrokeStyle, } from '@blocksuite/affine-model'; -import type { - ToolbarContext, - ToolbarGenericAction, - ToolbarModuleConfig, +import { + type ToolbarContext, + type ToolbarGenericAction, + type ToolbarModuleConfig, + ToolbarModuleExtension, } from '@blocksuite/affine-shared/services'; import { getMostCommonResolvedValue, @@ -37,6 +38,7 @@ import { LINE_STYLE_LIST, renderMenu, } from '@blocksuite/affine-widget-edgeless-toolbar'; +import { BlockFlavourIdentifier } from '@blocksuite/block-std'; import { Bound } from '@blocksuite/global/gfx'; import { AddTextIcon, @@ -417,3 +419,8 @@ function updateModelsWith< .updateElement(model.id, { [field]: value }); } } + +export const connectorToolbarExtension = ToolbarModuleExtension({ + id: BlockFlavourIdentifier('affine:surface:connector'), + config: connectorToolbarConfig, +}); diff --git a/blocksuite/affine/gfx/mindmap/package.json b/blocksuite/affine/gfx/mindmap/package.json new file mode 100644 index 000000000..b90f1f413 --- /dev/null +++ b/blocksuite/affine/gfx/mindmap/package.json @@ -0,0 +1,50 @@ +{ + "name": "@blocksuite/affine-gfx-mindmap", + "description": "Gfx mindmap for BlockSuite.", + "type": "module", + "scripts": { + "build": "tsc" + }, + "sideEffects": false, + "keywords": [], + "author": "toeverything", + "license": "MIT", + "dependencies": { + "@blocksuite/affine-block-attachment": "workspace:*", + "@blocksuite/affine-block-edgeless-text": "workspace:*", + "@blocksuite/affine-block-image": "workspace:*", + "@blocksuite/affine-block-surface": "workspace:*", + "@blocksuite/affine-components": "workspace:*", + "@blocksuite/affine-gfx-text": "workspace:*", + "@blocksuite/affine-model": "workspace:*", + "@blocksuite/affine-rich-text": "workspace:*", + "@blocksuite/affine-shared": "workspace:*", + "@blocksuite/affine-widget-edgeless-toolbar": "workspace:*", + "@blocksuite/block-std": "workspace:*", + "@blocksuite/global": "workspace:*", + "@blocksuite/icons": "^2.2.6", + "@blocksuite/store": "workspace:*", + "@lit/context": "^1.1.2", + "@preact/signals-core": "^1.8.0", + "@toeverything/theme": "^1.1.12", + "@types/lodash-es": "^4.17.12", + "lit": "^3.2.0", + "lodash-es": "^4.17.21", + "minimatch": "^10.0.1", + "rxjs": "^7.8.1", + "simple-xml-to-json": "^1.2.2", + "yjs": "^13.6.21", + "zod": "^3.23.8" + }, + "exports": { + ".": "./src/index.ts", + "./effects": "./src/effects.ts" + }, + "files": [ + "src", + "dist", + "!src/__tests__", + "!dist/__tests__" + ], + "version": "0.20.0" +} diff --git a/blocksuite/affine/gfx/mindmap/src/effects.ts b/blocksuite/affine/gfx/mindmap/src/effects.ts new file mode 100644 index 000000000..9a8686bf3 --- /dev/null +++ b/blocksuite/affine/gfx/mindmap/src/effects.ts @@ -0,0 +1,20 @@ +import { MindMapPlaceholder } from './toolbar/mindmap-importing-placeholder'; +import { EdgelessMindmapMenu } from './toolbar/mindmap-menu'; +import { EdgelessMindmapToolButton } from './toolbar/mindmap-tool-button'; + +export function effects() { + customElements.define( + 'edgeless-mindmap-tool-button', + EdgelessMindmapToolButton + ); + customElements.define('edgeless-mindmap-menu', EdgelessMindmapMenu); + customElements.define('mindmap-import-placeholder', MindMapPlaceholder); +} + +declare global { + interface HTMLElementTagNameMap { + 'edgeless-mindmap-tool-button': EdgelessMindmapToolButton; + 'edgeless-mindmap-menu': EdgelessMindmapMenu; + 'mindmap-import-placeholder': MindMapPlaceholder; + } +} diff --git a/blocksuite/affine/gfx/mindmap/src/index.ts b/blocksuite/affine/gfx/mindmap/src/index.ts new file mode 100644 index 000000000..bcd942867 --- /dev/null +++ b/blocksuite/affine/gfx/mindmap/src/index.ts @@ -0,0 +1 @@ +export * from './toolbar/config'; diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/assets.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/assets.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/assets.ts rename to blocksuite/affine/gfx/mindmap/src/toolbar/assets.ts diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/basket-elements.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/basket-elements.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/basket-elements.ts rename to blocksuite/affine/gfx/mindmap/src/toolbar/basket-elements.ts diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/mindmap.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/config.ts similarity index 89% rename from blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/mindmap.ts rename to blocksuite/affine/gfx/mindmap/src/toolbar/config.ts index 5e7b95e29..94f2a2623 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/configs/toolbar/mindmap.ts +++ b/blocksuite/affine/gfx/mindmap/src/toolbar/config.ts @@ -9,15 +9,17 @@ import { MindmapElementModel, MindmapStyle, } from '@blocksuite/affine-model'; -import type { - ToolbarContext, - ToolbarModuleConfig, +import { + type ToolbarContext, + type ToolbarModuleConfig, + ToolbarModuleExtension, } from '@blocksuite/affine-shared/services'; import { getMostCommonValue } from '@blocksuite/affine-shared/utils'; import { type MenuItem, renderMenu, } from '@blocksuite/affine-widget-edgeless-toolbar'; +import { BlockFlavourIdentifier } from '@blocksuite/block-std'; import { RadiantIcon, RightLayoutIcon, StyleIcon } from '@blocksuite/icons/lit'; const MINDMAP_STYLE_LIST = [ @@ -102,7 +104,7 @@ export const createMindmapLayoutActionMenu = ( }); }; -export const builtinMindmapToolbarConfig = { +export const mindmapToolbarConfig = { actions: [ { id: 'a.style', @@ -126,3 +128,8 @@ export const builtinMindmapToolbarConfig = { when: ctx => ctx.getSurfaceModelsByType(MindmapElementModel).length > 0, } as const satisfies ToolbarModuleConfig; + +export const mindmapToolbarExtension = ToolbarModuleExtension({ + id: BlockFlavourIdentifier('affine:surface:mindmap'), + config: mindmapToolbarConfig, +}); diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/icons.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/icons.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/icons.ts rename to blocksuite/affine/gfx/mindmap/src/toolbar/icons.ts diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/mindmap-importing-placeholder.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-importing-placeholder.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/mindmap-importing-placeholder.ts rename to blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-importing-placeholder.ts diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/mindmap-menu.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-menu.ts similarity index 96% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/mindmap-menu.ts rename to blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-menu.ts index 70c4851cb..9a0b54ff2 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/mindmap-menu.ts +++ b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-menu.ts @@ -21,7 +21,6 @@ import { css, html, LitElement, nothing, type TemplateResult } from 'lit'; import { property } from 'lit/decorators.js'; import { repeat } from 'lit/directives/repeat.js'; -import type { EdgelessRootBlockComponent } from '../../../index.js'; import { getMindMaps, type ToolbarMindmapItem } from './assets.js'; import { mediaRender, textRender } from './basket-elements.js'; import { importMindMapIcon, mindmapMenuMediaIcon, textIcon } from './icons.js'; @@ -124,12 +123,9 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin( ToolbarMindmapItem | TextItem | ImportItem | MediaItem >; + // @ts-expect-error FIXME: resolve after gfx tool refactor override type = 'empty' as const; - private get _rootBlock(): EdgelessRootBlockComponent { - return this.std.view.getBlock(this.model.id) as EdgelessRootBlockComponent; - } - get mindMaps() { return getMindMaps(this.theme); } @@ -164,7 +160,7 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin( }} @click=${() => { this.draggableController.cancel(); - const viewportBound = this._rootBlock.service.viewport.viewportBounds; + const viewportBound = this.gfx.viewport.viewportBounds; viewportBound.x += viewportBound.w / 2; viewportBound.y += viewportBound.h / 2; @@ -183,16 +179,13 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin( } private _onImportMindMap(bound: Bound) { - const edgelessBlock = this._rootBlock; - if (!edgelessBlock) return; - const placeholder = new MindMapPlaceholder(); placeholder.style.position = 'absolute'; placeholder.style.left = `${bound.x}px`; placeholder.style.top = `${bound.y}px`; - edgelessBlock.gfxViewportElm.append(placeholder); + this.gfx.viewport.element?.append(placeholder); this.onImportMindMap?.(bound) .then(() => { @@ -230,6 +223,7 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin( this.onActiveStyleChange?.(element.data.style); } // a workaround to active mindmap, so that menu cannot be closed by `Escape` + // @ts-expect-error FIXME: resolve after gfx tool refactor this.setEdgelessTool({ type: 'empty' }); }, onDrop: (element, bound) => { @@ -240,6 +234,7 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin( if (!id) return; if (element.data.type === 'mindmap') { this.onActiveStyleChange?.(element.data.style); + // @ts-expect-error FIXME: resolve after gfx tool refactor this.setEdgelessTool({ type: 'default' }); this.gfx.selection.set({ elements: [id], @@ -249,6 +244,7 @@ export class EdgelessMindmapMenu extends EdgelessToolbarToolMixin( element.data.type === 'text' || element.data.type === 'media' ) { + // @ts-expect-error FIXME: resolve after gfx tool refactor this.setEdgelessTool({ type: 'default' }); } }) diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/mindmap-tool-button.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts similarity index 97% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/mindmap-tool-button.ts rename to blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts index 7b1bf2f48..eddacf52c 100644 --- a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/mindmap-tool-button.ts +++ b/blocksuite/affine/gfx/mindmap/src/toolbar/mindmap-tool-button.ts @@ -150,6 +150,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin( override enableActiveBackground = true; + // @ts-expect-error FIXME: resolve after gfx tool refactor override type: GfxToolsFullOptionValue['type'][] = ['empty', 'text']; get draggableTools(): DraggableTool[] { @@ -191,6 +192,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin( private _toggleMenu() { if (this.tryDisposePopper()) return; + // @ts-expect-error FIXME: resolve after gfx tool refactor this.setEdgelessTool({ type: 'default' }); const menu = this.createPopper('edgeless-mindmap-menu', this); @@ -211,6 +213,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin( const element = this.crud.getElementById(id) as MindmapElementModel; this.tryDisposePopper(); + // @ts-expect-error FIXME: resolve after gfx tool refactor this.setEdgelessTool({ type: 'default' }); this.gfx.selection.set({ elements: [element.tree.id], @@ -271,12 +274,14 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin( if (!id) return; this.readyToDrop = false; if (el.data.name === 'mindmap') { + // @ts-expect-error FIXME: resolve after gfx tool refactor this.setEdgelessTool({ type: 'default' }); this.gfx.selection.set({ elements: [id], editing: false, }); } else if (el.data.name === 'text') { + // @ts-expect-error FIXME: resolve after gfx tool refactor this.setEdgelessTool({ type: 'default' }); } }) @@ -309,6 +314,7 @@ export class EdgelessMindmapToolButton extends EdgelessToolbarToolMixin( }); return; } + // @ts-expect-error FIXME: resolve after gfx tool refactor this.setEdgelessTool({ type: 'empty' }); const icon = this.mindmapElement; const { x, y } = gfx.tool.lastMousePos$.peek(); diff --git a/blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/utils/import-mindmap.ts b/blocksuite/affine/gfx/mindmap/src/toolbar/utils/import-mindmap.ts similarity index 100% rename from blocksuite/affine/blocks/block-root/src/edgeless/components/toolbar/mindmap/utils/import-mindmap.ts rename to blocksuite/affine/gfx/mindmap/src/toolbar/utils/import-mindmap.ts diff --git a/blocksuite/affine/gfx/mindmap/tsconfig.json b/blocksuite/affine/gfx/mindmap/tsconfig.json new file mode 100644 index 000000000..0f14f3d7a --- /dev/null +++ b/blocksuite/affine/gfx/mindmap/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../../tsconfig.json", + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "tsBuildInfoFile": "./dist/tsconfig.tsbuildinfo" + }, + "include": ["./src"], + "references": [ + { "path": "../../blocks/block-attachment" }, + { "path": "../../blocks/block-edgeless-text" }, + { "path": "../../blocks/block-image" }, + { "path": "../../blocks/block-surface" }, + { "path": "../../components" }, + { "path": "../text" }, + { "path": "../../model" }, + { "path": "../../rich-text" }, + { "path": "../../shared" }, + { "path": "../../widgets/widget-edgeless-toolbar" }, + { "path": "../../../framework/block-std" }, + { "path": "../../../framework/global" }, + { "path": "../../../framework/store" } + ] +} diff --git a/blocksuite/affine/gfx/shape/package.json b/blocksuite/affine/gfx/shape/package.json index f6922531e..d9f04e08a 100644 --- a/blocksuite/affine/gfx/shape/package.json +++ b/blocksuite/affine/gfx/shape/package.json @@ -12,6 +12,7 @@ "dependencies": { "@blocksuite/affine-block-surface": "workspace:*", "@blocksuite/affine-components": "workspace:*", + "@blocksuite/affine-gfx-mindmap": "workspace:*", "@blocksuite/affine-model": "workspace:*", "@blocksuite/affine-rich-text": "workspace:*", "@blocksuite/affine-shared": "workspace:*", diff --git a/blocksuite/affine/gfx/shape/src/toolbar/config.ts b/blocksuite/affine/gfx/shape/src/toolbar/config.ts index eaeb7131e..c8cef5037 100644 --- a/blocksuite/affine/gfx/shape/src/toolbar/config.ts +++ b/blocksuite/affine/gfx/shape/src/toolbar/config.ts @@ -7,6 +7,10 @@ import { type PickColorEvent, } from '@blocksuite/affine-components/color-picker'; import type { LineDetailType } from '@blocksuite/affine-components/edgeless-line-styles-panel'; +import { + createMindmapLayoutActionMenu, + createMindmapStyleActionMenu, +} from '@blocksuite/affine-gfx-mindmap'; import { type Color, DefaultTheme, @@ -24,9 +28,10 @@ import { ShapeType, StrokeStyle, } from '@blocksuite/affine-model'; -import type { - ToolbarGenericAction, - ToolbarModuleConfig, +import { + type ToolbarGenericAction, + type ToolbarModuleConfig, + ToolbarModuleExtension, } from '@blocksuite/affine-shared/services'; import { getMostCommonValue } from '@blocksuite/affine-shared/utils'; import { @@ -35,6 +40,7 @@ import { LINE_STYLE_LIST, renderMenu, } from '@blocksuite/affine-widget-edgeless-toolbar'; +import { BlockFlavourIdentifier } from '@blocksuite/block-std'; import { Bound } from '@blocksuite/global/gfx'; import { AddTextIcon, ShapeIcon } from '@blocksuite/icons/lit'; import { html } from 'lit'; @@ -46,6 +52,56 @@ import { ShapeComponentConfig } from './shape-menu-config'; export const shapeToolbarConfig = { actions: [ + { + id: 'a.mindmap-style', + when(ctx) { + const models = ctx.getSurfaceModelsByType(ShapeElementModel); + return models.some(hasGrouped); + }, + content(ctx) { + const models = ctx.getSurfaceModelsByType(ShapeElementModel); + if (!models.length) return null; + + let mindmaps = models + .map(model => model.group) + .filter(model => ctx.matchModel(model, MindmapElementModel)); + if (!mindmaps.length) return null; + + // Not displayed when there is both a normal shape and a mindmap shape. + if (models.length !== mindmaps.length) return null; + + mindmaps = Array.from(new Set(mindmaps)); + + return createMindmapStyleActionMenu(ctx, mindmaps); + }, + }, + { + id: 'b.mindmap-layout', + when(ctx) { + const models = ctx.getSurfaceModelsByType(ShapeElementModel); + return models.some(hasGrouped); + }, + content(ctx) { + const models = ctx.getSurfaceModelsByType(ShapeElementModel); + if (!models.length) return null; + + let mindmaps = models + .map(model => model.group) + .filter(model => ctx.matchModel(model, MindmapElementModel)); + if (!mindmaps.length) return null; + + // Not displayed when there is both a normal shape and a mindmap shape. + if (models.length !== mindmaps.length) return null; + + mindmaps = Array.from(new Set(mindmaps)); + + // It's a sub node. + if (models.length === 1 && mindmaps[0].tree.element !== models[0]) + return null; + + return createMindmapLayoutActionMenu(ctx, mindmaps); + }, + }, { id: 'c.switch-type', when(ctx) { @@ -326,3 +382,8 @@ function getTextColor(fillColor: Color, isNotTransparent = false) { function hasGrouped(model: ShapeElementModel) { return model.group instanceof MindmapElementModel; } + +export const shapeToolbarExtension = ToolbarModuleExtension({ + id: BlockFlavourIdentifier('affine:surface:shape'), + config: shapeToolbarConfig, +}); diff --git a/blocksuite/affine/gfx/shape/tsconfig.json b/blocksuite/affine/gfx/shape/tsconfig.json index 5b280fe89..749cbf30f 100644 --- a/blocksuite/affine/gfx/shape/tsconfig.json +++ b/blocksuite/affine/gfx/shape/tsconfig.json @@ -9,6 +9,7 @@ "references": [ { "path": "../../blocks/block-surface" }, { "path": "../../components" }, + { "path": "../mindmap" }, { "path": "../../model" }, { "path": "../../rich-text" }, { "path": "../../shared" }, diff --git a/blocksuite/affine/gfx/text/src/toolbar/config.ts b/blocksuite/affine/gfx/text/src/toolbar/config.ts index 8d084e052..c1df98557 100644 --- a/blocksuite/affine/gfx/text/src/toolbar/config.ts +++ b/blocksuite/affine/gfx/text/src/toolbar/config.ts @@ -3,8 +3,12 @@ import { TextUtils, } from '@blocksuite/affine-block-surface'; import { TextElementModel } from '@blocksuite/affine-model'; -import { type ToolbarModuleConfig } from '@blocksuite/affine-shared/services'; +import { + type ToolbarModuleConfig, + ToolbarModuleExtension, +} from '@blocksuite/affine-shared/services'; import { createTextActions } from '@blocksuite/affine-widget-edgeless-toolbar'; +import { BlockFlavourIdentifier } from '@blocksuite/block-std'; import { Bound } from '@blocksuite/global/gfx'; export const textToolbarConfig = { @@ -46,3 +50,8 @@ export const textToolbarConfig = { when: ctx => ctx.getSurfaceModelsByType(TextElementModel).length > 0, } as const satisfies ToolbarModuleConfig; + +export const textToolbarExtension = ToolbarModuleExtension({ + id: BlockFlavourIdentifier('affine:surface:text'), + config: textToolbarConfig, +}); diff --git a/tools/utils/src/workspace.gen.ts b/tools/utils/src/workspace.gen.ts index f2aa524f4..b12f056dd 100644 --- a/tools/utils/src/workspace.gen.ts +++ b/tools/utils/src/workspace.gen.ts @@ -29,6 +29,7 @@ export const PackageList = [ 'blocksuite/affine/fragments/fragment-frame-panel', 'blocksuite/affine/fragments/fragment-outline', 'blocksuite/affine/gfx/connector', + 'blocksuite/affine/gfx/mindmap', 'blocksuite/affine/gfx/note', 'blocksuite/affine/gfx/shape', 'blocksuite/affine/gfx/text', @@ -310,6 +311,7 @@ export const PackageList = [ 'blocksuite/affine/components', 'blocksuite/affine/fragments/fragment-doc-title', 'blocksuite/affine/gfx/connector', + 'blocksuite/affine/gfx/mindmap', 'blocksuite/affine/gfx/note', 'blocksuite/affine/gfx/shape', 'blocksuite/affine/gfx/text', @@ -460,6 +462,25 @@ export const PackageList = [ 'blocksuite/framework/store', ], }, + { + location: 'blocksuite/affine/gfx/mindmap', + name: '@blocksuite/affine-gfx-mindmap', + workspaceDependencies: [ + 'blocksuite/affine/blocks/block-attachment', + 'blocksuite/affine/blocks/block-edgeless-text', + 'blocksuite/affine/blocks/block-image', + 'blocksuite/affine/blocks/block-surface', + 'blocksuite/affine/components', + 'blocksuite/affine/gfx/text', + 'blocksuite/affine/model', + 'blocksuite/affine/rich-text', + 'blocksuite/affine/shared', + 'blocksuite/affine/widgets/widget-edgeless-toolbar', + 'blocksuite/framework/block-std', + 'blocksuite/framework/global', + 'blocksuite/framework/store', + ], + }, { location: 'blocksuite/affine/gfx/note', name: '@blocksuite/affine-gfx-note', @@ -484,6 +505,7 @@ export const PackageList = [ workspaceDependencies: [ 'blocksuite/affine/blocks/block-surface', 'blocksuite/affine/components', + 'blocksuite/affine/gfx/mindmap', 'blocksuite/affine/model', 'blocksuite/affine/rich-text', 'blocksuite/affine/shared', @@ -1093,6 +1115,7 @@ export type PackageName = | '@blocksuite/affine-fragment-frame-panel' | '@blocksuite/affine-fragment-outline' | '@blocksuite/affine-gfx-connector' + | '@blocksuite/affine-gfx-mindmap' | '@blocksuite/affine-gfx-note' | '@blocksuite/affine-gfx-shape' | '@blocksuite/affine-gfx-text' diff --git a/tsconfig.json b/tsconfig.json index 37d1092c6..035de53e3 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -76,6 +76,7 @@ { "path": "./blocksuite/affine/fragments/fragment-frame-panel" }, { "path": "./blocksuite/affine/fragments/fragment-outline" }, { "path": "./blocksuite/affine/gfx/connector" }, + { "path": "./blocksuite/affine/gfx/mindmap" }, { "path": "./blocksuite/affine/gfx/note" }, { "path": "./blocksuite/affine/gfx/shape" }, { "path": "./blocksuite/affine/gfx/text" }, diff --git a/yarn.lock b/yarn.lock index 934e84ee3..9349075e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2734,6 +2734,7 @@ __metadata: "@blocksuite/affine-components": "workspace:*" "@blocksuite/affine-fragment-doc-title": "workspace:*" "@blocksuite/affine-gfx-connector": "workspace:*" + "@blocksuite/affine-gfx-mindmap": "workspace:*" "@blocksuite/affine-gfx-note": "workspace:*" "@blocksuite/affine-gfx-shape": "workspace:*" "@blocksuite/affine-gfx-text": "workspace:*" @@ -2997,6 +2998,38 @@ __metadata: languageName: unknown linkType: soft +"@blocksuite/affine-gfx-mindmap@workspace:*, @blocksuite/affine-gfx-mindmap@workspace:blocksuite/affine/gfx/mindmap": + version: 0.0.0-use.local + resolution: "@blocksuite/affine-gfx-mindmap@workspace:blocksuite/affine/gfx/mindmap" + dependencies: + "@blocksuite/affine-block-attachment": "workspace:*" + "@blocksuite/affine-block-edgeless-text": "workspace:*" + "@blocksuite/affine-block-image": "workspace:*" + "@blocksuite/affine-block-surface": "workspace:*" + "@blocksuite/affine-components": "workspace:*" + "@blocksuite/affine-gfx-text": "workspace:*" + "@blocksuite/affine-model": "workspace:*" + "@blocksuite/affine-rich-text": "workspace:*" + "@blocksuite/affine-shared": "workspace:*" + "@blocksuite/affine-widget-edgeless-toolbar": "workspace:*" + "@blocksuite/block-std": "workspace:*" + "@blocksuite/global": "workspace:*" + "@blocksuite/icons": "npm:^2.2.6" + "@blocksuite/store": "workspace:*" + "@lit/context": "npm:^1.1.2" + "@preact/signals-core": "npm:^1.8.0" + "@toeverything/theme": "npm:^1.1.12" + "@types/lodash-es": "npm:^4.17.12" + lit: "npm:^3.2.0" + lodash-es: "npm:^4.17.21" + minimatch: "npm:^10.0.1" + rxjs: "npm:^7.8.1" + simple-xml-to-json: "npm:^1.2.2" + yjs: "npm:^13.6.21" + zod: "npm:^3.23.8" + languageName: unknown + linkType: soft + "@blocksuite/affine-gfx-note@workspace:*, @blocksuite/affine-gfx-note@workspace:blocksuite/affine/gfx/note": version: 0.0.0-use.local resolution: "@blocksuite/affine-gfx-note@workspace:blocksuite/affine/gfx/note" @@ -3033,6 +3066,7 @@ __metadata: dependencies: "@blocksuite/affine-block-surface": "workspace:*" "@blocksuite/affine-components": "workspace:*" + "@blocksuite/affine-gfx-mindmap": "workspace:*" "@blocksuite/affine-model": "workspace:*" "@blocksuite/affine-rich-text": "workspace:*" "@blocksuite/affine-shared": "workspace:*" @@ -3540,6 +3574,7 @@ __metadata: "@blocksuite/affine-fragment-frame-panel": "workspace:*" "@blocksuite/affine-fragment-outline": "workspace:*" "@blocksuite/affine-gfx-connector": "workspace:*" + "@blocksuite/affine-gfx-mindmap": "workspace:*" "@blocksuite/affine-gfx-note": "workspace:*" "@blocksuite/affine-gfx-shape": "workspace:*" "@blocksuite/affine-gfx-text": "workspace:*"