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-comment': 0,
|
||||
'@typescript-eslint/no-empty-function': 0,
|
||||
'@typescript-eslint/explicit-module-boundary-types': 0,
|
||||
'no-use-before-define': 0,
|
||||
'no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-unused-vars': ['warn'],
|
||||
indent: ['warn', 2, { SwitchCase: 1, flatTernaryExpressions: true }],
|
||||
quotes: ['error', 'single', { avoidEscape: true }],
|
||||
semi: ['error', 'always'],
|
||||
'import/no-mutable-exports': 0,
|
||||
'no-cond-assign': [2],
|
||||
'no-console': 'off',
|
||||
@@ -16,28 +16,27 @@ const baseRules = {
|
||||
'@typescript/camelcase': 0,
|
||||
'vue/no-v-html': 'off',
|
||||
'vue/attribute-hyphenation': 0,
|
||||
'prettier/prettier': [
|
||||
'warn',
|
||||
{
|
||||
singleQuote: true,
|
||||
trailingComma: 'none',
|
||||
semi: true,
|
||||
bracketSpacing: true,
|
||||
vueIndentScriptAndStyle: false,
|
||||
printWidth: 130
|
||||
}
|
||||
]
|
||||
};
|
||||
'no-undef': 'off',
|
||||
'prettier/prettier': ['error', {}, { usePrettierrc: true }]
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
jsx: true
|
||||
}
|
||||
},
|
||||
env: {
|
||||
node: true,
|
||||
commonjs: true,
|
||||
es6: 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: [],
|
||||
rules: baseRules
|
||||
};
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"semi": true,
|
||||
"semi": false,
|
||||
"bracketSpacing": true,
|
||||
"vueIndentScriptAndStyle": false,
|
||||
"printWidth": 130
|
||||
|
||||
+7
-7
@@ -1,6 +1,6 @@
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import Header from './Header';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import Header from './Header'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App',
|
||||
@@ -8,13 +8,13 @@ export default defineComponent({
|
||||
Header
|
||||
},
|
||||
setup() {
|
||||
const route = useRoute();
|
||||
const key = computed(() => route.fullPath);
|
||||
const route = useRoute()
|
||||
const key = computed(() => route.fullPath)
|
||||
return () => (
|
||||
<>
|
||||
<Header />
|
||||
<router-view ref="view" key={key.value} />
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
const onDragStart = (event: DragEvent, nodeType: string) => {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.setData('application/revueflow', nodeType);
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
event.dataTransfer.setData('application/revueflow', nodeType)
|
||||
event.dataTransfer.effectAllowed = 'move'
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const Sidebar = () => {
|
||||
return (
|
||||
@@ -19,7 +19,7 @@ const Sidebar = () => {
|
||||
Output Node
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default Sidebar;
|
||||
export default Sidebar
|
||||
|
||||
@@ -11,22 +11,22 @@ import RevueFlow, {
|
||||
Connection,
|
||||
Edge,
|
||||
Elements
|
||||
} from '../../src';
|
||||
import { getElements } from './utils';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
} from '../../src'
|
||||
import { getElements } from './utils'
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
const onLoad = (reactFlowInstance: OnLoadParams) => {
|
||||
reactFlowInstance.fitView();
|
||||
console.log(reactFlowInstance.getElements());
|
||||
};
|
||||
reactFlowInstance.fitView()
|
||||
console.log(reactFlowInstance.getElements())
|
||||
}
|
||||
|
||||
const initialElements = getElements();
|
||||
const initialElements = getElements()
|
||||
|
||||
const EdgeTypesFlow = defineComponent({
|
||||
setup() {
|
||||
const elements = ref<Elements>(initialElements);
|
||||
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value));
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
|
||||
const elements = ref<Elements>(initialElements)
|
||||
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||
|
||||
return () => (
|
||||
<RevueFlow
|
||||
@@ -36,8 +36,8 @@ const EdgeTypesFlow = defineComponent({
|
||||
onConnect={onConnect}
|
||||
minZoom={0.2}
|
||||
></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 nodeGapWidth = nodeWidth * 2;
|
||||
const nodeStyle = { width: nodeWidth + 'px', fontSize: '11px', color: 'white' };
|
||||
const nodeWidth = 80
|
||||
const nodeGapWidth = nodeWidth * 2
|
||||
const nodeStyle = { width: nodeWidth + 'px', fontSize: '11px', color: 'white' }
|
||||
|
||||
const sourceTargetPositions = [
|
||||
{ source: Position.Bottom, target: Position.Top },
|
||||
{ source: Position.Right, target: Position.Left }
|
||||
];
|
||||
]
|
||||
const nodeColors = [
|
||||
['#1e9e99', '#4cb3ac', '#6ec9c0', '#8ddfd4'],
|
||||
['#0f4c75', '#1b5d8b', '#276fa1', '#3282b8']
|
||||
];
|
||||
const edgeTypes = ['default', 'step', 'smoothstep', 'straight'];
|
||||
]
|
||||
const edgeTypes = ['default', 'step', 'smoothstep', 'straight']
|
||||
const offsets = [
|
||||
{
|
||||
x: 0,
|
||||
@@ -46,30 +46,30 @@ const offsets = [
|
||||
x: -nodeGapWidth,
|
||||
y: -nodeGapWidth
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
let id = 0;
|
||||
const getNodeId = (): ElementId => (id++).toString();
|
||||
let id = 0
|
||||
const getNodeId = (): ElementId => (id++).toString()
|
||||
|
||||
export function getElements(): Elements {
|
||||
const initialElements = [];
|
||||
const initialElements = []
|
||||
|
||||
for (let sourceTargetIndex = 0; sourceTargetIndex < sourceTargetPositions.length; sourceTargetIndex++) {
|
||||
const currSourceTargetPos = sourceTargetPositions[sourceTargetIndex];
|
||||
const currSourceTargetPos = sourceTargetPositions[sourceTargetIndex]
|
||||
|
||||
for (let edgeTypeIndex = 0; edgeTypeIndex < edgeTypes.length; edgeTypeIndex++) {
|
||||
const currEdgeType = edgeTypes[edgeTypeIndex];
|
||||
const currEdgeType = edgeTypes[edgeTypeIndex]
|
||||
|
||||
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 = {
|
||||
x: offsetIndex * nodeWidth * 4,
|
||||
y: edgeTypeIndex * 300 + sourceTargetIndex * edgeTypes.length * 300
|
||||
};
|
||||
const sourceId = getNodeId();
|
||||
const sourceData = { label: `Source ${sourceId}` };
|
||||
}
|
||||
const sourceId = getNodeId()
|
||||
const sourceData = { label: `Source ${sourceId}` }
|
||||
const sourceNode = {
|
||||
id: sourceId,
|
||||
style,
|
||||
@@ -77,14 +77,14 @@ export function getElements(): Elements {
|
||||
position: sourcePosition,
|
||||
sourcePosition: currSourceTargetPos.source,
|
||||
targetPosition: currSourceTargetPos.target
|
||||
};
|
||||
}
|
||||
|
||||
const targetId = getNodeId();
|
||||
const targetData = { label: `Target ${targetId}` };
|
||||
const targetId = getNodeId()
|
||||
const targetData = { label: `Target ${targetId}` }
|
||||
const targetPosition = {
|
||||
x: sourcePosition.x + currOffset.x,
|
||||
y: sourcePosition.y + currOffset.y
|
||||
};
|
||||
}
|
||||
const targetNode = {
|
||||
id: targetId,
|
||||
style,
|
||||
@@ -92,15 +92,15 @@ export function getElements(): Elements {
|
||||
position: targetPosition,
|
||||
sourcePosition: currSourceTargetPos.source,
|
||||
targetPosition: currSourceTargetPos.target
|
||||
};
|
||||
}
|
||||
|
||||
initialElements.push(sourceNode);
|
||||
initialElements.push(targetNode);
|
||||
initialElements.push(sourceNode)
|
||||
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 { getBezierPath, getEdgeCenter, getMarkerEnd, RevueFlowStore } from '../../src';
|
||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils';
|
||||
import { RevueFlowHooks } from '../../src/hooks/RevueFlowHooks';
|
||||
import { computed, defineComponent, inject } from 'vue'
|
||||
import { getBezierPath, getEdgeCenter, getMarkerEnd, RevueFlowStore } from '../../src'
|
||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
|
||||
import { RevueFlowHooks } from '../../src/hooks/RevueFlowHooks'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
...DefaultEdgeProps
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
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) {
|
||||
hooks.elementsRemove.trigger([edge]);
|
||||
hooks.elementsRemove.trigger([edge])
|
||||
}
|
||||
evt.stopPropagation();
|
||||
alert(`remove ${id}`);
|
||||
};
|
||||
evt.stopPropagation()
|
||||
alert(`remove ${id}`)
|
||||
}
|
||||
|
||||
const foreignObjectSize = 40;
|
||||
const foreignObjectSize = 40
|
||||
|
||||
const edgePath = computed(() =>
|
||||
getBezierPath({
|
||||
@@ -30,8 +30,8 @@ export default defineComponent({
|
||||
targetY: props.targetY,
|
||||
targetPosition: props.targetPosition
|
||||
})
|
||||
);
|
||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId));
|
||||
)
|
||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
||||
const center = computed(() =>
|
||||
getEdgeCenter({
|
||||
sourceX: props.sourceX,
|
||||
@@ -39,7 +39,7 @@ export default defineComponent({
|
||||
targetX: props.targetX,
|
||||
targetY: props.targetY
|
||||
})
|
||||
);
|
||||
)
|
||||
|
||||
return () => (
|
||||
<>
|
||||
@@ -59,6 +59,6 @@ export default defineComponent({
|
||||
</body>
|
||||
</foreignObject>
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { getBezierPath, getMarkerEnd } from '../../src';
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils';
|
||||
import { getBezierPath, getMarkerEnd } from '../../src'
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
|
||||
|
||||
const CustomEdge = defineComponent({
|
||||
props: {
|
||||
@@ -16,8 +16,8 @@ const CustomEdge = defineComponent({
|
||||
targetY: props.targetY,
|
||||
targetPosition: props.targetPosition
|
||||
})
|
||||
);
|
||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId));
|
||||
)
|
||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
||||
|
||||
return () => (
|
||||
<>
|
||||
@@ -28,8 +28,8 @@ const CustomEdge = defineComponent({
|
||||
</textPath>
|
||||
</text>
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export default CustomEdge;
|
||||
export default CustomEdge
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { computed, defineComponent } from 'vue';
|
||||
import { EdgeText, getBezierPath, getEdgeCenter, getMarkerEnd } from '../../src';
|
||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils';
|
||||
import { computed, defineComponent } from 'vue'
|
||||
import { EdgeText, getBezierPath, getEdgeCenter, getMarkerEnd } from '../../src'
|
||||
import { DefaultEdgeProps } from '../../src/components/Edges/utils'
|
||||
|
||||
const CustomEdge = defineComponent({
|
||||
props: {
|
||||
@@ -16,8 +16,8 @@ const CustomEdge = defineComponent({
|
||||
targetY: props.targetY,
|
||||
targetPosition: props.targetPosition
|
||||
})
|
||||
);
|
||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId));
|
||||
)
|
||||
const markerEnd = computed(() => getMarkerEnd(props.arrowHeadType, props.markerEndId))
|
||||
const center = computed(() =>
|
||||
getEdgeCenter({
|
||||
sourceX: props.sourceX,
|
||||
@@ -25,7 +25,7 @@ const CustomEdge = defineComponent({
|
||||
targetX: props.targetX,
|
||||
targetY: props.targetY
|
||||
})
|
||||
);
|
||||
)
|
||||
|
||||
return () => (
|
||||
<>
|
||||
@@ -42,8 +42,8 @@ const CustomEdge = defineComponent({
|
||||
onClick={() => console.log(props.data)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export default CustomEdge;
|
||||
export default CustomEdge
|
||||
|
||||
+15
-15
@@ -1,6 +1,6 @@
|
||||
import { ref } from 'vue';
|
||||
import CustomEdge from './CustomEdge';
|
||||
import CustomEdge2 from './CustomEdge2';
|
||||
import { ref } from 'vue'
|
||||
import CustomEdge from './CustomEdge'
|
||||
import CustomEdge2 from './CustomEdge2'
|
||||
import RevueFlow, {
|
||||
Elements,
|
||||
FlowElement,
|
||||
@@ -13,7 +13,7 @@ import RevueFlow, {
|
||||
Connection,
|
||||
Edge,
|
||||
removeElements
|
||||
} from '../../src';
|
||||
} from '../../src'
|
||||
|
||||
const initialElements = [
|
||||
{ id: '1', type: 'input', data: { label: 'Input 1' }, position: { x: 250, y: 0 } },
|
||||
@@ -73,21 +73,21 @@ const initialElements = [
|
||||
type: 'custom2',
|
||||
data: { text: 'custom edge 2' }
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
const edgeTypes: Record<string, any> = {
|
||||
custom: CustomEdge,
|
||||
custom2: CustomEdge2
|
||||
};
|
||||
}
|
||||
|
||||
const EdgesFlow = () => {
|
||||
const elements = ref<Elements>(initialElements as Elements);
|
||||
const elements = ref<Elements>(initialElements as Elements)
|
||||
|
||||
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView();
|
||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
||||
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value));
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
|
||||
const onLoad = (reactFlowInstance: OnLoadParams) => reactFlowInstance.fitView()
|
||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node)
|
||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element)
|
||||
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||
|
||||
return (
|
||||
<RevueFlow
|
||||
@@ -104,7 +104,7 @@ const EdgesFlow = () => {
|
||||
<Controls />
|
||||
<Background />
|
||||
</RevueFlow>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default EdgesFlow;
|
||||
export default EdgesFlow
|
||||
|
||||
+16
-17
@@ -12,32 +12,31 @@ import RevueFlow, {
|
||||
BackgroundVariant,
|
||||
Connection,
|
||||
Edge
|
||||
} from '../../src';
|
||||
import { CSSProperties, defineComponent, ref } from 'vue';
|
||||
} from '../../src'
|
||||
import { CSSProperties, defineComponent, ref } from 'vue'
|
||||
|
||||
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance);
|
||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||
const onLoad = (reactFlowInstance: OnLoadParams) => console.log('flow loaded:', reactFlowInstance)
|
||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element)
|
||||
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({
|
||||
setup() {
|
||||
{
|
||||
const elements = ref<Elements>([]);
|
||||
const onElementsRemove = (elementsToRemove: Elements) =>
|
||||
(elements.value = removeElements(elementsToRemove, elements.value));
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
|
||||
const elements = ref<Elements>([])
|
||||
const onElementsRemove = (elementsToRemove: Elements) => (elements.value = removeElements(elementsToRemove, elements.value))
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||
|
||||
const addRandomNode = () => {
|
||||
const nodeId: ElementId = (elements.value.length + 1).toString();
|
||||
const nodeId: ElementId = (elements.value.length + 1).toString()
|
||||
const newNode: Node = {
|
||||
id: nodeId,
|
||||
data: { label: `Node: ${nodeId}` },
|
||||
position: { x: Math.random() * window.innerWidth, y: Math.random() * window.innerHeight }
|
||||
} as Node;
|
||||
elements.value = [...elements.value, newNode];
|
||||
};
|
||||
} as Node
|
||||
elements.value = [...elements.value, newNode]
|
||||
}
|
||||
|
||||
return () => (
|
||||
<RevueFlow
|
||||
@@ -57,9 +56,9 @@ const EmptyFlow = defineComponent({
|
||||
add node
|
||||
</button>
|
||||
</RevueFlow>
|
||||
);
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export default EmptyFlow;
|
||||
export default EmptyFlow
|
||||
|
||||
+10
-10
@@ -1,14 +1,14 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { routes } from './router';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import { defineComponent } from 'vue'
|
||||
import { routes } from './router'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const Header = defineComponent({
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
const onChange = async (event: any) => {
|
||||
await router.push(event.target.value);
|
||||
};
|
||||
await router.push(event.target.value)
|
||||
}
|
||||
|
||||
return () => (
|
||||
<header>
|
||||
@@ -25,8 +25,8 @@ const Header = defineComponent({
|
||||
)}
|
||||
</select>
|
||||
</header>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export default Header;
|
||||
export default Header
|
||||
|
||||
+17
-17
@@ -1,5 +1,5 @@
|
||||
import RevueFlow, { addEdge, MiniMap, Controls, Connection, Edge, Elements } from '../../src';
|
||||
import { defineComponent, ref, watchEffect } from 'vue';
|
||||
import RevueFlow, { addEdge, MiniMap, Controls, Connection, Edge, Elements } from '../../src'
|
||||
import { defineComponent, ref, watchEffect } from 'vue'
|
||||
|
||||
const initialElements: Elements = [
|
||||
{ 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-3', source: '1', target: '3' },
|
||||
{ id: 'e3-4', source: '3', target: '4' }
|
||||
] as Elements;
|
||||
] as Elements
|
||||
|
||||
const HiddenFlow = defineComponent({
|
||||
setup() {
|
||||
const elements = ref<Elements>(initialElements);
|
||||
const isHidden = ref<boolean>(false);
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
|
||||
const elements = ref<Elements>(initialElements)
|
||||
const isHidden = ref<boolean>(false)
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||
|
||||
watchEffect(() => {
|
||||
console.log(isHidden.value);
|
||||
console.log(isHidden.value)
|
||||
elements.value = elements.value.map((e) => {
|
||||
e.isHidden = isHidden.value;
|
||||
return e;
|
||||
});
|
||||
});
|
||||
e.isHidden = isHidden.value
|
||||
return e
|
||||
})
|
||||
})
|
||||
|
||||
const toggle = (event) => {
|
||||
console.log(event.target);
|
||||
isHidden.value = event.target?.checked;
|
||||
};
|
||||
console.log(event.target)
|
||||
isHidden.value = event.target?.checked
|
||||
}
|
||||
|
||||
return () => (
|
||||
<RevueFlow v-model={elements.value} onConnect={onConnect}>
|
||||
@@ -44,8 +44,8 @@ const HiddenFlow = defineComponent({
|
||||
</div>
|
||||
</div>
|
||||
</RevueFlow>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export default HiddenFlow;
|
||||
export default HiddenFlow
|
||||
|
||||
@@ -9,8 +9,8 @@ import RevueFlow, {
|
||||
Edge,
|
||||
PanOnScrollMode,
|
||||
FlowTransform
|
||||
} from '../../src';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
} from '../../src'
|
||||
import { defineComponent, ref } from 'vue'
|
||||
|
||||
const initialElements: Elements = [
|
||||
{ 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: 'e1-2', source: '1', target: '2', animated: true },
|
||||
{ id: 'e1-3', source: '1', target: '3' }
|
||||
] as Elements;
|
||||
] as Elements
|
||||
|
||||
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node);
|
||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element);
|
||||
const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event);
|
||||
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event);
|
||||
const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event);
|
||||
const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform);
|
||||
const onNodeDragStart = (_: MouseEvent, node: Node) => console.log('drag start', node)
|
||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node)
|
||||
const onElementClick = (_: MouseEvent, element: FlowElement) => console.log('click', element)
|
||||
const onPaneClick = (event: MouseEvent) => console.log('onPaneClick', event)
|
||||
const onPaneScroll = (event?: WheelEvent) => console.log('onPaneScroll', event)
|
||||
const onPaneContextMenu = (event: MouseEvent) => console.log('onPaneContextMenu', event)
|
||||
const onMoveEnd = (flowTranasform?: FlowTransform) => console.log('onMoveEnd', flowTranasform)
|
||||
|
||||
const InteractionFlow = defineComponent({
|
||||
setup() {
|
||||
const elements = ref<Elements>(initialElements);
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value));
|
||||
const elements = ref<Elements>(initialElements)
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value))
|
||||
|
||||
const isSelectable = ref(false);
|
||||
const isDraggable = ref(false);
|
||||
const isConnectable = ref(false);
|
||||
const zoomOnScroll = ref(false);
|
||||
const zoomOnPinch = ref(false);
|
||||
const panOnScroll = ref(false);
|
||||
const panOnScrollMode = ref<PanOnScrollMode>(PanOnScrollMode.Free);
|
||||
const zoomOnDoubleClick = ref(false);
|
||||
const paneMoveable = ref(true);
|
||||
const captureZoomClick = ref(false);
|
||||
const captureZoomScroll = ref(false);
|
||||
const captureElementClick = ref(false);
|
||||
const isSelectable = ref(false)
|
||||
const isDraggable = ref(false)
|
||||
const isConnectable = ref(false)
|
||||
const zoomOnScroll = ref(false)
|
||||
const zoomOnPinch = ref(false)
|
||||
const panOnScroll = ref(false)
|
||||
const panOnScrollMode = ref<PanOnScrollMode>(PanOnScrollMode.Free)
|
||||
const zoomOnDoubleClick = ref(false)
|
||||
const paneMoveable = ref(true)
|
||||
const captureZoomClick = ref(false)
|
||||
const captureZoomScroll = ref(false)
|
||||
const captureElementClick = ref(false)
|
||||
|
||||
return () => (
|
||||
<RevueFlow
|
||||
@@ -165,8 +165,8 @@ const InteractionFlow = defineComponent({
|
||||
</div>
|
||||
</div>
|
||||
</RevueFlow>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export default InteractionFlow;
|
||||
export default InteractionFlow
|
||||
|
||||
+12
-12
@@ -1,14 +1,14 @@
|
||||
import { createApp } from 'vue';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import { router } from './router';
|
||||
import { createPinia } from 'pinia';
|
||||
import { DraggablePlugin } from '@braks/revue-draggable';
|
||||
import { createApp } from 'vue'
|
||||
import './index.css'
|
||||
import App from './App'
|
||||
import { router } from './router'
|
||||
import { createPinia } from 'pinia'
|
||||
import { DraggablePlugin } from '@braks/revue-draggable'
|
||||
|
||||
const app = createApp(App);
|
||||
const app = createApp(App)
|
||||
|
||||
app.config.performance = true;
|
||||
app.use(router);
|
||||
app.use(DraggablePlugin);
|
||||
app.use(createPinia());
|
||||
app.mount('#root');
|
||||
app.config.performance = true
|
||||
app.use(router)
|
||||
app.use(DraggablePlugin)
|
||||
app.use(createPinia())
|
||||
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'] = [
|
||||
{
|
||||
@@ -45,9 +45,9 @@ export const routes: RouterOptions['routes'] = [
|
||||
path: '/interaction',
|
||||
component: () => import('./Interaction')
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
export const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
});
|
||||
})
|
||||
|
||||
@@ -76,6 +76,7 @@
|
||||
"rollup-plugin-typescript2": "^0.30.0",
|
||||
"tslib": "^2.3.0",
|
||||
"typescript": "^4.3.5",
|
||||
"unplugin-auto-import": "^0.4.12",
|
||||
"vite": "^2.3.8",
|
||||
"vite-svg-loader": "^2.1.0",
|
||||
"vue": "^3.2.0-beta.8",
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
module.exports = {
|
||||
plugins: [require('autoprefixer'), require('postcss-nested')]
|
||||
};
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,3 +1,3 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('esbuild-register');
|
||||
module.exports = require('./rollup.ts');
|
||||
require('esbuild-register')
|
||||
module.exports = require('./rollup.ts')
|
||||
|
||||
@@ -1,39 +1,38 @@
|
||||
import typescript from 'rollup-plugin-typescript2';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import dts from 'rollup-plugin-dts';
|
||||
import { OutputOptions, RollupOptions } from 'rollup';
|
||||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import babel from '@rollup/plugin-babel';
|
||||
import typescript from 'rollup-plugin-typescript2'
|
||||
import { terser } from 'rollup-plugin-terser'
|
||||
import dts from 'rollup-plugin-dts'
|
||||
import commonjs from '@rollup/plugin-commonjs'
|
||||
import resolve from '@rollup/plugin-node-resolve'
|
||||
import babel from '@rollup/plugin-babel'
|
||||
// @ts-ignore
|
||||
import svg from 'rollup-plugin-svg';
|
||||
import postcss from 'rollup-plugin-postcss';
|
||||
import svg from 'rollup-plugin-svg'
|
||||
import postcss from 'rollup-plugin-postcss'
|
||||
// @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 = [
|
||||
{
|
||||
display: 'Revue-Flow',
|
||||
external: ['@vueuse/core']
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
// @ts-ignore
|
||||
for (const { external, iife } of activePackages) {
|
||||
const iifeGlobals = {
|
||||
vue: 'Vue',
|
||||
'@vueuse/core': 'VueUse'
|
||||
};
|
||||
}
|
||||
|
||||
const iifeName = 'RevueFlow';
|
||||
const functionNames = ['revue-flow'];
|
||||
const iifeName = 'RevueFlow'
|
||||
const functionNames = ['revue-flow']
|
||||
|
||||
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`,
|
||||
format: 'cjs'
|
||||
@@ -42,7 +41,7 @@ for (const { external, iife } of activePackages) {
|
||||
file: `dist/${fn}.esm.js`,
|
||||
format: 'es'
|
||||
}
|
||||
];
|
||||
]
|
||||
|
||||
if (iife !== false) {
|
||||
output.push(
|
||||
@@ -67,7 +66,7 @@ for (const { external, iife } of activePackages) {
|
||||
})
|
||||
]
|
||||
}
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
configs.push({
|
||||
@@ -95,7 +94,7 @@ for (const { external, iife } of activePackages) {
|
||||
})
|
||||
],
|
||||
external: ['vue', ...(external || [])]
|
||||
});
|
||||
})
|
||||
|
||||
configs.push({
|
||||
input,
|
||||
@@ -105,8 +104,8 @@ for (const { external, iife } of activePackages) {
|
||||
},
|
||||
plugins: [dts()],
|
||||
external: ['vue', ...(external || []), /\.css$/]
|
||||
});
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default configs;
|
||||
export default configs
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { BackgroundVariant, RevueFlowStore } from '../../types';
|
||||
import { createGridDotsPath, createGridLinesPath } from './utils';
|
||||
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue';
|
||||
import { BackgroundVariant, RevueFlowStore } from '../../types'
|
||||
import { createGridDotsPath, createGridLinesPath } from './utils'
|
||||
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue'
|
||||
|
||||
export interface BackgroundProps extends HTMLAttributes {
|
||||
variant?: BackgroundVariant;
|
||||
gap?: number;
|
||||
color?: string;
|
||||
size?: number;
|
||||
variant?: BackgroundVariant
|
||||
gap?: number
|
||||
color?: string
|
||||
size?: number
|
||||
}
|
||||
|
||||
const defaultColors = {
|
||||
[BackgroundVariant.Dots]: '#81818a',
|
||||
[BackgroundVariant.Lines]: '#eee'
|
||||
};
|
||||
}
|
||||
|
||||
const Background = defineComponent({
|
||||
name: 'Background',
|
||||
@@ -39,23 +39,23 @@ const Background = defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const transform = computed(() => store.transform);
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
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.
|
||||
const patternId = `pattern-${Math.floor(Math.random() * 100000)}`;
|
||||
const patternId = `pattern-${Math.floor(Math.random() * 100000)}`
|
||||
|
||||
const bgClasses = ['revue-flow__background'];
|
||||
const scaledGap = computed(() => props.gap && props.gap * transform.value[2]);
|
||||
const xOffset = computed(() => scaledGap.value && transform.value[0] % scaledGap.value);
|
||||
const yOffset = computed(() => scaledGap.value && transform.value[1] % scaledGap.value);
|
||||
const bgClasses = ['revue-flow__background']
|
||||
const scaledGap = computed(() => props.gap && props.gap * transform.value[2])
|
||||
const xOffset = computed(() => scaledGap.value && transform.value[0] % scaledGap.value)
|
||||
const yOffset = computed(() => scaledGap.value && transform.value[1] % scaledGap.value)
|
||||
|
||||
const isLines = computed(() => props.variant === BackgroundVariant.Lines);
|
||||
const bgColor = computed(() => (props.color ? props.color : defaultColors[props.variant || BackgroundVariant.Dots]));
|
||||
const isLines = computed(() => props.variant === BackgroundVariant.Lines)
|
||||
const bgColor = computed(() => (props.color ? props.color : defaultColors[props.variant || BackgroundVariant.Dots]))
|
||||
const path = computed(() =>
|
||||
isLines.value
|
||||
? scaledGap.value && props.size && createGridLinesPath(scaledGap.value, props.size, bgColor.value)
|
||||
: createGridDotsPath(props.size || 0.4 * transform.value[2], bgColor.value)
|
||||
);
|
||||
)
|
||||
|
||||
return () => (
|
||||
<svg
|
||||
@@ -77,8 +77,8 @@ const Background = defineComponent({
|
||||
</pattern>
|
||||
<rect x="0" y="0" width="100%" height="100%" fill={`url(#${patternId})`} />
|
||||
</svg>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export default Background;
|
||||
export default Background
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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) => {
|
||||
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 useZoomPanHelper from '../../hooks/useZoomPanHelper';
|
||||
import { FitViewParams, RevueFlowStore, ZoomPanHelperFunctions } from '../../types';
|
||||
import PlusIcon from '../../../assets/icons/plus.svg';
|
||||
import MinusIcon from '../../../assets/icons/minus.svg';
|
||||
import Fitview from '../../../assets/icons/fitview.svg';
|
||||
import Lock from '../../../assets/icons/lock.svg';
|
||||
import Unlock from '../../../assets/icons/unlock.svg';
|
||||
import { defineComponent, HTMLAttributes, inject, onMounted, PropType, ref } from 'vue'
|
||||
import useZoomPanHelper from '../../hooks/useZoomPanHelper'
|
||||
import { FitViewParams, RevueFlowStore, ZoomPanHelperFunctions } from '../../types'
|
||||
import PlusIcon from '../../../assets/icons/plus.svg'
|
||||
import MinusIcon from '../../../assets/icons/minus.svg'
|
||||
import Fitview from '../../../assets/icons/fitview.svg'
|
||||
import Lock from '../../../assets/icons/lock.svg'
|
||||
import Unlock from '../../../assets/icons/unlock.svg'
|
||||
|
||||
export interface ControlProps extends HTMLAttributes {
|
||||
showZoom?: boolean;
|
||||
showFitView?: boolean;
|
||||
showInteractive?: boolean;
|
||||
fitViewParams?: FitViewParams;
|
||||
onZoomIn?: () => void;
|
||||
onZoomOut?: () => void;
|
||||
onFitView?: () => void;
|
||||
onInteractiveChange?: (interactiveStatus: boolean) => void;
|
||||
showZoom?: boolean
|
||||
showFitView?: boolean
|
||||
showInteractive?: boolean
|
||||
fitViewParams?: FitViewParams
|
||||
onZoomIn?: () => void
|
||||
onZoomOut?: () => void
|
||||
onFitView?: () => void
|
||||
onInteractiveChange?: (interactiveStatus: boolean) => void
|
||||
}
|
||||
|
||||
export type ControlButtonProps = HTMLButtonElement;
|
||||
export type ControlButtonProps = HTMLButtonElement
|
||||
|
||||
export const ControlButton = defineComponent({
|
||||
props: {
|
||||
@@ -38,9 +38,9 @@ export const ControlButton = defineComponent({
|
||||
<button class={['revue-flow__controls-button']} {...props}>
|
||||
{slots.default ? slots.default() : ''}
|
||||
</button>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const Controls = defineComponent({
|
||||
name: 'Controls',
|
||||
@@ -87,41 +87,41 @@ const Controls = defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const isVisible = ref<boolean>(false);
|
||||
const zoomHelper = ref<ZoomPanHelperFunctions>();
|
||||
const { onReady } = useZoomPanHelper();
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const isVisible = ref<boolean>(false)
|
||||
const zoomHelper = ref<ZoomPanHelperFunctions>()
|
||||
const { onReady } = useZoomPanHelper()
|
||||
|
||||
onReady((helper) => {
|
||||
zoomHelper.value = helper;
|
||||
});
|
||||
zoomHelper.value = helper
|
||||
})
|
||||
|
||||
const isInteractive = store.nodesDraggable && store.nodesConnectable && store.elementsSelectable;
|
||||
const mapClasses = ['revue-flow__controls'];
|
||||
const isInteractive = store.nodesDraggable && store.nodesConnectable && store.elementsSelectable
|
||||
const mapClasses = ['revue-flow__controls']
|
||||
|
||||
const onZoomInHandler = () => {
|
||||
zoomHelper.value?.zoomIn?.();
|
||||
props.onZoomIn?.();
|
||||
};
|
||||
zoomHelper.value?.zoomIn?.()
|
||||
props.onZoomIn?.()
|
||||
}
|
||||
|
||||
const onZoomOutHandler = () => {
|
||||
zoomHelper.value?.zoomOut?.();
|
||||
props.onZoomOut?.();
|
||||
};
|
||||
zoomHelper.value?.zoomOut?.()
|
||||
props.onZoomOut?.()
|
||||
}
|
||||
|
||||
const onFitViewHandler = () => {
|
||||
zoomHelper.value?.fitView?.(props.fitViewParams);
|
||||
props.onFitView?.();
|
||||
};
|
||||
zoomHelper.value?.fitView?.(props.fitViewParams)
|
||||
props.onFitView?.()
|
||||
}
|
||||
|
||||
const onInteractiveChangeHandler = () => {
|
||||
store.setInteractive?.(!isInteractive);
|
||||
props.onInteractiveChange?.(!isInteractive);
|
||||
};
|
||||
store.setInteractive?.(!isInteractive)
|
||||
props.onInteractiveChange?.(!isInteractive)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
isVisible.value = true;
|
||||
});
|
||||
isVisible.value = true
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div class={mapClasses}>
|
||||
@@ -147,8 +147,8 @@ const Controls = defineComponent({
|
||||
)}
|
||||
{slots.default ? slots.default() : ''}
|
||||
</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 {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
borderRadius: number;
|
||||
color: string;
|
||||
shapeRendering: string;
|
||||
strokeColor: string;
|
||||
strokeWidth: number;
|
||||
x: number
|
||||
y: number
|
||||
width: number
|
||||
height: number
|
||||
borderRadius: number
|
||||
color: string
|
||||
shapeRendering: string
|
||||
strokeColor: string
|
||||
strokeWidth: number
|
||||
}
|
||||
|
||||
const MiniMapNode = defineComponent({
|
||||
@@ -62,8 +62,8 @@ const MiniMapNode = defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props, { attrs }: { attrs: Record<string, any> }) {
|
||||
const styles = attrs.style || {};
|
||||
const fill = computed(() => (props.color || styles.value.background || styles.value.backgroundColor) as string);
|
||||
const styles = attrs.style || {}
|
||||
const fill = computed(() => (props.color || styles.value.background || styles.value.backgroundColor) as string)
|
||||
|
||||
return () => (
|
||||
<rect
|
||||
@@ -79,8 +79,8 @@ const MiniMapNode = defineComponent({
|
||||
stroke-width={props.strokeWidth}
|
||||
shape-rendering={props.shapeRendering}
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export default MiniMapNode;
|
||||
export default MiniMapNode
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { getRectOfNodes, getBoundsofRects } from '../../utils/graph';
|
||||
import { Node, Rect, RevueFlowStore } from '../../types';
|
||||
import MiniMapNode from './MiniMapNode';
|
||||
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue';
|
||||
import { getRectOfNodes, getBoundsofRects } from '../../utils/graph'
|
||||
import { Node, Rect, RevueFlowStore } from '../../types'
|
||||
import MiniMapNode from './MiniMapNode'
|
||||
import { computed, defineComponent, HTMLAttributes, inject, PropType } from 'vue'
|
||||
|
||||
type StringFunc = (node: Node) => string;
|
||||
type StringFunc = (node: Node) => string
|
||||
|
||||
export interface MiniMapProps extends HTMLAttributes {
|
||||
nodeColor?: string | StringFunc;
|
||||
nodeStrokeColor?: string | StringFunc;
|
||||
nodeClassName?: string | StringFunc;
|
||||
nodeBorderRadius?: number;
|
||||
nodeStrokeWidth?: number;
|
||||
maskColor?: string;
|
||||
nodeColor?: string | StringFunc
|
||||
nodeStrokeColor?: string | StringFunc
|
||||
nodeClassName?: string | StringFunc
|
||||
nodeBorderRadius?: number
|
||||
nodeStrokeWidth?: number
|
||||
maskColor?: string
|
||||
}
|
||||
|
||||
declare const window: any;
|
||||
declare const window: any
|
||||
|
||||
const defaultWidth = 200;
|
||||
const defaultHeight = 150;
|
||||
const defaultWidth = 200
|
||||
const defaultHeight = 150
|
||||
|
||||
const MiniMap = defineComponent({
|
||||
name: 'MiniMap',
|
||||
@@ -54,37 +54,39 @@ const MiniMap = defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props, { attrs }: { attrs: Record<string, any> }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const transform = computed(() => store.transform);
|
||||
const elementWidth = computed(() => (attrs.style?.width || defaultWidth)! 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 store = inject<RevueFlowStore>('store')!
|
||||
const transform = computed(() => store.transform)
|
||||
const elementWidth = computed(() => (attrs.style?.width || defaultWidth)! 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 nodeStrokeColorFunc = computed(
|
||||
() => (props.nodeStrokeColor instanceof Function ? props.nodeStrokeColor : () => props.nodeStrokeColor) as StringFunc
|
||||
);
|
||||
)
|
||||
const nodeClassNameFunc = computed(
|
||||
() => (props.nodeClassName instanceof Function ? props.nodeClassName : () => props.nodeClassName) as StringFunc
|
||||
);
|
||||
const hasNodes = computed(() => store.nodes && store.nodes.length);
|
||||
const bb = computed(() => getRectOfNodes(store.nodes));
|
||||
)
|
||||
const hasNodes = computed(() => store.nodes && store.nodes.length)
|
||||
const bb = computed(() => getRectOfNodes(store.nodes))
|
||||
const viewBB = computed<Rect>(() => ({
|
||||
x: -transform.value[0] / transform.value[2],
|
||||
y: -transform.value[1] / transform.value[2],
|
||||
width: store.width / transform.value[2],
|
||||
height: store.height / transform.value[2]
|
||||
}));
|
||||
const boundingRect = computed(() => (hasNodes.value ? getBoundsofRects(bb.value, viewBB.value) : viewBB.value));
|
||||
const scaledWidth = computed(() => boundingRect.value.width / elementWidth.value);
|
||||
const scaledHeight = computed(() => boundingRect.value.height / elementHeight.value);
|
||||
const viewScale = computed(() => Math.max(scaledWidth.value, scaledHeight.value));
|
||||
const viewWidth = computed(() => viewScale.value * elementWidth.value);
|
||||
const viewHeight = computed(() => viewScale.value * elementHeight.value);
|
||||
const offset = computed(() => 5 * viewScale.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 width = computed(() => viewWidth.value + offset.value * 2);
|
||||
const height = computed(() => viewHeight.value + offset.value * 2);
|
||||
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
|
||||
}))
|
||||
const boundingRect = computed(() => (hasNodes.value ? getBoundsofRects(bb.value, viewBB.value) : viewBB.value))
|
||||
const scaledWidth = computed(() => boundingRect.value.width / elementWidth.value)
|
||||
const scaledHeight = computed(() => boundingRect.value.height / elementHeight.value)
|
||||
const viewScale = computed(() => Math.max(scaledWidth.value, scaledHeight.value))
|
||||
const viewWidth = computed(() => viewScale.value * elementWidth.value)
|
||||
const viewHeight = computed(() => viewScale.value * elementHeight.value)
|
||||
const offset = computed(() => 5 * viewScale.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 width = computed(() => viewWidth.value + offset.value * 2)
|
||||
const height = computed(() => viewHeight.value + offset.value * 2)
|
||||
const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision'
|
||||
|
||||
return () => (
|
||||
<svg
|
||||
@@ -121,8 +123,8 @@ const MiniMap = defineComponent({
|
||||
fill-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
export default MiniMap;
|
||||
export default MiniMap
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// These components are not used by revue Flow directly
|
||||
// but the user can add them as children of a revue Flow component
|
||||
|
||||
export { default as MiniMap } from './MiniMap';
|
||||
export { default as Controls, ControlButton } from './Controls';
|
||||
export { default as Background } from './Background';
|
||||
export { default as MiniMap } from './MiniMap'
|
||||
export { default as Controls, ControlButton } from './Controls'
|
||||
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 { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
||||
import { Node, HandleElement, Position, ConnectionLineType, ConnectionLineComponent, RevueFlowStore } from '../../types';
|
||||
import { getBezierPath } from '../Edges/BezierEdge'
|
||||
import { getSmoothStepPath } from '../Edges/SmoothStepEdge'
|
||||
import { Node, HandleElement, Position, ConnectionLineType, ConnectionLineComponent, RevueFlowStore } from '../../types'
|
||||
|
||||
interface ConnectionLineProps {
|
||||
connectionLineType: ConnectionLineType;
|
||||
connectionLineStyle?: CSSProperties;
|
||||
customConnectionLine?: ConnectionLineComponent;
|
||||
connectionLineType: ConnectionLineType
|
||||
connectionLineStyle?: CSSProperties
|
||||
customConnectionLine?: ConnectionLineComponent
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -29,35 +29,35 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const sourceNode = ref<Node | null>(store.nodes.find((n) => n.id === store.connectionNodeId) || null);
|
||||
const nodesConnectable = computed(() => store.nodesConnectable);
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const sourceNode = ref<Node | null>(store.nodes.find((n) => n.id === store.connectionNodeId) || null)
|
||||
const nodesConnectable = computed(() => store.nodesConnectable)
|
||||
|
||||
const sourceHandle = computed(() =>
|
||||
store.connectionHandleId && store.connectionHandleType
|
||||
? 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]
|
||||
);
|
||||
)
|
||||
const sourceHandleX = computed(() =>
|
||||
sourceHandle.value ? sourceHandle.value.x + sourceHandle.value.width / 2 : (sourceNode.value?.__rf.width as number) / 2
|
||||
);
|
||||
)
|
||||
const sourceHandleY = computed(() =>
|
||||
sourceHandle.value ? sourceHandle.value.y + sourceHandle.value.height / 2 : sourceNode.value?.__rf.height
|
||||
);
|
||||
const sourceX = computed(() => sourceNode.value?.__rf.position.x + sourceHandleX.value);
|
||||
const sourceY = computed(() => sourceNode.value?.__rf.position.y + sourceHandleY.value);
|
||||
)
|
||||
const sourceX = computed(() => sourceNode.value?.__rf.position.x + sourceHandleX.value)
|
||||
const sourceY = computed(() => sourceNode.value?.__rf.position.y + sourceHandleY.value)
|
||||
|
||||
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 targetX = computed(() => (store.connectionPosition.x - store.transform[0]) / store.transform[2])
|
||||
const targetY = computed(() => (store.connectionPosition.y - store.transform[1]) / store.transform[2])
|
||||
|
||||
const isRightOrLeft = computed(
|
||||
() => 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) {
|
||||
dAttr = computed(() =>
|
||||
@@ -69,7 +69,7 @@ export default defineComponent({
|
||||
targetY: targetY.value,
|
||||
targetPosition: targetPosition.value
|
||||
})
|
||||
);
|
||||
)
|
||||
} else if (props.connectionLineType === ConnectionLineType.Step) {
|
||||
dAttr = computed(() =>
|
||||
getSmoothStepPath({
|
||||
@@ -81,7 +81,7 @@ export default defineComponent({
|
||||
targetPosition: targetPosition.value,
|
||||
borderRadius: 0
|
||||
})
|
||||
);
|
||||
)
|
||||
} else if (props.connectionLineType === ConnectionLineType.SmoothStep) {
|
||||
dAttr = computed(() =>
|
||||
getSmoothStepPath({
|
||||
@@ -92,7 +92,7 @@ export default defineComponent({
|
||||
targetY: targetY.value,
|
||||
targetPosition: targetPosition.value
|
||||
})
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
if (props.customConnectionLine) {
|
||||
@@ -110,7 +110,7 @@ export default defineComponent({
|
||||
connectionLineStyle: props.connectionLineStyle
|
||||
})}
|
||||
</g>
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
return () =>
|
||||
@@ -120,6 +120,6 @@ export default defineComponent({
|
||||
</g>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import EdgeText from './EdgeText';
|
||||
import { getMarkerEnd, getCenter, GetCenterParams, DefaultEdgeProps } from './utils';
|
||||
import { ArrowHeadType, EdgeType, Position } from '../../types';
|
||||
import { reactify } from '@vueuse/core';
|
||||
import { defineComponent } from 'vue'
|
||||
import EdgeText from './EdgeText'
|
||||
import { getMarkerEnd, getCenter, GetCenterParams, DefaultEdgeProps } from './utils'
|
||||
import { ArrowHeadType, EdgeType, Position } from '../../types'
|
||||
import { reactify } from '@vueuse/core'
|
||||
|
||||
interface GetBezierPathParams {
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
sourcePosition?: Position;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
targetPosition?: Position;
|
||||
centerX?: number;
|
||||
centerY?: number;
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
sourcePosition?: Position
|
||||
targetX: number
|
||||
targetY: number
|
||||
targetPosition?: Position
|
||||
centerX?: number
|
||||
centerY?: number
|
||||
}
|
||||
|
||||
export function getBezierPath({
|
||||
@@ -25,23 +25,23 @@ export function getBezierPath({
|
||||
centerX,
|
||||
centerY
|
||||
}: GetBezierPathParams): string {
|
||||
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY });
|
||||
const leftAndRight = [Position.Left, Position.Right];
|
||||
const [_centerX, _centerY] = getCenter({ sourceX, sourceY, targetX, targetY })
|
||||
const leftAndRight = [Position.Left, Position.Right]
|
||||
|
||||
const cX = typeof centerX !== 'undefined' ? centerX : _centerX;
|
||||
const cY = typeof centerY !== 'undefined' ? centerY : _centerY;
|
||||
const cX = typeof centerX !== 'undefined' ? centerX : _centerX
|
||||
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)) {
|
||||
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)) {
|
||||
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)) {
|
||||
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({
|
||||
@@ -58,8 +58,8 @@ export default defineComponent({
|
||||
targetY: targetY,
|
||||
sourcePosition: sourcePosition,
|
||||
targetPosition: targetPosition
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
const path = reactify(({ sourceX, sourceY, targetX, targetY, targetPosition, sourcePosition }: GetBezierPathParams) => {
|
||||
return getBezierPath({
|
||||
sourceX: sourceX,
|
||||
@@ -68,10 +68,10 @@ export default defineComponent({
|
||||
targetY: targetY,
|
||||
targetPosition: targetPosition,
|
||||
sourcePosition: sourcePosition
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId));
|
||||
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId))
|
||||
|
||||
return () => (
|
||||
<>
|
||||
@@ -94,6 +94,6 @@ export default defineComponent({
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
}) as EdgeType;
|
||||
}) as EdgeType
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { computed, defineComponent, h, inject, PropType, ref, watchPostEffect } from 'vue';
|
||||
import EdgeAnchor from './EdgeAnchor';
|
||||
import { ConnectionMode, Edge, Elements, Node, Position, RevueFlowStore, Transform } from '../../types';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../container/EdgeRenderer/utils';
|
||||
import { isEdge } from '../../utils/graph';
|
||||
import { onMouseDown } from '../Handle/handler';
|
||||
import { controlledRef } from '@vueuse/core';
|
||||
import { computed, defineComponent, h, inject, PropType, ref, watchPostEffect } from 'vue'
|
||||
import EdgeAnchor from './EdgeAnchor'
|
||||
import { ConnectionMode, Edge, Elements, Node, Position, RevueFlowStore, Transform } from '../../types'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../container/EdgeRenderer/utils'
|
||||
import { isEdge } from '../../utils/graph'
|
||||
import { onMouseDown } from '../Handle/handler'
|
||||
import { controlledRef } from '@vueuse/core'
|
||||
|
||||
interface EdgeProps {
|
||||
edge: Edge;
|
||||
nodes: Node[];
|
||||
selectedElements: Elements | null;
|
||||
elementsSelectable: boolean;
|
||||
transform: Transform;
|
||||
width: number;
|
||||
height: number;
|
||||
onlyRenderVisibleElements: boolean;
|
||||
connectionMode?: ConnectionMode;
|
||||
markerEndId?: string;
|
||||
edgeUpdaterRadius?: number;
|
||||
edge: Edge
|
||||
nodes: Node[]
|
||||
selectedElements: Elements | null
|
||||
elementsSelectable: boolean
|
||||
transform: Transform
|
||||
width: number
|
||||
height: number
|
||||
onlyRenderVisibleElements: boolean
|
||||
connectionMode?: ConnectionMode
|
||||
markerEndId?: string
|
||||
edgeUpdaterRadius?: number
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -54,41 +54,41 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
const nodes = controlledRef(getSourceTargetNodes(props.edge, store.nodes), {
|
||||
onBeforeChange: (value, oldValue) => {
|
||||
if (JSON.stringify(value) === JSON.stringify(oldValue)) return false;
|
||||
if (JSON.stringify(value) === JSON.stringify(oldValue)) return false
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
watchPostEffect(() => {
|
||||
nodes.value = getSourceTargetNodes(props.edge, store.nodes);
|
||||
});
|
||||
nodes.value = getSourceTargetNodes(props.edge, store.nodes)
|
||||
})
|
||||
|
||||
hooks.connect.on((connection) => {
|
||||
hooks.edgeUpdate.trigger({ edge: props.edge, connection });
|
||||
});
|
||||
hooks.edgeUpdate.trigger({ edge: props.edge, connection })
|
||||
})
|
||||
|
||||
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) {
|
||||
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
|
||||
const targetNodeHandles =
|
||||
props.connectionMode === ConnectionMode.Strict
|
||||
? 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 =
|
||||
nodes.value.sourceNode && getHandle(nodes.value.sourceNode.__rf.handleBounds.source, props.edge.sourceHandle || null);
|
||||
const targetHandle = getHandle(targetNodeHandles, props.edge.targetHandle || null);
|
||||
const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom;
|
||||
const targetPosition = targetHandle ? targetHandle.position : Position.Top;
|
||||
nodes.value.sourceNode && getHandle(nodes.value.sourceNode.__rf.handleBounds.source, props.edge.sourceHandle || null)
|
||||
const targetHandle = getHandle(targetNodeHandles, props.edge.targetHandle || null)
|
||||
const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom
|
||||
const targetPosition = targetHandle ? targetHandle.position : Position.Top
|
||||
|
||||
const isVisible = ({ sourceX, sourceY, targetX, targetY }: Record<string, number>) => {
|
||||
return props.onlyRenderVisibleElements
|
||||
@@ -99,10 +99,10 @@ export default defineComponent({
|
||||
height: store.height || 0,
|
||||
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 el: Edge = {
|
||||
@@ -110,51 +110,51 @@ export default defineComponent({
|
||||
source: props.edge.source,
|
||||
target: props.edge.target,
|
||||
type: props.edge.type
|
||||
};
|
||||
}
|
||||
|
||||
if (props.edge.sourceHandle) {
|
||||
el.sourceHandle = props.edge.sourceHandle;
|
||||
el.sourceHandle = props.edge.sourceHandle
|
||||
}
|
||||
|
||||
if (props.edge.targetHandle) {
|
||||
el.targetHandle = props.edge.targetHandle;
|
||||
el.targetHandle = props.edge.targetHandle
|
||||
}
|
||||
|
||||
if (typeof props.edge.data !== 'undefined') {
|
||||
el.data = props.edge.data;
|
||||
el.data = props.edge.data
|
||||
}
|
||||
|
||||
return el;
|
||||
};
|
||||
return el
|
||||
}
|
||||
|
||||
const onEdgeClick = (event: MouseEvent) => {
|
||||
if (store.elementsSelectable) {
|
||||
store.unsetNodesSelection();
|
||||
store.addSelectedElements(edgeElement() as any);
|
||||
store.unsetNodesSelection()
|
||||
store.addSelectedElements(edgeElement() as any)
|
||||
}
|
||||
|
||||
hooks.edgeClick.trigger({ event, edge: edgeElement() });
|
||||
};
|
||||
hooks.edgeClick.trigger({ event, edge: edgeElement() })
|
||||
}
|
||||
|
||||
const onEdgeContextMenu = (event: MouseEvent) =>
|
||||
hooks.edgeContextMenu.trigger({
|
||||
event,
|
||||
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 nodeId = isSourceHandle ? props.edge.target : props.edge.source;
|
||||
const handleId = isSourceHandle ? props.edge.targetHandle : props.edge.sourceHandle;
|
||||
const isValidConnection = () => true;
|
||||
const isTarget = isSourceHandle;
|
||||
const nodeId = isSourceHandle ? props.edge.target : props.edge.source
|
||||
const handleId = isSourceHandle ? props.edge.targetHandle : props.edge.sourceHandle
|
||||
const isValidConnection = () => true
|
||||
const isTarget = isSourceHandle
|
||||
|
||||
hooks.edgeUpdateStart.trigger({ event, edge: edgeElement() });
|
||||
hooks.edgeUpdateStart.trigger({ event, edge: edgeElement() })
|
||||
onMouseDown(
|
||||
event,
|
||||
store,
|
||||
@@ -164,20 +164,20 @@ export default defineComponent({
|
||||
isTarget,
|
||||
isValidConnection,
|
||||
isSourceHandle ? 'target' : 'source'
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
const onEdgeUpdaterSourceMouseDown = (event: MouseEvent) => {
|
||||
handleEdgeUpdater(event, true);
|
||||
};
|
||||
handleEdgeUpdater(event, true)
|
||||
}
|
||||
|
||||
const onEdgeUpdaterTargetMouseDown = (event: MouseEvent) => {
|
||||
handleEdgeUpdater(event, false);
|
||||
};
|
||||
handleEdgeUpdater(event, false)
|
||||
}
|
||||
|
||||
const updating = ref<boolean>(false);
|
||||
const onEdgeUpdaterMouseEnter = () => (updating.value = true);
|
||||
const onEdgeUpdaterMouseOut = () => (updating.value = false);
|
||||
const updating = ref<boolean>(false)
|
||||
const onEdgeUpdaterMouseEnter = () => (updating.value = true)
|
||||
const onEdgeUpdaterMouseOut = () => (updating.value = false)
|
||||
|
||||
return () => {
|
||||
const { targetX, targetY, sourceX, sourceY } = getEdgePositions(
|
||||
@@ -187,7 +187,7 @@ export default defineComponent({
|
||||
nodes.value.targetNode as Node,
|
||||
targetHandle,
|
||||
targetPosition
|
||||
);
|
||||
)
|
||||
return !props.edge.isHidden && isVisible({ targetX, targetY, sourceX, sourceY }) ? (
|
||||
<g
|
||||
class={[
|
||||
@@ -242,7 +242,7 @@ export default defineComponent({
|
||||
</g>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { Position } from '../../types';
|
||||
import { defineComponent, HTMLAttributes, PropType } from 'vue';
|
||||
import { Position } from '../../types'
|
||||
import { defineComponent, HTMLAttributes, PropType } from 'vue'
|
||||
|
||||
const shiftX = (x: number, shift: number, position: Position): number => {
|
||||
if (position === Position.Left) return x - shift;
|
||||
if (position === Position.Right) return x + shift;
|
||||
return x;
|
||||
};
|
||||
if (position === Position.Left) return x - shift
|
||||
if (position === Position.Right) return x + shift
|
||||
return x
|
||||
}
|
||||
|
||||
const shiftY = (y: number, shift: number, position: Position): number => {
|
||||
if (position === Position.Top) return y - shift;
|
||||
if (position === Position.Bottom) return y + shift;
|
||||
return y;
|
||||
};
|
||||
if (position === Position.Top) return y - shift
|
||||
if (position === Position.Bottom) return y + shift
|
||||
return y
|
||||
}
|
||||
|
||||
export interface EdgeAnchorProps extends HTMLAttributes {
|
||||
position: Position;
|
||||
centerX: number;
|
||||
centerY: number;
|
||||
radius?: number;
|
||||
position: Position
|
||||
centerX: number
|
||||
centerY: number
|
||||
radius?: number
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -50,6 +50,6 @@ export default defineComponent({
|
||||
stroke="transparent"
|
||||
fill="transparent"
|
||||
/>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { EdgeTextProps, Rect } from '../../types';
|
||||
import { defineComponent, PropType, ref, watchEffect } from 'vue';
|
||||
import { EdgeTextProps, Rect } from '../../types'
|
||||
import { defineComponent, PropType, ref, watchEffect } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -37,11 +37,11 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const edgeRef = ref<SVGTextElement | null>(null);
|
||||
const edgeTextBox = ref<Rect>({ x: 0, y: 0, width: 0, height: 0 });
|
||||
const edgeRef = ref<SVGTextElement | null>(null)
|
||||
const edgeTextBox = ref<Rect>({ x: 0, y: 0, width: 0, height: 0 })
|
||||
|
||||
watchEffect(() => {
|
||||
const textBbox = edgeRef.value?.getBBox();
|
||||
const textBbox = edgeRef.value?.getBBox()
|
||||
|
||||
if (textBbox) {
|
||||
edgeTextBox.value = {
|
||||
@@ -49,9 +49,9 @@ export default defineComponent({
|
||||
y: textBbox.y,
|
||||
width: textBbox.width,
|
||||
height: textBbox.height
|
||||
};
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
return () => (
|
||||
<g
|
||||
@@ -75,6 +75,6 @@ export default defineComponent({
|
||||
</text>
|
||||
{slots.default ? slots.default() : ''}
|
||||
</g>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import EdgeText from './EdgeText';
|
||||
import { getMarkerEnd, getCenter, EdgeSmoothProps, GetCenterParams } from './utils';
|
||||
import { ArrowHeadType, EdgeType, Position } from '../../types';
|
||||
import { reactify } from '@vueuse/core';
|
||||
import { getBezierPath } from './BezierEdge';
|
||||
import { defineComponent } from 'vue'
|
||||
import EdgeText from './EdgeText'
|
||||
import { getMarkerEnd, getCenter, EdgeSmoothProps, GetCenterParams } from './utils'
|
||||
import { ArrowHeadType, EdgeType, Position } from '../../types'
|
||||
import { reactify } from '@vueuse/core'
|
||||
import { getBezierPath } from './BezierEdge'
|
||||
|
||||
// These are some helper methods for drawing the round corners
|
||||
// The name indicates the direction of the path. "bottomLeftCorner" goes
|
||||
// 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.
|
||||
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 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 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 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 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 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 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 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}`
|
||||
|
||||
interface GetSmoothStepPathParams {
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
sourcePosition?: Position;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
targetPosition?: Position;
|
||||
borderRadius?: number;
|
||||
centerX?: number;
|
||||
centerY?: number;
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
sourcePosition?: Position
|
||||
targetX: number
|
||||
targetY: number
|
||||
targetPosition?: Position
|
||||
borderRadius?: number
|
||||
centerX?: number
|
||||
centerY?: number
|
||||
}
|
||||
|
||||
export function getSmoothStepPath({
|
||||
@@ -41,56 +41,55 @@ export function getSmoothStepPath({
|
||||
centerX,
|
||||
centerY
|
||||
}: GetSmoothStepPathParams): string {
|
||||
const [_centerX, _centerY, offsetX, offsetY] = getCenter({ sourceX, sourceY, targetX, targetY });
|
||||
const cornerWidth = Math.min(borderRadius, Math.abs(targetX - sourceX));
|
||||
const cornerHeight = Math.min(borderRadius, Math.abs(targetY - sourceY));
|
||||
const cornerSize = Math.min(cornerWidth, cornerHeight, offsetX, offsetY);
|
||||
const leftAndRight = [Position.Left, Position.Right];
|
||||
const cX = typeof centerX !== 'undefined' ? centerX : _centerX;
|
||||
const cY = typeof centerY !== 'undefined' ? centerY : _centerY;
|
||||
const [_centerX, _centerY, offsetX, offsetY] = getCenter({ sourceX, sourceY, targetX, targetY })
|
||||
const cornerWidth = Math.min(borderRadius, Math.abs(targetX - sourceX))
|
||||
const cornerHeight = Math.min(borderRadius, Math.abs(targetY - sourceY))
|
||||
const cornerSize = Math.min(cornerWidth, cornerHeight, offsetX, offsetY)
|
||||
const leftAndRight = [Position.Left, Position.Right]
|
||||
const cX = typeof centerX !== 'undefined' ? centerX : _centerX
|
||||
const cY = typeof centerY !== 'undefined' ? centerY : _centerY
|
||||
|
||||
let firstCornerPath;
|
||||
let secondCornerPath;
|
||||
let firstCornerPath
|
||||
let secondCornerPath
|
||||
|
||||
if (sourceX <= targetX) {
|
||||
firstCornerPath = sourceY <= targetY ? bottomLeftCorner(sourceX, cY, cornerSize) : topLeftCorner(sourceX, cY, cornerSize);
|
||||
secondCornerPath = sourceY <= targetY ? rightTopCorner(targetX, cY, cornerSize) : rightBottomCorner(targetX, cY, cornerSize);
|
||||
firstCornerPath = sourceY <= targetY ? bottomLeftCorner(sourceX, cY, cornerSize) : topLeftCorner(sourceX, cY, cornerSize)
|
||||
secondCornerPath = sourceY <= targetY ? rightTopCorner(targetX, cY, cornerSize) : rightBottomCorner(targetX, cY, cornerSize)
|
||||
} else {
|
||||
firstCornerPath = sourceY < targetY ? bottomRightCorner(sourceX, cY, cornerSize) : topRightCorner(sourceX, cY, cornerSize);
|
||||
secondCornerPath = sourceY < targetY ? leftTopCorner(targetX, cY, cornerSize) : leftBottomCorner(targetX, cY, cornerSize);
|
||||
firstCornerPath = sourceY < targetY ? bottomRightCorner(sourceX, cY, cornerSize) : topRightCorner(sourceX, cY, cornerSize)
|
||||
secondCornerPath = sourceY < targetY ? leftTopCorner(targetX, cY, cornerSize) : leftBottomCorner(targetX, cY, cornerSize)
|
||||
}
|
||||
|
||||
if (leftAndRight.includes(sourcePosition) && leftAndRight.includes(targetPosition)) {
|
||||
if (sourceX <= targetX) {
|
||||
firstCornerPath = sourceY <= targetY ? rightTopCorner(cX, sourceY, cornerSize) : rightBottomCorner(cX, sourceY, cornerSize);
|
||||
secondCornerPath = sourceY <= targetY ? bottomLeftCorner(cX, targetY, cornerSize) : topLeftCorner(cX, targetY, cornerSize);
|
||||
firstCornerPath = sourceY <= targetY ? rightTopCorner(cX, sourceY, cornerSize) : rightBottomCorner(cX, sourceY, cornerSize)
|
||||
secondCornerPath = sourceY <= targetY ? bottomLeftCorner(cX, targetY, cornerSize) : topLeftCorner(cX, targetY, cornerSize)
|
||||
} else if (sourcePosition === Position.Right && targetPosition === Position.Left) {
|
||||
// and sourceX > targetX
|
||||
firstCornerPath = sourceY <= targetY ? leftTopCorner(cX, sourceY, cornerSize) : leftBottomCorner(cX, sourceY, cornerSize);
|
||||
secondCornerPath =
|
||||
sourceY <= targetY ? bottomRightCorner(cX, targetY, cornerSize) : topRightCorner(cX, targetY, cornerSize);
|
||||
firstCornerPath = sourceY <= targetY ? leftTopCorner(cX, sourceY, cornerSize) : leftBottomCorner(cX, sourceY, cornerSize)
|
||||
secondCornerPath = sourceY <= targetY ? bottomRightCorner(cX, targetY, cornerSize) : topRightCorner(cX, targetY, cornerSize)
|
||||
}
|
||||
} else if (leftAndRight.includes(sourcePosition) && !leftAndRight.includes(targetPosition)) {
|
||||
if (sourceX <= targetX) {
|
||||
firstCornerPath =
|
||||
sourceY <= targetY ? rightTopCorner(targetX, sourceY, cornerSize) : rightBottomCorner(targetX, sourceY, cornerSize);
|
||||
sourceY <= targetY ? rightTopCorner(targetX, sourceY, cornerSize) : rightBottomCorner(targetX, sourceY, cornerSize)
|
||||
} else {
|
||||
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)) {
|
||||
if (sourceX <= targetX) {
|
||||
firstCornerPath =
|
||||
sourceY <= targetY ? bottomLeftCorner(sourceX, targetY, cornerSize) : topLeftCorner(sourceX, targetY, cornerSize);
|
||||
sourceY <= targetY ? bottomLeftCorner(sourceX, targetY, cornerSize) : topLeftCorner(sourceX, targetY, cornerSize)
|
||||
} else {
|
||||
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({
|
||||
@@ -107,8 +106,8 @@ export default defineComponent({
|
||||
targetY: targetY,
|
||||
sourcePosition: sourcePosition,
|
||||
targetPosition: targetPosition
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
const path = reactify(({ sourceX, sourceY, targetX, targetY, targetPosition, sourcePosition }: GetSmoothStepPathParams) => {
|
||||
return getBezierPath({
|
||||
sourceX: sourceX,
|
||||
@@ -117,10 +116,10 @@ export default defineComponent({
|
||||
targetY: targetY,
|
||||
targetPosition: targetPosition,
|
||||
sourcePosition: sourcePosition
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId));
|
||||
const markerEnd = reactify((arrowHeadType?: ArrowHeadType, markerEndId?: string) => getMarkerEnd(arrowHeadType, markerEndId))
|
||||
|
||||
return () => (
|
||||
<>
|
||||
@@ -143,6 +142,6 @@ export default defineComponent({
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
}) as unknown as EdgeType;
|
||||
}) as unknown as EdgeType
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import SmoothStepEdge from './SmoothStepEdge';
|
||||
import { EdgeSmoothProps } from './utils';
|
||||
import { EdgeType } from '../../types';
|
||||
import { defineComponent } from 'vue'
|
||||
import SmoothStepEdge from './SmoothStepEdge'
|
||||
import { EdgeSmoothProps } from './utils'
|
||||
import { EdgeType } from '../../types'
|
||||
|
||||
export default defineComponent({
|
||||
components: { SmoothStepEdge },
|
||||
@@ -10,6 +10,6 @@ export default defineComponent({
|
||||
...EdgeSmoothProps
|
||||
},
|
||||
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 EdgeText from './EdgeText';
|
||||
import { getMarkerEnd, DefaultEdgeProps } from './utils';
|
||||
import { reactify } from '@vueuse/core';
|
||||
import { ArrowHeadType, EdgeType } from '../../types';
|
||||
import { defineComponent } from 'vue'
|
||||
import EdgeText from './EdgeText'
|
||||
import { getMarkerEnd, DefaultEdgeProps } from './utils'
|
||||
import { reactify } from '@vueuse/core'
|
||||
import { ArrowHeadType, EdgeType } from '../../types'
|
||||
|
||||
export default defineComponent({
|
||||
inheritAttrs: false,
|
||||
@@ -11,15 +11,15 @@ export default defineComponent({
|
||||
},
|
||||
setup(props) {
|
||||
const centerY = reactify((targetY: number, sourceY: number) => {
|
||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
||||
return targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
||||
});
|
||||
const yOffset = Math.abs(targetY - sourceY) / 2
|
||||
return targetY < sourceY ? targetY + yOffset : targetY - yOffset
|
||||
})
|
||||
const centerX = reactify((targetX: number, sourceX: number) => {
|
||||
const xOffset = Math.abs(targetX - sourceX) / 2;
|
||||
return targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
||||
});
|
||||
const xOffset = Math.abs(targetX - sourceX) / 2
|
||||
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 () => (
|
||||
<>
|
||||
@@ -42,6 +42,6 @@ export default defineComponent({
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
}) as unknown as EdgeType;
|
||||
}) as unknown as EdgeType
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
export { default as BezierEdge } from './BezierEdge';
|
||||
export { default as StepEdge } from './StepEdge';
|
||||
export { default as SmoothStepEdge } from './SmoothStepEdge';
|
||||
export { default as StraightEdge } from './StraightEdge';
|
||||
export { default as BezierEdge } from './BezierEdge'
|
||||
export { default as StepEdge } from './StepEdge'
|
||||
export { default as SmoothStepEdge } from './SmoothStepEdge'
|
||||
export { default as StraightEdge } from './StraightEdge'
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
import { ArrowHeadType, Position, EdgeProps, EdgeSmoothStepProps } from '../../types';
|
||||
import { PropType } from 'vue';
|
||||
import { ArrowHeadType, Position, EdgeProps, EdgeSmoothStepProps } from '../../types'
|
||||
import { PropType } from 'vue'
|
||||
|
||||
export const getMarkerEnd = (arrowHeadType?: ArrowHeadType, markerEndId?: string): string => {
|
||||
if (typeof markerEndId !== 'undefined' && markerEndId) {
|
||||
return `url(#${markerEndId})`;
|
||||
return `url(#${markerEndId})`
|
||||
}
|
||||
|
||||
return typeof arrowHeadType !== 'undefined' ? `url(#revue-flow__${arrowHeadType})` : 'none';
|
||||
};
|
||||
|
||||
export interface GetCenterParams {
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
sourcePosition?: Position;
|
||||
targetPosition?: Position;
|
||||
return typeof arrowHeadType !== 'undefined' ? `url(#revue-flow__${arrowHeadType})` : 'none'
|
||||
}
|
||||
|
||||
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 = ({
|
||||
sourceX,
|
||||
@@ -28,31 +28,31 @@ export const getCenter = ({
|
||||
sourcePosition = Position.Bottom,
|
||||
targetPosition = Position.Top
|
||||
}: GetCenterParams): [number, number, number, number] => {
|
||||
const sourceIsLeftOrRight = LeftOrRight.includes(sourcePosition);
|
||||
const targetIsLeftOrRight = LeftOrRight.includes(targetPosition);
|
||||
const sourceIsLeftOrRight = LeftOrRight.includes(sourcePosition)
|
||||
const targetIsLeftOrRight = LeftOrRight.includes(targetPosition)
|
||||
|
||||
// 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.
|
||||
const mixedEdge = (sourceIsLeftOrRight && !targetIsLeftOrRight) || (targetIsLeftOrRight && !sourceIsLeftOrRight);
|
||||
const mixedEdge = (sourceIsLeftOrRight && !targetIsLeftOrRight) || (targetIsLeftOrRight && !sourceIsLeftOrRight)
|
||||
|
||||
if (mixedEdge) {
|
||||
const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0;
|
||||
const centerX = sourceX > targetX ? sourceX - xOffset : sourceX + xOffset;
|
||||
const xOffset = sourceIsLeftOrRight ? Math.abs(targetX - sourceX) : 0
|
||||
const centerX = sourceX > targetX ? sourceX - xOffset : sourceX + xOffset
|
||||
|
||||
const yOffset = sourceIsLeftOrRight ? 0 : Math.abs(targetY - sourceY);
|
||||
const centerY = sourceY < targetY ? sourceY + yOffset : sourceY - yOffset;
|
||||
const yOffset = sourceIsLeftOrRight ? 0 : Math.abs(targetY - sourceY)
|
||||
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 centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset;
|
||||
const xOffset = Math.abs(targetX - sourceX) / 2
|
||||
const centerX = targetX < sourceX ? targetX + xOffset : targetX - xOffset
|
||||
|
||||
const yOffset = Math.abs(targetY - sourceY) / 2;
|
||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
|
||||
const yOffset = Math.abs(targetY - sourceY) / 2
|
||||
const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset
|
||||
|
||||
return [centerX, centerY, xOffset, yOffset];
|
||||
};
|
||||
return [centerX, centerY, xOffset, yOffset]
|
||||
}
|
||||
|
||||
export const DefaultEdgeProps = {
|
||||
id: {
|
||||
@@ -165,7 +165,7 @@ export const DefaultEdgeProps = {
|
||||
required: false,
|
||||
default: undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const EdgeSmoothProps = {
|
||||
...DefaultEdgeProps,
|
||||
@@ -174,4 +174,4 @@ export const EdgeSmoothProps = {
|
||||
required: false,
|
||||
default: 5
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { getHostForElement } from '../../utils';
|
||||
import { ElementId, ConnectionMode, Connection, HandleType, RevueFlowStore } from '../../types';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { getHostForElement } from '../../utils'
|
||||
import { ElementId, ConnectionMode, Connection, HandleType, RevueFlowStore } from '../../types'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
|
||||
export type ValidConnectionFunc = (connection: Connection) => boolean;
|
||||
export type ValidConnectionFunc = (connection: Connection) => boolean
|
||||
|
||||
type Result = {
|
||||
elementBelow: Element | null;
|
||||
isValid: boolean;
|
||||
connection: Connection;
|
||||
isHoveringHandle: boolean;
|
||||
};
|
||||
elementBelow: Element | null
|
||||
isValid: boolean
|
||||
connection: Connection
|
||||
isHoveringHandle: boolean
|
||||
}
|
||||
|
||||
// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
|
||||
function checkElementBelowIsValid(
|
||||
@@ -21,27 +21,27 @@ function checkElementBelowIsValid(
|
||||
isValidConnection: ValidConnectionFunc,
|
||||
doc: Document | ShadowRoot
|
||||
) {
|
||||
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY);
|
||||
const elementBelowIsTarget = elementBelow?.classList.contains('target') || false;
|
||||
const elementBelowIsSource = elementBelow?.classList.contains('source') || false;
|
||||
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
|
||||
const elementBelowIsTarget = elementBelow?.classList.contains('target') || false
|
||||
const elementBelowIsSource = elementBelow?.classList.contains('source') || false
|
||||
|
||||
const result: Result = {
|
||||
elementBelow,
|
||||
isValid: false,
|
||||
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
|
||||
isHoveringHandle: false
|
||||
};
|
||||
}
|
||||
|
||||
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
|
||||
const isValid =
|
||||
connectionMode === ConnectionMode.Strict ? (isTarget && elementBelowIsSource) || (!isTarget && elementBelowIsTarget) : true;
|
||||
connectionMode === ConnectionMode.Strict ? (isTarget && elementBelowIsSource) || (!isTarget && elementBelowIsTarget) : true
|
||||
|
||||
if (isValid) {
|
||||
const elementBelowNodeId = elementBelow.getAttribute('data-nodeid');
|
||||
const elementBelowHandleId = elementBelow.getAttribute('data-handleid');
|
||||
const elementBelowNodeId = elementBelow.getAttribute('data-nodeid')
|
||||
const elementBelowHandleId = elementBelow.getAttribute('data-handleid')
|
||||
const connection: Connection = isTarget
|
||||
? {
|
||||
source: elementBelowNodeId,
|
||||
@@ -54,30 +54,30 @@ function checkElementBelowIsValid(
|
||||
sourceHandle: handleId,
|
||||
target: elementBelowNodeId,
|
||||
targetHandle: elementBelowHandleId
|
||||
};
|
||||
}
|
||||
|
||||
result.connection = connection;
|
||||
result.isValid = isValidConnection(connection);
|
||||
result.connection = connection
|
||||
result.isValid = isValidConnection(connection)
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return result
|
||||
}
|
||||
|
||||
function resetRecentHandle(hoveredHandle: Element): void {
|
||||
hoveredHandle?.classList.remove('revue-flow__handle-valid');
|
||||
hoveredHandle?.classList.remove('revue-flow__handle-connecting');
|
||||
hoveredHandle?.classList.remove('revue-flow__handle-valid')
|
||||
hoveredHandle?.classList.remove('revue-flow__handle-connecting')
|
||||
}
|
||||
|
||||
export function onMouseDown(
|
||||
event: MouseEvent,
|
||||
store: RevueFlowStore,
|
||||
hooks: {
|
||||
connectStart: RevueFlowHooks['connectStart'];
|
||||
connectStop: RevueFlowHooks['connectStop'];
|
||||
connectEnd: RevueFlowHooks['connectEnd'];
|
||||
connect: RevueFlowHooks['connect'];
|
||||
edgeUpdateEnd: RevueFlowHooks['edgeUpdateEnd'];
|
||||
connectStart: RevueFlowHooks['connectStart']
|
||||
connectStop: RevueFlowHooks['connectStop']
|
||||
connectEnd: RevueFlowHooks['connectEnd']
|
||||
connect: RevueFlowHooks['connect']
|
||||
edgeUpdateEnd: RevueFlowHooks['edgeUpdateEnd']
|
||||
},
|
||||
handleId: ElementId | null,
|
||||
nodeId: ElementId,
|
||||
@@ -85,39 +85,39 @@ export function onMouseDown(
|
||||
isValidConnection: ValidConnectionFunc,
|
||||
elementEdgeUpdaterType?: HandleType
|
||||
): 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
|
||||
const doc = getHostForElement(event.target as HTMLElement);
|
||||
const doc = getHostForElement(event.target as HTMLElement)
|
||||
|
||||
if (!doc) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY);
|
||||
const elementBelowIsTarget = elementBelow?.classList.contains('target');
|
||||
const elementBelowIsSource = elementBelow?.classList.contains('source');
|
||||
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
|
||||
const elementBelowIsTarget = elementBelow?.classList.contains('target')
|
||||
const elementBelowIsSource = elementBelow?.classList.contains('source')
|
||||
|
||||
if (!revueFlowNode || (!elementBelowIsTarget && !elementBelowIsSource && !elementEdgeUpdaterType)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementBelowIsTarget ? 'target' : 'source';
|
||||
const containerBounds = revueFlowNode.getBoundingClientRect();
|
||||
let recentHoveredHandle: Element;
|
||||
const handleType = elementEdgeUpdaterType ? elementEdgeUpdaterType : elementBelowIsTarget ? 'target' : 'source'
|
||||
const containerBounds = revueFlowNode.getBoundingClientRect()
|
||||
let recentHoveredHandle: Element
|
||||
|
||||
store.connectionPosition.x = event.clientX - containerBounds.left;
|
||||
store.connectionPosition.y = event.clientY - containerBounds.top;
|
||||
store.connectionPosition.x = event.clientX - containerBounds.left
|
||||
store.connectionPosition.y = event.clientY - containerBounds.top
|
||||
|
||||
store.setConnectionNodeId({
|
||||
connectionNodeId: nodeId,
|
||||
connectionHandleId: handleId,
|
||||
connectionHandleType: handleType
|
||||
});
|
||||
hooks.connectStart.trigger({ event, params: { nodeId, handleId, handleType } });
|
||||
})
|
||||
hooks.connectStart.trigger({ event, params: { nodeId, handleId, handleType } })
|
||||
|
||||
function onMouseMove(event: MouseEvent) {
|
||||
store.connectionPosition.x = event.clientX - containerBounds.left;
|
||||
store.connectionPosition.y = event.clientY - containerBounds.top;
|
||||
store.connectionPosition.x = event.clientX - containerBounds.left
|
||||
store.connectionPosition.y = event.clientY - containerBounds.top
|
||||
|
||||
const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(
|
||||
event,
|
||||
@@ -127,18 +127,18 @@ export function onMouseDown(
|
||||
handleId,
|
||||
isValidConnection,
|
||||
doc
|
||||
);
|
||||
)
|
||||
|
||||
if (!isHoveringHandle) {
|
||||
return resetRecentHandle(recentHoveredHandle);
|
||||
return resetRecentHandle(recentHoveredHandle)
|
||||
}
|
||||
|
||||
const isOwnHandle = connection.source === connection.target;
|
||||
const isOwnHandle = connection.source === connection.target
|
||||
|
||||
if (!isOwnHandle && elementBelow) {
|
||||
recentHoveredHandle = elementBelow;
|
||||
elementBelow.classList.add('revue-flow__handle-connecting');
|
||||
elementBelow.classList.toggle('revue-flow__handle-valid', isValid);
|
||||
recentHoveredHandle = elementBelow
|
||||
elementBelow.classList.add('revue-flow__handle-connecting')
|
||||
elementBelow.classList.toggle('revue-flow__handle-valid', isValid)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,27 +151,27 @@ export function onMouseDown(
|
||||
handleId,
|
||||
isValidConnection,
|
||||
doc
|
||||
);
|
||||
)
|
||||
|
||||
hooks.connectStop.trigger(event);
|
||||
hooks.connectStop.trigger(event)
|
||||
|
||||
if (isValid) {
|
||||
hooks.connect.trigger(connection);
|
||||
hooks.connect.trigger(connection)
|
||||
}
|
||||
|
||||
hooks.connectEnd.trigger(event);
|
||||
hooks.connectEnd.trigger(event)
|
||||
|
||||
if (elementEdgeUpdaterType) {
|
||||
hooks.edgeUpdateEnd.trigger({ event } as any);
|
||||
hooks.edgeUpdateEnd.trigger({ event } as any)
|
||||
}
|
||||
|
||||
resetRecentHandle(recentHoveredHandle);
|
||||
store.setConnectionNodeId({ connectionNodeId: null, connectionHandleId: null, connectionHandleType: null });
|
||||
resetRecentHandle(recentHoveredHandle)
|
||||
store.setConnectionNodeId({ connectionNodeId: null, connectionHandleId: null, connectionHandleType: null })
|
||||
|
||||
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject);
|
||||
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
|
||||
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
|
||||
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
|
||||
}
|
||||
|
||||
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject);
|
||||
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject);
|
||||
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
|
||||
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { ElementId, Position, RevueFlowStore } from '../../types';
|
||||
import { onMouseDown, ValidConnectionFunc } from './handler';
|
||||
import { defineComponent, inject, PropType } from 'vue';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { ElementId, Position, RevueFlowStore } from '../../types'
|
||||
import { onMouseDown, ValidConnectionFunc } from './handler'
|
||||
import { defineComponent, inject, PropType } from 'vue'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
|
||||
const alwaysValid = () => true;
|
||||
const alwaysValid = () => true
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@@ -34,12 +34,12 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const nodeId = inject<ElementId>('NodeIdContext')!;
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
const nodeId = inject<ElementId>('NodeIdContext')!
|
||||
|
||||
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 () => (
|
||||
<div
|
||||
@@ -60,6 +60,6 @@ export default defineComponent({
|
||||
>
|
||||
{slots.default ? slots.default() : ''}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import Handle from '../../components/Handle';
|
||||
import { NodeProps, NodeType, Position } from '../../types';
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import Handle from '../../components/Handle'
|
||||
import { NodeProps, NodeType, Position } from '../../types'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DefaultNode',
|
||||
@@ -35,6 +35,6 @@ export default defineComponent({
|
||||
{props.data?.label}
|
||||
<Handle type="source" position={props.sourcePosition} isConnectable={props.isConnectable} />
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
}) as NodeType;
|
||||
}) as NodeType
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Handle from '../../components/Handle';
|
||||
import { NodeProps, NodeType, Position } from '../../types';
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import Handle from '../../components/Handle'
|
||||
import { NodeProps, NodeType, Position } from '../../types'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'InputNode',
|
||||
@@ -29,6 +29,6 @@ export default defineComponent({
|
||||
{props.data?.label}
|
||||
<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 { templateRef, useResizeObserver } from '@vueuse/core';
|
||||
import { DraggableCore, DraggableEventListener } from '@braks/revue-draggable';
|
||||
import { Node, NodeDimensionUpdate, NodeType, RevueFlowStore } from '../../types';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { computed, defineComponent, h, inject, onMounted, PropType, provide } from 'vue'
|
||||
import { templateRef, useResizeObserver } from '@vueuse/core'
|
||||
import { DraggableCore, DraggableEventListener } from '@braks/revue-draggable'
|
||||
import { Node, NodeDimensionUpdate, NodeType, RevueFlowStore } from '../../types'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
|
||||
interface NodeProps {
|
||||
nodeTypes: Record<string, NodeType>;
|
||||
snapToGrid: boolean;
|
||||
snapGrid: [number, number];
|
||||
selectNodesOnDrag: boolean;
|
||||
nodeTypes: Record<string, NodeType>
|
||||
snapToGrid: boolean
|
||||
snapGrid: [number, number]
|
||||
selectNodesOnDrag: boolean
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -39,22 +39,22 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
provide('NodeIdContext', props.node.id);
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
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(
|
||||
() => props.node.draggable || (store.nodesDraggable && typeof props.node.draggable === 'undefined')
|
||||
);
|
||||
)
|
||||
const isSelectable = computed(
|
||||
() => props.node.selectable || (store.elementsSelectable && typeof props.node.selectable === 'undefined')
|
||||
);
|
||||
)
|
||||
const isConnectable = computed(
|
||||
() => props.node.connectable || (store.nodesConnectable && typeof props.node.connectable === 'undefined')
|
||||
);
|
||||
)
|
||||
|
||||
const node = () =>
|
||||
({
|
||||
@@ -62,72 +62,72 @@ export default defineComponent({
|
||||
type: props.node.type,
|
||||
position: { x: props.node.__rf.position.x, y: props.node.__rf.position.y },
|
||||
data: props.node.data
|
||||
} as Node);
|
||||
} as Node)
|
||||
|
||||
const onMouseEnterHandler = () => {
|
||||
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 = () => {
|
||||
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 = () => {
|
||||
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 = () => {
|
||||
return (event: MouseEvent) => hooks.nodeContextMenu.trigger({ event, node: node() });
|
||||
};
|
||||
return (event: MouseEvent) => hooks.nodeContextMenu.trigger({ event, node: node() })
|
||||
}
|
||||
|
||||
const onSelectNodeHandler = (event: MouseEvent) => {
|
||||
if (!isDraggable.value) {
|
||||
const n = node();
|
||||
const n = node()
|
||||
if (isSelectable.value) {
|
||||
store.unsetNodesSelection();
|
||||
store.unsetNodesSelection()
|
||||
|
||||
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 n = node();
|
||||
hooks.nodeDragStart.trigger({ event, node: n });
|
||||
const n = node()
|
||||
hooks.nodeDragStart.trigger({ event, node: n })
|
||||
|
||||
if (props.selectNodesOnDrag && isSelectable.value) {
|
||||
store.unsetNodesSelection();
|
||||
store.unsetNodesSelection()
|
||||
|
||||
if (!selected.value) {
|
||||
store.addSelectedElements([n]);
|
||||
store.addSelectedElements([n])
|
||||
}
|
||||
} else if (!props.selectNodesOnDrag && !selected.value && isSelectable.value) {
|
||||
store.unsetNodesSelection();
|
||||
store.addSelectedElements([]);
|
||||
store.unsetNodesSelection()
|
||||
store.addSelectedElements([])
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const onDrag: DraggableEventListener = ({ event, data }) => {
|
||||
const n = node();
|
||||
n.position.x += data.deltaX;
|
||||
n.position.y += data.deltaY;
|
||||
hooks.nodeDrag.trigger({ event, node: n });
|
||||
const n = node()
|
||||
n.position.x += data.deltaX
|
||||
n.position.y += data.deltaY
|
||||
hooks.nodeDrag.trigger({ event, node: n })
|
||||
|
||||
store?.updateNodePosDiff({
|
||||
id: props.node.id as string,
|
||||
@@ -136,39 +136,39 @@ export default defineComponent({
|
||||
y: data.deltaY
|
||||
},
|
||||
isDragging: true
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
const onDragStop: DraggableEventListener = ({ event }) => {
|
||||
const n = node();
|
||||
const n = 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
|
||||
if (!props.node.__rf.isDragging) {
|
||||
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({
|
||||
id: n.id || '',
|
||||
isDragging: false
|
||||
});
|
||||
})
|
||||
|
||||
hooks.nodeDragStop.trigger({ event, node: n });
|
||||
};
|
||||
hooks.nodeDragStop.trigger({ event, node: n })
|
||||
}
|
||||
|
||||
useResizeObserver(nodeElement, (entries) => {
|
||||
const updates: NodeDimensionUpdate[] = entries.map((entry) => ({
|
||||
id: entry.target.getAttribute('data-id') || '',
|
||||
nodeElement: entry.target as HTMLDivElement
|
||||
}));
|
||||
}))
|
||||
|
||||
store.updateNodeDimensions(updates);
|
||||
});
|
||||
store.updateNodeDimensions(updates)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
store.updateNodeDimensions([
|
||||
@@ -177,8 +177,8 @@ export default defineComponent({
|
||||
nodeElement: nodeElement.value,
|
||||
forceUpdate: true
|
||||
}
|
||||
]);
|
||||
});
|
||||
])
|
||||
})
|
||||
|
||||
return () =>
|
||||
props.node.isHidden ? (
|
||||
@@ -231,6 +231,6 @@ export default defineComponent({
|
||||
})}
|
||||
</div>
|
||||
</DraggableCore>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import Handle from '../../components/Handle';
|
||||
import { NodeProps, NodeType, Position } from '../../types';
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import Handle from '../../components/Handle'
|
||||
import { NodeProps, NodeType, Position } from '../../types'
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'OutputNode',
|
||||
@@ -29,6 +29,6 @@ export default defineComponent({
|
||||
{props.data?.label}
|
||||
<Handle type="source" position={props.targetPosition} isConnectable={props.isConnectable} />
|
||||
</>
|
||||
);
|
||||
)
|
||||
}
|
||||
}) as NodeType;
|
||||
}) as NodeType
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { HandleElement, Position } from '../../types';
|
||||
import { getDimensions } from '../../utils';
|
||||
import { HandleElement, Position } from '../../types'
|
||||
import { getDimensions } from '../../utils'
|
||||
|
||||
export const getHandleBounds = (nodeElement: HTMLDivElement, scale: number) => {
|
||||
const bounds = nodeElement.getBoundingClientRect();
|
||||
const bounds = nodeElement.getBoundingClientRect()
|
||||
|
||||
return {
|
||||
source: getHandleBoundsByHandleType('.source', nodeElement, bounds, scale),
|
||||
target: getHandleBoundsByHandleType('.target', nodeElement, bounds, scale)
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const getHandleBoundsByHandleType = (
|
||||
selector: string,
|
||||
@@ -16,19 +16,19 @@ export const getHandleBoundsByHandleType = (
|
||||
parentBounds: ClientRect | DOMRect,
|
||||
k: number
|
||||
): HandleElement[] | null => {
|
||||
const handles = nodeElement.querySelectorAll(selector);
|
||||
const handles = nodeElement.querySelectorAll(selector)
|
||||
|
||||
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 => {
|
||||
const bounds = handle.getBoundingClientRect();
|
||||
const dimensions = getDimensions(handle);
|
||||
const handleId = handle.getAttribute('data-handleid');
|
||||
const handlePosition = handle.getAttribute('data-handlepos') as unknown as Position;
|
||||
const bounds = handle.getBoundingClientRect()
|
||||
const dimensions = getDimensions(handle)
|
||||
const handleId = handle.getAttribute('data-handleid')
|
||||
const handlePosition = handle.getAttribute('data-handlepos') as unknown as Position
|
||||
|
||||
return {
|
||||
id: handleId,
|
||||
@@ -36,6 +36,6 @@ export const getHandleBoundsByHandleType = (
|
||||
x: (bounds.left - parentBounds.left) / k,
|
||||
y: (bounds.top - parentBounds.top) / k,
|
||||
...dimensions
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -3,56 +3,56 @@
|
||||
* The nodes selection rectangle gets displayed when a user
|
||||
* made a selectio with on or several nodes
|
||||
*/
|
||||
import { computed, defineComponent, inject } from 'vue';
|
||||
import { Draggable, DraggableEventListener } from '@braks/revue-draggable';
|
||||
import { isNode } from '../../utils/graph';
|
||||
import { Node, RevueFlowStore } from '../../types';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { computed, defineComponent, inject } from 'vue'
|
||||
import { Draggable, DraggableEventListener } from '@braks/revue-draggable'
|
||||
import { isNode } from '../../utils/graph'
|
||||
import { Node, RevueFlowStore } from '../../types'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
|
||||
export interface NodesSelectionProps {
|
||||
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void;
|
||||
onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void;
|
||||
onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void;
|
||||
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
|
||||
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void
|
||||
onSelectionDrag?: (event: MouseEvent, nodes: Node[]) => void
|
||||
onSelectionDragStop?: (event: MouseEvent, nodes: Node[]) => void
|
||||
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void
|
||||
}
|
||||
|
||||
const NodesSelection = defineComponent({
|
||||
setup() {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number]);
|
||||
const transform = computed(() => store.transform);
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number])
|
||||
const transform = computed(() => store.transform)
|
||||
|
||||
const selectedNodes = computed(() =>
|
||||
store.selectedElements
|
||||
? 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 {
|
||||
...matchingNode,
|
||||
position: matchingNode?.__rf.position
|
||||
} as Node;
|
||||
} as Node
|
||||
})
|
||||
: []
|
||||
);
|
||||
)
|
||||
|
||||
const style = computed(() => ({
|
||||
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
|
||||
}));
|
||||
}))
|
||||
|
||||
const innerStyle = computed(() => ({
|
||||
width: `${store.selectedNodesBbox.width}px`,
|
||||
height: `${store.selectedNodesBbox.height}px`,
|
||||
top: `${store.selectedNodesBbox.y}px`,
|
||||
left: `${store.selectedNodesBbox.x}px`
|
||||
}));
|
||||
}))
|
||||
|
||||
const onStart: DraggableEventListener = ({ event }) => {
|
||||
hooks.selectionDragStart.trigger({ event, nodes: selectedNodes.value });
|
||||
};
|
||||
hooks.selectionDragStart.trigger({ event, nodes: selectedNodes.value })
|
||||
}
|
||||
|
||||
const onDrag: DraggableEventListener = ({ event, data }) => {
|
||||
hooks.selectionDrag.trigger({ event, nodes: selectedNodes.value });
|
||||
hooks.selectionDrag.trigger({ event, nodes: selectedNodes.value })
|
||||
|
||||
store.updateNodePosDiff({
|
||||
diff: {
|
||||
@@ -60,24 +60,24 @@ const NodesSelection = defineComponent({
|
||||
y: data.deltaY
|
||||
},
|
||||
isDragging: true
|
||||
});
|
||||
};
|
||||
})
|
||||
}
|
||||
|
||||
const onStop: DraggableEventListener = ({ event }) => {
|
||||
store.updateNodePosDiff({
|
||||
isDragging: false
|
||||
});
|
||||
})
|
||||
|
||||
hooks.selectionDragStop.trigger({ event, nodes: selectedNodes.value });
|
||||
};
|
||||
hooks.selectionDragStop.trigger({ event, nodes: selectedNodes.value })
|
||||
}
|
||||
|
||||
const onContextMenu = (event: MouseEvent) => {
|
||||
const selectedNodes: Node[] = store.selectedElements
|
||||
? 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 !store.selectedElements || store.selectionActive ? (
|
||||
@@ -95,9 +95,9 @@ const NodesSelection = defineComponent({
|
||||
<div class="revue-flow__nodesselection-rect" onContextmenu={onContextMenu} style={innerStyle.value} />
|
||||
</Draggable>
|
||||
</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
|
||||
*/
|
||||
import { RevueFlowStore, XYPosition } from '../../types';
|
||||
import { computed, defineComponent, inject } from 'vue';
|
||||
import { templateRef, useEventListener } from '@vueuse/core';
|
||||
import { RevueFlowStore, XYPosition } from '../../types'
|
||||
import { computed, defineComponent, inject } from 'vue'
|
||||
import { templateRef, useEventListener } from '@vueuse/core'
|
||||
|
||||
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) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const containerBounds = revueFlowNode.getBoundingClientRect();
|
||||
const containerBounds = revueFlowNode.getBoundingClientRect()
|
||||
|
||||
return {
|
||||
x: event.clientX - containerBounds.left,
|
||||
y: event.clientY - containerBounds.top
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
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)`
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: { SelectionRect },
|
||||
setup() {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const el = templateRef('user-selection', null);
|
||||
const shouldRender = computed(() => store.selectionActive || store.elementsSelectable);
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const el = templateRef('user-selection', null)
|
||||
const shouldRender = computed(() => store.selectionActive || store.elementsSelectable)
|
||||
|
||||
const onMouseDown = (event: MouseEvent) => {
|
||||
const mousePos = getMousePosition(event);
|
||||
const mousePos = getMousePosition(event)
|
||||
if (!mousePos) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
store.setUserSelection(mousePos);
|
||||
};
|
||||
store.setUserSelection(mousePos)
|
||||
}
|
||||
|
||||
const onMouseMove = (event: MouseEvent) => {
|
||||
if (!store.selectionActive) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
const mousePos = getMousePosition(event);
|
||||
const mousePos = getMousePosition(event)
|
||||
if (!mousePos) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
store.updateUserSelection(mousePos);
|
||||
};
|
||||
store.updateUserSelection(mousePos)
|
||||
}
|
||||
|
||||
const onMouseUp = () => {
|
||||
store.unsetUserSelection();
|
||||
};
|
||||
store.unsetUserSelection()
|
||||
}
|
||||
|
||||
const onMouseLeave = () => {
|
||||
store.unsetUserSelection();
|
||||
store.unsetNodesSelection();
|
||||
};
|
||||
store.unsetUserSelection()
|
||||
store.unsetNodesSelection()
|
||||
}
|
||||
|
||||
useEventListener(el, 'mousedown', onMouseDown);
|
||||
useEventListener(el, 'mousemove', onMouseMove);
|
||||
useEventListener(el, 'click', onMouseUp);
|
||||
useEventListener(el, 'mouseup', onMouseUp);
|
||||
useEventListener(el, 'mouseleave', onMouseLeave);
|
||||
useEventListener(el, 'mousedown', onMouseDown)
|
||||
useEventListener(el, 'mousemove', onMouseMove)
|
||||
useEventListener(el, 'click', onMouseUp)
|
||||
useEventListener(el, 'mouseup', onMouseUp)
|
||||
useEventListener(el, 'mouseleave', onMouseLeave)
|
||||
|
||||
return () =>
|
||||
shouldRender.value ? (
|
||||
@@ -91,6 +91,6 @@ export default defineComponent({
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
interface MarkerProps {
|
||||
id: string;
|
||||
id: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -25,6 +25,6 @@ export default defineComponent({
|
||||
>
|
||||
{slots.default ? slots.default() : ''}
|
||||
</marker>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import Marker from './Marker';
|
||||
import { defineComponent, PropType } from 'vue'
|
||||
import Marker from './Marker'
|
||||
|
||||
interface MarkerDefinitionsProps {
|
||||
color: string;
|
||||
color: string
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -38,6 +38,6 @@ export default defineComponent({
|
||||
/>
|
||||
</Marker>
|
||||
</defs>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
import { CSSProperties, defineComponent, inject, PropType } from 'vue';
|
||||
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types';
|
||||
import ConnectionLine from '../../components/ConnectionLine';
|
||||
import MarkerDefinitions from './MarkerDefinitions';
|
||||
import Edge from '../../components/Edges/Edge';
|
||||
import { CSSProperties, defineComponent, inject, PropType } from 'vue'
|
||||
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types'
|
||||
import ConnectionLine from '../../components/ConnectionLine'
|
||||
import MarkerDefinitions from './MarkerDefinitions'
|
||||
import Edge from '../../components/Edges/Edge'
|
||||
|
||||
interface EdgeRendererProps {
|
||||
edgeTypes: any;
|
||||
connectionLineType: ConnectionLineType;
|
||||
connectionLineStyle?: CSSProperties;
|
||||
connectionLineComponent?: ConnectionLineComponent;
|
||||
connectionMode?: ConnectionMode;
|
||||
arrowHeadColor: string;
|
||||
markerEndId?: string;
|
||||
onlyRenderVisibleElements: boolean;
|
||||
edgeUpdaterRadius?: number;
|
||||
edgeTypes: any
|
||||
connectionLineType: ConnectionLineType
|
||||
connectionLineStyle?: CSSProperties
|
||||
connectionLineComponent?: ConnectionLineComponent
|
||||
connectionMode?: ConnectionMode
|
||||
arrowHeadColor: string
|
||||
markerEndId?: string
|
||||
onlyRenderVisibleElements: boolean
|
||||
edgeUpdaterRadius?: number
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -70,7 +70,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
|
||||
return () => (
|
||||
<svg width={store.width} height={store.height} class="revue-flow__edges">
|
||||
@@ -94,6 +94,6 @@ export default defineComponent({
|
||||
)}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges';
|
||||
import { rectToBox } from '../../utils/graph';
|
||||
import { Position, Node, XYPosition, ElementId, HandleElement, Transform, Edge, EdgeType } from '../../types';
|
||||
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges'
|
||||
import { rectToBox } from '../../utils/graph'
|
||||
import { Position, Node, XYPosition, ElementId, HandleElement, Transform, Edge, EdgeType } from '../../types'
|
||||
|
||||
export function createEdgeTypes(edgeTypes: Record<string, EdgeType>): 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,
|
||||
step: edgeTypes.step || StepEdge,
|
||||
smoothstep: edgeTypes.step || SmoothStepEdge
|
||||
};
|
||||
}
|
||||
|
||||
const wrappedTypes = {} as Record<string, EdgeType>;
|
||||
const wrappedTypes = {} as Record<string, EdgeType>
|
||||
const specialTypes: Record<string, EdgeType> = Object.keys(edgeTypes)
|
||||
.filter((k) => !['default', 'bezier'].includes(k))
|
||||
.reduce((res, key) => {
|
||||
res[key] = edgeTypes[key] || BezierEdge;
|
||||
res[key] = edgeTypes[key] || BezierEdge
|
||||
|
||||
return res;
|
||||
}, wrappedTypes);
|
||||
return res
|
||||
}, wrappedTypes)
|
||||
|
||||
return {
|
||||
...standardTypes,
|
||||
...specialTypes
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function getHandlePosition(position: Position, node: Node, handle: any | null = null): XYPosition {
|
||||
const x = (handle?.x || 0) + node.__rf.position.x;
|
||||
const y = (handle?.y || 0) + node.__rf.position.y;
|
||||
const width = handle?.width || node.__rf.width;
|
||||
const height = handle?.height || node.__rf.height;
|
||||
const x = (handle?.x || 0) + node.__rf.position.x
|
||||
const y = (handle?.y || 0) + node.__rf.position.y
|
||||
const width = handle?.width || node.__rf.width
|
||||
const height = handle?.height || node.__rf.height
|
||||
|
||||
switch (position) {
|
||||
case Position.Top:
|
||||
return {
|
||||
x: x + width / 2,
|
||||
y
|
||||
};
|
||||
}
|
||||
case Position.Right:
|
||||
return {
|
||||
x: x + width,
|
||||
y: y + height / 2
|
||||
};
|
||||
}
|
||||
case Position.Bottom:
|
||||
return {
|
||||
x: x + width / 2,
|
||||
y: y + height
|
||||
};
|
||||
}
|
||||
case Position.Left:
|
||||
return {
|
||||
x,
|
||||
y: y + height / 2
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function getHandle(bounds: HandleElement[], handleId: ElementId | null): HandleElement | null {
|
||||
if (!bounds) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
// there is no handleId when there are no multiple handles/ handles with ids
|
||||
// so we just pick the first one
|
||||
let handle = null;
|
||||
let handle = null
|
||||
if (bounds.length === 1 || !handleId) {
|
||||
handle = bounds[0];
|
||||
handle = bounds[0]
|
||||
} 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 {
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
targetX: number
|
||||
targetY: number
|
||||
}
|
||||
|
||||
export const getEdgePositions = (
|
||||
@@ -87,23 +87,23 @@ export const getEdgePositions = (
|
||||
targetHandle: HandleElement | unknown,
|
||||
targetPosition: Position
|
||||
): EdgePositions => {
|
||||
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNode, sourceHandle);
|
||||
const targetHandlePos = getHandlePosition(targetPosition, targetNode, targetHandle);
|
||||
const sourceHandlePos = getHandlePosition(sourcePosition, sourceNode, sourceHandle)
|
||||
const targetHandlePos = getHandlePosition(targetPosition, targetNode, targetHandle)
|
||||
|
||||
return {
|
||||
sourceX: sourceHandlePos.x,
|
||||
sourceY: sourceHandlePos.y,
|
||||
targetX: targetHandlePos.x,
|
||||
targetY: targetHandlePos.y
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
interface IsEdgeVisibleParams {
|
||||
sourcePos: XYPosition;
|
||||
targetPos: XYPosition;
|
||||
width: number;
|
||||
height: number;
|
||||
transform: Transform;
|
||||
sourcePos: XYPosition
|
||||
targetPos: XYPosition
|
||||
width: number
|
||||
height: number
|
||||
transform: Transform
|
||||
}
|
||||
|
||||
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),
|
||||
x2: Math.max(sourcePos.x, targetPos.x),
|
||||
y2: Math.max(sourcePos.y, targetPos.y)
|
||||
};
|
||||
}
|
||||
|
||||
if (edgeBox.x === edgeBox.x2) {
|
||||
edgeBox.x2 += 1;
|
||||
edgeBox.x2 += 1
|
||||
}
|
||||
|
||||
if (edgeBox.y === edgeBox.y2) {
|
||||
edgeBox.y2 += 1;
|
||||
edgeBox.y2 += 1
|
||||
}
|
||||
|
||||
const viewBox = rectToBox({
|
||||
@@ -127,31 +127,31 @@ export function isEdgeVisible({ sourcePos, targetPos, width, height, transform }
|
||||
y: (0 - transform[1]) / transform[2],
|
||||
width: width / transform[2],
|
||||
height: height / transform[2]
|
||||
});
|
||||
})
|
||||
|
||||
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 overlappingArea = Math.ceil(xOverlap * yOverlap);
|
||||
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 overlappingArea = Math.ceil(xOverlap * yOverlap)
|
||||
|
||||
return overlappingArea > 0;
|
||||
return overlappingArea > 0
|
||||
}
|
||||
|
||||
type SourceTargetNode = {
|
||||
sourceNode: Node | null;
|
||||
targetNode: Node | null;
|
||||
};
|
||||
sourceNode: Node | null
|
||||
targetNode: Node | null
|
||||
}
|
||||
|
||||
export const getSourceTargetNodes = (edge: Edge, nodes: Node[]): SourceTargetNode => {
|
||||
return nodes.reduce(
|
||||
(res, node) => {
|
||||
if (node.id === edge.source) {
|
||||
res.sourceNode = node;
|
||||
res.sourceNode = node
|
||||
}
|
||||
if (node.id === edge.target) {
|
||||
res.targetNode = node;
|
||||
res.targetNode = node
|
||||
}
|
||||
return res;
|
||||
return res
|
||||
},
|
||||
{ sourceNode: null, targetNode: null } as SourceTargetNode
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { computed, defineComponent, inject, PropType } from 'vue';
|
||||
import ZoomPane from '../ZoomPane';
|
||||
import UserSelection from '../../components/UserSelection';
|
||||
import NodesSelection from '../../components/NodesSelection';
|
||||
import { RevueFlowStore, GraphViewProps } from '../../types';
|
||||
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
|
||||
import useKeyPress from '../../hooks/useKeyPress';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { computed, defineComponent, inject, PropType } from 'vue'
|
||||
import ZoomPane from '../ZoomPane'
|
||||
import UserSelection from '../../components/UserSelection'
|
||||
import NodesSelection from '../../components/NodesSelection'
|
||||
import { RevueFlowStore, GraphViewProps } from '../../types'
|
||||
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler'
|
||||
import useKeyPress from '../../hooks/useKeyPress'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
|
||||
type FlowRendererProps = Omit<
|
||||
GraphViewProps,
|
||||
@@ -18,7 +18,7 @@ type FlowRendererProps = Omit<
|
||||
| 'arrowHeadColor'
|
||||
| 'onlyRenderVisibleElements'
|
||||
| 'selectNodesOnDrag'
|
||||
>;
|
||||
>
|
||||
|
||||
export default defineComponent({
|
||||
name: 'FlowRenderer',
|
||||
@@ -101,28 +101,28 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const keyPressed = useKeyPress(props.selectionKeyCode);
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
const keyPressed = useKeyPress(props.selectionKeyCode)
|
||||
|
||||
useGlobalKeyHandler({
|
||||
store,
|
||||
onElementsRemove: hooks.elementsRemove.trigger,
|
||||
deleteKeyCode: props.deleteKeyCode || '',
|
||||
multiSelectionKeyCode: props.multiSelectionKeyCode || ''
|
||||
});
|
||||
})
|
||||
|
||||
const onClick = (event: MouseEvent) => {
|
||||
hooks.paneClick.trigger(event);
|
||||
store.unsetNodesSelection();
|
||||
store.resetSelectedElements();
|
||||
};
|
||||
hooks.paneClick.trigger(event)
|
||||
store.unsetNodesSelection()
|
||||
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 () => (
|
||||
<ZoomPane
|
||||
@@ -145,6 +145,6 @@ export default defineComponent({
|
||||
{selectionActive.value ? <NodesSelection /> : ''}
|
||||
<div class="revue-flow__pane" onClick={onClick} onContextmenu={onContextMenu} onWheel={onWheel} />
|
||||
</ZoomPane>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { defineComponent, inject, PropType } from 'vue';
|
||||
import FlowRenderer from '../FlowRenderer';
|
||||
import NodeRenderer from '../NodeRenderer';
|
||||
import EdgeRenderer from '../EdgeRenderer';
|
||||
import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph';
|
||||
import { GraphViewProps, RevueFlowStore } from '../../types';
|
||||
import useZoomPanHelper from '../../hooks/useZoomPanHelper';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { tryOnMounted } from '@vueuse/core';
|
||||
import { defineComponent, inject, PropType } from 'vue'
|
||||
import FlowRenderer from '../FlowRenderer'
|
||||
import NodeRenderer from '../NodeRenderer'
|
||||
import EdgeRenderer from '../EdgeRenderer'
|
||||
import { onLoadProject, onLoadGetElements, onLoadToObject } from '../../utils/graph'
|
||||
import { GraphViewProps, RevueFlowStore } from '../../types'
|
||||
import useZoomPanHelper from '../../hooks/useZoomPanHelper'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
import { tryOnMounted } from '@vueuse/core'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'GraphView',
|
||||
@@ -177,10 +177,10 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
|
||||
const { onReady } = useZoomPanHelper();
|
||||
const { onReady } = useZoomPanHelper()
|
||||
onReady(({ zoomIn, zoomOut, zoomTo, transform, fitView }) => {
|
||||
hooks.load.trigger({
|
||||
fitView: (params = { padding: 0.1 }) => fitView(params),
|
||||
@@ -191,52 +191,52 @@ export default defineComponent({
|
||||
project: onLoadProject(store),
|
||||
getElements: onLoadGetElements(store),
|
||||
toObject: onLoadToObject(store)
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
const init = () => {
|
||||
if (typeof props.snapToGrid !== 'undefined') {
|
||||
store.snapToGrid = props.snapToGrid;
|
||||
store.snapToGrid = props.snapToGrid
|
||||
}
|
||||
|
||||
if (typeof props.snapGrid !== 'undefined') {
|
||||
store.snapGrid = props.snapGrid;
|
||||
store.snapGrid = props.snapGrid
|
||||
}
|
||||
|
||||
if (typeof props.minZoom !== 'undefined') {
|
||||
store.setMinZoom(props.minZoom);
|
||||
store.setMinZoom(props.minZoom)
|
||||
}
|
||||
|
||||
if (typeof props.maxZoom !== 'undefined') {
|
||||
store.setMaxZoom(props.maxZoom);
|
||||
store.setMaxZoom(props.maxZoom)
|
||||
}
|
||||
|
||||
if (typeof props.translateExtent !== 'undefined') {
|
||||
store.setTranslateExtent(props.translateExtent);
|
||||
store.setTranslateExtent(props.translateExtent)
|
||||
}
|
||||
|
||||
if (typeof props.nodeExtent !== 'undefined') {
|
||||
store.setNodeExtent(props.nodeExtent);
|
||||
store.setNodeExtent(props.nodeExtent)
|
||||
}
|
||||
|
||||
if (typeof props.nodesConnectable !== 'undefined') {
|
||||
store.nodesConnectable = !!props.nodesConnectable;
|
||||
store.nodesConnectable = !!props.nodesConnectable
|
||||
}
|
||||
if (typeof props.elementsSelectable !== 'undefined') {
|
||||
store.elementsSelectable = !!props.elementsSelectable;
|
||||
store.elementsSelectable = !!props.elementsSelectable
|
||||
}
|
||||
if (typeof props.nodesDraggable !== 'undefined') {
|
||||
store.nodesDraggable = !!props.nodesDraggable;
|
||||
store.nodesDraggable = !!props.nodesDraggable
|
||||
}
|
||||
|
||||
if (typeof props.connectionMode !== 'undefined') {
|
||||
store.connectionMode = props.connectionMode;
|
||||
store.connectionMode = props.connectionMode
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
tryOnMounted(() => {
|
||||
init();
|
||||
});
|
||||
init()
|
||||
})
|
||||
|
||||
return () => (
|
||||
<FlowRenderer
|
||||
@@ -275,6 +275,6 @@ export default defineComponent({
|
||||
edgeUpdaterRadius={props.edgeUpdaterRadius}
|
||||
/>
|
||||
</FlowRenderer>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import { defineComponent, inject, PropType } from 'vue';
|
||||
import Node from '../../components/Nodes/Node';
|
||||
import { getNodesInside } from '../../utils/graph';
|
||||
import { NodeType, RevueFlowStore } from '../../types';
|
||||
import { defineComponent, inject, PropType } from 'vue'
|
||||
import Node from '../../components/Nodes/Node'
|
||||
import { getNodesInside } from '../../utils/graph'
|
||||
import { NodeType, RevueFlowStore } from '../../types'
|
||||
|
||||
interface NodeRendererProps {
|
||||
nodeTypes: Record<string, NodeType>;
|
||||
selectNodesOnDrag: boolean;
|
||||
snapToGrid: boolean;
|
||||
snapGrid: [number, number];
|
||||
onlyRenderVisibleElements: boolean;
|
||||
nodeTypes: Record<string, NodeType>
|
||||
selectNodesOnDrag: boolean
|
||||
snapToGrid: boolean
|
||||
snapGrid: [number, number]
|
||||
onlyRenderVisibleElements: boolean
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -42,7 +42,7 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
|
||||
const getNodes = () => {
|
||||
return props.onlyRenderVisibleElements
|
||||
@@ -58,30 +58,8 @@ export default defineComponent({
|
||||
store.transform,
|
||||
true
|
||||
)
|
||||
: 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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
: store.nodes
|
||||
}
|
||||
|
||||
return () => (
|
||||
<div
|
||||
@@ -90,8 +68,27 @@ export default defineComponent({
|
||||
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>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
import DefaultNode from '../../components/Nodes/DefaultNode';
|
||||
import InputNode from '../../components/Nodes/InputNode';
|
||||
import OutputNode from '../../components/Nodes/OutputNode';
|
||||
import { NodeType } from '../../types';
|
||||
import DefaultNode from '../../components/Nodes/DefaultNode'
|
||||
import InputNode from '../../components/Nodes/InputNode'
|
||||
import OutputNode from '../../components/Nodes/OutputNode'
|
||||
import { NodeType } from '../../types'
|
||||
|
||||
export function createNodeTypes(nodeTypes: Record<string, NodeType>): Record<string, NodeType> {
|
||||
const standardTypes: Record<string, NodeType> = {
|
||||
input: nodeTypes.input || InputNode,
|
||||
default: nodeTypes.default || DefaultNode,
|
||||
output: nodeTypes.output || OutputNode
|
||||
};
|
||||
}
|
||||
|
||||
const wrappedTypes = {} as NodeType;
|
||||
const wrappedTypes = {} as NodeType
|
||||
const specialTypes: NodeType = Object.keys(nodeTypes)
|
||||
.filter((k) => !['input', 'default', 'output'].includes(k))
|
||||
.reduce((res, key) => {
|
||||
res[key] = nodeTypes[key] || DefaultNode;
|
||||
res[key] = nodeTypes[key] || DefaultNode
|
||||
|
||||
return res;
|
||||
}, wrappedTypes);
|
||||
return res
|
||||
}, wrappedTypes)
|
||||
|
||||
return {
|
||||
...standardTypes,
|
||||
...specialTypes
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
import { defineComponent, onBeforeUnmount, onUpdated, PropType, provide, watch } from 'vue';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import '../../style.css';
|
||||
import '../../theme-default.css';
|
||||
import GraphView from '../GraphView';
|
||||
import DefaultNode from '../../components/Nodes/DefaultNode';
|
||||
import InputNode from '../../components/Nodes/InputNode';
|
||||
import OutputNode from '../../components/Nodes/OutputNode';
|
||||
import { createNodeTypes } from '../NodeRenderer/utils';
|
||||
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges';
|
||||
import { createEdgeTypes } from '../EdgeRenderer/utils';
|
||||
import { ConnectionMode, ConnectionLineType, PanOnScrollMode, RevueFlowStore, RevueFlowProps } from '../../types';
|
||||
import { initialState } from '../../store';
|
||||
import configureStore from '../../store/configure-store';
|
||||
import { RevueFlowHooks, useRevueFlow } from '../../hooks/RevueFlowHooks';
|
||||
import { PropType } from 'vue'
|
||||
import '../../style.css'
|
||||
import '../../theme-default.css'
|
||||
import GraphView from '../GraphView'
|
||||
import DefaultNode from '../../components/Nodes/DefaultNode'
|
||||
import InputNode from '../../components/Nodes/InputNode'
|
||||
import OutputNode from '../../components/Nodes/OutputNode'
|
||||
import { createNodeTypes } from '../NodeRenderer/utils'
|
||||
import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges'
|
||||
import { createEdgeTypes } from '../EdgeRenderer/utils'
|
||||
import { ConnectionMode, ConnectionLineType, PanOnScrollMode, RevueFlowStore, RevueFlowProps } from '../../types'
|
||||
import { initialState } from '../../store'
|
||||
import configureStore from '../../store/configure-store'
|
||||
import { RevueFlowHooks, useRevueFlow } from '../../hooks/RevueFlowHooks'
|
||||
|
||||
const defaultNodeTypes = {
|
||||
input: InputNode,
|
||||
default: DefaultNode,
|
||||
output: OutputNode
|
||||
};
|
||||
}
|
||||
|
||||
const defaultEdgeTypes = {
|
||||
default: BezierEdge,
|
||||
straight: StraightEdge,
|
||||
step: StepEdge,
|
||||
smoothstep: SmoothStepEdge
|
||||
};
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RevueFlow',
|
||||
@@ -220,25 +219,25 @@ export default defineComponent({
|
||||
},
|
||||
emits: Object.keys(useRevueFlow()),
|
||||
setup(props, { emit, slots }) {
|
||||
const store = configureStore(initialState)();
|
||||
provide<RevueFlowStore>('store', store);
|
||||
const hooks = useRevueFlow().bind(emit);
|
||||
provide<RevueFlowHooks>('hooks', hooks);
|
||||
const elements = useVModel(props, 'modelValue', emit);
|
||||
const store = configureStore(initialState)()
|
||||
provide<RevueFlowStore>('store', store)
|
||||
const hooks = useRevueFlow().bind(emit)
|
||||
provide<RevueFlowHooks>('hooks', hooks)
|
||||
const elements = useVModel(props, 'modelValue', emit)
|
||||
|
||||
store.setElements(elements.value);
|
||||
store.setElements(elements.value)
|
||||
watch(elements.value, () => {
|
||||
store.setElements(elements.value);
|
||||
});
|
||||
store.setElements(elements.value)
|
||||
})
|
||||
|
||||
onUpdated(() => {
|
||||
store.setElements(elements.value);
|
||||
});
|
||||
store.setElements(elements.value)
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
store.$reset();
|
||||
store.setElements([]);
|
||||
});
|
||||
store.$reset()
|
||||
store.setElements([])
|
||||
})
|
||||
|
||||
return () => (
|
||||
<div class="revue-flow">
|
||||
@@ -279,6 +278,6 @@ export default defineComponent({
|
||||
/>
|
||||
{slots.default ? slots.default() : ''}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom';
|
||||
import { select, pointer } from 'd3-selection';
|
||||
import { clamp } from '../../utils';
|
||||
import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode, RevueFlowStore } from '../../types';
|
||||
import { defineComponent, inject, PropType, ref, watchPostEffect } from 'vue';
|
||||
import useKeyPress from '../../hooks/useKeyPress';
|
||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { templateRef } from '@vueuse/core';
|
||||
import { ZoomBehavior } from 'd3';
|
||||
import { D3ZoomEvent, zoom, zoomIdentity, ZoomTransform } from 'd3-zoom'
|
||||
import { select, pointer } from 'd3-selection'
|
||||
import { clamp } from '../../utils'
|
||||
import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode, RevueFlowStore } from '../../types'
|
||||
import { defineComponent, inject, PropType, ref, watchPostEffect } from 'vue'
|
||||
import useKeyPress from '../../hooks/useKeyPress'
|
||||
import useResizeHandler from '../../hooks/useResizeHandler'
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks'
|
||||
import { templateRef } from '@vueuse/core'
|
||||
import { ZoomBehavior } from 'd3'
|
||||
|
||||
interface ZoomPaneProps {
|
||||
elementsSelectable?: boolean;
|
||||
zoomOnScroll?: boolean;
|
||||
zoomOnPinch?: boolean;
|
||||
panOnScroll?: boolean;
|
||||
panOnScrollSpeed?: number;
|
||||
panOnScrollMode?: PanOnScrollMode;
|
||||
zoomOnDoubleClick?: boolean;
|
||||
paneMoveable?: boolean;
|
||||
defaultPosition?: [number, number];
|
||||
defaultZoom?: number;
|
||||
translateExtent?: TranslateExtent;
|
||||
zoomActivationKeyCode?: KeyCode;
|
||||
selectionKeyCode?: KeyCode;
|
||||
elementsSelectable?: boolean
|
||||
zoomOnScroll?: boolean
|
||||
zoomOnPinch?: boolean
|
||||
panOnScroll?: boolean
|
||||
panOnScrollSpeed?: number
|
||||
panOnScrollMode?: PanOnScrollMode
|
||||
zoomOnDoubleClick?: boolean
|
||||
paneMoveable?: boolean
|
||||
defaultPosition?: [number, number]
|
||||
defaultZoom?: number
|
||||
translateExtent?: TranslateExtent
|
||||
zoomActivationKeyCode?: KeyCode
|
||||
selectionKeyCode?: KeyCode
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
@@ -95,35 +95,35 @@ export default defineComponent({
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null);
|
||||
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 });
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!
|
||||
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null)
|
||||
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 })
|
||||
|
||||
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 => ({
|
||||
x: eventTransform.x,
|
||||
y: eventTransform.y,
|
||||
zoom: eventTransform.k
|
||||
});
|
||||
})
|
||||
|
||||
const init = () => {
|
||||
const { onReady } = useResizeHandler(zoomPane, store.updateSize);
|
||||
const { onReady } = useResizeHandler(zoomPane, store.updateSize)
|
||||
onReady(() => {
|
||||
if (props.defaultPosition && props.defaultZoom) {
|
||||
const currentTranslateExtent =
|
||||
typeof props.translateExtent !== 'undefined' ? props.translateExtent : store.translateExtent;
|
||||
const d3ZoomInstance = zoom().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(currentTranslateExtent);
|
||||
const selection = select(zoomPane.value as Element).call(d3ZoomInstance);
|
||||
typeof props.translateExtent !== 'undefined' ? props.translateExtent : store.translateExtent
|
||||
const d3ZoomInstance = zoom().scaleExtent([store.minZoom, store.maxZoom]).translateExtent(currentTranslateExtent)
|
||||
const selection = select(zoomPane.value as Element).call(d3ZoomInstance)
|
||||
|
||||
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 clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom);
|
||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom);
|
||||
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 clampedZoom = clamp(props.defaultZoom, store.minZoom, store.maxZoom)
|
||||
const updatedTransform = zoomIdentity.translate(clampedX, clampedY).scale(clampedZoom)
|
||||
|
||||
d3ZoomInstance.transform(selection, updatedTransform);
|
||||
d3ZoomInstance.transform(selection, updatedTransform)
|
||||
|
||||
store.initD3Zoom({
|
||||
d3Zoom: d3ZoomInstance,
|
||||
@@ -131,106 +131,106 @@ export default defineComponent({
|
||||
d3ZoomHandler: selection.on('wheel.zoom'),
|
||||
// we need to pass transform because zoom handler is not registered when we set the initial transform
|
||||
transform: [clampedX, clampedY, clampedZoom]
|
||||
});
|
||||
applyZoomHandlers(d3ZoomInstance);
|
||||
applyZoomFilter(d3ZoomInstance);
|
||||
})
|
||||
applyZoomHandlers(d3ZoomInstance)
|
||||
applyZoomFilter(d3ZoomInstance)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const applyZoomHandlers = (d3Zoom: ZoomBehavior<Element, unknown>) => {
|
||||
d3Zoom.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
const flowTransform = eventToFlowTransform(event.transform)
|
||||
prevTransform.value = flowTransform
|
||||
|
||||
hooks.moveStart.trigger(flowTransform);
|
||||
hooks.moveStart.trigger(flowTransform)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
d3Zoom.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
const flowTransform = eventToFlowTransform(event.transform)
|
||||
prevTransform.value = flowTransform
|
||||
|
||||
hooks.moveEnd.trigger(flowTransform);
|
||||
hooks.moveEnd.trigger(flowTransform)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
useKeyPress(props.selectionKeyCode, (keyPress) => {
|
||||
if (keyPress) {
|
||||
d3Zoom.on('zoom', null);
|
||||
d3Zoom.on('zoom', null)
|
||||
} else {
|
||||
d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
store.transform = [event.transform.x, event.transform.y, event.transform.k];
|
||||
hooks.move.trigger(eventToFlowTransform(event.transform));
|
||||
});
|
||||
store.transform = [event.transform.x, event.transform.y, event.transform.k]
|
||||
hooks.move.trigger(eventToFlowTransform(event.transform))
|
||||
})
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
useKeyPress(props.zoomActivationKeyCode, (keyPress) => {
|
||||
if (props.panOnScroll && keyPress) {
|
||||
store.d3Selection
|
||||
?.on('wheel', (event: WheelEvent) => {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
event.preventDefault()
|
||||
event.stopImmediatePropagation()
|
||||
|
||||
const currentZoom = store.d3Selection?.property('__zoom').k || 1;
|
||||
const currentZoom = store.d3Selection?.property('__zoom').k || 1
|
||||
|
||||
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
|
||||
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10;
|
||||
const zoom = currentZoom * Math.pow(2, pinchDelta);
|
||||
if (store.d3Selection) store.d3Zoom?.scaleTo(store.d3Selection, zoom, point);
|
||||
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10
|
||||
const zoom = currentZoom * Math.pow(2, pinchDelta)
|
||||
if (store.d3Selection) store.d3Zoom?.scaleTo(store.d3Selection, zoom, point)
|
||||
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// increase scroll speed in firefox
|
||||
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
||||
const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
|
||||
const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize;
|
||||
const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize;
|
||||
const deltaNormalize = event.deltaMode === 1 ? 20 : 1
|
||||
const deltaX = props.panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize
|
||||
const deltaY = props.panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize
|
||||
|
||||
if (store.d3Selection && props.panOnScrollSpeed)
|
||||
store.d3Zoom?.translateBy(
|
||||
store.d3Selection,
|
||||
-(deltaX / currentZoom) * props.panOnScrollSpeed,
|
||||
-(deltaY / currentZoom) * props.panOnScrollSpeed
|
||||
);
|
||||
)
|
||||
})
|
||||
.on('wheel.zoom', null);
|
||||
.on('wheel.zoom', null)
|
||||
} 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 keyPress = useKeyPress(props.selectionKeyCode);
|
||||
const keyPress = useKeyPress(props.selectionKeyCode)
|
||||
d3Zoom.filter((event: MouseEvent) => {
|
||||
const zoomScroll = props.zoomOnScroll;
|
||||
const pinchZoom = props.zoomOnPinch && event.ctrlKey;
|
||||
const zoomScroll = props.zoomOnScroll
|
||||
const pinchZoom = props.zoomOnPinch && event.ctrlKey
|
||||
|
||||
// if all interactions are disabled, we prevent all zoom events
|
||||
if (!props.paneMoveable && !zoomScroll && !props.panOnScroll && !props.zoomOnDoubleClick && !props.zoomOnPinch) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// during a selection we prevent all other interactions
|
||||
if (keyPress.value) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// if zoom on double click is disabled, we prevent the double click event
|
||||
if (!props.zoomOnDoubleClick && event.type === 'dblclick') {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
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
|
||||
@@ -238,40 +238,40 @@ export default defineComponent({
|
||||
((event.target as Element).closest('.revue-flow__node') || (event.target as Element).closest('.revue-flow__edge')) &&
|
||||
event.type !== 'wheel'
|
||||
) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// when the target element is a node selection, we still allow zooming
|
||||
if ((event.target as Element).closest('.revue-flow__nodesselection') && event.type !== 'wheel') {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
if (!props.zoomOnPinch && event.ctrlKey && event.type === 'wheel') {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// when there is no scroll handling enabled, we prevent all wheel events
|
||||
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 (!props.paneMoveable && (event.type === 'mousedown' || event.type === 'touchstart')) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
|
||||
// default filter for d3-zoom
|
||||
return (!event.ctrlKey || event.type === 'wheel') && !event.button;
|
||||
});
|
||||
};
|
||||
};
|
||||
return (!event.ctrlKey || event.type === 'wheel') && !event.button
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
init()
|
||||
|
||||
return () => (
|
||||
<div ref="zoom-pane" class="revue-flow__renderer revue-flow__zoompane">
|
||||
{slots.default ? slots.default() : ''}
|
||||
</div>
|
||||
);
|
||||
)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
Vendored
+2
-2
@@ -1,2 +1,2 @@
|
||||
declare const __REVUE_FLOW_VERSION__: string;
|
||||
declare const __ENV__: string;
|
||||
declare const __REVUE_FLOW_VERSION__: string
|
||||
declare const __ENV__: string
|
||||
|
||||
+49
-49
@@ -1,47 +1,47 @@
|
||||
// global hooks
|
||||
import { Connection, Edge, Elements, FlowTransform, Node, OnConnectStartParams, OnLoadParams } from '../types';
|
||||
import { createEventHook, EventHook } from '@vueuse/core';
|
||||
import { Connection, Edge, Elements, FlowTransform, Node, OnConnectStartParams, OnLoadParams } from '../types'
|
||||
import { createEventHook, EventHook } from '@vueuse/core'
|
||||
|
||||
export interface RevueFlowHooks {
|
||||
elementClick: EventHook<{ event: MouseEvent; element: Node | Edge }>;
|
||||
elementsRemove: EventHook<Elements>;
|
||||
nodeDoubleClick: EventHook<{ event: MouseEvent; node: Node }>;
|
||||
nodeClick: EventHook<{ event: MouseEvent; node: Node }>;
|
||||
nodeMouseEnter: EventHook<{ event: MouseEvent; node: Node }>;
|
||||
nodeMouseMove: EventHook<{ event: MouseEvent; node: Node }>;
|
||||
nodeMouseLeave: EventHook<{ event: MouseEvent; node: Node }>;
|
||||
nodeContextMenu: EventHook<{ event: MouseEvent; node: Node }>;
|
||||
nodeDragStart: EventHook<{ event: MouseEvent; node: Node }>;
|
||||
nodeDrag: EventHook<{ event: MouseEvent; node: Node }>;
|
||||
nodeDragStop: EventHook<{ event: MouseEvent; node: Node }>;
|
||||
connect: EventHook<Connection>;
|
||||
elementClick: EventHook<{ event: MouseEvent; element: Node | Edge }>
|
||||
elementsRemove: EventHook<Elements>
|
||||
nodeDoubleClick: EventHook<{ event: MouseEvent; node: Node }>
|
||||
nodeClick: EventHook<{ event: MouseEvent; node: Node }>
|
||||
nodeMouseEnter: EventHook<{ event: MouseEvent; node: Node }>
|
||||
nodeMouseMove: EventHook<{ event: MouseEvent; node: Node }>
|
||||
nodeMouseLeave: EventHook<{ event: MouseEvent; node: Node }>
|
||||
nodeContextMenu: EventHook<{ event: MouseEvent; node: Node }>
|
||||
nodeDragStart: EventHook<{ event: MouseEvent; node: Node }>
|
||||
nodeDrag: EventHook<{ event: MouseEvent; node: Node }>
|
||||
nodeDragStop: EventHook<{ event: MouseEvent; node: Node }>
|
||||
connect: EventHook<Connection>
|
||||
connectStart: EventHook<{
|
||||
event: MouseEvent;
|
||||
params: OnConnectStartParams;
|
||||
}>;
|
||||
connectStop: EventHook<MouseEvent>;
|
||||
connectEnd: EventHook<MouseEvent>;
|
||||
load: EventHook<OnLoadParams>;
|
||||
move: EventHook<FlowTransform | undefined>;
|
||||
moveStart: EventHook<FlowTransform | undefined>;
|
||||
moveEnd: EventHook<FlowTransform | undefined>;
|
||||
selectionChange: EventHook<Elements | null>;
|
||||
selectionDragStart: EventHook<{ event: MouseEvent; nodes: Node[] }>;
|
||||
selectionDrag: EventHook<{ event: MouseEvent; nodes: Node[] }>;
|
||||
selectionDragStop: EventHook<{ event: MouseEvent; nodes: Node[] }>;
|
||||
selectionContextMenu: EventHook<{ event: MouseEvent; nodes: Node[] }>;
|
||||
paneScroll: EventHook<WheelEvent | undefined>;
|
||||
paneClick: EventHook<MouseEvent>;
|
||||
paneContextMenu: EventHook<MouseEvent>;
|
||||
edgeUpdate: EventHook<{ edge: Edge; connection: Connection }>;
|
||||
edgeContextMenu: EventHook<{ event: MouseEvent; edge: Edge }>;
|
||||
edgeMouseEnter: EventHook<{ event: MouseEvent; edge: Edge }>;
|
||||
edgeMouseMove: EventHook<{ event: MouseEvent; edge: Edge }>;
|
||||
edgeMouseLeave: EventHook<{ event: MouseEvent; edge: Edge }>;
|
||||
edgeDoubleClick: EventHook<{ event: MouseEvent; edge: Edge }>;
|
||||
edgeClick: EventHook<{ event: MouseEvent; edge: Edge }>;
|
||||
edgeUpdateStart: EventHook<{ event: MouseEvent; edge: Edge }>;
|
||||
edgeUpdateEnd: EventHook<{ event: MouseEvent; edge: Edge }>;
|
||||
event: MouseEvent
|
||||
params: OnConnectStartParams
|
||||
}>
|
||||
connectStop: EventHook<MouseEvent>
|
||||
connectEnd: EventHook<MouseEvent>
|
||||
load: EventHook<OnLoadParams>
|
||||
move: EventHook<FlowTransform | undefined>
|
||||
moveStart: EventHook<FlowTransform | undefined>
|
||||
moveEnd: EventHook<FlowTransform | undefined>
|
||||
selectionChange: EventHook<Elements | null>
|
||||
selectionDragStart: EventHook<{ event: MouseEvent; nodes: Node[] }>
|
||||
selectionDrag: EventHook<{ event: MouseEvent; nodes: Node[] }>
|
||||
selectionDragStop: EventHook<{ event: MouseEvent; nodes: Node[] }>
|
||||
selectionContextMenu: EventHook<{ event: MouseEvent; nodes: Node[] }>
|
||||
paneScroll: EventHook<WheelEvent | undefined>
|
||||
paneClick: EventHook<MouseEvent>
|
||||
paneContextMenu: EventHook<MouseEvent>
|
||||
edgeUpdate: EventHook<{ edge: Edge; connection: Connection }>
|
||||
edgeContextMenu: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||
edgeMouseEnter: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||
edgeMouseMove: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||
edgeMouseLeave: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||
edgeDoubleClick: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||
edgeClick: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||
edgeUpdateStart: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||
edgeUpdateEnd: EventHook<{ event: MouseEvent; edge: Edge }>
|
||||
}
|
||||
|
||||
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 }>(),
|
||||
connect: createEventHook<Connection>(),
|
||||
connectStart: createEventHook<{
|
||||
event: MouseEvent;
|
||||
params: OnConnectStartParams;
|
||||
event: MouseEvent
|
||||
params: OnConnectStartParams
|
||||
}>(),
|
||||
connectStop: createEventHook<MouseEvent>(),
|
||||
connectEnd: createEventHook<MouseEvent>(),
|
||||
@@ -85,20 +85,20 @@ export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string,
|
||||
edgeClick: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||
edgeUpdateStart: createEventHook<{ event: MouseEvent; edge: Edge }>(),
|
||||
edgeUpdateEnd: createEventHook<{ event: MouseEvent; edge: Edge }>()
|
||||
};
|
||||
}
|
||||
|
||||
const bind = (emit: (event: string, args: any) => void) => {
|
||||
for (const [key, value] of Object.entries(eventHooks)) {
|
||||
value.on((data: any) => {
|
||||
emit(key, data);
|
||||
});
|
||||
emit(key, data)
|
||||
})
|
||||
}
|
||||
|
||||
return eventHooks;
|
||||
};
|
||||
return eventHooks
|
||||
}
|
||||
|
||||
return {
|
||||
...eventHooks,
|
||||
bind
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,37 +1,37 @@
|
||||
import useKeyPress from './useKeyPress';
|
||||
import { isNode, getConnectedEdges } from '../utils/graph';
|
||||
import { Elements, KeyCode, ElementId, FlowElement, RevueFlowStore, Edge } from '../types';
|
||||
import { computed, watch } from 'vue';
|
||||
import useKeyPress from './useKeyPress'
|
||||
import { isNode, getConnectedEdges } from '../utils/graph'
|
||||
import { Elements, KeyCode, ElementId, FlowElement, RevueFlowStore, Edge } from '../types'
|
||||
import { computed, watch } from 'vue'
|
||||
|
||||
interface HookParams {
|
||||
store: RevueFlowStore;
|
||||
deleteKeyCode: KeyCode;
|
||||
multiSelectionKeyCode: KeyCode;
|
||||
onElementsRemove?: (elements: Elements) => void;
|
||||
store: RevueFlowStore
|
||||
deleteKeyCode: KeyCode
|
||||
multiSelectionKeyCode: KeyCode
|
||||
onElementsRemove?: (elements: Elements) => void
|
||||
}
|
||||
|
||||
export default ({ store, deleteKeyCode, multiSelectionKeyCode, onElementsRemove }: HookParams): void => {
|
||||
const deleteKeyPressed = useKeyPress(deleteKeyCode);
|
||||
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
|
||||
const selectedElements = computed(() => store.selectedElements || []);
|
||||
const edges = computed(() => store.edges);
|
||||
const deleteKeyPressed = useKeyPress(deleteKeyCode)
|
||||
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode)
|
||||
const selectedElements = computed(() => store.selectedElements || [])
|
||||
const edges = computed(() => store.edges)
|
||||
|
||||
watch(selectedElements, () => {
|
||||
if (onElementsRemove && deleteKeyPressed.value && selectedElements.value.length > 0) {
|
||||
const selectedNodes = selectedElements.value.filter(isNode);
|
||||
const connectedEdges = getConnectedEdges(selectedNodes, edges.value as Edge[]);
|
||||
const selectedNodes = selectedElements.value.filter(isNode)
|
||||
const connectedEdges = getConnectedEdges(selectedNodes, edges.value as Edge[])
|
||||
const elementsToRemove = [...selectedElements.value, ...connectedEdges].reduce(
|
||||
(res, item) => res.set(item.id, item),
|
||||
new Map<ElementId, FlowElement>()
|
||||
);
|
||||
)
|
||||
|
||||
onElementsRemove(Array.from(elementsToRemove.values()));
|
||||
store.unsetNodesSelection();
|
||||
store.resetSelectedElements();
|
||||
onElementsRemove(Array.from(elementsToRemove.values()))
|
||||
store.unsetNodesSelection()
|
||||
store.resetSelectedElements()
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
watch(multiSelectionKeyPressed, () => {
|
||||
store.multiSelectionActive = multiSelectionKeyPressed.value;
|
||||
});
|
||||
};
|
||||
store.multiSelectionActive = multiSelectionKeyPressed.value
|
||||
})
|
||||
}
|
||||
|
||||
+20
-20
@@ -1,47 +1,47 @@
|
||||
import { Ref } from 'vue';
|
||||
import { KeyCode } from '../types';
|
||||
import { controlledRef, onKeyStroke, useEventListener } from '@vueuse/core';
|
||||
import { Ref } from 'vue'
|
||||
import { KeyCode } from '../types'
|
||||
import { controlledRef, onKeyStroke, useEventListener } from '@vueuse/core'
|
||||
|
||||
export default (keyCode?: KeyCode, cb?: (keyPress: boolean) => void): Ref<boolean> => {
|
||||
const keyPressed = controlledRef<boolean>(false, {
|
||||
onBeforeChange(val, oldVal) {
|
||||
if (val === oldVal) return false;
|
||||
if (val === oldVal) return false
|
||||
},
|
||||
onChanged() {
|
||||
if (cb && typeof cb === 'function') cb(keyPressed.value);
|
||||
if (cb && typeof cb === 'function') cb(keyPressed.value)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
onKeyStroke(
|
||||
(event) => event.key === keyCode || event.keyCode === keyCode,
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
keyPressed.value = true;
|
||||
e.preventDefault()
|
||||
keyPressed.value = true
|
||||
},
|
||||
{ eventName: 'keypress' }
|
||||
);
|
||||
)
|
||||
onKeyStroke(
|
||||
(event) => event.key === keyCode || event.keyCode === keyCode,
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
keyPressed.value = true;
|
||||
e.preventDefault()
|
||||
keyPressed.value = true
|
||||
},
|
||||
{ eventName: 'keydown' }
|
||||
);
|
||||
)
|
||||
onKeyStroke(
|
||||
(event) => event.key === keyCode || event.keyCode === keyCode,
|
||||
(e) => {
|
||||
e.preventDefault();
|
||||
keyPressed.value = false;
|
||||
e.preventDefault()
|
||||
keyPressed.value = false
|
||||
},
|
||||
{ eventName: 'keyup' }
|
||||
);
|
||||
)
|
||||
|
||||
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 { getDimensions } from '../utils';
|
||||
import { Dimensions } from '../types';
|
||||
import { createEventHook, MaybeRef, unrefElement, until, useEventListener, useResizeObserver } from '@vueuse/core'
|
||||
import { getDimensions } from '../utils'
|
||||
import { Dimensions } from '../types'
|
||||
|
||||
export default (rendererNode: MaybeRef<HTMLDivElement>, updateSize: (size: Dimensions) => any) => {
|
||||
const resizeHandlerHook = createEventHook();
|
||||
const resizeHandlerHook = createEventHook()
|
||||
const updateDimensions = () => {
|
||||
if (!unrefElement(rendererNode)) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
const size = getDimensions(unrefElement(rendererNode));
|
||||
const size = getDimensions(unrefElement(rendererNode))
|
||||
|
||||
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)
|
||||
.toBeTruthy()
|
||||
.then(() => {
|
||||
updateDimensions();
|
||||
useEventListener(window, 'resize', updateDimensions);
|
||||
useResizeObserver(unrefElement(rendererNode), () => updateDimensions());
|
||||
resizeHandlerHook.trigger(true);
|
||||
});
|
||||
updateDimensions()
|
||||
useEventListener(window, 'resize', updateDimensions)
|
||||
useResizeObserver(unrefElement(rendererNode), () => updateDimensions())
|
||||
resizeHandlerHook.trigger(true)
|
||||
})
|
||||
|
||||
return {
|
||||
onReady: resizeHandlerHook.on
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { ElementId, RevueFlowStore, UpdateNodeInternals } from '../types';
|
||||
import { inject } from 'vue';
|
||||
import { ElementId, RevueFlowStore, UpdateNodeInternals } from '../types'
|
||||
import { inject } from 'vue'
|
||||
|
||||
function useUpdateNodeInternals(): UpdateNodeInternals {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
|
||||
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) {
|
||||
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 { computed, effectScope, inject } from 'vue';
|
||||
import { createEventHook, EventHookOn, until } from '@vueuse/core';
|
||||
import { Selection, ZoomBehavior } from 'd3';
|
||||
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph';
|
||||
import { FitViewParams, FlowTransform, ZoomPanHelperFunctions, Rect, XYPosition, RevueFlowStore } from '../types';
|
||||
import { zoomIdentity } from 'd3-zoom'
|
||||
import { computed, effectScope, inject } from 'vue'
|
||||
import { createEventHook, EventHookOn, until } from '@vueuse/core'
|
||||
import { Selection, ZoomBehavior } from 'd3'
|
||||
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph'
|
||||
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 zoomPanHelperEvent = createEventHook<ZoomPanHelperFunctions>();
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const zoomPanHelperEvent = createEventHook<ZoomPanHelperFunctions>()
|
||||
const store = inject<RevueFlowStore>('store')!
|
||||
|
||||
const scope = effectScope();
|
||||
const scope = effectScope()
|
||||
scope.run(() => {
|
||||
const d3Zoom = computed(() => store.d3Zoom);
|
||||
const d3Selection = computed(() => store.d3Selection);
|
||||
const d3Zoom = computed(() => store.d3Zoom)
|
||||
const d3Selection = computed(() => store.d3Selection)
|
||||
|
||||
const createZoomPanHelper = (
|
||||
d3Zoom: ZoomBehavior<Element, unknown>,
|
||||
@@ -24,18 +24,18 @@ const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } =>
|
||||
zoomOut: () => d3Zoom?.scaleBy(d3Selection, 1 / 1.2),
|
||||
zoomTo: (zoomLevel: number) => d3Zoom.scaleTo(d3Selection, zoomLevel),
|
||||
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 }) => {
|
||||
const { nodes, width, height, minZoom, maxZoom } = store;
|
||||
const { nodes, width, height, minZoom, maxZoom } = store
|
||||
|
||||
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(
|
||||
bounds,
|
||||
width,
|
||||
@@ -43,49 +43,49 @@ const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } =>
|
||||
options.minZoom || minZoom,
|
||||
options.maxZoom || maxZoom,
|
||||
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) => {
|
||||
const { width, height, maxZoom } = store;
|
||||
const { width, height, maxZoom } = store
|
||||
|
||||
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom;
|
||||
const centerX = width / 2 - x * nextZoom;
|
||||
const centerY = height / 2 - y * nextZoom;
|
||||
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom);
|
||||
const nextZoom = typeof zoom !== 'undefined' ? zoom : maxZoom
|
||||
const centerX = width / 2 - x * nextZoom
|
||||
const centerY = height / 2 - y * nextZoom
|
||||
const transform = zoomIdentity.translate(centerX, centerY).scale(nextZoom)
|
||||
|
||||
d3Zoom.transform(d3Selection, transform);
|
||||
d3Zoom.transform(d3Selection, transform)
|
||||
},
|
||||
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
|
||||
const { width, height, minZoom, maxZoom } = store;
|
||||
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding);
|
||||
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
||||
const { width, height, minZoom, maxZoom } = store
|
||||
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding)
|
||||
const transform = zoomIdentity.translate(x, y).scale(zoom)
|
||||
|
||||
d3Zoom.transform(d3Selection, transform);
|
||||
d3Zoom.transform(d3Selection, transform)
|
||||
},
|
||||
project: (position: XYPosition) => {
|
||||
const { transform, snapToGrid, snapGrid } = store;
|
||||
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
||||
const { transform, snapToGrid, snapGrid } = store
|
||||
return pointToRendererPoint(position, transform, snapToGrid, snapGrid)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
until(d3Zoom)
|
||||
.toBeTruthy()
|
||||
.then(() => {
|
||||
if (d3Zoom.value && d3Selection.value) {
|
||||
zoomPanHelperEvent.trigger(createZoomPanHelper(d3Zoom.value, d3Selection.value));
|
||||
zoomPanHelperEvent.trigger(createZoomPanHelper(d3Zoom.value, d3Selection.value))
|
||||
} else {
|
||||
throw Error('D3 could not be initialized.');
|
||||
throw Error('D3 could not be initialized.')
|
||||
}
|
||||
scope.stop();
|
||||
});
|
||||
});
|
||||
scope.stop()
|
||||
})
|
||||
})
|
||||
|
||||
return {
|
||||
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 Handle } from './components/Handle';
|
||||
export { default as EdgeText } from './components/Edges/EdgeText';
|
||||
export { getBezierPath } from './components/Edges/BezierEdge';
|
||||
export { getSmoothStepPath } from './components/Edges/SmoothStepEdge';
|
||||
export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils';
|
||||
export { default as default } from './container/RevueFlow'
|
||||
export { default as Handle } from './components/Handle'
|
||||
export { default as EdgeText } from './components/Edges/EdgeText'
|
||||
export { getBezierPath } from './components/Edges/BezierEdge'
|
||||
export { getSmoothStepPath } from './components/Edges/SmoothStepEdge'
|
||||
export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils'
|
||||
|
||||
export {
|
||||
isNode,
|
||||
@@ -16,9 +16,9 @@ export {
|
||||
updateEdge,
|
||||
getTransformForBounds,
|
||||
getRectOfNodes
|
||||
} from './utils/graph';
|
||||
export { default as useZoomPanHelper } from './hooks/useZoomPanHelper';
|
||||
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals';
|
||||
} from './utils/graph'
|
||||
export { default as useZoomPanHelper } from './hooks/useZoomPanHelper'
|
||||
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals'
|
||||
|
||||
export * from './additional-components';
|
||||
export * from './types';
|
||||
export * from './additional-components'
|
||||
export * from './types'
|
||||
|
||||
Vendored
+2
-2
@@ -1,4 +1,4 @@
|
||||
import Vue, { VNode } from 'vue';
|
||||
import Vue, { VNode } from 'vue'
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
@@ -9,7 +9,7 @@ declare global {
|
||||
interface ElementClass extends Vue {}
|
||||
|
||||
interface IntrinsicAttributes {
|
||||
[prop: string]: any;
|
||||
[prop: string]: any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+3
-3
@@ -1,6 +1,6 @@
|
||||
declare module '*.vue' {
|
||||
import { DefineComponent } from 'vue';
|
||||
import { DefineComponent } from 'vue'
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
export default component;
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
export default component
|
||||
}
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia';
|
||||
import isEqual from 'fast-deep-equal';
|
||||
import { Edge, Node, NodeDiffUpdate, RevueFlowState, RevueFlowActions, XYPosition } from '../types';
|
||||
import { getConnectedEdges, getNodesInside, getRectOfNodes, isEdge, isNode, parseEdge, parseNode } from '../utils/graph';
|
||||
import { clampPosition, getDimensions } from '../utils';
|
||||
import { getHandleBounds } from '../components/Nodes/utils';
|
||||
import { setActivePinia, createPinia, defineStore, StoreDefinition } from 'pinia'
|
||||
import isEqual from 'fast-deep-equal'
|
||||
import { Edge, Node, NodeDiffUpdate, RevueFlowState, RevueFlowActions, XYPosition } from '../types'
|
||||
import { getConnectedEdges, getNodesInside, getRectOfNodes, isEdge, isNode, parseEdge, parseNode } from '../utils/graph'
|
||||
import { clampPosition, getDimensions } from '../utils'
|
||||
import { getHandleBounds } from '../components/Nodes/utils'
|
||||
|
||||
type NextElements = {
|
||||
nextNodes: Node[];
|
||||
nextEdges: Edge[];
|
||||
};
|
||||
const pinia = createPinia();
|
||||
nextNodes: Node[]
|
||||
nextEdges: Edge[]
|
||||
}
|
||||
const pinia = createPinia()
|
||||
|
||||
export default function configureStore(
|
||||
preloadedState: RevueFlowState
|
||||
): StoreDefinition<string, RevueFlowState, any, RevueFlowActions> {
|
||||
setActivePinia(pinia);
|
||||
setActivePinia(pinia)
|
||||
|
||||
return defineStore({
|
||||
id: 'revue-flow-' + Math.random(),
|
||||
@@ -24,66 +24,66 @@ export default function configureStore(
|
||||
getters: {},
|
||||
actions: {
|
||||
setElements(elements) {
|
||||
const propElements = elements;
|
||||
const propElements = elements
|
||||
const nextElements: NextElements = {
|
||||
nextNodes: [],
|
||||
nextEdges: []
|
||||
};
|
||||
}
|
||||
const { nextNodes, nextEdges } = propElements.reduce((res, propElement): NextElements => {
|
||||
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) {
|
||||
const updatedNode: Node = {
|
||||
...storeNode,
|
||||
...propElement
|
||||
};
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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.
|
||||
updatedNode.__rf.width = null;
|
||||
updatedNode.__rf.width = null
|
||||
}
|
||||
|
||||
res.nextNodes.push(updatedNode);
|
||||
res.nextNodes.push(updatedNode)
|
||||
} else {
|
||||
res.nextNodes.push(parseNode(propElement, this.nodeExtent));
|
||||
res.nextNodes.push(parseNode(propElement, this.nodeExtent))
|
||||
}
|
||||
} 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) {
|
||||
res.nextEdges.push({
|
||||
...storeEdge,
|
||||
...propElement
|
||||
});
|
||||
})
|
||||
} else {
|
||||
res.nextEdges.push(parseEdge(propElement));
|
||||
res.nextEdges.push(parseEdge(propElement))
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}, nextElements);
|
||||
return res
|
||||
}, nextElements)
|
||||
|
||||
this.nodes = nextNodes;
|
||||
this.edges = nextEdges;
|
||||
this.nodes = nextNodes
|
||||
this.edges = nextEdges
|
||||
},
|
||||
updateNodeDimensions(updates) {
|
||||
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) {
|
||||
const dimensions = getDimensions(update.nodeElement);
|
||||
const dimensions = getDimensions(update.nodeElement)
|
||||
const doUpdate =
|
||||
dimensions.width &&
|
||||
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) {
|
||||
const handleBounds = getHandleBounds(update.nodeElement, this.transform[2]);
|
||||
const handleBounds = getHandleBounds(update.nodeElement, this.transform[2])
|
||||
|
||||
return {
|
||||
...node,
|
||||
@@ -92,23 +92,23 @@ export default function configureStore(
|
||||
...dimensions,
|
||||
handleBounds
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
});
|
||||
return node
|
||||
})
|
||||
},
|
||||
updateNodePos(payload) {
|
||||
const { id, pos } = payload;
|
||||
let position: XYPosition = pos;
|
||||
const { id, pos } = payload
|
||||
let position: XYPosition = pos
|
||||
|
||||
if (this.snapToGrid) {
|
||||
const [gridSizeX, gridSizeY] = this.snapGrid;
|
||||
const [gridSizeX, gridSizeY] = this.snapGrid
|
||||
position = {
|
||||
x: gridSizeX * Math.round(pos.x / gridSizeX),
|
||||
y: gridSizeY * Math.round(pos.y / gridSizeY)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
this.nodes = this.nodes.map((node) => {
|
||||
@@ -119,14 +119,14 @@ export default function configureStore(
|
||||
...node.__rf,
|
||||
position
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return node;
|
||||
});
|
||||
return node
|
||||
})
|
||||
},
|
||||
updateNodePosDiff(payload: NodeDiffUpdate) {
|
||||
const { id, diff, isDragging } = payload;
|
||||
const { id, diff, isDragging } = payload
|
||||
|
||||
this.nodes = this.nodes.map((node) => {
|
||||
if (id === node.id || this.selectedElements?.find((sNode) => sNode.id === node.id)) {
|
||||
@@ -136,23 +136,23 @@ export default function configureStore(
|
||||
...node.__rf,
|
||||
isDragging
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (diff) {
|
||||
updatedNode.__rf.position = {
|
||||
x: node.__rf.position.x + diff.x,
|
||||
y: node.__rf.position.y + diff.y
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return updatedNode;
|
||||
return updatedNode
|
||||
}
|
||||
|
||||
return node;
|
||||
});
|
||||
return node
|
||||
})
|
||||
},
|
||||
setUserSelection(mousePos) {
|
||||
this.selectionActive = true;
|
||||
this.selectionActive = true
|
||||
this.userSelectionRect = {
|
||||
width: 0,
|
||||
height: 0,
|
||||
@@ -161,11 +161,11 @@ export default function configureStore(
|
||||
x: mousePos.x,
|
||||
y: mousePos.y,
|
||||
draw: true
|
||||
};
|
||||
}
|
||||
},
|
||||
updateUserSelection(mousePos) {
|
||||
const startX = this.userSelectionRect.startX || 0;
|
||||
const startY = this.userSelectionRect.startY || 0;
|
||||
const startX = this.userSelectionRect.startX || 0
|
||||
const startY = this.userSelectionRect.startY || 0
|
||||
|
||||
const nextUserSelectRect: RevueFlowState['userSelectionRect'] = {
|
||||
...this.userSelectionRect,
|
||||
@@ -173,59 +173,59 @@ export default function configureStore(
|
||||
y: mousePos.y < startY ? mousePos.y : this.userSelectionRect.y,
|
||||
width: Math.abs(mousePos.x - startX),
|
||||
height: Math.abs(mousePos.y - startY)
|
||||
};
|
||||
}
|
||||
|
||||
const selectedNodes = getNodesInside(this.nodes, nextUserSelectRect, this.transform);
|
||||
const selectedEdges = getConnectedEdges(selectedNodes, this.edges);
|
||||
const selectedNodes = getNodesInside(this.nodes, nextUserSelectRect, this.transform)
|
||||
const selectedEdges = getConnectedEdges(selectedNodes, this.edges)
|
||||
|
||||
const nextSelectedElements = [...selectedNodes, ...selectedEdges];
|
||||
this.userSelectionRect = nextUserSelectRect;
|
||||
this.selectedElements = nextSelectedElements;
|
||||
const nextSelectedElements = [...selectedNodes, ...selectedEdges]
|
||||
this.userSelectionRect = nextUserSelectRect
|
||||
this.selectedElements = nextSelectedElements
|
||||
},
|
||||
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.userSelectionRect.draw = false;
|
||||
this.selectionActive = false
|
||||
this.userSelectionRect.draw = false
|
||||
|
||||
if (!selectedNodes || selectedNodes.length === 0) {
|
||||
this.selectedElements = null;
|
||||
this.nodesSelectionActive = false;
|
||||
this.selectedElements = null
|
||||
this.nodesSelectionActive = false
|
||||
} else {
|
||||
this.selectedNodesBbox = getRectOfNodes(selectedNodes);
|
||||
this.nodesSelectionActive = true;
|
||||
this.selectedNodesBbox = getRectOfNodes(selectedNodes)
|
||||
this.nodesSelectionActive = true
|
||||
}
|
||||
},
|
||||
addSelectedElements(elements) {
|
||||
const selectedElementsArr = Array.isArray(elements) ? elements : [elements];
|
||||
const selectedElementsUpdated = !isEqual(selectedElementsArr, this.selectedElements);
|
||||
this.selectedElements = selectedElementsUpdated ? selectedElementsArr : this.selectedElements;
|
||||
const selectedElementsArr = Array.isArray(elements) ? elements : [elements]
|
||||
const selectedElementsUpdated = !isEqual(selectedElementsArr, this.selectedElements)
|
||||
this.selectedElements = selectedElementsUpdated ? selectedElementsArr : this.selectedElements
|
||||
},
|
||||
initD3Zoom(payload) {
|
||||
const { d3Zoom, d3Selection, d3ZoomHandler, transform } = payload;
|
||||
const { d3Zoom, d3Selection, d3ZoomHandler, transform } = payload
|
||||
|
||||
this.d3Zoom = d3Zoom;
|
||||
this.d3Selection = d3Selection;
|
||||
this.d3ZoomHandler = d3ZoomHandler;
|
||||
this.transform = transform;
|
||||
this.d3Zoom = d3Zoom
|
||||
this.d3Selection = d3Selection
|
||||
this.d3ZoomHandler = d3ZoomHandler
|
||||
this.transform = transform
|
||||
},
|
||||
setMinZoom(minZoom) {
|
||||
this.d3Zoom?.scaleExtent([minZoom, this.maxZoom]);
|
||||
this.d3Zoom?.scaleExtent([minZoom, this.maxZoom])
|
||||
|
||||
this.minZoom = minZoom;
|
||||
this.minZoom = minZoom
|
||||
},
|
||||
setMaxZoom(maxZoom) {
|
||||
this.d3Zoom?.scaleExtent([this.minZoom, maxZoom]);
|
||||
this.d3Zoom?.scaleExtent([this.minZoom, maxZoom])
|
||||
|
||||
this.maxZoom = maxZoom;
|
||||
this.maxZoom = maxZoom
|
||||
},
|
||||
setTranslateExtent(translateExtent) {
|
||||
this.d3Zoom?.translateExtent(translateExtent);
|
||||
this.d3Zoom?.translateExtent(translateExtent)
|
||||
|
||||
this.translateExtent = translateExtent;
|
||||
this.translateExtent = translateExtent
|
||||
},
|
||||
setNodeExtent(nodeExtent) {
|
||||
this.nodeExtent = nodeExtent;
|
||||
this.nodeExtent = nodeExtent
|
||||
this.nodes = this.nodes.map((node) => {
|
||||
return {
|
||||
...node,
|
||||
@@ -233,29 +233,29 @@ export default function configureStore(
|
||||
...node.__rf,
|
||||
position: clampPosition(node.__rf.position, nodeExtent)
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
})
|
||||
},
|
||||
resetSelectedElements() {
|
||||
this.selectedElements = null;
|
||||
this.selectedElements = null
|
||||
},
|
||||
unsetNodesSelection() {
|
||||
this.nodesSelectionActive = false;
|
||||
this.nodesSelectionActive = false
|
||||
},
|
||||
updateSize(size) {
|
||||
this.height = size.height;
|
||||
this.width = size.width;
|
||||
this.height = size.height
|
||||
this.width = size.width
|
||||
},
|
||||
setConnectionNodeId(payload) {
|
||||
this.connectionNodeId = payload.connectionNodeId;
|
||||
this.connectionHandleId = payload.connectionHandleId;
|
||||
this.connectionHandleType = payload.connectionHandleType;
|
||||
this.connectionNodeId = payload.connectionNodeId
|
||||
this.connectionHandleId = payload.connectionHandleId
|
||||
this.connectionHandleType = payload.connectionHandleType
|
||||
},
|
||||
setInteractive(isInteractive) {
|
||||
this.nodesDraggable = isInteractive;
|
||||
this.nodesConnectable = isInteractive;
|
||||
this.elementsSelectable = isInteractive;
|
||||
this.nodesDraggable = isInteractive
|
||||
this.nodesConnectable = isInteractive
|
||||
this.elementsSelectable = isInteractive
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
import { RevueFlowState, ConnectionMode } from '../types';
|
||||
import { RevueFlowState, ConnectionMode } from '../types'
|
||||
|
||||
export const initialState: RevueFlowState = {
|
||||
width: 0,
|
||||
@@ -52,4 +52,4 @@ export const initialState: RevueFlowState = {
|
||||
multiSelectionActive: false,
|
||||
|
||||
revueFlowVersion: typeof __REVUE_FLOW_VERSION__ !== 'undefined' ? __REVUE_FLOW_VERSION__ : '-'
|
||||
};
|
||||
}
|
||||
|
||||
+287
-287
@@ -1,14 +1,14 @@
|
||||
import { Selection as D3Selection, ZoomBehavior } from 'd3';
|
||||
import { CSSProperties, DefineComponent, HTMLAttributes, VNode } from 'vue';
|
||||
import { Store } from 'pinia';
|
||||
import { Selection as D3Selection, ZoomBehavior } from 'd3'
|
||||
import { CSSProperties, DefineComponent, HTMLAttributes, VNode } from 'vue'
|
||||
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 {
|
||||
Left = 'left',
|
||||
@@ -18,44 +18,44 @@ export enum Position {
|
||||
}
|
||||
|
||||
export interface XYPosition {
|
||||
x: number;
|
||||
y: number;
|
||||
x: number
|
||||
y: number
|
||||
}
|
||||
|
||||
export interface Dimensions {
|
||||
width: number;
|
||||
height: number;
|
||||
width: number
|
||||
height: number
|
||||
}
|
||||
|
||||
export interface Rect extends Dimensions, XYPosition {}
|
||||
|
||||
export interface Box extends XYPosition {
|
||||
x2: number;
|
||||
y2: number;
|
||||
x2: number
|
||||
y2: number
|
||||
}
|
||||
|
||||
export type SnapGrid = [number, number];
|
||||
export type SnapGrid = [number, number]
|
||||
|
||||
export interface Node<T = any> {
|
||||
id: ElementId;
|
||||
position: XYPosition;
|
||||
type?: string;
|
||||
id: ElementId
|
||||
position: XYPosition
|
||||
type?: string
|
||||
__rf: {
|
||||
position: XYPosition;
|
||||
isDragging?: boolean;
|
||||
width: number | null;
|
||||
height: number | null;
|
||||
handleBounds?: any;
|
||||
};
|
||||
data?: T;
|
||||
style?: any;
|
||||
className?: string;
|
||||
targetPosition?: Position;
|
||||
sourcePosition?: Position;
|
||||
isHidden?: boolean;
|
||||
draggable?: boolean;
|
||||
selectable?: boolean;
|
||||
connectable?: boolean;
|
||||
position: XYPosition
|
||||
isDragging?: boolean
|
||||
width: number | null
|
||||
height: number | null
|
||||
handleBounds?: any
|
||||
}
|
||||
data?: T
|
||||
style?: any
|
||||
className?: string
|
||||
targetPosition?: Position
|
||||
sourcePosition?: Position
|
||||
isHidden?: boolean
|
||||
draggable?: boolean
|
||||
selectable?: boolean
|
||||
connectable?: boolean
|
||||
}
|
||||
|
||||
export enum ArrowHeadType {
|
||||
@@ -64,24 +64,24 @@ export enum ArrowHeadType {
|
||||
}
|
||||
|
||||
export interface Edge<T = any> {
|
||||
id: ElementId;
|
||||
type?: string;
|
||||
source: ElementId;
|
||||
target: ElementId;
|
||||
sourceHandle?: ElementId | null;
|
||||
targetHandle?: ElementId | null;
|
||||
label?: string | VNode;
|
||||
labelStyle?: any;
|
||||
labelShowBg?: boolean;
|
||||
labelBgStyle?: any;
|
||||
labelBgPadding?: [number, number];
|
||||
labelBgBorderRadius?: number;
|
||||
style?: any;
|
||||
animated?: boolean;
|
||||
arrowHeadType?: ArrowHeadType;
|
||||
isHidden?: boolean;
|
||||
data?: T;
|
||||
className?: string;
|
||||
id: ElementId
|
||||
type?: string
|
||||
source: ElementId
|
||||
target: ElementId
|
||||
sourceHandle?: ElementId | null
|
||||
targetHandle?: ElementId | null
|
||||
label?: string | VNode
|
||||
labelStyle?: any
|
||||
labelShowBg?: boolean
|
||||
labelBgStyle?: any
|
||||
labelBgPadding?: [number, number]
|
||||
labelBgBorderRadius?: number
|
||||
style?: any
|
||||
animated?: boolean
|
||||
arrowHeadType?: ArrowHeadType
|
||||
isHidden?: boolean
|
||||
data?: T
|
||||
className?: string
|
||||
}
|
||||
|
||||
export enum BackgroundVariant {
|
||||
@@ -89,107 +89,107 @@ export enum BackgroundVariant {
|
||||
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 {
|
||||
startX: number;
|
||||
startY: number;
|
||||
draw: boolean;
|
||||
startX: number
|
||||
startY: number
|
||||
draw: boolean
|
||||
}
|
||||
|
||||
export interface EdgeProps<T = any> {
|
||||
id: ElementId;
|
||||
source: ElementId;
|
||||
target: ElementId;
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
selected?: boolean;
|
||||
animated?: boolean;
|
||||
sourcePosition: Position;
|
||||
targetPosition: Position;
|
||||
label?: string | VNode;
|
||||
labelStyle?: any;
|
||||
labelShowBg?: boolean;
|
||||
labelBgStyle?: any;
|
||||
labelBgPadding?: [number, number];
|
||||
labelBgBorderRadius?: number;
|
||||
style?: any;
|
||||
arrowHeadType?: ArrowHeadType;
|
||||
markerEndId?: string;
|
||||
data?: T;
|
||||
sourceHandleId?: ElementId | null;
|
||||
targetHandleId?: ElementId | null;
|
||||
id: ElementId
|
||||
source: ElementId
|
||||
target: ElementId
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
targetX: number
|
||||
targetY: number
|
||||
selected?: boolean
|
||||
animated?: boolean
|
||||
sourcePosition: Position
|
||||
targetPosition: Position
|
||||
label?: string | VNode
|
||||
labelStyle?: any
|
||||
labelShowBg?: boolean
|
||||
labelBgStyle?: any
|
||||
labelBgPadding?: [number, number]
|
||||
labelBgBorderRadius?: number
|
||||
style?: any
|
||||
arrowHeadType?: ArrowHeadType
|
||||
markerEndId?: string
|
||||
data?: T
|
||||
sourceHandleId?: ElementId | null
|
||||
targetHandleId?: ElementId | null
|
||||
}
|
||||
|
||||
export interface EdgeSmoothStepProps<T = any> extends EdgeProps<T> {
|
||||
borderRadius?: number;
|
||||
borderRadius?: number
|
||||
}
|
||||
|
||||
export interface EdgeTextProps extends HTMLAttributes {
|
||||
x: number;
|
||||
y: number;
|
||||
label?: string | VNode;
|
||||
labelStyle?: any;
|
||||
labelShowBg?: boolean;
|
||||
labelBgStyle?: any;
|
||||
labelBgPadding?: [number, number];
|
||||
labelBgBorderRadius?: number;
|
||||
x: number
|
||||
y: number
|
||||
label?: string | VNode
|
||||
labelStyle?: any
|
||||
labelShowBg?: boolean
|
||||
labelBgStyle?: any
|
||||
labelBgPadding?: [number, number]
|
||||
labelBgBorderRadius?: number
|
||||
}
|
||||
|
||||
export interface NodeProps<T = any> {
|
||||
id: ElementId;
|
||||
type: string;
|
||||
data: T;
|
||||
selected: boolean;
|
||||
isConnectable: boolean;
|
||||
xPos?: number;
|
||||
yPos?: number;
|
||||
targetPosition?: Position;
|
||||
sourcePosition?: Position;
|
||||
isDragging?: boolean;
|
||||
id: ElementId
|
||||
type: string
|
||||
data: T
|
||||
selected: boolean
|
||||
isConnectable: boolean
|
||||
xPos?: number
|
||||
yPos?: number
|
||||
targetPosition?: Position
|
||||
sourcePosition?: Position
|
||||
isDragging?: boolean
|
||||
}
|
||||
|
||||
export type FitViewParams = {
|
||||
padding?: number;
|
||||
includeHiddenNodes?: boolean;
|
||||
minZoom?: number;
|
||||
maxZoom?: number;
|
||||
};
|
||||
padding?: number
|
||||
includeHiddenNodes?: boolean
|
||||
minZoom?: number
|
||||
maxZoom?: number
|
||||
}
|
||||
|
||||
export type FlowExportObject<T = any> = {
|
||||
elements: Elements<T>;
|
||||
position: [number, number];
|
||||
zoom: number;
|
||||
};
|
||||
elements: Elements<T>
|
||||
position: [number, number]
|
||||
zoom: number
|
||||
}
|
||||
|
||||
export type FitViewFunc = (fitViewOptions?: FitViewParams) => void;
|
||||
export type ProjectFunc = (position: XYPosition) => XYPosition;
|
||||
export type ToObjectFunc<T = any> = () => FlowExportObject<T>;
|
||||
export type FitViewFunc = (fitViewOptions?: FitViewParams) => void
|
||||
export type ProjectFunc = (position: XYPosition) => XYPosition
|
||||
export type ToObjectFunc<T = any> = () => FlowExportObject<T>
|
||||
|
||||
export type OnLoadParams<T = any> = {
|
||||
zoomIn: () => void;
|
||||
zoomOut: () => void;
|
||||
zoomTo: (zoomLevel: number) => void;
|
||||
fitView: FitViewFunc;
|
||||
project: ProjectFunc;
|
||||
getElements: () => Elements<T>;
|
||||
setTransform: (transform: FlowTransform) => void;
|
||||
toObject: ToObjectFunc<T>;
|
||||
};
|
||||
zoomIn: () => void
|
||||
zoomOut: () => void
|
||||
zoomTo: (zoomLevel: number) => void
|
||||
fitView: FitViewFunc
|
||||
project: ProjectFunc
|
||||
getElements: () => Elements<T>
|
||||
setTransform: (transform: FlowTransform) => void
|
||||
toObject: ToObjectFunc<T>
|
||||
}
|
||||
|
||||
export type OnLoadFunc<T = any> = (params: OnLoadParams<T>) => void;
|
||||
export type OnLoadFunc<T = any> = (params: OnLoadParams<T>) => void
|
||||
|
||||
export interface Connection {
|
||||
source: ElementId | null;
|
||||
target: ElementId | null;
|
||||
sourceHandle: ElementId | null;
|
||||
targetHandle: ElementId | null;
|
||||
source: ElementId | null
|
||||
target: ElementId | null
|
||||
sourceHandle: ElementId | null
|
||||
targetHandle: ElementId | null
|
||||
}
|
||||
|
||||
export enum ConnectionMode {
|
||||
@@ -205,69 +205,69 @@ export enum ConnectionLineType {
|
||||
}
|
||||
|
||||
export type ConnectionLineComponentProps = {
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
sourcePosition?: Position;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
targetPosition?: Position;
|
||||
connectionLineStyle?: any;
|
||||
connectionLineType: ConnectionLineType;
|
||||
};
|
||||
sourceX: number
|
||||
sourceY: number
|
||||
sourcePosition?: Position
|
||||
targetX: number
|
||||
targetY: number
|
||||
targetPosition?: Position
|
||||
connectionLineStyle?: any
|
||||
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 = {
|
||||
nodeId: ElementId | null;
|
||||
handleId: ElementId | null;
|
||||
handleType: HandleType | null;
|
||||
};
|
||||
export type OnConnectStartFunc = (event: MouseEvent, params: OnConnectStartParams) => void;
|
||||
export type OnConnectStopFunc = (event: MouseEvent) => void;
|
||||
export type OnConnectEndFunc = (event: MouseEvent) => void;
|
||||
nodeId: ElementId | null
|
||||
handleId: ElementId | null
|
||||
handleType: HandleType | null
|
||||
}
|
||||
export type OnConnectStartFunc = (event: MouseEvent, params: OnConnectStartParams) => void
|
||||
export type OnConnectStopFunc = (event: MouseEvent) => void
|
||||
export type OnConnectEndFunc = (event: MouseEvent) => void
|
||||
|
||||
export type SetConnectionId = {
|
||||
connectionNodeId: ElementId | null;
|
||||
connectionHandleId: ElementId | null;
|
||||
connectionHandleType: HandleType | null;
|
||||
};
|
||||
connectionNodeId: ElementId | null
|
||||
connectionHandleId: ElementId | null
|
||||
connectionHandleType: HandleType | null
|
||||
}
|
||||
|
||||
export interface HandleElement extends XYPosition, Dimensions {
|
||||
id?: ElementId | null;
|
||||
position: Position;
|
||||
id?: ElementId | null
|
||||
position: Position
|
||||
}
|
||||
|
||||
export interface HandleProps {
|
||||
type: HandleType;
|
||||
position: Position;
|
||||
isConnectable?: boolean;
|
||||
onConnect?: OnConnectFunc;
|
||||
isValidConnection?: (connection: Connection) => boolean;
|
||||
id?: ElementId;
|
||||
type: HandleType
|
||||
position: Position
|
||||
isConnectable?: boolean
|
||||
onConnect?: OnConnectFunc
|
||||
isValidConnection?: (connection: Connection) => boolean
|
||||
id?: ElementId
|
||||
}
|
||||
|
||||
export type NodePosUpdate = {
|
||||
id: ElementId;
|
||||
pos: XYPosition;
|
||||
};
|
||||
id: ElementId
|
||||
pos: XYPosition
|
||||
}
|
||||
|
||||
export type NodeDiffUpdate = {
|
||||
id?: ElementId;
|
||||
diff?: XYPosition;
|
||||
isDragging?: boolean;
|
||||
};
|
||||
id?: ElementId
|
||||
diff?: XYPosition
|
||||
isDragging?: boolean
|
||||
}
|
||||
|
||||
export type FlowTransform = {
|
||||
x: number;
|
||||
y: number;
|
||||
zoom: number;
|
||||
};
|
||||
x: number
|
||||
y: number
|
||||
zoom: number
|
||||
}
|
||||
|
||||
export type TranslateExtent = [[number, number], [number, number]];
|
||||
export type NodeExtent = TranslateExtent;
|
||||
export type TranslateExtent = [[number, number], [number, number]]
|
||||
export type NodeExtent = TranslateExtent
|
||||
|
||||
export type KeyCode = number | string;
|
||||
export type KeyCode = number | string
|
||||
|
||||
export enum PanOnScrollMode {
|
||||
Free = 'free',
|
||||
@@ -276,150 +276,150 @@ export enum PanOnScrollMode {
|
||||
}
|
||||
|
||||
export interface ZoomPanHelperFunctions {
|
||||
zoomIn: () => void;
|
||||
zoomOut: () => void;
|
||||
zoomTo: (zoomLevel: number) => void;
|
||||
transform: (transform: FlowTransform) => void;
|
||||
fitView: FitViewFunc;
|
||||
setCenter: (x: number, y: number, zoom?: number) => void;
|
||||
fitBounds: (bounds: Rect, padding?: number) => void;
|
||||
project: (position: XYPosition) => XYPosition;
|
||||
zoomIn: () => void
|
||||
zoomOut: () => void
|
||||
zoomTo: (zoomLevel: number) => void
|
||||
transform: (transform: FlowTransform) => void
|
||||
fitView: FitViewFunc
|
||||
setCenter: (x: number, y: number, zoom?: number) => void
|
||||
fitBounds: (bounds: Rect, padding?: number) => void
|
||||
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 = {
|
||||
id: ElementId;
|
||||
nodeElement: HTMLDivElement;
|
||||
forceUpdate?: boolean;
|
||||
};
|
||||
id: ElementId
|
||||
nodeElement: HTMLDivElement
|
||||
forceUpdate?: boolean
|
||||
}
|
||||
|
||||
export type InitD3ZoomPayload = {
|
||||
d3Zoom: ZoomBehavior<Element, unknown>;
|
||||
d3Selection: D3Selection<Element, unknown, null, undefined>;
|
||||
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
|
||||
transform: Transform;
|
||||
};
|
||||
d3Zoom: ZoomBehavior<Element, unknown>
|
||||
d3Selection: D3Selection<Element, unknown, null, undefined>
|
||||
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined
|
||||
transform: Transform
|
||||
}
|
||||
|
||||
export interface RevueFlowState {
|
||||
width: number;
|
||||
height: number;
|
||||
transform: Transform;
|
||||
nodes: Node[];
|
||||
edges: Edge[];
|
||||
selectedElements: Elements | null;
|
||||
selectedNodesBbox: Rect;
|
||||
width: number
|
||||
height: number
|
||||
transform: Transform
|
||||
nodes: Node[]
|
||||
edges: Edge[]
|
||||
selectedElements: Elements | null
|
||||
selectedNodesBbox: Rect
|
||||
|
||||
d3Zoom: ZoomBehavior<Element, unknown> | null;
|
||||
d3Selection: D3Selection<Element, unknown, null, undefined> | null;
|
||||
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined;
|
||||
minZoom: number;
|
||||
maxZoom: number;
|
||||
translateExtent: TranslateExtent;
|
||||
nodeExtent: NodeExtent;
|
||||
d3Zoom: ZoomBehavior<Element, unknown> | null
|
||||
d3Selection: D3Selection<Element, unknown, null, undefined> | null
|
||||
d3ZoomHandler: ((this: Element, event: any, d: unknown) => void) | undefined
|
||||
minZoom: number
|
||||
maxZoom: number
|
||||
translateExtent: TranslateExtent
|
||||
nodeExtent: NodeExtent
|
||||
|
||||
nodesSelectionActive: boolean;
|
||||
selectionActive: boolean;
|
||||
nodesSelectionActive: boolean
|
||||
selectionActive: boolean
|
||||
|
||||
userSelectionRect: SelectionRect;
|
||||
userSelectionRect: SelectionRect
|
||||
|
||||
connectionNodeId: ElementId | null;
|
||||
connectionHandleId: ElementId | null;
|
||||
connectionHandleType: HandleType | null;
|
||||
connectionPosition: XYPosition;
|
||||
connectionMode: ConnectionMode;
|
||||
connectionNodeId: ElementId | null
|
||||
connectionHandleId: ElementId | null
|
||||
connectionHandleType: HandleType | null
|
||||
connectionPosition: XYPosition
|
||||
connectionMode: ConnectionMode
|
||||
|
||||
snapToGrid: boolean;
|
||||
snapGrid: SnapGrid;
|
||||
snapToGrid: boolean
|
||||
snapGrid: SnapGrid
|
||||
|
||||
nodesDraggable: boolean;
|
||||
nodesConnectable: boolean;
|
||||
elementsSelectable: boolean;
|
||||
nodesDraggable: boolean
|
||||
nodesConnectable: boolean
|
||||
elementsSelectable: boolean
|
||||
|
||||
multiSelectionActive: boolean;
|
||||
multiSelectionActive: boolean
|
||||
|
||||
revueFlowVersion: string;
|
||||
revueFlowVersion: string
|
||||
|
||||
onConnect?: OnConnectFunc;
|
||||
onConnectStart?: OnConnectStartFunc;
|
||||
onConnectStop?: OnConnectStopFunc;
|
||||
onConnectEnd?: OnConnectEndFunc;
|
||||
onConnect?: OnConnectFunc
|
||||
onConnectStart?: OnConnectStartFunc
|
||||
onConnectStop?: OnConnectStopFunc
|
||||
onConnectEnd?: OnConnectEndFunc
|
||||
}
|
||||
|
||||
export type RevueFlowActions = {
|
||||
setElements: (elements: Elements) => void;
|
||||
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void;
|
||||
updateNodePos: (payload: NodePosUpdate) => void;
|
||||
updateNodePosDiff: (payload: NodeDiffUpdate) => void;
|
||||
setUserSelection: (mousePos: XYPosition) => void;
|
||||
updateUserSelection: (mousePos: XYPosition) => void;
|
||||
unsetUserSelection: () => void;
|
||||
addSelectedElements: (elements: Elements) => void;
|
||||
initD3Zoom: (payload: InitD3ZoomPayload) => void;
|
||||
setMinZoom: (zoom: number) => void;
|
||||
setMaxZoom: (zoom: number) => void;
|
||||
setTranslateExtent: (translateExtent: TranslateExtent) => void;
|
||||
setNodeExtent: (nodeExtent: NodeExtent) => void;
|
||||
resetSelectedElements: () => void;
|
||||
unsetNodesSelection: () => void;
|
||||
updateSize: (size: Dimensions) => void;
|
||||
setConnectionNodeId: (payload: SetConnectionId) => void;
|
||||
setInteractive: (isInteractive: boolean) => void;
|
||||
};
|
||||
setElements: (elements: Elements) => void
|
||||
updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void
|
||||
updateNodePos: (payload: NodePosUpdate) => void
|
||||
updateNodePosDiff: (payload: NodeDiffUpdate) => void
|
||||
setUserSelection: (mousePos: XYPosition) => void
|
||||
updateUserSelection: (mousePos: XYPosition) => void
|
||||
unsetUserSelection: () => void
|
||||
addSelectedElements: (elements: Elements) => void
|
||||
initD3Zoom: (payload: InitD3ZoomPayload) => void
|
||||
setMinZoom: (zoom: number) => void
|
||||
setMaxZoom: (zoom: number) => void
|
||||
setTranslateExtent: (translateExtent: TranslateExtent) => void
|
||||
setNodeExtent: (nodeExtent: NodeExtent) => void
|
||||
resetSelectedElements: () => void
|
||||
unsetNodesSelection: () => void
|
||||
updateSize: (size: Dimensions) => void
|
||||
setConnectionNodeId: (payload: SetConnectionId) => 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'> {
|
||||
modelValue: Elements;
|
||||
nodeTypes?: Record<string, NodeType>;
|
||||
edgeTypes?: Record<string, EdgeType>;
|
||||
connectionMode?: ConnectionMode;
|
||||
connectionLineType?: ConnectionLineType;
|
||||
connectionLineStyle?: CSSProperties;
|
||||
connectionLineComponent?: ConnectionLineComponent;
|
||||
deleteKeyCode?: KeyCode;
|
||||
selectionKeyCode?: KeyCode;
|
||||
multiSelectionKeyCode?: KeyCode;
|
||||
zoomActivationKeyCode?: KeyCode;
|
||||
snapToGrid?: boolean;
|
||||
snapGrid?: [number, number];
|
||||
onlyRenderVisibleElements?: boolean;
|
||||
nodesDraggable?: boolean;
|
||||
nodesConnectable?: boolean;
|
||||
elementsSelectable?: boolean;
|
||||
selectNodesOnDrag?: boolean;
|
||||
paneMoveable?: boolean;
|
||||
minZoom?: number;
|
||||
maxZoom?: number;
|
||||
defaultZoom?: number;
|
||||
defaultPosition?: [number, number];
|
||||
translateExtent?: TranslateExtent;
|
||||
nodeExtent?: NodeExtent;
|
||||
arrowHeadColor?: string;
|
||||
markerEndId?: string;
|
||||
zoomOnScroll?: boolean;
|
||||
zoomOnPinch?: boolean;
|
||||
panOnScroll?: boolean;
|
||||
panOnScrollSpeed?: number;
|
||||
panOnScrollMode?: PanOnScrollMode;
|
||||
zoomOnDoubleClick?: boolean;
|
||||
edgeUpdaterRadius?: number;
|
||||
nodeTypesId?: string;
|
||||
edgeTypesId?: string;
|
||||
modelValue: Elements
|
||||
nodeTypes?: Record<string, NodeType>
|
||||
edgeTypes?: Record<string, EdgeType>
|
||||
connectionMode?: ConnectionMode
|
||||
connectionLineType?: ConnectionLineType
|
||||
connectionLineStyle?: CSSProperties
|
||||
connectionLineComponent?: ConnectionLineComponent
|
||||
deleteKeyCode?: KeyCode
|
||||
selectionKeyCode?: KeyCode
|
||||
multiSelectionKeyCode?: KeyCode
|
||||
zoomActivationKeyCode?: KeyCode
|
||||
snapToGrid?: boolean
|
||||
snapGrid?: [number, number]
|
||||
onlyRenderVisibleElements?: boolean
|
||||
nodesDraggable?: boolean
|
||||
nodesConnectable?: boolean
|
||||
elementsSelectable?: boolean
|
||||
selectNodesOnDrag?: boolean
|
||||
paneMoveable?: boolean
|
||||
minZoom?: number
|
||||
maxZoom?: number
|
||||
defaultZoom?: number
|
||||
defaultPosition?: [number, number]
|
||||
translateExtent?: TranslateExtent
|
||||
nodeExtent?: NodeExtent
|
||||
arrowHeadColor?: string
|
||||
markerEndId?: string
|
||||
zoomOnScroll?: boolean
|
||||
zoomOnPinch?: boolean
|
||||
panOnScroll?: boolean
|
||||
panOnScrollSpeed?: number
|
||||
panOnScrollMode?: PanOnScrollMode
|
||||
zoomOnDoubleClick?: boolean
|
||||
edgeUpdaterRadius?: number
|
||||
nodeTypesId?: string
|
||||
edgeTypesId?: string
|
||||
}
|
||||
|
||||
export interface GraphViewProps extends Required<RevueFlowProps> {
|
||||
selectionKeyCode: KeyCode;
|
||||
deleteKeyCode: KeyCode;
|
||||
multiSelectionKeyCode: KeyCode;
|
||||
connectionLineType: ConnectionLineType;
|
||||
snapToGrid: boolean;
|
||||
snapGrid: [number, number];
|
||||
onlyRenderVisibleElements: boolean;
|
||||
defaultZoom: number;
|
||||
defaultPosition: [number, number];
|
||||
arrowHeadColor: string;
|
||||
selectNodesOnDrag: boolean;
|
||||
selectionKeyCode: KeyCode
|
||||
deleteKeyCode: KeyCode
|
||||
multiSelectionKeyCode: KeyCode
|
||||
connectionLineType: ConnectionLineType
|
||||
snapToGrid: boolean
|
||||
snapGrid: [number, number]
|
||||
onlyRenderVisibleElements: boolean
|
||||
defaultZoom: number
|
||||
defaultPosition: [number, number]
|
||||
arrowHeadColor: string
|
||||
selectNodesOnDrag: boolean
|
||||
}
|
||||
|
||||
+91
-91
@@ -1,4 +1,4 @@
|
||||
import { clampPosition, clamp } from './index';
|
||||
import { clampPosition, clamp } from './index'
|
||||
|
||||
import {
|
||||
ElementId,
|
||||
@@ -13,47 +13,47 @@ import {
|
||||
FlowExportObject,
|
||||
NodeExtent,
|
||||
RevueFlowStore
|
||||
} from '../types';
|
||||
} from '../types'
|
||||
|
||||
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 =>
|
||||
'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[] => {
|
||||
if (!isNode(node)) {
|
||||
return [];
|
||||
return []
|
||||
}
|
||||
|
||||
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[];
|
||||
};
|
||||
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[]
|
||||
}
|
||||
|
||||
export const getIncomers = (node: Node, elements: Elements): Node[] => {
|
||||
if (!isNode(node)) {
|
||||
return [];
|
||||
return []
|
||||
}
|
||||
|
||||
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[];
|
||||
};
|
||||
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[]
|
||||
}
|
||||
|
||||
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) => {
|
||||
const edgeElement = element as Edge;
|
||||
const edgeElement = element as Edge
|
||||
return !(
|
||||
nodeIdsToRemove.includes(element.id) ||
|
||||
nodeIdsToRemove.includes(edgeElement.target) ||
|
||||
nodeIdsToRemove.includes(edgeElement.source)
|
||||
);
|
||||
});
|
||||
};
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
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) => {
|
||||
return elements.some(
|
||||
@@ -63,43 +63,43 @@ const connectionExists = (edge: Edge, elements: Elements) => {
|
||||
el.target === edge.target &&
|
||||
(el.sourceHandle === edge.sourceHandle || (!el.sourceHandle && !edge.sourceHandle)) &&
|
||||
(el.targetHandle === edge.targetHandle || (!el.targetHandle && !edge.targetHandle))
|
||||
);
|
||||
};
|
||||
)
|
||||
}
|
||||
|
||||
export const addEdge = (edgeParams: Edge | Connection, elements: Elements): Elements => {
|
||||
if (!edgeParams.source || !edgeParams.target) {
|
||||
console.log("Can't create edge. An edge needs a source and a target.");
|
||||
return elements;
|
||||
console.log("Can't create edge. An edge needs a source and a target.")
|
||||
return elements
|
||||
}
|
||||
|
||||
let edge: Edge;
|
||||
let edge: Edge
|
||||
if (isEdge(edgeParams)) {
|
||||
edge = { ...edgeParams };
|
||||
edge = { ...edgeParams }
|
||||
} else {
|
||||
edge = {
|
||||
...edgeParams,
|
||||
id: getEdgeId(edgeParams)
|
||||
} as Edge;
|
||||
} as Edge
|
||||
}
|
||||
|
||||
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 => {
|
||||
if (!newConnection.source || !newConnection.target) {
|
||||
console.warn("Can't create new edge. An edge needs a source and a target.");
|
||||
return elements;
|
||||
console.warn("Can't create new edge. An edge needs a source and a target.")
|
||||
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) {
|
||||
console.warn(`The old edge with id=${oldEdge.id} does not exist.`);
|
||||
return elements;
|
||||
console.warn(`The old edge with id=${oldEdge.id} does not exist.`)
|
||||
return elements
|
||||
}
|
||||
|
||||
// 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,
|
||||
sourceHandle: newConnection.sourceHandle,
|
||||
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 = (
|
||||
{ x, y }: XYPosition,
|
||||
@@ -124,23 +124,23 @@ export const pointToRendererPoint = (
|
||||
const position: XYPosition = {
|
||||
x: (x - tx) / tScale,
|
||||
y: (y - ty) / tScale
|
||||
};
|
||||
}
|
||||
|
||||
if (snapToGrid) {
|
||||
return {
|
||||
x: snapX * Math.round(position.x / snapX),
|
||||
y: snapY * Math.round(position.y / snapY)
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return position;
|
||||
};
|
||||
return position
|
||||
}
|
||||
|
||||
export const onLoadProject = (currentStore: RevueFlowStore) => {
|
||||
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 => {
|
||||
return {
|
||||
@@ -154,8 +154,8 @@ export const parseNode = (node: Node, nodeExtent: NodeExtent): Node => {
|
||||
handleBounds: {},
|
||||
isDragging: false
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const parseEdge = (edge: Edge): Edge => {
|
||||
return {
|
||||
@@ -166,32 +166,32 @@ export const parseEdge = (edge: Edge): Edge => {
|
||||
targetHandle: edge.targetHandle ? edge.targetHandle.toString() : null,
|
||||
id: edge.id.toString(),
|
||||
type: edge.type || 'default'
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const getBoundsOfBoxes = (box1: Box, box2: Box): Box => ({
|
||||
x: Math.min(box1.x, box2.x),
|
||||
y: Math.min(box1.y, box2.y),
|
||||
x2: Math.max(box1.x2, box2.x2),
|
||||
y2: Math.max(box1.y2, box2.y2)
|
||||
});
|
||||
})
|
||||
|
||||
export const rectToBox = ({ x, y, width, height }: Rect): Box => ({
|
||||
x,
|
||||
y,
|
||||
x2: x + width,
|
||||
y2: y + height
|
||||
});
|
||||
})
|
||||
|
||||
export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
|
||||
x,
|
||||
y,
|
||||
width: x2 - x,
|
||||
height: y2 - y
|
||||
});
|
||||
})
|
||||
|
||||
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 => {
|
||||
const box = nodes.reduce(
|
||||
@@ -205,15 +205,15 @@ export const getRectOfNodes = (nodes: Node[]): Rect => {
|
||||
} as any)
|
||||
),
|
||||
{ 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 => ({
|
||||
x: x * tScale + tx,
|
||||
y: y * tScale + ty
|
||||
});
|
||||
})
|
||||
|
||||
export const getNodesInside = (nodes: Node[], rect: Rect, [tx, ty, tScale]: Transform = [0, 0, 1], partially = false): Node[] => {
|
||||
const rBox = rectToBox({
|
||||
@@ -221,54 +221,54 @@ export const getNodesInside = (nodes: Node[], rect: Rect, [tx, ty, tScale]: Tran
|
||||
y: (rect.y - ty) / tScale,
|
||||
width: rect.width / tScale,
|
||||
height: rect.height / tScale
|
||||
});
|
||||
})
|
||||
|
||||
return nodes.filter(({ __rf: { position, width, height, isDragging } }) => {
|
||||
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 yOverlap = Math.max(0, Math.min(rBox.y2, nBox.y2) - Math.max(rBox.y, nBox.y));
|
||||
const overlappingArea = Math.ceil(xOverlap * yOverlap);
|
||||
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 yOverlap = Math.max(0, Math.min(rBox.y2, nBox.y2) - Math.max(rBox.y, nBox.y))
|
||||
const overlappingArea = Math.ceil(xOverlap * yOverlap)
|
||||
|
||||
if (width === null || height === null || isDragging) {
|
||||
// nodes are initialized with width and height = null
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
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[] => {
|
||||
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 => {
|
||||
return [
|
||||
...nodes.map((node) => {
|
||||
const n = { ...node };
|
||||
const n = { ...node }
|
||||
|
||||
n.position = n.__rf.position;
|
||||
n.position = n.__rf.position
|
||||
|
||||
n.__rf = {} as any;
|
||||
return n;
|
||||
n.__rf = {} as any
|
||||
return n
|
||||
}),
|
||||
...edges.map((e) => ({ ...e }))
|
||||
];
|
||||
};
|
||||
]
|
||||
}
|
||||
|
||||
export const onLoadGetElements = (currentStore: RevueFlowStore) => {
|
||||
return (): Elements => {
|
||||
return parseElements(currentStore.nodes || [], currentStore.edges || []);
|
||||
};
|
||||
};
|
||||
return parseElements(currentStore.nodes || [], currentStore.edges || [])
|
||||
}
|
||||
}
|
||||
|
||||
export const onLoadToObject = (currentStore: RevueFlowStore) => {
|
||||
return (): FlowExportObject => {
|
||||
@@ -276,9 +276,9 @@ export const onLoadToObject = (currentStore: RevueFlowStore) => {
|
||||
elements: parseElements(currentStore.nodes || [], currentStore.edges || []),
|
||||
position: [currentStore.transform[0], currentStore.transform[1]],
|
||||
zoom: currentStore.transform[2]
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const getTransformForBounds = (
|
||||
bounds: Rect,
|
||||
@@ -288,14 +288,14 @@ export const getTransformForBounds = (
|
||||
maxZoom: number,
|
||||
padding = 0.1
|
||||
): Transform => {
|
||||
const xZoom = width / (bounds.width * (1 + padding));
|
||||
const yZoom = height / (bounds.height * (1 + padding));
|
||||
const zoom = Math.min(xZoom, yZoom);
|
||||
const clampedZoom = clamp(zoom, minZoom, maxZoom);
|
||||
const boundsCenterX = bounds.x + bounds.width / 2;
|
||||
const boundsCenterY = bounds.y + bounds.height / 2;
|
||||
const x = width / 2 - boundsCenterX * clampedZoom;
|
||||
const y = height / 2 - boundsCenterY * clampedZoom;
|
||||
const xZoom = width / (bounds.width * (1 + padding))
|
||||
const yZoom = height / (bounds.height * (1 + padding))
|
||||
const zoom = Math.min(xZoom, yZoom)
|
||||
const clampedZoom = clamp(zoom, minZoom, maxZoom)
|
||||
const boundsCenterX = bounds.x + bounds.width / 2
|
||||
const boundsCenterY = bounds.y + bounds.height / 2
|
||||
const x = width / 2 - boundsCenterX * 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 => {
|
||||
const target = e.target as HTMLElement;
|
||||
return ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName) || target.hasAttribute('contentEditable');
|
||||
};
|
||||
const target = e.target as HTMLElement
|
||||
return ['INPUT', 'SELECT', 'TEXTAREA', 'BUTTON'].includes(target.nodeName) || target.hasAttribute('contentEditable')
|
||||
}
|
||||
|
||||
export const getDimensions = (node: HTMLDivElement): Dimensions => ({
|
||||
width: node.offsetWidth,
|
||||
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 => ({
|
||||
x: clamp(position.x, extent[0][0], extent[1][0]),
|
||||
y: clamp(position.y, extent[0][1], extent[1][1])
|
||||
});
|
||||
})
|
||||
|
||||
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 { RevueFlowStore } from '../types';
|
||||
import { RevueFlowHooks } from '../hooks/RevueFlowHooks';
|
||||
import { InjectionKey } from 'vue'
|
||||
import { RevueFlowStore } from '../types'
|
||||
import { RevueFlowHooks } from '../hooks/RevueFlowHooks'
|
||||
|
||||
export const StoreKey: InjectionKey<RevueFlowStore> = Symbol('store');
|
||||
export const HooksKey: InjectionKey<RevueFlowHooks> = Symbol('hooks');
|
||||
export const NodeIdContextKey: InjectionKey<string> = Symbol('NodeIdContext');
|
||||
export const StoreKey: InjectionKey<RevueFlowStore> = Symbol('store')
|
||||
export const HooksKey: InjectionKey<RevueFlowHooks> = Symbol('hooks')
|
||||
export const NodeIdContextKey: InjectionKey<string> = Symbol('NodeIdContext')
|
||||
|
||||
+35
-21
@@ -1,28 +1,42 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"baseUrl": ".",
|
||||
"module": "ESNext",
|
||||
"target": "es2016",
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ESNext"
|
||||
],
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"jsxFactory": "",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"lib": ["es6", "dom", "es2016", "es2017", "es2020"],
|
||||
"types": ["@types/node"],
|
||||
"noImplicitAny": true,
|
||||
"strictNullChecks": true,
|
||||
"strictFunctionTypes": true,
|
||||
"strictPropertyInitialization": true,
|
||||
"noImplicitThis": true,
|
||||
"alwaysStrict": true,
|
||||
"incremental": false,
|
||||
"skipLibCheck": true,
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": false,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strictNullChecks": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"jsx": "preserve",
|
||||
"types": [
|
||||
"vite/client",
|
||||
"@types/node"
|
||||
],
|
||||
"paths": {
|
||||
"~/*": [
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||||
"exclude": ["node_modules", "build", "dist", "example"]
|
||||
"include": [
|
||||
"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 vue from '@vitejs/plugin-vue';
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx';
|
||||
import svgLoader from 'vite-svg-loader';
|
||||
import { defineConfig } from 'vite'
|
||||
import { resolve } from 'path'
|
||||
import vue from '@vitejs/plugin-vue'
|
||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
||||
import svgLoader from 'vite-svg-loader'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
resolve: {
|
||||
alias: {
|
||||
'~/': `${resolve(__dirname, 'src')}/`
|
||||
}
|
||||
},
|
||||
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