User handle in React Diagram component

19 Sep 202424 minutes to read

  • User handles are used to add some frequently used commands around the selector. To create user handles, define and add them to the userHandles collection of the selectedItems property.
  • The name property of user handle is used to define the name of the user handle and its further used to find the user handle at runtime and do any customization.

Alignment

User handles can be aligned relative to the node boundaries. It has margin, offset, side, horizontalAlignment, and verticalAlignment settings. It is quite tricky when all four alignments are used together but gives more control over alignment.

Offset for user handle

The offset property of userHandles is used to align the user handle based on fractions. 0 represents top/left corner, 1 represents bottom/right corner, and 0.5 represents half of width/height.

Side

The side property of userHandles is used to align the user handle by using the Top, Bottom, Left, and Right options.

Horizontal and vertical alignments

The horizontalAlignment property of userHandles is used to set how the user handle is horizontally aligned at the position based on the offset. The verticalAlignment property is used to set how user handle is vertically aligned at the position.

Margin for the user handle

Margin is an absolute value used to add some blank space in any one of its four sides. The userHandles can be displaced with the margin property.

Appearance

The appearance of the user handle can be customized by using the size, borderColor, backgroundColor, visible, pathData, and pathColor properties of the userHandles.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent, MoveTool, SelectorConstraints, randomId, cloneObject } from "@syncfusion/ej2-react-diagrams";
let diagramInstance;
let shape = {
    type: 'Basic',
    shape: 'Rectangle'
};
let node1 = [{
        id: 'node',
        offsetX: 100,
        offsetY: 100,
        shape: shape
    }];
let handles = [{
        name: 'clone',
        pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3, 0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z',
        visible: true,
        offset: 0,
        side: 'Bottom',
        pathColor: "white",
        margin: {
            top: 0,
            bottom: 0,
            left: 0,
            right: 0
        }
    }];
ReactDOM.render(<DiagramComponent id="container" ref={diagram => (diagramInstance = diagram)} width={"100%"} height={"600px"} nodes={node1} selectedItems={{
        constraints: SelectorConstraints.UserHandle,
        userHandles: handles
    }} 
//set Node default value
getNodeDefaults={(node) => {
        node.height = 100;
        node.width = 100;
        node.style.fill = '#6BA5D7';
        node.style.strokeColor = '#6BA5D7';
        return node;
    }} 
//set CustomTool
getCustomTool={getTool}/>, document.getElementById("diagram"));
function getTool(action) {
    let tool;
    if (action === 'clone') {
        tool = new CloneTool(diagramInstance.commandHandler);
    }
    return tool;
}
//Defines the clone tool used to copy Node/Connector
class CloneTool extends MoveTool {
    mouseDown(args) {
        let newObject;
        if (diagramInstance.selectedItems.nodes.length > 0) {
            newObject = cloneObject(diagramInstance.selectedItems.nodes[0]);
        }
        else {
            newObject = cloneObject(diagramInstance.selectedItems.connectors[0]);
        }
        newObject.id += randomId();
        diagramInstance.paste([newObject]);
        args.source = diagramInstance.nodes[diagramInstance.nodes.length - 1];
        args.sourceWrapper = args.source.wrapper;
        super.mouseDown(args);
        this.inAction = true;
    }
}
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    DiagramComponent,
    NodeModel,
    ConnectorModel,
    Diagram,
    NodeModel,
    BasicShapeModel,
    MoveTool,
    MouseEventArgs,
    IElement,
    UserHandleModel,
    ToolBase,
    SelectorConstraints,
    Actions,
    randomId,
    cloneObject,
    Node,
    Side,
    SnapConstraints
    } from "@syncfusion/ej2-react-diagrams";
