User handle in Vue Diagram component

14 Dec 202424 minutes to read

User handles are used to add frequently used commands around the selector.

Create user handle

To create user handles, define and add them to the userHandles collection of the selectedItems property. The name property of userHandles is used to define the name of the user handle, which can then be used at runtime for identification and customization. The pathData property is used to define the path data of userhandle.

The following example shows how to render user handle.

<template>
    <div id="app">
         <ejs-diagram id="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems'></ejs-diagram>
    </div>
</template>
<script setup>
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';
const nodes = [{
    id: 'node1',
    offsetX: 100,
    offsetY: 100,
    height: 100,
    width: 100
}]
const handles = [{
    name: 'copy',
    pathData: 'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
}];

const width = "100%";
const height = "350px";
//Define user handles in selectedItems property
const selectedItems = {
   userHandles: handles
}

</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
<div id="app">
    <ejs-diagram id="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems'></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';
const nodes = [{
  id: 'node1',
  offsetX: 100,
  offsetY: 100,
  height: 100,
  width: 100
}];
const handles = [{
  name: 'copy',
  pathData: 'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
}];

export default {
name: "App",
components: {
    "ejs-diagram": DiagramComponent
},
data() {
    return {
        width: "100%",
        height: "350px",
        nodes: nodes,
        //Define user handles in selectedItems property
        selectedItems: {
           userHandles: handles
        },
    }
},

}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

Customize User handle click

When the user handle is clicked, the onUserHandleMouseDown event allows us to identify which user handle was clicked using the name property of userHandle. Based on this name, we can customize the diagram elements accordingly. Several events are triggered while interacting with a user handle. In the following example, we use the onUserHandleMouseDown event to clone nodes on user handle click.

<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown"></ejs-diagram>
    </div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 100,
    offsetY: 100,
    height: 100,
    width: 100
}]
const handles = [{
    name: 'clone',
    pathData: 'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
}];

const width = "100%";
const height = "350px";
//Define user handles in selectedItems property
const selectedItems = {
   userHandles: handles
};

const onUserHandleMouseDown = (event) => {
    const diagramInstance = diagram.value.ej2Instances;
    if (event && event.element) {
      //To clone the selected node
      diagramInstance.copy();
      diagramInstance.paste();
    }
};
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
<div id="app">
    <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown"></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';
const nodes = [{
    id: 'node1',
    offsetX: 100,
    offsetY: 100,
    height: 100,
    width: 100
}];
const handles = [{
  name: 'clone',
  pathData: 'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
}];

export default {
name: "App",
components: {
    "ejs-diagram": DiagramComponent
},
data() {
    return {
        width: "100%",
        height: "350px",
        nodes: nodes,
        //Define user handles in selectedItems property
        selectedItems: {
           userHandles: handles
        },
      
    }
},
methods: {
  onUserHandleMouseDown(event) {
    const diagramInstance = this.$refs.diagram.ej2Instances;
    if (event && event.element) {
      //To clone the selected node  
      diagramInstance.copy();
      diagramInstance.paste();
    }
  },
},
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

Alignment

User handles can be aligned relative to the node boundaries. It has margin, offset, side, horizontalAlignment, and verticalAlignment properties to align user handle based on user’s needs.

Offset

The offset, property of userHandles aligns the user handle based on fractions. For example, 0 represents the top-left corner, 1 represents the top-right corner, and 0.5 represents the top-center.

Side

The side property of userHandles aligns the user handle using the following options: Top, Bottom, Left, and Right.

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

The margin property adds blank space to any of the four sides of the user handle, allowing for precise displacement.

In the following example, the user handle is aligned to the bottom-right corner of the node.

<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown"></ejs-diagram>
    </div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 100,
    offsetY: 100,
    height: 100,
    width: 100
}]
const handles = [{
    name: 'clone',
    pathData:
      'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
    //Alignment options
    offset: 1,
    side: 'Bottom',
    horizontalAlignment: 'Left',
    verticalAlignment: 'Bottom',
    margin: { left: 5, bottom: 10 },
}];

const width = "100%";
const height = "350px";
//Define user handles in selectedItems property
const selectedItems = {
   userHandles: handles
};

const onUserHandleMouseDown = (event) => {
    const diagramInstance = diagram.value.ej2Instances;
    if (event && event.element) {
      //To clone the selected node  
      diagramInstance.copy();
      diagramInstance.paste();
    }
};
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
<div id="app">
    <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown"></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';
const nodes = [{
    id: 'node1',
    offsetX: 100,
    offsetY: 100,
    height: 100,
    width: 100
}];
const handles = [{
    name: 'clone',
    pathData:
      'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
    //Alignment options
    offset: 1,
    side: 'Bottom',
    horizontalAlignment: 'Left',
    verticalAlignment: 'Bottom',
    margin: { left: 5, bottom: 10 },
}];

export default {
name: "App",
components: {
    "ejs-diagram": DiagramComponent
},
data() {
    return {
        width: "100%",
        height: "350px",
        nodes: nodes,
        //Define user handles in selectedItems property
        selectedItems: {
           userHandles: handles
        },
      
    }
},
methods: {
  onUserHandleMouseDown(event) {
    const diagramInstance = this.$refs.diagram.ej2Instances;
    if (event && event.element) {
        //To clone the selected node
        diagramInstance.copy();
        diagramInstance.paste();
    }
  },
},
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

The following table shows all the possible alignments of user handle around the node.

Offset side Output
0 Left user handle for node
0 Right user handle for node
0 Top user handle for node
0 Bottom user handle for node
1 Left user handle for node
1 Right user handle for node
1 Top user handle for node
1 Bottom user handle for node

User handle tooltip

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

<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown"></ejs-diagram>
    </div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 200,
    offsetY: 200,
    height: 100,
    width: 100,
}]
const handles = [{
    name: 'clone',
    pathData:
        'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
    offset: 1,
    //Sets tooltip for user handle
    tooltip: { content: 'Clone Node' },
}];

const width = "100%";
const height = "350px";
//Define user handles in selectedItems property
const selectedItems = {
   userHandles: handles
};

const onUserHandleMouseDown = (event) => {
    const diagramInstance = diagram.value.ej2Instances;
    if (event && event.element) {
      //To clone the selected node  
      diagramInstance.copy();
      diagramInstance.paste();
    }
};
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown"></ejs-diagram>
    </div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';
const nodes = [{
    id: 'node1',
    offsetX: 200,
    offsetY: 200,
    height: 100,
    width: 100,
}];
const handles = [{
  name: 'clone',
  pathData:
    'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
  offset: 1,
  //Sets tooltip for user handle
  tooltip: { content: 'Clone Node' },
}];

export default {
    name: "App",
    components: {
        "ejs-diagram": DiagramComponent
    },
    data() {
        return {
            width: "100%",
            height: "350px",
            nodes: nodes,
            //Define user handles in selectedItems property
            selectedItems: {
            userHandles: handles
            },
        
        }
    },
    methods: {
    onUserHandleMouseDown(event) {
        const diagramInstance = this.$refs.diagram.ej2Instances;
        if (event && event.element) {
            //To clone the selected node
            diagramInstance.copy();
            diagramInstance.paste();
        }
    },
    },
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

You can also customize other properties of the tooltip, such as position, width, height, etc. For more information refer to the tooltip section.

Appearance

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

The following example demonstrates, how to use these properties to customize the appearance of user handle

<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown"></ejs-diagram>
    </div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 200,
    offsetY: 200,
    height: 100,
    width: 100
}]
const handles = [{
    name: 'clone',
    pathData:
      'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
    offset: 1,
    //Sets the border width of user handle
    borderWidth: 5,
    //Sets the border color of user handle
    borderColor: '#64Abbb',
    //Sets the background color of user handle
    backgroundColor: 'yellow',
    //Sets the path data color of user handle
    pathColor: 'green',
    //Sets the size of user handle
    size: 40,
    //Sets the visibility of user handle
    visible: true,
}];

