Skip to content

Stencil

This chapter mainly introduces knowledge related to the Stencil plugin. By reading, you can learn about

  • How to enhance DnD with the Stencil plugin
  • How to configure grouping, collapse, and search

Usage

Stencil is a further encapsulation based on Dnd, providing a sidebar-like UI component that supports grouping, collapsing, searching, and more. You can enable the stencil sidebar with the Stencil plugin. Example:

import { Graph, Stencil } from '@antv/x6'

const graph = new Graph({
  background: {
    color: '#F2F7FA',
  },
})

const stencil = new Stencil({
  target: graph,
  groups: [
    {
      name: 'group1',
    },
    {
      name: 'group2',
    },
  ],
})

const rect1 = graph.createNode({
  shape: 'rect',
  width: 100,
  height: 40,
})
const rect2 = rect1.clone()

// A DOM container to hold the stencil, stencilContainer
stencilContainer.appendChild(stencil.container)
stencil.load([rect1, rect2], 'group1')

Demo

import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph, Stencil, Snapline } from '@antv/x6'

const commonAttrs = {
  body: {
    fill: '#fff',
    stroke: '#8f8f8f',
    strokeWidth: 1,
  },
}

export default class Example extends React.Component {
  private container: HTMLDivElement
  private stencilContainer: HTMLDivElement

  componentDidMount() {
    const graph = new Graph({
      container: this.container,
      background: {
        color: '#F2F7FA',
      },
    })
    graph.use(
      new Snapline({
        enabled: true,
        sharp: true,
      }),
    )

    const source = graph.addNode({
      x: 130,
      y: 30,
      width: 100,
      height: 40,
      label: 'Hello',
      attrs: {
        body: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
          fill: '#fff',
          rx: 6,
          ry: 6,
        },
      },
    })

    const target = graph.addNode({
      x: 320,
      y: 240,
      width: 100,
      height: 40,
      label: 'World',
      attrs: {
        body: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
          fill: '#fff',
          rx: 6,
          ry: 6,
        },
      },
    })

    graph.addEdge({
      source,
      target,
      attrs: {
        line: {
          stroke: '#8f8f8f',
          strokeWidth: 1,
        },
      },
    })

    graph.centerContent()

    const stencil = new Stencil({
      title: 'Stencil',
      target: graph,
      search(cell, keyword) {
        return cell.shape.indexOf(keyword) !== -1
      },
      placeholder: 'Search by shape name',
      notFoundText: 'Not Found',
      collapsable: true,
      stencilGraphHeight: 0,
      groups: [
        {
          name: 'group1',
          title: 'Group(Collapsable)',
        },
        {
          name: 'group2',
          title: 'Group',
          collapsable: false,
        },
      ],
    })

    this.stencilContainer.appendChild(stencil.container)

    const n1 = graph.createNode({
      shape: 'rect',
      x: 40,
      y: 40,
      width: 80,
      height: 40,
      label: 'rect',
      attrs: commonAttrs,
    })

    const n2 = graph.createNode({
      shape: 'circle',
      x: 180,
      y: 40,
      width: 40,
      height: 40,
      label: 'circle',
      attrs: commonAttrs,
    })

    const n3 = graph.createNode({
      shape: 'ellipse',
      x: 280,
      y: 40,
      width: 80,
      height: 40,
      label: 'ellipse',
      attrs: commonAttrs,
    })

    const n4 = graph.createNode({
      shape: 'path',
      x: 420,
      y: 40,
      width: 40,
      height: 40,
      // https://www.svgrepo.com/svg/13653/like
      path: 'M24.85,10.126c2.018-4.783,6.628-8.125,11.99-8.125c7.223,0,12.425,6.179,13.079,13.543c0,0,0.353,1.828-0.424,5.119c-1.058,4.482-3.545,8.464-6.898,11.503L24.85,48L7.402,32.165c-3.353-3.038-5.84-7.021-6.898-11.503c-0.777-3.291-0.424-5.119-0.424-5.119C0.734,8.179,5.936,2,13.159,2C18.522,2,22.832,5.343,24.85,10.126z',
      attrs: commonAttrs,
      label: 'path',
    })

    stencil.load([n1, n2], 'group1')
    stencil.load([n3, n4], 'group2')
  }

  refContainer = (container: HTMLDivElement) => {
    this.container = container
  }

  refStencil = (container: HTMLDivElement) => {
    this.stencilContainer = container
  }

  render() {
    return (
      <div className="stencil-app">
        <div className="app-stencil" ref={this.refStencil} />
        <div className="app-content" ref={this.refContainer} />
      </div>
    )
  }
}

const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })

Configuration

