Skip to content

BlockSuite API Documentation / @blocksuite/affine-gfx-mindmap

@blocksuite/affine-gfx-mindmap

Classes

Extension

MindMapDragExtension

Understanding Extensions

Extensions provide a way to extend the functionality of a system using dependency injection. They allow you to register services, implementations, and factories in the DI container, which can then be retrieved and used by different parts of the application.

Extensions are particularly useful for:

  • Registering different implementations for different types
  • Creating pluggable architecture where components can be added or removed
  • Managing dependencies between different parts of the application

Usage Example: Fruit Processing System

Let's consider a fruit processing system where different types of fruits need different processing methods. We'll show how to implement this using extensions.

Step 1: Define the interfaces

ts
interface FruitProcessor {
  process(fruit: Fruit): void;
}

interface Fruit {
  type: string;
  // other properties
}

Step 2: Create a service identifier

ts
import { createIdentifier } from '@blocksuite/global/di';

const FruitProcessorProvider = createIdentifier<FruitProcessor>('fruit-processor-provider');

Step 3: Create implementations

ts
class AppleProcessor implements FruitProcessor {
  process(fruit: Fruit): void {
    console.log('Slicing apple');
    // Apple-specific processing
  }
}

class BananaProcessor implements FruitProcessor {
  process(fruit: Fruit): void {
    console.log('Peeling banana');
    // Banana-specific processing
  }
}

Step 4: Create an extension factory

ts
const FruitProcessorExtension = (
  fruitType: string,
  implementation: new () => FruitProcessor
): ExtensionType => {
  return {
    setup: di => {
      di.addImpl(FruitProcessorProvider(fruitType), implementation);
    }
  };
};

Step 5: Create concrete extensions

ts
export const AppleProcessorExtension = FruitProcessorExtension('apple', AppleProcessor);
export const BananaProcessorExtension = FruitProcessorExtension('banana', BananaProcessor);

Step 6: Use the extensions

ts
import { Container } from '@blocksuite/global/di';

class FruitProcessingSystem {
  provider: ServiceProvider;

  constructor(extensions: ExtensionType[]) {
    const container = new Container();

    // Set up all extensions
    extensions.forEach(ext => ext.setup(container));

    // Create a provider from the container
    this.provider = container.provider();
  }

  processFruit(fruit: Fruit) {
    // Get the appropriate processor based on fruit type
    const processor = this.provider.get(FruitProcessorProvider(fruit.type));

    // Process the fruit
    processor.process(fruit);
  }
}

// Initialize the system with extensions
const system = new FruitProcessingSystem([
  AppleProcessorExtension,
  BananaProcessorExtension
]);

// Use the system
system.processFruit({ type: 'apple' });  // Output: Slicing apple
system.processFruit({ type: 'banana' }); // Output: Peeling banana

Note: We deliberately used a non-block specific example here. In BlockSuite, the extension pattern can be applied to any entity that can be configured by third parties, not just blocks. This includes different tools in the whiteboard, different column types in database blocks, and many other extensible components. The pattern remains the same regardless of what you're extending.

Extends
Constructors
Properties
key

static key: string = 'mind-map-drag'

Overrides

InteractivityExtension.key

Accessors
Methods
mounted()

mounted(): void

Returns

void

Overrides

InteractivityExtension.mounted

Other

MindMapIndicatorOverlay

An overlay is a layer covered on top of elements, can be used for rendering non-CRDT state indicators.

Extends
Constructors
Properties
currentDragPos

currentDragPos: IVec | null = null

direction

direction: LEFT | RIGHT = LayoutType.RIGHT

dragNodeImage

dragNodeImage: HTMLCanvasElement | null = null

dragNodePos

dragNodePos: IVec

mode

mode: ConnectorMode = ConnectorMode.Straight

parentBound

parentBound: Bound | null = null

pathGen

pathGen: PathGenerator

targetBound

targetBound: Bound | null = null

INDICATOR_SIZE

static INDICATOR_SIZE: number[]

overlayName

static overlayName: string = 'mindmap-indicator'

Overrides

Overlay.overlayName

Accessors
themeService
Get Signature

get themeService(): ThemeService

Returns

ThemeService

Methods
clear()

clear(): void

Returns

void

Overrides

Overlay.clear

render()

render(ctx): void

Parameters
ctx

CanvasRenderingContext2D

Returns

void

Overrides

Overlay.render

setIndicatorInfo()

setIndicatorInfo(options): void

Parameters
options
insertPosition

{ layoutDir: LEFT | RIGHT; position: "next" | "prev"; type: "sibling"; } | { layoutDir: LEFT | RIGHT; type: "child"; }