const width = "100%";
const height = "350px";
//Define user handles in selectedItems property
const selectedItems = {
   userHandles: handles
};

const onUserHandleMouseDown = (event) => {
    const diagramInstance = diagram.value.ej2Instances;
    if (event && event.element) {
      //To clone the selected node
      diagramInstance.copy();
      diagramInstance.paste();
    }
};
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
<div id="app">
    <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown"></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';
const nodes = [{
    id: 'node1',
    offsetX: 200,
    offsetY: 200,
    height: 100,
    width: 100
}];
const handles = [{
    name: 'clone',
    pathData:
      'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
    offset: 1,
    //Sets the border width of user handle
    borderWidth: 5,
    //Sets the border color of user handle
    borderColor: '#64Abbb',
    //Sets the background color of user handle
    backgroundColor: 'yellow',
    //Sets the path data color of user handle
    pathColor: 'green',
    //Sets the size of user handle
    size: 40,
    //Sets the visibility of user handle
    visible: true,
}];

export default {
name: "App",
components: {
    "ejs-diagram": DiagramComponent
},
data() {
    return {
        width: "100%",
        height: "350px",
        nodes: nodes,
        //Define user handles in selectedItems property
        selectedItems: {
           userHandles: handles
        },
      
    }
},
methods: {
  onUserHandleMouseDown(event) {
    const diagramInstance = this.$refs.diagram.ej2Instances;
    if (event && event.element) {
      //To clone the selected node  
      diagramInstance.copy();
      diagramInstance.paste();
    }
  },
},
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

Multiple user handle

Multiple user handles can be rendered for the selected objects (nodes/connectors) at a time to perform different operations.

Disable Nodes and disable Connectors

User handles are typically defined within the selectedItems property of the diagram, applying them universally to both nodes and connectors. However, in some scenarios, specific user handles may need to be excluded from connectors or nodes selectively. To address this, the disableNodes and disableConnectors properties come into play. These properties allow certain user handles to be disabled based on the type of selected item.

In the example below, multiple user handles are utilized for various functionalities, with some handles hidden selectively for nodes or connectors depending on their intended functionality.

<template>
    <div id="app">
       <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown"></ejs-diagram>
    </div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 450,
    offsetY: 150,
    height: 100,
    width: 100
}];
const connectors = [{
  id: 'connector1',
  type: 'Orthogonal',
  sourcePoint: { x: 100, y: 100 },
  targetPoint: { x: 300, y: 200 },
}];
const handles = [
{
  name: 'clone',
  pathData:
    'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
  tooltip: { content: 'Clone' },
  offset: 1,
  side: 'Bottom',
},
{
  name: 'delete',
  pathData:
    'M0.97,3.04 L12.78,3.04 L12.78,12.21 C12.78,12.64,12.59,13,12.2,13.3 C11.82,13.6,11.35,13.75,10.8,13.75 L2.95,13.75 C2.4,13.75,1.93,13.6,1.55,13.3 C1.16,13,0.97,12.64,0.97,12.21 Z M4.43,0 L9.32,0 L10.34,0.75 L13.75,0.75 L13.75,2.29 L0,2.29 L0,0.75 L3.41,0.75 Z ',
  tooltip: { content: 'Delete' },
  offset: 0,
  side: 'Bottom',
},
{
  name: 'star',
  pathData:
    'M50,5 63,37 100,37 70,60 82,92 50,75 18,92 30,60 0,37 37,37z    ',
  tooltip: { content: 'Star' },
  offset: 0,
  side: 'Top',
  disableConnectors: true,
},
{
  name: 'triangle',
  pathData: 'M2,8 L5,2 L8,8 L2,8, z',
  tooltip: { content: 'Triangle' },
  offset: 1,
  side: 'Top',
  disableConnectors: true,
},
{
  name: 'rectangle',
  pathData: 'M10,10 L90,10 L90,90 L10,90 Z',
  tooltip: { content: 'Rectangle' },
  offset: 0.5,
  side: 'Left',
  disableConnectors: true,
},
{
  name: 'triangle',
  pathData: 'M2,8 L5,2 L8,8 L2,8, z',
  tooltip: { content: 'Triangle' },
  offset: 1,
  side: 'Top',
  disableConnectors: true,
},
{
  name: 'changeConnectorType',
  pathData:
    'M6.09,0 L13.75,6.88 L6.09,13.75 L6.09,9.64 L0,9.64 L0,4.11 L6.09,4.11 Z ',
  tooltip: { content: 'Change Connector Type' },
  offset: 0.7,
  side: 'Bottom',
  disableNodes: true,
},
];

const width = "100%";
const height = "350px";
//Define user handles in selectedItems property
const selectedItems = {
   userHandles: handles
};

const onUserHandleMouseDown = (event) => {
    const diagramInstance = diagram.value.ej2Instances;
    switch (event.element.name) {
      // To clone the selected node
      case 'clone':
        diagramInstance.copy();
        diagramInstance.paste();
        break;
      case 'delete':
        diagramInstance.remove();
        break;
      case 'star':
      case 'rectangle':
      case 'triangle':
        diagramInstance.selectedItems.nodes[0].shape = {
          type: 'Basic',
          shape: event.element.tooltip.content,
        };
        diagramInstance.dataBind();
        break;
      case 'changeConnectorType':
        diagramInstance.selectedItems.connectors[0].type =
        diagramInstance.selectedItems.connectors[0].type === 'Orthogonal'
            ? 'Straight'
            : diagramInstance.selectedItems.connectors[0].type === 'Straight'
            ? 'Bezier'
            : 'Orthogonal';
        diagramInstance.dataBind();
        break;
    }
};
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
<div id="app">
    <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown"></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';
const nodes = [{
  id: 'node1',
  offsetX: 450,
  offsetY: 150,
  height: 100,
  width: 100
}];

const connectors = [{
  id: 'connector1',
  type: 'Orthogonal',
  sourcePoint: { x: 100, y: 100 },
  targetPoint: { x: 300, y: 200 },
}];
const handles = [
{
  name: 'clone',
  pathData:
    'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
  tooltip: { content: 'Clone' },
  offset: 1,
  side: 'Bottom',
},
{
  name: 'delete',
  pathData:
    'M0.97,3.04 L12.78,3.04 L12.78,12.21 C12.78,12.64,12.59,13,12.2,13.3 C11.82,13.6,11.35,13.75,10.8,13.75 L2.95,13.75 C2.4,13.75,1.93,13.6,1.55,13.3 C1.16,13,0.97,12.64,0.97,12.21 Z M4.43,0 L9.32,0 L10.34,0.75 L13.75,0.75 L13.75,2.29 L0,2.29 L0,0.75 L3.41,0.75 Z ',
  tooltip: { content: 'Delete' },
  offset: 0,
  side: 'Bottom',
},
{
  name: 'star',
  pathData:
    'M50,5 63,37 100,37 70,60 82,92 50,75 18,92 30,60 0,37 37,37z    ',
  tooltip: { content: 'Star' },
  offset: 0,
  side: 'Top',
  disableConnectors: true,
},
{
  name: 'triangle',
  pathData: 'M2,8 L5,2 L8,8 L2,8, z',
  tooltip: { content: 'Triangle' },
  offset: 1,
  side: 'Top',
  disableConnectors: true,
},
{
  name: 'rectangle',
  pathData: 'M10,10 L90,10 L90,90 L10,90 Z',
  tooltip: { content: 'Rectangle' },
  offset: 0.5,
  side: 'Left',
  disableConnectors: true,
},
{
  name: 'triangle',
  pathData: 'M2,8 L5,2 L8,8 L2,8, z',
  tooltip: { content: 'Triangle' },
  offset: 1,
  side: 'Top',
  disableConnectors: true,
},
{
  name: 'changeConnectorType',
  pathData:
    'M6.09,0 L13.75,6.88 L6.09,13.75 L6.09,9.64 L0,9.64 L0,4.11 L6.09,4.11 Z ',
  tooltip: { content: 'Change Connector Type' },
  offset: 0.7,
  side: 'Bottom',
  disableNodes: true,
},
];