let diagramInstance: DiagramComponent;
let shape: BasicShapeModel = {
    type: 'Basic',
    shape: 'Rectangle'
};
let node1: NodeModel[] = [{
    id: 'node',
    offsetX: 100,
    offsetY: 100,
    shape: shape
}];
let handles: UserHandleModel[] = [{
    name: 'clone',
    pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3, 0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z',
    visible: true,
    offset: 0,
    side: 'Bottom',
    pathColor: "white",
    margin: {
        top: 0,
        bottom: 0,
        left: 0,
        right: 0
    }
}];
ReactDOM.render(
    <DiagramComponent id="container"  ref={diagram => (diagramInstance = diagram)}
    width = {
        "100%"
    }
    height = {
        "600px"
    }
    nodes = {
        node1
    }
    selectedItems = {
        {
              constraints: SelectorConstraints.UserHandle,
              userHandles: handles
        }
    }
    //set Node default value
    getNodeDefaults = {
        (node: NodeModel): NodeModel => {
            node.height = 100;
            node.width = 100;
            node.style.fill = '#6BA5D7';
            node.style.strokeColor = '#6BA5D7';
            return node;
        }
    }
    //set CustomTool
    getCustomTool = {
        getTool
    }
/> , document.getElementById("diagram")
);
function getTool(action: string): ToolBase {
    let tool: ToolBase;
    if (action === 'clone') {
        tool = new CloneTool(diagramInstance.commandHandler);
    }
    return tool;
}
//Defines the clone tool used to copy Node/Connector
class CloneTool extends MoveTool {
    public mouseDown(args: MouseEventArgs): void {
        let newObject: any;
        if (diagramInstance.selectedItems.nodes.length > 0) {
            newObject = cloneObject(diagramInstance.selectedItems.nodes[0]) as NodeModel;
        } else {
            newObject = cloneObject(diagramInstance.selectedItems.connectors[0]) as ConnectorModel;
        }
        newObject.id += randomId();
        diagramInstance.paste([newObject]);
        args.source = diagramInstance.nodes[diagramInstance.nodes.length - 1] as IElement;
        args.sourceWrapper = args.source.wrapper;
        super.mouseDown(args);
        this.inAction = true;
    }
}

Fixed user handles

The fixed user handles are used to add some frequently used commands around the node and connector even without selecting it.

Initialization an fixed user handles

