chore(lint): Update eslint rules
* Remove semi
This commit is contained in:
+14
-15
@@ -3,12 +3,12 @@ const baseRules = {
|
|||||||
'@typescript-eslint/ban-ts-ignore': 0,
|
'@typescript-eslint/ban-ts-ignore': 0,
|
||||||
'@typescript-eslint/ban-ts-comment': 0,
|
'@typescript-eslint/ban-ts-comment': 0,
|
||||||
'@typescript-eslint/no-empty-function': 0,
|
'@typescript-eslint/no-empty-function': 0,
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 0,
|
||||||
'no-use-before-define': 0,
|
'no-use-before-define': 0,
|
||||||
'no-unused-vars': 'off',
|
'no-unused-vars': 'off',
|
||||||
'@typescript-eslint/no-unused-vars': ['warn'],
|
'@typescript-eslint/no-unused-vars': ['warn'],
|
||||||
indent: ['warn', 2, { SwitchCase: 1, flatTernaryExpressions: true }],
|
indent: ['warn', 2, { SwitchCase: 1, flatTernaryExpressions: true }],
|
||||||
quotes: ['error', 'single', { avoidEscape: true }],
|
quotes: ['error', 'single', { avoidEscape: true }],
|
||||||
semi: ['error', 'always'],
|
|
||||||
'import/no-mutable-exports': 0,
|
'import/no-mutable-exports': 0,
|
||||||
'no-cond-assign': [2],
|
'no-cond-assign': [2],
|
||||||
'no-console': 'off',
|
'no-console': 'off',
|
||||||
@@ -16,28 +16,27 @@ const baseRules = {
|
|||||||
'@typescript/camelcase': 0,
|
'@typescript/camelcase': 0,
|
||||||
'vue/no-v-html': 'off',
|
'vue/no-v-html': 'off',
|
||||||
'vue/attribute-hyphenation': 0,
|
'vue/attribute-hyphenation': 0,
|
||||||
'prettier/prettier': [
|
'no-undef': 'off',
|
||||||
'warn',
|
'prettier/prettier': ['error', {}, { usePrettierrc: true }]
|
||||||
{
|
}
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'none',
|
|
||||||
semi: true,
|
|
||||||
bracketSpacing: true,
|
|
||||||
vueIndentScriptAndStyle: false,
|
|
||||||
printWidth: 130
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
ecmaVersion: 2020,
|
||||||
|
sourceType: 'module',
|
||||||
|
ecmaFeatures: {
|
||||||
|
jsx: true
|
||||||
|
}
|
||||||
|
},
|
||||||
env: {
|
env: {
|
||||||
node: true,
|
node: true,
|
||||||
commonjs: true,
|
commonjs: true,
|
||||||
es6: true,
|
es6: true,
|
||||||
browser: true
|
browser: true
|
||||||
},
|
},
|
||||||
extends: ['plugin:vue/vue3-recommended', 'eslint:recommended', '@vue/typescript/recommended', 'plugin:prettier/recommended'],
|
extends: ['eslint:recommended', 'plugin:vue/vue3-recommended', '@vue/typescript/recommended', 'plugin:prettier/recommended'],
|
||||||
plugins: [],
|
plugins: [],
|
||||||
rules: baseRules
|
rules: baseRules
|
||||||
};
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
"semi": true,
|
"semi": false,
|
||||||
"bracketSpacing": true,
|
"bracketSpacing": true,
|
||||||
"vueIndentScriptAndStyle": false,
|
"vueIndentScriptAndStyle": false,
|
||||||
"printWidth": 130
|
"printWidth": 130
|
||||||
|
|||||||
+7
-7
@@ -1,6 +1,6 @@
|
|||||||
import { computed, defineComponent } from 'vue';
|
import { computed, defineComponent } from 'vue'
|
||||||
import Header from './Header';
|
import Header from './Header'
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'App',
|
name: 'App',
|
||||||
@@ -8,13 +8,13 @@ export default defineComponent({
|
|||||||
Header
|
Header
|
||||||
},
|
},
|
||||||
setup() {
|
setup() {
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const key = computed(() => route.fullPath);
|
const key = computed(() => route.fullPath)
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
<Header />
|
<Header />
|
||||||
<router-view ref="view" key={key.value} />
|
<router-view ref="view" key={key.value} />
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
const onDragStart = (event: DragEvent, nodeType: string) => {
|
const onDragStart = (event: DragEvent, nodeType: string) => {
|
||||||
if (event.dataTransfer) {
|
if (event.dataTransfer) {
|
||||||
event.dataTransfer.setData('application/revueflow', nodeType);
|
event.dataTransfer.setData('application/revueflow', nodeType)
|
||||||
event.dataTransfer.effectAllowed = 'move';
|
event.dataTransfer.effectAllowed = 'move'
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const Sidebar = () => {
|
const Sidebar = () => {
|
||||||
return (
|
return (
|
||||||
@@ -19,7 +19,7 @@ const Sidebar = () => {
|
|||||||
Output Node
|
Output Node
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Sidebar;
|
export default Sidebar
|
||||||
|
|||||||
@@ -11,22 +11,22 @@ import RevueFlow, {
|
|||||||
Connection,
|
Connection,
|
||||||
Edge,
|
Edge,
|
||||||
Elements
|
Elements
|
||||||
} from '../../src';
|
} from '../../src'
|
||||||
import { getElements } from './utils';
|
import { getElements } from './utils'
|
||||||
import { defineComponent, ref } from 'vue';
|
import { defineComponent, ref } from 'vue'
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
||||||
reactFlowInstance.fitView();
|
reactFlowInstance.fitView()
|
||||||
console.log(reactFlowInstance.getElements());
|
console.log(reactFlowInstance.getElements())
|
||||||
};
|
}
|
||||||
|
|
||||||
const initialElements = getElements();
|
const initialElements = getElements()
|
||||||
|
|
||||||
const EdgeTypesFlow = defineComponent({
|
const EdgeTypesFlow = defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const elements = ref<Elements>(initialElements);
|
const elements = ref<Elements>(initialElements)
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value));
|
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
||||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
|
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<RevueFlow
|
<RevueFlow
|
||||||
@@ -36,8 +36,8 @@ const EdgeTypesFlow = defineComponent({
|
|||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
minZoom={0.2}
|
minZoom={0.2}
|
||||||
></RevueFlow>
|
></RevueFlow>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default EdgeTypesFlow;
|
export default EdgeTypesFlow
|
||||||
|
|||||||
+27
-27
@@ -1,18 +1,18 @@
|
|||||||
import { ElementId, Elements, Position } from '../../src';
|
import { ElementId, Elements, Position } from '../../src'
|
||||||
|
|
||||||
const nodeWidth = 80;
|
const nodeWidth = 80
|
||||||
const nodeGapWidth = nodeWidth * 2;
|
const nodeGapWidth = nodeWidth * 2
|
||||||
const nodeStyle = { width: nodeWidth + 'px', fontSize: '11px', color: 'white' };
|
const nodeStyle = { width: nodeWidth + 'px', fontSize: '11px', color: 'white' }
|
||||||
|
|
||||||
const sourceTargetPositions = [
|
const sourceTargetPositions = [
|
||||||
{ source: Position.Bottom, target: Position.Top },
|
{ source: Position.Bottom, target: Position.Top },
|
||||||
{ source: Position.Right, target: Position.Left }
|
{ source: Position.Right, target: Position.Left }
|
||||||
];
|
]
|
||||||
const nodeColors = [
|
const nodeColors = [
|
||||||
['#1e9e99', '#4cb3ac', '#6ec9c0', '#8ddfd4'],
|
['#1e9e99', '#4cb3ac', '#6ec9c0', '#8ddfd4'],
|
||||||
['#0f4c75', '#1b5d8b', '#276fa1', '#3282b8']
|
['#0f4c75', '#1b5d8b', '#276fa1', '#3282b8']
|
||||||
];
|
]
|
||||||
const edgeTypes = ['default', 'step', 'smoothstep', 'straight'];
|
const edgeTypes = ['default', 'step', 'smoothstep', 'straight']
|
||||||
const offsets = [
|
const offsets = [
|
||||||
{
|
{
|
||||||
x: 0,
|
x: 0,
|
||||||
@@ -46,30 +46,30 @@ const offsets = [
|
|||||||
x: -nodeGapWidth,
|
x: -nodeGapWidth,
|
||||||
y: -nodeGapWidth
|
y: -nodeGapWidth
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
|
||||||
let id = 0;
|
let id = 0
|
||||||
const getNodeId = (): ElementId => (id++).toString();
|
const getNodeId = (): ElementId => (id++).toString()
|
||||||
|
|
||||||
export function getElements(): Elements {
|
export function getElements(): Elements {
|
||||||
const initialElements = [];
|
const initialElements = []
|
||||||
|
|
||||||
for (let sourceTargetIndex = 0; sourceTargetIndex < sourceTargetPositions.length; sourceTargetIndex++) {
|
for (let sourceTargetIndex = 0; sourceTargetIndex < sourceTargetPositions.length; sourceTargetIndex++) {
|
||||||
const currSourceTargetPos = sourceTargetPositions[sourceTargetIndex];
|
const currSourceTargetPos = sourceTargetPositions[sourceTargetIndex]
|
||||||
|
|
||||||
for (let edgeTypeIndex = 0; edgeTypeIndex < edgeTypes.length; edgeTypeIndex++) {
|
for (let edgeTypeIndex = 0; edgeTypeIndex < edgeTypes.length; edgeTypeIndex++) {
|
||||||
const currEdgeType = edgeTypes[edgeTypeIndex];
|
const currEdgeType = edgeTypes[edgeTypeIndex]
|
||||||
|
|
||||||
for (let offsetIndex = 0; offsetIndex < offsets.length; offsetIndex++) {
|
for (let offsetIndex = 0; offsetIndex < offsets.length; offsetIndex++) {
|
||||||
const currOffset = offsets[offsetIndex];
|
const currOffset = offsets[offsetIndex]
|
||||||
|
|
||||||
const style = { ...nodeStyle, background: nodeColors[sourceTargetIndex][edgeTypeIndex] };
|
const style = { ...nodeStyle, background: nodeColors[sourceTargetIndex][edgeTypeIndex] }
|
||||||
const sourcePosition = {
|
const sourcePosition = {
|
||||||
x: offsetIndex * nodeWidth * 4,
|
x: offsetIndex * nodeWidth * 4,
|
||||||
y: edgeTypeIndex * 300 + sourceTargetIndex * edgeTypes.length * 300
|
y: edgeTypeIndex * 300 + sourceTargetIndex * edgeTypes.length * 300
|
||||||
};
|
}
|
||||||
const sourceId = getNodeId();
|
const sourceId = getNodeId()
|
||||||
const sourceData = { label: `Source ${sourceId}` };
|
const sourceData = { label: `Source ${sourceId}` }
|
||||||
const sourceNode = {
|
const sourceNode = {
|
||||||
id: sourceId,
|
id: sourceId,
|
||||||
style,
|
style,
|
||||||
@@ -77,14 +77,14 @@ export function getElements(): Elements {
|
|||||||
position: sourcePosition,
|
position: sourcePosition,
|
||||||
sourcePosition: currSourceTargetPos.source,
|
sourcePosition: currSourceTargetPos.source,
|
||||||
targetPosition: currSourceTargetPos.target
|
targetPosition: currSourceTargetPos.target
|
||||||
};
|
}
|
||||||
|
|
||||||
const targetId = getNodeId();
|
const targetId = getNodeId()
|
||||||
const targetData = { label: `Target ${targetId}` };
|
const targetData = { label: `Target ${targetId}` }
|
||||||
const targetPosition = {
|
const targetPosition = {
|
||||||
x: sourcePosition.x + currOffset.x,
|
x: sourcePosition.x + currOffset.x,
|
||||||
y: sourcePosition.y + currOffset.y
|
y: sourcePosition.y + currOffset.y
|
||||||
};
|
}
|
||||||
const targetNode = {
|
const targetNode = {
|
||||||
id: targetId,
|
id: targetId,
|
||||||
style,
|
style,
|
||||||
@@ -92,15 +92,15 @@ export function getElements(): Elements {
|
|||||||
position: targetPosition,
|
position: targetPosition,
|
||||||
sourcePosition: currSourceTargetPos.source,
|
sourcePosition: currSourceTargetPos.source,
|
||||||
targetPosition: currSourceTargetPos.target
|
targetPosition: currSourceTargetPos.target
|
||||||
};
|
}
|
||||||
|
|
||||||
initialElements.push(sourceNode);
|
initialElements.push(sourceNode)
|
||||||
initialElements.push(targetNode);
|
initialElements.push(targetNode)
|
||||||
|
|
||||||
initialElements.push({ id: `${sourceId}-${targetId}`, source: sourceId, target: targetId, type: currEdgeType });
|
initialElements.push({ id: `${sourceId}-${targetId}`, source: sourceId, target: targetId, type: currEdgeType })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return initialElements as Elements;
|
return initialElements as Elements
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,25 @@
|
|||||||
import { computed, defineComponent, inject } from 'vue';
|
import { computed, defineComponent, inject } from 'vue'
|
||||||
import { getBezierPath, getEdgeCenter, getMarkerEnd, RevueFlowStore } from '../../src';
|
import { getBezierPath, getEdgeCenter, getMarkerEnd, RevueFlowStore } from '../../src'
|
||||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils';
|
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
|
||||||
import { RevueFlowHooks } from '../../src/hooks/RevueFlowHooks';
|
import { RevueFlowHooks } from '../../src/hooks/RevueFlowHooks'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
...DefaultEdgeProps
|
...DefaultEdgeProps
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||||
const onEdgeClick = (evt: Event, id: string) => {
|
const onEdgeClick = (evt: Event, id: string) => {
|
||||||
const edge = store.edges.find((edge) => edge.id === id);
|
const edge = store.edges.find((edge) => edge.id === id)
|
||||||
if (edge) {
|
if (edge) {
|
||||||
hooks.elementsRemove.trigger([edge]);
|
hooks.elementsRemove.trigger([edge])
|
||||||
}
|
}
|
||||||
evt.stopPropagation();
|
evt.stopPropagation()
|
||||||
alert(`remove ${id}`);
|
alert(`remove ${id}`)
|
||||||
};
|
}
|
||||||
|
|
||||||
const foreignObjectSize = 40;
|
const foreignObjectSize = 40
|
||||||
|
|
||||||
const edgePath = computed(() =>
|
const edgePath = computed(() =>
|
||||||
getBezierPath({
|
getBezierPath({
|
||||||
@@ -30,8 +30,8 @@ export default defineComponent({
|
|||||||
targetY: props.targetY,
|
targetY: props.targetY,
|
||||||
targetPosition: props.targetPosition
|
targetPosition: props.targetPosition
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId));
|
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
||||||
const center = computed(() =>
|
const center = computed(() =>
|
||||||
getEdgeCenter({
|
getEdgeCenter({
|
||||||
sourceX: props.sourceX,
|
sourceX: props.sourceX,
|
||||||
@@ -39,7 +39,7 @@ export default defineComponent({
|
|||||||
targetX: props.targetX,
|
targetX: props.targetX,
|
||||||
targetY: props.targetY
|
targetY: props.targetY
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
@@ -59,6 +59,6 @@ export default defineComponent({
|
|||||||
</body>
|
</body>
|
||||||
</foreignObject>
|
</foreignObject>
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { getBezierPath, getMarkerEnd } from '../../src';
|
import { getBezierPath, getMarkerEnd } from '../../src'
|
||||||
import { computed, defineComponent } from 'vue';
|
import { computed, defineComponent } from 'vue'
|
||||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils';
|
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
|
||||||
|
|
||||||
const CustomEdge = defineComponent({
|
const CustomEdge = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -16,8 +16,8 @@ const CustomEdge = defineComponent({
|
|||||||
targetY: props.targetY,
|
targetY: props.targetY,
|
||||||
targetPosition: props.targetPosition
|
targetPosition: props.targetPosition
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId));
|
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
@@ -28,8 +28,8 @@ const CustomEdge = defineComponent({
|
|||||||
</textPath>
|
</textPath>
|
||||||
</text>
|
</text>
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default CustomEdge;
|
export default CustomEdge
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { computed, defineComponent } from 'vue';
|
import { computed, defineComponent } from 'vue'
|
||||||
import { EdgeText, getBezierPath, getEdgeCenter, getMarkerEnd } from '../../src';
|
import { EdgeText, getBezierPath, getEdgeCenter, getMarkerEnd } from '../../src'
|
||||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils';
|
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
|
||||||
|
|
||||||
const CustomEdge = defineComponent({
|
const CustomEdge = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -16,8 +16,8 @@ const CustomEdge = defineComponent({
|
|||||||
targetY: props.targetY,
|
targetY: props.targetY,
|
||||||
targetPosition: props.targetPosition
|
targetPosition: props.targetPosition
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId));
|
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
||||||
const center = computed(() =>
|
const center = computed(() =>
|
||||||
getEdgeCenter({
|
getEdgeCenter({
|
||||||
sourceX: props.sourceX,
|
sourceX: props.sourceX,
|
||||||
@@ -25,7 +25,7 @@ const CustomEdge = defineComponent({
|
|||||||
targetX: props.targetX,
|
targetX: props.targetX,
|
||||||
targetY: props.targetY
|
targetY: props.targetY
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
@@ -42,8 +42,8 @@ const CustomEdge = defineComponent({
|
|||||||
onClick={() => console.log(props.data)}
|
onClick={() => console.log(props.data)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default CustomEdge;
|
export default CustomEdge
|
||||||
|
|||||||
+15
-15
@@ -1,6 +1,6 @@
|
|||||||
import { ref } from 'vue';
|
import { ref } from 'vue'
|
||||||
import CustomEdge from './CustomEdge';
|
import CustomEdge from './CustomEdge'
|
||||||
import CustomEdge2 from './CustomEdge2';
|
import CustomEdge2 from './CustomEdge2'
|
||||||
import RevueFlow, {
|
import RevueFlow, {
|
||||||
Elements,
|
Elements,
|
||||||
FlowElement,
|
FlowElement,
|
||||||
@@ -13,7 +13,7 @@ import RevueFlow, {
|
|||||||
Connection,
|
Connection,
|
||||||
Edge,
|
Edge,
|
||||||
removeElements
|
removeElements
|
||||||
} from '../../src';
|
} from '../../src'
|
||||||
|
|
||||||
const initialElements = [
|
const initialElements = [
|
||||||
{ id: '1', type: 'input', data: { label: 'Input 1' }, position: { x: 250, y: 0 } },
|
{ id: '1', type: 'input', data: { label: 'Input 1' }, position: { x: 250, y: 0 } },
|
||||||
@@ -73,21 +73,21 @@ const initialElements = [
|
|||||||
type: 'custom2',
|
type: 'custom2',
|
||||||
data: { text: 'custom edge 2' }
|
data: { text: 'custom edge 2' }
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
|
||||||
const edgeTypes: Record<string, any> = {
|
const edgeTypes: Record<string, any> = {
|
||||||
custom: CustomEdge,
|
custom: CustomEdge,
|
||||||
custom2: CustomEdge2
|
custom2: CustomEdge2
|
||||||
};
|
}
|
||||||
|
|
||||||
const EdgesFlow = () => {
|
const EdgesFlow = () => {
|
||||||
const elements = ref<Elements>(initialElements as Elements);
|
const elements = ref<Elements>(initialElements as Elements)
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
|
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView()
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node)
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element)
|
||||||
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value));
|
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
||||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
|
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RevueFlow
|
<RevueFlow
|
||||||
@@ -104,7 +104,7 @@ const EdgesFlow = () => {
|
|||||||
<Controls />
|
<Controls />
|
||||||
<Background />
|
<Background />
|
||||||
</RevueFlow>
|
</RevueFlow>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default EdgesFlow;
|
export default EdgesFlow
|
||||||
|
|||||||
+16
-17
@@ -12,32 +12,31 @@ import RevueFlow, {
|
|||||||
BackgroundVariant,
|
BackgroundVariant,
|
||||||
Connection,
|
Connection,
|
||||||
Edge
|
Edge
|
||||||
} from '../../src';
|
} from '../../src'
|
||||||
import { CSSProperties, defineComponent, ref } from 'vue';
|
import { CSSProperties, defineComponent, ref } from 'vue'
|
||||||
|
|
||||||
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
|
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance)
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element)
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node)
|
||||||
|
|
||||||
const buttonStyle: CSSProperties = { position: 'absolute', left: '10px', top: '10px', zIndex: 4 };
|
const buttonStyle: CSSProperties = { position: 'absolute', left: '10px', top: '10px', zIndex: 4 }
|
||||||
|
|
||||||
const EmptyFlow = defineComponent({
|
const EmptyFlow = defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
{
|
{
|
||||||
const elements = ref<Elements>([]);
|
const elements = ref<Elements>([])
|
||||||
const onElementsRemove = (elementsToRemove: Elements) =>
|
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
||||||
(elements.value = removeElements(elementsToRemove, elements.value));
|
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
|
|
||||||
|
|
||||||
const addRandomNode = () => {
|
const addRandomNode = () => {
|
||||||
const nodeId: ElementId = (elements.value.length + 1).toString();
|
const nodeId: ElementId = (elements.value.length + 1).toString()
|
||||||
const newNode: Node = {
|
const newNode: Node = {
|
||||||
id: nodeId,
|
id: nodeId,
|
||||||
data: { label: `Node: ${nodeId}` },
|
data: { label: `Node: ${nodeId}` },
|
||||||
position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight }
|
position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight }
|
||||||
} as Node;
|
} as Node
|
||||||
elements.value = [...elements.value, newNode];
|
elements.value = [...elements.value, newNode]
|
||||||
};
|
}
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<RevueFlow
|
<RevueFlow
|
||||||
@@ -57,9 +56,9 @@ const EmptyFlow = defineComponent({
|
|||||||
add node
|
add node
|
||||||
</button>
|
</button>
|
||||||
</RevueFlow>
|
</RevueFlow>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default EmptyFlow;
|
export default EmptyFlow
|
||||||
|
|||||||
+10
-10
@@ -1,14 +1,14 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue'
|
||||||
import { routes } from './router';
|
import { routes } from './router'
|
||||||
import { useRoute, useRouter } from 'vue-router';
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
const Header = defineComponent({
|
const Header = defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
const onChange = async (event: any) => {
|
const onChange = async (event: any) => {
|
||||||
await router.push(event.target.value);
|
await router.push(event.target.value)
|
||||||
};
|
}
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<header>
|
<header>
|
||||||
@@ -25,8 +25,8 @@ const Header = defineComponent({
|
|||||||
)}
|
)}
|
||||||
</select>
|
</select>
|
||||||
</header>
|
</header>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default Header;
|
export default Header
|
||||||
|
|||||||
+17
-17
@@ -1,5 +1,5 @@
|
|||||||
import RevueFlow, { addEdge, MiniMap, Controls, Connection, Edge, Elements } from '../../src';
|
import RevueFlow, { addEdge, MiniMap, Controls, Connection, Edge, Elements } from '../../src'
|
||||||
import { defineComponent, ref, watchEffect } from 'vue';
|
import { defineComponent, ref, watchEffect } from 'vue'
|
||||||
|
|
||||||
const initialElements: Elements = [
|
const initialElements: Elements = [
|
||||||
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
||||||
@@ -9,26 +9,26 @@ const initialElements: Elements = [
|
|||||||
{ id: 'e1-2', source: '1', target: '2' },
|
{ id: 'e1-2', source: '1', target: '2' },
|
||||||
{ id: 'e1-3', source: '1', target: '3' },
|
{ id: 'e1-3', source: '1', target: '3' },
|
||||||
{ id: 'e3-4', source: '3', target: '4' }
|
{ id: 'e3-4', source: '3', target: '4' }
|
||||||
] as Elements;
|
] as Elements
|
||||||
|
|
||||||
const HiddenFlow = defineComponent({
|
const HiddenFlow = defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const elements = ref<Elements>(initialElements);
|
const elements = ref<Elements>(initialElements)
|
||||||
const isHidden = ref<boolean>(false);
|
const isHidden = ref<boolean>(false)
|
||||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
|
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
console.log(isHidden.value);
|
console.log(isHidden.value)
|
||||||
elements.value = elements.value.map((e) => {
|
elements.value = elements.value.map((e) => {
|
||||||
e.isHidden = isHidden.value;
|
e.isHidden = isHidden.value
|
||||||
return e;
|
return e
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
const toggle = (event) => {
|
const toggle = (event) => {
|
||||||
console.log(event.target);
|
console.log(event.target)
|
||||||
isHidden.value = event.target?.checked;
|
isHidden.value = event.target?.checked
|
||||||
};
|
}
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<RevueFlow v-model={elements.value} onConnect={onConnect}>
|
<RevueFlow v-model={elements.value} onConnect={onConnect}>
|
||||||
@@ -44,8 +44,8 @@ const HiddenFlow = defineComponent({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</RevueFlow>
|
</RevueFlow>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default HiddenFlow;
|
export default HiddenFlow
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import RevueFlow, {
|
|||||||
Edge,
|
Edge,
|
||||||
PanOnScrollMode,
|
PanOnScrollMode,
|
||||||
FlowTransform
|
FlowTransform
|
||||||
} from '../../src';
|
} from '../../src'
|
||||||
import { defineComponent, ref } from 'vue';
|
import { defineComponent, ref } from 'vue'
|
||||||
|
|
||||||
const initialElements: Elements = [
|
const initialElements: Elements = [
|
||||||
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } },
|
||||||
@@ -19,33 +19,33 @@ const initialElements: Elements = [
|
|||||||
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
|
{ id: '4', data: { label: 'Node 4' }, position: { x: 400, y: 200 } },
|
||||||
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
{ id: 'e1-2', source: '1', target: '2', animated: true },
|
||||||
{ id: 'e1-3', source: '1', target: '3' }
|
{ id: 'e1-3', source: '1', target: '3' }
|
||||||
] as Elements;
|
] as Elements
|
||||||
|
|
||||||
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
|
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node)
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node)
|
||||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element)
|
||||||
const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event);
|
const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event)
|
||||||
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event);
|
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event)
|
||||||
const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event);
|
const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event)
|
||||||
const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform);
|
const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform)
|
||||||
|
|
||||||
const InteractionFlow = defineComponent({
|
const InteractionFlow = defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const elements = ref<Elements>(initialElements);
|
const elements = ref<Elements>(initialElements)
|
||||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
|
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||||
|
|
||||||
const isSelectable = ref(false);
|
const isSelectable = ref(false)
|
||||||
const isDraggable = ref(false);
|
const isDraggable = ref(false)
|
||||||
const isConnectable = ref(false);
|
const isConnectable = ref(false)
|
||||||
const zoomOnScroll = ref(false);
|
const zoomOnScroll = ref(false)
|
||||||
const zoomOnPinch = ref(false);
|
const zoomOnPinch = ref(false)
|
||||||
const panOnScroll = ref(false);
|
const panOnScroll = ref(false)
|
||||||
const panOnScrollMode = ref<PanOnScrollMode>(PanOnScrollMode.Free);
|
const panOnScrollMode = ref<PanOnScrollMode>(PanOnScrollMode.Free)
|
||||||
const zoomOnDoubleClick = ref(false);
|
const zoomOnDoubleClick = ref(false)
|
||||||
const paneMoveable = ref(true);
|
const paneMoveable = ref(true)
|
||||||
const captureZoomClick = ref(false);
|
const captureZoomClick = ref(false)
|
||||||
const captureZoomScroll = ref(false);
|
const captureZoomScroll = ref(false)
|
||||||
const captureElementClick = ref(false);
|
const captureElementClick = ref(false)
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<RevueFlow
|
<RevueFlow
|
||||||
@@ -165,8 +165,8 @@ const InteractionFlow = defineComponent({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</RevueFlow>
|
</RevueFlow>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default InteractionFlow;
|
export default InteractionFlow
|
||||||
|
|||||||
+12
-12
@@ -1,14 +1,14 @@
|
|||||||
import { createApp } from 'vue';
|
import { createApp } from 'vue'
|
||||||
import './index.css';
|
import './index.css'
|
||||||
import App from './App';
|
import App from './App'
|
||||||
import { router } from './router';
|
import { router } from './router'
|
||||||
import { createPinia } from 'pinia';
|
import { createPinia } from 'pinia'
|
||||||
import { DraggablePlugin } from '@braks/revue-draggable';
|
import { DraggablePlugin } from '@braks/revue-draggable'
|
||||||
|
|
||||||
const app = createApp(App);
|
const app = createApp(App)
|
||||||
|
|
||||||
app.config.performance = true;
|
app.config.performance = true
|
||||||
app.use(router);
|
app.use(router)
|
||||||
app.use(DraggablePlugin);
|
app.use(DraggablePlugin)
|
||||||
app.use(createPinia());
|
app.use(createPinia())
|
||||||
app.mount('#root');
|
app.mount('#root')
|
||||||
|
|||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
import { createRouter, createWebHashHistory, RouterOptions } from 'vue-router';
|
import { createRouter, createWebHashHistory, RouterOptions } from 'vue-router'
|
||||||
|
|
||||||
export const routes: RouterOptions['routes'] = [
|
export const routes: RouterOptions['routes'] = [
|
||||||
{
|
{
|
||||||
@@ -45,9 +45,9 @@ export const routes: RouterOptions['routes'] = [
|
|||||||
path: '/interaction',
|
path: '/interaction',
|
||||||
component: () => import('./Interaction')
|
component: () => import('./Interaction')
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
|
||||||
export const router = createRouter({
|
export const router = createRouter({
|
||||||
history: createWebHashHistory(),
|
history: createWebHashHistory(),
|
||||||
routes
|
routes
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -76,6 +76,7 @@
|
|||||||
"rollup-plugin-typescript2": "^0.30.0",
|
"rollup-plugin-typescript2": "^0.30.0",
|
||||||
"tslib": "^2.3.0",
|
"tslib": "^2.3.0",
|
||||||
"typescript": "^4.3.5",
|
"typescript": "^4.3.5",
|
||||||
|
"unplugin-auto-import": "^0.4.12",
|
||||||
"vite": "^2.3.8",
|
"vite": "^2.3.8",
|
||||||
"vite-svg-loader": "^2.1.0",
|
"vite-svg-loader": "^2.1.0",
|
||||||
"vue": "^3.2.0-beta.8",
|
"vue": "^3.2.0-beta.8",
|
||||||
|
|||||||
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
plugins: [require('autoprefixer'), require('postcss-nested')]
|
plugins: [require('autoprefixer'), require('postcss-nested')]
|
||||||
};
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
require('esbuild-register');
|
require('esbuild-register')
|
||||||
module.exports = require('./rollup.ts');
|
module.exports = require('./rollup.ts')
|
||||||
|
|||||||
@@ -1,39 +1,38 @@
|
|||||||
import typescript from 'rollup-plugin-typescript2';
|
import typescript from 'rollup-plugin-typescript2'
|
||||||
import { terser } from 'rollup-plugin-terser';
|
import { terser } from 'rollup-plugin-terser'
|
||||||
import dts from 'rollup-plugin-dts';
|
import dts from 'rollup-plugin-dts'
|
||||||
import { OutputOptions, RollupOptions } from 'rollup';
|
import commonjs from '@rollup/plugin-commonjs'
|
||||||
import commonjs from '@rollup/plugin-commonjs';
|
import resolve from '@rollup/plugin-node-resolve'
|
||||||
import resolve from '@rollup/plugin-node-resolve';
|
import babel from '@rollup/plugin-babel'
|
||||||
import babel from '@rollup/plugin-babel';
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import svg from 'rollup-plugin-svg';
|
import svg from 'rollup-plugin-svg'
|
||||||
import postcss from 'rollup-plugin-postcss';
|
import postcss from 'rollup-plugin-postcss'
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import { DEFAULT_EXTENSIONS as DEFAULT_BABEL_EXTENSIONS } from '@babel/core';
|
import { DEFAULT_EXTENSIONS as DEFAULT_BABEL_EXTENSIONS } from '@babel/core'
|
||||||
|
|
||||||
const configs: RollupOptions[] = [];
|
const configs: any[] = []
|
||||||
|
|
||||||
const activePackages = [
|
const activePackages = [
|
||||||
{
|
{
|
||||||
display: 'Revue-Flow',
|
display: 'Revue-Flow',
|
||||||
external: ['@vueuse/core']
|
external: ['@vueuse/core']
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
for (const { external, iife } of activePackages) {
|
for (const { external, iife } of activePackages) {
|
||||||
const iifeGlobals = {
|
const iifeGlobals = {
|
||||||
vue: 'Vue',
|
vue: 'Vue',
|
||||||
'@vueuse/core': 'VueUse'
|
'@vueuse/core': 'VueUse'
|
||||||
};
|
}
|
||||||
|
|
||||||
const iifeName = 'RevueFlow';
|
const iifeName = 'RevueFlow'
|
||||||
const functionNames = ['revue-flow'];
|
const functionNames = ['revue-flow']
|
||||||
|
|
||||||
for (const fn of functionNames) {
|
for (const fn of functionNames) {
|
||||||
const input = 'src/index.ts';
|
const input = 'src/index.ts'
|
||||||
|
|
||||||
const output: OutputOptions[] = [
|
const output: any[] = [
|
||||||
{
|
{
|
||||||
file: `dist/${fn}.cjs.js`,
|
file: `dist/${fn}.cjs.js`,
|
||||||
format: 'cjs'
|
format: 'cjs'
|
||||||
@@ -42,7 +41,7 @@ for (const { external, iife } of activePackages) {
|
|||||||
file: `dist/${fn}.esm.js`,
|
file: `dist/${fn}.esm.js`,
|
||||||
format: 'es'
|
format: 'es'
|
||||||
}
|
}
|
||||||
];
|
]
|
||||||
|
|
||||||
if (iife !== false) {
|
if (iife !== false) {
|
||||||
output.push(
|
output.push(
|
||||||
@@ -67,7 +66,7 @@ for (const { external, iife } of activePackages) {
|
|||||||
})
|
})
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
configs.push({
|
configs.push({
|
||||||
@@ -95,7 +94,7 @@ for (const { external, iife } of activePackages) {
|
|||||||
})
|
})
|
||||||
],
|
],
|
||||||
external: ['vue', ...(external || [])]
|
external: ['vue', ...(external || [])]
|
||||||
});
|
})
|
||||||
|
|
||||||
configs.push({
|
configs.push({
|
||||||
input,
|
input,
|
||||||
@@ -105,8 +104,8 @@ for (const { external, iife } of activePackages) {
|
|||||||
},
|
},
|
||||||
plugins: [dts()],
|
plugins: [dts()],
|
||||||
external: ['vue', ...(external || []), /\.css$/]
|
external: ['vue', ...(external || []), /\.css$/]
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default configs;
|
export default configs
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { BackgroundVariant, RevueFlowStore } from '../../types';
|
import { BackgroundVariant, RevueFlowStore } from '../../types'
|
||||||
import { createGridDotsPath, createGridLinesPath } from './utils';
|
import { createGridDotsPath, createGridLinesPath } from './utils'
|
||||||
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue';
|
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue'
|
||||||
|
|
||||||
export interface BackgroundProps extends HTMLAttributes {
|
export interface BackgroundProps extends HTMLAttributes {
|
||||||
variant?: BackgroundVariant;
|
variant?: BackgroundVariant
|
||||||
gap?: number;
|
gap?: number
|
||||||
color?: string;
|
color?: string
|
||||||
size?: number;
|
size?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultColors = {
|
const defaultColors = {
|
||||||
[BackgroundVariant.Dots]: '#81818a',
|
[BackgroundVariant.Dots]: '#81818a',
|
||||||
[BackgroundVariant.Lines]: '#eee'
|
[BackgroundVariant.Lines]: '#eee'
|
||||||
};
|
}
|
||||||
|
|
||||||
const Background = defineComponent({
|
const Background = defineComponent({
|
||||||
name: 'Background',
|
name: 'Background',
|
||||||
@@ -39,23 +39,23 @@ const Background = defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const transform = computed(() => store.transform);
|
const transform = computed(() => store.transform)
|
||||||
// when there are multiple flows on a page we need to make sure that every background gets its own pattern.
|
// when there are multiple flows on a page we need to make sure that every background gets its own pattern.
|
||||||
const patternId = `pattern-${Math.floor(Math.random() * 100000)}`;
|
const patternId = `pattern-${Math.floor(Math.random() * 100000)}`
|
||||||
|
|
||||||
const bgClasses = ['revue-flow__background'];
|
const bgClasses = ['revue-flow__background']
|
||||||
const scaledGap = computed(() => props.gap && props.gap * transform.value[2]);
|
const scaledGap = computed(() => props.gap && props.gap * transform.value[2])
|
||||||
const xOffset = computed(() => scaledGap.value && transform.value[0] % scaledGap.value);
|
const xOffset = computed(() => scaledGap.value && transform.value[0] % scaledGap.value)
|
||||||
const yOffset = computed(() => scaledGap.value && transform.value[1] % scaledGap.value);
|
const yOffset = computed(() => scaledGap.value && transform.value[1] % scaledGap.value)
|
||||||
|
|
||||||
const isLines = computed(() => props.variant === BackgroundVariant.Lines);
|
const isLines = computed(() => props.variant === BackgroundVariant.Lines)
|
||||||
const bgColor = computed(() => (props.color ? props.color : defaultColors[props.variant || BackgroundVariant.Dots]));
|
const bgColor = computed(() => (props.color ? props.color : defaultColors[props.variant || BackgroundVariant.Dots]))
|
||||||
const path = computed(() =>
|
const path = computed(() =>
|
||||||
isLines.value
|
isLines.value
|
||||||
? scaledGap.value && props.size && createGridLinesPath(scaledGap.value, props.size, bgColor.value)
|
? scaledGap.value && props.size && createGridLinesPath(scaledGap.value, props.size, bgColor.value)
|
||||||
: createGridDotsPath(props.size || 0.4 * transform.value[2], bgColor.value)
|
: createGridDotsPath(props.size || 0.4 * transform.value[2], bgColor.value)
|
||||||
);
|
)
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<svg
|
<svg
|
||||||
@@ -77,8 +77,8 @@ const Background = defineComponent({
|
|||||||
</pattern>
|
</pattern>
|
||||||
<rect x="0" y="0" width="100%" height="100%" fill={`url(#${patternId})`} />
|
<rect x="0" y="0" width="100%" height="100%" fill={`url(#${patternId})`} />
|
||||||
</svg>
|
</svg>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default Background;
|
export default Background
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
export const createGridLinesPath = (size: number, strokeWidth: number, stroke: string) => {
|
export const createGridLinesPath = (size: number, strokeWidth: number, stroke: string) => {
|
||||||
return <path stroke={stroke} stroke-width={strokeWidth} d={`M${size / 2} 0 V${size} M0 ${size / 2} H${size}`} />;
|
return <path stroke={stroke} stroke-width={strokeWidth} d={`M${size / 2} 0 V${size} M0 ${size / 2} H${size}`} />
|
||||||
};
|
}
|
||||||
|
|
||||||
export const createGridDotsPath = (size: number, fill: string) => {
|
export const createGridDotsPath = (size: number, fill: string) => {
|
||||||
return <circle cx={size} cy={size} r={size} fill={fill} />;
|
return <circle cx={size} cy={size} r={size} fill={fill} />
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import { defineComponent, HTMLAttributes, inject, onMounted, PropType, ref } from 'vue';
|
import { defineComponent, HTMLAttributes, inject, onMounted, PropType, ref } from 'vue'
|
||||||
import useZoomPanHelper from '../../hooks/useZoomPanHelper';
|
import useZoomPanHelper from '../../hooks/useZoomPanHelper'
|
||||||
import { FitViewParams, RevueFlowStore, ZoomPanHelperFunctions } from '../../types';
|
import { FitViewParams, RevueFlowStore, ZoomPanHelperFunctions } from '../../types'
|
||||||
import PlusIcon from '../../../assets/icons/plus.svg';
|
import PlusIcon from '../../../assets/icons/plus.svg'
|
||||||
import MinusIcon from '../../../assets/icons/minus.svg';
|
import MinusIcon from '../../../assets/icons/minus.svg'
|
||||||
import Fitview from '../../../assets/icons/fitview.svg';
|
import Fitview from '../../../assets/icons/fitview.svg'
|
||||||
import Lock from '../../../assets/icons/lock.svg';
|
import Lock from '../../../assets/icons/lock.svg'
|
||||||
import Unlock from '../../../assets/icons/unlock.svg';
|
import Unlock from '../../../assets/icons/unlock.svg'
|
||||||
|
|
||||||
export interface ControlProps extends HTMLAttributes {
|
export interface ControlProps extends HTMLAttributes {
|
||||||
showZoom?: boolean;
|
showZoom?: boolean
|
||||||
showFitView?: boolean;
|
showFitView?: boolean
|
||||||
showInteractive?: boolean;
|
showInteractive?: boolean
|
||||||
fitViewParams?: FitViewParams;
|
fitViewParams?: FitViewParams
|
||||||
onZoomIn?: () => void;
|
onZoomIn?: () => void
|
||||||
onZoomOut?: () => void;
|
onZoomOut?: () => void
|
||||||
onFitView?: () => void;
|
onFitView?: () => void
|
||||||
onInteractiveChange?: (interactiveStatus: boolean) => void;
|
onInteractiveChange?: (interactiveStatus: boolean) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ControlButtonProps = HTMLButtonElement;
|
export type ControlButtonProps = HTMLButtonElement
|
||||||
|
|
||||||
export const ControlButton = defineComponent({
|
export const ControlButton = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -38,9 +38,9 @@ export const ControlButton = defineComponent({
|
|||||||
<button class={['revue-flow__controls-button']} {...props}>
|
<button class={['revue-flow__controls-button']} {...props}>
|
||||||
{slots.default ? slots.default() : ''}
|
{slots.default ? slots.default() : ''}
|
||||||
</button>
|
</button>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const Controls = defineComponent({
|
const Controls = defineComponent({
|
||||||
name: 'Controls',
|
name: 'Controls',
|
||||||
@@ -87,41 +87,41 @@ const Controls = defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const isVisible = ref<boolean>(false);
|
const isVisible = ref<boolean>(false)
|
||||||
const zoomHelper = ref<ZoomPanHelperFunctions>();
|
const zoomHelper = ref<ZoomPanHelperFunctions>()
|
||||||
const { onReady } = useZoomPanHelper();
|
const { onReady } = useZoomPanHelper()
|
||||||
|
|
||||||
onReady((helper) => {
|
onReady((helper) => {
|
||||||
zoomHelper.value = helper;
|
zoomHelper.value = helper
|
||||||
});
|
})
|
||||||
|
|
||||||
const isInteractive = store.nodesDraggable && store.nodesConnectable && store.elementsSelectable;
|
const isInteractive = store.nodesDraggable && store.nodesConnectable && store.elementsSelectable
|
||||||
const mapClasses = ['revue-flow__controls'];
|
const mapClasses = ['revue-flow__controls']
|
||||||
|
|
||||||
const onZoomInHandler = () => {
|
const onZoomInHandler = () => {
|
||||||
zoomHelper.value?.zoomIn?.();
|
zoomHelper.value?.zoomIn?.()
|
||||||
props.onZoomIn?.();
|
props.onZoomIn?.()
|
||||||
};
|
}
|
||||||
|
|
||||||
const onZoomOutHandler = () => {
|
const onZoomOutHandler = () => {
|
||||||
zoomHelper.value?.zoomOut?.();
|
zoomHelper.value?.zoomOut?.()
|
||||||
props.onZoomOut?.();
|
props.onZoomOut?.()
|
||||||
};
|
}
|
||||||
|
|
||||||
const onFitViewHandler = () => {
|
const onFitViewHandler = () => {
|
||||||
zoomHelper.value?.fitView?.(props.fitViewParams);
|
zoomHelper.value?.fitView?.(props.fitViewParams)
|
||||||
props.onFitView?.();
|
props.onFitView?.()
|
||||||
};
|
}
|
||||||
|
|
||||||
const onInteractiveChangeHandler = () => {
|
const onInteractiveChangeHandler = () => {
|
||||||
store.setInteractive?.(!isInteractive);
|
store.setInteractive?.(!isInteractive)
|
||||||
props.onInteractiveChange?.(!isInteractive);
|
props.onInteractiveChange?.(!isInteractive)
|
||||||
};
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
isVisible.value = true;
|
isVisible.value = true
|
||||||
});
|
})
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div class={mapClasses}>
|
<div class={mapClasses}>
|
||||||
@@ -147,8 +147,8 @@ const Controls = defineComponent({
|
|||||||
)}
|
)}
|
||||||
{slots.default ? slots.default() : ''}
|
{slots.default ? slots.default() : ''}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default Controls;
|
export default Controls
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { computed, defineComponent, PropType } from 'vue';
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
|
|
||||||
interface MiniMapNodeProps {
|
interface MiniMapNodeProps {
|
||||||
x: number;
|
x: number
|
||||||
y: number;
|
y: number
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
borderRadius: number;
|
borderRadius: number
|
||||||
color: string;
|
color: string
|
||||||
shapeRendering: string;
|
shapeRendering: string
|
||||||
strokeColor: string;
|
strokeColor: string
|
||||||
strokeWidth: number;
|
strokeWidth: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const MiniMapNode = defineComponent({
|
const MiniMapNode = defineComponent({
|
||||||
@@ -62,8 +62,8 @@ const MiniMapNode = defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props, { attrs }: { attrs: Record<string, any> }) {
|
setup(props, { attrs }: { attrs: Record<string, any> }) {
|
||||||
const styles = attrs.style || {};
|
const styles = attrs.style || {}
|
||||||
const fill = computed(() => (props.color || styles.value.background || styles.value.backgroundColor) as string);
|
const fill = computed(() => (props.color || styles.value.background || styles.value.backgroundColor) as string)
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<rect
|
<rect
|
||||||
@@ -79,8 +79,8 @@ const MiniMapNode = defineComponent({
|
|||||||
stroke-width={props.strokeWidth}
|
stroke-width={props.strokeWidth}
|
||||||
shape-rendering={props.shapeRendering}
|
shape-rendering={props.shapeRendering}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default MiniMapNode;
|
export default MiniMapNode
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import { getRectOfNodes, getBoundsofRects } from '../../utils/graph';
|
import { getRectOfNodes, getBoundsofRects } from '../../utils/graph'
|
||||||
import { Node, Rect, RevueFlowStore } from '../../types';
|
import { Node, Rect, RevueFlowStore } from '../../types'
|
||||||
import MiniMapNode from './MiniMapNode';
|
import MiniMapNode from './MiniMapNode'
|
||||||
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue';
|
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue'
|
||||||
|
|
||||||
type StringFunc = (node: Node) => string;
|
type StringFunc = (node: Node) => string
|
||||||
|
|
||||||
export interface MiniMapProps extends HTMLAttributes {
|
export interface MiniMapProps extends HTMLAttributes {
|
||||||
nodeColor?: string | StringFunc;
|
nodeColor?: string | StringFunc
|
||||||
nodeStrokeColor?: string | StringFunc;
|
nodeStrokeColor?: string | StringFunc
|
||||||
nodeClassName?: string | StringFunc;
|
nodeClassName?: string | StringFunc
|
||||||
nodeBorderRadius?: number;
|
nodeBorderRadius?: number
|
||||||
nodeStrokeWidth?: number;
|
nodeStrokeWidth?: number
|
||||||
maskColor?: string;
|
maskColor?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
declare const window: any;
|
declare const window: any
|
||||||
|
|
||||||
const defaultWidth = 200;
|
const defaultWidth = 200
|
||||||
const defaultHeight = 150;
|
const defaultHeight = 150
|
||||||
|
|
||||||
const MiniMap = defineComponent({
|
const MiniMap = defineComponent({
|
||||||
name: 'MiniMap',
|
name: 'MiniMap',
|
||||||
@@ -54,37 +54,39 @@ const MiniMap = defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props, { attrs }: { attrs: Record<string, any> }) {
|
setup(props, { attrs }: { attrs: Record<string, any> }) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const transform = computed(() => store.transform);
|
const transform = computed(() => store.transform)
|
||||||
const elementWidth = computed(() => (attrs.style?.width || defaultWidth)! as number);
|
const elementWidth = computed(() => (attrs.style?.width || defaultWidth)! as number)
|
||||||
const elementHeight = computed(() => (attrs.style?.height || defaultHeight)! as number);
|
const elementHeight = computed(() => (attrs.style?.height || defaultHeight)! as number)
|
||||||
const nodeColorFunc = computed(() => (props.nodeColor instanceof Function ? props.nodeColor : () => props.nodeColor) as StringFunc);
|
const nodeColorFunc = computed(
|
||||||
|
() => (props.nodeColor instanceof Function ? props.nodeColor : () => props.nodeColor) as StringFunc
|
||||||
|
)
|
||||||
const nodeStrokeColorFunc = computed(
|
const nodeStrokeColorFunc = computed(
|
||||||
() => (props.nodeStrokeColor instanceof Function ? props.nodeStrokeColor : () => props.nodeStrokeColor) as StringFunc
|
() => (props.nodeStrokeColor instanceof Function ? props.nodeStrokeColor : () => props.nodeStrokeColor) as StringFunc
|
||||||
);
|
)
|
||||||
const nodeClassNameFunc = computed(
|
const nodeClassNameFunc = computed(
|
||||||
() => (props.nodeClassName instanceof Function ? props.nodeClassName : () => props.nodeClassName) as StringFunc
|
() => (props.nodeClassName instanceof Function ? props.nodeClassName : () => props.nodeClassName) as StringFunc
|
||||||
);
|
)
|
||||||
const hasNodes = computed(() => store.nodes && store.nodes.length);
|
const hasNodes = computed(() => store.nodes && store.nodes.length)
|
||||||
const bb = computed(() => getRectOfNodes(store.nodes));
|
const bb = computed(() => getRectOfNodes(store.nodes))
|
||||||
const viewBB = computed<Rect>(() => ({
|
const viewBB = computed<Rect>(() => ({
|
||||||
x: -transform.value[0] / transform.value[2],
|
x: -transform.value[0] / transform.value[2],
|
||||||
y: -transform.value[1] / transform.value[2],
|
y: -transform.value[1] / transform.value[2],
|
||||||
width: store.width / transform.value[2],
|
width: store.width / transform.value[2],
|
||||||
height: store.height / transform.value[2]
|
height: store.height / transform.value[2]
|
||||||
}));
|
}))
|
||||||
const boundingRect = computed(() => (hasNodes.value ? getBoundsofRects(bb.value, viewBB.value) : viewBB.value));
|
const boundingRect = computed(() => (hasNodes.value ? getBoundsofRects(bb.value, viewBB.value) : viewBB.value))
|
||||||
const scaledWidth = computed(() => boundingRect.value.width / elementWidth.value);
|
const scaledWidth = computed(() => boundingRect.value.width / elementWidth.value)
|
||||||
const scaledHeight = computed(() => boundingRect.value.height / elementHeight.value);
|
const scaledHeight = computed(() => boundingRect.value.height / elementHeight.value)
|
||||||
const viewScale = computed(() => Math.max(scaledWidth.value, scaledHeight.value));
|
const viewScale = computed(() => Math.max(scaledWidth.value, scaledHeight.value))
|
||||||
const viewWidth = computed(() => viewScale.value * elementWidth.value);
|
const viewWidth = computed(() => viewScale.value * elementWidth.value)
|
||||||
const viewHeight = computed(() => viewScale.value * elementHeight.value);
|
const viewHeight = computed(() => viewScale.value * elementHeight.value)
|
||||||
const offset = computed(() => 5 * viewScale.value);
|
const offset = computed(() => 5 * viewScale.value)
|
||||||
const x = computed(() => boundingRect.value.x - (viewWidth.value - boundingRect.value.width) / 2 - offset.value);
|
const x = computed(() => boundingRect.value.x - (viewWidth.value - boundingRect.value.width) / 2 - offset.value)
|
||||||
const y = computed(() => boundingRect.value.y - (viewHeight.value - boundingRect.value.height) / 2 - offset.value);
|
const y = computed(() => boundingRect.value.y - (viewHeight.value - boundingRect.value.height) / 2 - offset.value)
|
||||||
const width = computed(() => viewWidth.value + offset.value * 2);
|
const width = computed(() => viewWidth.value + offset.value * 2)
|
||||||
const height = computed(() => viewHeight.value + offset.value * 2);
|
const height = computed(() => viewHeight.value + offset.value * 2)
|
||||||
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
|
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision'
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<svg
|
<svg
|
||||||
@@ -121,8 +123,8 @@ const MiniMap = defineComponent({
|
|||||||
fill-rule="evenodd"
|
fill-rule="evenodd"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default MiniMap;
|
export default MiniMap
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// These components are not used by revue Flow directly
|
// These components are not used by revue Flow directly
|
||||||
// but the user can add them as children of a revue Flow component
|
// but the user can add them as children of a revue Flow component
|
||||||
|
|
||||||
export { default as MiniMap } from './MiniMap';
|
export { default as MiniMap } from './MiniMap'
|
||||||
export { default as Controls, ControlButton } from './Controls';
|
export { default as Controls, ControlButton } from './Controls'
|
||||||
export { default as Background } from './Background';
|
export { default as Background } from './Background'
|
||||||
|
|||||||
Vendored
+156
@@ -0,0 +1,156 @@
|
|||||||
|
// Generated by 'unplugin-auto-import'
|
||||||
|
// We suggest you to commit this file into source control
|
||||||
|
declare global {
|
||||||
|
const asyncComputed: typeof import('@vueuse/core')['asyncComputed']
|
||||||
|
const autoResetRef: typeof import('@vueuse/core')['autoResetRef']
|
||||||
|
const biSyncRef: typeof import('@vueuse/core')['biSyncRef']
|
||||||
|
const computed: typeof import('vue')['computed']
|
||||||
|
const computedInject: typeof import('@vueuse/core')['computedInject']
|
||||||
|
const controlledComputed: typeof import('@vueuse/core')['controlledComputed']
|
||||||
|
const controlledRef: typeof import('@vueuse/core')['controlledRef']
|
||||||
|
const createApp: typeof import('vue')['createApp']
|
||||||
|
const createEventHook: typeof import('@vueuse/core')['createEventHook']
|
||||||
|
const createGlobalState: typeof import('@vueuse/core')['createGlobalState']
|
||||||
|
const createSharedComposable: typeof import('@vueuse/core')['createSharedComposable']
|
||||||
|
const customRef: typeof import('vue')['customRef']
|
||||||
|
const debouncedWatch: typeof import('@vueuse/core')['debouncedWatch']
|
||||||
|
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
|
||||||
|
const defineComponent: typeof import('vue')['defineComponent']
|
||||||
|
const extendRef: typeof import('@vueuse/core')['extendRef']
|
||||||
|
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
|
||||||
|
const h: typeof import('vue')['h']
|
||||||
|
const ignorableWatch: typeof import('@vueuse/core')['ignorableWatch']
|
||||||
|
const inject: typeof import('vue')['inject']
|
||||||
|
const isReadonly: typeof import('vue')['isReadonly']
|
||||||
|
const isRef: typeof import('vue')['isRef']
|
||||||
|
const makeDestructurable: typeof import('@vueuse/core')['makeDestructurable']
|
||||||
|
const markRaw: typeof import('vue')['markRaw']
|
||||||
|
const nextTick: typeof import('vue')['nextTick']
|
||||||
|
const onActivated: typeof import('vue')['onActivated']
|
||||||
|
const onBeforeMount: typeof import('vue')['onBeforeMount']
|
||||||
|
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
|
||||||
|
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
|
||||||
|
const onClickOutside: typeof import('@vueuse/core')['onClickOutside']
|
||||||
|
const onDeactivated: typeof import('vue')['onDeactivated']
|
||||||
|
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
|
||||||
|
const onKeyStroke: typeof import('@vueuse/core')['onKeyStroke']
|
||||||
|
const onMounted: typeof import('vue')['onMounted']
|
||||||
|
const onRenderTracked: typeof import('vue')['onRenderTracked']
|
||||||
|
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
|
||||||
|
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
|
||||||
|
const onStartTyping: typeof import('@vueuse/core')['onStartTyping']
|
||||||
|
const onUnmounted: typeof import('vue')['onUnmounted']
|
||||||
|
const onUpdated: typeof import('vue')['onUpdated']
|
||||||
|
const pausableWatch: typeof import('@vueuse/core')['pausableWatch']
|
||||||
|
const provide: typeof import('vue')['provide']
|
||||||
|
const reactify: typeof import('@vueuse/core')['reactify']
|
||||||
|
const reactifyObject: typeof import('@vueuse/core')['reactifyObject']
|
||||||
|
const reactive: typeof import('vue')['reactive']
|
||||||
|
const reactivePick: typeof import('@vueuse/core')['reactivePick']
|
||||||
|
const readonly: typeof import('vue')['readonly']
|
||||||
|
const ref: typeof import('vue')['ref']
|
||||||
|
const shallowReactive: typeof import('vue')['shallowReactive']
|
||||||
|
const shallowReadonly: typeof import('vue')['shallowReadonly']
|
||||||
|
const shallowRef: typeof import('vue')['shallowRef']
|
||||||
|
const syncRef: typeof import('@vueuse/core')['syncRef']
|
||||||
|
const templateRef: typeof import('@vueuse/core')['templateRef']
|
||||||
|
const throttledWatch: typeof import('@vueuse/core')['throttledWatch']
|
||||||
|
const toRaw: typeof import('vue')['toRaw']
|
||||||
|
const toReactive: typeof import('@vueuse/core')['toReactive']
|
||||||
|
const toRef: typeof import('vue')['toRef']
|
||||||
|
const toRefs: typeof import('vue')['toRefs']
|
||||||
|
const triggerRef: typeof import('vue')['triggerRef']
|
||||||
|
const tryOnMounted: typeof import('@vueuse/core')['tryOnMounted']
|
||||||
|
const tryOnScopeDispose: typeof import('@vueuse/core')['tryOnScopeDispose']
|
||||||
|
const tryOnUnmounted: typeof import('@vueuse/core')['tryOnUnmounted']
|
||||||
|
const unref: typeof import('vue')['unref']
|
||||||
|
const unrefElement: typeof import('@vueuse/core')['unrefElement']
|
||||||
|
const until: typeof import('@vueuse/core')['until']
|
||||||
|
const useActiveElement: typeof import('@vueuse/core')['useActiveElement']
|
||||||
|
const useAsyncState: typeof import('@vueuse/core')['useAsyncState']
|
||||||
|
const useAttrs: typeof import('vue')['useAttrs']
|
||||||
|
const useBattery: typeof import('@vueuse/core')['useBattery']
|
||||||
|
const useBreakpoints: typeof import('@vueuse/core')['useBreakpoints']
|
||||||
|
const useBrowserLocation: typeof import('@vueuse/core')['useBrowserLocation']
|
||||||
|
const useClipboard: typeof import('@vueuse/core')['useClipboard']
|
||||||
|
const useCounter: typeof import('@vueuse/core')['useCounter']
|
||||||
|
const useCssModule: typeof import('vue')['useCssModule']
|
||||||
|
const useCssVar: typeof import('@vueuse/core')['useCssVar']
|
||||||
|
const useDark: typeof import('@vueuse/core')['useDark']
|
||||||
|
const useDebounce: typeof import('@vueuse/core')['useDebounce']
|
||||||
|
const useDebounceFn: typeof import('@vueuse/core')['useDebounceFn']
|
||||||
|
const useDeviceMotion: typeof import('@vueuse/core')['useDeviceMotion']
|
||||||
|
const useDeviceOrientation: typeof import('@vueuse/core')['useDeviceOrientation']
|
||||||
|
const useDevicePixelRatio: typeof import('@vueuse/core')['useDevicePixelRatio']
|
||||||
|
const useDevicesList: typeof import('@vueuse/core')['useDevicesList']
|
||||||
|
const useDocumentVisibility: typeof import('@vueuse/core')['useDocumentVisibility']
|
||||||
|
const useElementBounding: typeof import('@vueuse/core')['useElementBounding']
|
||||||
|
const useElementSize: typeof import('@vueuse/core')['useElementSize']
|
||||||
|
const useElementVisibility: typeof import('@vueuse/core')['useElementVisibility']
|
||||||
|
const useEventBus: typeof import('@vueuse/core')['useEventBus']
|
||||||
|
const useEventListener: typeof import('@vueuse/core')['useEventListener']
|
||||||
|
const useEventSource: typeof import('@vueuse/core')['useEventSource']
|
||||||
|
const useFavicon: typeof import('@vueuse/core')['useFavicon']
|
||||||
|
const useFetch: typeof import('@vueuse/core')['useFetch']
|
||||||
|
const useFullscreen: typeof import('@vueuse/core')['useFullscreen']
|
||||||
|
const useGeolocation: typeof import('@vueuse/core')['useGeolocation']
|
||||||
|
const useIdle: typeof import('@vueuse/core')['useIdle']
|
||||||
|
const useIntersectionObserver: typeof import('@vueuse/core')['useIntersectionObserver']
|
||||||
|
const useInterval: typeof import('@vueuse/core')['useInterval']
|
||||||
|
const useIntervalFn: typeof import('@vueuse/core')['useIntervalFn']
|
||||||
|
const useLastChanged: typeof import('@vueuse/core')['useLastChanged']
|
||||||
|
const useLocalStorage: typeof import('@vueuse/core')['useLocalStorage']
|
||||||
|
const useMagicKeys: typeof import('@vueuse/core')['useMagicKeys']
|
||||||
|
const useManualRefHistory: typeof import('@vueuse/core')['useManualRefHistory']
|
||||||
|
const useMediaControls: typeof import('@vueuse/core')['useMediaControls']
|
||||||
|
const useMediaQuery: typeof import('@vueuse/core')['useMediaQuery']
|
||||||
|
const useMouse: typeof import('@vueuse/core')['useMouse']
|
||||||
|
const useMouseInElement: typeof import('@vueuse/core')['useMouseInElement']
|
||||||
|
const useMousePressed: typeof import('@vueuse/core')['useMousePressed']
|
||||||
|
const useMutationObserver: typeof import('@vueuse/core')['useMutationObserver']
|
||||||
|
const useNetwork: typeof import('@vueuse/core')['useNetwork']
|
||||||
|
const useNow: typeof import('@vueuse/core')['useNow']
|
||||||
|
const useOnline: typeof import('@vueuse/core')['useOnline']
|
||||||
|
const usePageLeave: typeof import('@vueuse/core')['usePageLeave']
|
||||||
|
const useParallax: typeof import('@vueuse/core')['useParallax']
|
||||||
|
const usePermission: typeof import('@vueuse/core')['usePermission']
|
||||||
|
const usePointerSwipe: typeof import('@vueuse/core')['usePointerSwipe']
|
||||||
|
const usePreferredColorScheme: typeof import('@vueuse/core')['usePreferredColorScheme']
|
||||||
|
const usePreferredDark: typeof import('@vueuse/core')['usePreferredDark']
|
||||||
|
const usePreferredLanguages: typeof import('@vueuse/core')['usePreferredLanguages']
|
||||||
|
const useRafFn: typeof import('@vueuse/core')['useRafFn']
|
||||||
|
const useRefHistory: typeof import('@vueuse/core')['useRefHistory']
|
||||||
|
const useResizeObserver: typeof import('@vueuse/core')['useResizeObserver']
|
||||||
|
const useScriptTag: typeof import('@vueuse/core')['useScriptTag']
|
||||||
|
const useSessionStorage: typeof import('@vueuse/core')['useSessionStorage']
|
||||||
|
const useShare: typeof import('@vueuse/core')['useShare']
|
||||||
|
const useSlots: typeof import('vue')['useSlots']
|
||||||
|
const useSpeechRecognition: typeof import('@vueuse/core')['useSpeechRecognition']
|
||||||
|
const useStorage: typeof import('@vueuse/core')['useStorage']
|
||||||
|
const useSwipe: typeof import('@vueuse/core')['useSwipe']
|
||||||
|
const useTemplateRefsList: typeof import('@vueuse/core')['useTemplateRefsList']
|
||||||
|
const useThrottle: typeof import('@vueuse/core')['useThrottle']
|
||||||
|
const useThrottleFn: typeof import('@vueuse/core')['useThrottleFn']
|
||||||
|
const useTimeAgo: typeof import('@vueuse/core')['useTimeAgo']
|
||||||
|
const useTimeout: typeof import('@vueuse/core')['useTimeout']
|
||||||
|
const useTimeoutFn: typeof import('@vueuse/core')['useTimeoutFn']
|
||||||
|
const useTimestamp: typeof import('@vueuse/core')['useTimestamp']
|
||||||
|
const useTitle: typeof import('@vueuse/core')['useTitle']
|
||||||
|
const useToggle: typeof import('@vueuse/core')['useToggle']
|
||||||
|
const useTransition: typeof import('@vueuse/core')['useTransition']
|
||||||
|
const useUrlSearchParams: typeof import('@vueuse/core')['useUrlSearchParams']
|
||||||
|
const useUserMedia: typeof import('@vueuse/core')['useUserMedia']
|
||||||
|
const useVModel: typeof import('@vueuse/core')['useVModel']
|
||||||
|
const useVModels: typeof import('@vueuse/core')['useVModels']
|
||||||
|
const useWebSocket: typeof import('@vueuse/core')['useWebSocket']
|
||||||
|
const useWebWorker: typeof import('@vueuse/core')['useWebWorker']
|
||||||
|
const useWebWorkerFn: typeof import('@vueuse/core')['useWebWorkerFn']
|
||||||
|
const useWindowFocus: typeof import('@vueuse/core')['useWindowFocus']
|
||||||
|
const useWindowScroll: typeof import('@vueuse/core')['useWindowScroll']
|
||||||
|
const useWindowSize: typeof import('@vueuse/core')['useWindowSize']
|
||||||
|
const watch: typeof import('vue')['watch']
|
||||||
|
const watchEffect: typeof import('vue')['watchEffect']
|
||||||
|
const watchWithFilter: typeof import('@vueuse/core')['watchWithFilter']
|
||||||
|
const whenever: typeof import('@vueuse/core')['whenever']
|
||||||
|
}
|
||||||
|
export {}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
import { ref, defineComponent, CSSProperties, PropType, computed, inject, h } from 'vue';
|
import { ref, defineComponent, CSSProperties, PropType, computed, inject, h } from 'vue'
|
||||||
|
|
||||||
import { getBezierPath } from '../Edges/BezierEdge';
|
import { getBezierPath } from '../Edges/BezierEdge'
|
||||||
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
import { getSmoothStepPath } from '../Edges/SmoothStepEdge'
|
||||||
import { Node, HandleElement, Position, ConnectionLineType, ConnectionLineComponent, RevueFlowStore } from '../../types';
|
import { Node, HandleElement, Position, ConnectionLineType, ConnectionLineComponent, RevueFlowStore } from '../../types'
|
||||||
|
|
||||||
interface ConnectionLineProps {
|
interface ConnectionLineProps {
|
||||||
connectionLineType: ConnectionLineType;
|
connectionLineType: ConnectionLineType
|
||||||
connectionLineStyle?: CSSProperties;
|
connectionLineStyle?: CSSProperties
|
||||||
customConnectionLine?: ConnectionLineComponent;
|
customConnectionLine?: ConnectionLineComponent
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -29,35 +29,35 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const sourceNode = ref<Node | null>(store.nodes.find((n) => n.id === store.connectionNodeId) || null);
|
const sourceNode = ref<Node | null>(store.nodes.find((n) => n.id === store.connectionNodeId) || null)
|
||||||
const nodesConnectable = computed(() => store.nodesConnectable);
|
const nodesConnectable = computed(() => store.nodesConnectable)
|
||||||
|
|
||||||
const sourceHandle = computed(() =>
|
const sourceHandle = computed(() =>
|
||||||
store.connectionHandleId && store.connectionHandleType
|
store.connectionHandleId && store.connectionHandleType
|
||||||
? sourceNode.value?.__rf.handleBounds[store.connectionHandleType].find(
|
? sourceNode.value?.__rf.handleBounds[store.connectionHandleType].find(
|
||||||
(d: HandleElement) => d.id === store.connectionHandleId
|
(d: HandleElement) => d.id === store.connectionHandleId
|
||||||
)
|
)
|
||||||
: store.connectionHandleType && sourceNode.value?.__rf.handleBounds[store.connectionHandleType][0]
|
: store.connectionHandleType && sourceNode.value?.__rf.handleBounds[store.connectionHandleType][0]
|
||||||
);
|
)
|
||||||
const sourceHandleX = computed(() =>
|
const sourceHandleX = computed(() =>
|
||||||
sourceHandle.value ? sourceHandle.value.x + sourceHandle.value.width / 2 : (sourceNode.value?.__rf.width as number) / 2
|
sourceHandle.value ? sourceHandle.value.x + sourceHandle.value.width / 2 : (sourceNode.value?.__rf.width as number) / 2
|
||||||
);
|
)
|
||||||
const sourceHandleY = computed(() =>
|
const sourceHandleY = computed(() =>
|
||||||
sourceHandle.value ? sourceHandle.value.y + sourceHandle.value.height / 2 : sourceNode.value?.__rf.height
|
sourceHandle.value ? sourceHandle.value.y + sourceHandle.value.height / 2 : sourceNode.value?.__rf.height
|
||||||
);
|
)
|
||||||
const sourceX = computed(() => sourceNode.value?.__rf.position.x + sourceHandleX.value);
|
const sourceX = computed(() => sourceNode.value?.__rf.position.x + sourceHandleX.value)
|
||||||
const sourceY = computed(() => sourceNode.value?.__rf.position.y + sourceHandleY.value);
|
const sourceY = computed(() => sourceNode.value?.__rf.position.y + sourceHandleY.value)
|
||||||
|
|
||||||
const targetX = computed(() => (store.connectionPosition.x - store.transform[0]) / store.transform[2]);
|
const targetX = computed(() => (store.connectionPosition.x - store.transform[0]) / store.transform[2])
|
||||||
const targetY = computed(() => (store.connectionPosition.y - store.transform[1]) / store.transform[2]);
|
const targetY = computed(() => (store.connectionPosition.y - store.transform[1]) / store.transform[2])
|
||||||
|
|
||||||
const isRightOrLeft = computed(
|
const isRightOrLeft = computed(
|
||||||
() => sourceHandle.value?.position === Position.Left || sourceHandle.value?.position === Position.Right
|
() => sourceHandle.value?.position === Position.Left || sourceHandle.value?.position === Position.Right
|
||||||
);
|
)
|
||||||
const targetPosition = computed(() => (isRightOrLeft.value ? Position.Left : Position.Top));
|
const targetPosition = computed(() => (isRightOrLeft.value ? Position.Left : Position.Top))
|
||||||
|
|
||||||
let dAttr = computed(() => `M${sourceX.value},${sourceY.value} ${targetX.value},${targetY.value}`);
|
let dAttr = computed(() => `M${sourceX.value},${sourceY.value} ${targetX.value},${targetY.value}`)
|
||||||
|
|
||||||
if (props.connectionLineType === ConnectionLineType.Bezier) {
|
if (props.connectionLineType === ConnectionLineType.Bezier) {
|
||||||
dAttr = computed(() =>
|
dAttr = computed(() =>
|
||||||
@@ -69,7 +69,7 @@ export default defineComponent({
|
|||||||
targetY: targetY.value,
|
targetY: targetY.value,
|
||||||
targetPosition: targetPosition.value
|
targetPosition: targetPosition.value
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
} else if (props.connectionLineType === ConnectionLineType.Step) {
|
} else if (props.connectionLineType === ConnectionLineType.Step) {
|
||||||
dAttr = computed(() =>
|
dAttr = computed(() =>
|
||||||
getSmoothStepPath({
|
getSmoothStepPath({
|
||||||
@@ -81,7 +81,7 @@ export default defineComponent({
|
|||||||
targetPosition: targetPosition.value,
|
targetPosition: targetPosition.value,
|
||||||
borderRadius: 0
|
borderRadius: 0
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
} else if (props.connectionLineType === ConnectionLineType.SmoothStep) {
|
} else if (props.connectionLineType === ConnectionLineType.SmoothStep) {
|
||||||
dAttr = computed(() =>
|
dAttr = computed(() =>
|
||||||
getSmoothStepPath({
|
getSmoothStepPath({
|
||||||
@@ -92,7 +92,7 @@ export default defineComponent({
|
|||||||
targetY: targetY.value,
|
targetY: targetY.value,
|
||||||
targetPosition: targetPosition.value
|
targetPosition: targetPosition.value
|
||||||
})
|
})
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.customConnectionLine) {
|
if (props.customConnectionLine) {
|
||||||
@@ -110,7 +110,7 @@ export default defineComponent({
|
|||||||
connectionLineStyle: props.connectionLineStyle
|
connectionLineStyle: props.connectionLineStyle
|
||||||
})}
|
})}
|
||||||
</g>
|
</g>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return () =>
|
return () =>
|
||||||
@@ -120,6 +120,6 @@ export default defineComponent({
|
|||||||
</g>
|
</g>
|
||||||
) : (
|
) : (
|
||||||
''
|
''
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue'
|
||||||
import EdgeText from './EdgeText';
|
import EdgeText from './EdgeText'
|
||||||
import { getMarkerEnd, getCenter, GetCenterParams, DefaultEdgeProps } from './utils';
|
import { getMarkerEnd, getCenter, GetCenterParams, DefaultEdgeProps } from './utils'
|
||||||
import { ArrowHeadType, EdgeType, Position } from '../../types';
|
import { ArrowHeadType, EdgeType, Position } from '../../types'
|
||||||
import { reactify } from '@vueuse/core';
|
import { reactify } from '@vueuse/core'
|
||||||
|
|
||||||
interface GetBezierPathParams {
|
interface GetBezierPathParams {
|
||||||
sourceX: number;
|
sourceX: number
|
||||||
sourceY: number;
|
sourceY: number
|
||||||
sourcePosition?: Position;
|
sourcePosition?: Position
|
||||||
targetX: number;
|
targetX: number
|
||||||
targetY: number;
|
targetY: number
|
||||||
targetPosition?: Position;
|
targetPosition?: Position
|
||||||
centerX?: number;
|
centerX?: number
|
||||||
centerY?: number;
|
centerY?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getBezierPath({
|
export function getBezierPath({
|
||||||
@@ -25,23 +25,23 @@ export function getBezierPath({
|
|||||||
centerX,
|
centerX,
|
||||||
centerY
|
centerY
|
||||||
}: GetBezierPathParams): string {
|
}: GetBezierPathParams): string {
|
||||||
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY });
|
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY })
|
||||||
const leftAndRight = [Position.Left, Position.Right];
|
const leftAndRight = [Position.Left, Position.Right]
|
||||||
|
|
||||||
const cX = typeof centerX !== 'undefined' ? centerX : _centerX;
|
const cX = typeof centerX !== 'undefined' ? centerX : _centerX
|
||||||
const cY = typeof centerY !== 'undefined' ? centerY : _centerY;
|
const cY = typeof centerY !== 'undefined' ? centerY : _centerY
|
||||||
|
|
||||||
let path = `M${sourceX},${sourceY} C${sourceX},${cY} ${targetX},${cY} ${targetX},${targetY}`;
|
let path = `M${sourceX},${sourceY} C${sourceX},${cY} ${targetX},${cY} ${targetX},${targetY}`
|
||||||
|
|
||||||
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
||||||
path = `M${sourceX},${sourceY} C${cX},${sourceY} ${cX},${targetY} ${targetX},${targetY}`;
|
path = `M${sourceX},${sourceY} C${cX},${sourceY} ${cX},${targetY} ${targetX},${targetY}`
|
||||||
} else if (leftAndRight.includes(targetPosition)) {
|
} else if (leftAndRight.includes(targetPosition)) {
|
||||||
path = `M${sourceX},${sourceY} C${sourceX},${targetY} ${sourceX},${targetY} ${targetX},${targetY}`;
|
path = `M${sourceX},${sourceY} C${sourceX},${targetY} ${sourceX},${targetY} ${targetX},${targetY}`
|
||||||
} else if (leftAndRight.includes(sourcePosition)) {
|
} else if (leftAndRight.includes(sourcePosition)) {
|
||||||
path = `M${sourceX},${sourceY} C${targetX},${sourceY} ${targetX},${sourceY} ${targetX},${targetY}`;
|
path = `M${sourceX},${sourceY} C${targetX},${sourceY} ${targetX},${sourceY} ${targetX},${targetY}`
|
||||||
}
|
}
|
||||||
|
|
||||||
return path;
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -58,8 +58,8 @@ export default defineComponent({
|
|||||||
targetY: targetY,
|
targetY: targetY,
|
||||||
sourcePosition: sourcePosition,
|
sourcePosition: sourcePosition,
|
||||||
targetPosition: targetPosition
|
targetPosition: targetPosition
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
const path = reactify(({ sourceX, sourceY, targetX, targetY, targetPosition, sourcePosition }: GetBezierPathParams) => {
|
const path = reactify(({ sourceX, sourceY, targetX, targetY, targetPosition, sourcePosition }: GetBezierPathParams) => {
|
||||||
return getBezierPath({
|
return getBezierPath({
|
||||||
sourceX: sourceX,
|
sourceX: sourceX,
|
||||||
@@ -68,10 +68,10 @@ export default defineComponent({
|
|||||||
targetY: targetY,
|
targetY: targetY,
|
||||||
targetPosition: targetPosition,
|
targetPosition: targetPosition,
|
||||||
sourcePosition: sourcePosition
|
sourcePosition: sourcePosition
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId));
|
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId))
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
@@ -94,6 +94,6 @@ export default defineComponent({
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}) as EdgeType;
|
}) as EdgeType
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import { computed, defineComponent, h, inject, PropType, ref, watchPostEffect } from 'vue';
|
import { computed, defineComponent, h, inject, PropType, ref, watchPostEffect } from 'vue'
|
||||||
import EdgeAnchor from './EdgeAnchor';
|
import EdgeAnchor from './EdgeAnchor'
|
||||||
import { ConnectionMode, Edge, Elements, Node, Position, RevueFlowStore, Transform } from '../../types';
|
import { ConnectionMode, Edge, Elements, Node, Position, RevueFlowStore, Transform } from '../../types'
|
||||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||||
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../container/EdgeRenderer/utils';
|
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../container/EdgeRenderer/utils'
|
||||||
import { isEdge } from '../../utils/graph';
|
import { isEdge } from '../../utils/graph'
|
||||||
import { onMouseDown } from '../Handle/handler';
|
import { onMouseDown } from '../Handle/handler'
|
||||||
import { controlledRef } from '@vueuse/core';
|
import { controlledRef } from '@vueuse/core'
|
||||||
|
|
||||||
interface EdgeProps {
|
interface EdgeProps {
|
||||||
edge: Edge;
|
edge: Edge
|
||||||
nodes: Node[];
|
nodes: Node[]
|
||||||
selectedElements: Elements | null;
|
selectedElements: Elements | null
|
||||||
elementsSelectable: boolean;
|
elementsSelectable: boolean
|
||||||
transform: Transform;
|
transform: Transform
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
onlyRenderVisibleElements: boolean;
|
onlyRenderVisibleElements: boolean
|
||||||
connectionMode?: ConnectionMode;
|
connectionMode?: ConnectionMode
|
||||||
markerEndId?: string;
|
markerEndId?: string
|
||||||
edgeUpdaterRadius?: number;
|
edgeUpdaterRadius?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -54,41 +54,41 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||||
const nodes = controlledRef(getSourceTargetNodes(props.edge, store.nodes), {
|
const nodes = controlledRef(getSourceTargetNodes(props.edge, store.nodes), {
|
||||||
onBeforeChange: (value, oldValue) => {
|
onBeforeChange: (value, oldValue) => {
|
||||||
if (JSON.stringify(value) === JSON.stringify(oldValue)) return false;
|
if (JSON.stringify(value) === JSON.stringify(oldValue)) return false
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
watchPostEffect(() => {
|
watchPostEffect(() => {
|
||||||
nodes.value = getSourceTargetNodes(props.edge, store.nodes);
|
nodes.value = getSourceTargetNodes(props.edge, store.nodes)
|
||||||
});
|
})
|
||||||
|
|
||||||
hooks.connect.on((connection) => {
|
hooks.connect.on((connection) => {
|
||||||
hooks.edgeUpdate.trigger({ edge: props.edge, connection });
|
hooks.edgeUpdate.trigger({ edge: props.edge, connection })
|
||||||
});
|
})
|
||||||
|
|
||||||
if (!nodes.value.sourceNode) {
|
if (!nodes.value.sourceNode) {
|
||||||
console.warn(`couldn't create edge for source id: ${props.edge.source}; edge id: ${props.edge.id}`);
|
console.warn(`couldn't create edge for source id: ${props.edge.source}; edge id: ${props.edge.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nodes.value.targetNode) {
|
if (!nodes.value.targetNode) {
|
||||||
console.warn(`couldn't create edge for target id: ${props.edge.target}; edge id: ${props.edge.id}`);
|
console.warn(`couldn't create edge for target id: ${props.edge.target}; edge id: ${props.edge.id}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
// when connection type is loose we can define all handles as sources
|
// when connection type is loose we can define all handles as sources
|
||||||
const targetNodeHandles =
|
const targetNodeHandles =
|
||||||
props.connectionMode === ConnectionMode.Strict
|
props.connectionMode === ConnectionMode.Strict
|
||||||
? nodes.value.targetNode?.__rf.handleBounds.target
|
? nodes.value.targetNode?.__rf.handleBounds.target
|
||||||
: nodes.value.targetNode?.__rf.handleBounds.target || nodes.value.targetNode?.__rf.handleBounds.source;
|
: nodes.value.targetNode?.__rf.handleBounds.target || nodes.value.targetNode?.__rf.handleBounds.source
|
||||||
|
|
||||||
const sourceHandle =
|
const sourceHandle =
|
||||||
nodes.value.sourceNode && getHandle(nodes.value.sourceNode.__rf.handleBounds.source, props.edge.sourceHandle || null);
|
nodes.value.sourceNode && getHandle(nodes.value.sourceNode.__rf.handleBounds.source, props.edge.sourceHandle || null)
|
||||||
const targetHandle = getHandle(targetNodeHandles, props.edge.targetHandle || null);
|
const targetHandle = getHandle(targetNodeHandles, props.edge.targetHandle || null)
|
||||||
const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom;
|
const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom
|
||||||
const targetPosition = targetHandle ? targetHandle.position : Position.Top;
|
const targetPosition = targetHandle ? targetHandle.position : Position.Top
|
||||||
|
|
||||||
const isVisible = ({ sourceX, sourceY, targetX, targetY }: Record<string, number>) => {
|
const isVisible = ({ sourceX, sourceY, targetX, targetY }: Record<string, number>) => {
|
||||||
return props.onlyRenderVisibleElements
|
return props.onlyRenderVisibleElements
|
||||||
@@ -99,10 +99,10 @@ export default defineComponent({
|
|||||||
height: store.height || 0,
|
height: store.height || 0,
|
||||||
transform: store.transform
|
transform: store.transform
|
||||||
})
|
})
|
||||||
: true;
|
: true
|
||||||
};
|
}
|
||||||
|
|
||||||
const isSelected = computed(() => store.selectedElements?.some((elm) => isEdge(elm) && elm.id === props.edge.id) || false);
|
const isSelected = computed(() => store.selectedElements?.some((elm) => isEdge(elm) && elm.id === props.edge.id) || false)
|
||||||
|
|
||||||
const edgeElement = () => {
|
const edgeElement = () => {
|
||||||
const el: Edge = {
|
const el: Edge = {
|
||||||
@@ -110,51 +110,51 @@ export default defineComponent({
|
|||||||
source: props.edge.source,
|
source: props.edge.source,
|
||||||
target: props.edge.target,
|
target: props.edge.target,
|
||||||
type: props.edge.type
|
type: props.edge.type
|
||||||
};
|
}
|
||||||
|
|
||||||
if (props.edge.sourceHandle) {
|
if (props.edge.sourceHandle) {
|
||||||
el.sourceHandle = props.edge.sourceHandle;
|
el.sourceHandle = props.edge.sourceHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.edge.targetHandle) {
|
if (props.edge.targetHandle) {
|
||||||
el.targetHandle = props.edge.targetHandle;
|
el.targetHandle = props.edge.targetHandle
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof props.edge.data !== 'undefined') {
|
if (typeof props.edge.data !== 'undefined') {
|
||||||
el.data = props.edge.data;
|
el.data = props.edge.data
|
||||||
}
|
}
|
||||||
|
|
||||||
return el;
|
return el
|
||||||
};
|
}
|
||||||
|
|
||||||
const onEdgeClick = (event: MouseEvent) => {
|
const onEdgeClick = (event: MouseEvent) => {
|
||||||
if (store.elementsSelectable) {
|
if (store.elementsSelectable) {
|
||||||
store.unsetNodesSelection();
|
store.unsetNodesSelection()
|
||||||
store.addSelectedElements(edgeElement() as any);
|
store.addSelectedElements(edgeElement() as any)
|
||||||
}
|
}
|
||||||
|
|
||||||
hooks.edgeClick.trigger({ event, edge: edgeElement() });
|
hooks.edgeClick.trigger({ event, edge: edgeElement() })
|
||||||
};
|
}
|
||||||
|
|
||||||
const onEdgeContextMenu = (event: MouseEvent) =>
|
const onEdgeContextMenu = (event: MouseEvent) =>
|
||||||
hooks.edgeContextMenu.trigger({
|
hooks.edgeContextMenu.trigger({
|
||||||
event,
|
event,
|
||||||
edge: edgeElement()
|
edge: edgeElement()
|
||||||
});
|
})
|
||||||
|
|
||||||
const onEdgeMouseEnter = (event: MouseEvent) => hooks.edgeMouseEnter.trigger({ event, edge: edgeElement() });
|
const onEdgeMouseEnter = (event: MouseEvent) => hooks.edgeMouseEnter.trigger({ event, edge: edgeElement() })
|
||||||
|
|
||||||
const onEdgeMouseMove = (event: MouseEvent) => hooks.edgeMouseMove.trigger({ event, edge: edgeElement() });
|
const onEdgeMouseMove = (event: MouseEvent) => hooks.edgeMouseMove.trigger({ event, edge: edgeElement() })
|
||||||
|
|
||||||
const onEdgeMouseLeave = (event: MouseEvent) => hooks.edgeMouseLeave.trigger({ event, edge: edgeElement() });
|
const onEdgeMouseLeave = (event: MouseEvent) => hooks.edgeMouseLeave.trigger({ event, edge: edgeElement() })
|
||||||
|
|
||||||
const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => {
|
const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => {
|
||||||
const nodeId = isSourceHandle ? props.edge.target : props.edge.source;
|
const nodeId = isSourceHandle ? props.edge.target : props.edge.source
|
||||||
const handleId = isSourceHandle ? props.edge.targetHandle : props.edge.sourceHandle;
|
const handleId = isSourceHandle ? props.edge.targetHandle : props.edge.sourceHandle
|
||||||
const isValidConnection = () => true;
|
const isValidConnection = () => true
|
||||||
const isTarget = isSourceHandle;
|
const isTarget = isSourceHandle
|
||||||
|
|
||||||
hooks.edgeUpdateStart.trigger({ event, edge: edgeElement() });
|
hooks.edgeUpdateStart.trigger({ event, edge: edgeElement() })
|
||||||
onMouseDown(
|
onMouseDown(
|
||||||
event,
|
event,
|
||||||
store,
|
store,
|
||||||
@@ -164,20 +164,20 @@ export default defineComponent({
|
|||||||
isTarget,
|
isTarget,
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
isSourceHandle ? 'target' : 'source'
|
isSourceHandle ? 'target' : 'source'
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
const onEdgeUpdaterSourceMouseDown = (event: MouseEvent) => {
|
const onEdgeUpdaterSourceMouseDown = (event: MouseEvent) => {
|
||||||
handleEdgeUpdater(event, true);
|
handleEdgeUpdater(event, true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const onEdgeUpdaterTargetMouseDown = (event: MouseEvent) => {
|
const onEdgeUpdaterTargetMouseDown = (event: MouseEvent) => {
|
||||||
handleEdgeUpdater(event, false);
|
handleEdgeUpdater(event, false)
|
||||||
};
|
}
|
||||||
|
|
||||||
const updating = ref<boolean>(false);
|
const updating = ref<boolean>(false)
|
||||||
const onEdgeUpdaterMouseEnter = () => (updating.value = true);
|
const onEdgeUpdaterMouseEnter = () => (updating.value = true)
|
||||||
const onEdgeUpdaterMouseOut = () => (updating.value = false);
|
const onEdgeUpdaterMouseOut = () => (updating.value = false)
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
const { targetX, targetY, sourceX, sourceY } = getEdgePositions(
|
const { targetX, targetY, sourceX, sourceY } = getEdgePositions(
|
||||||
@@ -187,7 +187,7 @@ export default defineComponent({
|
|||||||
nodes.value.targetNode as Node,
|
nodes.value.targetNode as Node,
|
||||||
targetHandle,
|
targetHandle,
|
||||||
targetPosition
|
targetPosition
|
||||||
);
|
)
|
||||||
return !props.edge.isHidden && isVisible({ targetX, targetY, sourceX, sourceY }) ? (
|
return !props.edge.isHidden && isVisible({ targetX, targetY, sourceX, sourceY }) ? (
|
||||||
<g
|
<g
|
||||||
class={[
|
class={[
|
||||||
@@ -242,7 +242,7 @@ export default defineComponent({
|
|||||||
</g>
|
</g>
|
||||||
) : (
|
) : (
|
||||||
''
|
''
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import { Position } from '../../types';
|
import { Position } from '../../types'
|
||||||
import { defineComponent, HTMLAttributes, PropType } from 'vue';
|
import { defineComponent, HTMLAttributes, PropType } from 'vue'
|
||||||
|
|
||||||
const shiftX = (x: number, shift: number, position: Position): number => {
|
const shiftX = (x: number, shift: number, position: Position): number => {
|
||||||
if (position === Position.Left) return x - shift;
|
if (position === Position.Left) return x - shift
|
||||||
if (position === Position.Right) return x + shift;
|
if (position === Position.Right) return x + shift
|
||||||
return x;
|
return x
|
||||||
};
|
}
|
||||||
|
|
||||||
const shiftY = (y: number, shift: number, position: Position): number => {
|
const shiftY = (y: number, shift: number, position: Position): number => {
|
||||||
if (position === Position.Top) return y - shift;
|
if (position === Position.Top) return y - shift
|
||||||
if (position === Position.Bottom) return y + shift;
|
if (position === Position.Bottom) return y + shift
|
||||||
return y;
|
return y
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface EdgeAnchorProps extends HTMLAttributes {
|
export interface EdgeAnchorProps extends HTMLAttributes {
|
||||||
position: Position;
|
position: Position
|
||||||
centerX: number;
|
centerX: number
|
||||||
centerY: number;
|
centerY: number
|
||||||
radius?: number;
|
radius?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -50,6 +50,6 @@ export default defineComponent({
|
|||||||
stroke="transparent"
|
stroke="transparent"
|
||||||
fill="transparent"
|
fill="transparent"
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { EdgeTextProps, Rect } from '../../types';
|
import { EdgeTextProps, Rect } from '../../types'
|
||||||
import { defineComponent, PropType, ref, watchEffect } from 'vue';
|
import { defineComponent, PropType, ref, watchEffect } from 'vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -37,11 +37,11 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const edgeRef = ref<SVGTextElement | null>(null);
|
const edgeRef = ref<SVGTextElement | null>(null)
|
||||||
const edgeTextBox = ref<Rect>({ x: 0, y: 0, width: 0, height: 0 });
|
const edgeTextBox = ref<Rect>({ x: 0, y: 0, width: 0, height: 0 })
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
const textBbox = edgeRef.value?.getBBox();
|
const textBbox = edgeRef.value?.getBBox()
|
||||||
|
|
||||||
if (textBbox) {
|
if (textBbox) {
|
||||||
edgeTextBox.value = {
|
edgeTextBox.value = {
|
||||||
@@ -49,9 +49,9 @@ export default defineComponent({
|
|||||||
y: textBbox.y,
|
y: textBbox.y,
|
||||||
width: textBbox.width,
|
width: textBbox.width,
|
||||||
height: textBbox.height
|
height: textBbox.height
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<g
|
<g
|
||||||
@@ -75,6 +75,6 @@ export default defineComponent({
|
|||||||
</text>
|
</text>
|
||||||
{slots.default ? slots.default() : ''}
|
{slots.default ? slots.default() : ''}
|
||||||
</g>
|
</g>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue'
|
||||||
import EdgeText from './EdgeText';
|
import EdgeText from './EdgeText'
|
||||||
import { getMarkerEnd, getCenter, EdgeSmoothProps, GetCenterParams } from './utils';
|
import { getMarkerEnd, getCenter, EdgeSmoothProps, GetCenterParams } from './utils'
|
||||||
import { ArrowHeadType, EdgeType, Position } from '../../types';
|
import { ArrowHeadType, EdgeType, Position } from '../../types'
|
||||||
import { reactify } from '@vueuse/core';
|
import { reactify } from '@vueuse/core'
|
||||||
import { getBezierPath } from './BezierEdge';
|
import { getBezierPath } from './BezierEdge'
|
||||||
|
|
||||||
// These are some helper methods for drawing the round corners
|
// These are some helper methods for drawing the round corners
|
||||||
// The name indicates the direction of the path. "bottomLeftCorner" goes
|
// The name indicates the direction of the path. "bottomLeftCorner" goes
|
||||||
// from bottom to the left and "leftBottomCorner" goes from left to the bottom.
|
// from bottom to the left and "leftBottomCorner" goes from left to the bottom.
|
||||||
// We have to consider the direction of the paths because of the animated lines.
|
// We have to consider the direction of the paths because of the animated lines.
|
||||||
const bottomLeftCorner = (x: number, y: number, size: number): string => `L ${x},${y - size}Q ${x},${y} ${x + size},${y}`;
|
const bottomLeftCorner = (x: number, y: number, size: number): string => `L ${x},${y - size}Q ${x},${y} ${x + size},${y}`
|
||||||
const leftBottomCorner = (x: number, y: number, size: number): string => `L ${x + size},${y}Q ${x},${y} ${x},${y - size}`;
|
const leftBottomCorner = (x: number, y: number, size: number): string => `L ${x + size},${y}Q ${x},${y} ${x},${y - size}`
|
||||||
const bottomRightCorner = (x: number, y: number, size: number): string => `L ${x},${y - size}Q ${x},${y} ${x - size},${y}`;
|
const bottomRightCorner = (x: number, y: number, size: number): string => `L ${x},${y - size}Q ${x},${y} ${x - size},${y}`
|
||||||
const rightBottomCorner = (x: number, y: number, size: number): string => `L ${x - size},${y}Q ${x},${y} ${x},${y - size}`;
|
const rightBottomCorner = (x: number, y: number, size: number): string => `L ${x - size},${y}Q ${x},${y} ${x},${y - size}`
|
||||||
const leftTopCorner = (x: number, y: number, size: number): string => `L ${x + size},${y}Q ${x},${y} ${x},${y + size}`;
|
const leftTopCorner = (x: number, y: number, size: number): string => `L ${x + size},${y}Q ${x},${y} ${x},${y + size}`
|
||||||
const topLeftCorner = (x: number, y: number, size: number): string => `L ${x},${y + size}Q ${x},${y} ${x + size},${y}`;
|
const topLeftCorner = (x: number, y: number, size: number): string => `L ${x},${y + size}Q ${x},${y} ${x + size},${y}`
|
||||||
const topRightCorner = (x: number, y: number, size: number): string => `L ${x},${y + size}Q ${x},${y} ${x - size},${y}`;
|
const topRightCorner = (x: number, y: number, size: number): string => `L ${x},${y + size}Q ${x},${y} ${x - size},${y}`
|
||||||
const rightTopCorner = (x: number, y: number, size: number): string => `L ${x - size},${y}Q ${x},${y} ${x},${y + size}`;
|
const rightTopCorner = (x: number, y: number, size: number): string => `L ${x - size},${y}Q ${x},${y} ${x},${y + size}`
|
||||||
|
|
||||||
interface GetSmoothStepPathParams {
|
interface GetSmoothStepPathParams {
|
||||||
sourceX: number;
|
sourceX: number
|
||||||
sourceY: number;
|
sourceY: number
|
||||||
sourcePosition?: Position;
|
sourcePosition?: Position
|
||||||
targetX: number;
|
targetX: number
|
||||||
targetY: number;
|
targetY: number
|
||||||
targetPosition?: Position;
|
targetPosition?: Position
|
||||||
borderRadius?: number;
|
borderRadius?: number
|
||||||
centerX?: number;
|
centerX?: number
|
||||||
centerY?: number;
|
centerY?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSmoothStepPath({
|
export function getSmoothStepPath({
|
||||||
@@ -41,56 +41,55 @@ export function getSmoothStepPath({
|
|||||||
centerX,
|
centerX,
|
||||||
centerY
|
centerY
|
||||||
}: GetSmoothStepPathParams): string {
|
}: GetSmoothStepPathParams): string {
|
||||||
const [_centerX, _centerY, offsetX, offsetY] = getCenter({ sourceX, sourceY, targetX, targetY });
|
const [_centerX, _centerY, offsetX, offsetY] = getCenter({ sourceX, sourceY, targetX, targetY })
|
||||||
const cornerWidth = Math.min(borderRadius, Math.abs(targetX - sourceX));
|
const cornerWidth = Math.min(borderRadius, Math.abs(targetX - sourceX))
|
||||||
const cornerHeight = Math.min(borderRadius, Math.abs(targetY - sourceY));
|
const cornerHeight = Math.min(borderRadius, Math.abs(targetY - sourceY))
|
||||||
const cornerSize = Math.min(cornerWidth, cornerHeight, offsetX, offsetY);
|
const cornerSize = Math.min(cornerWidth, cornerHeight, offsetX, offsetY)
|
||||||
const leftAndRight = [Position.Left, Position.Right];
|
const leftAndRight = [Position.Left, Position.Right]
|
||||||
const cX = typeof centerX !== 'undefined' ? centerX : _centerX;
|
const cX = typeof centerX !== 'undefined' ? centerX : _centerX
|
||||||
const cY = typeof centerY !== 'undefined' ? centerY : _centerY;
|
const cY = typeof centerY !== 'undefined' ? centerY : _centerY
|
||||||
|
|
||||||
let firstCornerPath;
|
let firstCornerPath
|
||||||
let secondCornerPath;
|
let secondCornerPath
|
||||||
|
|
||||||
if (sourceX <= targetX) {
|
if (sourceX <= targetX) {
|
||||||
firstCornerPath = sourceY <= targetY ? bottomLeftCorner(sourceX, cY, cornerSize) : topLeftCorner(sourceX, cY, cornerSize);
|
firstCornerPath = sourceY <= targetY ? bottomLeftCorner(sourceX, cY, cornerSize) : topLeftCorner(sourceX, cY, cornerSize)
|
||||||
secondCornerPath = sourceY <= targetY ? rightTopCorner(targetX, cY, cornerSize) : rightBottomCorner(targetX, cY, cornerSize);
|
secondCornerPath = sourceY <= targetY ? rightTopCorner(targetX, cY, cornerSize) : rightBottomCorner(targetX, cY, cornerSize)
|
||||||
} else {
|
} else {
|
||||||
firstCornerPath = sourceY < targetY ? bottomRightCorner(sourceX, cY, cornerSize) : topRightCorner(sourceX, cY, cornerSize);
|
firstCornerPath = sourceY < targetY ? bottomRightCorner(sourceX, cY, cornerSize) : topRightCorner(sourceX, cY, cornerSize)
|
||||||
secondCornerPath = sourceY < targetY ? leftTopCorner(targetX, cY, cornerSize) : leftBottomCorner(targetX, cY, cornerSize);
|
secondCornerPath = sourceY < targetY ? leftTopCorner(targetX, cY, cornerSize) : leftBottomCorner(targetX, cY, cornerSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
||||||
if (sourceX <= targetX) {
|
if (sourceX <= targetX) {
|
||||||
firstCornerPath = sourceY <= targetY ? rightTopCorner(cX, sourceY, cornerSize) : rightBottomCorner(cX, sourceY, cornerSize);
|
firstCornerPath = sourceY <= targetY ? rightTopCorner(cX, sourceY, cornerSize) : rightBottomCorner(cX, sourceY, cornerSize)
|
||||||
secondCornerPath = sourceY <= targetY ? bottomLeftCorner(cX, targetY, cornerSize) : topLeftCorner(cX, targetY, cornerSize);
|
secondCornerPath = sourceY <= targetY ? bottomLeftCorner(cX, targetY, cornerSize) : topLeftCorner(cX, targetY, cornerSize)
|
||||||
} else if (sourcePosition === Position.Right && targetPosition === Position.Left) {
|
} else if (sourcePosition === Position.Right && targetPosition === Position.Left) {
|
||||||
// and sourceX > targetX
|
// and sourceX > targetX
|
||||||
firstCornerPath = sourceY <= targetY ? leftTopCorner(cX, sourceY, cornerSize) : leftBottomCorner(cX, sourceY, cornerSize);
|
firstCornerPath = sourceY <= targetY ? leftTopCorner(cX, sourceY, cornerSize) : leftBottomCorner(cX, sourceY, cornerSize)
|
||||||
secondCornerPath =
|
secondCornerPath = sourceY <= targetY ? bottomRightCorner(cX, targetY, cornerSize) : topRightCorner(cX, targetY, cornerSize)
|
||||||
sourceY <= targetY ? bottomRightCorner(cX, targetY, cornerSize) : topRightCorner(cX, targetY, cornerSize);
|
|
||||||
}
|
}
|
||||||
} else if (leftAndRight.includes(sourcePosition) && !leftAndRight.includes(targetPosition)) {
|
} else if (leftAndRight.includes(sourcePosition) && !leftAndRight.includes(targetPosition)) {
|
||||||
if (sourceX <= targetX) {
|
if (sourceX <= targetX) {
|
||||||
firstCornerPath =
|
firstCornerPath =
|
||||||
sourceY <= targetY ? rightTopCorner(targetX, sourceY, cornerSize) : rightBottomCorner(targetX, sourceY, cornerSize);
|
sourceY <= targetY ? rightTopCorner(targetX, sourceY, cornerSize) : rightBottomCorner(targetX, sourceY, cornerSize)
|
||||||
} else {
|
} else {
|
||||||
firstCornerPath =
|
firstCornerPath =
|
||||||
sourceY <= targetY ? leftTopCorner(targetX, sourceY, cornerSize) : leftBottomCorner(targetX, sourceY, cornerSize);
|
sourceY <= targetY ? leftTopCorner(targetX, sourceY, cornerSize) : leftBottomCorner(targetX, sourceY, cornerSize)
|
||||||
}
|
}
|
||||||
secondCornerPath = '';
|
secondCornerPath = ''
|
||||||
} else if (!leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
} else if (!leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
||||||
if (sourceX <= targetX) {
|
if (sourceX <= targetX) {
|
||||||
firstCornerPath =
|
firstCornerPath =
|
||||||
sourceY <= targetY ? bottomLeftCorner(sourceX, targetY, cornerSize) : topLeftCorner(sourceX, targetY, cornerSize);
|
sourceY <= targetY ? bottomLeftCorner(sourceX, targetY, cornerSize) : topLeftCorner(sourceX, targetY, cornerSize)
|
||||||
} else {
|
} else {
|
||||||
firstCornerPath =
|
firstCornerPath =
|
||||||
sourceY <= targetY ? bottomRightCorner(sourceX, targetY, cornerSize) : topRightCorner(sourceX, targetY, cornerSize);
|
sourceY <= targetY ? bottomRightCorner(sourceX, targetY, cornerSize) : topRightCorner(sourceX, targetY, cornerSize)
|
||||||
}
|
}
|
||||||
secondCornerPath = '';
|
secondCornerPath = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return `M ${sourceX},${sourceY}${firstCornerPath}${secondCornerPath}L ${targetX},${targetY}`;
|
return `M ${sourceX},${sourceY}${firstCornerPath}${secondCornerPath}L ${targetX},${targetY}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -107,8 +106,8 @@ export default defineComponent({
|
|||||||
targetY: targetY,
|
targetY: targetY,
|
||||||
sourcePosition: sourcePosition,
|
sourcePosition: sourcePosition,
|
||||||
targetPosition: targetPosition
|
targetPosition: targetPosition
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
const path = reactify(({ sourceX, sourceY, targetX, targetY, targetPosition, sourcePosition }: GetSmoothStepPathParams) => {
|
const path = reactify(({ sourceX, sourceY, targetX, targetY, targetPosition, sourcePosition }: GetSmoothStepPathParams) => {
|
||||||
return getBezierPath({
|
return getBezierPath({
|
||||||
sourceX: sourceX,
|
sourceX: sourceX,
|
||||||
@@ -117,10 +116,10 @@ export default defineComponent({
|
|||||||
targetY: targetY,
|
targetY: targetY,
|
||||||
targetPosition: targetPosition,
|
targetPosition: targetPosition,
|
||||||
sourcePosition: sourcePosition
|
sourcePosition: sourcePosition
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId));
|
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId))
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
@@ -143,6 +142,6 @@ export default defineComponent({
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}) as unknown as EdgeType;
|
}) as unknown as EdgeType
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue'
|
||||||
import SmoothStepEdge from './SmoothStepEdge';
|
import SmoothStepEdge from './SmoothStepEdge'
|
||||||
import { EdgeSmoothProps } from './utils';
|
import { EdgeSmoothProps } from './utils'
|
||||||
import { EdgeType } from '../../types';
|
import { EdgeType } from '../../types'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { SmoothStepEdge },
|
components: { SmoothStepEdge },
|
||||||
@@ -10,6 +10,6 @@ export default defineComponent({
|
|||||||
...EdgeSmoothProps
|
...EdgeSmoothProps
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
return () => <SmoothStepEdge {...props} borderRadius={0} />;
|
return () => <SmoothStepEdge {...props} borderRadius={0} />
|
||||||
}
|
}
|
||||||
}) as EdgeType;
|
}) as EdgeType
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue'
|
||||||
import EdgeText from './EdgeText';
|
import EdgeText from './EdgeText'
|
||||||
import { getMarkerEnd, DefaultEdgeProps } from './utils';
|
import { getMarkerEnd, DefaultEdgeProps } from './utils'
|
||||||
import { reactify } from '@vueuse/core';
|
import { reactify } from '@vueuse/core'
|
||||||
import { ArrowHeadType, EdgeType } from '../../types';
|
import { ArrowHeadType, EdgeType } from '../../types'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
inheritAttrs: false,
|
inheritAttrs: false,
|
||||||
@@ -11,15 +11,15 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const centerY = reactify((targetY: number, sourceY: number) => {
|
const centerY = reactify((targetY: number, sourceY: number) => {
|
||||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
const yOffset = Math.abs(targetY - sourceY) / 2
|
||||||
return targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
return targetY < sourceY ? targetY + yOffset : targetY - yOffset
|
||||||
});
|
})
|
||||||
const centerX = reactify((targetX: number, sourceX: number) => {
|
const centerX = reactify((targetX: number, sourceX: number) => {
|
||||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
const xOffset = Math.abs(targetX - sourceX) / 2
|
||||||
return targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
return targetX < sourceX ? targetX + xOffset : targetX - xOffset
|
||||||
});
|
})
|
||||||
|
|
||||||
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId));
|
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId))
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<>
|
<>
|
||||||
@@ -42,6 +42,6 @@ export default defineComponent({
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}) as unknown as EdgeType;
|
}) as unknown as EdgeType
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export { default as BezierEdge } from './BezierEdge';
|
export { default as BezierEdge } from './BezierEdge'
|
||||||
export { default as StepEdge } from './StepEdge';
|
export { default as StepEdge } from './StepEdge'
|
||||||
export { default as SmoothStepEdge } from './SmoothStepEdge';
|
export { default as SmoothStepEdge } from './SmoothStepEdge'
|
||||||
export { default as StraightEdge } from './StraightEdge';
|
export { default as StraightEdge } from './StraightEdge'
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import { ArrowHeadType, Position, EdgeProps, EdgeSmoothStepProps } from '../../types';
|
import { ArrowHeadType, Position, EdgeProps, EdgeSmoothStepProps } from '../../types'
|
||||||
import { PropType } from 'vue';
|
import { PropType } from 'vue'
|
||||||
|
|
||||||
export const getMarkerEnd = (arrowHeadType?: ArrowHeadType, markerEndId?: string): string => {
|
export const getMarkerEnd = (arrowHeadType?: ArrowHeadType, markerEndId?: string): string => {
|
||||||
if (typeof markerEndId !== 'undefined' && markerEndId) {
|
if (typeof markerEndId !== 'undefined' && markerEndId) {
|
||||||
return `url(#${markerEndId})`;
|
return `url(#${markerEndId})`
|
||||||
}
|
}
|
||||||
|
|
||||||
return typeof arrowHeadType !== 'undefined' ? `url(#revue-flow__${arrowHeadType})` : 'none';
|
return typeof arrowHeadType !== 'undefined' ? `url(#revue-flow__${arrowHeadType})` : 'none'
|
||||||
};
|
|
||||||
|
|
||||||
export interface GetCenterParams {
|
|
||||||
sourceX: number;
|
|
||||||
sourceY: number;
|
|
||||||
targetX: number;
|
|
||||||
targetY: number;
|
|
||||||
sourcePosition?: Position;
|
|
||||||
targetPosition?: Position;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const LeftOrRight = [Position.Left, Position.Right];
|
export interface GetCenterParams {
|
||||||
|
sourceX: number
|
||||||
|
sourceY: number
|
||||||
|
targetX: number
|
||||||
|
targetY: number
|
||||||
|
sourcePosition?: Position
|
||||||
|
targetPosition?: Position
|
||||||
|
}
|
||||||
|
|
||||||
|
const LeftOrRight = [Position.Left, Position.Right]
|
||||||
|
|
||||||
export const getCenter = ({
|
export const getCenter = ({
|
||||||
sourceX,
|
sourceX,
|
||||||
@@ -28,31 +28,31 @@ export const getCenter = ({
|
|||||||
sourcePosition = Position.Bottom,
|
sourcePosition = Position.Bottom,
|
||||||
targetPosition = Position.Top
|
targetPosition = Position.Top
|
||||||
}: GetCenterParams): [number, number, number, number] => {
|
}: GetCenterParams): [number, number, number, number] => {
|
||||||
const sourceIsLeftOrRight = LeftOrRight.includes(sourcePosition);
|
const sourceIsLeftOrRight = LeftOrRight.includes(sourcePosition)
|
||||||
const targetIsLeftOrRight = LeftOrRight.includes(targetPosition);
|
const targetIsLeftOrRight = LeftOrRight.includes(targetPosition)
|
||||||
|
|
||||||
// we expect flows to be horizontal or vertical (all handles left or right respectively top or bottom)
|
// we expect flows to be horizontal or vertical (all handles left or right respectively top or bottom)
|
||||||
// a mixed edge is when one the source is on the left and the target is on the top for example.
|
// a mixed edge is when one the source is on the left and the target is on the top for example.
|
||||||
const mixedEdge = (sourceIsLeftOrRight && !targetIsLeftOrRight) || (targetIsLeftOrRight && !sourceIsLeftOrRight);
|
const mixedEdge = (sourceIsLeftOrRight && !targetIsLeftOrRight) || (targetIsLeftOrRight && !sourceIsLeftOrRight)
|
||||||
|
|
||||||
if (mixedEdge) {
|
if (mixedEdge) {
|
||||||
const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0;
|
const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0
|
||||||
const centerX = sourceX > targetX ? sourceX - xOffset : sourceX + xOffset;
|
const centerX = sourceX > targetX ? sourceX - xOffset : sourceX + xOffset
|
||||||
|
|
||||||
const yOffset = sourceIsLeftOrRight ? 0 : Math.abs(targetY - sourceY);
|
const yOffset = sourceIsLeftOrRight ? 0 : Math.abs(targetY - sourceY)
|
||||||
const centerY = sourceY < targetY ? sourceY + yOffset : sourceY - yOffset;
|
const centerY = sourceY < targetY ? sourceY + yOffset : sourceY - yOffset
|
||||||
|
|
||||||
return [centerX, centerY, xOffset, yOffset];
|
return [centerX, centerY, xOffset, yOffset]
|
||||||
}
|
}
|
||||||
|
|
||||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
const xOffset = Math.abs(targetX - sourceX) / 2
|
||||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset
|
||||||
|
|
||||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
const yOffset = Math.abs(targetY - sourceY) / 2
|
||||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset
|
||||||
|
|
||||||
return [centerX, centerY, xOffset, yOffset];
|
return [centerX, centerY, xOffset, yOffset]
|
||||||
};
|
}
|
||||||
|
|
||||||
export const DefaultEdgeProps = {
|
export const DefaultEdgeProps = {
|
||||||
id: {
|
id: {
|
||||||
@@ -165,7 +165,7 @@ export const DefaultEdgeProps = {
|
|||||||
required: false,
|
required: false,
|
||||||
default: undefined
|
default: undefined
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const EdgeSmoothProps = {
|
export const EdgeSmoothProps = {
|
||||||
...DefaultEdgeProps,
|
...DefaultEdgeProps,
|
||||||
@@ -174,4 +174,4 @@ export const EdgeSmoothProps = {
|
|||||||
required: false,
|
required: false,
|
||||||
default: 5
|
default: 5
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import { getHostForElement } from '../../utils';
|
import { getHostForElement } from '../../utils'
|
||||||
import { ElementId, ConnectionMode, Connection, HandleType, RevueFlowStore } from '../../types';
|
import { ElementId, ConnectionMode, Connection, HandleType, RevueFlowStore } from '../../types'
|
||||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||||
|
|
||||||
export type ValidConnectionFunc = (connection: Connection) => boolean;
|
export type ValidConnectionFunc = (connection: Connection) => boolean
|
||||||
|
|
||||||
type Result = {
|
type Result = {
|
||||||
elementBelow: Element | null;
|
elementBelow: Element | null
|
||||||
isValid: boolean;
|
isValid: boolean
|
||||||
connection: Connection;
|
connection: Connection
|
||||||
isHoveringHandle: boolean;
|
isHoveringHandle: boolean
|
||||||
};
|
}
|
||||||
|
|
||||||
// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
|
// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
|
||||||
function checkElementBelowIsValid(
|
function checkElementBelowIsValid(
|
||||||
@@ -21,27 +21,27 @@ function checkElementBelowIsValid(
|
|||||||
isValidConnection: ValidConnectionFunc,
|
isValidConnection: ValidConnectionFunc,
|
||||||
doc: Document | ShadowRoot
|
doc: Document | ShadowRoot
|
||||||
) {
|
) {
|
||||||
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY);
|
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
|
||||||
const elementBelowIsTarget = elementBelow?.classList.contains('target') || false;
|
const elementBelowIsTarget = elementBelow?.classList.contains('target') || false
|
||||||
const elementBelowIsSource = elementBelow?.classList.contains('source') || false;
|
const elementBelowIsSource = elementBelow?.classList.contains('source') || false
|
||||||
|
|
||||||
const result: Result = {
|
const result: Result = {
|
||||||
elementBelow,
|
elementBelow,
|
||||||
isValid: false,
|
isValid: false,
|
||||||
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
|
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
|
||||||
isHoveringHandle: false
|
isHoveringHandle: false
|
||||||
};
|
}
|
||||||
|
|
||||||
if (elementBelow && (elementBelowIsTarget || elementBelowIsSource)) {
|
if (elementBelow && (elementBelowIsTarget || elementBelowIsSource)) {
|
||||||
result.isHoveringHandle = true;
|
result.isHoveringHandle = true
|
||||||
|
|
||||||
// in strict mode we don't allow target to target or source to source connections
|
// in strict mode we don't allow target to target or source to source connections
|
||||||
const isValid =
|
const isValid =
|
||||||
connectionMode === ConnectionMode.Strict ? (isTarget && elementBelowIsSource) || (!isTarget && elementBelowIsTarget) : true;
|
connectionMode === ConnectionMode.Strict ? (isTarget && elementBelowIsSource) || (!isTarget && elementBelowIsTarget) : true
|
||||||
|
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
const elementBelowNodeId = elementBelow.getAttribute('data-nodeid');
|
const elementBelowNodeId = elementBelow.getAttribute('data-nodeid')
|
||||||
const elementBelowHandleId = elementBelow.getAttribute('data-handleid');
|
const elementBelowHandleId = elementBelow.getAttribute('data-handleid')
|
||||||
const connection: Connection = isTarget
|
const connection: Connection = isTarget
|
||||||
? {
|
? {
|
||||||
source: elementBelowNodeId,
|
source: elementBelowNodeId,
|
||||||
@@ -54,30 +54,30 @@ function checkElementBelowIsValid(
|
|||||||
sourceHandle: handleId,
|
sourceHandle: handleId,
|
||||||
target: elementBelowNodeId,
|
target: elementBelowNodeId,
|
||||||
targetHandle: elementBelowHandleId
|
targetHandle: elementBelowHandleId
|
||||||
};
|
}
|
||||||
|
|
||||||
result.connection = connection;
|
result.connection = connection
|
||||||
result.isValid = isValidConnection(connection);
|
result.isValid = isValidConnection(connection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetRecentHandle(hoveredHandle: Element): void {
|
function resetRecentHandle(hoveredHandle: Element): void {
|
||||||
hoveredHandle?.classList.remove('revue-flow__handle-valid');
|
hoveredHandle?.classList.remove('revue-flow__handle-valid')
|
||||||
hoveredHandle?.classList.remove('revue-flow__handle-connecting');
|
hoveredHandle?.classList.remove('revue-flow__handle-connecting')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function onMouseDown(
|
export function onMouseDown(
|
||||||
event: MouseEvent,
|
event: MouseEvent,
|
||||||
store: RevueFlowStore,
|
store: RevueFlowStore,
|
||||||
hooks: {
|
hooks: {
|
||||||
connectStart: RevueFlowHooks['connectStart'];
|
connectStart: RevueFlowHooks['connectStart']
|
||||||
connectStop: RevueFlowHooks['connectStop'];
|
connectStop: RevueFlowHooks['connectStop']
|
||||||
connectEnd: RevueFlowHooks['connectEnd'];
|
connectEnd: RevueFlowHooks['connectEnd']
|
||||||
connect: RevueFlowHooks['connect'];
|
connect: RevueFlowHooks['connect']
|
||||||
edgeUpdateEnd: RevueFlowHooks['edgeUpdateEnd'];
|
edgeUpdateEnd: RevueFlowHooks['edgeUpdateEnd']
|
||||||
},
|
},
|
||||||
handleId: ElementId | null,
|
handleId: ElementId | null,
|
||||||
nodeId: ElementId,
|
nodeId: ElementId,
|
||||||
@@ -85,39 +85,39 @@ export function onMouseDown(
|
|||||||
isValidConnection: ValidConnectionFunc,
|
isValidConnection: ValidConnectionFunc,
|
||||||
elementEdgeUpdaterType?: HandleType
|
elementEdgeUpdaterType?: HandleType
|
||||||
): void {
|
): void {
|
||||||
const revueFlowNode = (event.target as Element).closest('.revue-flow');
|
const revueFlowNode = (event.target as Element).closest('.revue-flow')
|
||||||
// when revue-flow is used inside a shadow root we can't use document
|
// when revue-flow is used inside a shadow root we can't use document
|
||||||
const doc = getHostForElement(event.target as HTMLElement);
|
const doc = getHostForElement(event.target as HTMLElement)
|
||||||
|
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY);
|
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
|
||||||
const elementBelowIsTarget = elementBelow?.classList.contains('target');
|
const elementBelowIsTarget = elementBelow?.classList.contains('target')
|
||||||
const elementBelowIsSource = elementBelow?.classList.contains('source');
|
const elementBelowIsSource = elementBelow?.classList.contains('source')
|
||||||
|
|
||||||
if (!revueFlowNode || (!elementBelowIsTarget && !elementBelowIsSource && !elementEdgeUpdaterType)) {
|
if (!revueFlowNode || (!elementBelowIsTarget && !elementBelowIsSource && !elementEdgeUpdaterType)) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementBelowIsTarget ? 'target' : 'source';
|
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementBelowIsTarget ? 'target' : 'source'
|
||||||
const containerBounds = revueFlowNode.getBoundingClientRect();
|
const containerBounds = revueFlowNode.getBoundingClientRect()
|
||||||
let recentHoveredHandle: Element;
|
let recentHoveredHandle: Element
|
||||||
|
|
||||||
store.connectionPosition.x = event.clientX - containerBounds.left;
|
store.connectionPosition.x = event.clientX - containerBounds.left
|
||||||
store.connectionPosition.y = event.clientY - containerBounds.top;
|
store.connectionPosition.y = event.clientY - containerBounds.top
|
||||||
|
|
||||||
store.setConnectionNodeId({
|
store.setConnectionNodeId({
|
||||||
connectionNodeId: nodeId,
|
connectionNodeId: nodeId,
|
||||||
connectionHandleId: handleId,
|
connectionHandleId: handleId,
|
||||||
connectionHandleType: handleType
|
connectionHandleType: handleType
|
||||||
});
|
})
|
||||||
hooks.connectStart.trigger({ event, params: { nodeId, handleId, handleType } });
|
hooks.connectStart.trigger({ event, params: { nodeId, handleId, handleType } })
|
||||||
|
|
||||||
function onMouseMove(event: MouseEvent) {
|
function onMouseMove(event: MouseEvent) {
|
||||||
store.connectionPosition.x = event.clientX - containerBounds.left;
|
store.connectionPosition.x = event.clientX - containerBounds.left
|
||||||
store.connectionPosition.y = event.clientY - containerBounds.top;
|
store.connectionPosition.y = event.clientY - containerBounds.top
|
||||||
|
|
||||||
const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(
|
const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(
|
||||||
event,
|
event,
|
||||||
@@ -127,18 +127,18 @@ export function onMouseDown(
|
|||||||
handleId,
|
handleId,
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
doc
|
doc
|
||||||
);
|
)
|
||||||
|
|
||||||
if (!isHoveringHandle) {
|
if (!isHoveringHandle) {
|
||||||
return resetRecentHandle(recentHoveredHandle);
|
return resetRecentHandle(recentHoveredHandle)
|
||||||
}
|
}
|
||||||
|
|
||||||
const isOwnHandle = connection.source === connection.target;
|
const isOwnHandle = connection.source === connection.target
|
||||||
|
|
||||||
if (!isOwnHandle && elementBelow) {
|
if (!isOwnHandle && elementBelow) {
|
||||||
recentHoveredHandle = elementBelow;
|
recentHoveredHandle = elementBelow
|
||||||
elementBelow.classList.add('revue-flow__handle-connecting');
|
elementBelow.classList.add('revue-flow__handle-connecting')
|
||||||
elementBelow.classList.toggle('revue-flow__handle-valid', isValid);
|
elementBelow.classList.toggle('revue-flow__handle-valid', isValid)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,27 +151,27 @@ export function onMouseDown(
|
|||||||
handleId,
|
handleId,
|
||||||
isValidConnection,
|
isValidConnection,
|
||||||
doc
|
doc
|
||||||
);
|
)
|
||||||
|
|
||||||
hooks.connectStop.trigger(event);
|
hooks.connectStop.trigger(event)
|
||||||
|
|
||||||
if (isValid) {
|
if (isValid) {
|
||||||
hooks.connect.trigger(connection);
|
hooks.connect.trigger(connection)
|
||||||
}
|
}
|
||||||
|
|
||||||
hooks.connectEnd.trigger(event);
|
hooks.connectEnd.trigger(event)
|
||||||
|
|
||||||
if (elementEdgeUpdaterType) {
|
if (elementEdgeUpdaterType) {
|
||||||
hooks.edgeUpdateEnd.trigger({ event } as any);
|
hooks.edgeUpdateEnd.trigger({ event } as any)
|
||||||
}
|
}
|
||||||
|
|
||||||
resetRecentHandle(recentHoveredHandle);
|
resetRecentHandle(recentHoveredHandle)
|
||||||
store.setConnectionNodeId({ connectionNodeId: null, connectionHandleId: null, connectionHandleType: null });
|
store.setConnectionNodeId({ connectionNodeId: null, connectionHandleId: null, connectionHandleType: null })
|
||||||
|
|
||||||
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject);
|
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
|
||||||
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
|
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
|
||||||
}
|
}
|
||||||
|
|
||||||
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject);
|
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
|
||||||
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
|
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { ElementId, Position, RevueFlowStore } from '../../types';
|
import { ElementId, Position, RevueFlowStore } from '../../types'
|
||||||
import { onMouseDown, ValidConnectionFunc } from './handler';
|
import { onMouseDown, ValidConnectionFunc } from './handler'
|
||||||
import { defineComponent, inject, PropType } from 'vue';
|
import { defineComponent, inject, PropType } from 'vue'
|
||||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||||
|
|
||||||
const alwaysValid = () => true;
|
const alwaysValid = () => true
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
@@ -34,12 +34,12 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||||
const nodeId = inject<ElementId>('NodeIdContext')!;
|
const nodeId = inject<ElementId>('NodeIdContext')!
|
||||||
|
|
||||||
const onMouseDownHandler = (event: MouseEvent) =>
|
const onMouseDownHandler = (event: MouseEvent) =>
|
||||||
onMouseDown(event, store, hooks, props.id as string, nodeId, props.type === 'target', props.isValidConnection);
|
onMouseDown(event, store, hooks, props.id as string, nodeId, props.type === 'target', props.isValidConnection)
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
@@ -60,6 +60,6 @@ export default defineComponent({
|
|||||||
>
|
>
|
||||||
{slots.default ? slots.default() : ''}
|
{slots.default ? slots.default() : ''}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { defineComponent, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue'
|
||||||
import Handle from '../../components/Handle';
|
import Handle from '../../components/Handle'
|
||||||
import { NodeProps, NodeType, Position } from '../../types';
|
import { NodeProps, NodeType, Position } from '../../types'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'DefaultNode',
|
name: 'DefaultNode',
|
||||||
@@ -35,6 +35,6 @@ export default defineComponent({
|
|||||||
{props.data?.label}
|
{props.data?.label}
|
||||||
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
|
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}) as NodeType;
|
}) as NodeType
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Handle from '../../components/Handle';
|
import Handle from '../../components/Handle'
|
||||||
import { NodeProps, NodeType, Position } from '../../types';
|
import { NodeProps, NodeType, Position } from '../../types'
|
||||||
import { defineComponent, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'InputNode',
|
name: 'InputNode',
|
||||||
@@ -29,6 +29,6 @@ export default defineComponent({
|
|||||||
{props.data?.label}
|
{props.data?.label}
|
||||||
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
|
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}) as NodeType;
|
}) as NodeType
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { computed, defineComponent, h, inject, onMounted, PropType, provide } from 'vue';
|
import { computed, defineComponent, h, inject, onMounted, PropType, provide } from 'vue'
|
||||||
import { templateRef, useResizeObserver } from '@vueuse/core';
|
import { templateRef, useResizeObserver } from '@vueuse/core'
|
||||||
import { DraggableCore, DraggableEventListener } from '@braks/revue-draggable';
|
import { DraggableCore, DraggableEventListener } from '@braks/revue-draggable'
|
||||||
import { Node, NodeDimensionUpdate, NodeType, RevueFlowStore } from '../../types';
|
import { Node, NodeDimensionUpdate, NodeType, RevueFlowStore } from '../../types'
|
||||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||||
|
|
||||||
interface NodeProps {
|
interface NodeProps {
|
||||||
nodeTypes: Record<string, NodeType>;
|
nodeTypes: Record<string, NodeType>
|
||||||
snapToGrid: boolean;
|
snapToGrid: boolean
|
||||||
snapGrid: [number, number];
|
snapGrid: [number, number]
|
||||||
selectNodesOnDrag: boolean;
|
selectNodesOnDrag: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -39,22 +39,22 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||||
provide('NodeIdContext', props.node.id);
|
provide('NodeIdContext', props.node.id)
|
||||||
|
|
||||||
const nodeElement = templateRef<HTMLDivElement>('nodeElement', null);
|
const nodeElement = templateRef<HTMLDivElement>('nodeElement', null)
|
||||||
|
|
||||||
const selected = computed(() => store.selectedElements?.some(({ id }) => id === props.node.id) || false);
|
const selected = computed(() => store.selectedElements?.some(({ id }) => id === props.node.id) || false)
|
||||||
const isDraggable = computed(
|
const isDraggable = computed(
|
||||||
() => props.node.draggable || (store.nodesDraggable && typeof props.node.draggable === 'undefined')
|
() => props.node.draggable || (store.nodesDraggable && typeof props.node.draggable === 'undefined')
|
||||||
);
|
)
|
||||||
const isSelectable = computed(
|
const isSelectable = computed(
|
||||||
() => props.node.selectable || (store.elementsSelectable && typeof props.node.selectable === 'undefined')
|
() => props.node.selectable || (store.elementsSelectable && typeof props.node.selectable === 'undefined')
|
||||||
);
|
)
|
||||||
const isConnectable = computed(
|
const isConnectable = computed(
|
||||||
() => props.node.connectable || (store.nodesConnectable && typeof props.node.connectable === 'undefined')
|
() => props.node.connectable || (store.nodesConnectable && typeof props.node.connectable === 'undefined')
|
||||||
);
|
)
|
||||||
|
|
||||||
const node = () =>
|
const node = () =>
|
||||||
({
|
({
|
||||||
@@ -62,72 +62,72 @@ export default defineComponent({
|
|||||||
type: props.node.type,
|
type: props.node.type,
|
||||||
position: { x: props.node.__rf.position.x, y: props.node.__rf.position.y },
|
position: { x: props.node.__rf.position.x, y: props.node.__rf.position.y },
|
||||||
data: props.node.data
|
data: props.node.data
|
||||||
} as Node);
|
} as Node)
|
||||||
|
|
||||||
const onMouseEnterHandler = () => {
|
const onMouseEnterHandler = () => {
|
||||||
if (props.node.__rf.isDragging) {
|
if (props.node.__rf.isDragging) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return (event: MouseEvent) => hooks.nodeMouseEnter.trigger({ event, node: node() });
|
return (event: MouseEvent) => hooks.nodeMouseEnter.trigger({ event, node: node() })
|
||||||
};
|
}
|
||||||
|
|
||||||
const onMouseMoveHandler = () => {
|
const onMouseMoveHandler = () => {
|
||||||
if (props.node.__rf.isDragging) {
|
if (props.node.__rf.isDragging) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return (event: MouseEvent) => hooks.nodeMouseMove.trigger({ event, node: node() });
|
return (event: MouseEvent) => hooks.nodeMouseMove.trigger({ event, node: node() })
|
||||||
};
|
}
|
||||||
|
|
||||||
const onMouseLeaveHandler = () => {
|
const onMouseLeaveHandler = () => {
|
||||||
if (props.node.__rf.isDragging) {
|
if (props.node.__rf.isDragging) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
return (event: MouseEvent) => hooks.nodeMouseLeave.trigger({ event, node: node() });
|
return (event: MouseEvent) => hooks.nodeMouseLeave.trigger({ event, node: node() })
|
||||||
};
|
}
|
||||||
|
|
||||||
const onContextMenuHandler = () => {
|
const onContextMenuHandler = () => {
|
||||||
return (event: MouseEvent) => hooks.nodeContextMenu.trigger({ event, node: node() });
|
return (event: MouseEvent) => hooks.nodeContextMenu.trigger({ event, node: node() })
|
||||||
};
|
}
|
||||||
|
|
||||||
const onSelectNodeHandler = (event: MouseEvent) => {
|
const onSelectNodeHandler = (event: MouseEvent) => {
|
||||||
if (!isDraggable.value) {
|
if (!isDraggable.value) {
|
||||||
const n = node();
|
const n = node()
|
||||||
if (isSelectable.value) {
|
if (isSelectable.value) {
|
||||||
store.unsetNodesSelection();
|
store.unsetNodesSelection()
|
||||||
|
|
||||||
if (!selected.value) {
|
if (!selected.value) {
|
||||||
store.addSelectedElements([n]);
|
store.addSelectedElements([n])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hooks.nodeClick.trigger({ event, node: n });
|
hooks.nodeClick.trigger({ event, node: n })
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const onDragStart: DraggableEventListener = ({ event }) => {
|
const onDragStart: DraggableEventListener = ({ event }) => {
|
||||||
const n = node();
|
const n = node()
|
||||||
hooks.nodeDragStart.trigger({ event, node: n });
|
hooks.nodeDragStart.trigger({ event, node: n })
|
||||||
|
|
||||||
if (props.selectNodesOnDrag && isSelectable.value) {
|
if (props.selectNodesOnDrag && isSelectable.value) {
|
||||||
store.unsetNodesSelection();
|
store.unsetNodesSelection()
|
||||||
|
|
||||||
if (!selected.value) {
|
if (!selected.value) {
|
||||||
store.addSelectedElements([n]);
|
store.addSelectedElements([n])
|
||||||
}
|
}
|
||||||
} else if (!props.selectNodesOnDrag && !selected.value && isSelectable.value) {
|
} else if (!props.selectNodesOnDrag && !selected.value && isSelectable.value) {
|
||||||
store.unsetNodesSelection();
|
store.unsetNodesSelection()
|
||||||
store.addSelectedElements([]);
|
store.addSelectedElements([])
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const onDrag: DraggableEventListener = ({ event, data }) => {
|
const onDrag: DraggableEventListener = ({ event, data }) => {
|
||||||
const n = node();
|
const n = node()
|
||||||
n.position.x += data.deltaX;
|
n.position.x += data.deltaX
|
||||||
n.position.y += data.deltaY;
|
n.position.y += data.deltaY
|
||||||
hooks.nodeDrag.trigger({ event, node: n });
|
hooks.nodeDrag.trigger({ event, node: n })
|
||||||
|
|
||||||
store?.updateNodePosDiff({
|
store?.updateNodePosDiff({
|
||||||
id: props.node.id as string,
|
id: props.node.id as string,
|
||||||
@@ -136,39 +136,39 @@ export default defineComponent({
|
|||||||
y: data.deltaY
|
y: data.deltaY
|
||||||
},
|
},
|
||||||
isDragging: true
|
isDragging: true
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const onDragStop: DraggableEventListener = ({ event }) => {
|
const onDragStop: DraggableEventListener = ({ event }) => {
|
||||||
const n = node();
|
const n = node()
|
||||||
// onDragStop also gets called when user just clicks on a node.
|
// onDragStop also gets called when user just clicks on a node.
|
||||||
// Because of that we set dragging to true inside the onDrag handler and handle the click here
|
// Because of that we set dragging to true inside the onDrag handler and handle the click here
|
||||||
if (!props.node.__rf.isDragging) {
|
if (!props.node.__rf.isDragging) {
|
||||||
if (isSelectable.value && !props.selectNodesOnDrag && !selected.value) {
|
if (isSelectable.value && !props.selectNodesOnDrag && !selected.value) {
|
||||||
store.addSelectedElements([n]);
|
store.addSelectedElements([n])
|
||||||
}
|
}
|
||||||
|
|
||||||
hooks.nodeClick.trigger({ event, node: n });
|
hooks.nodeClick.trigger({ event, node: n })
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
store.updateNodePosDiff({
|
store.updateNodePosDiff({
|
||||||
id: n.id || '',
|
id: n.id || '',
|
||||||
isDragging: false
|
isDragging: false
|
||||||
});
|
})
|
||||||
|
|
||||||
hooks.nodeDragStop.trigger({ event, node: n });
|
hooks.nodeDragStop.trigger({ event, node: n })
|
||||||
};
|
}
|
||||||
|
|
||||||
useResizeObserver(nodeElement, (entries) => {
|
useResizeObserver(nodeElement, (entries) => {
|
||||||
const updates: NodeDimensionUpdate[] = entries.map((entry) => ({
|
const updates: NodeDimensionUpdate[] = entries.map((entry) => ({
|
||||||
id: entry.target.getAttribute('data-id') || '',
|
id: entry.target.getAttribute('data-id') || '',
|
||||||
nodeElement: entry.target as HTMLDivElement
|
nodeElement: entry.target as HTMLDivElement
|
||||||
}));
|
}))
|
||||||
|
|
||||||
store.updateNodeDimensions(updates);
|
store.updateNodeDimensions(updates)
|
||||||
});
|
})
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
store.updateNodeDimensions([
|
store.updateNodeDimensions([
|
||||||
@@ -177,8 +177,8 @@ export default defineComponent({
|
|||||||
nodeElement: nodeElement.value,
|
nodeElement: nodeElement.value,
|
||||||
forceUpdate: true
|
forceUpdate: true
|
||||||
}
|
}
|
||||||
]);
|
])
|
||||||
});
|
})
|
||||||
|
|
||||||
return () =>
|
return () =>
|
||||||
props.node.isHidden ? (
|
props.node.isHidden ? (
|
||||||
@@ -231,6 +231,6 @@ export default defineComponent({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</DraggableCore>
|
</DraggableCore>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import Handle from '../../components/Handle';
|
import Handle from '../../components/Handle'
|
||||||
import { NodeProps, NodeType, Position } from '../../types';
|
import { NodeProps, NodeType, Position } from '../../types'
|
||||||
import { defineComponent, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'OutputNode',
|
name: 'OutputNode',
|
||||||
@@ -29,6 +29,6 @@ export default defineComponent({
|
|||||||
{props.data?.label}
|
{props.data?.label}
|
||||||
<Handle type="source" position={props.targetPosition} isConnectable={props.isConnectable} />
|
<Handle type="source" position={props.targetPosition} isConnectable={props.isConnectable} />
|
||||||
</>
|
</>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
}) as NodeType;
|
}) as NodeType
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { HandleElement, Position } from '../../types';
|
import { HandleElement, Position } from '../../types'
|
||||||
import { getDimensions } from '../../utils';
|
import { getDimensions } from '../../utils'
|
||||||
|
|
||||||
export const getHandleBounds = (nodeElement: HTMLDivElement, scale: number) => {
|
export const getHandleBounds = (nodeElement: HTMLDivElement, scale: number) => {
|
||||||
const bounds = nodeElement.getBoundingClientRect();
|
const bounds = nodeElement.getBoundingClientRect()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
source: getHandleBoundsByHandleType('.source', nodeElement, bounds, scale),
|
source: getHandleBoundsByHandleType('.source', nodeElement, bounds, scale),
|
||||||
target: getHandleBoundsByHandleType('.target', nodeElement, bounds, scale)
|
target: getHandleBoundsByHandleType('.target', nodeElement, bounds, scale)
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getHandleBoundsByHandleType = (
|
export const getHandleBoundsByHandleType = (
|
||||||
selector: string,
|
selector: string,
|
||||||
@@ -16,19 +16,19 @@ export const getHandleBoundsByHandleType = (
|
|||||||
parentBounds: ClientRect | DOMRect,
|
parentBounds: ClientRect | DOMRect,
|
||||||
k: number
|
k: number
|
||||||
): HandleElement[] | null => {
|
): HandleElement[] | null => {
|
||||||
const handles = nodeElement.querySelectorAll(selector);
|
const handles = nodeElement.querySelectorAll(selector)
|
||||||
|
|
||||||
if (!handles || !handles.length) {
|
if (!handles || !handles.length) {
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
const handlesArray = Array.from(handles) as HTMLDivElement[];
|
const handlesArray = Array.from(handles) as HTMLDivElement[]
|
||||||
|
|
||||||
return handlesArray.map((handle): HandleElement => {
|
return handlesArray.map((handle): HandleElement => {
|
||||||
const bounds = handle.getBoundingClientRect();
|
const bounds = handle.getBoundingClientRect()
|
||||||
const dimensions = getDimensions(handle);
|
const dimensions = getDimensions(handle)
|
||||||
const handleId = handle.getAttribute('data-handleid');
|
const handleId = handle.getAttribute('data-handleid')
|
||||||
const handlePosition = handle.getAttribute('data-handlepos') as unknown as Position;
|
const handlePosition = handle.getAttribute('data-handlepos') as unknown as Position
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: handleId,
|
id: handleId,
|
||||||
@@ -36,6 +36,6 @@ export const getHandleBoundsByHandleType = (
|
|||||||
x: (bounds.left - parentBounds.left) / k,
|
x: (bounds.left - parentBounds.left) / k,
|
||||||
y: (bounds.top - parentBounds.top) / k,
|
y: (bounds.top - parentBounds.top) / k,
|
||||||
...dimensions
|
...dimensions
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -3,56 +3,56 @@
|
|||||||
* The nodes selection rectangle gets displayed when a user
|
* The nodes selection rectangle gets displayed when a user
|
||||||
* made a selectio with on or several nodes
|
* made a selectio with on or several nodes
|
||||||
*/
|
*/
|
||||||
import { computed, defineComponent, inject } from 'vue';
|
import { computed, defineComponent, inject } from 'vue'
|
||||||
import { Draggable, DraggableEventListener } from '@braks/revue-draggable';
|
import { Draggable, DraggableEventListener } from '@braks/revue-draggable'
|
||||||
import { isNode } from '../../utils/graph';
|
import { isNode } from '../../utils/graph'
|
||||||
import { Node, RevueFlowStore } from '../../types';
|
import { Node, RevueFlowStore } from '../../types'
|
||||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||||
|
|
||||||
export interface NodesSelectionProps {
|
export interface NodesSelectionProps {
|
||||||
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void;
|
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void
|
||||||
onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void;
|
onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void
|
||||||
onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void;
|
onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void
|
||||||
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
|
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const NodesSelection = defineComponent({
|
const NodesSelection = defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||||
const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number]);
|
const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number])
|
||||||
const transform = computed(() => store.transform);
|
const transform = computed(() => store.transform)
|
||||||
|
|
||||||
const selectedNodes = computed(() =>
|
const selectedNodes = computed(() =>
|
||||||
store.selectedElements
|
store.selectedElements
|
||||||
? store.selectedElements.filter(isNode).map((selectedNode) => {
|
? store.selectedElements.filter(isNode).map((selectedNode) => {
|
||||||
const matchingNode = store.nodes.find((node) => node.id === selectedNode.id);
|
const matchingNode = store.nodes.find((node) => node.id === selectedNode.id)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...matchingNode,
|
...matchingNode,
|
||||||
position: matchingNode?.__rf.position
|
position: matchingNode?.__rf.position
|
||||||
} as Node;
|
} as Node
|
||||||
})
|
})
|
||||||
: []
|
: []
|
||||||
);
|
)
|
||||||
|
|
||||||
const style = computed(() => ({
|
const style = computed(() => ({
|
||||||
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
|
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
|
||||||
}));
|
}))
|
||||||
|
|
||||||
const innerStyle = computed(() => ({
|
const innerStyle = computed(() => ({
|
||||||
width: `${store.selectedNodesBbox.width}px`,
|
width: `${store.selectedNodesBbox.width}px`,
|
||||||
height: `${store.selectedNodesBbox.height}px`,
|
height: `${store.selectedNodesBbox.height}px`,
|
||||||
top: `${store.selectedNodesBbox.y}px`,
|
top: `${store.selectedNodesBbox.y}px`,
|
||||||
left: `${store.selectedNodesBbox.x}px`
|
left: `${store.selectedNodesBbox.x}px`
|
||||||
}));
|
}))
|
||||||
|
|
||||||
const onStart: DraggableEventListener = ({ event }) => {
|
const onStart: DraggableEventListener = ({ event }) => {
|
||||||
hooks.selectionDragStart.trigger({ event, nodes: selectedNodes.value });
|
hooks.selectionDragStart.trigger({ event, nodes: selectedNodes.value })
|
||||||
};
|
}
|
||||||
|
|
||||||
const onDrag: DraggableEventListener = ({ event, data }) => {
|
const onDrag: DraggableEventListener = ({ event, data }) => {
|
||||||
hooks.selectionDrag.trigger({ event, nodes: selectedNodes.value });
|
hooks.selectionDrag.trigger({ event, nodes: selectedNodes.value })
|
||||||
|
|
||||||
store.updateNodePosDiff({
|
store.updateNodePosDiff({
|
||||||
diff: {
|
diff: {
|
||||||
@@ -60,24 +60,24 @@ const NodesSelection = defineComponent({
|
|||||||
y: data.deltaY
|
y: data.deltaY
|
||||||
},
|
},
|
||||||
isDragging: true
|
isDragging: true
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const onStop: DraggableEventListener = ({ event }) => {
|
const onStop: DraggableEventListener = ({ event }) => {
|
||||||
store.updateNodePosDiff({
|
store.updateNodePosDiff({
|
||||||
isDragging: false
|
isDragging: false
|
||||||
});
|
})
|
||||||
|
|
||||||
hooks.selectionDragStop.trigger({ event, nodes: selectedNodes.value });
|
hooks.selectionDragStop.trigger({ event, nodes: selectedNodes.value })
|
||||||
};
|
}
|
||||||
|
|
||||||
const onContextMenu = (event: MouseEvent) => {
|
const onContextMenu = (event: MouseEvent) => {
|
||||||
const selectedNodes: Node[] = store.selectedElements
|
const selectedNodes: Node[] = store.selectedElements
|
||||||
? store.selectedElements.filter(isNode).map((selectedNode) => store.nodes.find((node) => node.id === selectedNode.id))
|
? store.selectedElements.filter(isNode).map((selectedNode) => store.nodes.find((node) => node.id === selectedNode.id))
|
||||||
: [];
|
: []
|
||||||
|
|
||||||
hooks.selectionContextMenu.trigger({ event, nodes: selectedNodes });
|
hooks.selectionContextMenu.trigger({ event, nodes: selectedNodes })
|
||||||
};
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
return !store.selectedElements || store.selectionActive ? (
|
return !store.selectedElements || store.selectionActive ? (
|
||||||
@@ -95,9 +95,9 @@ const NodesSelection = defineComponent({
|
|||||||
<div class="revue-flow__nodesselection-rect" onContextmenu={onContextMenu} style={innerStyle.value} />
|
<div class="revue-flow__nodesselection-rect" onContextmenu={onContextMenu} style={innerStyle.value} />
|
||||||
</Draggable>
|
</Draggable>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
export default NodesSelection;
|
export default NodesSelection
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
/**
|
/**
|
||||||
* The user selection rectangle gets displayed when a user drags the mouse while pressing shift
|
* The user selection rectangle gets displayed when a user drags the mouse while pressing shift
|
||||||
*/
|
*/
|
||||||
import { RevueFlowStore, XYPosition } from '../../types';
|
import { RevueFlowStore, XYPosition } from '../../types'
|
||||||
import { computed, defineComponent, inject } from 'vue';
|
import { computed, defineComponent, inject } from 'vue'
|
||||||
import { templateRef, useEventListener } from '@vueuse/core';
|
import { templateRef, useEventListener } from '@vueuse/core'
|
||||||
|
|
||||||
function getMousePosition(event: MouseEvent): XYPosition | void {
|
function getMousePosition(event: MouseEvent): XYPosition | void {
|
||||||
const revueFlowNode = (event.target as Element).closest('.revue-flow');
|
const revueFlowNode = (event.target as Element).closest('.revue-flow')
|
||||||
if (!revueFlowNode) {
|
if (!revueFlowNode) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const containerBounds = revueFlowNode.getBoundingClientRect();
|
const containerBounds = revueFlowNode.getBoundingClientRect()
|
||||||
|
|
||||||
return {
|
return {
|
||||||
x: event.clientX - containerBounds.left,
|
x: event.clientX - containerBounds.left,
|
||||||
y: event.clientY - containerBounds.top
|
y: event.clientY - containerBounds.top
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const SelectionRect = (props: { width: number; height: number; x: number; y: number }) => {
|
const SelectionRect = (props: { width: number; height: number; x: number; y: number }) => {
|
||||||
@@ -29,51 +29,51 @@ const SelectionRect = (props: { width: number; height: number; x: number; y: num
|
|||||||
transform: `translate(${props.x}px, ${props.y}px)`
|
transform: `translate(${props.x}px, ${props.y}px)`
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { SelectionRect },
|
components: { SelectionRect },
|
||||||
setup() {
|
setup() {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const el = templateRef('user-selection', null);
|
const el = templateRef('user-selection', null)
|
||||||
const shouldRender = computed(() => store.selectionActive || store.elementsSelectable);
|
const shouldRender = computed(() => store.selectionActive || store.elementsSelectable)
|
||||||
|
|
||||||
const onMouseDown = (event: MouseEvent) => {
|
const onMouseDown = (event: MouseEvent) => {
|
||||||
const mousePos = getMousePosition(event);
|
const mousePos = getMousePosition(event)
|
||||||
if (!mousePos) {
|
if (!mousePos) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
store.setUserSelection(mousePos);
|
store.setUserSelection(mousePos)
|
||||||
};
|
}
|
||||||
|
|
||||||
const onMouseMove = (event: MouseEvent) => {
|
const onMouseMove = (event: MouseEvent) => {
|
||||||
if (!store.selectionActive) {
|
if (!store.selectionActive) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
const mousePos = getMousePosition(event);
|
const mousePos = getMousePosition(event)
|
||||||
if (!mousePos) {
|
if (!mousePos) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
store.updateUserSelection(mousePos);
|
store.updateUserSelection(mousePos)
|
||||||
};
|
}
|
||||||
|
|
||||||
const onMouseUp = () => {
|
const onMouseUp = () => {
|
||||||
store.unsetUserSelection();
|
store.unsetUserSelection()
|
||||||
};
|
}
|
||||||
|
|
||||||
const onMouseLeave = () => {
|
const onMouseLeave = () => {
|
||||||
store.unsetUserSelection();
|
store.unsetUserSelection()
|
||||||
store.unsetNodesSelection();
|
store.unsetNodesSelection()
|
||||||
};
|
}
|
||||||
|
|
||||||
useEventListener(el, 'mousedown', onMouseDown);
|
useEventListener(el, 'mousedown', onMouseDown)
|
||||||
useEventListener(el, 'mousemove', onMouseMove);
|
useEventListener(el, 'mousemove', onMouseMove)
|
||||||
useEventListener(el, 'click', onMouseUp);
|
useEventListener(el, 'click', onMouseUp)
|
||||||
useEventListener(el, 'mouseup', onMouseUp);
|
useEventListener(el, 'mouseup', onMouseUp)
|
||||||
useEventListener(el, 'mouseleave', onMouseLeave);
|
useEventListener(el, 'mouseleave', onMouseLeave)
|
||||||
|
|
||||||
return () =>
|
return () =>
|
||||||
shouldRender.value ? (
|
shouldRender.value ? (
|
||||||
@@ -91,6 +91,6 @@ export default defineComponent({
|
|||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
''
|
''
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { defineComponent } from 'vue';
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
interface MarkerProps {
|
interface MarkerProps {
|
||||||
id: string;
|
id: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -25,6 +25,6 @@ export default defineComponent({
|
|||||||
>
|
>
|
||||||
{slots.default ? slots.default() : ''}
|
{slots.default ? slots.default() : ''}
|
||||||
</marker>
|
</marker>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { defineComponent, PropType } from 'vue';
|
import { defineComponent, PropType } from 'vue'
|
||||||
import Marker from './Marker';
|
import Marker from './Marker'
|
||||||
|
|
||||||
interface MarkerDefinitionsProps {
|
interface MarkerDefinitionsProps {
|
||||||
color: string;
|
color: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -38,6 +38,6 @@ export default defineComponent({
|
|||||||
/>
|
/>
|
||||||
</Marker>
|
</Marker>
|
||||||
</defs>
|
</defs>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
import { CSSProperties, defineComponent, inject, PropType } from 'vue';
|
import { CSSProperties, defineComponent, inject, PropType } from 'vue'
|
||||||
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types';
|
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types'
|
||||||
import ConnectionLine from '../../components/ConnectionLine';
|
import ConnectionLine from '../../components/ConnectionLine'
|
||||||
import MarkerDefinitions from './MarkerDefinitions';
|
import MarkerDefinitions from './MarkerDefinitions'
|
||||||
import Edge from '../../components/Edges/Edge';
|
import Edge from '../../components/Edges/Edge'
|
||||||
|
|
||||||
interface EdgeRendererProps {
|
interface EdgeRendererProps {
|
||||||
edgeTypes: any;
|
edgeTypes: any
|
||||||
connectionLineType: ConnectionLineType;
|
connectionLineType: ConnectionLineType
|
||||||
connectionLineStyle?: CSSProperties;
|
connectionLineStyle?: CSSProperties
|
||||||
connectionLineComponent?: ConnectionLineComponent;
|
connectionLineComponent?: ConnectionLineComponent
|
||||||
connectionMode?: ConnectionMode;
|
connectionMode?: ConnectionMode
|
||||||
arrowHeadColor: string;
|
arrowHeadColor: string
|
||||||
markerEndId?: string;
|
markerEndId?: string
|
||||||
onlyRenderVisibleElements: boolean;
|
onlyRenderVisibleElements: boolean
|
||||||
edgeUpdaterRadius?: number;
|
edgeUpdaterRadius?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -70,7 +70,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<svg width={store.width} height={store.height} class="revue-flow__edges">
|
<svg width={store.width} height={store.height} class="revue-flow__edges">
|
||||||
@@ -94,6 +94,6 @@ export default defineComponent({
|
|||||||
)}
|
)}
|
||||||
</g>
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges';
|
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges'
|
||||||
import { rectToBox } from '../../utils/graph';
|
import { rectToBox } from '../../utils/graph'
|
||||||
import { Position, Node, XYPosition, ElementId, HandleElement, Transform, Edge, EdgeType } from '../../types';
|
import { Position, Node, XYPosition, ElementId, HandleElement, Transform, Edge, EdgeType } from '../../types'
|
||||||
|
|
||||||
export function createEdgeTypes(edgeTypes: Record<string, EdgeType>): Record<string, EdgeType> {
|
export function createEdgeTypes(edgeTypes: Record<string, EdgeType>): Record<string, EdgeType> {
|
||||||
const standardTypes: Record<string, EdgeType> = {
|
const standardTypes: Record<string, EdgeType> = {
|
||||||
@@ -8,75 +8,75 @@ export function createEdgeTypes(edgeTypes: Record<string, EdgeType>): Record<str
|
|||||||
straight: edgeTypes.bezier || StraightEdge,
|
straight: edgeTypes.bezier || StraightEdge,
|
||||||
step: edgeTypes.step || StepEdge,
|
step: edgeTypes.step || StepEdge,
|
||||||
smoothstep: edgeTypes.step || SmoothStepEdge
|
smoothstep: edgeTypes.step || SmoothStepEdge
|
||||||
};
|
}
|
||||||
|
|
||||||
const wrappedTypes = {} as Record<string, EdgeType>;
|
const wrappedTypes = {} as Record<string, EdgeType>
|
||||||
const specialTypes: Record<string, EdgeType> = Object.keys(edgeTypes)
|
const specialTypes: Record<string, EdgeType> = Object.keys(edgeTypes)
|
||||||
.filter((k) => !['default', 'bezier'].includes(k))
|
.filter((k) => !['default', 'bezier'].includes(k))
|
||||||
.reduce((res, key) => {
|
.reduce((res, key) => {
|
||||||
res[key] = edgeTypes[key] || BezierEdge;
|
res[key] = edgeTypes[key] || BezierEdge
|
||||||
|
|
||||||
return res;
|
return res
|
||||||
}, wrappedTypes);
|
}, wrappedTypes)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...standardTypes,
|
...standardTypes,
|
||||||
...specialTypes
|
...specialTypes
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHandlePosition(position: Position, node: Node, handle: any | null = null): XYPosition {
|
export function getHandlePosition(position: Position, node: Node, handle: any | null = null): XYPosition {
|
||||||
const x = (handle?.x || 0) + node.__rf.position.x;
|
const x = (handle?.x || 0) + node.__rf.position.x
|
||||||
const y = (handle?.y || 0) + node.__rf.position.y;
|
const y = (handle?.y || 0) + node.__rf.position.y
|
||||||
const width = handle?.width || node.__rf.width;
|
const width = handle?.width || node.__rf.width
|
||||||
const height = handle?.height || node.__rf.height;
|
const height = handle?.height || node.__rf.height
|
||||||
|
|
||||||
switch (position) {
|
switch (position) {
|
||||||
case Position.Top:
|
case Position.Top:
|
||||||
return {
|
return {
|
||||||
x: x + width / 2,
|
x: x + width / 2,
|
||||||
y
|
y
|
||||||
};
|
}
|
||||||
case Position.Right:
|
case Position.Right:
|
||||||
return {
|
return {
|
||||||
x: x + width,
|
x: x + width,
|
||||||
y: y + height / 2
|
y: y + height / 2
|
||||||
};
|
}
|
||||||
case Position.Bottom:
|
case Position.Bottom:
|
||||||
return {
|
return {
|
||||||
x: x + width / 2,
|
x: x + width / 2,
|
||||||
y: y + height
|
y: y + height
|
||||||
};
|
}
|
||||||
case Position.Left:
|
case Position.Left:
|
||||||
return {
|
return {
|
||||||
x,
|
x,
|
||||||
y: y + height / 2
|
y: y + height / 2
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHandle(bounds: HandleElement[], handleId: ElementId | null): HandleElement | null {
|
export function getHandle(bounds: HandleElement[], handleId: ElementId | null): HandleElement | null {
|
||||||
if (!bounds) {
|
if (!bounds) {
|
||||||
return null;
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// there is no handleId when there are no multiple handles/ handles with ids
|
// there is no handleId when there are no multiple handles/ handles with ids
|
||||||
// so we just pick the first one
|
// so we just pick the first one
|
||||||
let handle = null;
|
let handle = null
|
||||||
if (bounds.length === 1 || !handleId) {
|
if (bounds.length === 1 || !handleId) {
|
||||||
handle = bounds[0];
|
handle = bounds[0]
|
||||||
} else if (handleId) {
|
} else if (handleId) {
|
||||||
handle = bounds.find((d) => d.id === handleId);
|
handle = bounds.find((d) => d.id === handleId)
|
||||||
}
|
}
|
||||||
|
|
||||||
return typeof handle === 'undefined' ? null : handle;
|
return typeof handle === 'undefined' ? null : handle
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EdgePositions {
|
interface EdgePositions {
|
||||||
sourceX: number;
|
sourceX: number
|
||||||
sourceY: number;
|
sourceY: number
|
||||||
targetX: number;
|
targetX: number
|
||||||
targetY: number;
|
targetY: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getEdgePositions = (
|
export const getEdgePositions = (
|
||||||
@@ -87,23 +87,23 @@ export const getEdgePositions = (
|
|||||||
targetHandle: HandleElement | unknown,
|
targetHandle: HandleElement | unknown,
|
||||||
targetPosition: Position
|
targetPosition: Position
|
||||||
): EdgePositions => {
|
): EdgePositions => {
|
||||||
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNode, sourceHandle);
|
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNode, sourceHandle)
|
||||||
const targetHandlePos = getHandlePosition(targetPosition, targetNode, targetHandle);
|
const targetHandlePos = getHandlePosition(targetPosition, targetNode, targetHandle)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
sourceX: sourceHandlePos.x,
|
sourceX: sourceHandlePos.x,
|
||||||
sourceY: sourceHandlePos.y,
|
sourceY: sourceHandlePos.y,
|
||||||
targetX: targetHandlePos.x,
|
targetX: targetHandlePos.x,
|
||||||
targetY: targetHandlePos.y
|
targetY: targetHandlePos.y
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
interface IsEdgeVisibleParams {
|
interface IsEdgeVisibleParams {
|
||||||
sourcePos: XYPosition;
|
sourcePos: XYPosition
|
||||||
targetPos: XYPosition;
|
targetPos: XYPosition
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
transform: Transform;
|
transform: Transform
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }: IsEdgeVisibleParams): boolean {
|
export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }: IsEdgeVisibleParams): boolean {
|
||||||
@@ -112,14 +112,14 @@ export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }
|
|||||||
y: Math.min(sourcePos.y, targetPos.y),
|
y: Math.min(sourcePos.y, targetPos.y),
|
||||||
x2: Math.max(sourcePos.x, targetPos.x),
|
x2: Math.max(sourcePos.x, targetPos.x),
|
||||||
y2: Math.max(sourcePos.y, targetPos.y)
|
y2: Math.max(sourcePos.y, targetPos.y)
|
||||||
};
|
}
|
||||||
|
|
||||||
if (edgeBox.x === edgeBox.x2) {
|
if (edgeBox.x === edgeBox.x2) {
|
||||||
edgeBox.x2 += 1;
|
edgeBox.x2 += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edgeBox.y === edgeBox.y2) {
|
if (edgeBox.y === edgeBox.y2) {
|
||||||
edgeBox.y2 += 1;
|
edgeBox.y2 += 1
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewBox = rectToBox({
|
const viewBox = rectToBox({
|
||||||
@@ -127,31 +127,31 @@ export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }
|
|||||||
y: (0 - transform[1]) / transform[2],
|
y: (0 - transform[1]) / transform[2],
|
||||||
width: width / transform[2],
|
width: width / transform[2],
|
||||||
height: height / transform[2]
|
height: height / transform[2]
|
||||||
});
|
})
|
||||||
|
|
||||||
const xOverlap = Math.max(0, Math.min(viewBox.x2, edgeBox.x2) - Math.max(viewBox.x, edgeBox.x));
|
const xOverlap = Math.max(0, Math.min(viewBox.x2, edgeBox.x2) - Math.max(viewBox.x, edgeBox.x))
|
||||||
const yOverlap = Math.max(0, Math.min(viewBox.y2, edgeBox.y2) - Math.max(viewBox.y, edgeBox.y));
|
const yOverlap = Math.max(0, Math.min(viewBox.y2, edgeBox.y2) - Math.max(viewBox.y, edgeBox.y))
|
||||||
const overlappingArea = Math.ceil(xOverlap * yOverlap);
|
const overlappingArea = Math.ceil(xOverlap * yOverlap)
|
||||||
|
|
||||||
return overlappingArea > 0;
|
return overlappingArea > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
type SourceTargetNode = {
|
type SourceTargetNode = {
|
||||||
sourceNode: Node | null;
|
sourceNode: Node | null
|
||||||
targetNode: Node | null;
|
targetNode: Node | null
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getSourceTargetNodes = (edge: Edge, nodes: Node[]): SourceTargetNode => {
|
export const getSourceTargetNodes = (edge: Edge, nodes: Node[]): SourceTargetNode => {
|
||||||
return nodes.reduce(
|
return nodes.reduce(
|
||||||
(res, node) => {
|
(res, node) => {
|
||||||
if (node.id === edge.source) {
|
if (node.id === edge.source) {
|
||||||
res.sourceNode = node;
|
res.sourceNode = node
|
||||||
}
|
}
|
||||||
if (node.id === edge.target) {
|
if (node.id === edge.target) {
|
||||||
res.targetNode = node;
|
res.targetNode = node
|
||||||
}
|
}
|
||||||
return res;
|
return res
|
||||||
},
|
},
|
||||||
{ sourceNode: null, targetNode: null } as SourceTargetNode
|
{ sourceNode: null, targetNode: null } as SourceTargetNode
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { computed, defineComponent, inject, PropType } from 'vue';
|
import { computed, defineComponent, inject, PropType } from 'vue'
|
||||||
import ZoomPane from '../ZoomPane';
|
import ZoomPane from '../ZoomPane'
|
||||||
import UserSelection from '../../components/UserSelection';
|
import UserSelection from '../../components/UserSelection'
|
||||||
import NodesSelection from '../../components/NodesSelection';
|
import NodesSelection from '../../components/NodesSelection'
|
||||||
import { RevueFlowStore, GraphViewProps } from '../../types';
|
import { RevueFlowStore, GraphViewProps } from '../../types'
|
||||||
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
|
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler'
|
||||||
import useKeyPress from '../../hooks/useKeyPress';
|
import useKeyPress from '../../hooks/useKeyPress'
|
||||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||||
|
|
||||||
type FlowRendererProps = Omit<
|
type FlowRendererProps = Omit<
|
||||||
GraphViewProps,
|
GraphViewProps,
|
||||||
@@ -18,7 +18,7 @@ type FlowRendererProps = Omit<
|
|||||||
| 'arrowHeadColor'
|
| 'arrowHeadColor'
|
||||||
| 'onlyRenderVisibleElements'
|
| 'onlyRenderVisibleElements'
|
||||||
| 'selectNodesOnDrag'
|
| 'selectNodesOnDrag'
|
||||||
>;
|
>
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'FlowRenderer',
|
name: 'FlowRenderer',
|
||||||
@@ -101,28 +101,28 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||||
const keyPressed = useKeyPress(props.selectionKeyCode);
|
const keyPressed = useKeyPress(props.selectionKeyCode)
|
||||||
|
|
||||||
useGlobalKeyHandler({
|
useGlobalKeyHandler({
|
||||||
store,
|
store,
|
||||||
onElementsRemove: hooks.elementsRemove.trigger,
|
onElementsRemove: hooks.elementsRemove.trigger,
|
||||||
deleteKeyCode: props.deleteKeyCode || '',
|
deleteKeyCode: props.deleteKeyCode || '',
|
||||||
multiSelectionKeyCode: props.multiSelectionKeyCode || ''
|
multiSelectionKeyCode: props.multiSelectionKeyCode || ''
|
||||||
});
|
})
|
||||||
|
|
||||||
const onClick = (event: MouseEvent) => {
|
const onClick = (event: MouseEvent) => {
|
||||||
hooks.paneClick.trigger(event);
|
hooks.paneClick.trigger(event)
|
||||||
store.unsetNodesSelection();
|
store.unsetNodesSelection()
|
||||||
store.resetSelectedElements();
|
store.resetSelectedElements()
|
||||||
};
|
}
|
||||||
|
|
||||||
const onContextMenu = (event: MouseEvent) => hooks.paneContextMenu.trigger(event);
|
const onContextMenu = (event: MouseEvent) => hooks.paneContextMenu.trigger(event)
|
||||||
|
|
||||||
const onWheel = (event: WheelEvent) => hooks.paneScroll.trigger(event);
|
const onWheel = (event: WheelEvent) => hooks.paneScroll.trigger(event)
|
||||||
|
|
||||||
const selectionActive = computed(() => store.nodesSelectionActive);
|
const selectionActive = computed(() => store.nodesSelectionActive)
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<ZoomPane
|
<ZoomPane
|
||||||
@@ -145,6 +145,6 @@ export default defineComponent({
|
|||||||
{selectionActive.value ? <NodesSelection /> : ''}
|
{selectionActive.value ? <NodesSelection /> : ''}
|
||||||
<div class="revue-flow__pane" onClick={onClick} onContextmenu={onContextMenu} onWheel={onWheel} />
|
<div class="revue-flow__pane" onClick={onClick} onContextmenu={onContextMenu} onWheel={onWheel} />
|
||||||
</ZoomPane>
|
</ZoomPane>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { defineComponent, inject, PropType } from 'vue';
|
import { defineComponent, inject, PropType } from 'vue'
|
||||||
import FlowRenderer from '../FlowRenderer';
|
import FlowRenderer from '../FlowRenderer'
|
||||||
import NodeRenderer from '../NodeRenderer';
|
import NodeRenderer from '../NodeRenderer'
|
||||||
import EdgeRenderer from '../EdgeRenderer';
|
import EdgeRenderer from '../EdgeRenderer'
|
||||||
import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph';
|
import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph'
|
||||||
import { GraphViewProps, RevueFlowStore } from '../../types';
|
import { GraphViewProps, RevueFlowStore } from '../../types'
|
||||||
import useZoomPanHelper from '../../hooks/useZoomPanHelper';
|
import useZoomPanHelper from '../../hooks/useZoomPanHelper'
|
||||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||||
import { tryOnMounted } from '@vueuse/core';
|
import { tryOnMounted } from '@vueuse/core'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'GraphView',
|
name: 'GraphView',
|
||||||
@@ -177,10 +177,10 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||||
|
|
||||||
const { onReady } = useZoomPanHelper();
|
const { onReady } = useZoomPanHelper()
|
||||||
onReady(({ zoomIn, zoomOut, zoomTo, transform, fitView }) => {
|
onReady(({ zoomIn, zoomOut, zoomTo, transform, fitView }) => {
|
||||||
hooks.load.trigger({
|
hooks.load.trigger({
|
||||||
fitView: (params = { padding: 0.1 }) => fitView(params),
|
fitView: (params = { padding: 0.1 }) => fitView(params),
|
||||||
@@ -191,52 +191,52 @@ export default defineComponent({
|
|||||||
project: onLoadProject(store),
|
project: onLoadProject(store),
|
||||||
getElements: onLoadGetElements(store),
|
getElements: onLoadGetElements(store),
|
||||||
toObject: onLoadToObject(store)
|
toObject: onLoadToObject(store)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
if (typeof props.snapToGrid !== 'undefined') {
|
if (typeof props.snapToGrid !== 'undefined') {
|
||||||
store.snapToGrid = props.snapToGrid;
|
store.snapToGrid = props.snapToGrid
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof props.snapGrid !== 'undefined') {
|
if (typeof props.snapGrid !== 'undefined') {
|
||||||
store.snapGrid = props.snapGrid;
|
store.snapGrid = props.snapGrid
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof props.minZoom !== 'undefined') {
|
if (typeof props.minZoom !== 'undefined') {
|
||||||
store.setMinZoom(props.minZoom);
|
store.setMinZoom(props.minZoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof props.maxZoom !== 'undefined') {
|
if (typeof props.maxZoom !== 'undefined') {
|
||||||
store.setMaxZoom(props.maxZoom);
|
store.setMaxZoom(props.maxZoom)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof props.translateExtent !== 'undefined') {
|
if (typeof props.translateExtent !== 'undefined') {
|
||||||
store.setTranslateExtent(props.translateExtent);
|
store.setTranslateExtent(props.translateExtent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof props.nodeExtent !== 'undefined') {
|
if (typeof props.nodeExtent !== 'undefined') {
|
||||||
store.setNodeExtent(props.nodeExtent);
|
store.setNodeExtent(props.nodeExtent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof props.nodesConnectable !== 'undefined') {
|
if (typeof props.nodesConnectable !== 'undefined') {
|
||||||
store.nodesConnectable = !!props.nodesConnectable;
|
store.nodesConnectable = !!props.nodesConnectable
|
||||||
}
|
}
|
||||||
if (typeof props.elementsSelectable !== 'undefined') {
|
if (typeof props.elementsSelectable !== 'undefined') {
|
||||||
store.elementsSelectable = !!props.elementsSelectable;
|
store.elementsSelectable = !!props.elementsSelectable
|
||||||
}
|
}
|
||||||
if (typeof props.nodesDraggable !== 'undefined') {
|
if (typeof props.nodesDraggable !== 'undefined') {
|
||||||
store.nodesDraggable = !!props.nodesDraggable;
|
store.nodesDraggable = !!props.nodesDraggable
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof props.connectionMode !== 'undefined') {
|
if (typeof props.connectionMode !== 'undefined') {
|
||||||
store.connectionMode = props.connectionMode;
|
store.connectionMode = props.connectionMode
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
tryOnMounted(() => {
|
tryOnMounted(() => {
|
||||||
init();
|
init()
|
||||||
});
|
})
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<FlowRenderer
|
<FlowRenderer
|
||||||
@@ -275,6 +275,6 @@ export default defineComponent({
|
|||||||
edgeUpdaterRadius={props.edgeUpdaterRadius}
|
edgeUpdaterRadius={props.edgeUpdaterRadius}
|
||||||
/>
|
/>
|
||||||
</FlowRenderer>
|
</FlowRenderer>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import { defineComponent, inject, PropType } from 'vue';
|
import { defineComponent, inject, PropType } from 'vue'
|
||||||
import Node from '../../components/Nodes/Node';
|
import Node from '../../components/Nodes/Node'
|
||||||
import { getNodesInside } from '../../utils/graph';
|
import { getNodesInside } from '../../utils/graph'
|
||||||
import { NodeType, RevueFlowStore } from '../../types';
|
import { NodeType, RevueFlowStore } from '../../types'
|
||||||
|
|
||||||
interface NodeRendererProps {
|
interface NodeRendererProps {
|
||||||
nodeTypes: Record<string, NodeType>;
|
nodeTypes: Record<string, NodeType>
|
||||||
selectNodesOnDrag: boolean;
|
selectNodesOnDrag: boolean
|
||||||
snapToGrid: boolean;
|
snapToGrid: boolean
|
||||||
snapGrid: [number, number];
|
snapGrid: [number, number]
|
||||||
onlyRenderVisibleElements: boolean;
|
onlyRenderVisibleElements: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -42,7 +42,7 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
|
|
||||||
const getNodes = () => {
|
const getNodes = () => {
|
||||||
return props.onlyRenderVisibleElements
|
return props.onlyRenderVisibleElements
|
||||||
@@ -58,30 +58,8 @@ export default defineComponent({
|
|||||||
store.transform,
|
store.transform,
|
||||||
true
|
true
|
||||||
)
|
)
|
||||||
: store.nodes;
|
: store.nodes
|
||||||
};
|
}
|
||||||
|
|
||||||
const visibleNodes = () =>
|
|
||||||
getNodes().map((node, i) => {
|
|
||||||
const nodeType = node.type || 'default';
|
|
||||||
if (props.nodeTypes) {
|
|
||||||
const type = props.nodeTypes[nodeType] || props.nodeTypes.default;
|
|
||||||
if (!props.nodeTypes[nodeType]) {
|
|
||||||
console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Node
|
|
||||||
node={node}
|
|
||||||
snapGrid={props.snapGrid}
|
|
||||||
snapToGrid={props.snapToGrid}
|
|
||||||
selectNodesOnDrag={props.selectNodesOnDrag}
|
|
||||||
type={type}
|
|
||||||
key={node.id + i}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
@@ -90,8 +68,27 @@ export default defineComponent({
|
|||||||
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
|
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{visibleNodes()}
|
{getNodes().map((node, i) => {
|
||||||
|
const nodeType = node.type || 'default'
|
||||||
|
if (props.nodeTypes) {
|
||||||
|
const type = props.nodeTypes[nodeType] || props.nodeTypes.default
|
||||||
|
if (!props.nodeTypes[nodeType]) {
|
||||||
|
console.warn(`Node type "${nodeType}" not found. Using fallback type "default".`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Node
|
||||||
|
node={node}
|
||||||
|
snapGrid={props.snapGrid}
|
||||||
|
snapToGrid={props.snapToGrid}
|
||||||
|
selectNodesOnDrag={props.selectNodesOnDrag}
|
||||||
|
type={type}
|
||||||
|
key={node.id + i}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
import DefaultNode from '../../components/Nodes/DefaultNode';
|
import DefaultNode from '../../components/Nodes/DefaultNode'
|
||||||
import InputNode from '../../components/Nodes/InputNode';
|
import InputNode from '../../components/Nodes/InputNode'
|
||||||
import OutputNode from '../../components/Nodes/OutputNode';
|
import OutputNode from '../../components/Nodes/OutputNode'
|
||||||
import { NodeType } from '../../types';
|
import { NodeType } from '../../types'
|
||||||
|
|
||||||
export function createNodeTypes(nodeTypes: Record<string, NodeType>): Record<string, NodeType> {
|
export function createNodeTypes(nodeTypes: Record<string, NodeType>): Record<string, NodeType> {
|
||||||
const standardTypes: Record<string, NodeType> = {
|
const standardTypes: Record<string, NodeType> = {
|
||||||
input: nodeTypes.input || InputNode,
|
input: nodeTypes.input || InputNode,
|
||||||
default: nodeTypes.default || DefaultNode,
|
default: nodeTypes.default || DefaultNode,
|
||||||
output: nodeTypes.output || OutputNode
|
output: nodeTypes.output || OutputNode
|
||||||
};
|
}
|
||||||
|
|
||||||
const wrappedTypes = {} as NodeType;
|
const wrappedTypes = {} as NodeType
|
||||||
const specialTypes: NodeType = Object.keys(nodeTypes)
|
const specialTypes: NodeType = Object.keys(nodeTypes)
|
||||||
.filter((k) => !['input', 'default', 'output'].includes(k))
|
.filter((k) => !['input', 'default', 'output'].includes(k))
|
||||||
.reduce((res, key) => {
|
.reduce((res, key) => {
|
||||||
res[key] = nodeTypes[key] || DefaultNode;
|
res[key] = nodeTypes[key] || DefaultNode
|
||||||
|
|
||||||
return res;
|
return res
|
||||||
}, wrappedTypes);
|
}, wrappedTypes)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...standardTypes,
|
...standardTypes,
|
||||||
...specialTypes
|
...specialTypes
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,30 @@
|
|||||||
import { defineComponent, onBeforeUnmount, onUpdated, PropType, provide, watch } from 'vue';
|
import { PropType } from 'vue'
|
||||||
import { useVModel } from '@vueuse/core';
|
import '../../style.css'
|
||||||
import '../../style.css';
|
import '../../theme-default.css'
|
||||||
import '../../theme-default.css';
|
import GraphView from '../GraphView'
|
||||||
import GraphView from '../GraphView';
|
import DefaultNode from '../../components/Nodes/DefaultNode'
|
||||||
import DefaultNode from '../../components/Nodes/DefaultNode';
|
import InputNode from '../../components/Nodes/InputNode'
|
||||||
import InputNode from '../../components/Nodes/InputNode';
|
import OutputNode from '../../components/Nodes/OutputNode'
|
||||||
import OutputNode from '../../components/Nodes/OutputNode';
|
import { createNodeTypes } from '../NodeRenderer/utils'
|
||||||
import { createNodeTypes } from '../NodeRenderer/utils';
|
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges'
|
||||||
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges';
|
import { createEdgeTypes } from '../EdgeRenderer/utils'
|
||||||
import { createEdgeTypes } from '../EdgeRenderer/utils';
|
import { ConnectionMode, ConnectionLineType, PanOnScrollMode, RevueFlowStore, RevueFlowProps } from '../../types'
|
||||||
import { ConnectionMode, ConnectionLineType, PanOnScrollMode, RevueFlowStore, RevueFlowProps } from '../../types';
|
import { initialState } from '../../store'
|
||||||
import { initialState } from '../../store';
|
import configureStore from '../../store/configure-store'
|
||||||
import configureStore from '../../store/configure-store';
|
import { RevueFlowHooks, useRevueFlow } from '../../hooks/RevueFlowHooks'
|
||||||
import { RevueFlowHooks, useRevueFlow } from '../../hooks/RevueFlowHooks';
|
|
||||||
|
|
||||||
const defaultNodeTypes = {
|
const defaultNodeTypes = {
|
||||||
input: InputNode,
|
input: InputNode,
|
||||||
default: DefaultNode,
|
default: DefaultNode,
|
||||||
output: OutputNode
|
output: OutputNode
|
||||||
};
|
}
|
||||||
|
|
||||||
const defaultEdgeTypes = {
|
const defaultEdgeTypes = {
|
||||||
default: BezierEdge,
|
default: BezierEdge,
|
||||||
straight: StraightEdge,
|
straight: StraightEdge,
|
||||||
step: StepEdge,
|
step: StepEdge,
|
||||||
smoothstep: SmoothStepEdge
|
smoothstep: SmoothStepEdge
|
||||||
};
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'RevueFlow',
|
name: 'RevueFlow',
|
||||||
@@ -220,25 +219,25 @@ export default defineComponent({
|
|||||||
},
|
},
|
||||||
emits: Object.keys(useRevueFlow()),
|
emits: Object.keys(useRevueFlow()),
|
||||||
setup(props, { emit, slots }) {
|
setup(props, { emit, slots }) {
|
||||||
const store = configureStore(initialState)();
|
const store = configureStore(initialState)()
|
||||||
provide<RevueFlowStore>('store', store);
|
provide<RevueFlowStore>('store', store)
|
||||||
const hooks = useRevueFlow().bind(emit);
|
const hooks = useRevueFlow().bind(emit)
|
||||||
provide<RevueFlowHooks>('hooks', hooks);
|
provide<RevueFlowHooks>('hooks', hooks)
|
||||||
const elements = useVModel(props, 'modelValue', emit);
|
const elements = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
store.setElements(elements.value);
|
store.setElements(elements.value)
|
||||||
watch(elements.value, () => {
|
watch(elements.value, () => {
|
||||||
store.setElements(elements.value);
|
store.setElements(elements.value)
|
||||||
});
|
})
|
||||||
|
|
||||||
onUpdated(() => {
|
onUpdated(() => {
|
||||||
store.setElements(elements.value);
|
store.setElements(elements.value)
|
||||||
});
|
})
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
store.$reset();
|
store.$reset()
|
||||||
store.setElements([]);
|
store.setElements([])
|
||||||
});
|
})
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div class="revue-flow">
|
<div class="revue-flow">
|
||||||
@@ -279,6 +278,6 @@ export default defineComponent({
|
|||||||
/>
|
/>
|
||||||
{slots.default ? slots.default() : ''}
|
{slots.default ? slots.default() : ''}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom';
|
import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom'
|
||||||
import { select, pointer } from 'd3-selection';
|
import { select, pointer } from 'd3-selection'
|
||||||
import { clamp } from '../../utils';
|
import { clamp } from '../../utils'
|
||||||
import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode, RevueFlowStore } from '../../types';
|
import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode, RevueFlowStore } from '../../types'
|
||||||
import { defineComponent, inject, PropType, ref, watchPostEffect } from 'vue';
|
import { defineComponent, inject, PropType, ref, watchPostEffect } from 'vue'
|
||||||
import useKeyPress from '../../hooks/useKeyPress';
|
import useKeyPress from '../../hooks/useKeyPress'
|
||||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
import useResizeHandler from '../../hooks/useResizeHandler'
|
||||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||||
import { templateRef } from '@vueuse/core';
|
import { templateRef } from '@vueuse/core'
|
||||||
import { ZoomBehavior } from 'd3';
|
import { ZoomBehavior } from 'd3'
|
||||||
|
|
||||||
interface ZoomPaneProps {
|
interface ZoomPaneProps {
|
||||||
elementsSelectable?: boolean;
|
elementsSelectable?: boolean
|
||||||
zoomOnScroll?: boolean;
|
zoomOnScroll?: boolean
|
||||||
zoomOnPinch?: boolean;
|
zoomOnPinch?: boolean
|
||||||
panOnScroll?: boolean;
|
panOnScroll?: boolean
|
||||||
panOnScrollSpeed?: number;
|
panOnScrollSpeed?: number
|
||||||
panOnScrollMode?: PanOnScrollMode;
|
panOnScrollMode?: PanOnScrollMode
|
||||||
zoomOnDoubleClick?: boolean;
|
zoomOnDoubleClick?: boolean
|
||||||
paneMoveable?: boolean;
|
paneMoveable?: boolean
|
||||||
defaultPosition?: [number, number];
|
defaultPosition?: [number, number]
|
||||||
defaultZoom?: number;
|
defaultZoom?: number
|
||||||
translateExtent?: TranslateExtent;
|
translateExtent?: TranslateExtent
|
||||||
zoomActivationKeyCode?: KeyCode;
|
zoomActivationKeyCode?: KeyCode
|
||||||
selectionKeyCode?: KeyCode;
|
selectionKeyCode?: KeyCode
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
@@ -95,35 +95,35 @@ export default defineComponent({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props, { slots }) {
|
setup(props, { slots }) {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||||
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null);
|
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null)
|
||||||
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 });
|
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 })
|
||||||
|
|
||||||
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
|
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
|
||||||
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k;
|
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k
|
||||||
|
|
||||||
const eventToFlowTransform = (eventTransform: ZoomTransform): FlowTransform => ({
|
const eventToFlowTransform = (eventTransform: ZoomTransform): FlowTransform => ({
|
||||||
x: eventTransform.x,
|
x: eventTransform.x,
|
||||||
y: eventTransform.y,
|
y: eventTransform.y,
|
||||||
zoom: eventTransform.k
|
zoom: eventTransform.k
|
||||||
});
|
})
|
||||||
|
|
||||||
const init = () => {
|
const init = () => {
|
||||||
const { onReady } = useResizeHandler(zoomPane, store.updateSize);
|
const { onReady } = useResizeHandler(zoomPane, store.updateSize)
|
||||||
onReady(() => {
|
onReady(() => {
|
||||||
if (props.defaultPosition && props.defaultZoom) {
|
if (props.defaultPosition && props.defaultZoom) {
|
||||||
const currentTranslateExtent =
|
const currentTranslateExtent =
|
||||||
typeof props.translateExtent !== 'undefined' ? props.translateExtent : store.translateExtent;
|
typeof props.translateExtent !== 'undefined' ? props.translateExtent : store.translateExtent
|
||||||
const d3ZoomInstance = zoom().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(currentTranslateExtent);
|
const d3ZoomInstance = zoom().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(currentTranslateExtent)
|
||||||
const selection = select(zoomPane.value as Element).call(d3ZoomInstance);
|
const selection = select(zoomPane.value as Element).call(d3ZoomInstance)
|
||||||
|
|
||||||
const clampedX = clamp(props.defaultPosition[0], currentTranslateExtent[0][0], currentTranslateExtent[1][0]);
|
const clampedX = clamp(props.defaultPosition[0], currentTranslateExtent[0][0], currentTranslateExtent[1][0])
|
||||||
const clampedY = clamp(props.defaultPosition[1], currentTranslateExtent[0][1], currentTranslateExtent[1][1]);
|
const clampedY = clamp(props.defaultPosition[1], currentTranslateExtent[0][1], currentTranslateExtent[1][1])
|
||||||
const clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom);
|
const clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom)
|
||||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom);
|
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom)
|
||||||
|
|
||||||
d3ZoomInstance.transform(selection, updatedTransform);
|
d3ZoomInstance.transform(selection, updatedTransform)
|
||||||
|
|
||||||
store.initD3Zoom({
|
store.initD3Zoom({
|
||||||
d3Zoom: d3ZoomInstance,
|
d3Zoom: d3ZoomInstance,
|
||||||
@@ -131,106 +131,106 @@ export default defineComponent({
|
|||||||
d3ZoomHandler: selection.on('wheel.zoom'),
|
d3ZoomHandler: selection.on('wheel.zoom'),
|
||||||
// we need to pass transform because zoom handler is not registered when we set the initial transform
|
// we need to pass transform because zoom handler is not registered when we set the initial transform
|
||||||
transform: [clampedX, clampedY, clampedZoom]
|
transform: [clampedX, clampedY, clampedZoom]
|
||||||
});
|
})
|
||||||
applyZoomHandlers(d3ZoomInstance);
|
applyZoomHandlers(d3ZoomInstance)
|
||||||
applyZoomFilter(d3ZoomInstance);
|
applyZoomFilter(d3ZoomInstance)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
const applyZoomHandlers = (d3Zoom: ZoomBehavior<Element, unknown>) => {
|
const applyZoomHandlers = (d3Zoom: ZoomBehavior<Element, unknown>) => {
|
||||||
d3Zoom.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3Zoom.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
if (viewChanged(prevTransform.value, event.transform)) {
|
if (viewChanged(prevTransform.value, event.transform)) {
|
||||||
const flowTransform = eventToFlowTransform(event.transform);
|
const flowTransform = eventToFlowTransform(event.transform)
|
||||||
prevTransform.value = flowTransform;
|
prevTransform.value = flowTransform
|
||||||
|
|
||||||
hooks.moveStart.trigger(flowTransform);
|
hooks.moveStart.trigger(flowTransform)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
d3Zoom.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3Zoom.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
if (viewChanged(prevTransform.value, event.transform)) {
|
if (viewChanged(prevTransform.value, event.transform)) {
|
||||||
const flowTransform = eventToFlowTransform(event.transform);
|
const flowTransform = eventToFlowTransform(event.transform)
|
||||||
prevTransform.value = flowTransform;
|
prevTransform.value = flowTransform
|
||||||
|
|
||||||
hooks.moveEnd.trigger(flowTransform);
|
hooks.moveEnd.trigger(flowTransform)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
useKeyPress(props.selectionKeyCode, (keyPress) => {
|
useKeyPress(props.selectionKeyCode, (keyPress) => {
|
||||||
if (keyPress) {
|
if (keyPress) {
|
||||||
d3Zoom.on('zoom', null);
|
d3Zoom.on('zoom', null)
|
||||||
} else {
|
} else {
|
||||||
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||||
store.transform = [event.transform.x, event.transform.y, event.transform.k];
|
store.transform = [event.transform.x, event.transform.y, event.transform.k]
|
||||||
hooks.move.trigger(eventToFlowTransform(event.transform));
|
hooks.move.trigger(eventToFlowTransform(event.transform))
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
useKeyPress(props.zoomActivationKeyCode, (keyPress) => {
|
useKeyPress(props.zoomActivationKeyCode, (keyPress) => {
|
||||||
if (props.panOnScroll && keyPress) {
|
if (props.panOnScroll && keyPress) {
|
||||||
store.d3Selection
|
store.d3Selection
|
||||||
?.on('wheel', (event: WheelEvent) => {
|
?.on('wheel', (event: WheelEvent) => {
|
||||||
event.preventDefault();
|
event.preventDefault()
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation()
|
||||||
|
|
||||||
const currentZoom = store.d3Selection?.property('__zoom').k || 1;
|
const currentZoom = store.d3Selection?.property('__zoom').k || 1
|
||||||
|
|
||||||
if (event.ctrlKey && props.zoomOnPinch) {
|
if (event.ctrlKey && props.zoomOnPinch) {
|
||||||
const point = pointer(event);
|
const point = pointer(event)
|
||||||
// taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js
|
// taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js
|
||||||
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10;
|
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10
|
||||||
const zoom = currentZoom * Math.pow(2, pinchDelta);
|
const zoom = currentZoom * Math.pow(2, pinchDelta)
|
||||||
if (store.d3Selection) store.d3Zoom?.scaleTo(store.d3Selection, zoom, point);
|
if (store.d3Selection) store.d3Zoom?.scaleTo(store.d3Selection, zoom, point)
|
||||||
|
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// increase scroll speed in firefox
|
// increase scroll speed in firefox
|
||||||
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
||||||
const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
|
const deltaNormalize = event.deltaMode === 1 ? 20 : 1
|
||||||
const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize;
|
const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize
|
||||||
const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize;
|
const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
|
||||||
|
|
||||||
if (store.d3Selection && props.panOnScrollSpeed)
|
if (store.d3Selection && props.panOnScrollSpeed)
|
||||||
store.d3Zoom?.translateBy(
|
store.d3Zoom?.translateBy(
|
||||||
store.d3Selection,
|
store.d3Selection,
|
||||||
-(deltaX / currentZoom) * props.panOnScrollSpeed,
|
-(deltaX / currentZoom) * props.panOnScrollSpeed,
|
||||||
-(deltaY / currentZoom) * props.panOnScrollSpeed
|
-(deltaY / currentZoom) * props.panOnScrollSpeed
|
||||||
);
|
)
|
||||||
})
|
})
|
||||||
.on('wheel.zoom', null);
|
.on('wheel.zoom', null)
|
||||||
} else if (typeof store.d3ZoomHandler !== 'undefined') {
|
} else if (typeof store.d3ZoomHandler !== 'undefined') {
|
||||||
store.d3Selection?.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler);
|
store.d3Selection?.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
watchPostEffect(() => store.d3Zoom && applyZoomFilter(store.d3Zoom));
|
watchPostEffect(() => store.d3Zoom && applyZoomFilter(store.d3Zoom))
|
||||||
|
|
||||||
const applyZoomFilter = (d3Zoom: ZoomBehavior<Element, unknown>) => {
|
const applyZoomFilter = (d3Zoom: ZoomBehavior<Element, unknown>) => {
|
||||||
const keyPress = useKeyPress(props.selectionKeyCode);
|
const keyPress = useKeyPress(props.selectionKeyCode)
|
||||||
d3Zoom.filter((event: MouseEvent) => {
|
d3Zoom.filter((event: MouseEvent) => {
|
||||||
const zoomScroll = props.zoomOnScroll;
|
const zoomScroll = props.zoomOnScroll
|
||||||
const pinchZoom = props.zoomOnPinch && event.ctrlKey;
|
const pinchZoom = props.zoomOnPinch && event.ctrlKey
|
||||||
|
|
||||||
// if all interactions are disabled, we prevent all zoom events
|
// if all interactions are disabled, we prevent all zoom events
|
||||||
if (!props.paneMoveable && !zoomScroll && !props.panOnScroll && !props.zoomOnDoubleClick && !props.zoomOnPinch) {
|
if (!props.paneMoveable && !zoomScroll && !props.panOnScroll && !props.zoomOnDoubleClick && !props.zoomOnPinch) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// during a selection we prevent all other interactions
|
// during a selection we prevent all other interactions
|
||||||
if (keyPress.value) {
|
if (keyPress.value) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// if zoom on double click is disabled, we prevent the double click event
|
// if zoom on double click is disabled, we prevent the double click event
|
||||||
if (!props.zoomOnDoubleClick && event.type === 'dblclick') {
|
if (!props.zoomOnDoubleClick && event.type === 'dblclick') {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((event.target as Element).closest('.nowheel') && event.type === 'wheel') {
|
if ((event.target as Element).closest('.nowheel') && event.type === 'wheel') {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// when the target element is a node, we still allow zooming
|
// when the target element is a node, we still allow zooming
|
||||||
@@ -238,40 +238,40 @@ export default defineComponent({
|
|||||||
((event.target as Element).closest('.revue-flow__node') || (event.target as Element).closest('.revue-flow__edge')) &&
|
((event.target as Element).closest('.revue-flow__node') || (event.target as Element).closest('.revue-flow__edge')) &&
|
||||||
event.type !== 'wheel'
|
event.type !== 'wheel'
|
||||||
) {
|
) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// when the target element is a node selection, we still allow zooming
|
// when the target element is a node selection, we still allow zooming
|
||||||
if ((event.target as Element).closest('.revue-flow__nodesselection') && event.type !== 'wheel') {
|
if ((event.target as Element).closest('.revue-flow__nodesselection') && event.type !== 'wheel') {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') {
|
if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// when there is no scroll handling enabled, we prevent all wheel events
|
// when there is no scroll handling enabled, we prevent all wheel events
|
||||||
if (!zoomScroll && !props.panOnScroll && !pinchZoom && event.type === 'wheel') {
|
if (!zoomScroll && !props.panOnScroll && !pinchZoom && event.type === 'wheel') {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the pane is not movable, we prevent dragging it with mousestart or touchstart
|
// if the pane is not movable, we prevent dragging it with mousestart or touchstart
|
||||||
if (!props.paneMoveable && (event.type === 'mousedown' || event.type === 'touchstart')) {
|
if (!props.paneMoveable && (event.type === 'mousedown' || event.type === 'touchstart')) {
|
||||||
return false;
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// default filter for d3-zoom
|
// default filter for d3-zoom
|
||||||
return (!event.ctrlKey || event.type === 'wheel') && !event.button;
|
return (!event.ctrlKey || event.type === 'wheel') && !event.button
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
init();
|
init()
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane">
|
<div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane">
|
||||||
{slots.default ? slots.default() : ''}
|
{slots.default ? slots.default() : ''}
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|||||||
Vendored
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
declare const __REVUE_FLOW_VERSION__: string;
|
declare const __REVUE_FLOW_VERSION__: string
|
||||||
declare const __ENV__: string;
|
declare const __ENV__: string
|
||||||
|
|||||||
+49
-49
@@ -1,47 +1,47 @@
|
|||||||
// global hooks
|
// global hooks
|
||||||
import { Connection, Edge, Elements, FlowTransform, Node, OnConnectStartParams, OnLoadParams } from '../types';
|
import { Connection, Edge, Elements, FlowTransform, Node, OnConnectStartParams, OnLoadParams } from '../types'
|
||||||
import { createEventHook, EventHook } from '@vueuse/core';
|
import { createEventHook, EventHook } from '@vueuse/core'
|
||||||
|
|
||||||
export interface RevueFlowHooks {
|
export interface RevueFlowHooks {
|
||||||
elementClick: EventHook<{ event: MouseEvent; element: Node | Edge }>;
|
elementClick: EventHook<{ event: MouseEvent; element: Node | Edge }>
|
||||||
elementsRemove: EventHook<Elements>;
|
elementsRemove: EventHook<Elements>
|
||||||
nodeDoubleClick: EventHook<{ event: MouseEvent; node: Node }>;
|
nodeDoubleClick: EventHook<{ event: MouseEvent; node: Node }>
|
||||||
nodeClick: EventHook<{ event: MouseEvent; node: Node }>;
|
nodeClick: EventHook<{ event: MouseEvent; node: Node }>
|
||||||
nodeMouseEnter: EventHook<{ event: MouseEvent; node: Node }>;
|
nodeMouseEnter: EventHook<{ event: MouseEvent; node: Node }>
|
||||||
nodeMouseMove: EventHook<{ event: MouseEvent; node: Node }>;
|
nodeMouseMove: EventHook<{ event: MouseEvent; node: Node }>
|
||||||
nodeMouseLeave: EventHook<{ event: MouseEvent; node: Node }>;
|
nodeMouseLeave: EventHook<{ event: MouseEvent; node: Node }>
|
||||||
nodeContextMenu: EventHook<{ event: MouseEvent; node: Node }>;
|
nodeContextMenu: EventHook<{ event: MouseEvent; node: Node }>
|
||||||
nodeDragStart: EventHook<{ event: MouseEvent; node: Node }>;
|
nodeDragStart: EventHook<{ event: MouseEvent; node: Node }>
|
||||||
nodeDrag: EventHook<{ event: MouseEvent; node: Node }>;
|
nodeDrag: EventHook<{ event: MouseEvent; node: Node }>
|
||||||
nodeDragStop: EventHook<{ event: MouseEvent; node: Node }>;
|
nodeDragStop: EventHook<{ event: MouseEvent; node: Node }>
|
||||||
connect: EventHook<Connection>;
|
connect: EventHook<Connection>
|
||||||
connectStart: EventHook<{
|
connectStart: EventHook<{
|
||||||
event: MouseEvent;
|
event: MouseEvent
|
||||||
params: OnConnectStartParams;
|
params: OnConnectStartParams
|
||||||
}>;
|
}>
|
||||||
connectStop: EventHook<MouseEvent>;
|
connectStop: EventHook<MouseEvent>
|
||||||
connectEnd: EventHook<MouseEvent>;
|
connectEnd: EventHook<MouseEvent>
|
||||||
load: EventHook<OnLoadParams>;
|
load: EventHook<OnLoadParams>
|
||||||
move: EventHook<FlowTransform | undefined>;
|
move: EventHook<FlowTransform | undefined>
|
||||||
moveStart: EventHook<FlowTransform | undefined>;
|
moveStart: EventHook<FlowTransform | undefined>
|
||||||
moveEnd: EventHook<FlowTransform | undefined>;
|
moveEnd: EventHook<FlowTransform | undefined>
|
||||||
selectionChange: EventHook<Elements | null>;
|
selectionChange: EventHook<Elements | null>
|
||||||
selectionDragStart: EventHook<{ event: MouseEvent; nodes: Node[] }>;
|
selectionDragStart: EventHook<{ event: MouseEvent; nodes: Node[] }>
|
||||||
selectionDrag: EventHook<{ event: MouseEvent; nodes: Node[] }>;
|
selectionDrag: EventHook<{ event: MouseEvent; nodes: Node[] }>
|
||||||
selectionDragStop: EventHook<{ event: MouseEvent; nodes: Node[] }>;
|
selectionDragStop: EventHook<{ event: MouseEvent; nodes: Node[] }>
|
||||||
selectionContextMenu: EventHook<{ event: MouseEvent; nodes: Node[] }>;
|
selectionContextMenu: EventHook<{ event: MouseEvent; nodes: Node[] }>
|
||||||
paneScroll: EventHook<WheelEvent | undefined>;
|
paneScroll: EventHook<WheelEvent | undefined>
|
||||||
paneClick: EventHook<MouseEvent>;
|
paneClick: EventHook<MouseEvent>
|
||||||
paneContextMenu: EventHook<MouseEvent>;
|
paneContextMenu: EventHook<MouseEvent>
|
||||||
edgeUpdate: EventHook<{ edge: Edge; connection: Connection }>;
|
edgeUpdate: EventHook<{ edge: Edge; connection: Connection }>
|
||||||
edgeContextMenu: EventHook<{ event: MouseEvent; edge: Edge }>;
|
edgeContextMenu: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||||
edgeMouseEnter: EventHook<{ event: MouseEvent; edge: Edge }>;
|
edgeMouseEnter: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||||
edgeMouseMove: EventHook<{ event: MouseEvent; edge: Edge }>;
|
edgeMouseMove: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||||
edgeMouseLeave: EventHook<{ event: MouseEvent; edge: Edge }>;
|
edgeMouseLeave: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||||
edgeDoubleClick: EventHook<{ event: MouseEvent; edge: Edge }>;
|
edgeDoubleClick: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||||
edgeClick: EventHook<{ event: MouseEvent; edge: Edge }>;
|
edgeClick: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||||
edgeUpdateStart: EventHook<{ event: MouseEvent; edge: Edge }>;
|
edgeUpdateStart: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||||
edgeUpdateEnd: EventHook<{ event: MouseEvent; edge: Edge }>;
|
edgeUpdateEnd: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string, ...args: any[]) => void) => RevueFlowHooks } => {
|
export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string, ...args: any[]) => void) => RevueFlowHooks } => {
|
||||||
@@ -59,8 +59,8 @@ export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string,
|
|||||||
nodeDragStop: createEventHook<{ event: MouseEvent; node: Node }>(),
|
nodeDragStop: createEventHook<{ event: MouseEvent; node: Node }>(),
|
||||||
connect: createEventHook<Connection>(),
|
connect: createEventHook<Connection>(),
|
||||||
connectStart: createEventHook<{
|
connectStart: createEventHook<{
|
||||||
event: MouseEvent;
|
event: MouseEvent
|
||||||
params: OnConnectStartParams;
|
params: OnConnectStartParams
|
||||||
}>(),
|
}>(),
|
||||||
connectStop: createEventHook<MouseEvent>(),
|
connectStop: createEventHook<MouseEvent>(),
|
||||||
connectEnd: createEventHook<MouseEvent>(),
|
connectEnd: createEventHook<MouseEvent>(),
|
||||||
@@ -85,20 +85,20 @@ export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string,
|
|||||||
edgeClick: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
edgeClick: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||||
edgeUpdateStart: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
edgeUpdateStart: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||||
edgeUpdateEnd: createEventHook<{ event: MouseEvent; edge: Edge }>()
|
edgeUpdateEnd: createEventHook<{ event: MouseEvent; edge: Edge }>()
|
||||||
};
|
}
|
||||||
|
|
||||||
const bind = (emit: (event: string, args: any) => void) => {
|
const bind = (emit: (event: string, args: any) => void) => {
|
||||||
for (const [key, value] of Object.entries(eventHooks)) {
|
for (const [key, value] of Object.entries(eventHooks)) {
|
||||||
value.on((data: any) => {
|
value.on((data: any) => {
|
||||||
emit(key, data);
|
emit(key, data)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return eventHooks;
|
return eventHooks
|
||||||
};
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...eventHooks,
|
...eventHooks,
|
||||||
bind
|
bind
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,37 +1,37 @@
|
|||||||
import useKeyPress from './useKeyPress';
|
import useKeyPress from './useKeyPress'
|
||||||
import { isNode, getConnectedEdges } from '../utils/graph';
|
import { isNode, getConnectedEdges } from '../utils/graph'
|
||||||
import { Elements, KeyCode, ElementId, FlowElement, RevueFlowStore, Edge } from '../types';
|
import { Elements, KeyCode, ElementId, FlowElement, RevueFlowStore, Edge } from '../types'
|
||||||
import { computed, watch } from 'vue';
|
import { computed, watch } from 'vue'
|
||||||
|
|
||||||
interface HookParams {
|
interface HookParams {
|
||||||
store: RevueFlowStore;
|
store: RevueFlowStore
|
||||||
deleteKeyCode: KeyCode;
|
deleteKeyCode: KeyCode
|
||||||
multiSelectionKeyCode: KeyCode;
|
multiSelectionKeyCode: KeyCode
|
||||||
onElementsRemove?: (elements: Elements) => void;
|
onElementsRemove?: (elements: Elements) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ({ store, deleteKeyCode, multiSelectionKeyCode, onElementsRemove }: HookParams): void => {
|
export default ({ store, deleteKeyCode, multiSelectionKeyCode, onElementsRemove }: HookParams): void => {
|
||||||
const deleteKeyPressed = useKeyPress(deleteKeyCode);
|
const deleteKeyPressed = useKeyPress(deleteKeyCode)
|
||||||
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
|
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode)
|
||||||
const selectedElements = computed(() => store.selectedElements || []);
|
const selectedElements = computed(() => store.selectedElements || [])
|
||||||
const edges = computed(() => store.edges);
|
const edges = computed(() => store.edges)
|
||||||
|
|
||||||
watch(selectedElements, () => {
|
watch(selectedElements, () => {
|
||||||
if (onElementsRemove && deleteKeyPressed.value && selectedElements.value.length > 0) {
|
if (onElementsRemove && deleteKeyPressed.value && selectedElements.value.length > 0) {
|
||||||
const selectedNodes = selectedElements.value.filter(isNode);
|
const selectedNodes = selectedElements.value.filter(isNode)
|
||||||
const connectedEdges = getConnectedEdges(selectedNodes, edges.value as Edge[]);
|
const connectedEdges = getConnectedEdges(selectedNodes, edges.value as Edge[])
|
||||||
const elementsToRemove = [...selectedElements.value, ...connectedEdges].reduce(
|
const elementsToRemove = [...selectedElements.value, ...connectedEdges].reduce(
|
||||||
(res, item) => res.set(item.id, item),
|
(res, item) => res.set(item.id, item),
|
||||||
new Map<ElementId, FlowElement>()
|
new Map<ElementId, FlowElement>()
|
||||||
);
|
)
|
||||||
|
|
||||||
onElementsRemove(Array.from(elementsToRemove.values()));
|
onElementsRemove(Array.from(elementsToRemove.values()))
|
||||||
store.unsetNodesSelection();
|
store.unsetNodesSelection()
|
||||||
store.resetSelectedElements();
|
store.resetSelectedElements()
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
watch(multiSelectionKeyPressed, () => {
|
watch(multiSelectionKeyPressed, () => {
|
||||||
store.multiSelectionActive = multiSelectionKeyPressed.value;
|
store.multiSelectionActive = multiSelectionKeyPressed.value
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|||||||
+20
-20
@@ -1,47 +1,47 @@
|
|||||||
import { Ref } from 'vue';
|
import { Ref } from 'vue'
|
||||||
import { KeyCode } from '../types';
|
import { KeyCode } from '../types'
|
||||||
import { controlledRef, onKeyStroke, useEventListener } from '@vueuse/core';
|
import { controlledRef, onKeyStroke, useEventListener } from '@vueuse/core'
|
||||||
|
|
||||||
export default (keyCode?: KeyCode, cb?: (keyPress: boolean) => void): Ref<boolean> => {
|
export default (keyCode?: KeyCode, cb?: (keyPress: boolean) => void): Ref<boolean> => {
|
||||||
const keyPressed = controlledRef<boolean>(false, {
|
const keyPressed = controlledRef<boolean>(false, {
|
||||||
onBeforeChange(val, oldVal) {
|
onBeforeChange(val, oldVal) {
|
||||||
if (val === oldVal) return false;
|
if (val === oldVal) return false
|
||||||
},
|
},
|
||||||
onChanged() {
|
onChanged() {
|
||||||
if (cb && typeof cb === 'function') cb(keyPressed.value);
|
if (cb && typeof cb === 'function') cb(keyPressed.value)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
onKeyStroke(
|
onKeyStroke(
|
||||||
(event) => event.key === keyCode || event.keyCode === keyCode,
|
(event) => event.key === keyCode || event.keyCode === keyCode,
|
||||||
(e) => {
|
(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
keyPressed.value = true;
|
keyPressed.value = true
|
||||||
},
|
},
|
||||||
{ eventName: 'keypress' }
|
{ eventName: 'keypress' }
|
||||||
);
|
)
|
||||||
onKeyStroke(
|
onKeyStroke(
|
||||||
(event) => event.key === keyCode || event.keyCode === keyCode,
|
(event) => event.key === keyCode || event.keyCode === keyCode,
|
||||||
(e) => {
|
(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
keyPressed.value = true;
|
keyPressed.value = true
|
||||||
},
|
},
|
||||||
{ eventName: 'keydown' }
|
{ eventName: 'keydown' }
|
||||||
);
|
)
|
||||||
onKeyStroke(
|
onKeyStroke(
|
||||||
(event) => event.key === keyCode || event.keyCode === keyCode,
|
(event) => event.key === keyCode || event.keyCode === keyCode,
|
||||||
(e) => {
|
(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
keyPressed.value = false;
|
keyPressed.value = false
|
||||||
},
|
},
|
||||||
{ eventName: 'keyup' }
|
{ eventName: 'keyup' }
|
||||||
);
|
)
|
||||||
|
|
||||||
useEventListener(window, 'blur', () => {
|
useEventListener(window, 'blur', () => {
|
||||||
keyPressed.value = false;
|
keyPressed.value = false
|
||||||
});
|
})
|
||||||
|
|
||||||
if (cb && typeof cb === 'function') cb(keyPressed.value);
|
if (cb && typeof cb === 'function') cb(keyPressed.value)
|
||||||
|
|
||||||
return keyPressed;
|
return keyPressed
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,33 +1,33 @@
|
|||||||
import { createEventHook, MaybeRef, unrefElement, until, useEventListener, useResizeObserver } from '@vueuse/core';
|
import { createEventHook, MaybeRef, unrefElement, until, useEventListener, useResizeObserver } from '@vueuse/core'
|
||||||
import { getDimensions } from '../utils';
|
import { getDimensions } from '../utils'
|
||||||
import { Dimensions } from '../types';
|
import { Dimensions } from '../types'
|
||||||
|
|
||||||
export default (rendererNode: MaybeRef<HTMLDivElement>, updateSize: (size: Dimensions) => any) => {
|
export default (rendererNode: MaybeRef<HTMLDivElement>, updateSize: (size: Dimensions) => any) => {
|
||||||
const resizeHandlerHook = createEventHook();
|
const resizeHandlerHook = createEventHook()
|
||||||
const updateDimensions = () => {
|
const updateDimensions = () => {
|
||||||
if (!unrefElement(rendererNode)) {
|
if (!unrefElement(rendererNode)) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const size = getDimensions(unrefElement(rendererNode));
|
const size = getDimensions(unrefElement(rendererNode))
|
||||||
|
|
||||||
if (size.height === 0 || size.width === 0) {
|
if (size.height === 0 || size.width === 0) {
|
||||||
console.log('The revue Flow parent container needs a width and a height to render the graph.');
|
console.log('The revue Flow parent container needs a width and a height to render the graph.')
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSize(size);
|
updateSize(size)
|
||||||
};
|
}
|
||||||
|
|
||||||
until(rendererNode)
|
until(rendererNode)
|
||||||
.toBeTruthy()
|
.toBeTruthy()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
updateDimensions();
|
updateDimensions()
|
||||||
useEventListener(window, 'resize', updateDimensions);
|
useEventListener(window, 'resize', updateDimensions)
|
||||||
useResizeObserver(unrefElement(rendererNode), () => updateDimensions());
|
useResizeObserver(unrefElement(rendererNode), () => updateDimensions())
|
||||||
resizeHandlerHook.trigger(true);
|
resizeHandlerHook.trigger(true)
|
||||||
});
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onReady: resizeHandlerHook.on
|
onReady: resizeHandlerHook.on
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
import { ElementId, RevueFlowStore, UpdateNodeInternals } from '../types';
|
import { ElementId, RevueFlowStore, UpdateNodeInternals } from '../types'
|
||||||
import { inject } from 'vue';
|
import { inject } from 'vue'
|
||||||
|
|
||||||
function useUpdateNodeInternals(): UpdateNodeInternals {
|
function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
|
|
||||||
return (id: ElementId) => {
|
return (id: ElementId) => {
|
||||||
const nodeElement: HTMLDivElement | null = document.querySelector(`.revue-flow__node[data-id="${id}"]`);
|
const nodeElement: HTMLDivElement | null = document.querySelector(`.revue-flow__node[data-id="${id}"]`)
|
||||||
|
|
||||||
if (nodeElement) {
|
if (nodeElement) {
|
||||||
store.updateNodeDimensions([{ id, nodeElement, forceUpdate: true }]);
|
store.updateNodeDimensions([{ id, nodeElement, forceUpdate: true }])
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default useUpdateNodeInternals;
|
export default useUpdateNodeInternals
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { zoomIdentity } from 'd3-zoom';
|
import { zoomIdentity } from 'd3-zoom'
|
||||||
import { computed, effectScope, inject } from 'vue';
|
import { computed, effectScope, inject } from 'vue'
|
||||||
import { createEventHook, EventHookOn, until } from '@vueuse/core';
|
import { createEventHook, EventHookOn, until } from '@vueuse/core'
|
||||||
import { Selection, ZoomBehavior } from 'd3';
|
import { Selection, ZoomBehavior } from 'd3'
|
||||||
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph';
|
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph'
|
||||||
import { FitViewParams, FlowTransform, ZoomPanHelperFunctions, Rect, XYPosition, RevueFlowStore } from '../types';
|
import { FitViewParams, FlowTransform, ZoomPanHelperFunctions, Rect, XYPosition, RevueFlowStore } from '../types'
|
||||||
|
|
||||||
const DEFAULT_PADDING = 0.1;
|
const DEFAULT_PADDING = 0.1
|
||||||
|
|
||||||
const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } => {
|
const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } => {
|
||||||
const zoomPanHelperEvent = createEventHook<ZoomPanHelperFunctions>();
|
const zoomPanHelperEvent = createEventHook<ZoomPanHelperFunctions>()
|
||||||
const store = inject<RevueFlowStore>('store')!;
|
const store = inject<RevueFlowStore>('store')!
|
||||||
|
|
||||||
const scope = effectScope();
|
const scope = effectScope()
|
||||||
scope.run(() => {
|
scope.run(() => {
|
||||||
const d3Zoom = computed(() => store.d3Zoom);
|
const d3Zoom = computed(() => store.d3Zoom)
|
||||||
const d3Selection = computed(() => store.d3Selection);
|
const d3Selection = computed(() => store.d3Selection)
|
||||||
|
|
||||||
const createZoomPanHelper = (
|
const createZoomPanHelper = (
|
||||||
d3Zoom: ZoomBehavior<Element, unknown>,
|
d3Zoom: ZoomBehavior<Element, unknown>,
|
||||||
@@ -24,18 +24,18 @@ const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } =>
|
|||||||
zoomOut: () => d3Zoom?.scaleBy(d3Selection, 1 / 1.2),
|
zoomOut: () => d3Zoom?.scaleBy(d3Selection, 1 / 1.2),
|
||||||
zoomTo: (zoomLevel: number) => d3Zoom.scaleTo(d3Selection, zoomLevel),
|
zoomTo: (zoomLevel: number) => d3Zoom.scaleTo(d3Selection, zoomLevel),
|
||||||
transform: (transform: FlowTransform) => {
|
transform: (transform: FlowTransform) => {
|
||||||
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom);
|
const nextTransform = zoomIdentity.translate(transform.x, transform.y).scale(transform.zoom)
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, nextTransform);
|
d3Zoom.transform(d3Selection, nextTransform)
|
||||||
},
|
},
|
||||||
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
|
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
|
||||||
const { nodes, width, height, minZoom, maxZoom } = store;
|
const { nodes, width, height, minZoom, maxZoom } = store
|
||||||
|
|
||||||
if (!nodes.length) {
|
if (!nodes.length) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const bounds = getRectOfNodes(options.includeHiddenNodes ? nodes : nodes.filter((node) => !node.isHidden));
|
const bounds = getRectOfNodes(options.includeHiddenNodes ? nodes : nodes.filter((node) => !node.isHidden))
|
||||||
const [x, y, zoom] = getTransformForBounds(
|
const [x, y, zoom] = getTransformForBounds(
|
||||||
bounds,
|
bounds,
|
||||||
width,
|
width,
|
||||||
@@ -43,49 +43,49 @@ const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } =>
|
|||||||
options.minZoom || minZoom,
|
options.minZoom || minZoom,
|
||||||
options.maxZoom || maxZoom,
|
options.maxZoom || maxZoom,
|
||||||
options.padding || DEFAULT_PADDING
|
options.padding || DEFAULT_PADDING
|
||||||
);
|
)
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
const transform = zoomIdentity.translate(x, y).scale(zoom)
|
||||||
|
|
||||||
d3Zoom?.transform(d3Selection, transform);
|
d3Zoom?.transform(d3Selection, transform)
|
||||||
},
|
},
|
||||||
setCenter: (x: number, y: number, zoom?: number) => {
|
setCenter: (x: number, y: number, zoom?: number) => {
|
||||||
const { width, height, maxZoom } = store;
|
const { width, height, maxZoom } = store
|
||||||
|
|
||||||
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom;
|
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom
|
||||||
const centerX = width / 2 - x * nextZoom;
|
const centerX = width / 2 - x * nextZoom
|
||||||
const centerY = height / 2 - y * nextZoom;
|
const centerY = height / 2 - y * nextZoom
|
||||||
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom);
|
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom)
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, transform);
|
d3Zoom.transform(d3Selection, transform)
|
||||||
},
|
},
|
||||||
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
|
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
|
||||||
const { width, height, minZoom, maxZoom } = store;
|
const { width, height, minZoom, maxZoom } = store
|
||||||
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding);
|
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding)
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
const transform = zoomIdentity.translate(x, y).scale(zoom)
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, transform);
|
d3Zoom.transform(d3Selection, transform)
|
||||||
},
|
},
|
||||||
project: (position: XYPosition) => {
|
project: (position: XYPosition) => {
|
||||||
const { transform, snapToGrid, snapGrid } = store;
|
const { transform, snapToGrid, snapGrid } = store
|
||||||
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
return pointToRendererPoint(position, transform, snapToGrid, snapGrid)
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
until(d3Zoom)
|
until(d3Zoom)
|
||||||
.toBeTruthy()
|
.toBeTruthy()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (d3Zoom.value && d3Selection.value) {
|
if (d3Zoom.value && d3Selection.value) {
|
||||||
zoomPanHelperEvent.trigger(createZoomPanHelper(d3Zoom.value, d3Selection.value));
|
zoomPanHelperEvent.trigger(createZoomPanHelper(d3Zoom.value, d3Selection.value))
|
||||||
} else {
|
} else {
|
||||||
throw Error('D3 could not be initialized.');
|
throw Error('D3 could not be initialized.')
|
||||||
}
|
}
|
||||||
scope.stop();
|
scope.stop()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
onReady: zoomPanHelperEvent.on
|
onReady: zoomPanHelperEvent.on
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default useZoomPanHelper;
|
export default useZoomPanHelper
|
||||||
|
|||||||
+11
-11
@@ -1,9 +1,9 @@
|
|||||||
export { default as default } from './container/RevueFlow';
|
export { default as default } from './container/RevueFlow'
|
||||||
export { default as Handle } from './components/Handle';
|
export { default as Handle } from './components/Handle'
|
||||||
export { default as EdgeText } from './components/Edges/EdgeText';
|
export { default as EdgeText } from './components/Edges/EdgeText'
|
||||||
export { getBezierPath } from './components/Edges/BezierEdge';
|
export { getBezierPath } from './components/Edges/BezierEdge'
|
||||||
export { getSmoothStepPath } from './components/Edges/SmoothStepEdge';
|
export { getSmoothStepPath } from './components/Edges/SmoothStepEdge'
|
||||||
export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils';
|
export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils'
|
||||||
|
|
||||||
export {
|
export {
|
||||||
isNode,
|
isNode,
|
||||||
@@ -16,9 +16,9 @@ export {
|
|||||||
updateEdge,
|
updateEdge,
|
||||||
getTransformForBounds,
|
getTransformForBounds,
|
||||||
getRectOfNodes
|
getRectOfNodes
|
||||||
} from './utils/graph';
|
} from './utils/graph'
|
||||||
export { default as useZoomPanHelper } from './hooks/useZoomPanHelper';
|
export { default as useZoomPanHelper } from './hooks/useZoomPanHelper'
|
||||||
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals';
|
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals'
|
||||||
|
|
||||||
export * from './additional-components';
|
export * from './additional-components'
|
||||||
export * from './types';
|
export * from './types'
|
||||||
|
|||||||
Vendored
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
import Vue, { VNode } from 'vue';
|
import Vue, { VNode } from 'vue'
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
namespace JSX {
|
namespace JSX {
|
||||||
@@ -9,7 +9,7 @@ declare global {
|
|||||||
interface ElementClass extends Vue {}
|
interface ElementClass extends Vue {}
|
||||||
|
|
||||||
interface IntrinsicAttributes {
|
interface IntrinsicAttributes {
|
||||||
[prop: string]: any;
|
[prop: string]: any
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+3
-3
@@ -1,6 +1,6 @@
|
|||||||
declare module '*.vue' {
|
declare module '*.vue' {
|
||||||
import { DefineComponent } from 'vue';
|
import { DefineComponent } from 'vue'
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||||
const component: DefineComponent<{}, {}, any>;
|
const component: DefineComponent<{}, {}, any>
|
||||||
export default component;
|
export default component
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia';
|
import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia'
|
||||||
import isEqual from 'fast-deep-equal';
|
import isEqual from 'fast-deep-equal'
|
||||||
import { Edge, Node, NodeDiffUpdate, RevueFlowState, RevueFlowActions, XYPosition } from '../types';
|
import { Edge, Node, NodeDiffUpdate, RevueFlowState, RevueFlowActions, XYPosition } from '../types'
|
||||||
import { getConnectedEdges, getNodesInside, getRectOfNodes, isEdge, isNode, parseEdge, parseNode } from '../utils/graph';
|
import { getConnectedEdges, getNodesInside, getRectOfNodes, isEdge, isNode, parseEdge, parseNode } from '../utils/graph'
|
||||||
import { clampPosition, getDimensions } from '../utils';
|
import { clampPosition, getDimensions } from '../utils'
|
||||||
import { getHandleBounds } from '../components/Nodes/utils';
|
import { getHandleBounds } from '../components/Nodes/utils'
|
||||||
|
|
||||||
type NextElements = {
|
type NextElements = {
|
||||||
nextNodes: Node[];
|
nextNodes: Node[]
|
||||||
nextEdges: Edge[];
|
nextEdges: Edge[]
|
||||||
};
|
}
|
||||||
const pinia = createPinia();
|
const pinia = createPinia()
|
||||||
|
|
||||||
export default function configureStore(
|
export default function configureStore(
|
||||||
preloadedState: RevueFlowState
|
preloadedState: RevueFlowState
|
||||||
): StoreDefinition<string, RevueFlowState, any, RevueFlowActions> {
|
): StoreDefinition<string, RevueFlowState, any, RevueFlowActions> {
|
||||||
setActivePinia(pinia);
|
setActivePinia(pinia)
|
||||||
|
|
||||||
return defineStore({
|
return defineStore({
|
||||||
id: 'revue-flow-' + Math.random(),
|
id: 'revue-flow-' + Math.random(),
|
||||||
@@ -24,66 +24,66 @@ export default function configureStore(
|
|||||||
getters: {},
|
getters: {},
|
||||||
actions: {
|
actions: {
|
||||||
setElements(elements) {
|
setElements(elements) {
|
||||||
const propElements = elements;
|
const propElements = elements
|
||||||
const nextElements: NextElements = {
|
const nextElements: NextElements = {
|
||||||
nextNodes: [],
|
nextNodes: [],
|
||||||
nextEdges: []
|
nextEdges: []
|
||||||
};
|
}
|
||||||
const { nextNodes, nextEdges } = propElements.reduce((res, propElement): NextElements => {
|
const { nextNodes, nextEdges } = propElements.reduce((res, propElement): NextElements => {
|
||||||
if (isNode(propElement)) {
|
if (isNode(propElement)) {
|
||||||
const storeNode = this.nodes.find((node) => node.id === propElement.id);
|
const storeNode = this.nodes.find((node) => node.id === propElement.id)
|
||||||
|
|
||||||
if (storeNode) {
|
if (storeNode) {
|
||||||
const updatedNode: Node = {
|
const updatedNode: Node = {
|
||||||
...storeNode,
|
...storeNode,
|
||||||
...propElement
|
...propElement
|
||||||
};
|
}
|
||||||
|
|
||||||
if (storeNode.position.x !== propElement.position.x || storeNode.position.y !== propElement.position.y) {
|
if (storeNode.position.x !== propElement.position.x || storeNode.position.y !== propElement.position.y) {
|
||||||
updatedNode.__rf.position = propElement.position;
|
updatedNode.__rf.position = propElement.position
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof propElement.type !== 'undefined' && propElement.type !== storeNode.type) {
|
if (typeof propElement.type !== 'undefined' && propElement.type !== storeNode.type) {
|
||||||
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
|
// we reset the elements dimensions here in order to force a re-calculation of the bounds.
|
||||||
// When the type of a node changes it is possible that the number or positions of handles changes too.
|
// When the type of a node changes it is possible that the number or positions of handles changes too.
|
||||||
updatedNode.__rf.width = null;
|
updatedNode.__rf.width = null
|
||||||
}
|
}
|
||||||
|
|
||||||
res.nextNodes.push(updatedNode);
|
res.nextNodes.push(updatedNode)
|
||||||
} else {
|
} else {
|
||||||
res.nextNodes.push(parseNode(propElement, this.nodeExtent));
|
res.nextNodes.push(parseNode(propElement, this.nodeExtent))
|
||||||
}
|
}
|
||||||
} else if (isEdge(propElement)) {
|
} else if (isEdge(propElement)) {
|
||||||
const storeEdge = this.edges.find((se) => se.id === propElement.id);
|
const storeEdge = this.edges.find((se) => se.id === propElement.id)
|
||||||
|
|
||||||
if (storeEdge) {
|
if (storeEdge) {
|
||||||
res.nextEdges.push({
|
res.nextEdges.push({
|
||||||
...storeEdge,
|
...storeEdge,
|
||||||
...propElement
|
...propElement
|
||||||
});
|
})
|
||||||
} else {
|
} else {
|
||||||
res.nextEdges.push(parseEdge(propElement));
|
res.nextEdges.push(parseEdge(propElement))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res
|
||||||
}, nextElements);
|
}, nextElements)
|
||||||
|
|
||||||
this.nodes = nextNodes;
|
this.nodes = nextNodes
|
||||||
this.edges = nextEdges;
|
this.edges = nextEdges
|
||||||
},
|
},
|
||||||
updateNodeDimensions(updates) {
|
updateNodeDimensions(updates) {
|
||||||
this.nodes = this.nodes.map((node) => {
|
this.nodes = this.nodes.map((node) => {
|
||||||
const update = updates.find((u) => u.id === node.id);
|
const update = updates.find((u) => u.id === node.id)
|
||||||
if (update) {
|
if (update) {
|
||||||
const dimensions = getDimensions(update.nodeElement);
|
const dimensions = getDimensions(update.nodeElement)
|
||||||
const doUpdate =
|
const doUpdate =
|
||||||
dimensions.width &&
|
dimensions.width &&
|
||||||
dimensions.height &&
|
dimensions.height &&
|
||||||
(node.__rf.width !== dimensions.width || node.__rf.height !== dimensions.height || update.forceUpdate);
|
(node.__rf.width !== dimensions.width || node.__rf.height !== dimensions.height || update.forceUpdate)
|
||||||
|
|
||||||
if (doUpdate) {
|
if (doUpdate) {
|
||||||
const handleBounds = getHandleBounds(update.nodeElement, this.transform[2]);
|
const handleBounds = getHandleBounds(update.nodeElement, this.transform[2])
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
@@ -92,23 +92,23 @@ export default function configureStore(
|
|||||||
...dimensions,
|
...dimensions,
|
||||||
handleBounds
|
handleBounds
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
updateNodePos(payload) {
|
updateNodePos(payload) {
|
||||||
const { id, pos } = payload;
|
const { id, pos } = payload
|
||||||
let position: XYPosition = pos;
|
let position: XYPosition = pos
|
||||||
|
|
||||||
if (this.snapToGrid) {
|
if (this.snapToGrid) {
|
||||||
const [gridSizeX, gridSizeY] = this.snapGrid;
|
const [gridSizeX, gridSizeY] = this.snapGrid
|
||||||
position = {
|
position = {
|
||||||
x: gridSizeX * Math.round(pos.x / gridSizeX),
|
x: gridSizeX * Math.round(pos.x / gridSizeX),
|
||||||
y: gridSizeY * Math.round(pos.y / gridSizeY)
|
y: gridSizeY * Math.round(pos.y / gridSizeY)
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.nodes = this.nodes.map((node) => {
|
this.nodes = this.nodes.map((node) => {
|
||||||
@@ -119,14 +119,14 @@ export default function configureStore(
|
|||||||
...node.__rf,
|
...node.__rf,
|
||||||
position
|
position
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
updateNodePosDiff(payload: NodeDiffUpdate) {
|
updateNodePosDiff(payload: NodeDiffUpdate) {
|
||||||
const { id, diff, isDragging } = payload;
|
const { id, diff, isDragging } = payload
|
||||||
|
|
||||||
this.nodes = this.nodes.map((node) => {
|
this.nodes = this.nodes.map((node) => {
|
||||||
if (id === node.id || this.selectedElements?.find((sNode) => sNode.id === node.id)) {
|
if (id === node.id || this.selectedElements?.find((sNode) => sNode.id === node.id)) {
|
||||||
@@ -136,23 +136,23 @@ export default function configureStore(
|
|||||||
...node.__rf,
|
...node.__rf,
|
||||||
isDragging
|
isDragging
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
if (diff) {
|
if (diff) {
|
||||||
updatedNode.__rf.position = {
|
updatedNode.__rf.position = {
|
||||||
x: node.__rf.position.x + diff.x,
|
x: node.__rf.position.x + diff.x,
|
||||||
y: node.__rf.position.y + diff.y
|
y: node.__rf.position.y + diff.y
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return updatedNode;
|
return updatedNode
|
||||||
}
|
}
|
||||||
|
|
||||||
return node;
|
return node
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
setUserSelection(mousePos) {
|
setUserSelection(mousePos) {
|
||||||
this.selectionActive = true;
|
this.selectionActive = true
|
||||||
this.userSelectionRect = {
|
this.userSelectionRect = {
|
||||||
width: 0,
|
width: 0,
|
||||||
height: 0,
|
height: 0,
|
||||||
@@ -161,11 +161,11 @@ export default function configureStore(
|
|||||||
x: mousePos.x,
|
x: mousePos.x,
|
||||||
y: mousePos.y,
|
y: mousePos.y,
|
||||||
draw: true
|
draw: true
|
||||||
};
|
}
|
||||||
},
|
},
|
||||||
updateUserSelection(mousePos) {
|
updateUserSelection(mousePos) {
|
||||||
const startX = this.userSelectionRect.startX || 0;
|
const startX = this.userSelectionRect.startX || 0
|
||||||
const startY = this.userSelectionRect.startY || 0;
|
const startY = this.userSelectionRect.startY || 0
|
||||||
|
|
||||||
const nextUserSelectRect: RevueFlowState['userSelectionRect'] = {
|
const nextUserSelectRect: RevueFlowState['userSelectionRect'] = {
|
||||||
...this.userSelectionRect,
|
...this.userSelectionRect,
|
||||||
@@ -173,59 +173,59 @@ export default function configureStore(
|
|||||||
y: mousePos.y < startY ? mousePos.y : this.userSelectionRect.y,
|
y: mousePos.y < startY ? mousePos.y : this.userSelectionRect.y,
|
||||||
width: Math.abs(mousePos.x - startX),
|
width: Math.abs(mousePos.x - startX),
|
||||||
height: Math.abs(mousePos.y - startY)
|
height: Math.abs(mousePos.y - startY)
|
||||||
};
|
}
|
||||||
|
|
||||||
const selectedNodes = getNodesInside(this.nodes, nextUserSelectRect, this.transform);
|
const selectedNodes = getNodesInside(this.nodes, nextUserSelectRect, this.transform)
|
||||||
const selectedEdges = getConnectedEdges(selectedNodes, this.edges);
|
const selectedEdges = getConnectedEdges(selectedNodes, this.edges)
|
||||||
|
|
||||||
const nextSelectedElements = [...selectedNodes, ...selectedEdges];
|
const nextSelectedElements = [...selectedNodes, ...selectedEdges]
|
||||||
this.userSelectionRect = nextUserSelectRect;
|
this.userSelectionRect = nextUserSelectRect
|
||||||
this.selectedElements = nextSelectedElements;
|
this.selectedElements = nextSelectedElements
|
||||||
},
|
},
|
||||||
unsetUserSelection() {
|
unsetUserSelection() {
|
||||||
const selectedNodes = this.selectedElements?.filter((node) => isNode(node) && node.__rf) as Node[];
|
const selectedNodes = this.selectedElements?.filter((node) => isNode(node) && node.__rf) as Node[]
|
||||||
|
|
||||||
this.selectionActive = false;
|
this.selectionActive = false
|
||||||
this.userSelectionRect.draw = false;
|
this.userSelectionRect.draw = false
|
||||||
|
|
||||||
if (!selectedNodes || selectedNodes.length === 0) {
|
if (!selectedNodes || selectedNodes.length === 0) {
|
||||||
this.selectedElements = null;
|
this.selectedElements = null
|
||||||
this.nodesSelectionActive = false;
|
this.nodesSelectionActive = false
|
||||||
} else {
|
} else {
|
||||||
this.selectedNodesBbox = getRectOfNodes(selectedNodes);
|
this.selectedNodesBbox = getRectOfNodes(selectedNodes)
|
||||||
this.nodesSelectionActive = true;
|
this.nodesSelectionActive = true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
addSelectedElements(elements) {
|
addSelectedElements(elements) {
|
||||||
const selectedElementsArr = Array.isArray(elements) ? elements : [elements];
|
const selectedElementsArr = Array.isArray(elements) ? elements : [elements]
|
||||||
const selectedElementsUpdated = !isEqual(selectedElementsArr, this.selectedElements);
|
const selectedElementsUpdated = !isEqual(selectedElementsArr, this.selectedElements)
|
||||||
this.selectedElements = selectedElementsUpdated ? selectedElementsArr : this.selectedElements;
|
this.selectedElements = selectedElementsUpdated ? selectedElementsArr : this.selectedElements
|
||||||
},
|
},
|
||||||
initD3Zoom(payload) {
|
initD3Zoom(payload) {
|
||||||
const { d3Zoom, d3Selection, d3ZoomHandler, transform } = payload;
|
const { d3Zoom, d3Selection, d3ZoomHandler, transform } = payload
|
||||||
|
|
||||||
this.d3Zoom = d3Zoom;
|
this.d3Zoom = d3Zoom
|
||||||
this.d3Selection = d3Selection;
|
this.d3Selection = d3Selection
|
||||||
this.d3ZoomHandler = d3ZoomHandler;
|
this.d3ZoomHandler = d3ZoomHandler
|
||||||
this.transform = transform;
|
this.transform = transform
|
||||||
},
|
},
|
||||||
setMinZoom(minZoom) {
|
setMinZoom(minZoom) {
|
||||||
this.d3Zoom?.scaleExtent([minZoom, this.maxZoom]);
|
this.d3Zoom?.scaleExtent([minZoom, this.maxZoom])
|
||||||
|
|
||||||
this.minZoom = minZoom;
|
this.minZoom = minZoom
|
||||||
},
|
},
|
||||||
setMaxZoom(maxZoom) {
|
setMaxZoom(maxZoom) {
|
||||||
this.d3Zoom?.scaleExtent([this.minZoom, maxZoom]);
|
this.d3Zoom?.scaleExtent([this.minZoom, maxZoom])
|
||||||
|
|
||||||
this.maxZoom = maxZoom;
|
this.maxZoom = maxZoom
|
||||||
},
|
},
|
||||||
setTranslateExtent(translateExtent) {
|
setTranslateExtent(translateExtent) {
|
||||||
this.d3Zoom?.translateExtent(translateExtent);
|
this.d3Zoom?.translateExtent(translateExtent)
|
||||||
|
|
||||||
this.translateExtent = translateExtent;
|
this.translateExtent = translateExtent
|
||||||
},
|
},
|
||||||
setNodeExtent(nodeExtent) {
|
setNodeExtent(nodeExtent) {
|
||||||
this.nodeExtent = nodeExtent;
|
this.nodeExtent = nodeExtent
|
||||||
this.nodes = this.nodes.map((node) => {
|
this.nodes = this.nodes.map((node) => {
|
||||||
return {
|
return {
|
||||||
...node,
|
...node,
|
||||||
@@ -233,29 +233,29 @@ export default function configureStore(
|
|||||||
...node.__rf,
|
...node.__rf,
|
||||||
position: clampPosition(node.__rf.position, nodeExtent)
|
position: clampPosition(node.__rf.position, nodeExtent)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
},
|
},
|
||||||
resetSelectedElements() {
|
resetSelectedElements() {
|
||||||
this.selectedElements = null;
|
this.selectedElements = null
|
||||||
},
|
},
|
||||||
unsetNodesSelection() {
|
unsetNodesSelection() {
|
||||||
this.nodesSelectionActive = false;
|
this.nodesSelectionActive = false
|
||||||
},
|
},
|
||||||
updateSize(size) {
|
updateSize(size) {
|
||||||
this.height = size.height;
|
this.height = size.height
|
||||||
this.width = size.width;
|
this.width = size.width
|
||||||
},
|
},
|
||||||
setConnectionNodeId(payload) {
|
setConnectionNodeId(payload) {
|
||||||
this.connectionNodeId = payload.connectionNodeId;
|
this.connectionNodeId = payload.connectionNodeId
|
||||||
this.connectionHandleId = payload.connectionHandleId;
|
this.connectionHandleId = payload.connectionHandleId
|
||||||
this.connectionHandleType = payload.connectionHandleType;
|
this.connectionHandleType = payload.connectionHandleType
|
||||||
},
|
},
|
||||||
setInteractive(isInteractive) {
|
setInteractive(isInteractive) {
|
||||||
this.nodesDraggable = isInteractive;
|
this.nodesDraggable = isInteractive
|
||||||
this.nodesConnectable = isInteractive;
|
this.nodesConnectable = isInteractive
|
||||||
this.elementsSelectable = isInteractive;
|
this.elementsSelectable = isInteractive
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,4 +1,4 @@
|
|||||||
import { RevueFlowState, ConnectionMode } from '../types';
|
import { RevueFlowState, ConnectionMode } from '../types'
|
||||||
|
|
||||||
export const initialState: RevueFlowState = {
|
export const initialState: RevueFlowState = {
|
||||||
width: 0,
|
width: 0,
|
||||||
@@ -52,4 +52,4 @@ export const initialState: RevueFlowState = {
|
|||||||
multiSelectionActive: false,
|
multiSelectionActive: false,
|
||||||
|
|
||||||
revueFlowVersion: typeof __REVUE_FLOW_VERSION__ !== 'undefined' ? __REVUE_FLOW_VERSION__ : '-'
|
revueFlowVersion: typeof __REVUE_FLOW_VERSION__ !== 'undefined' ? __REVUE_FLOW_VERSION__ : '-'
|
||||||
};
|
}
|
||||||
|
|||||||
+287
-287
@@ -1,14 +1,14 @@
|
|||||||
import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
import { Selection as D3Selection, ZoomBehavior } from 'd3'
|
||||||
import { CSSProperties, DefineComponent, HTMLAttributes, VNode } from 'vue';
|
import { CSSProperties, DefineComponent, HTMLAttributes, VNode } from 'vue'
|
||||||
import { Store } from 'pinia';
|
import { Store } from 'pinia'
|
||||||
|
|
||||||
export type ElementId = string;
|
export type ElementId = string
|
||||||
|
|
||||||
export type FlowElement<T = any> = Node<T> | Edge<T>;
|
export type FlowElement<T = any> = Node<T> | Edge<T>
|
||||||
|
|
||||||
export type Elements<T = any> = Array<FlowElement<T>>;
|
export type Elements<T = any> = Array<FlowElement<T>>
|
||||||
|
|
||||||
export type Transform = [number, number, number];
|
export type Transform = [number, number, number]
|
||||||
|
|
||||||
export enum Position {
|
export enum Position {
|
||||||
Left = 'left',
|
Left = 'left',
|
||||||
@@ -18,44 +18,44 @@ export enum Position {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface XYPosition {
|
export interface XYPosition {
|
||||||
x: number;
|
x: number
|
||||||
y: number;
|
y: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Dimensions {
|
export interface Dimensions {
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Rect extends Dimensions, XYPosition {}
|
export interface Rect extends Dimensions, XYPosition {}
|
||||||
|
|
||||||
export interface Box extends XYPosition {
|
export interface Box extends XYPosition {
|
||||||
x2: number;
|
x2: number
|
||||||
y2: number;
|
y2: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SnapGrid = [number, number];
|
export type SnapGrid = [number, number]
|
||||||
|
|
||||||
export interface Node<T = any> {
|
export interface Node<T = any> {
|
||||||
id: ElementId;
|
id: ElementId
|
||||||
position: XYPosition;
|
position: XYPosition
|
||||||
type?: string;
|
type?: string
|
||||||
__rf: {
|
__rf: {
|
||||||
position: XYPosition;
|
position: XYPosition
|
||||||
isDragging?: boolean;
|
isDragging?: boolean
|
||||||
width: number | null;
|
width: number | null
|
||||||
height: number | null;
|
height: number | null
|
||||||
handleBounds?: any;
|
handleBounds?: any
|
||||||
};
|
}
|
||||||
data?: T;
|
data?: T
|
||||||
style?: any;
|
style?: any
|
||||||
className?: string;
|
className?: string
|
||||||
targetPosition?: Position;
|
targetPosition?: Position
|
||||||
sourcePosition?: Position;
|
sourcePosition?: Position
|
||||||
isHidden?: boolean;
|
isHidden?: boolean
|
||||||
draggable?: boolean;
|
draggable?: boolean
|
||||||
selectable?: boolean;
|
selectable?: boolean
|
||||||
connectable?: boolean;
|
connectable?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ArrowHeadType {
|
export enum ArrowHeadType {
|
||||||
@@ -64,24 +64,24 @@ export enum ArrowHeadType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Edge<T = any> {
|
export interface Edge<T = any> {
|
||||||
id: ElementId;
|
id: ElementId
|
||||||
type?: string;
|
type?: string
|
||||||
source: ElementId;
|
source: ElementId
|
||||||
target: ElementId;
|
target: ElementId
|
||||||
sourceHandle?: ElementId | null;
|
sourceHandle?: ElementId | null
|
||||||
targetHandle?: ElementId | null;
|
targetHandle?: ElementId | null
|
||||||
label?: string | VNode;
|
label?: string | VNode
|
||||||
labelStyle?: any;
|
labelStyle?: any
|
||||||
labelShowBg?: boolean;
|
labelShowBg?: boolean
|
||||||
labelBgStyle?: any;
|
labelBgStyle?: any
|
||||||
labelBgPadding?: [number, number];
|
labelBgPadding?: [number, number]
|
||||||
labelBgBorderRadius?: number;
|
labelBgBorderRadius?: number
|
||||||
style?: any;
|
style?: any
|
||||||
animated?: boolean;
|
animated?: boolean
|
||||||
arrowHeadType?: ArrowHeadType;
|
arrowHeadType?: ArrowHeadType
|
||||||
isHidden?: boolean;
|
isHidden?: boolean
|
||||||
data?: T;
|
data?: T
|
||||||
className?: string;
|
className?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum BackgroundVariant {
|
export enum BackgroundVariant {
|
||||||
@@ -89,107 +89,107 @@ export enum BackgroundVariant {
|
|||||||
Dots = 'dots'
|
Dots = 'dots'
|
||||||
}
|
}
|
||||||
|
|
||||||
export type HandleType = 'source' | 'target';
|
export type HandleType = 'source' | 'target'
|
||||||
|
|
||||||
export type NodeType = DefineComponent<NodeProps>;
|
export type NodeType = DefineComponent<NodeProps>
|
||||||
|
|
||||||
export type EdgeType = DefineComponent<EdgeSmoothStepProps>;
|
export type EdgeType = DefineComponent<EdgeSmoothStepProps>
|
||||||
|
|
||||||
export interface SelectionRect extends Rect {
|
export interface SelectionRect extends Rect {
|
||||||
startX: number;
|
startX: number
|
||||||
startY: number;
|
startY: number
|
||||||
draw: boolean;
|
draw: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EdgeProps<T = any> {
|
export interface EdgeProps<T = any> {
|
||||||
id: ElementId;
|
id: ElementId
|
||||||
source: ElementId;
|
source: ElementId
|
||||||
target: ElementId;
|
target: ElementId
|
||||||
sourceX: number;
|
sourceX: number
|
||||||
sourceY: number;
|
sourceY: number
|
||||||
targetX: number;
|
targetX: number
|
||||||
targetY: number;
|
targetY: number
|
||||||
selected?: boolean;
|
selected?: boolean
|
||||||
animated?: boolean;
|
animated?: boolean
|
||||||
sourcePosition: Position;
|
sourcePosition: Position
|
||||||
targetPosition: Position;
|
targetPosition: Position
|
||||||
label?: string | VNode;
|
label?: string | VNode
|
||||||
labelStyle?: any;
|
labelStyle?: any
|
||||||
labelShowBg?: boolean;
|
labelShowBg?: boolean
|
||||||
labelBgStyle?: any;
|
labelBgStyle?: any
|
||||||
labelBgPadding?: [number, number];
|
labelBgPadding?: [number, number]
|
||||||
labelBgBorderRadius?: number;
|
labelBgBorderRadius?: number
|
||||||
style?: any;
|
style?: any
|
||||||
arrowHeadType?: ArrowHeadType;
|
arrowHeadType?: ArrowHeadType
|
||||||
markerEndId?: string;
|
markerEndId?: string
|
||||||
data?: T;
|
data?: T
|
||||||
sourceHandleId?: ElementId | null;
|
sourceHandleId?: ElementId | null
|
||||||
targetHandleId?: ElementId | null;
|
targetHandleId?: ElementId | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EdgeSmoothStepProps<T = any> extends EdgeProps<T> {
|
export interface EdgeSmoothStepProps<T = any> extends EdgeProps<T> {
|
||||||
borderRadius?: number;
|
borderRadius?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EdgeTextProps extends HTMLAttributes {
|
export interface EdgeTextProps extends HTMLAttributes {
|
||||||
x: number;
|
x: number
|
||||||
y: number;
|
y: number
|
||||||
label?: string | VNode;
|
label?: string | VNode
|
||||||
labelStyle?: any;
|
labelStyle?: any
|
||||||
labelShowBg?: boolean;
|
labelShowBg?: boolean
|
||||||
labelBgStyle?: any;
|
labelBgStyle?: any
|
||||||
labelBgPadding?: [number, number];
|
labelBgPadding?: [number, number]
|
||||||
labelBgBorderRadius?: number;
|
labelBgBorderRadius?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface NodeProps<T = any> {
|
export interface NodeProps<T = any> {
|
||||||
id: ElementId;
|
id: ElementId
|
||||||
type: string;
|
type: string
|
||||||
data: T;
|
data: T
|
||||||
selected: boolean;
|
selected: boolean
|
||||||
isConnectable: boolean;
|
isConnectable: boolean
|
||||||
xPos?: number;
|
xPos?: number
|
||||||
yPos?: number;
|
yPos?: number
|
||||||
targetPosition?: Position;
|
targetPosition?: Position
|
||||||
sourcePosition?: Position;
|
sourcePosition?: Position
|
||||||
isDragging?: boolean;
|
isDragging?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FitViewParams = {
|
export type FitViewParams = {
|
||||||
padding?: number;
|
padding?: number
|
||||||
includeHiddenNodes?: boolean;
|
includeHiddenNodes?: boolean
|
||||||
minZoom?: number;
|
minZoom?: number
|
||||||
maxZoom?: number;
|
maxZoom?: number
|
||||||
};
|
}
|
||||||
|
|
||||||
export type FlowExportObject<T = any> = {
|
export type FlowExportObject<T = any> = {
|
||||||
elements: Elements<T>;
|
elements: Elements<T>
|
||||||
position: [number, number];
|
position: [number, number]
|
||||||
zoom: number;
|
zoom: number
|
||||||
};
|
}
|
||||||
|
|
||||||
export type FitViewFunc = (fitViewOptions?: FitViewParams) => void;
|
export type FitViewFunc = (fitViewOptions?: FitViewParams) => void
|
||||||
export type ProjectFunc = (position: XYPosition) => XYPosition;
|
export type ProjectFunc = (position: XYPosition) => XYPosition
|
||||||
export type ToObjectFunc<T = any> = () => FlowExportObject<T>;
|
export type ToObjectFunc<T = any> = () => FlowExportObject<T>
|
||||||
|
|
||||||
export type OnLoadParams<T = any> = {
|
export type OnLoadParams<T = any> = {
|
||||||
zoomIn: () => void;
|
zoomIn: () => void
|
||||||
zoomOut: () => void;
|
zoomOut: () => void
|
||||||
zoomTo: (zoomLevel: number) => void;
|
zoomTo: (zoomLevel: number) => void
|
||||||
fitView: FitViewFunc;
|
fitView: FitViewFunc
|
||||||
project: ProjectFunc;
|
project: ProjectFunc
|
||||||
getElements: () => Elements<T>;
|
getElements: () => Elements<T>
|
||||||
setTransform: (transform: FlowTransform) => void;
|
setTransform: (transform: FlowTransform) => void
|
||||||
toObject: ToObjectFunc<T>;
|
toObject: ToObjectFunc<T>
|
||||||
};
|
}
|
||||||
|
|
||||||
export type OnLoadFunc<T = any> = (params: OnLoadParams<T>) => void;
|
export type OnLoadFunc<T = any> = (params: OnLoadParams<T>) => void
|
||||||
|
|
||||||
export interface Connection {
|
export interface Connection {
|
||||||
source: ElementId | null;
|
source: ElementId | null
|
||||||
target: ElementId | null;
|
target: ElementId | null
|
||||||
sourceHandle: ElementId | null;
|
sourceHandle: ElementId | null
|
||||||
targetHandle: ElementId | null;
|
targetHandle: ElementId | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ConnectionMode {
|
export enum ConnectionMode {
|
||||||
@@ -205,69 +205,69 @@ export enum ConnectionLineType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ConnectionLineComponentProps = {
|
export type ConnectionLineComponentProps = {
|
||||||
sourceX: number;
|
sourceX: number
|
||||||
sourceY: number;
|
sourceY: number
|
||||||
sourcePosition?: Position;
|
sourcePosition?: Position
|
||||||
targetX: number;
|
targetX: number
|
||||||
targetY: number;
|
targetY: number
|
||||||
targetPosition?: Position;
|
targetPosition?: Position
|
||||||
connectionLineStyle?: any;
|
connectionLineStyle?: any
|
||||||
connectionLineType: ConnectionLineType;
|
connectionLineType: ConnectionLineType
|
||||||
};
|
}
|
||||||
|
|
||||||
export type ConnectionLineComponent = DefineComponent<ConnectionLineComponentProps>;
|
export type ConnectionLineComponent = DefineComponent<ConnectionLineComponentProps>
|
||||||
|
|
||||||
export type OnConnectFunc = (connection: Connection) => void;
|
export type OnConnectFunc = (connection: Connection) => void
|
||||||
export type OnConnectStartParams = {
|
export type OnConnectStartParams = {
|
||||||
nodeId: ElementId | null;
|
nodeId: ElementId | null
|
||||||
handleId: ElementId | null;
|
handleId: ElementId | null
|
||||||
handleType: HandleType | null;
|
handleType: HandleType | null
|
||||||
};
|
}
|
||||||
export type OnConnectStartFunc = (event: MouseEvent, params: OnConnectStartParams) => void;
|
export type OnConnectStartFunc = (event: MouseEvent, params: OnConnectStartParams) => void
|
||||||
export type OnConnectStopFunc = (event: MouseEvent) => void;
|
export type OnConnectStopFunc = (event: MouseEvent) => void
|
||||||
export type OnConnectEndFunc = (event: MouseEvent) => void;
|
export type OnConnectEndFunc = (event: MouseEvent) => void
|
||||||
|
|
||||||
export type SetConnectionId = {
|
export type SetConnectionId = {
|
||||||
connectionNodeId: ElementId | null;
|
connectionNodeId: ElementId | null
|
||||||
connectionHandleId: ElementId | null;
|
connectionHandleId: ElementId | null
|
||||||
connectionHandleType: HandleType | null;
|
connectionHandleType: HandleType | null
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface HandleElement extends XYPosition, Dimensions {
|
export interface HandleElement extends XYPosition, Dimensions {
|
||||||
id?: ElementId | null;
|
id?: ElementId | null
|
||||||
position: Position;
|
position: Position
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface HandleProps {
|
export interface HandleProps {
|
||||||
type: HandleType;
|
type: HandleType
|
||||||
position: Position;
|
position: Position
|
||||||
isConnectable?: boolean;
|
isConnectable?: boolean
|
||||||
onConnect?: OnConnectFunc;
|
onConnect?: OnConnectFunc
|
||||||
isValidConnection?: (connection: Connection) => boolean;
|
isValidConnection?: (connection: Connection) => boolean
|
||||||
id?: ElementId;
|
id?: ElementId
|
||||||
}
|
}
|
||||||
|
|
||||||
export type NodePosUpdate = {
|
export type NodePosUpdate = {
|
||||||
id: ElementId;
|
id: ElementId
|
||||||
pos: XYPosition;
|
pos: XYPosition
|
||||||
};
|
}
|
||||||
|
|
||||||
export type NodeDiffUpdate = {
|
export type NodeDiffUpdate = {
|
||||||
id?: ElementId;
|
id?: ElementId
|
||||||
diff?: XYPosition;
|
diff?: XYPosition
|
||||||
isDragging?: boolean;
|
isDragging?: boolean
|
||||||
};
|
}
|
||||||
|
|
||||||
export type FlowTransform = {
|
export type FlowTransform = {
|
||||||
x: number;
|
x: number
|
||||||
y: number;
|
y: number
|
||||||
zoom: number;
|
zoom: number
|
||||||
};
|
}
|
||||||
|
|
||||||
export type TranslateExtent = [[number, number], [number, number]];
|
export type TranslateExtent = [[number, number], [number, number]]
|
||||||
export type NodeExtent = TranslateExtent;
|
export type NodeExtent = TranslateExtent
|
||||||
|
|
||||||
export type KeyCode = number | string;
|
export type KeyCode = number | string
|
||||||
|
|
||||||
export enum PanOnScrollMode {
|
export enum PanOnScrollMode {
|
||||||
Free = 'free',
|
Free = 'free',
|
||||||
@@ -276,150 +276,150 @@ export enum PanOnScrollMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ZoomPanHelperFunctions {
|
export interface ZoomPanHelperFunctions {
|
||||||
zoomIn: () => void;
|
zoomIn: () => void
|
||||||
zoomOut: () => void;
|
zoomOut: () => void
|
||||||
zoomTo: (zoomLevel: number) => void;
|
zoomTo: (zoomLevel: number) => void
|
||||||
transform: (transform: FlowTransform) => void;
|
transform: (transform: FlowTransform) => void
|
||||||
fitView: FitViewFunc;
|
fitView: FitViewFunc
|
||||||
setCenter: (x: number, y: number, zoom?: number) => void;
|
setCenter: (x: number, y: number, zoom?: number) => void
|
||||||
fitBounds: (bounds: Rect, padding?: number) => void;
|
fitBounds: (bounds: Rect, padding?: number) => void
|
||||||
project: (position: XYPosition) => XYPosition;
|
project: (position: XYPosition) => XYPosition
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void;
|
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void
|
||||||
|
|
||||||
export type NodeDimensionUpdate = {
|
export type NodeDimensionUpdate = {
|
||||||
id: ElementId;
|
id: ElementId
|
||||||
nodeElement: HTMLDivElement;
|
nodeElement: HTMLDivElement
|
||||||
forceUpdate?: boolean;
|
forceUpdate?: boolean
|
||||||
};
|
}
|
||||||
|
|
||||||
export type InitD3ZoomPayload = {
|
export type InitD3ZoomPayload = {
|
||||||
d3Zoom: ZoomBehavior<Element, unknown>;
|
d3Zoom: ZoomBehavior<Element, unknown>
|
||||||
d3Selection: D3Selection<Element, unknown, null, undefined>;
|
d3Selection: D3Selection<Element, unknown, null, undefined>
|
||||||
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
|
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined
|
||||||
transform: Transform;
|
transform: Transform
|
||||||
};
|
}
|
||||||
|
|
||||||
export interface RevueFlowState {
|
export interface RevueFlowState {
|
||||||
width: number;
|
width: number
|
||||||
height: number;
|
height: number
|
||||||
transform: Transform;
|
transform: Transform
|
||||||
nodes: Node[];
|
nodes: Node[]
|
||||||
edges: Edge[];
|
edges: Edge[]
|
||||||
selectedElements: Elements | null;
|
selectedElements: Elements | null
|
||||||
selectedNodesBbox: Rect;
|
selectedNodesBbox: Rect
|
||||||
|
|
||||||
d3Zoom: ZoomBehavior<Element, unknown> | null;
|
d3Zoom: ZoomBehavior<Element, unknown> | null
|
||||||
d3Selection: D3Selection<Element, unknown, null, undefined> | null;
|
d3Selection: D3Selection<Element, unknown, null, undefined> | null
|
||||||
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
|
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined
|
||||||
minZoom: number;
|
minZoom: number
|
||||||
maxZoom: number;
|
maxZoom: number
|
||||||
translateExtent: TranslateExtent;
|
translateExtent: TranslateExtent
|
||||||
nodeExtent: NodeExtent;
|
nodeExtent: NodeExtent
|
||||||
|
|
||||||
nodesSelectionActive: boolean;
|
nodesSelectionActive: boolean
|
||||||
selectionActive: boolean;
|
selectionActive: boolean
|
||||||
|
|
||||||
userSelectionRect: SelectionRect;
|
userSelectionRect: SelectionRect
|
||||||
|
|
||||||
connectionNodeId: ElementId | null;
|
connectionNodeId: ElementId | null
|
||||||
connectionHandleId: ElementId | null;
|
connectionHandleId: ElementId | null
|
||||||
connectionHandleType: HandleType | null;
|
connectionHandleType: HandleType | null
|
||||||
connectionPosition: XYPosition;
|
connectionPosition: XYPosition
|
||||||
connectionMode: ConnectionMode;
|
connectionMode: ConnectionMode
|
||||||
|
|
||||||
snapToGrid: boolean;
|
snapToGrid: boolean
|
||||||
snapGrid: SnapGrid;
|
snapGrid: SnapGrid
|
||||||
|
|
||||||
nodesDraggable: boolean;
|
nodesDraggable: boolean
|
||||||
nodesConnectable: boolean;
|
nodesConnectable: boolean
|
||||||
elementsSelectable: boolean;
|
elementsSelectable: boolean
|
||||||
|
|
||||||
multiSelectionActive: boolean;
|
multiSelectionActive: boolean
|
||||||
|
|
||||||
revueFlowVersion: string;
|
revueFlowVersion: string
|
||||||
|
|
||||||
onConnect?: OnConnectFunc;
|
onConnect?: OnConnectFunc
|
||||||
onConnectStart?: OnConnectStartFunc;
|
onConnectStart?: OnConnectStartFunc
|
||||||
onConnectStop?: OnConnectStopFunc;
|
onConnectStop?: OnConnectStopFunc
|
||||||
onConnectEnd?: OnConnectEndFunc;
|
onConnectEnd?: OnConnectEndFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
export type RevueFlowActions = {
|
export type RevueFlowActions = {
|
||||||
setElements: (elements: Elements) => void;
|
setElements: (elements: Elements) => void
|
||||||
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
|
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void
|
||||||
updateNodePos: (payload: NodePosUpdate) => void;
|
updateNodePos: (payload: NodePosUpdate) => void
|
||||||
updateNodePosDiff: (payload: NodeDiffUpdate) => void;
|
updateNodePosDiff: (payload: NodeDiffUpdate) => void
|
||||||
setUserSelection: (mousePos: XYPosition) => void;
|
setUserSelection: (mousePos: XYPosition) => void
|
||||||
updateUserSelection: (mousePos: XYPosition) => void;
|
updateUserSelection: (mousePos: XYPosition) => void
|
||||||
unsetUserSelection: () => void;
|
unsetUserSelection: () => void
|
||||||
addSelectedElements: (elements: Elements) => void;
|
addSelectedElements: (elements: Elements) => void
|
||||||
initD3Zoom: (payload: InitD3ZoomPayload) => void;
|
initD3Zoom: (payload: InitD3ZoomPayload) => void
|
||||||
setMinZoom: (zoom: number) => void;
|
setMinZoom: (zoom: number) => void
|
||||||
setMaxZoom: (zoom: number) => void;
|
setMaxZoom: (zoom: number) => void
|
||||||
setTranslateExtent: (translateExtent: TranslateExtent) => void;
|
setTranslateExtent: (translateExtent: TranslateExtent) => void
|
||||||
setNodeExtent: (nodeExtent: NodeExtent) => void;
|
setNodeExtent: (nodeExtent: NodeExtent) => void
|
||||||
resetSelectedElements: () => void;
|
resetSelectedElements: () => void
|
||||||
unsetNodesSelection: () => void;
|
unsetNodesSelection: () => void
|
||||||
updateSize: (size: Dimensions) => void;
|
updateSize: (size: Dimensions) => void
|
||||||
setConnectionNodeId: (payload: SetConnectionId) => void;
|
setConnectionNodeId: (payload: SetConnectionId) => void
|
||||||
setInteractive: (isInteractive: boolean) => void;
|
setInteractive: (isInteractive: boolean) => void
|
||||||
};
|
}
|
||||||
|
|
||||||
export type RevueFlowStore = Store<string, RevueFlowState, any, RevueFlowActions>;
|
export type RevueFlowStore = Store<string, RevueFlowState, any, RevueFlowActions>
|
||||||
|
|
||||||
export type UpdateNodeInternals = (nodeId: ElementId) => void;
|
export type UpdateNodeInternals = (nodeId: ElementId) => void
|
||||||
|
|
||||||
export interface RevueFlowProps extends Omit<HTMLAttributes, 'onLoad'> {
|
export interface RevueFlowProps extends Omit<HTMLAttributes, 'onLoad'> {
|
||||||
modelValue: Elements;
|
modelValue: Elements
|
||||||
nodeTypes?: Record<string, NodeType>;
|
nodeTypes?: Record<string, NodeType>
|
||||||
edgeTypes?: Record<string, EdgeType>;
|
edgeTypes?: Record<string, EdgeType>
|
||||||
connectionMode?: ConnectionMode;
|
connectionMode?: ConnectionMode
|
||||||
connectionLineType?: ConnectionLineType;
|
connectionLineType?: ConnectionLineType
|
||||||
connectionLineStyle?: CSSProperties;
|
connectionLineStyle?: CSSProperties
|
||||||
connectionLineComponent?: ConnectionLineComponent;
|
connectionLineComponent?: ConnectionLineComponent
|
||||||
deleteKeyCode?: KeyCode;
|
deleteKeyCode?: KeyCode
|
||||||
selectionKeyCode?: KeyCode;
|
selectionKeyCode?: KeyCode
|
||||||
multiSelectionKeyCode?: KeyCode;
|
multiSelectionKeyCode?: KeyCode
|
||||||
zoomActivationKeyCode?: KeyCode;
|
zoomActivationKeyCode?: KeyCode
|
||||||
snapToGrid?: boolean;
|
snapToGrid?: boolean
|
||||||
snapGrid?: [number, number];
|
snapGrid?: [number, number]
|
||||||
onlyRenderVisibleElements?: boolean;
|
onlyRenderVisibleElements?: boolean
|
||||||
nodesDraggable?: boolean;
|
nodesDraggable?: boolean
|
||||||
nodesConnectable?: boolean;
|
nodesConnectable?: boolean
|
||||||
elementsSelectable?: boolean;
|
elementsSelectable?: boolean
|
||||||
selectNodesOnDrag?: boolean;
|
selectNodesOnDrag?: boolean
|
||||||
paneMoveable?: boolean;
|
paneMoveable?: boolean
|
||||||
minZoom?: number;
|
minZoom?: number
|
||||||
maxZoom?: number;
|
maxZoom?: number
|
||||||
defaultZoom?: number;
|
defaultZoom?: number
|
||||||
defaultPosition?: [number, number];
|
defaultPosition?: [number, number]
|
||||||
translateExtent?: TranslateExtent;
|
translateExtent?: TranslateExtent
|
||||||
nodeExtent?: NodeExtent;
|
nodeExtent?: NodeExtent
|
||||||
arrowHeadColor?: string;
|
arrowHeadColor?: string
|
||||||
markerEndId?: string;
|
markerEndId?: string
|
||||||
zoomOnScroll?: boolean;
|
zoomOnScroll?: boolean
|
||||||
zoomOnPinch?: boolean;
|
zoomOnPinch?: boolean
|
||||||
panOnScroll?: boolean;
|
panOnScroll?: boolean
|
||||||
panOnScrollSpeed?: number;
|
panOnScrollSpeed?: number
|
||||||
panOnScrollMode?: PanOnScrollMode;
|
panOnScrollMode?: PanOnScrollMode
|
||||||
zoomOnDoubleClick?: boolean;
|
zoomOnDoubleClick?: boolean
|
||||||
edgeUpdaterRadius?: number;
|
edgeUpdaterRadius?: number
|
||||||
nodeTypesId?: string;
|
nodeTypesId?: string
|
||||||
edgeTypesId?: string;
|
edgeTypesId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GraphViewProps extends Required<RevueFlowProps> {
|
export interface GraphViewProps extends Required<RevueFlowProps> {
|
||||||
selectionKeyCode: KeyCode;
|
selectionKeyCode: KeyCode
|
||||||
deleteKeyCode: KeyCode;
|
deleteKeyCode: KeyCode
|
||||||
multiSelectionKeyCode: KeyCode;
|
multiSelectionKeyCode: KeyCode
|
||||||
connectionLineType: ConnectionLineType;
|
connectionLineType: ConnectionLineType
|
||||||
snapToGrid: boolean;
|
snapToGrid: boolean
|
||||||
snapGrid: [number, number];
|
snapGrid: [number, number]
|
||||||
onlyRenderVisibleElements: boolean;
|
onlyRenderVisibleElements: boolean
|
||||||
defaultZoom: number;
|
defaultZoom: number
|
||||||
defaultPosition: [number, number];
|
defaultPosition: [number, number]
|
||||||
arrowHeadColor: string;
|
arrowHeadColor: string
|
||||||
selectNodesOnDrag: boolean;
|
selectNodesOnDrag: boolean
|
||||||
}
|
}
|
||||||
|
|||||||
+91
-91
@@ -1,4 +1,4 @@
|
|||||||
import { clampPosition, clamp } from './index';
|
import { clampPosition, clamp } from './index'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ElementId,
|
ElementId,
|
||||||
@@ -13,47 +13,47 @@ import {
|
|||||||
FlowExportObject,
|
FlowExportObject,
|
||||||
NodeExtent,
|
NodeExtent,
|
||||||
RevueFlowStore
|
RevueFlowStore
|
||||||
} from '../types';
|
} from '../types'
|
||||||
|
|
||||||
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
|
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
|
||||||
'id' in element && 'source' in element && 'target' in element;
|
'id' in element && 'source' in element && 'target' in element
|
||||||
|
|
||||||
export const isNode = (element: Node | Connection | Edge): element is Node =>
|
export const isNode = (element: Node | Connection | Edge): element is Node =>
|
||||||
'id' in element && !('source' in element) && !('target' in element);
|
'id' in element && !('source' in element) && !('target' in element)
|
||||||
|
|
||||||
export const getOutgoers = (node: Node, elements: Elements): Node[] => {
|
export const getOutgoers = (node: Node, elements: Elements): Node[] => {
|
||||||
if (!isNode(node)) {
|
if (!isNode(node)) {
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const outgoerIds = elements.filter((e) => isEdge(e) && e.source === node.id).map((e) => (e as Edge).target);
|
const outgoerIds = elements.filter((e) => isEdge(e) && e.source === node.id).map((e) => (e as Edge).target)
|
||||||
return elements.filter((e) => outgoerIds.includes(e.id)) as Node[];
|
return elements.filter((e) => outgoerIds.includes(e.id)) as Node[]
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getIncomers = (node: Node, elements: Elements): Node[] => {
|
export const getIncomers = (node: Node, elements: Elements): Node[] => {
|
||||||
if (!isNode(node)) {
|
if (!isNode(node)) {
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
const incomersIds = elements.filter((e) => isEdge(e) && e.target === node.id).map((e) => (e as Edge).source);
|
const incomersIds = elements.filter((e) => isEdge(e) && e.target === node.id).map((e) => (e as Edge).source)
|
||||||
return elements.filter((e) => incomersIds.includes(e.id)) as Node[];
|
return elements.filter((e) => incomersIds.includes(e.id)) as Node[]
|
||||||
};
|
}
|
||||||
|
|
||||||
export const removeElements = (elementsToRemove: Elements, elements: Elements): Elements => {
|
export const removeElements = (elementsToRemove: Elements, elements: Elements): Elements => {
|
||||||
const nodeIdsToRemove = elementsToRemove.map((n) => n.id);
|
const nodeIdsToRemove = elementsToRemove.map((n) => n.id)
|
||||||
|
|
||||||
return elements.filter((element) => {
|
return elements.filter((element) => {
|
||||||
const edgeElement = element as Edge;
|
const edgeElement = element as Edge
|
||||||
return !(
|
return !(
|
||||||
nodeIdsToRemove.includes(element.id) ||
|
nodeIdsToRemove.includes(element.id) ||
|
||||||
nodeIdsToRemove.includes(edgeElement.target) ||
|
nodeIdsToRemove.includes(edgeElement.target) ||
|
||||||
nodeIdsToRemove.includes(edgeElement.source)
|
nodeIdsToRemove.includes(edgeElement.source)
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection): ElementId =>
|
const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection): ElementId =>
|
||||||
`revueflow__edge-${source}${sourceHandle}-${target}${targetHandle}`;
|
`revueflow__edge-${source}${sourceHandle}-${target}${targetHandle}`
|
||||||
|
|
||||||
const connectionExists = (edge: Edge, elements: Elements) => {
|
const connectionExists = (edge: Edge, elements: Elements) => {
|
||||||
return elements.some(
|
return elements.some(
|
||||||
@@ -63,43 +63,43 @@ const connectionExists = (edge: Edge, elements: Elements) => {
|
|||||||
el.target === edge.target &&
|
el.target === edge.target &&
|
||||||
(el.sourceHandle === edge.sourceHandle || (!el.sourceHandle && !edge.sourceHandle)) &&
|
(el.sourceHandle === edge.sourceHandle || (!el.sourceHandle && !edge.sourceHandle)) &&
|
||||||
(el.targetHandle === edge.targetHandle || (!el.targetHandle && !edge.targetHandle))
|
(el.targetHandle === edge.targetHandle || (!el.targetHandle && !edge.targetHandle))
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export const addEdge = (edgeParams: Edge | Connection, elements: Elements): Elements => {
|
export const addEdge = (edgeParams: Edge | Connection, elements: Elements): Elements => {
|
||||||
if (!edgeParams.source || !edgeParams.target) {
|
if (!edgeParams.source || !edgeParams.target) {
|
||||||
console.log("Can't create edge. An edge needs a source and a target.");
|
console.log("Can't create edge. An edge needs a source and a target.")
|
||||||
return elements;
|
return elements
|
||||||
}
|
}
|
||||||
|
|
||||||
let edge: Edge;
|
let edge: Edge
|
||||||
if (isEdge(edgeParams)) {
|
if (isEdge(edgeParams)) {
|
||||||
edge = { ...edgeParams };
|
edge = { ...edgeParams }
|
||||||
} else {
|
} else {
|
||||||
edge = {
|
edge = {
|
||||||
...edgeParams,
|
...edgeParams,
|
||||||
id: getEdgeId(edgeParams)
|
id: getEdgeId(edgeParams)
|
||||||
} as Edge;
|
} as Edge
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectionExists(edge, elements)) {
|
if (connectionExists(edge, elements)) {
|
||||||
return elements;
|
return elements
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements.concat(edge);
|
return elements.concat(edge)
|
||||||
};
|
}
|
||||||
|
|
||||||
export const updateEdge = (oldEdge: Edge, newConnection: Connection, elements: Elements): Elements => {
|
export const updateEdge = (oldEdge: Edge, newConnection: Connection, elements: Elements): Elements => {
|
||||||
if (!newConnection.source || !newConnection.target) {
|
if (!newConnection.source || !newConnection.target) {
|
||||||
console.warn("Can't create new edge. An edge needs a source and a target.");
|
console.warn("Can't create new edge. An edge needs a source and a target.")
|
||||||
return elements;
|
return elements
|
||||||
}
|
}
|
||||||
|
|
||||||
const foundEdge = elements.find((e) => isEdge(e) && e.id === oldEdge.id) as Edge;
|
const foundEdge = elements.find((e) => isEdge(e) && e.id === oldEdge.id) as Edge
|
||||||
|
|
||||||
if (!foundEdge) {
|
if (!foundEdge) {
|
||||||
console.warn(`The old edge with id=${oldEdge.id} does not exist.`);
|
console.warn(`The old edge with id=${oldEdge.id} does not exist.`)
|
||||||
return elements;
|
return elements
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove old edge and create the new edge with parameters of old edge.
|
// Remove old edge and create the new edge with parameters of old edge.
|
||||||
@@ -110,10 +110,10 @@ export const updateEdge = (oldEdge: Edge, newConnection: Connection, elements: E
|
|||||||
target: newConnection.target,
|
target: newConnection.target,
|
||||||
sourceHandle: newConnection.sourceHandle,
|
sourceHandle: newConnection.sourceHandle,
|
||||||
targetHandle: newConnection.targetHandle
|
targetHandle: newConnection.targetHandle
|
||||||
} as Edge;
|
} as Edge
|
||||||
|
|
||||||
return elements.filter((e) => e.id !== oldEdge.id).concat(edge);
|
return elements.filter((e) => e.id !== oldEdge.id).concat(edge)
|
||||||
};
|
}
|
||||||
|
|
||||||
export const pointToRendererPoint = (
|
export const pointToRendererPoint = (
|
||||||
{ x, y }: XYPosition,
|
{ x, y }: XYPosition,
|
||||||
@@ -124,23 +124,23 @@ export const pointToRendererPoint = (
|
|||||||
const position: XYPosition = {
|
const position: XYPosition = {
|
||||||
x: (x - tx) / tScale,
|
x: (x - tx) / tScale,
|
||||||
y: (y - ty) / tScale
|
y: (y - ty) / tScale
|
||||||
};
|
}
|
||||||
|
|
||||||
if (snapToGrid) {
|
if (snapToGrid) {
|
||||||
return {
|
return {
|
||||||
x: snapX * Math.round(position.x / snapX),
|
x: snapX * Math.round(position.x / snapX),
|
||||||
y: snapY * Math.round(position.y / snapY)
|
y: snapY * Math.round(position.y / snapY)
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return position;
|
return position
|
||||||
};
|
}
|
||||||
|
|
||||||
export const onLoadProject = (currentStore: RevueFlowStore) => {
|
export const onLoadProject = (currentStore: RevueFlowStore) => {
|
||||||
return (position: XYPosition): XYPosition => {
|
return (position: XYPosition): XYPosition => {
|
||||||
return pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid);
|
return pointToRendererPoint(position, currentStore.transform, currentStore.snapToGrid, currentStore.snapGrid)
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const parseNode = (node: Node, nodeExtent: NodeExtent): Node => {
|
export const parseNode = (node: Node, nodeExtent: NodeExtent): Node => {
|
||||||
return {
|
return {
|
||||||
@@ -154,8 +154,8 @@ export const parseNode = (node: Node, nodeExtent: NodeExtent): Node => {
|
|||||||
handleBounds: {},
|
handleBounds: {},
|
||||||
isDragging: false
|
isDragging: false
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const parseEdge = (edge: Edge): Edge => {
|
export const parseEdge = (edge: Edge): Edge => {
|
||||||
return {
|
return {
|
||||||
@@ -166,32 +166,32 @@ export const parseEdge = (edge: Edge): Edge => {
|
|||||||
targetHandle: edge.targetHandle ? edge.targetHandle.toString() : null,
|
targetHandle: edge.targetHandle ? edge.targetHandle.toString() : null,
|
||||||
id: edge.id.toString(),
|
id: edge.id.toString(),
|
||||||
type: edge.type || 'default'
|
type: edge.type || 'default'
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const getBoundsOfBoxes = (box1: Box, box2: Box): Box => ({
|
const getBoundsOfBoxes = (box1: Box, box2: Box): Box => ({
|
||||||
x: Math.min(box1.x, box2.x),
|
x: Math.min(box1.x, box2.x),
|
||||||
y: Math.min(box1.y, box2.y),
|
y: Math.min(box1.y, box2.y),
|
||||||
x2: Math.max(box1.x2, box2.x2),
|
x2: Math.max(box1.x2, box2.x2),
|
||||||
y2: Math.max(box1.y2, box2.y2)
|
y2: Math.max(box1.y2, box2.y2)
|
||||||
});
|
})
|
||||||
|
|
||||||
export const rectToBox = ({ x, y, width, height }: Rect): Box => ({
|
export const rectToBox = ({ x, y, width, height }: Rect): Box => ({
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
x2: x + width,
|
x2: x + width,
|
||||||
y2: y + height
|
y2: y + height
|
||||||
});
|
})
|
||||||
|
|
||||||
export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
|
export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
width: x2 - x,
|
width: x2 - x,
|
||||||
height: y2 - y
|
height: y2 - y
|
||||||
});
|
})
|
||||||
|
|
||||||
export const getBoundsofRects = (rect1: Rect, rect2: Rect): Rect =>
|
export const getBoundsofRects = (rect1: Rect, rect2: Rect): Rect =>
|
||||||
boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)));
|
boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)))
|
||||||
|
|
||||||
export const getRectOfNodes = (nodes: Node[]): Rect => {
|
export const getRectOfNodes = (nodes: Node[]): Rect => {
|
||||||
const box = nodes.reduce(
|
const box = nodes.reduce(
|
||||||
@@ -205,15 +205,15 @@ export const getRectOfNodes = (nodes: Node[]): Rect => {
|
|||||||
} as any)
|
} as any)
|
||||||
),
|
),
|
||||||
{ x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity }
|
{ x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity }
|
||||||
);
|
)
|
||||||
|
|
||||||
return boxToRect(box);
|
return boxToRect(box)
|
||||||
};
|
}
|
||||||
|
|
||||||
export const graphPosToZoomedPos = ({ x, y }: XYPosition, [tx, ty, tScale]: Transform): XYPosition => ({
|
export const graphPosToZoomedPos = ({ x, y }: XYPosition, [tx, ty, tScale]: Transform): XYPosition => ({
|
||||||
x: x * tScale + tx,
|
x: x * tScale + tx,
|
||||||
y: y * tScale + ty
|
y: y * tScale + ty
|
||||||
});
|
})
|
||||||
|
|
||||||
export const getNodesInside = (nodes: Node[], rect: Rect, [tx, ty, tScale]: Transform = [0, 0, 1], partially = false): Node[] => {
|
export const getNodesInside = (nodes: Node[], rect: Rect, [tx, ty, tScale]: Transform = [0, 0, 1], partially = false): Node[] => {
|
||||||
const rBox = rectToBox({
|
const rBox = rectToBox({
|
||||||
@@ -221,54 +221,54 @@ export const getNodesInside = (nodes: Node[], rect: Rect, [tx, ty, tScale]: Tran
|
|||||||
y: (rect.y - ty) / tScale,
|
y: (rect.y - ty) / tScale,
|
||||||
width: rect.width / tScale,
|
width: rect.width / tScale,
|
||||||
height: rect.height / tScale
|
height: rect.height / tScale
|
||||||
});
|
})
|
||||||
|
|
||||||
return nodes.filter(({ __rf: { position, width, height, isDragging } }) => {
|
return nodes.filter(({ __rf: { position, width, height, isDragging } }) => {
|
||||||
const nBox = rectToBox({ ...position, width, height } as any);
|
const nBox = rectToBox({ ...position, width, height } as any)
|
||||||
const xOverlap = Math.max(0, Math.min(rBox.x2, nBox.x2) - Math.max(rBox.x, nBox.x));
|
const xOverlap = Math.max(0, Math.min(rBox.x2, nBox.x2) - Math.max(rBox.x, nBox.x))
|
||||||
const yOverlap = Math.max(0, Math.min(rBox.y2, nBox.y2) - Math.max(rBox.y, nBox.y));
|
const yOverlap = Math.max(0, Math.min(rBox.y2, nBox.y2) - Math.max(rBox.y, nBox.y))
|
||||||
const overlappingArea = Math.ceil(xOverlap * yOverlap);
|
const overlappingArea = Math.ceil(xOverlap * yOverlap)
|
||||||
|
|
||||||
if (width === null || height === null || isDragging) {
|
if (width === null || height === null || isDragging) {
|
||||||
// nodes are initialized with width and height = null
|
// nodes are initialized with width and height = null
|
||||||
return true;
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (partially) {
|
if (partially) {
|
||||||
return overlappingArea > 0;
|
return overlappingArea > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const area = width * height;
|
const area = width * height
|
||||||
|
|
||||||
return overlappingArea >= area;
|
return overlappingArea >= area
|
||||||
});
|
})
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getConnectedEdges = (nodes: Node[], edges: Edge[]): Edge[] => {
|
export const getConnectedEdges = (nodes: Node[], edges: Edge[]): Edge[] => {
|
||||||
const nodeIds = nodes.map((node) => node.id);
|
const nodeIds = nodes.map((node) => node.id)
|
||||||
|
|
||||||
return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target));
|
return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target))
|
||||||
};
|
}
|
||||||
|
|
||||||
const parseElements = (nodes: Node[], edges: Edge[]): Elements => {
|
const parseElements = (nodes: Node[], edges: Edge[]): Elements => {
|
||||||
return [
|
return [
|
||||||
...nodes.map((node) => {
|
...nodes.map((node) => {
|
||||||
const n = { ...node };
|
const n = { ...node }
|
||||||
|
|
||||||
n.position = n.__rf.position;
|
n.position = n.__rf.position
|
||||||
|
|
||||||
n.__rf = {} as any;
|
n.__rf = {} as any
|
||||||
return n;
|
return n
|
||||||
}),
|
}),
|
||||||
...edges.map((e) => ({ ...e }))
|
...edges.map((e) => ({ ...e }))
|
||||||
];
|
]
|
||||||
};
|
}
|
||||||
|
|
||||||
export const onLoadGetElements = (currentStore: RevueFlowStore) => {
|
export const onLoadGetElements = (currentStore: RevueFlowStore) => {
|
||||||
return (): Elements => {
|
return (): Elements => {
|
||||||
return parseElements(currentStore.nodes || [], currentStore.edges || []);
|
return parseElements(currentStore.nodes || [], currentStore.edges || [])
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const onLoadToObject = (currentStore: RevueFlowStore) => {
|
export const onLoadToObject = (currentStore: RevueFlowStore) => {
|
||||||
return (): FlowExportObject => {
|
return (): FlowExportObject => {
|
||||||
@@ -276,9 +276,9 @@ export const onLoadToObject = (currentStore: RevueFlowStore) => {
|
|||||||
elements: parseElements(currentStore.nodes || [], currentStore.edges || []),
|
elements: parseElements(currentStore.nodes || [], currentStore.edges || []),
|
||||||
position: [currentStore.transform[0], currentStore.transform[1]],
|
position: [currentStore.transform[0], currentStore.transform[1]],
|
||||||
zoom: currentStore.transform[2]
|
zoom: currentStore.transform[2]
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getTransformForBounds = (
|
export const getTransformForBounds = (
|
||||||
bounds: Rect,
|
bounds: Rect,
|
||||||
@@ -288,14 +288,14 @@ export const getTransformForBounds = (
|
|||||||
maxZoom: number,
|
maxZoom: number,
|
||||||
padding = 0.1
|
padding = 0.1
|
||||||
): Transform => {
|
): Transform => {
|
||||||
const xZoom = width / (bounds.width * (1 + padding));
|
const xZoom = width / (bounds.width * (1 + padding))
|
||||||
const yZoom = height / (bounds.height * (1 + padding));
|
const yZoom = height / (bounds.height * (1 + padding))
|
||||||
const zoom = Math.min(xZoom, yZoom);
|
const zoom = Math.min(xZoom, yZoom)
|
||||||
const clampedZoom = clamp(zoom, minZoom, maxZoom);
|
const clampedZoom = clamp(zoom, minZoom, maxZoom)
|
||||||
const boundsCenterX = bounds.x + bounds.width / 2;
|
const boundsCenterX = bounds.x + bounds.width / 2
|
||||||
const boundsCenterY = bounds.y + bounds.height / 2;
|
const boundsCenterY = bounds.y + bounds.height / 2
|
||||||
const x = width / 2 - boundsCenterX * clampedZoom;
|
const x = width / 2 - boundsCenterX * clampedZoom
|
||||||
const y = height / 2 - boundsCenterY * clampedZoom;
|
const y = height / 2 - boundsCenterY * clampedZoom
|
||||||
|
|
||||||
return [x, y, clampedZoom];
|
return [x, y, clampedZoom]
|
||||||
};
|
}
|
||||||
|
|||||||
+8
-8
@@ -1,21 +1,21 @@
|
|||||||
import { Dimensions, XYPosition, NodeExtent } from '../types';
|
import { Dimensions, XYPosition, NodeExtent } from '../types'
|
||||||
|
|
||||||
export const isInputDOMNode = (e: KeyboardEvent | MouseEvent): boolean => {
|
export const isInputDOMNode = (e: KeyboardEvent | MouseEvent): boolean => {
|
||||||
const target = e.target as HTMLElement;
|
const target = e.target as HTMLElement
|
||||||
return ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName) || target.hasAttribute('contentEditable');
|
return ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName) || target.hasAttribute('contentEditable')
|
||||||
};
|
}
|
||||||
|
|
||||||
export const getDimensions = (node: HTMLDivElement): Dimensions => ({
|
export const getDimensions = (node: HTMLDivElement): Dimensions => ({
|
||||||
width: node.offsetWidth,
|
width: node.offsetWidth,
|
||||||
height: node.offsetHeight
|
height: node.offsetHeight
|
||||||
});
|
})
|
||||||
|
|
||||||
export const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max);
|
export const clamp = (val: number, min = 0, max = 1): number => Math.min(Math.max(val, min), max)
|
||||||
|
|
||||||
export const clampPosition = (position: XYPosition, extent: NodeExtent): XYPosition => ({
|
export const clampPosition = (position: XYPosition, extent: NodeExtent): XYPosition => ({
|
||||||
x: clamp(position.x, extent[0][0], extent[1][0]),
|
x: clamp(position.x, extent[0][0], extent[1][0]),
|
||||||
y: clamp(position.y, extent[0][1], extent[1][1])
|
y: clamp(position.y, extent[0][1], extent[1][1])
|
||||||
});
|
})
|
||||||
|
|
||||||
export const getHostForElement = (element: HTMLElement): Document | ShadowRoot =>
|
export const getHostForElement = (element: HTMLElement): Document | ShadowRoot =>
|
||||||
(element.getRootNode() as Document | ShadowRoot) || window.document;
|
(element.getRootNode() as Document | ShadowRoot) || window.document
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { InjectionKey } from 'vue';
|
import { InjectionKey } from 'vue'
|
||||||
import { RevueFlowStore } from '../types';
|
import { RevueFlowStore } from '../types'
|
||||||
import { RevueFlowHooks } from '../hooks/RevueFlowHooks';
|
import { RevueFlowHooks } from '../hooks/RevueFlowHooks'
|
||||||
|
|
||||||
export const StoreKey: InjectionKey<RevueFlowStore> = Symbol('store');
|
export const StoreKey: InjectionKey<RevueFlowStore> = Symbol('store')
|
||||||
export const HooksKey: InjectionKey<RevueFlowHooks> = Symbol('hooks');
|
export const HooksKey: InjectionKey<RevueFlowHooks> = Symbol('hooks')
|
||||||
export const NodeIdContextKey: InjectionKey<string> = Symbol('NodeIdContext');
|
export const NodeIdContextKey: InjectionKey<string> = Symbol('NodeIdContext')
|
||||||
|
|||||||
+35
-21
@@ -1,28 +1,42 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "esnext",
|
"baseUrl": ".",
|
||||||
"module": "esnext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "node",
|
"target": "es2016",
|
||||||
|
"lib": [
|
||||||
|
"DOM",
|
||||||
|
"ESNext"
|
||||||
|
],
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"jsx": "preserve",
|
|
||||||
"jsxFactory": "",
|
|
||||||
"sourceMap": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
"lib": ["es6", "dom", "es2016", "es2017", "es2020"],
|
"incremental": false,
|
||||||
"types": ["@types/node"],
|
"skipLibCheck": true,
|
||||||
"noImplicitAny": true,
|
"moduleResolution": "node",
|
||||||
"strictNullChecks": true,
|
"resolveJsonModule": true,
|
||||||
"strictFunctionTypes": true,
|
|
||||||
"strictPropertyInitialization": true,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"alwaysStrict": true,
|
|
||||||
"noUnusedLocals": true,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": true,
|
"strictNullChecks": true,
|
||||||
"noImplicitReturns": false,
|
"forceConsistentCasingInFileNames": true,
|
||||||
"noFallthroughCasesInSwitch": true,
|
"jsx": "preserve",
|
||||||
"allowSyntheticDefaultImports": true,
|
"types": [
|
||||||
|
"vite/client",
|
||||||
|
"@types/node"
|
||||||
|
],
|
||||||
|
"paths": {
|
||||||
|
"~/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
"include": [
|
||||||
"exclude": ["node_modules", "build", "dist", "example"]
|
"src/**/*.ts",
|
||||||
|
"src/**/*.d.ts",
|
||||||
|
"src/**/*.tsx",
|
||||||
|
"src/**/*.vue"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"build",
|
||||||
|
"dist",
|
||||||
|
"example"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-6
@@ -1,12 +1,27 @@
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite'
|
||||||
import vue from '@vitejs/plugin-vue';
|
import { resolve } from 'path'
|
||||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
import vue from '@vitejs/plugin-vue'
|
||||||
import svgLoader from 'vite-svg-loader';
|
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||||
|
import svgLoader from 'vite-svg-loader'
|
||||||
|
import AutoImport from 'unplugin-auto-import/vite'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'~/': `${resolve(__dirname, 'src')}/`
|
||||||
|
}
|
||||||
|
},
|
||||||
build: {
|
build: {
|
||||||
outDir: 'build'
|
outDir: 'build'
|
||||||
},
|
},
|
||||||
plugins: [vue(), vueJsx(), svgLoader()]
|
plugins: [
|
||||||
});
|
vue(),
|
||||||
|
vueJsx(),
|
||||||
|
svgLoader(), // https://github.com/antfu/unplugin-auto-import
|
||||||
|
AutoImport({
|
||||||
|
imports: ['vue', '@vueuse/core'],
|
||||||
|
dts: 'src/auto-imports.d.ts'
|
||||||
|
})
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user