export default {
name: "App",
components: {
    "ejs-diagram": DiagramComponent
},
data() {
    return {
        width: "100%",
        height: "350px",
        nodes: nodes,
        connectors: connectors,
        //Define user handles in selectedItems property
        selectedItems: {
           userHandles: handles
        },
      
    }
},
methods: {
  onUserHandleMouseDown(event) {
    const diagramInstance = this.$refs.diagram.ej2Instances;
    switch (event.element.name) {
      // To clone the selected node
      case 'clone':
        diagramInstance.copy();
        diagramInstance.paste();
        break;
      case 'delete':
        diagramInstance.remove();
        break;
      case 'star':
      case 'rectangle':
      case 'triangle':
        diagramInstance.selectedItems.nodes[0].shape = {
          type: 'Basic',
          shape: event.element.tooltip.content,
        };
        diagramInstance.dataBind();
        break;
      case 'changeConnectorType':
        diagramInstance.selectedItems.connectors[0].type =
        diagramInstance.selectedItems.connectors[0].type === 'Orthogonal'
            ? 'Straight'
            : diagramInstance.selectedItems.connectors[0].type === 'Straight'
            ? 'Bezier'
            : 'Orthogonal';
        diagramInstance.dataBind();
        break;
    }
  },
},
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

Different types of user handle

Diagram provides support to render different types of user handles:

  • Source: Renders an image as a user handle using an image source.
  • Content: Renders a user handle using SVG content.
  • pathData: Renders a user handle using custom path data.
  • template: Renders a user handle using a predefined template (userHandleTemplate) defined in the diagram.

The precedence order for user handles is as follows:

  1. pathData
  2. Content
  3. Source
  4. userHandleTemplate

This means that if multiple options are used for the user handle, the one with higher precedence will be rendered.

The below example code demonstrating different types of user handles.

<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' :userHandleTemplate="userHandleTemplate"></ejs-diagram>
    </div>
</template>
<script setup>
import { ref, onMounted, createApp } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const itemVue = createApp({}).component("userHandleTemplate", {
  template: `<input id="colorPicker" type="color"  value="#008000"/>`,
  data() {
    return {};
  }
});

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 200,
    offsetY: 200,
    height: 100,
    width: 100
}]
const handles = [{
  name: 'handle1',
  pathData:
    'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
  tooltip: { content: 'Path data user handle' },
  offset: 1,
  side: 'Bottom',
},
{
  name: 'handle2',
  source:
    'data:image/png;base64,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',
  tooltip: { content: 'Image user handle' },
  offset: 0,
  side: 'Bottom',
},
{
  name: 'handle3',
  content:
    '<g><path d="M90,43.841c0,24.213-19.779,43.841-44.182,43.841c-7.747,0-15.025-1.98-21.357-5.455L0,90l7.975-23.522' +
    'c-4.023-6.606-6.34-14.354-6.34-22.637C1.635,19.628,21.416,0,45.818,0C70.223,0,90,19.628,90,43.841z M45.818,6.982' +
    'c-20.484,0-37.146,16.535-37.146,36.859c0,8.065,2.629,15.534,7.076,21.61L11.107,79.14l14.275-4.537' +
    'c5.865,3.851,12.891,6.097,20.437,6.097c20.481,0,37.146-16.533,37.146-36.857S66.301,6.982,45.818,6.982z' +
    ' M68.129,53.938' +
    'c-0.273-0.447-0.994-0.717-2.076-1.254c-1.084-0.537-6.41-3.138-7.4-3.495c-0.993-0.358-1.717-0.538-2.438,0.537' +
    'c-0.721,1.076-2.797,3.495-3.43,4.212c-0.632,0.719-1.263,0.809-2.347,0.271c-1.082-0.537-4.571-1.673-8.708-5.333' +
    'c-3.219-2.848-5.393-6.364-6.025-7.441c-0.631-1.075-0.066-1.656,0.475-2.191c0.488-0.482,1.084-1.255,1.625-1.882' +
    'c0.543-0.628,0.723-1.075,1.082-1.793c0.363-0.717,0.182-1.344-0.09-1.883c-0.27-0.537-2.438-5.825-3.34-7.977' +
    'c-0.902-2.15-1.803-1.792-2.436-1.792c-0.631,0-1.354-0.09-2.076-0.09c-0.722,0-1.896,0.269-2.889,1.344' +
    'c-0.992,1.076-3.789,3.676-3.789,8.963c0,5.288,3.879,10.397,4.422,11.113c0.541,0.716,7.49,11.92,18.5,16.223' +
    'C58.2,65.771,58.2,64.336,60.186,64.156c1.984-0.179,6.406-2.599,7.312-5.107' +
    'C68.398,56.537,68.398,54.386,68.129,53.938z"></path></g>',
  tooltip: { content: 'Content user handle' },
  offset: 0,
  side: 'Top',
},
{
  name: 'handle4',
  tooltip: { content: 'Template user handle' },
  offset: 1,
  side: 'Top',
}];

const width = "100%";
const height = "350px";
//Define user handles in selectedItems property
const selectedItems = {
   userHandles: handles
};

const userHandleTemplate = function () {
  return { template: itemVue };
} 


</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
<div id="app">
    <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' :userHandleTemplate='userHandleTemplate'></ejs-diagram>
</div>
</template>
<script>
import { createApp } from "vue";
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';

const itemVue = createApp({}).component("userHandleTemplate", {
  template: `<input id="colorPicker" type="color"  value="#008000"/>`,
  data() {
    return {};
  }
});
const nodes = [{
    id: 'node1',
    offsetX: 200,
    offsetY: 200,
    height: 100,
    width: 100
}];
const handles = [ {
  name: 'handle1',
  pathData:
    'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
  tooltip: { content: 'Path data user handle' },
  offset: 1,
  side: 'Bottom',
},
{
  name: 'handle2',
  source:
    'data:image/png;base64,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',
  tooltip: { content: 'Image user handle' },
  offset: 0,
  side: 'Bottom',
},
{
  name: 'handle3',
  content:
    '<g><path d="M90,43.841c0,24.213-19.779,43.841-44.182,43.841c-7.747,0-15.025-1.98-21.357-5.455L0,90l7.975-23.522' +
    'c-4.023-6.606-6.34-14.354-6.34-22.637C1.635,19.628,21.416,0,45.818,0C70.223,0,90,19.628,90,43.841z M45.818,6.982' +
    'c-20.484,0-37.146,16.535-37.146,36.859c0,8.065,2.629,15.534,7.076,21.61L11.107,79.14l14.275-4.537' +
    'c5.865,3.851,12.891,6.097,20.437,6.097c20.481,0,37.146-16.533,37.146-36.857S66.301,6.982,45.818,6.982z' +
    ' M68.129,53.938' +
    'c-0.273-0.447-0.994-0.717-2.076-1.254c-1.084-0.537-6.41-3.138-7.4-3.495c-0.993-0.358-1.717-0.538-2.438,0.537' +
    'c-0.721,1.076-2.797,3.495-3.43,4.212c-0.632,0.719-1.263,0.809-2.347,0.271c-1.082-0.537-4.571-1.673-8.708-5.333' +
    'c-3.219-2.848-5.393-6.364-6.025-7.441c-0.631-1.075-0.066-1.656,0.475-2.191c0.488-0.482,1.084-1.255,1.625-1.882' +
    'c0.543-0.628,0.723-1.075,1.082-1.793c0.363-0.717,0.182-1.344-0.09-1.883c-0.27-0.537-2.438-5.825-3.34-7.977' +
    'c-0.902-2.15-1.803-1.792-2.436-1.792c-0.631,0-1.354-0.09-2.076-0.09c-0.722,0-1.896,0.269-2.889,1.344' +
    'c-0.992,1.076-3.789,3.676-3.789,8.963c0,5.288,3.879,10.397,4.422,11.113c0.541,0.716,7.49,11.92,18.5,16.223' +
    'C58.2,65.771,58.2,64.336,60.186,64.156c1.984-0.179,6.406-2.599,7.312-5.107' +
    'C68.398,56.537,68.398,54.386,68.129,53.938z"></path></g>',
  tooltip: { content: 'Content user handle' },
  offset: 0,
  side: 'Top',
},
{
  name: 'handle4',
  tooltip: { content: 'Template user handle' },
  offset: 1,
  side: 'Top',
},
];