Option Type Required Default Value Description
title string 'Stencil' Title.
groups Group[] ✓️ - Group information.
search Filter false Search option.
placeholder string 'Search' Placeholder text for the search input.
notFoundText string 'No matches found' Text displayed when no search results are found.
collapsable boolean false Whether to show a global collapse/expand button.
layout (this: Stencil, model: Model, group?: Group | null) => any Grid layout Layout method for nodes in the stencil canvas.
layoutOptions any - Layout options.
stencilGraphWidth number 200 Width of the stencil canvas.
stencilGraphHeight number 800 Height of the stencil canvas. Set to 0 for auto-adjustment.
stencilGraphPadding number 10 Padding for the stencil canvas.
stencilGraphOptions Graph.Options - Options for the stencil canvas.

Tip

In addition to the configurations above, Stencil also inherits all configurations from Dnd.

Grouping

When initializing, a template canvas will be rendered in each group according to the groups provided. The type definition for groups is as follows:

export interface Group {
  name: string // Group name
  title?: string // Group title, defaults to `name`
  collapsable?: boolean // Whether the group is collapsible, defaults to true
  collapsed?: boolean // Initial state whether it is collapsed
  nodeMovable?: boolean // Whether nodes in the stencil canvas can be dragged
  graphWidth?: number // Width of the stencil canvas
  graphHeight?: number // Height of the stencil canvas
  graphPadding?: number // Padding for the stencil canvas
  graphOptions?: Graph.Options // Options for the stencil canvas
  layout?: (this: Stencil, model: Model, group?: Group | null) => any
  layoutOptions?: any // Layout options
}

As you can see, some configurations within the group overlap with the outer configurations, such as graphWidth/stencilGraphWidth and graphHeight/stencilGraphHeight, with the group configurations taking higher priority.

Layout

When adding nodes, use the group or global layout and layoutOptions to automatically layout the nodes. By default, the grid layout method is used to layout the template nodes, and the supported layout options are:

Option Type Default Value Description
columns number 2 Number of columns in the grid layout, defaults to 2. The number of rows is calculated automatically based on the number of nodes.
columnWidth number | ‘auto’ | ‘compact’ 'auto' Column width. auto: width of the widest node among all nodes, compact: width of the widest node in that column.
rowHeight number | ‘auto’ | ‘compact’ 'auto' Row height. auto: height of the tallest node among all nodes, compact: height of the tallest node in that row.
dx number 10 Offset of the cell on the X-axis, defaults to 10.
dy number 10 Offset of the cell on the Y-axis, defaults to 10.
marginX number 0 Margin of the cell on the X-axis, defaults to 0.
marginY number 0 Margin of the cell on the Y-axis, defaults to 0.
center boolean true Whether the nodes are center-aligned with the grid, defaults to true.
resizeToFit boolean false Whether to automatically adjust the size of the nodes to fit the grid size, defaults to false.

You can also implement a custom layout according to the signature (this: Stencil, model: Model, group?: Group | null) => any.

Stencil also provides search capabilities.

// Only search for rect nodes
const stencil = new Stencil({
  search: (cell, keyword, groupName, stencil) => {
    if (keyword) {
      return cell.shape === 'rect'
    }
    return true
  },
})

// Search for rect nodes whose text contains the keyword
const stencil = new Stencil({
  search: (cell, keyword, groupName, stencil) => {
    if (keyword) {
      return cell.shape === 'rect' && cell.attr('text/text').includes(keyword)
    }
    return true
  },
})

API

stencil.load(…)

load(nodes: (Node | Node.Metadata)[], groupName?: string): this

Load nodes. Parameters are as follows:

Name Type Required Default Value Description
nodes (Node | Node.Metadata)[] ✓️ - Nodes to load.
groupName string - Name of the group to load nodes into.

stencil.unload(…)

unload(nodes: (Node | Node.Metadata)[], groupName?: string): this

Unload nodes. Parameters are as follows:

Name Type Required Default Value Description
nodes (Node | Node.Metadata)[] ✓️ - Nodes to unload.
groupName string - Name of the group to unload nodes from.

stencil.addGroup(…)

addGroup(group: Stencil.Group | Stencil.Group[])

Add a new group.

stencil.addGroup({
  name: 'group1',
  graphHeight: 100,
})

stencil.removeGroup(…)

removeGroup(groupName: string | string[])

Remove a group.

stencil.resizeGroup(…)

resizeGroup(groupName: string, size: { width: number; height: number })

Modify the size of a group.

© Copyright 2026 Ant Group Co., Ltd..备案号:京ICP备15032932号-38
Site search
Documentation and examplesType to start searching

Search documentation, APIs, and examples

↑↓Select↵OpenEscClose