parent

MindmapNode

parentChildren

MindmapNode[]

path

number[]

target

MindmapNode

targetMindMap

MindmapElementModel

Returns

void


MindMapView

The methods that a graphic element should implement. It is already included in the GfxCompatibleInterface interface.

Extends
Constructors
Properties
type

static type: string = 'mindmap'

Overrides

GfxElementModelView.type

Accessors
Methods
getCollapseButton()

getCollapseButton(node): LocalShapeElementModel | undefined

Parameters
node

The mindmap node or its id to get the collapse button

string | MindmapNode

Returns

LocalShapeElementModel | undefined

onBoxSelected()

onBoxSelected(context): boolean

When the element is selected by box selection, return false to prevent the default selection behavior.

Parameters
context

BoxSelectionContext

Returns

boolean

Overrides

GfxElementModelView.onBoxSelected

onCreated()

onCreated(): void

Returns

void

Overrides

GfxElementModelView.onCreated

onDestroyed()

onDestroyed(): void

Called when the view is destroyed. Override this method requires calling super.onDestroyed().

Returns

void

Overrides

GfxElementModelView.onDestroyed

Variables

mindmap

const mindmap: ElementRenderer<MindmapElementModel>


MindmapDomRendererExtension

const MindmapDomRendererExtension: ExtensionType


MindmapElementRendererExtension

const MindmapElementRendererExtension: ExtensionType & object

Type Declaration

identifier

identifier: ServiceIdentifier<ElementRenderer<MindmapElementModel>>


MindMapInteraction

const MindMapInteraction: ExtensionType


mindMapSeniorTool

const mindMapSeniorTool: ExtensionType


MindmapStyleFour

const MindmapStyleFour: TemplateResult<1>


MindmapStyleOne

const MindmapStyleOne: TemplateResult<1>


MindmapStyleThree

const MindmapStyleThree: TemplateResult<1>


MindmapStyleTwo

const MindmapStyleTwo: TemplateResult<1>


mindmapToMarkdownAdapterMatcher

const mindmapToMarkdownAdapterMatcher: ExtensionType & object

Type Declaration

identifier

identifier: ServiceIdentifier<ElementToMarkdownAdapterMatcher>


mindmapToolbarConfig

const mindmapToolbarConfig: object

Type Declaration

actions

readonly actions: [{ id: "a.style"; content: TemplateResult<1> | null; }, { id: "b.layout"; content: TemplateResult<1> | null; }]

when()

readonly when: (ctx) => boolean

Parameters
ctx

ToolbarContext

Returns

boolean


mindmapToolbarExtension

const mindmapToolbarExtension: ExtensionType


mindmapToPlainTextAdapterMatcher

const mindmapToPlainTextAdapterMatcher: ExtensionType & object

Type Declaration

identifier

identifier: ServiceIdentifier<ElementToPlainTextAdapterMatcher>


NODE_FIRST_LEVEL_HORIZONTAL_SPACING

const NODE_FIRST_LEVEL_HORIZONTAL_SPACING: 200 = 200


NODE_HORIZONTAL_SPACING

const NODE_HORIZONTAL_SPACING: 110 = 110


NODE_VERTICAL_SPACING

const NODE_VERTICAL_SPACING: 45 = 45


shapeMindmapToolbarConfig

const shapeMindmapToolbarConfig: object

Type Declaration

actions

readonly actions: [{ id: "a.mindmap-style"; content: TemplateResult<1> | null; when: boolean; }, { id: "b.mindmap-layout"; content: TemplateResult<1> | null; when: boolean; }]

when()

readonly when: (ctx) => boolean

Parameters
ctx

ToolbarContext

Returns

boolean


shapeMindmapToolbarExtension

const shapeMindmapToolbarExtension: ExtensionType

Functions

addNode()

addNode(mindmap, parent, node, targetIndex?): void

Parameters

mindmap

MindmapElementModel

the mind map to add the node to

parent

the parent node or the parent node id

string | MindmapNode

node

MindmapNode

the node must be an detached node

targetIndex?

number

the index to insert the node at

Returns

void


addTree()

addTree(mindmap, parent, tree, sibling?): MindmapNode | null | undefined

Parameters

mindmap

MindmapElementModel

parent

string | MindmapNode

tree

MindmapNode | Node

sibling?

sibling indicates where to insert a subtree among peer elements. If it's a string, it represents a peer element's ID; if it's a number, it represents its index. The subtree will be inserted before the sibling element.

string | number

Returns