export default {
name: "App",
components: {
    "ejs-diagram": DiagramComponent
},
data() {
    return {
        width: "100%",
        height: "350px",
        nodes: nodes,
        //Define user handles in selectedItems property
        selectedItems: {
           userHandles: handles
        },
        userHandleTemplate: function()
        {
          return { template: itemVue}
        }
      
    }
},
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

User handle events

When interacting with user handles, certain events are triggered that can be used to customize the appearance and functionality of the handles. The user handle events are explained below.

In the following example, the above events are used to customize the appearance of user handles.

<template>
    <div id="app">
         <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown" @onUserHandleMouseEnter="onUserHandleMouseEnter" @onUserHandleMouseUp="onUserHandleMouseUp" @onUserHandleMouseLeave="onUserHandleMouseLeave"></ejs-diagram>
    </div>
</template>
<script setup>
import { ref, onMounted } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 200,
    offsetY: 200,
    height: 100,
    width: 100
}]
const handles = [{
    name: 'clone',
    pathData:
        'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
    offset: 1,
    //Sets the border width of user handle
    borderWidth: 5,
    //Sets the border color of user handle
    borderColor: '#64Abbb',
    //Sets the background color of user handle
    backgroundColor: 'yellow',
    //Sets the path data color of user handle
    pathColor: 'green',
    //Sets the size of user handle
    size: 40,
    //Sets the visibility of user handle
    visible: true,
}];

const width = "100%";
const height = "350px";
//Define user handles in selectedItems property
const selectedItems = {
   userHandles: handles
};

const onUserHandleMouseDown = (event) => {
    const diagramInstance = diagram.value.ej2Instances;
    if (event && event.element) {
      //To clone the selected node
      diagramInstance.copy();
      diagramInstance.paste();
    }
};

const  onUserHandleMouseEnter = (event) => {
    if (event && event.element) {
      event.element.pathColor = 'red';
      event.element.backgroundColor = 'pink';
    }
};

const onUserHandleMouseUp = (event) => {
    if (event && event.element) {
      event.element.pathColor = 'white';
      event.element.backgroundColor = 'blue';
    }
};
  
const onUserHandleMouseLeave = (event) => {
    if (event && event.element) {
      event.element.pathColor = 'green';
      event.element.backgroundColor = 'yellow';
    }
};


</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
<div id="app">
    <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :selectedItems='selectedItems' @onUserHandleMouseDown="onUserHandleMouseDown" @onUserHandleMouseEnter="onUserHandleMouseEnter" @onUserHandleMouseUp="onUserHandleMouseUp" @onUserHandleMouseLeave="onUserHandleMouseLeave"></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';
const nodes = [{
    id: 'node1',
    offsetX: 200,
    offsetY: 200,
    height: 100,
    width: 100
}];
const handles = [ {
  name: 'clone',
  pathData:
    'M0,3.42 L1.36,3.42 L1.36,12.39 L9.62,12.39 L9.62,13.75 L1.36,13.75 C0.97,13.75,0.65,13.62,0.39,13.36 C0.13,13.1,0,12.78,0,12.39 Z M4.13,0 L12.39,0 C12.78,0,13.1,0.13,13.36,0.39 C13.62,0.65,13.75,0.97,13.75,1.36 L13.75,9.62 C13.75,10.01,13.62,10.33,13.36,10.6 C13.1,10.87,12.78,11.01,12.39,11.01 L4.13,11.01 C3.72,11.01,3.39,10.87,3.13,10.6 C2.87,10.33,2.74,10.01,2.74,9.62 L2.74,1.36 C2.74,0.97,2.87,0.65,3.13,0.39 C3.39,0.13,3.72,0,4.13,0 Z ',
  offset: 1,
  //Sets the border width of user handle
  borderWidth: 5,
  //Sets the border color of user handle
  borderColor: '#64Abbb',
  //Sets the background color of user handle
  backgroundColor: 'yellow',
  //Sets the path data color of user handle
  pathColor: 'green',
  //Sets the size of user handle
  size: 40,
  //Sets the visibility of user handle
  visible: true,
},];

export default {
name: "App",
components: {
    "ejs-diagram": DiagramComponent
},
data() {
    return {
        width: "100%",
        height: "350px",
        nodes: nodes,
        //Define user handles in selectedItems property
        selectedItems: {
           userHandles: handles
        },
      
    }
},
methods: {
  onUserHandleMouseDown(event) {
    const diagramInstance = this.$refs.diagram.ej2Instances;
    if (event && event.element) {
      //To clone the selected node  
      diagramInstance.copy();
      diagramInstance.paste();
    }
  },
  onUserHandleMouseEnter(event)
  {
    if (event && event.element) {
      event.element.pathColor = 'red';
      event.element.backgroundColor = 'pink';
    }
  },
  onUserHandleMouseUp(event)
  {
    if (event && event.element) {
      event.element.pathColor = 'white';
      event.element.backgroundColor = 'blue';
    }
  },
  onUserHandleMouseLeave(event)
  {
    if (event && event.element) {
      event.element.pathColor = 'green';
      event.element.backgroundColor = 'yellow';
    }
  },
},
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

Fixed user handles

Fixed user handles are used to perform specific actions when interacted with. Unlike regular user handles, fixedUserHandles are defined within the node/connector object, allowing different fixed user handles to be added to different nodes.

Create fixed user handles

To create the fixedUserHandles, define and add them to the collection of nodes and connectors. The pathData property of fixedUserHandles is used to define the path data for the fixed user handle. The id property in fixedUserHandles assigns a unique identifier to each handle. This identifier helps locate and modify fixed user handles during runtime. You can handle the click event of a fixed user handle using the fixedUserHandleClick event. This event allows customization based on the type of fixed user handle clicked.

The following code example demonstrates how to create fixed user handles for nodes and connectors and how to handle fixed user handle click:

<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' @fixedUserHandleClick="fixedUserHandleClick"></ejs-diagram>
    </div>
</template>
<script setup>
import { ref } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    offsetX: 250,
    offsetY: 250,
    width: 100,
    height: 100,
    fixedUserHandles: [
    {
        id: 'color',
        pathData:
        'M31.5,13.5 C31.5,20.95,24.44,27,15.75,27 C7.059999999999999,27,0,20.95,0,13.5 C0,6.050000000000001,7.06,0,15.75,0 C24.44,0,31.5,6.05,31.5,13.5 Z M13.12,4.5 L13.12,11.25 L5.25,11.25 L5.25,15.75 L13.12,15.75 L13.12,22.5 L18.38,22.5 L18.38,15.75 L26.25,15.75 L26.25,11.25 L18.38,11.25 L18.38,4.5 Z ',
        width: 20,
        height: 20,
    },
    ],
}]

const width = "100%";
const height = "350px";

const fixedUserHandleClick = (event) => {
     const diagramInstance = diagram.value.ej2Instances;
     const node = event.element;
     node.style.fill = node.style.fill === '#64A6' ? '#64Abbb' : '#64A6';
     diagramInstance.dataBind();
};

