,
+ required: true
+ }
+ },
+ setup(props) {
+ return () => (
+
+
+
+
+ );
+ }
+});
+
+export default ConnectionLine;
diff --git a/examples/CustomConnectionLine/index.tsx b/examples/CustomConnectionLine/index.tsx
new file mode 100644
index 00000000..62e400f7
--- /dev/null
+++ b/examples/CustomConnectionLine/index.tsx
@@ -0,0 +1,28 @@
+import RevueFlow, { removeElements, addEdge, Background, BackgroundVariant, Elements, Connection, Edge } from '../../src';
+
+import ConnectionLine from './ConnectionLine';
+import { defineComponent, ref } from 'vue';
+
+const initialElements: Elements = [{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }];
+
+const ConnectionLineFlow = defineComponent({
+ components: { Background },
+ setup() {
+ const elements = ref(initialElements);
+ const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value));
+ const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
+
+ return () => (
+
+
+
+ );
+ }
+});
+
+export default ConnectionLineFlow;
diff --git a/examples/CustomNode/ColorSelectorNode.tsx b/examples/CustomNode/ColorSelectorNode.tsx
new file mode 100644
index 00000000..3058fe7e
--- /dev/null
+++ b/examples/CustomNode/ColorSelectorNode.tsx
@@ -0,0 +1,33 @@
+import { CSSProperties, defineComponent } from 'vue';
+import { Handle, Position, Connection, Edge } from '../../src';
+
+const targetHandleStyle: CSSProperties = { background: '#555' };
+const sourceHandleStyleA: CSSProperties = { ...targetHandleStyle, top: 10 };
+const sourceHandleStyleB: CSSProperties = { ...targetHandleStyle, bottom: 10, top: 'auto' };
+
+const onConnect = (params: Connection | Edge) => console.log('handle onConnect', params);
+
+const ColorSelectorNode = defineComponent({
+ props: {
+ data: {
+ type: Object,
+ required: false,
+ default: () => ({})
+ }
+ },
+ setup(props) {
+ return () => (
+ <>
+
+
+ Custom Color Picker Node: {props.data.color}
+
+
+
+
+ >
+ );
+ }
+});
+
+export default ColorSelectorNode;
diff --git a/examples/CustomNode/index.tsx b/examples/CustomNode/index.tsx
new file mode 100644
index 00000000..33150d6e
--- /dev/null
+++ b/examples/CustomNode/index.tsx
@@ -0,0 +1,133 @@
+import { defineComponent, onMounted, ref } from 'vue';
+import RevueFlow, {
+ isEdge,
+ removeElements,
+ addEdge,
+ MiniMap,
+ Controls,
+ Node,
+ FlowElement,
+ OnLoadParams,
+ Elements,
+ Position,
+ SnapGrid,
+ Connection,
+ Edge
+} from '../../src';
+import ColorSelectorNode from './ColorSelectorNode';
+
+const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
+const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
+const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
+
+const initBgColor = '#1A192B';
+
+const connectionLineStyle = { stroke: '#fff' };
+const snapGrid: SnapGrid = [16, 16];
+const nodeTypes = {
+ selectorNode: ColorSelectorNode
+};
+
+const CustomNodeFlow = defineComponent({
+ components: { RevueFlow, MiniMap, Controls },
+ setup() {
+ const elements = ref([]);
+ const bgColor = ref(initBgColor);
+
+ onMounted(() => {
+ const onChange = (event: Event) => {
+ elements.value = elements.value.map((e) => {
+ if (isEdge(e) || e.id !== '2') {
+ return e;
+ }
+
+ const color = (event.target as HTMLInputElement).value;
+
+ bgColor.value = color;
+
+ return {
+ ...e,
+ data: {
+ ...e.data,
+ color
+ }
+ };
+ });
+ };
+
+ elements.value = [
+ {
+ id: '1',
+ type: 'input',
+ data: { label: 'An input node' },
+ position: { x: 0, y: 50 },
+ sourcePosition: Position.Right
+ },
+ {
+ id: '2',
+ type: 'selectorNode',
+ data: { onChange: onChange, color: initBgColor },
+ style: { border: '1px solid #777', padding: 10 },
+ position: { x: 250, y: 50 }
+ },
+ {
+ id: '3',
+ type: 'output',
+ data: { label: 'Output A' },
+ position: { x: 550, y: 25 },
+ targetPosition: Position.Left
+ },
+ {
+ id: '4',
+ type: 'output',
+ data: { label: 'Output B' },
+ position: { x: 550, y: 100 },
+ targetPosition: Position.Left
+ },
+
+ { id: 'e1-2', source: '1', target: '2', animated: true, style: { stroke: '#fff' } },
+ { id: 'e2a-3', source: '2', sourceHandle: 'a', target: '3', animated: true, style: { stroke: '#fff' } },
+ { id: 'e2b-4', source: '2', sourceHandle: 'b', target: '4', animated: true, style: { stroke: '#fff' } }
+ ];
+ });
+
+ const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value));
+ const onConnect = (params: Connection | Edge) =>
+ (elements.value = addEdge({ ...params, animated: true, style: { stroke: '#fff' } }, elements.value));
+
+ return () => (
+
+ {
+ if (n.type === 'input') return '#0041d0';
+ if (n.type === 'selectorNode') return bgColor.value;
+ if (n.type === 'output') return '#ff0072';
+
+ return '#eee';
+ }}
+ nodeColor={(n: Node): string => {
+ if (n.type === 'selectorNode') return bgColor.value;
+
+ return '#fff';
+ }}
+ />
+
+
+ );
+ }
+});
+
+export default CustomNodeFlow;
diff --git a/examples/Header.tsx b/examples/Header.tsx
new file mode 100644
index 00000000..abce664b
--- /dev/null
+++ b/examples/Header.tsx
@@ -0,0 +1,30 @@
+import { defineComponent } from 'vue';
+import { routes } from './router';
+import { useRoute, useRouter } from 'vue-router';
+
+const Header = defineComponent({
+ setup() {
+ const router = useRouter();
+ const route = useRoute();
+ const onChange = async (event: any) => {
+ await router.push(event.target.value);
+ };
+
+ return () => (
+
+
+ Revue Flow Dev
+
+
+
+ );
+ }
+});
+
+export default Header;
diff --git a/examples/index.css b/examples/index.css
new file mode 100644
index 00000000..a4d65e65
--- /dev/null
+++ b/examples/index.css
@@ -0,0 +1,91 @@
+body {
+ font-family: sans-serif;
+ color: #111;
+}
+
+#root {
+ font-family: Avenir, Helvetica, Arial, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ text-align: center;
+ color: #2c3e50;
+ margin-top: 60px;
+}
+
+html,
+body,
+#root {
+ margin: 0;
+ height: 100%;
+}
+
+#root {
+ display: flex;
+ flex-direction: column;
+}
+
+header {
+ padding: 10px;
+ border-bottom: 1px solid #eee;
+ display: flex;
+ font-weight: 700;
+ align-items: center;
+}
+
+.logo {
+ text-decoration: none;
+ display: block;
+ line-height: 1;
+}
+
+header a,
+header a:focus,
+header a:active,
+header a:visited {
+ color: #111;
+}
+
+header a:hover {
+ color: #333;
+}
+
+header select {
+ margin-left: 1em;
+}
+
+.overview-example__add {
+ display: none;
+}
+
+.revue-flow__node a {
+ font-weight: 700;
+ color: #111;
+}
+
+.revue-flow__node.dark-node {
+ background: #0041d0;
+ color: #f8f8f8;
+}
+
+.revue-flow__node.dark {
+ background: #557;
+ color: #f8f8f8;
+}
+
+.revue-flow__node-selectorNode {
+ font-size: 12px;
+ background: #f0f2f3;
+ border: 1px solid #555;
+ border-radius: 5px;
+ text-align: center;
+}
+
+.revue-flow__node-selectorNode .revue-flow__handle {
+ border-color: #f0f2f3;
+}
+
+@media screen and (min-width: 768px) {
+ .overview-example__add {
+ display: block;
+ }
+}
diff --git a/examples/main.ts b/examples/main.ts
new file mode 100644
index 00000000..4b0cac1a
--- /dev/null
+++ b/examples/main.ts
@@ -0,0 +1,9 @@
+import { createApp } from 'vue';
+import './index.css';
+import App from './App';
+import { router } from './router';
+const app = createApp(App);
+
+app.config.performance = true;
+app.use(router);
+app.mount('#root');
diff --git a/examples/router.ts b/examples/router.ts
new file mode 100644
index 00000000..5687e5f1
--- /dev/null
+++ b/examples/router.ts
@@ -0,0 +1,21 @@
+import { createRouter, createWebHashHistory } from 'vue-router';
+
+export const routes = [
+ {
+ path: '/basic',
+ component: () => import('./Basic')
+ },
+ {
+ path: '/custom-connectionline',
+ component: () => import('./CustomConnectionLine')
+ },
+ {
+ path: '/custom-node',
+ component: () => import('./CustomNode')
+ }
+];
+
+export const router = createRouter({
+ history: createWebHashHistory(),
+ routes
+});
diff --git a/index.html b/index.html
index 11603f87..1321d38e 100644
--- a/index.html
+++ b/index.html
@@ -2,12 +2,11 @@
-
- Vite App
+ Revue Flow Examples
-
-
+
+
diff --git a/package.json b/package.json
index ebc02416..8b6d175d 100644
--- a/package.json
+++ b/package.json
@@ -33,7 +33,8 @@
"d3-selection": "^3.0.0",
"d3-zoom": "^3.0.0",
"fast-deep-equal": "^3.1.3",
- "pinia": "^2.0.0-beta.3"
+ "pinia": "^2.0.0-beta.3",
+ "vue": "^3.0.5"
},
"devDependencies": {
"@babel/core": "^7.14.6",
@@ -41,7 +42,6 @@
"@rollup/plugin-commonjs": "^19.0.0",
"@rollup/plugin-node-resolve": "^13.0.0",
"@rollup/plugin-replace": "^2.4.2",
- "@svgr/rollup": "^5.5.0",
"@typescript-eslint/eslint-plugin": "^4.28.1",
"@typescript-eslint/parser": "^4.28.1",
"@vitejs/plugin-vue": "^1.2.3",
@@ -64,12 +64,15 @@
"rollup": "^2.52.2",
"rollup-plugin-postcss": "^4.0.0",
"rollup-plugin-serve": "^1.1.0",
+ "rollup-plugin-svg": "^2.0.0",
"rollup-plugin-terser": "^7.0.2",
"rollup-plugin-typescript2": "^0.30.0",
+ "rollup-plugin-vue-inline-svg": "^1.1.2",
"tslib": "^2.3.0",
"typescript": "^4.3.5",
"vite": "^2.3.8",
- "vue": "^3.0.5",
+ "vite-svg-loader": "^2.1.0",
+ "vue-router": "4",
"vue-tsc": "^0.0.24"
},
"peerDependencies": {
diff --git a/rollup.config.js b/rollup.config.js
index 245d62bd..4537ab82 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -7,6 +7,7 @@ import pascalcase from 'pascalcase';
import pkg from './package.json';
import postcss from 'rollup-plugin-postcss';
import babel from '@rollup/plugin-babel';
+import svg from 'rollup-plugin-svg';
import { DEFAULT_EXTENSIONS as DEFAULT_BABEL_EXTENSIONS } from '@babel/core';
const name = pkg.name;
@@ -44,14 +45,6 @@ const outputConfigs = {
file: pkg.main,
format: 'cjs'
},
- 'global-vue-3': {
- file: pkg.unpkg.replace('2', '3'),
- format: 'iife'
- },
- 'global-vue-2': {
- file: pkg.unpkg,
- format: 'iife'
- },
esm: {
file: pkg.browser,
format: 'es'
@@ -113,8 +106,6 @@ function createConfig(format, output, plugins = []) {
const external = ['vue'];
- const nodePlugins = [resolve(), commonjs({ include: 'node_modules/**' })];
-
return {
input: 'src/index.ts',
// Global and Browser ESM builds inlines everything so that they can be
@@ -130,8 +121,9 @@ function createConfig(format, output, plugins = []) {
isGlobalBuild,
isNodeBuild
),
- ...nodePlugins,
- ...plugins,
+ svg(),
+ resolve(),
+ commonjs({ include: 'node_modules/**' }),
postcss({
minimize: true,
inject: true
@@ -140,7 +132,8 @@ function createConfig(format, output, plugins = []) {
extensions: [...DEFAULT_BABEL_EXTENSIONS, '.ts', '.tsx'],
exclude: 'node_modules/**',
babelHelpers: 'inline'
- })
+ }),
+ ...plugins
],
output
// onwarn: (msg, warn) => {
diff --git a/src/App.vue b/src/App.vue
deleted file mode 100644
index 06efe323..00000000
--- a/src/App.vue
+++ /dev/null
@@ -1,38 +0,0 @@
-
-
-
Revue Flow
-
-
-
-
-
-
-
diff --git a/src/additional-components/Controls/index.tsx b/src/additional-components/Controls/index.tsx
index 2f1b2906..5aa2d448 100644
--- a/src/additional-components/Controls/index.tsx
+++ b/src/additional-components/Controls/index.tsx
@@ -1,7 +1,12 @@
+import { defineComponent, HTMLAttributes, onMounted, PropType, ref } from 'vue';
import useZoomPanHelper from '../../hooks/useZoomPanHelper';
import { FitViewParams } from '../../types';
-import { defineComponent, HTMLAttributes, onMounted, PropType, ref } from 'vue';
import store from '../../store';
+import PlusIcon from '../../../assets/icons/plus.svg';
+import MinusIcon from '../../../assets/icons/minus.svg';
+import Fitview from '../../../assets/icons/fitview.svg';
+import Lock from '../../../assets/icons/lock.svg';
+import Unlock from '../../../assets/icons/unlock.svg';
export interface ControlProps extends HTMLAttributes {
showZoom?: boolean;
@@ -123,25 +128,21 @@ const Controls = defineComponent({
{props.showZoom && (
<>
-
+
-
+
>
)}
{props.showFitView && (
-
+
)}
{props.showInteractive && (
- {isInteractive ? (
-
- ) : (
-
- )}
+ {isInteractive ? : }
)}
{slots.default ? slots.default() : ''}
diff --git a/src/additional-components/MiniMap/MiniMapNode.tsx b/src/additional-components/MiniMap/MiniMapNode.tsx
index f8af5302..bd761267 100644
--- a/src/additional-components/MiniMap/MiniMapNode.tsx
+++ b/src/additional-components/MiniMap/MiniMapNode.tsx
@@ -1,4 +1,4 @@
-import { defineComponent, PropType } from 'vue';
+import { computed, defineComponent, PropType } from 'vue';
interface MiniMapNodeProps {
x: number;
@@ -62,19 +62,19 @@ const MiniMapNode = defineComponent({
}
},
setup(props, { attrs }: { attrs: Record }) {
- const { background, backgroundColor } = attrs.style || {};
- const fill = (props.color || background || backgroundColor) as string;
+ const styles = attrs.style || {};
+ const fill = computed(() => (props.color || styles.value.background || styles.value.backgroundColor) as string);
return () => (
,
+ type: [String, Function] as PropType,
required: false,
default: '#555'
},
nodeColor: {
- type: (String || Function) as PropType,
+ type: [String, Function] as PropType,
required: false,
default: '#fff'
},
nodeClassName: {
- type: (String || Function) as PropType,
+ type: [String, Function] as PropType,
required: false,
default: ''
},
@@ -57,17 +57,15 @@ const MiniMap = defineComponent({
setup(props, { attrs }: { attrs: Record }) {
const pinia = store();
const transform = computed(() => pinia.transform);
-
- const mapClasses = ['revue-flow__minimap'];
const elementWidth = computed(() => (attrs.style?.width || defaultWidth)! as number);
const elementHeight = computed(() => (attrs.style?.height || defaultHeight)! as number);
- const nodeColorFunc = (props.nodeColor instanceof Function ? props.nodeColor : () => props.nodeColor) as StringFunc;
- const nodeStrokeColorFunc = (
- props.nodeStrokeColor instanceof Function ? props.nodeStrokeColor : () => props.nodeStrokeColor
- ) as StringFunc;
- const nodeClassNameFunc = (
- props.nodeClassName instanceof Function ? props.nodeClassName : () => props.nodeClassName
- ) as StringFunc;
+ const nodeColorFunc = computed(() => (props.nodeColor instanceof Function ? props.nodeColor : () => props.nodeColor) as StringFunc);
+ const nodeStrokeColorFunc = computed(
+ () => (props.nodeStrokeColor instanceof Function ? props.nodeStrokeColor : () => props.nodeStrokeColor) as StringFunc
+ );
+ const nodeClassNameFunc = computed(
+ () => (props.nodeClassName instanceof Function ? props.nodeClassName : () => props.nodeClassName) as StringFunc
+ );
const hasNodes = computed(() => pinia.nodes && pinia.nodes.length);
const bb = computed(() => getRectOfNodes(pinia.nodes));
const viewBB = computed(() => ({
@@ -94,7 +92,7 @@ const MiniMap = defineComponent({
width={elementWidth.value}
height={elementHeight.value}
viewBox={`${x.value} ${y.value} ${width.value} ${height.value}`}
- class={mapClasses}
+ class="revue-flow__minimap"
>
{pinia.nodes
.filter((node) => !node.isHidden)
@@ -106,10 +104,10 @@ const MiniMap = defineComponent({
width={node.__rf.width}
height={node.__rf.height}
style={node.style}
- class={nodeClassNameFunc(node)}
- color={nodeColorFunc(node)}
+ class={nodeClassNameFunc.value(node)}
+ color={nodeColorFunc.value(node)}
borderRadius={props.nodeBorderRadius}
- strokeColor={nodeStrokeColorFunc(node)}
+ strokeColor={nodeStrokeColorFunc.value(node)}
strokeWidth={props.nodeStrokeWidth}
shapeRendering={shapeRendering}
/>
diff --git a/src/components/Edges/BezierEdge.tsx b/src/components/Edges/BezierEdge.tsx
index faa204e6..db107d09 100644
--- a/src/components/Edges/BezierEdge.tsx
+++ b/src/components/Edges/BezierEdge.tsx
@@ -35,10 +35,13 @@ export function getBezierPath({
let path = `M${sourceX},${sourceY} C${sourceX},${cY} ${targetX},${cY} ${targetX},${targetY}`;
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
+ console.log('foo');
path = `M${sourceX},${sourceY} C${cX},${sourceY} ${cX},${targetY} ${targetX},${targetY}`;
} else if (leftAndRight.includes(targetPosition)) {
+ console.log('bar');
path = `M${sourceX},${sourceY} C${sourceX},${targetY} ${sourceX},${targetY} ${targetX},${targetY}`;
} else if (leftAndRight.includes(sourcePosition)) {
+ console.log('baz');
path = `M${sourceX},${sourceY} C${targetX},${sourceY} ${targetX},${sourceY} ${targetX},${targetY}`;
}
@@ -82,9 +85,9 @@ const BezierEdge = defineComponent({
required: true
},
labelStyle: {
- type: Object as PropType,
+ type: Object,
required: true,
- default: () => ({})
+ default: undefined
},
labelShowBg: {
type: Boolean,
@@ -153,11 +156,11 @@ const BezierEdge = defineComponent({
/>
) : null;
- const markerEnd = getMarkerEnd(props.arrowHeadType, props.markerEndId);
+ const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId));
return () => (
<>
-
+
{text}
>
);
diff --git a/src/components/Edges/EdgeAnchor.tsx b/src/components/Edges/EdgeAnchor.tsx
index aaf12564..1a6303e8 100644
--- a/src/components/Edges/EdgeAnchor.tsx
+++ b/src/components/Edges/EdgeAnchor.tsx
@@ -1,5 +1,5 @@
import { Position } from '../../types';
-import { defineComponent, HTMLAttributes, PropType } from 'vue';
+import { computed, defineComponent, HTMLAttributes, PropType } from 'vue';
const shiftX = (x: number, shift: number, position: Position): number => {
if (position === Position.Left) return x - shift;
@@ -41,13 +41,13 @@ export const EdgeAnchor = defineComponent({
}
},
setup(props) {
- const radius = props.radius || 10;
+ const radius = computed(() => props.radius || 10);
return () => (
diff --git a/src/components/Edges/EdgeText.tsx b/src/components/Edges/EdgeText.tsx
index f954c737..6f866398 100644
--- a/src/components/Edges/EdgeText.tsx
+++ b/src/components/Edges/EdgeText.tsx
@@ -28,8 +28,7 @@ const EdgeText = defineComponent({
default: () => ({})
},
labelBgPadding: {
- /* @ts-ignore */
- type: Array as PropType<[number, number]>,
+ type: Array as unknown as PropType<[number, number]>,
default: () => [2, 4]
},
labelBgBorderRadius: {
diff --git a/src/components/Edges/wrapEdge.tsx b/src/components/Edges/wrapEdge.tsx
index 0ccd3d2e..f3759ff8 100644
--- a/src/components/Edges/wrapEdge.tsx
+++ b/src/components/Edges/wrapEdge.tsx
@@ -1,4 +1,4 @@
-import { Component, computed, defineComponent, ref } from 'vue';
+import { Component, computed, defineComponent, PropType, ref } from 'vue';
import store from '../../store';
import { Edge, EdgeProps, Position, WrapEdgeProps } from '../../types';
@@ -8,11 +8,188 @@ import { EdgeAnchor } from './EdgeAnchor';
export default (EdgeComponent: any): Component => {
return defineComponent({
components: { EdgeComponent },
- props: WrapEdgeProps,
+ props: {
+ id: {
+ type: String as PropType,
+ required: false,
+ default: undefined
+ },
+ type: {
+ type: String as PropType,
+ required: true
+ },
+ data: {
+ type: String as PropType,
+ required: false,
+ default: undefined
+ },
+ onClick: {
+ type: Function as unknown as PropType,
+ required: false,
+ default: undefined
+ },
+ onEdgeDoubleClick: {
+ type: Function as unknown as PropType,
+ required: false,
+ default: undefined
+ },
+ selected: {
+ type: Boolean as PropType,
+ required: true,
+ default: false
+ },
+ animated: {
+ type: Boolean as PropType,
+ required: false,
+ default: false
+ },
+ label: {
+ type: Object as PropType,
+ required: false,
+ default: undefined
+ },
+ labelStyle: {
+ type: Object as PropType,
+ required: false,
+ default: undefined
+ },
+ labelShowBg: {
+ type: Boolean as PropType,
+ required: false,
+ default: false
+ },
+ labelBgStyle: {
+ type: Object as PropType,
+ required: false,
+ default: undefined
+ },
+ labelBgPadding: {
+ type: Array as unknown as PropType,
+ required: false,
+ default: undefined
+ },
+ labelBgBorderRadius: {
+ type: Number as PropType,
+ required: false,
+ default: undefined
+ },
+ style: {
+ type: Object as PropType,
+ required: false,
+ default: undefined
+ },
+ arrowHeadType: {
+ type: String as PropType,
+ required: false,
+ default: undefined
+ },
+ source: {
+ type: String as PropType,
+ required: true
+ },
+ target: {
+ type: String as PropType,
+ required: true
+ },
+ sourceHandleId: {
+ type: String as PropType,
+ required: true,
+ default: null
+ },
+ targetHandleId: {
+ type: String as PropType,
+ required: true,
+ default: null
+ },
+ sourceX: {
+ type: Number as PropType,
+ required: true
+ },
+ sourceY: {
+ type: Number as PropType,
+ required: true
+ },
+ targetX: {
+ type: Number as PropType,
+ required: true
+ },
+ targetY: {
+ type: Number as PropType,
+ required: true
+ },
+ sourcePosition: {
+ type: String as PropType,
+ required: true
+ },
+ targetPosition: {
+ type: String as PropType,
+ required: true
+ },
+ elementsSelectable: {
+ type: Boolean as PropType,
+ required: false,
+ default: false
+ },
+ markerEndId: {
+ type: String as PropType,
+ required: false,
+ default: undefined
+ },
+ isHidden: {
+ type: Boolean as PropType,
+ required: false,
+ default: false
+ },
+ handleEdgeUpdate: {
+ type: Boolean as PropType,
+ required: false,
+ default: false
+ },
+ onConnectEdge: {
+ type: Function as PropType,
+ required: false,
+ default: undefined
+ },
+ onContextMenu: {
+ type: Function as unknown as PropType,
+ required: false,
+ default: undefined
+ },
+ onMouseEnter: {
+ type: Function as unknown as PropType,
+ required: false,
+ default: undefined
+ },
+ onMouseMove: {
+ type: Function as unknown as PropType,
+ required: false,
+ default: undefined
+ },
+ onMouseLeave: {
+ type: Function as unknown as PropType,
+ required: false,
+ default: undefined
+ },
+ edgeUpdaterRadius: {
+ type: Number as PropType,
+ required: false,
+ default: undefined
+ },
+ onEdgeUpdateStart: {
+ type: Function as unknown as PropType,
+ required: false,
+ default: undefined
+ },
+ onEdgeUpdateEnd: {
+ type: Function as unknown as PropType,
+ required: false,
+ default: undefined
+ }
+ },
setup(props) {
const pinia = store();
const updating = ref(false);
- const inactive = !props.elementsSelectable && !props.onClick;
+ const inactive = computed(() => !props.elementsSelectable && !props.onClick);
const edgeClasses = computed(() => [
'revue-flow__edge',
`revue-flow__edge-${props.type}`,
@@ -21,9 +198,9 @@ export default (EdgeComponent: any): Component => {
const edgeElement = computed(() => {
const el: Edge = {
- id: props.id || '',
- source: props.source || '',
- target: props.target || '',
+ id: props.id as string,
+ source: props.source,
+ target: props.target,
type: props.type
};
@@ -48,23 +225,23 @@ export default (EdgeComponent: any): Component => {
pinia.addSelectedElements(edgeElement.value as any);
}
- if (props.onClick) props.onClick?.(event, edgeElement.value);
+ if (typeof props.onClick === 'function') props.onClick(event, edgeElement.value);
};
const onEdgeContextMenu = (event: MouseEvent) => {
- if (typeof props.onContextMenu === 'function') props.onContextMenu?.(event, edgeElement.value);
+ if (typeof props.onContextMenu === 'function') props.onContextMenu(event, edgeElement.value);
};
const onEdgeMouseEnter = (event: MouseEvent) => {
- if (typeof props.onMouseEnter === 'function') props.onMouseEnter?.(event, edgeElement.value);
+ if (typeof props.onMouseEnter === 'function') props.onMouseEnter(event, edgeElement.value);
};
const onEdgeMouseMove = (event: MouseEvent) => {
- if (typeof props.onMouseMove === 'function') props.onMouseMove?.(event, edgeElement.value);
+ if (typeof props.onMouseMove === 'function') props.onMouseMove(event, edgeElement.value);
};
const onEdgeMouseLeave = (event: MouseEvent) => {
- if (typeof props.onMouseLeave === 'function') props.onMouseLeave?.(event, edgeElement.value);
+ if (typeof props.onMouseLeave === 'function') props.onMouseLeave(event, edgeElement.value);
};
const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => {
@@ -84,7 +261,7 @@ export default (EdgeComponent: any): Component => {
onMouseDown(
event,
handleId,
- nodeId || '',
+ nodeId,
pinia.setConnectionNodeId,
pinia.setConnectionPosition,
props.onConnectEdge as any,
diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx
index 16c35cf1..38055ce0 100644
--- a/src/container/EdgeRenderer/index.tsx
+++ b/src/container/EdgeRenderer/index.tsx
@@ -15,6 +15,7 @@ import {
} from '../../types';
import { computed, CSSProperties, defineComponent, PropType } from 'vue';
import store from '../../store';
+import MarkerDefinitions from './MarkerDefinitions';
interface EdgeRendererProps {
edgeTypes: any;
@@ -101,8 +102,6 @@ const EdgeCmp = defineComponent({
}
},
setup(props) {
- const sourceHandleId = computed(() => props.edge.sourceHandle || null);
- const targetHandleId = computed(() => props.edge.targetHandle || null);
const nodes = computed(() => getSourceTargetNodes(props.edge, props.nodes));
const onConnectEdge = (connection: Connection) => {
@@ -119,20 +118,20 @@ const EdgeCmp = defineComponent({
return () => null;
}
- const edgeType = props.edge.type || 'default';
- const EdgeComponent: any = props.props.edgeTypes[edgeType] || props.props.edgeTypes.default;
- const targetNodeBounds = nodes.value.targetNode.__rf.handleBounds;
+ const EdgeComponent: any = props.props.edgeTypes[props.edge.type as string] || props.props.edgeTypes.default;
+ const targetNodeBounds = computed(() => nodes.value.targetNode?.__rf.handleBounds);
// when connection type is loose we can define all handles as sources
- const targetNodeHandles =
+ const targetNodeHandles = computed(() =>
props.connectionMode === ConnectionMode.Strict
- ? targetNodeBounds.target
- : targetNodeBounds.target || targetNodeBounds.source;
- const sourceHandle = computed(
- () => nodes.value.sourceNode && getHandle(nodes.value.sourceNode.__rf.handleBounds.source, sourceHandleId.value)
+ ? targetNodeBounds.value?.target
+ : targetNodeBounds.value?.target || targetNodeBounds.value?.source
);
- const targetHandle = computed(() => getHandle(targetNodeHandles, targetHandleId.value));
- const sourcePosition = sourceHandle.value ? sourceHandle.value.position : Position.Bottom;
- const targetPosition = targetHandle.value ? targetHandle.value.position : Position.Top;
+ const sourceHandle = computed(
+ () => nodes.value.sourceNode && getHandle(nodes.value.sourceNode.__rf.handleBounds.source, props.edge.sourceHandle || null)
+ );
+ const targetHandle = computed(() => getHandle(targetNodeHandles.value, props.edge.targetHandle || null));
+ const sourcePosition = computed(() => (sourceHandle.value ? sourceHandle.value.position : Position.Bottom));
+ const targetPosition = computed(() => (targetHandle.value ? targetHandle.value.position : Position.Top));
const edgePositions = computed(() => {
return (
@@ -140,11 +139,11 @@ const EdgeCmp = defineComponent({
nodes.value.targetNode &&
getEdgePositions(
nodes.value.sourceNode,
- sourceHandle,
- sourcePosition,
+ sourceHandle.value,
+ sourcePosition.value,
nodes.value.targetNode,
- targetHandle,
- targetPosition
+ targetHandle.value,
+ targetPosition.value
)
);
});
@@ -188,8 +187,8 @@ const EdgeCmp = defineComponent({
arrowHeadType={props.edge.arrowHeadType}
source={props.edge.source}
target={props.edge.target}
- sourceHandleId={sourceHandleId}
- targetHandleId={targetHandleId}
+ sourceHandleId={props.edge.sourceHandle}
+ targetHandleId={props.edge.targetHandle}
sourceX={edgePositions.value?.sourceX}
sourceY={edgePositions.value?.sourceY}
targetX={edgePositions.value?.targetX}
@@ -218,7 +217,8 @@ const EdgeRenderer = defineComponent({
name: 'EdgeRenderer',
components: {
EdgeCmp,
- ConnectionLine
+ ConnectionLine,
+ MarkerDefinitions
},
props: {
edgeTypes: {
@@ -247,12 +247,12 @@ const EdgeRenderer = defineComponent({
default: undefined
},
onElementClick: {
- type: Function() as PropType,
+ type: Function as unknown as PropType,
required: false,
default: undefined
},
onEdgeDoubleClick: {
- type: Function() as PropType,
+ type: Function as unknown as PropType,
required: false,
default: undefined
},
@@ -272,37 +272,37 @@ const EdgeRenderer = defineComponent({
default: undefined
},
onEdgeUpdate: {
- type: Function() as PropType,
+ type: Function as unknown as PropType,
required: false,
default: undefined
},
onEdgeContextMenu: {
- type: Function() as PropType,
+ type: Function as unknown as PropType,
required: false,
default: undefined
},
onEdgeMouseEnter: {
- type: Function() as PropType,
+ type: Function as unknown as PropType,
required: false,
default: undefined
},
onEdgeMouseMove: {
- type: Function() as PropType,
+ type: Function as unknown as PropType,
required: false,
default: undefined
},
onEdgeMouseLeave: {
- type: Function() as PropType,
+ type: Function as unknown as PropType,
required: false,
default: undefined
},
onEdgeUpdateStart: {
- type: Function() as PropType,
+ type: Function as unknown as PropType,
required: false,
default: undefined
},
onEdgeUpdateEnd: {
- type: Function() as PropType,
+ type: Function as unknown as PropType,
required: false,
default: undefined
},
@@ -314,11 +314,15 @@ const EdgeRenderer = defineComponent({
},
setup(props) {
const pinia = store();
- const transformStyle = computed(() => `translate(${pinia.transform[0]},${pinia.transform[1]}) scale(${pinia.transform[2]})`);
+ const transform = computed(() => pinia.transform);
+ const transformStyle = computed(() => {
+ return `translate(${transform.value[0]},${transform.value[1]}) scale(${transform.value[2]})`;
+ });
const renderConnectionLine = computed(() => pinia.connectionNodeId && pinia.connectionHandleType);
return () => (