To create the fixed user handles, define and add them to the collection of nodes and connectors property. The following code example used to create an fixed user handles for the nodes and connectors.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent } from "@syncfusion/ej2-react-diagrams";
let node = [{
        offsetX: 250,
        offsetY: 250,
        width: 100,
        height: 100,
        style: {
            fill: '#6BA5D7',
            strokeColor: 'white'
        },
        // A fixed user handle is created and stored in fixed user handle collection of Node.
        fixedUserHandles: [{ margin: { right: 20 }, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
    }];
function App() {
    return (<DiagramComponent id="container" width={'100%'} height={'600px'} nodes={node}/>);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    Diagram,
    DiagramComponent,
    NodeModel
} from "@syncfusion/ej2-react-diagrams";
let node: NodeModel[] = [{
    offsetX: 250,
    offsetY: 250,
    width: 100,
    height: 100,
    style: {
        fill: '#6BA5D7',
        strokeColor: 'white'
    },
    // A fixed user handle is created and stored in fixed user handle collection of Node.
    fixedUserHandles: [{ margin: { right: 20 }, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
}];
function App() {
  return (
    <DiagramComponent
      id="container"
      width={'100%'}
      height={'600px'}
      nodes={node}
    />
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);

Customization

  • The id property of fixed user handle is used to define the unique identification of the fixed user handle and it is further used to add custom events to the fixed user handle.

  • The fixed user handle can be positioned relative to the node and connector boundaries. It has offset, padding and cornerRadius settings. It is used to position and customize the fixed user handle.

  • The Padding is used to leave the space that is inside the fixed user handle between the icon and border.

  • The corner radius allows to create a fixed user handles with rounded corners. The radius of the rounded corner is set with the cornerRadius property.

Note: The PathData needs to be provided to render fixed user handle.

Size

Diagram allows you set size for the fixed user handles by using the width and height property. The default value of the width and height property is 10.

Style

  • You can change the style of the fixed user handles with the specific properties of borderColor, borderWidth, and background color using the handleStrokeColor, handleStrokeWidth, and fill properties, and the icon borderColor, and borderWidth using the iconStrokeColor and iconStrokeWidth.

  • The fixed user handle’s iconStrokeColor and iconStrokeWidth property used to change the stroke color and stroke width of the given pathData.

  • The fixed user handle handleStrokeColor and fill properties are used to define the background color and border color of the userhandle and the handleStrokeWidth property is used to define the border width of the fixed user handle.

  • The visible property of the fixed user handle enables or disables the visibility of fixed user handle.

The following code explains how to customize the appearance of the fixed user handles.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent } from "@syncfusion/ej2-react-diagrams";
let diagramInstance;
let node = [{
        offsetX: 150,
        offsetY: 150,
        width: 100,
        height: 100,
        style: {
            fill: '#6BA5D7',
            strokeColor: 'white'
        },
        // A fixed user handle is created and stored in fixed user handle collection of Node.
        fixedUserHandles: [{ offset: { x: 0, y: 0 }, margin: { right: 20 }, padding: { left: 3, right: 3, top: 3, bottom: 3 }, iconStrokeColor: 'white', fill: 'black', height: 20, width: 20, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
    }];
let connector = [{
        sourcePoint: {
            x: 300,
            y: 100
        },
        targetPoint: {
            x: 400,
            y: 200
        },
        type: 'Orthogonal',
        style: {
            strokeColor: '#6BA5D7'
        },
        // A fixed user handle is created and stored in fixed user handle collection of Connector.
        fixedUserHandles: [{ offset: 0.5, width: 20, alignment: 'Before', padding: { left: 3, right: 3, top: 3, bottom: 3 }, iconStrokeColor: 'white', fill: 'black', height: 20, id: 'usercon1', displacement: { x: 10, y: 10 }, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
    }];
function App() {
    return (<DiagramComponent id="container" width={'100%'} height={'600px'} nodes={node} connectors={connector}/>);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    Diagram,
    DiagramComponent,
    NodeModel
} from "@syncfusion/ej2-react-diagrams";
let diagramInstance: DiagramComponent;
let node: NodeModel[] = [{
    offsetX: 150,
    offsetY: 150,
    width: 100,
    height: 100,
    style: {
        fill: '#6BA5D7',
        strokeColor: 'white'
    },
    // A fixed user handle is created and stored in fixed user handle collection of Node.
    fixedUserHandles: [{ offset: { x: 0, y: 0 }, margin: { right: 20 },padding:{left:3,right:3,top:3,bottom:3},iconStrokeColor:'white',fill:'black', height: 20, width: 20, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
}];
let connector: ConnectorModel[] = [{
    sourcePoint: {
        x: 300,
        y: 100
    },
    targetPoint: {
        x: 400,
        y: 200
    },
    type: 'Orthogonal',
    style: {
        strokeColor: '#6BA5D7'
    },
    // A fixed user handle is created and stored in fixed user handle collection of Connector.
     fixedUserHandles: [{ offset: 0.5, width: 20, alignment: 'Before',padding:{left:3,right:3,top:3,bottom:3},iconStrokeColor:'white',fill:'black', height: 20, id: 'usercon1',  displacement:{x:10,y:10}, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
}];
function App() {
  return (
    <DiagramComponent
      id="container"
      width={'100%'}
      height={'600px'}
      nodes={node}
      connectors={connector}
    />
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);

Note: The fixed user handle id need to be unique.

Customizing the node fixed user handle

  • The node fixed user handle can be aligned relative to the node boundaries. It has margin and offset settings. It is quite useful to position the node fixed userhandle and used together and gives you more control over the node fixed user handle positioning.

Margin for the node fixed user handle

Margin is an absolute value used to add some blank space in any one of its four sides. The fixed user handle can be displaced with the margin property.

Offset for the node fixed user handle

The offset property of fixed user handle is used to align the user handle based on the x and y points. (0,0) represents the top or left corner and (1,1) represents the bottom or right corner.

The following table shows all the possible alignments visually shows the fixed user handle positions.

Offset Margin Output
(0,0) Right = 20 fixed user handle for node
(0.5,0) Bottom = 20 fixed user handle for node
(1,0) Left = 20 fixed user handle for node
(0,0.5) Right = 20 fixed user handle for node
(0,1) Left = 20 fixed user handle for node
(0,1) Right = 20 fixed user handle for node
(0.5,1) Top = 20 fixed user handle for node
(1,1) Left = 20 fixed user handle for node

The following code explains how to customize the node fixed user handle.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent } from "@syncfusion/ej2-react-diagrams";
let node = [{
        offsetX: 250,
        offsetY: 250,
        width: 100,
        height: 100,
        style: {
            fill: '#6BA5D7',
            strokeColor: 'white'
        },
        // A fixed user handle is created and stored in fixed user handle collection of Node.
        fixedUserHandles: [{ offset: { x: 0, y: 0 }, margin: { right: 20 }, width: 20, height: 20, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
    }];
function App() {
    return (<DiagramComponent id="container" width={'100%'} height={'600px'} nodes={node}/>);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    Diagram,
    DiagramComponent,
    NodeModel
} from "@syncfusion/ej2-react-diagrams";
let node: NodeModel[] = [{
    offsetX: 250,
    offsetY: 250,
    width: 100,
    height: 100,
    style: {
        fill: '#6BA5D7',
        strokeColor: 'white'
    },
    // A fixed user handle is created and stored in fixed user handle collection of Node.
    fixedUserHandles: [{ offset: { x: 0, y: 0 }, margin: { right: 20 }, width: 20, height: 20, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
}];
function App() {
  return (
    <DiagramComponent
      id="container"
      width={'100%'}
      height={'600px'}
      nodes={node}
    />
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);

Customizing the connector fixed user handle

  • The connector fixed user handle can be aligned relative to the connector boundaries. It has alignment, displacement and offset settings. It is useful to position the connector fixed userhandle and used together and gives you more control over the connector fixed user handle positioning.

  • The offset and alignment properties of fixed user handle allows you to align the connector fixed user handles to the segments.

Offset for the connector fixed user handle

The offset property of connector fixed user handle is used to align the user handle based on fractions. 0 represents the connector source point, 1 represents the connector target point, and 0.5 represents the center point of the connector segment.

Alignment

The connector’s fixed user handle can be aligned over its segment path using the alignment property of fixed user handle.

The following table shows all the possible alignments visually shows the fixed user handle positions.

Offset Alignment Output
0 Before fixed user handle for node
0.5 Center fixed user handle for node
1 After fixed user handle for node

Displacement

  • The displacement property allows you to specify the space to be left from the connector segment based on the x and y value provided.

The following table shows all the possible alignments visually shows the fixed user handle positions.

Displacment Alignment Output
x=10 Before fixed user handle for node
x=10 After fixed user handle for node
y=10 Before fixed user handle for node
y=10 After fixed user handle for node

Note: Displacement will not be done if the alignment is set to be center.

The following code explains how to customize the connector fixed user handle.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent } from "@syncfusion/ej2-react-diagrams";
let connectors = [{
        id: "connector1",
        style: {
            strokeColor: '#6BA5D7',
            fill: '#6BA5D7',
            strokeWidth: 2
        },
        targetDecorator: {
            style: {
                fill: '#6BA5D7',
                strokeColor: '#6BA5D7'
            }
        },
        sourcePoint: {
            x: 100,
            y: 100
        },
        targetPoint: {
            x: 200,
            y: 200
        }, type: 'Orthogonal',
        // A fixed user handle is created and stored in fixed user handle collection of Connector.
        fixedUserHandles: [{ offset: 0.5, width: 20, alignment: 'Before', height: 20, id: 'usercon1', displacement: { x: 10, y: 10 }, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
    }];
function App() {
    return (<DiagramComponent id="container" width={'100%'} height={'600px'} connectors={connectors}/>);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    Diagram,
    DiagramComponent,
    ConnectorModel
} from "@syncfusion/ej2-react-diagrams";
let connectors: ConnectorModel = [{
    id: "connector1",
    style: {
        strokeColor: '#6BA5D7',
        fill: '#6BA5D7',
        strokeWidth: 2
    },
    targetDecorator: {
        style: {
            fill: '#6BA5D7',
            strokeColor: '#6BA5D7'
        }
    },
    sourcePoint: {
        x: 100,
        y: 100
    },
    targetPoint: {
        x: 200,
        y: 200
    },type: 'Orthogonal',
    // A fixed user handle is created and stored in fixed user handle collection of Connector.
     fixedUserHandles: [{ offset: 0.5, width: 20, alignment: 'Before', height: 20, id: 'usercon1',  displacement:{x:10,y:10}, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
}];
function App() {
  return (
    <DiagramComponent
      id="container"
      width={'100%'}
      height={'600px'}
      connectors={connectors}
    />
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);

Tooltip support for User Handle

The diagram provides support to show tooltip when the mouse hovers over any user handle.
To show tooltip on mouse over, the tooltip property of diagram model needs to be set with the tooltip content and position as shown in the following example.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent, SelectorConstraints, NodeConstraints } from "@syncfusion/ej2-react-diagrams";
let node = [{
        offsetX: 250,
        offsetY: 250,
        width: 100,
        height: 100,
        style: {
            fill: '#6BA5D7',
            strokeColor: 'white'
        },
        tooltip: { content: 'node1', position: 'BottomRight', relativeMode: 'Object' },
        constraints: NodeConstraints.Default | NodeConstraints.Tooltip
    }];
let handles = [{
        name: 'clone', pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,' +
            '0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z ' +
            'M68.5,72.5h-30V34.4h30V72.5z',
        visible: true, offset: 0, side: 'Bottom', margin: { top: 0, bottom: 0, left: 0, right: 0 },
        tooltip: { content: 'handle1', position: 'BottomRight', relativeMode: 'Object' }
    }];
function App() {
    return (<DiagramComponent id="container" width={'100%'} height={'600px'} nodes={node} selectedItems={{
            constraints: SelectorConstraints.All,
            userHandles: handles,
        }}/>);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    Diagram,
    DiagramComponent,
    NodeModel,
    SelectorConstraints,
    NodeConstraints,
    UserHandleModel
} from "@syncfusion/ej2-react-diagrams";
let node: NodeModel[] = [{
    offsetX: 250,
    offsetY: 250,
    width: 100,
    height: 100,
    style: {
        fill: '#6BA5D7',
        strokeColor: 'white'
    },
    tooltip: { content: 'node1', position: 'BottomRight', relativeMode: 'Object' },
    constraints: NodeConstraints.Default | NodeConstraints.Tooltip
}];
let handles: UserHandleModel[] = [{
     name: 'clone', pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,' +
            '0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z ' +
            'M68.5,72.5h-30V34.4h30V72.5z',
        visible: true, offset: 0, side: 'Bottom', margin: { top: 0, bottom: 0, left: 0, right: 0 },
        tooltip: { content: 'handle1', position: 'BottomRight', relativeMode: 'Object' }
}];
function App() {
  return (
    <DiagramComponent
      id="container"
      width={'100%'}
      height={'600px'}
      nodes={node}
      selectedItems={{
        constraints: SelectorConstraints.All,
        userHandles: handles,
      }}
    />
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);

Tooltip support for Fixed User Handle

The diagram provides support to show tooltip when the mouse hovers over any fixed user handle. To show the tooltip on mouse over, the tooltip property of diagram model needs to be set with the tooltip content and position as shown in the following example.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent, NodeConstraints } from "@syncfusion/ej2-react-diagrams";
let node = [{
        offsetX: 250,
        offsetY: 250,
        width: 100,
        height: 100,
        style: {
            fill: '#6BA5D7',
            strokeColor: 'white'
        },
        tooltip: { content: 'node1', position: 'BottomRight', relativeMode: 'Object' },
        constraints: NodeConstraints.Default | NodeConstraints.Tooltip,
        fixedUserHandles: [{ tooltip: { content: 'handle1', position: 'BottomRight', relativeMode: 'Object' }, offset: { x: 0, y: 0 }, margin: { right: 20 }, width: 20, height: 20, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
    }];
function App() {
    return (<DiagramComponent id="container" width={'100%'} height={'600px'} nodes={node} />);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    Diagram,
    DiagramComponent,
    NodeModel,
    NodeConstraints
} from "@syncfusion/ej2-react-diagrams";
let node: NodeModel[] = [{
    offsetX: 250,
    offsetY: 250,
    width: 100,
    height: 100,
    style: {
        fill: '#6BA5D7',
        strokeColor: 'white'
    },
    tooltip: { content: 'node1', position: 'BottomRight', relativeMode: 'Object' },
    constraints: NodeConstraints.Default | NodeConstraints.Tooltip,
    fixedUserHandles: [{ tooltip: { content: 'handle1', position: 'BottomRight', relativeMode: 'Object' }, offset: { x: 0, y: 0 }, margin: { right: 20 }, width: 20, height: 20, pathData: 'M60.3,18H27.5c-3,0-5.5,2.4-5.5,5.5v38.2h5.5V23.5h32.7V18z M68.5,28.9h-30c-3,0-5.5,2.4-5.5,5.5v38.2c0,3,2.4,5.5,5.5,5.5h30c3,0,5.5-2.4,5.5-5.5V34.4C73.9,31.4,71.5,28.9,68.5,28.9z M68.5,72.5h-30V34.4h30V72.5z' }]
}];
function App() {
  return (
    <DiagramComponent
      id="container"
      width={'100%'}
      height={'600px'}
      nodes={node}
    />
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);

Customizing Fixed User Handles with HTML Templates

Fixed user handles are interactive elements added to nodes and connectors. Their appearance can be customized using HTML templates. To render fixed user handle with HTML templates, we need to create an HTML element inside a <script> tag then add the template reference using the fixedUserHandleTemplate property in the diagram model.

import * as React from "react";
import * as ReactDOM from "react-dom";
import { DiagramComponent } from "@syncfusion/ej2-react-diagrams";
let diagramInstance;
let colorPickerInstance;
let node = [ {
    id:"node1",
    offsetX: 250,
    offsetY: 250,
    width: 100,
    height: 100,
    style: {
        fill: '#6BA5D7',
        strokeColor: 'white'
    },
    fixedUserHandles: [{ offset: { x: 0, y: 0 }, margin: { right: 20 }, width: 50, height: 20, id: 'usercon1' }]
}];
let connector = [{
    id: "connector1",
    style: {
        strokeColor: '#6BA5D7',
        fill: '#6BA5D7',
        strokeWidth: 2
    },
    targetDecorator: {
        style: {
            fill: '#6BA5D7',
            strokeColor: '#6BA5D7'
        }
    },
    sourcePoint: {
        x: 400,
        y: 200
    },
    targetPoint: {
        x: 500,
        y: 300
    }, type: 'Orthogonal',
    fixedUserHandles: [{ offset: 0.5, width: 120, alignment: 'Before', height: 20, id: 'usercon2', displacement: { x: 10, y: 10 } }]
}];


function fixedUserHandleTemplate(props) {
    const { id, width } = props; 
    {
        return (
            <div style=>
               {id === 'usercon1' ? (
                <input 
                    id="colorPicker" 
                    ref={colorPicker => (colorPickerInstance = colorPicker)} 
                    type="color" 
                    defaultValue="#008000" 
                    onChange={handleColorChange}
                    style= // Apply width
                />
            ) : id === 'usercon2' ? (
                <input 
                    type="button" 
                    value="FixedUserHandleTemplate" 
                    style= // Apply width and color
                />
            ) : null}
            </div>
        );
    }




}

function handleColorChange()
{
    let currentColor = colorPickerInstance.value;
    diagramInstance.nodes[0].style.fill = currentColor;
    
}

function App() {
    return (<DiagramComponent id="container" width={'100%'} height={'600px'} nodes={node} connectors={connector} ref={diagram => (diagramInstance = diagram)}  fixedUserHandleTemplate={fixedUserHandleTemplate.bind(this)}/>);
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);
import * as React from "react";
import * as ReactDOM from "react-dom";
import {
    Diagram,
    DiagramComponent,
    ConnectorModel,
    NodeModel
} from "@syncfusion/ej2-react-diagrams";
let diagramInstance;
let colorPickerInstance;

let node: NodeModel = [ {
    id:"node1",
    offsetX: 250,
    offsetY: 250,
    width: 100,
    height: 100,
    style: {
        fill: '#6BA5D7',
        strokeColor: 'white'
    },
    fixedUserHandles: [{ offset: { x: 0, y: 0 }, margin: { right: 20 }, width: 50, height: 20, id: 'usercon1' }]
}];
let connectors: ConnectorModel = [{
    id: "connector1",
    style: {
        strokeColor: '#6BA5D7',
        fill: '#6BA5D7',
        strokeWidth: 2
    },
    targetDecorator: {
        style: {
            fill: '#6BA5D7',
            strokeColor: '#6BA5D7'
        }
    },
    sourcePoint: {
        x: 400,
        y: 200
    },
    targetPoint: {
        x: 500,
        y: 300
    }, type: 'Orthogonal',
    fixedUserHandles: [{ offset: 0.5, width: 120, alignment: 'Before', height: 20, id: 'usercon2', displacement: { x: 10, y: 10 } }]

}];


function fixedUserHandleTemplate(props) {
    const { id, width } = props; 
    {
        return (
            <div style=>
               {id === 'usercon1' ? (
                <input 
                    id="colorPicker" 
                    ref={colorPicker => (colorPickerInstance = colorPicker)} 
                    type="color" 
                    defaultValue="#008000" 
                    onChange={handleColorChange}
                    style= // Apply width
                />
            ) : id === 'usercon2' ? (
                <input 
                    type="button" 
                    value="FixedUserHandleTemplate" 
                    style= // Apply width and color
                />
            ) : null}
            </div>
        );
    }




}

function handleColorChange()
{
    let currentColor = colorPickerInstance.value;
    diagramInstance.nodes[0].style.fill = currentColor;
    
}

function App() {
  return (
    <DiagramComponent
      id="container"
      width={'100%'}
      height={'600px'}
      nodes={node}
      connectors={connectors}
      ref={diagram => (diagramInstance = diagram)}  fixedUserHandleTemplate={fixedUserHandleTemplate.bind(this)}
    />
  );
}
const root = ReactDOM.createRoot(document.getElementById('diagram'));
root.render(<App />);