</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' @fixedUserHandleClick="fixedUserHandleClick"></ejs-diagram>
    </div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';

const nodes = [{
    offsetX: 250,
    offsetY: 250,
    width: 100,
    height: 100,
    fixedUserHandles: [
    {
        id: 'color',
        pathData:
        'M31.5,13.5 C31.5,20.95,24.44,27,15.75,27 C7.059999999999999,27,0,20.95,0,13.5 C0,6.050000000000001,7.06,0,15.75,0 C24.44,0,31.5,6.05,31.5,13.5 Z M13.12,4.5 L13.12,11.25 L5.25,11.25 L5.25,15.75 L13.12,15.75 L13.12,22.5 L18.38,22.5 L18.38,15.75 L26.25,15.75 L26.25,11.25 L18.38,11.25 L18.38,4.5 Z ',
        width: 20,
        height: 20,
    },
    ],
}];

export default {
    name: "App",
    components: {
        "ejs-diagram": DiagramComponent
    },
    data() {
        return {
            width: "100%",
            height: "350px",
            nodes: nodes,
        }
    },
    methods: {
        fixedUserHandleClick(event) {
            const diagramInstance = this.$refs.diagram.ej2Instances;
            const node = event.element;
            node.style.fill = node.style.fill === '#64A6' ? '#64Abbb' : '#64A6';
            diagramInstance.dataBind();
        },
    }
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

NOTE

The fixed user handle id need to be unique.

Alignment

Fixed user handles can be aligned relative to the node boundaries. It has margin, offset, padding properties to align them based on user’s needs.

Margin

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

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-left corner and (1,1) represents the bottom-right corner.

Padding

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

The following example demonstrates how to align fixed user handle for both node and connector.

<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' @onFixedUserHandleMouseDown="onFixedUserHandleMouseDown"></ejs-diagram>
    </div>
</template>
<script setup>
import { ref } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 300,
    offsetY: 300,
    height: 100,
    width: 100,
    style: { fill: '#64a6', strokeColor: '#64Abbb', strokeWidth: 3 },
    fixedUserHandles: [
        {
        id: 'color',
        pathData:
            'M31.5,13.5 C31.5,20.95,24.44,27,15.75,27 C7.059999999999999,27,0,20.95,0,13.5 C0,6.050000000000001,7.06,0,15.75,0 C24.44,0,31.5,6.05,31.5,13.5 Z M13.12,4.5 L13.12,11.25 L5.25,11.25 L5.25,15.75 L13.12,15.75 L13.12,22.5 L18.38,22.5 L18.38,15.75 L26.25,15.75 L26.25,11.25 L18.38,11.25 L18.38,4.5 Z ',
        width: 20,
        height: 20,
        offset: { x: 1, y: 0 },
        margin: { left: 20, bottom: 10 },
        },
    ],
}];

const connectors = [{
  id: 'connector1',
    sourcePoint: { x: 100, y: 100 },
    targetPoint: { x: 300, y: 200 },
    style: { strokeColor: '#64Abbb', fill: '#64A6', strokeWidth: 3 },
    fixedUserHandles: [
      {
        id: 'stroke',
        pathData:
          'M0,13.85 L15.62,13.85 L15.62,20 L25,9.74 L15.62,0 L15.62,6.41 L0,6.41 L0,13.85 Z ',
        width: 20,
        height: 10,
      },
    ],
}];

const width = "100%";
const height = "350px";

const onFixedUserHandleMouseDown = (event) => {
    const diagramInstance = diagram.value.ej2Instances;
    const node = event.element.parentObj;
    node.style.strokeColor = node.style.strokeColor === '#64A6' ? '#64Abbb' : '#64A6';
    diagramInstance.dataBind();
}

</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
<div id="app">
    <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' @onFixedUserHandleMouseDown="onFixedUserHandleMouseDown"></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';

let nodes = [{
  id: 'node1',
  offsetX: 300,
  offsetY: 300,
  height: 100,
  width: 100,
  style: { strokeColor: '#64Abbb', strokeWidth: 3 },
  fixedUserHandles: [
    {
      id: 'color',
      pathData:
        'M31.5,13.5 C31.5,20.95,24.44,27,15.75,27 C7.059999999999999,27,0,20.95,0,13.5 C0,6.050000000000001,7.06,0,15.75,0 C24.44,0,31.5,6.05,31.5,13.5 Z M13.12,4.5 L13.12,11.25 L5.25,11.25 L5.25,15.75 L13.12,15.75 L13.12,22.5 L18.38,22.5 L18.38,15.75 L26.25,15.75 L26.25,11.25 L18.38,11.25 L18.38,4.5 Z ',
      width: 20,
      height: 20,
      offset: { x: 1, y: 0 },
      margin: { left: 20, bottom: 10 },
    },
  ],
}];

let connectors = [{
  id: 'connector1',
    sourcePoint: { x: 100, y: 100 },
    targetPoint: { x: 300, y: 200 },
    style: { strokeColor: '#64Abbb', strokeWidth: 3 },
    fixedUserHandles: [
      {
        id: 'stroke',
        pathData:
          'M0,13.85 L15.62,13.85 L15.62,20 L25,9.74 L15.62,0 L15.62,6.41 L0,6.41 L0,13.85 Z ',
        width: 20,
        height: 10,
      },
    ],
}];

export default {
name: "App",
components: {
    "ejs-diagram": DiagramComponent
},
data() {
    return {
        width: "100%",
        height: "350px",
        nodes: nodes,
        connectors: connectors
    }
},
methods: {
    onFixedUserHandleMouseDown(event) {
        let diagramInstance = this.$refs.diagram.ej2Instances;
        let node = event.element.parentObj;
        node.style.strokeColor = node.style.strokeColor === '#64A6' ? '#64Abbb' : '#64A6';
        diagramInstance.dataBind();
    },
}
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

The following table shows all the possible alignments of fixed user handle around the node.

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

NOTE

Both displacement and alignment are applicable only to connector fixed user handles.

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. The displacement property displaces the handle from its aligned position and its functioning only when the alignment property is set to ‘After’ or ‘Before’.

Offset

The offset property of fixed user handle aligns the fixed user handle based on fractions. For example, 0 represents the left or top corner, 1 represents the bottom or right corner, and 0.5 represents the center.

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 offset and alignment combinations of connector fixed user handle.

Offset Alignment Output
0 Before fixed user handle align before offset 0
0.5 Before fixed user handle align before offset 0.5
1 Before fixed user handle align before offset 1
0 Center fixed user handle align center offset 0
0.5 Center fixed user handle align center offset 0.5
1 Center fixed user handle align center offset 1
0 After fixed user handle align after offset 0
0.5 After fixed user handle align after offset 0.5
1 After fixed user handle for align after offset 1
Displacement

The displacement property displaces the handle from its aligned position based on the provided x and y value.

The following table shows all the possible alignment and displacement combinations of fixed user handle.

Displacement 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 alignment of connector fixed user handle.

<template>
    <div id="app">
        <ejs-diagram id="diagram" :width='width' :height='height' :connectors='connectors'></ejs-diagram>
    </div>
</template>
<script setup>
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const connectors = [{
    id: 'connector1',
    type: 'Orthogonal',
    sourcePoint: { x: 300, y: 100 },
    targetPoint: { x: 400, y: 200 },
    // 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' }]
}]

const width = "100%";
const height = "350px";

</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
    <div id="app">
        <ejs-diagram id="diagram" :width='width' :height='height' :connectors='connectors'></ejs-diagram>
    </div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';

let connectors = [{
  id: 'connector1',
    type: 'Orthogonal',
    sourcePoint: { x: 300, y: 100 },
    targetPoint: { x: 400, y: 200 },
    // 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' }]
}];

export default {
    name: "App",
    components: {
        "ejs-diagram": DiagramComponent
    },
    data() {
        return {
            width: "100%",
            height: "350px",
            connectors: connectors
        }
    },
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

Fixed user handle tooltip

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

<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' @onFixedUserHandleMouseDown="onFixedUserHandleMouseDown" ></ejs-diagram>
    </div>
</template>
<script setup>
import { ref } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 300,
    offsetY: 300,
    height: 100,
    width: 100,
    style: { strokeColor: '#64Abbb', strokeWidth: 3 },
    fixedUserHandles: [
      {
        id: 'color',
        pathData:
          'M31.5,13.5 C31.5,20.95,24.44,27,15.75,27 C7.059999999999999,27,0,20.95,0,13.5 C0,6.050000000000001,7.06,0,15.75,0 C24.44,0,31.5,6.05,31.5,13.5 Z M13.12,4.5 L13.12,11.25 L5.25,11.25 L5.25,15.75 L13.12,15.75 L13.12,22.5 L18.38,22.5 L18.38,15.75 L26.25,15.75 L26.25,11.25 L18.38,11.25 L18.38,4.5 Z ',
        width: 20,
        height: 20,
        offset: { x: 1, y: 0 },
        margin: { left: 20, bottom: 10 },
        //Tooltip for fixed user handle
        tooltip: { content: 'Change stroke color' },
      },
    ],
}];