MindmapNode | null | undefined


applyStyle()

applyStyle(mindmap, shouldFitContent): void

Parameters

mindmap

MindmapElementModel

shouldFitContent

boolean = false

Returns

void


containsNode()

containsNode(mindmap, targetNode, searchParent?): boolean

Check if the mind map contains the target node.

Parameters

mindmap

MindmapElementModel

Mind map to check

targetNode

MindmapNode

Node to check

searchParent?

MindmapNode

If provided, check if the node is a descendant of the parent node. Otherwise, check the whole mind map.

Returns

boolean


createFromTree()

createFromTree(tree, style, layoutType, surface): MindmapElementModel

Parameters

tree

MindmapNode

style

MindmapStyle

layoutType

LayoutType

surface

SurfaceBlockModel

Returns

MindmapElementModel


createMindmapLayoutActionMenu()

createMindmapLayoutActionMenu(ctx, models): TemplateResult<1>

Parameters

ctx

ToolbarContext

models

MindmapElementModel[]

Returns

TemplateResult<1>


createMindmapStyleActionMenu()

createMindmapStyleActionMenu(ctx, models): TemplateResult<1>

Parameters

ctx

ToolbarContext

models

MindmapElementModel[]

Returns

TemplateResult<1>


detachMindmap()

detachMindmap(mindmap, subtree): MindmapNode | undefined

Detach a mindmap node or subtree. It is similar to removeChild but it does not delete the node.

So the node can be used to create a new mind map or merge into other mind map

Parameters

mindmap

MindmapElementModel

the mind map that the subtree belongs to

subtree

the subtree to detach

string | MindmapNode

Returns

MindmapNode | undefined


findTargetNode()

findTargetNode(mindmap, position): MindmapNode | null

Parameters

mindmap

MindmapElementModel

position

IVec

Returns

MindmapNode | null


getHoveredArea()

getHoveredArea(target, position, layoutDir): "top-left" | "top-right" | "bottom-left" | "bottom-right"

Parameters

target

ShapeElementModel

position

[number, number]

layoutDir

LayoutType

Returns

"top-left" | "top-right" | "bottom-left" | "bottom-right"


getNearestTranslation()

getNearestTranslation(viewport, element, padding): number[]

Parameters

viewport

Viewport

element

GfxModel

padding

[number, number] = ...

Returns

number[]


handleLayout()

handleLayout(mindmap, tree?, shouldApplyStyle?, layoutType?): void

Parameters

mindmap

MindmapElementModel

tree?

MindmapNode | MindmapRoot

shouldApplyStyle?

boolean = true

layoutType?

LayoutType

Returns

void


hideNodeConnector()

hideNodeConnector(mindmap, target): () => void | undefined

Hide the connector between the target node and its parent

Parameters

mindmap

MindmapElementModel

target

MindmapNode

The mind map node which's connector will be hide

Returns

() => void | undefined


isElementOutsideViewport()

isElementOutsideViewport(viewport, element, padding): boolean

Parameters

viewport

Viewport

element

GfxModel

padding

[number, number] = ...

Returns

boolean


isMindmapNode()

isMindmapNode(element): boolean

Parameters

element

GfxModel | GfxBlockElementModel<GfxCompatibleProps> | null

Returns

boolean


isSingleMindMapNode()

isSingleMindMapNode(els): boolean

Parameters

els

GfxModel[]

Returns

boolean


layout()

layout(root, mindmap, layoutDir, path): void

Parameters

root

MindmapNode

mindmap

MindmapElementModel

layoutDir

LayoutType | null

path

number[]

Returns

void


moveNode()

moveNode(from, subtree, to, parent, index): void | MindmapNode

Move a subtree from one mind map to another

Parameters

from

MindmapElementModel

the mind map that the subtree belongs to

subtree

MindmapNode

the subtree to move

to

MindmapElementModel

the mind map to move the subtree to

parent

the new parent node to attach the subtree to

string | MindmapNode

index

number

the index to insert the subtree at

Returns

void | MindmapNode


tryMoveNode()

tryMoveNode(targetMindMap, target, sourceMindMap, source, position, callback): { abort: () => void; merge: () => void; } | null

Try to move a node to another mind map. It will show a merge indicator if the node can be merged to the target mind map.

Parameters

targetMindMap

MindmapElementModel

target

MindmapNode

sourceMindMap

MindmapElementModel

source

MindmapNode

position

IVec

callback

(option) => () => void

Returns

{ abort: () => void; merge: () => void; } | null

return two functions, abort and merge. abort will cancel the operation and merge will merge the node to the target mind map.