const connectors = [
  {
    id: 'connector1',
    sourcePoint: { x: 100, y: 100 },
    targetPoint: { x: 300, y: 200 },
    style: { strokeColor: '#64Abbb',  strokeWidth: 3 },
    fixedUserHandles: [
      {
        id: 'stroke',
        pathData:
          'M0,13.85 L15.62,13.85 L15.62,20 L25,9.74 L15.62,0 L15.62,6.41 L0,6.41 L0,13.85 Z ',
        width: 20,
        height: 10,
        //Offset of fixed user handle
        offset: 0.5,
        //Tooltip for fixed user handle
        tooltip: { content: 'Change stroke color' },
      },
    ],
  },
];

const width = "100%";
const height = "350px";

const onFixedUserHandleMouseDown = (event) => {
     const diagramInstance = diagram.value.ej2Instances;
     const node = event.element.parentObj;
     node.style.strokeColor = node.style.strokeColor === '#64A6' ? '#64Abbb' : '#64A6';
     diagramInstance.dataBind();
};

</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' @onFixedUserHandleMouseDown="onFixedUserHandleMouseDown" ></ejs-diagram>
    </div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';

const nodes = [
  {
    id: 'node1',
    offsetX: 300,
    offsetY: 300,
    height: 100,
    width: 100,
    style: { strokeColor: '#64Abbb', strokeWidth: 3 },
    fixedUserHandles: [
      {
        id: 'color',
        pathData:
          'M31.5,13.5 C31.5,20.95,24.44,27,15.75,27 C7.059999999999999,27,0,20.95,0,13.5 C0,6.050000000000001,7.06,0,15.75,0 C24.44,0,31.5,6.05,31.5,13.5 Z M13.12,4.5 L13.12,11.25 L5.25,11.25 L5.25,15.75 L13.12,15.75 L13.12,22.5 L18.38,22.5 L18.38,15.75 L26.25,15.75 L26.25,11.25 L18.38,11.25 L18.38,4.5 Z ',
        width: 20,
        height: 20,
        offset: { x: 1, y: 0 },
        margin: { left: 20, bottom: 10 },
        //Tooltip for fixed user handle
        tooltip: { content: 'Change stroke color' },
      },
    ],
  },
];
const connectors = [
  {
    id: 'connector1',
    sourcePoint: { x: 100, y: 100 },
    targetPoint: { x: 300, y: 200 },
    style: { strokeColor: '#64Abbb',  strokeWidth: 3 },
    fixedUserHandles: [
      {
        id: 'stroke',
        pathData:
          'M0,13.85 L15.62,13.85 L15.62,20 L25,9.74 L15.62,0 L15.62,6.41 L0,6.41 L0,13.85 Z ',
        width: 20,
        height: 10,
        //Offset of fixed user handle
        offset: 0.5,
        //Tooltip for fixed user handle
        tooltip: { content: 'Change stroke color' },
      },
    ],
  },
];


export default {
    name: "App",
    components: {
        "ejs-diagram": DiagramComponent
    },
    data() {
        return {
            width: "100%",
            height: "350px",
            nodes: nodes,
            connectors: connectors
        }
    },
    methods: {
        onFixedUserHandleMouseDown(event) {
            const diagramInstance = this.$refs.diagram.ej2Instances;
            const node = event.element.parentObj;
            node.style.strokeColor = node.style.strokeColor === '#64A6' ? '#64Abbb' : '#64A6';
            diagramInstance.dataBind();
        }
    }
}

</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

You can also customize other properties of the tooltip, such as position, width, height, etc. For more information refer to the tooltip section.

Appearance

The appearance of the fixed user handle can be customized by using the cornerRadius, fill, handleStrokeColor, handleStrokeWidth, iconStrokeColor, iconStrokeWidth and visibility properties of the fixed user handles.

Size

The height and width properties of fixed user handle is used to define the size of the fixed user handle.

Style

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’s handleStrokeColor and handleStrokeWidth, properties are used to define the stroke color and stroke width of the fixed user handle and the fill, property is used to define the fill color of fixed user handle.

The cornerRadius property of the fixed user handle is used to apply border radius for the fixed user handle.

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

The following example demonstrates, how to use these properties to customize the appearance of the fixed user handle.

<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' @onFixedUserHandleMouseDown="onFixedUserHandleMouseDown" ></ejs-diagram>
    </div>
</template>
<script setup>
import { ref } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 300,
    offsetY: 300,
    height: 100,
    width: 100,
    style: { strokeColor: '#64Abbb', strokeWidth: 3 },
    fixedUserHandles: [
        {
        id: 'color',
        pathData:
            'M31.5,13.5 C31.5,20.95,24.44,27,15.75,27 C7.059999999999999,27,0,20.95,0,13.5 C0,6.050000000000001,7.06,0,15.75,0 C24.44,0,31.5,6.05,31.5,13.5 Z M13.12,4.5 L13.12,11.25 L5.25,11.25 L5.25,15.75 L13.12,15.75 L13.12,22.5 L18.38,22.5 L18.38,15.75 L26.25,15.75 L26.25,11.25 L18.38,11.25 L18.38,4.5 Z ',
        width: 30,
        height: 30,
        offset: { x: 1, y: 0 },
        margin: { left: 20, bottom: 10 },
        //Sets the stroke color of fixed user handle
        handleStrokeColor: 'green',
        //Sets the stroke width of fixed user handle
        handleStrokeWidth: 4,
        //Sets the stroke color of icon
        iconStrokeColor: '#64Abbb',
        //Sets the stroke width of icon
        iconStrokeWidth: 1,
        //Sets the fill color of the fixed user handle
        fill: 'yellow',
        //Sets the corner radius of the fixed user handle
        cornerRadius: 5,
        },
    ],
}];
const connectors = [
{
  id: 'connector1',
  sourcePoint: { x: 100, y: 100 },
  targetPoint: { x: 300, y: 200 },
  style: { strokeColor: '#64Abbb', strokeWidth: 3 },
  fixedUserHandles: [
    {
      id: 'stroke',
      pathData:
        'M0,13.85 L15.62,13.85 L15.62,20 L25,9.74 L15.62,0 L15.62,6.41 L0,6.41 L0,13.85 Z ',
      width: 30,
      height: 25,
      //Offset of fixed user handle
      offset: 0.5,
      //Sets the stroke color of fixed user handle
      handleStrokeColor: 'green',
      //Sets the stroke width of fixed user handle
      handleStrokeWidth: 4,
      //Sets the stroke color of icon
      iconStrokeColor: '#64Abbb',
      //Sets the stroke width of icon
      iconStrokeWidth: 1,
      //Sets the fill color of the fixed user handle
      fill: 'yellow',
      //Sets the corner radius of the fixed user handle
      cornerRadius: 5,
    },
  ],
},
];


const width = "100%";
const height = "350px";

const onFixedUserHandleMouseDown = (event) => {
        const diagramInstance = diagram.value.ej2Instances;
        const node = event.element.parentObj;
        node.style.strokeColor = node.style.strokeColor === '#64A6' ? '#64Abbb' : '#64A6';
        diagramInstance.dataBind();
}


</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
<div id="app">
    <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' @onFixedUserHandleMouseDown="onFixedUserHandleMouseDown" ></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';

const nodes = [
{
  id: 'node1',
  offsetX: 300,
  offsetY: 300,
  height: 100,
  width: 100,
  style: { strokeColor: '#64Abbb', strokeWidth: 3 },
  fixedUserHandles: [
    {
      id: 'color',
      pathData:
        'M31.5,13.5 C31.5,20.95,24.44,27,15.75,27 C7.059999999999999,27,0,20.95,0,13.5 C0,6.050000000000001,7.06,0,15.75,0 C24.44,0,31.5,6.05,31.5,13.5 Z M13.12,4.5 L13.12,11.25 L5.25,11.25 L5.25,15.75 L13.12,15.75 L13.12,22.5 L18.38,22.5 L18.38,15.75 L26.25,15.75 L26.25,11.25 L18.38,11.25 L18.38,4.5 Z ',
      width: 30,
      height: 30,
      offset: { x: 1, y: 0 },
      margin: { left: 20, bottom: 10 },
      //Sets the stroke color of fixed user handle
      handleStrokeColor: 'green',
      //Sets the stroke width of fixed user handle
      handleStrokeWidth: 4,
      //Sets the stroke color of icon
      iconStrokeColor: '#64Abbb',
      //Sets the stroke width of icon
      iconStrokeWidth: 1,
      //Sets the fill color of the fixed user handle
      fill: 'yellow',
      //Sets the corner radius of the fixed user handle
      cornerRadius: 5,
    },
  ],
},
];
const connectors = [
{
  id: 'connector1',
  sourcePoint: { x: 100, y: 100 },
  targetPoint: { x: 300, y: 200 },
  style: { strokeColor: '#64Abbb', strokeWidth: 3 },
  fixedUserHandles: [
    {
      id: 'stroke',
      pathData:
        'M0,13.85 L15.62,13.85 L15.62,20 L25,9.74 L15.62,0 L15.62,6.41 L0,6.41 L0,13.85 Z ',
      width: 30,
      height: 25,
      //Offset of fixed user handle
      offset: 0.5,
      //Sets the stroke color of fixed user handle
      handleStrokeColor: 'green',
      //Sets the stroke width of fixed user handle
      handleStrokeWidth: 4,
      //Sets the stroke color of icon
      iconStrokeColor: '#64Abbb',
      //Sets the stroke width of icon
      iconStrokeWidth: 1,
      //Sets the fill color of the fixed user handle
      fill: 'yellow',
      //Sets the corner radius of the fixed user handle
      cornerRadius: 5,
    },
  ],
},
];


export default {
name: "App",
components: {
    "ejs-diagram": DiagramComponent
},
data() {
    return {
        width: "100%",
        height: "350px",
        nodes: nodes,
        connectors: connectors
    }
},
methods: {
    onFixedUserHandleMouseDown(event) {
        const diagramInstance = this.$refs.diagram.ej2Instances;
        const node = event.element.parentObj;
        node.style.strokeColor = node.style.strokeColor === '#64A6' ? '#64Abbb' : '#64A6';
        diagramInstance.dataBind();
    }
}
}

</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

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 template tag then add the template reference using the fixedUserHandleTemplate property in the diagram model.

<template>
    <div id="app">
        <ejs-diagram ref="diagram" id="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' :fixedUserHandleTemplate="fixedUserHandleTemplate"  ></ejs-diagram>
    </div>
</template>
<script setup>
import { createApp, ref, onMounted} from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id:"node1",
    offsetX: 250,
    offsetY: 250,
    width: 100,
    height: 100,
    style: { strokeColor: '#64Abbb', strokeWidth: 3 },
    // 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: 50, height: 20, id: 'usercon1' }]
}];

const connectors = [{
     id: "connector1",
        style: { strokeColor: '#64Abbb', strokeWidth: 3 },
        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 } }]
}]

const width = "100%";
const height = "350px";

const itemVue = createApp({}).component("fixedUserHandleTemplate", {
  template: ` 
    <div v-if="data.id == 'usercon1'">
        <div style="width:100%;height:100%">
            <input id="colorPicker" type="color" value="#008000"/>
        </div>
    </div>
    <div v-else-if="data.id=='usercon2'">
        <div style="width:100%;height:100%">
            <input type="button" value="FixedUserHandleTemplate" style="color:red; "/>
        </div>
    </div> `,
  data() {
    return {};
  }
})

const fixedUserHandleTemplate = function()
{
    return { template: itemVue };
}


onMounted(function () {
    const diagramInstance = diagram.value.ej2Instances;
    colorPicker.addEventListener('change', (event) => {
    const currentColor  = event.target.value;
    diagramInstance.nodes[0].style.fill = currentColor; 
    });

})

</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagramObj" :width='width' :height='height' :nodes='nodes' :connectors='connectors' :fixedUserHandleTemplate="fixedUserHandleTemplate"></ejs-diagram>
    </div>
</template>
<script>

import { createApp } from "vue";
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';

let nodes = [{
    id:"node1",
    offsetX: 250,
    offsetY: 250,
    width: 100,
    height: 100,
    style: { strokeColor: '#64Abbb', strokeWidth: 3 },
    // 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: 50, height: 20, id: 'usercon1' }]
}];


let connectors = [{
     id: "connector1",
        style: { strokeColor: '#64Abbb', strokeWidth: 3 },
        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 } }]
}];

let itemVue = createApp({}).component("fixedUserHandleTemplate", {
    template: ` 
    <div v-if="data.id == 'usercon1'">
        <div style="width:100%;height:100%">
            <input id="colorPicker" type="color" value="#008000"/>
        </div>
    </div>
    <div v-else-if="data.id=='usercon2'">
        <div style="width:100%;height:100%">
            <input type="button" value="FixedUserHandleTemplate" style="color:red; "/>
        </div>
    </div> `,
  data() {
    return {};
  }
});

let diagramInstance;

export default {
    name: "App",
    components: {
        "ejs-diagram": DiagramComponent
    },
    data() {
        return {
            width: "100%",
            height: "350px",
            nodes: nodes,
            connectors: connectors,
            fixedUserHandleTemplate: function()
            {
                return { template: itemVue };
            }
            
        }
    },

    mounted: function () {
        diagramInstance = this.$refs.diagramObj.ej2Instances;
        colorPicker.addEventListener('change', (event) => {
        let currentColor  = event.target.value;
        diagramInstance.nodes[0].style.fill = currentColor; 
        });
    },
    
}
</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>

Fixed user handle events

When interacting with fixed user handles, certain events are triggered that can be used to customize the appearance and functionality of the handles. The fixed user handle events are explained below.

In the following example, the above events are used to customize the appearance of fixed user handles.

<template>
    <div id="app">
        <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' @onFixedUserHandleMouseDown="onFixedUserHandleMouseDown" @onFixedUserHandleMouseEnter="onFixedUserHandleMouseEnter" @onFixedUserHandleMouseUp="onFixedUserHandleMouseUp" @onFixedUserHandleMouseLeave="onFixedUserHandleMouseLeave"></ejs-diagram>
    </div>
</template>
<script setup>
import { ref } from "vue";
import { DiagramComponent as EjsDiagram } from '@syncfusion/ej2-vue-diagrams';

const diagram = ref(null);
const nodes = [{
    id: 'node1',
    offsetX: 300,
    offsetY: 300,
    height: 100,
    width: 100,
    style: { strokeColor: '#64Abbb', strokeWidth: 3 },
    fixedUserHandles: [
        {
        id: 'color',
        pathData:
            'M31.5,13.5 C31.5,20.95,24.44,27,15.75,27 C7.059999999999999,27,0,20.95,0,13.5 C0,6.050000000000001,7.06,0,15.75,0 C24.44,0,31.5,6.05,31.5,13.5 Z M13.12,4.5 L13.12,11.25 L5.25,11.25 L5.25,15.75 L13.12,15.75 L13.12,22.5 L18.38,22.5 L18.38,15.75 L26.25,15.75 L26.25,11.25 L18.38,11.25 L18.38,4.5 Z ',
        width: 30,
        height: 30,
        offset: { x: 1, y: 0 },
        margin: { left: 20, bottom: 10 },
        //Sets the stroke color of fixed user handle
        handleStrokeColor: 'green',
        //Sets the stroke width of fixed user handle
        handleStrokeWidth: 4,
        //Sets the stroke color of icon
        iconStrokeColor: '#64Abbb',
        //Sets the stroke width of icon
        iconStrokeWidth: 1,
        //Sets the fill color of the fixed user handle
        fill: 'yellow',
        //Sets the corner radius of the fixed user handle
        cornerRadius: 5,
        },
    ],
}];
const connectors = [
{
  id: 'connector1',
  sourcePoint: { x: 100, y: 100 },
  targetPoint: { x: 300, y: 200 },
  style: { strokeColor: '#64Abbb', strokeWidth: 3 },
  fixedUserHandles: [
    {
      id: 'stroke',
      pathData:
        'M0,13.85 L15.62,13.85 L15.62,20 L25,9.74 L15.62,0 L15.62,6.41 L0,6.41 L0,13.85 Z ',
      width: 30,
      height: 25,
      //Offset of fixed user handle
      offset: 0.5,
      //Sets the stroke color of fixed user handle
      handleStrokeColor: 'green',
      //Sets the stroke width of fixed user handle
      handleStrokeWidth: 4,
      //Sets the stroke color of icon
      iconStrokeColor: '#64Abbb',
      //Sets the stroke width of icon
      iconStrokeWidth: 1,
      //Sets the fill color of the fixed user handle
      fill: 'yellow',
      //Sets the corner radius of the fixed user handle
      cornerRadius: 5,
    },
  ],
},
];


const width = "100%";
const height = "350px";

const onFixedUserHandleMouseDown = (event) => {
    const diagramInstance = diagram.value.ej2Instances;
    const node = event.element.parentObj;
    node.style.strokeColor = node.style.strokeColor === '#64A6' ? '#64Abbb' : '#64A6';
    diagramInstance.dataBind();
};

const onFixedUserHandleMouseEnter = (event) => {
    if (event.element) {
    event.element.fill = 'red';
    event.element.handleStrokeColor = 'pink';
    }
};
const  onFixedUserHandleMouseUp = (event) => {
    if (event.element) {
    event.element.fill = 'white';
    event.element.handleStrokeColor = 'blue';
    }
};

const onFixedUserHandleMouseLeave = (event) => {
    if (event.element) {
    event.element.fill = 'yellow';
    event.element.handleStrokeColor = 'green';
    }
};


</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>
<template>
<div id="app">
    <ejs-diagram id="diagram" ref="diagram" :width='width' :height='height' :nodes='nodes' :connectors='connectors' @onFixedUserHandleMouseDown="onFixedUserHandleMouseDown" @onFixedUserHandleMouseEnter="onFixedUserHandleMouseEnter" @onFixedUserHandleMouseUp="onFixedUserHandleMouseUp" @onFixedUserHandleMouseLeave="onFixedUserHandleMouseLeave"></ejs-diagram>
</div>
</template>
<script>
import { DiagramComponent } from '@syncfusion/ej2-vue-diagrams';

const nodes = [
{
  id: 'node1',
  offsetX: 300,
  offsetY: 300,
  height: 100,
  width: 100,
  style: { strokeColor: '#64Abbb', strokeWidth: 3 },
  fixedUserHandles: [
    {
      id: 'color',
      pathData:
        'M31.5,13.5 C31.5,20.95,24.44,27,15.75,27 C7.059999999999999,27,0,20.95,0,13.5 C0,6.050000000000001,7.06,0,15.75,0 C24.44,0,31.5,6.05,31.5,13.5 Z M13.12,4.5 L13.12,11.25 L5.25,11.25 L5.25,15.75 L13.12,15.75 L13.12,22.5 L18.38,22.5 L18.38,15.75 L26.25,15.75 L26.25,11.25 L18.38,11.25 L18.38,4.5 Z ',
      width: 30,
      height: 30,
      offset: { x: 1, y: 0 },
      margin: { left: 20, bottom: 10 },
      //Sets the stroke color of fixed user handle
      handleStrokeColor: 'green',
      //Sets the stroke width of fixed user handle
      handleStrokeWidth: 4,
      //Sets the stroke color of icon
      iconStrokeColor: '#64Abbb',
      //Sets the stroke width of icon
      iconStrokeWidth: 1,
      //Sets the fill color of the fixed user handle
      fill: 'yellow',
      //Sets the corner radius of the fixed user handle
      cornerRadius: 5,
    },
  ],
},
];
const connectors = [
{
  id: 'connector1',
  sourcePoint: { x: 100, y: 100 },
  targetPoint: { x: 300, y: 200 },
  style: { strokeColor: '#64Abbb', strokeWidth: 3 },
  fixedUserHandles: [
    {
      id: 'stroke',
      pathData:
        'M0,13.85 L15.62,13.85 L15.62,20 L25,9.74 L15.62,0 L15.62,6.41 L0,6.41 L0,13.85 Z ',
      width: 30,
      height: 25,
      //Offset of fixed user handle
      offset: 0.5,
      //Sets the stroke color of fixed user handle
      handleStrokeColor: 'green',
      //Sets the stroke width of fixed user handle
      handleStrokeWidth: 4,
      //Sets the stroke color of icon
      iconStrokeColor: '#64Abbb',
      //Sets the stroke width of icon
      iconStrokeWidth: 1,
      //Sets the fill color of the fixed user handle
      fill: 'yellow',
      //Sets the corner radius of the fixed user handle
      cornerRadius: 5,
    },
  ],
},
];


export default {
name: "App",
components: {
    "ejs-diagram": DiagramComponent
},
data() {
    return {
        width: "100%",
        height: "350px",
        nodes: nodes,
        connectors: connectors
    }
},
methods: {
    onFixedUserHandleMouseDown(event) {
        const diagramInstance = this.$refs.diagram.ej2Instances;
        const node = event.element.parentObj;
        node.style.strokeColor = node.style.strokeColor === '#64A6' ? '#64Abbb' : '#64A6';
        diagramInstance.dataBind();
    },
    onFixedUserHandleMouseEnter(event) {
      if (event.element) {
        event.element.fill = 'red';
        event.element.handleStrokeColor = 'pink';
      }
    },
    onFixedUserHandleMouseUp(event) {
      if (event.element) {
        event.element.fill = 'white';
        event.element.handleStrokeColor = 'blue';
      }
    },
    onFixedUserHandleMouseLeave(event) {
      if (event.element) {
        event.element.fill = 'yellow';
        event.element.handleStrokeColor = 'green';
      }
    },
}
}

</script>
<style>
@import "../node_modules/@syncfusion/ej2-vue-diagrams/styles/material.css";
</style>