refactor: back to jsx 🥇
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import { ConnectionLineComponentProps } from '../../src';
|
||||
|
||||
const ConnectionLine = defineComponent({
|
||||
export default defineComponent({
|
||||
props: {
|
||||
sourceX: {
|
||||
type: Number as PropType<ConnectionLineComponentProps['sourceX']>,
|
||||
@@ -34,6 +34,4 @@ const ConnectionLine = defineComponent({
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default ConnectionLine;
|
||||
</script>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<RevueFlow
|
||||
v-model="elements"
|
||||
:connectionLineComponent="ConnectionLine"
|
||||
:onElementsRemove="onElementsRemove"
|
||||
:onConnect="onConnect"
|
||||
:connection-line-component="ConnectionLine"
|
||||
@elementsRemove="onElementsRemove"
|
||||
@connect="onConnect"
|
||||
>
|
||||
<Background :variant="bgVariant" />
|
||||
</RevueFlow>
|
||||
@@ -14,10 +14,17 @@ import RevueFlow, { removeElements, addEdge, Background, BackgroundVariant, Elem
|
||||
import ConnectionLine from './ConnectionLine.vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
|
||||
const ConnectionLineFlow = defineComponent({
|
||||
export default defineComponent({
|
||||
components: { RevueFlow, Background },
|
||||
setup() {
|
||||
const elements = ref<Elements>([{ id: '1', type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 } }]);
|
||||
const elements = ref<Elements>([
|
||||
{
|
||||
id: '1',
|
||||
type: 'input',
|
||||
data: { label: 'Node 1' },
|
||||
position: { x: 250, y: 5 }
|
||||
}
|
||||
] as Elements);
|
||||
const onElementsRemove = (elementsToRemove: Elements) =>
|
||||
(elements.value = removeElements(elementsToRemove, elements.value as Elements));
|
||||
const onConnect = (params: Connection | Edge) => (elements.value = addEdge(params, elements.value as Elements));
|
||||
@@ -31,6 +38,4 @@ const ConnectionLineFlow = defineComponent({
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
export default ConnectionLineFlow;
|
||||
</script>
|
||||
|
||||
@@ -116,7 +116,7 @@ const CustomNodeFlow = defineComponent({
|
||||
{ id: 'e1-2', source: '1', target: '2', animated: true, style: { stroke: '#fff' } },
|
||||
{ id: 'e2a-3', source: '2', sourceHandle: 'a', target: '3', animated: true, style: { stroke: '#fff' } },
|
||||
{ id: 'e2b-4', source: '2', sourceHandle: 'b', target: '4', animated: true, style: { stroke: '#fff' } }
|
||||
];
|
||||
] as Elements;
|
||||
});
|
||||
|
||||
const onElementsRemove = (elementsToRemove: Elements) =>
|
||||
|
||||
@@ -1,14 +1,7 @@
|
||||
<template>
|
||||
<div class="dndflow">
|
||||
<div class="revueflow-wrapper" @drop="onDrop">
|
||||
<RevueFlow
|
||||
v-model="elements"
|
||||
:onConnect="onConnect"
|
||||
:onElementsRemove="onElementsRemove"
|
||||
:onLoad="onLoad"
|
||||
@dragover="onDragOver"
|
||||
@click="onDrop"
|
||||
>
|
||||
<RevueFlow v-model="elements" @elementsRemove="onElementsRemove" @load="onLoad" @connect="onConnect" @dragover="onDragOver">
|
||||
<Controls />
|
||||
</RevueFlow>
|
||||
</div>
|
||||
@@ -43,7 +36,7 @@ const DnDFlow = defineComponent({
|
||||
data: { label: 'input node' },
|
||||
position: { x: 250, y: 5 }
|
||||
}
|
||||
]);
|
||||
] as Elements);
|
||||
|
||||
let id = 0;
|
||||
const getId = (): ElementId => `dndnode_${id++}`;
|
||||
@@ -67,12 +60,12 @@ const DnDFlow = defineComponent({
|
||||
console.log(event.dataTransfer?.getData('application/revueflow'));
|
||||
const type = event.dataTransfer?.getData('application/revueflow');
|
||||
const position = revueFlowInstance.value.project({ x: event.clientX, y: event.clientY - 40 });
|
||||
const newNode: Node = {
|
||||
const newNode = {
|
||||
id: getId(),
|
||||
type,
|
||||
position,
|
||||
data: { label: `${type} node` }
|
||||
};
|
||||
} as Node;
|
||||
|
||||
elements.value.push(newNode);
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"lint": "yarn lint:js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@braks/revue-draggable": "^0.2.4",
|
||||
"@braks/revue-draggable": "^0.2.5",
|
||||
"@types/d3": "^7.0.0",
|
||||
"@vueuse/core": "^5.1.4",
|
||||
"d3": "^7.0.0",
|
||||
|
||||
@@ -90,7 +90,7 @@ const Controls = defineComponent({
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const isVisible = ref<boolean>(false);
|
||||
const zoomHelper = ref<ZoomPanHelperFunctions>();
|
||||
const { onReady } = useZoomPanHelper(store);
|
||||
const { onReady } = useZoomPanHelper();
|
||||
|
||||
onReady((helper) => {
|
||||
zoomHelper.value = helper;
|
||||
|
||||
@@ -1,23 +1,4 @@
|
||||
<template>
|
||||
<g v-show="sourceNode && nodesConnectable" class="revue-flow__connection">
|
||||
<g v-if="CustomConnectionLineComponent" class="revue-flow__connection">
|
||||
<component
|
||||
:is="CustomConnectionLineComponent"
|
||||
:sourceX="sourceX"
|
||||
:sourceY="sourceY"
|
||||
:sourcePosition="sourceHandle.position"
|
||||
:targetX="targetX"
|
||||
:targetY="targetY"
|
||||
:targetPosition="targetPosition"
|
||||
:connectionLineType="connectionLineType"
|
||||
:connectionLineStyle="connectionLineStyle"
|
||||
/>
|
||||
</g>
|
||||
<path v-else class="revue-flow__connection-path" :d="dAttr" :style="connectionLineStyle" />
|
||||
</g>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { ref, defineComponent, CSSProperties, PropType, computed, inject } from 'vue';
|
||||
import { ref, defineComponent, CSSProperties, PropType, computed, inject, h } from 'vue';
|
||||
|
||||
import { getBezierPath } from '../Edges/BezierEdge';
|
||||
import { getSmoothStepPath } from '../Edges/SmoothStepEdge';
|
||||
@@ -26,23 +7,23 @@ import { Node, HandleElement, Position, ConnectionLineType, ConnectionLineCompon
|
||||
interface ConnectionLineProps {
|
||||
connectionLineType: ConnectionLineType;
|
||||
connectionLineStyle?: CSSProperties;
|
||||
CustomConnectionLineComponent?: ConnectionLineComponent;
|
||||
customConnectionLine?: ConnectionLineComponent;
|
||||
}
|
||||
|
||||
const ConnectionLine = defineComponent({
|
||||
export default defineComponent({
|
||||
props: {
|
||||
connectionLineType: {
|
||||
type: String as PropType<ConnectionLineProps['connectionLineType']>,
|
||||
required: false,
|
||||
default: ConnectionLineType.Bezier
|
||||
},
|
||||
connectionLineStyle: {
|
||||
type: Object as PropType<ConnectionLineProps['connectionLineStyle']>,
|
||||
required: false,
|
||||
default: () => {}
|
||||
},
|
||||
CustomConnectionLineComponent: {
|
||||
type: Object as PropType<ConnectionLineProps['CustomConnectionLineComponent']>,
|
||||
connectionLineType: {
|
||||
type: String as PropType<ConnectionLineProps['connectionLineType']>,
|
||||
required: false,
|
||||
default: ConnectionLineType.Bezier
|
||||
},
|
||||
customConnectionLine: {
|
||||
type: Object as PropType<ConnectionLineProps['customConnectionLine']>,
|
||||
required: false,
|
||||
default: undefined
|
||||
}
|
||||
@@ -114,20 +95,31 @@ const ConnectionLine = defineComponent({
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
dAttr,
|
||||
targetX,
|
||||
targetY,
|
||||
sourceX,
|
||||
sourceY,
|
||||
sourceHandle,
|
||||
sourceNode,
|
||||
sourceHandleX,
|
||||
sourceHandleY,
|
||||
nodesConnectable
|
||||
};
|
||||
if (props.customConnectionLine) {
|
||||
return () => (
|
||||
<g class="revue-flow__connection">
|
||||
{props.customConnectionLine &&
|
||||
h(props.customConnectionLine, {
|
||||
sourceX: sourceX.value,
|
||||
sourceY: sourceY.value,
|
||||
sourcePosition: sourceHandle.value?.position,
|
||||
targetX: targetX.value,
|
||||
targetY: targetY.value,
|
||||
targetPosition: targetPosition.value,
|
||||
connectionLineType: props.connectionLineType,
|
||||
connectionLineStyle: props.connectionLineStyle
|
||||
})}
|
||||
</g>
|
||||
);
|
||||
}
|
||||
|
||||
return () =>
|
||||
nodesConnectable.value && sourceNode.value ? (
|
||||
<g class="revue-flow__connection">
|
||||
<path d={dAttr.value} class="revue-flow__connection-path" style={props.connectionLineStyle} />
|
||||
</g>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default ConnectionLine;
|
||||
</script>
|
||||
@@ -121,7 +121,7 @@ const BezierEdge = defineComponent({
|
||||
style: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
required: false,
|
||||
default: () => ({})
|
||||
default: () => {}
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
|
||||
255
src/components/Edges/Edge.tsx
Normal file
255
src/components/Edges/Edge.tsx
Normal file
@@ -0,0 +1,255 @@
|
||||
import { EdgeAnchor } from './EdgeAnchor';
|
||||
import { ConnectionMode, Edge, Elements, Node, Position, RevueFlowStore, Transform } from '../../types';
|
||||
import { computed, defineComponent, h, inject, PropType, ref } from 'vue';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '../../container/EdgeRenderer/utils';
|
||||
import { isEdge } from '../../utils/graph';
|
||||
import { onMouseDown } from '../Handle/handler';
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: { EdgeAnchor },
|
||||
props: {
|
||||
edge: {
|
||||
type: Object as PropType<EdgeProps['edge']>,
|
||||
required: true
|
||||
},
|
||||
markerEndId: {
|
||||
type: Object as PropType<EdgeProps['markerEndId']>,
|
||||
required: false,
|
||||
default: undefined
|
||||
},
|
||||
edgeUpdaterRadius: {
|
||||
type: Number as PropType<EdgeProps['edgeUpdaterRadius']>,
|
||||
required: false,
|
||||
default: undefined
|
||||
},
|
||||
type: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
onlyRenderVisibleElements: {
|
||||
type: Boolean as PropType<EdgeProps['onlyRenderVisibleElements']>,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
connectionMode: {
|
||||
type: String as PropType<EdgeProps['connectionMode']>,
|
||||
required: false,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const nodes = computed(() => getSourceTargetNodes(props.edge, store.nodes));
|
||||
|
||||
hooks.connect.on((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}`);
|
||||
}
|
||||
|
||||
if (!nodes.value.targetNode) {
|
||||
console.warn(`couldn't create edge for target id: ${props.edge.target}; edge id: ${props.edge.id}`);
|
||||
}
|
||||
|
||||
const targetNodeBounds = computed(() => nodes.value.targetNode?.__rf.handleBounds);
|
||||
// when connection type is loose we can define all handles as sources
|
||||
const targetNodeHandles = computed(() =>
|
||||
props.connectionMode === ConnectionMode.Strict
|
||||
? targetNodeBounds.value?.target
|
||||
: targetNodeBounds.value?.target || targetNodeBounds.value?.source
|
||||
);
|
||||
const sourceHandle = computed(
|
||||
() => nodes.value.sourceNode && getHandle(nodes.value.sourceNode.__rf.handleBounds.source, props.edge.sourceHandle || null)
|
||||
);
|
||||
const targetHandle = computed(() => getHandle(targetNodeHandles.value, props.edge.targetHandle || null));
|
||||
const sourcePosition = computed(() => (sourceHandle.value ? sourceHandle.value.position : Position.Bottom));
|
||||
const targetPosition = computed(() => (targetHandle.value ? targetHandle.value.position : Position.Top));
|
||||
|
||||
const edgePositions = computed<{
|
||||
sourceX: number;
|
||||
sourceY: number;
|
||||
targetX: number;
|
||||
targetY: number;
|
||||
}>(() => {
|
||||
return getEdgePositions(
|
||||
nodes.value.sourceNode as Node,
|
||||
sourceHandle.value,
|
||||
sourcePosition.value,
|
||||
nodes.value.targetNode as Node,
|
||||
targetHandle.value,
|
||||
targetPosition.value
|
||||
);
|
||||
});
|
||||
|
||||
const isVisible = computed(() => {
|
||||
return props.onlyRenderVisibleElements
|
||||
? edgePositions.value &&
|
||||
isEdgeVisible({
|
||||
sourcePos: { x: edgePositions.value?.sourceX, y: edgePositions.value?.sourceY },
|
||||
targetPos: { x: edgePositions.value?.targetX, y: edgePositions.value?.targetY },
|
||||
width: store.width || 0,
|
||||
height: store.height || 0,
|
||||
transform: store.transform
|
||||
})
|
||||
: true;
|
||||
});
|
||||
|
||||
const isSelected = computed(() => store.selectedElements?.some((elm) => isEdge(elm) && elm.id === props.edge.id) || false);
|
||||
|
||||
const updating = ref<boolean>(false);
|
||||
const inactive = computed(() => !store.elementsSelectable);
|
||||
const edgeClasses = computed(() => [
|
||||
'revue-flow__edge',
|
||||
`revue-flow__edge-${props.type}`,
|
||||
{ selected: isSelected.value, animated: props.edge.animated, inactive: inactive.value, updating: updating.value }
|
||||
]);
|
||||
|
||||
const edgeElement = computed<Edge>(() => {
|
||||
const el: Edge = {
|
||||
id: props.edge.id || '',
|
||||
source: props.edge.source,
|
||||
target: props.edge.target,
|
||||
type: props.edge.type
|
||||
};
|
||||
|
||||
if (props.edge.sourceHandle) {
|
||||
el.sourceHandle = props.edge.sourceHandle;
|
||||
}
|
||||
|
||||
if (props.edge.targetHandle) {
|
||||
el.targetHandle = props.edge.targetHandle;
|
||||
}
|
||||
|
||||
if (typeof props.edge.data !== 'undefined') {
|
||||
el.data = props.edge.data;
|
||||
}
|
||||
|
||||
return el;
|
||||
});
|
||||
|
||||
const onEdgeClick = (event: MouseEvent) => {
|
||||
if (store.elementsSelectable) {
|
||||
store.unsetNodesSelection();
|
||||
store.addSelectedElements(edgeElement.value as any);
|
||||
}
|
||||
|
||||
hooks.edgeClick.trigger({ event, edge: edgeElement.value });
|
||||
};
|
||||
|
||||
const onEdgeContextMenu = (event: MouseEvent) =>
|
||||
hooks.edgeContextMenu.trigger({
|
||||
event,
|
||||
edge: edgeElement.value
|
||||
});
|
||||
|
||||
const onEdgeMouseEnter = (event: MouseEvent) => hooks.edgeMouseEnter.trigger({ event, edge: edgeElement.value });
|
||||
|
||||
const onEdgeMouseMove = (event: MouseEvent) => hooks.edgeMouseMove.trigger({ event, edge: edgeElement.value });
|
||||
|
||||
const onEdgeMouseLeave = (event: MouseEvent) => hooks.edgeMouseLeave.trigger({ event, edge: edgeElement.value });
|
||||
|
||||
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;
|
||||
|
||||
hooks.edgeUpdateStart.trigger({ event, edge: edgeElement.value });
|
||||
onMouseDown(
|
||||
event,
|
||||
store,
|
||||
hooks,
|
||||
handleId as string,
|
||||
nodeId,
|
||||
isTarget,
|
||||
isValidConnection,
|
||||
isSourceHandle ? 'target' : 'source'
|
||||
);
|
||||
};
|
||||
|
||||
const onEdgeUpdaterSourceMouseDown = (event: MouseEvent) => {
|
||||
handleEdgeUpdater(event, true);
|
||||
};
|
||||
|
||||
const onEdgeUpdaterTargetMouseDown = (event: MouseEvent) => {
|
||||
handleEdgeUpdater(event, false);
|
||||
};
|
||||
|
||||
const onEdgeUpdaterMouseEnter = () => (updating.value = true);
|
||||
const onEdgeUpdaterMouseOut = () => (updating.value = false);
|
||||
|
||||
return () =>
|
||||
isVisible.value ? (
|
||||
<g
|
||||
class={edgeClasses.value}
|
||||
onClick={onEdgeClick}
|
||||
onContextmenu={onEdgeContextMenu}
|
||||
onMouseenter={onEdgeMouseEnter}
|
||||
onMousemove={onEdgeMouseMove}
|
||||
onMouseleave={onEdgeMouseLeave}
|
||||
>
|
||||
{h(props.type, {
|
||||
id: props.edge.id,
|
||||
source: props.edge.source,
|
||||
target: props.edge.target,
|
||||
selected: isSelected,
|
||||
animated: props.edge.animated,
|
||||
label: props.edge.label,
|
||||
labelStyle: props.edge.labelStyle,
|
||||
labelShowBg: props.edge.labelShowBg,
|
||||
labelBgStyle: props.edge.labelBgStyle,
|
||||
labelBgPadding: props.edge.labelBgPadding,
|
||||
labelBgBorderRadius: props.edge.labelBgBorderRadius,
|
||||
data: props.edge.data,
|
||||
style: props.edge.style,
|
||||
arrowHeadType: props.edge.arrowHeadType,
|
||||
sourceX: edgePositions.value.sourceX,
|
||||
sourceY: edgePositions.value.sourceY,
|
||||
targetX: edgePositions.value.targetX,
|
||||
targetY: edgePositions.value.targetY,
|
||||
sourcePosition: sourcePosition.value,
|
||||
targetPosition: targetPosition.value,
|
||||
markerEndId: props.markerEndId,
|
||||
sourceHandleId: props.edge.sourceHandle,
|
||||
targetHandleId: props.edge.targetHandle
|
||||
}, {})}
|
||||
<g onMousedown={onEdgeUpdaterSourceMouseDown} onMouseenter={onEdgeUpdaterMouseEnter} onMouseout={onEdgeUpdaterMouseOut}>
|
||||
<EdgeAnchor
|
||||
position={sourcePosition.value}
|
||||
centerX={edgePositions.value.sourceX}
|
||||
centerY={edgePositions.value.sourceY}
|
||||
radius={props.edgeUpdaterRadius}
|
||||
/>
|
||||
</g>
|
||||
<g onMousedown={onEdgeUpdaterTargetMouseDown} onMouseenter={onEdgeUpdaterMouseEnter} onMouseout={onEdgeUpdaterMouseOut}>
|
||||
<EdgeAnchor
|
||||
position={sourcePosition.value}
|
||||
centerX={edgePositions.value.sourceX}
|
||||
centerY={edgePositions.value.sourceY}
|
||||
radius={props.edgeUpdaterRadius}
|
||||
/>
|
||||
</g>
|
||||
</g>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -144,8 +144,6 @@ export default defineComponent({
|
||||
targetY: number;
|
||||
}>(() => {
|
||||
return (
|
||||
nodes.value.sourceNode &&
|
||||
nodes.value.targetNode &&
|
||||
getEdgePositions(
|
||||
nodes.value.sourceNode,
|
||||
sourceHandle.value,
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
import { ElementId, Position, RevueFlowStore } from '../../types';
|
||||
import { onMouseDown, ValidConnectionFunc } from './handler';
|
||||
import { computed, defineComponent, h, inject, PropType } from 'vue';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
|
||||
const alwaysValid = () => true;
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: 'source'
|
||||
},
|
||||
position: {
|
||||
type: String as PropType<Position>,
|
||||
required: false,
|
||||
default: Position.Top
|
||||
},
|
||||
isValidConnection: {
|
||||
type: Function as PropType<ValidConnectionFunc>,
|
||||
required: false,
|
||||
default: alwaysValid
|
||||
},
|
||||
isConnectable: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const nodeId = inject<ElementId>('NodeIdContext') as ElementId;
|
||||
const isTarget = computed(() => props.type === 'target');
|
||||
|
||||
const onMouseDownHandler = (event: MouseEvent) => {
|
||||
onMouseDown(event, store, hooks, props.id || '', nodeId, isTarget.value, props.isValidConnection);
|
||||
};
|
||||
|
||||
const handleClasses = computed(() => [
|
||||
'revue-flow__handle',
|
||||
`revue-flow__handle-${props.position}`,
|
||||
'nodrag',
|
||||
{
|
||||
source: !isTarget.value,
|
||||
target: isTarget.value,
|
||||
connectable: props.isConnectable
|
||||
}
|
||||
]);
|
||||
|
||||
return () =>
|
||||
h(
|
||||
'div',
|
||||
{
|
||||
dataHandleid: props.id,
|
||||
dataNodeid: nodeId,
|
||||
dataHandlepos: props.position,
|
||||
class: handleClasses.value,
|
||||
onMouseDown: onMouseDownHandler
|
||||
},
|
||||
slots.default?.()
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,11 +1,8 @@
|
||||
import { getHostForElement } from '../../utils';
|
||||
import { ElementId, XYPosition, ConnectionMode, SetConnectionId, Connection, HandleType, RevueFlowStore } from '../../types';
|
||||
import { ElementId, ConnectionMode, Connection, HandleType, RevueFlowStore } from '../../types';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
|
||||
export type ValidConnectionFunc = (connection: Connection) => boolean;
|
||||
export type SetSourceIdFunc = (params: SetConnectionId) => void;
|
||||
|
||||
export type SetPosition = (pos: XYPosition) => void;
|
||||
|
||||
type Result = {
|
||||
elementBelow: Element | null;
|
||||
|
||||
@@ -5,7 +5,7 @@ import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
|
||||
const alwaysValid = () => true;
|
||||
|
||||
const Handle = defineComponent({
|
||||
export default defineComponent({
|
||||
props: {
|
||||
type: {
|
||||
type: String,
|
||||
@@ -41,21 +41,18 @@ const Handle = defineComponent({
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const nodeId = inject<ElementId>('NodeIdContext') as ElementId;
|
||||
const isTarget = computed(() => props.type === 'target');
|
||||
const nodeId = inject<ElementId>('NodeIdContext')!;
|
||||
|
||||
const onMouseDownHandler = (event: MouseEvent) => {
|
||||
console.log('mousedown');
|
||||
onMouseDown(event, store, hooks, props.id as string, nodeId, isTarget.value, props.isValidConnection);
|
||||
};
|
||||
const onMouseDownHandler = (event: MouseEvent) =>
|
||||
onMouseDown(event, store, hooks, props.id as string, nodeId, props.type === 'target', props.isValidConnection);
|
||||
|
||||
const handleClasses = computed(() => [
|
||||
'revue-flow__handle',
|
||||
`revue-flow__handle-${props.position}`,
|
||||
'nodrag',
|
||||
{
|
||||
source: !isTarget.value,
|
||||
target: isTarget.value,
|
||||
source: props.type !== 'target',
|
||||
target: props.type === 'target',
|
||||
connectable: props.isConnectable
|
||||
}
|
||||
]);
|
||||
@@ -73,5 +70,3 @@ const Handle = defineComponent({
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default Handle;
|
||||
|
||||
@@ -57,7 +57,7 @@ export default defineComponent({
|
||||
required: true
|
||||
},
|
||||
type: {
|
||||
type: Object,
|
||||
type: [Object, String],
|
||||
required: true
|
||||
},
|
||||
snapToGrid: {
|
||||
|
||||
@@ -38,9 +38,6 @@ export default defineComponent({
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const el = templateRef('user-selection', null);
|
||||
const shouldRender = computed(() => store.selectionActive || store.elementsSelectable);
|
||||
if (!shouldRender.value) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const onMouseDown = (event: MouseEvent) => {
|
||||
const mousePos = getMousePosition(event);
|
||||
@@ -52,12 +49,10 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const onMouseMove = (event: MouseEvent) => {
|
||||
|
||||
if (!store.selectionActive) {
|
||||
return;
|
||||
}
|
||||
const mousePos = getMousePosition(event);
|
||||
|
||||
if (!mousePos) {
|
||||
return;
|
||||
}
|
||||
@@ -80,19 +75,22 @@ export default defineComponent({
|
||||
useEventListener(el, 'mouseup', onMouseUp);
|
||||
useEventListener(el, 'mouseleave', onMouseLeave);
|
||||
|
||||
return () => (
|
||||
<div class="revue-flow__selectionpane" ref="user-selection">
|
||||
{store?.userSelectionRect.draw ? (
|
||||
<SelectionRect
|
||||
width={store.userSelectionRect.width}
|
||||
height={store.userSelectionRect.height}
|
||||
x={store.userSelectionRect.x}
|
||||
y={store.userSelectionRect.y}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
return () =>
|
||||
shouldRender.value ? (
|
||||
<div class="revue-flow__selectionpane" ref="user-selection">
|
||||
{store?.userSelectionRect.draw ? (
|
||||
<SelectionRect
|
||||
width={store.userSelectionRect.width}
|
||||
height={store.userSelectionRect.height}
|
||||
x={store.userSelectionRect.x}
|
||||
y={store.userSelectionRect.y}
|
||||
/>
|
||||
) : (
|
||||
''
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
22
src/container/EdgeRenderer/Marker.tsx
Normal file
22
src/container/EdgeRenderer/Marker.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
import { FunctionalComponent } from 'vue';
|
||||
|
||||
interface MarkerProps {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export default ((props: MarkerProps, { slots }) => {
|
||||
return () => (
|
||||
<marker
|
||||
id={props.id}
|
||||
class="revue-flow__arrowhead"
|
||||
markerWidth="12.5"
|
||||
markerHeight="12.5"
|
||||
viewBox="-10 -10 20 20"
|
||||
orient="auto"
|
||||
refX="0"
|
||||
refY="0"
|
||||
>
|
||||
{slots.default ? slots.default() : ''}
|
||||
</marker>
|
||||
);
|
||||
}) as FunctionalComponent<MarkerProps>;
|
||||
@@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<marker
|
||||
:id="id"
|
||||
class="revue-flow__arrowhead"
|
||||
markerWidth="12.5"
|
||||
markerHeight="12.5"
|
||||
viewBox="-10 -10 20 20"
|
||||
orient="auto"
|
||||
refX="0"
|
||||
refY="0"
|
||||
>
|
||||
<slot></slot>
|
||||
</marker>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
|
||||
interface MarkerProps {
|
||||
id: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
id: {
|
||||
type: String as PropType<MarkerProps['id']>,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
42
src/container/EdgeRenderer/MarkerDefinitions.tsx
Normal file
42
src/container/EdgeRenderer/MarkerDefinitions.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import Marker from './Marker';
|
||||
|
||||
interface MarkerDefinitionsProps {
|
||||
color: string;
|
||||
}
|
||||
|
||||
export default defineComponent({
|
||||
components: { Marker },
|
||||
props: {
|
||||
color: {
|
||||
type: String as PropType<MarkerDefinitionsProps['color']>,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
return () => (
|
||||
<defs>
|
||||
<Marker id="revue-flow__arrowclosed">
|
||||
<polyline
|
||||
stroke={props.color}
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
fill={props.color}
|
||||
points="-5,-4 0,0 -5,4 -5,-4"
|
||||
/>
|
||||
</Marker>
|
||||
<Marker id="revue-flow__arrow">
|
||||
<polyline
|
||||
stroke={props.color}
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
points="-5,-4 0,0 -5,4"
|
||||
/>
|
||||
</Marker>
|
||||
</defs>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,45 +0,0 @@
|
||||
<template>
|
||||
<defs>
|
||||
<Marker id="revue-flow__arrowclosed">
|
||||
<polyline
|
||||
:stroke="color"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1"
|
||||
:fill="color"
|
||||
points="-5,-4 0,0 -5,4 -5,-4"
|
||||
/>
|
||||
</Marker>
|
||||
<Marker id="revue-flow__arrow">
|
||||
<polyline
|
||||
:stroke="color"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
fill="none"
|
||||
points="-5,-4 0,0 -5,4"
|
||||
/>
|
||||
</Marker>
|
||||
</defs>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import Marker from './Marker.vue';
|
||||
|
||||
interface MarkerDefinitionsProps {
|
||||
color: string;
|
||||
}
|
||||
|
||||
const MarkerDefinitions = defineComponent({
|
||||
name: 'MarkerDefinitions',
|
||||
components: { Marker },
|
||||
props: {
|
||||
color: {
|
||||
type: String as PropType<MarkerDefinitionsProps['color']>,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
export default MarkerDefinitions;
|
||||
</script>
|
||||
@@ -1,30 +1,8 @@
|
||||
<template>
|
||||
<svg :width="width" :height="height" class="revue-flow__edges">
|
||||
<MarkerDefinitions :color="arrowHeadColor" />
|
||||
<g :transform="transformStyle">
|
||||
<template v-for="edge of edges" :key="edge.id">
|
||||
<Edge
|
||||
:edge="edge"
|
||||
:marker-end-id="markerEndId"
|
||||
:onlyRenderVisibleElements="onlyRenderVisibleElements"
|
||||
:type="edgeTypes[edge.type]"
|
||||
/>
|
||||
<ConnectionLine
|
||||
v-if="renderConnectionLine"
|
||||
:connectionLineStyle="connectionLineStyle"
|
||||
:connectionLineType="connectionLineType"
|
||||
:CustomConnectionLineComponent="connectionLineComponent"
|
||||
/>
|
||||
</template>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, CSSProperties, defineComponent, inject, PropType } from 'vue';
|
||||
import Edge from '../../components/Edges/Edge.vue';
|
||||
import ConnectionLine from '../../components/ConnectionLine/ConnectionLine.vue';
|
||||
import { ConnectionLineType, ConnectionLineComponent, ConnectionMode, RevueFlowStore } from '../../types';
|
||||
import MarkerDefinitions from './MarkerDefinitions.vue';
|
||||
import ConnectionLine from '../../components/ConnectionLine';
|
||||
import MarkerDefinitions from './MarkerDefinitions';
|
||||
import Edge from '../../components/Edges/Edge';
|
||||
|
||||
interface EdgeRendererProps {
|
||||
edgeTypes: any;
|
||||
@@ -38,7 +16,7 @@ interface EdgeRendererProps {
|
||||
edgeUpdaterRadius?: number;
|
||||
}
|
||||
|
||||
const EdgeRenderer = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'EdgeRenderer',
|
||||
components: {
|
||||
Edge,
|
||||
@@ -48,8 +26,7 @@ const EdgeRenderer = defineComponent({
|
||||
props: {
|
||||
edgeTypes: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: undefined
|
||||
required: true
|
||||
},
|
||||
connectionLineType: {
|
||||
type: String as PropType<EdgeRendererProps['connectionLineType']>,
|
||||
@@ -92,27 +69,36 @@ const EdgeRenderer = defineComponent({
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const edges = computed(() => store.edges);
|
||||
const transform = computed(() => store.transform);
|
||||
setup(props) {
|
||||
const store = inject<RevueFlowStore>('store');
|
||||
const transform = computed(() => store?.transform);
|
||||
const transformStyle = computed(() => {
|
||||
return `translate(${transform.value?.[0]},${transform.value?.[1]}) scale(${transform.value?.[2]})`;
|
||||
});
|
||||
const renderConnectionLine = computed(() => store.connectionNodeId && store.connectionHandleType);
|
||||
const width = computed(() => store.width);
|
||||
const height = computed(() => store.height);
|
||||
const renderConnectionLine = computed(() => store?.connectionNodeId && store?.connectionHandleType);
|
||||
|
||||
return {
|
||||
transform,
|
||||
transformStyle,
|
||||
renderConnectionLine,
|
||||
edges,
|
||||
width,
|
||||
height
|
||||
};
|
||||
return () => (
|
||||
<svg width={store?.width} height={store?.height} class="revue-flow__edges">
|
||||
<MarkerDefinitions color={props.arrowHeadColor as string} />
|
||||
<g transform={transformStyle.value}>
|
||||
{store?.edges.map((edge) => (
|
||||
<Edge
|
||||
key={edge.id}
|
||||
edge={edge}
|
||||
type={props.edgeTypes[edge.type || 'default']}
|
||||
onlyRenderVisibleElements={props.onlyRenderVisibleElements}
|
||||
markerEndId={props.markerEndId}
|
||||
/>
|
||||
))}
|
||||
{renderConnectionLine.value && (
|
||||
<ConnectionLine
|
||||
connectionLineStyle={props.connectionLineStyle}
|
||||
connectionLineType={props.connectionLineType}
|
||||
customConnectionLine={props.connectionLineComponent}
|
||||
/>
|
||||
)}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default EdgeRenderer;
|
||||
</script>
|
||||
@@ -1,26 +1,3 @@
|
||||
<template>
|
||||
<ZoomPane
|
||||
:selectionKeyPressed="keyPressed"
|
||||
:elementsSelectable="elementsSelectable"
|
||||
:zoomOnScroll="zoomOnScroll"
|
||||
:zoomOnPinch="zoomOnPinch"
|
||||
:panOnScroll="panOnScroll"
|
||||
:panOnScrollSpeed="panOnScrollSpeed"
|
||||
:panOnScrollMode="panOnScrollMode"
|
||||
:zoomOnDoubleClick="zoomOnDoubleClick"
|
||||
:paneMoveable="paneMoveable"
|
||||
:defaultPosition="defaultPosition"
|
||||
:defaultZoom="defaultZoom"
|
||||
:translateExtent="translateExtent"
|
||||
:zoomActivationKeyCode="zoomActivationKeyCode"
|
||||
>
|
||||
<slot></slot>
|
||||
<UserSelection v-if="keyPressed" />
|
||||
<NodesSelection v-if="selectionActive" />
|
||||
<div class="revue-flow__pane" @click="onClick" @onContextmenu="onContextMenu" @wheel="onWheel" />
|
||||
</ZoomPane>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, inject, PropType } from 'vue';
|
||||
import ZoomPane from '../ZoomPane';
|
||||
import UserSelection from '../../components/UserSelection';
|
||||
@@ -43,7 +20,7 @@ type FlowRendererProps = Omit<
|
||||
| 'selectNodesOnDrag'
|
||||
>;
|
||||
|
||||
const FlowRenderer = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'FlowRenderer',
|
||||
components: { UserSelection, ZoomPane, NodesSelection },
|
||||
props: {
|
||||
@@ -123,7 +100,7 @@ const FlowRenderer = defineComponent({
|
||||
default: undefined
|
||||
}
|
||||
},
|
||||
setup(props) {
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const keyPressed = useKeyPress(props.selectionKeyCode);
|
||||
@@ -147,15 +124,27 @@ const FlowRenderer = defineComponent({
|
||||
|
||||
const selectionActive = computed(() => store.nodesSelectionActive);
|
||||
|
||||
return {
|
||||
onClick,
|
||||
onContextMenu,
|
||||
onWheel,
|
||||
keyPressed,
|
||||
selectionActive
|
||||
};
|
||||
return () => (
|
||||
<ZoomPane
|
||||
selectionKeyPressed={keyPressed.value}
|
||||
elementsSelectable={props.elementsSelectable}
|
||||
zoomOnScroll={props.zoomOnScroll}
|
||||
zoomOnPinch={props.zoomOnPinch}
|
||||
panOnScroll={props.panOnScroll}
|
||||
panOnScrollSpeed={props.panOnScrollSpeed}
|
||||
panOnScrollMode={props.panOnScrollMode}
|
||||
zoomOnDoubleClick={props.zoomOnDoubleClick}
|
||||
paneMoveable={props.paneMoveable}
|
||||
defaultPosition={props.defaultPosition}
|
||||
defaultZoom={props.defaultZoom}
|
||||
translateExtent={props.translateExtent}
|
||||
zoomActivationKeyCode={props.zoomActivationKeyCode}
|
||||
>
|
||||
{slots.default ? slots.default() : ''}
|
||||
{keyPressed.value ? <UserSelection /> : ''}
|
||||
{selectionActive.value ? <NodesSelection /> : ''}
|
||||
<div class="revue-flow__pane" onClick={onClick} onContextmenu={onContextMenu} onWheel={onWheel} />
|
||||
</ZoomPane>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default FlowRenderer;
|
||||
</script>
|
||||
@@ -1,52 +1,52 @@
|
||||
<template>
|
||||
<FlowRenderer
|
||||
:deleteKeyCode="deleteKeyCode"
|
||||
:selectionKeyCode="selectionKeyCode"
|
||||
:multiSelectionKeyCode="multiSelectionKeyCode"
|
||||
:zoomActivationKeyCode="zoomActivationKeyCode"
|
||||
:elementsSelectable="elementsSelectable"
|
||||
:zoomOnScroll="zoomOnScroll"
|
||||
:zoomOnPinch="zoomOnPinch"
|
||||
:zoomOnDoubleClick="zoomOnDoubleClick"
|
||||
:panOnScroll="panOnScroll"
|
||||
:panOnScrollSpeed="panOnScrollSpeed"
|
||||
:panOnScrollMode="panOnScrollMode"
|
||||
:paneMoveable="paneMoveable"
|
||||
:defaultPosition="defaultPosition"
|
||||
:defaultZoom="defaultZoom"
|
||||
:translateExtent="translateExtent"
|
||||
:delete-key-code="deleteKeyCode"
|
||||
:selection-key-code="selectionKeyCode"
|
||||
:multi-selection-key-code="multiSelectionKeyCode"
|
||||
:zoom-activation-key-code="zoomActivationKeyCode"
|
||||
:elements-selectable="elementsSelectable"
|
||||
:zoom-on-scroll="zoomOnScroll"
|
||||
:zoom-on-pinch="zoomOnPinch"
|
||||
:zoom-on-double-click="zoomOnDoubleClick"
|
||||
:pan-on-scroll="panOnScroll"
|
||||
:pan-on-scroll-speed="panOnScrollSpeed"
|
||||
:pan-on-scroll-mode="panOnScrollMode"
|
||||
:pane-moveable="paneMoveable"
|
||||
:default-position="defaultPosition"
|
||||
:default-zoom="defaultZoom"
|
||||
:translate-extent="translateExtent"
|
||||
>
|
||||
<NodeRenderer
|
||||
:nodeTypes="nodeTypes"
|
||||
:selectNodesOnDrag="selectNodesOnDrag"
|
||||
:snapToGrid="snapToGrid"
|
||||
:snapGrid="snapGrid"
|
||||
:onlyRenderVisibleElements="onlyRenderVisibleElements"
|
||||
:node-types="nodeTypes"
|
||||
:select-nodes-on-drag="selectNodesOnDrag"
|
||||
:snap-to-grid="snapToGrid"
|
||||
:snap-grid="snapGrid"
|
||||
:only-render-visible-elements="onlyRenderVisibleElements"
|
||||
/>
|
||||
<EdgeRenderer
|
||||
:edgeTypes="edgeTypes"
|
||||
:connectionLineType="connectionLineType"
|
||||
:connectionLineStyle="connectionLineStyle"
|
||||
:connectionLineComponent="connectionLineComponent"
|
||||
:connectionMode="connectionMode"
|
||||
:arrowHeadColor="arrowHeadColor"
|
||||
:markerEndId="markerEndId"
|
||||
:onlyRenderVisibleElements="onlyRenderVisibleElements"
|
||||
:edgeUpdaterRadius="edgeUpdaterRadius"
|
||||
:edge-types="edgeTypes"
|
||||
:connection-line-type="connectionLineType"
|
||||
:connection-line-style="connectionLineStyle"
|
||||
:connection-line-component="connectionLineComponent"
|
||||
:connection-mode="connectionMode"
|
||||
:arrow-head-color="arrowHeadColor"
|
||||
:marker-end-id="markerEndId"
|
||||
:only-render-visible-elements="onlyRenderVisibleElements"
|
||||
:edge-updater-radius="edgeUpdaterRadius"
|
||||
/>
|
||||
</FlowRenderer>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { defineComponent, inject, PropType, CSSProperties, watchEffect } from 'vue';
|
||||
import FlowRenderer from '../FlowRenderer/FlowRenderer.vue';
|
||||
import NodeRenderer from '../NodeRenderer/NodeRenderer.vue';
|
||||
import EdgeRenderer from '../EdgeRenderer/EdgeRenderer.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';
|
||||
|
||||
const GraphView = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'GraphView',
|
||||
components: { NodeRenderer, EdgeRenderer, FlowRenderer },
|
||||
props: {
|
||||
@@ -220,20 +220,18 @@ const GraphView = defineComponent({
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
|
||||
const { onReady } = useZoomPanHelper(store);
|
||||
onReady(({ zoomIn, zoomOut, zoomTo, transform, fitView, initialized }) => {
|
||||
if (initialized) {
|
||||
hooks.load.trigger({
|
||||
fitView: (params = { padding: 0.1 }) => fitView(params),
|
||||
zoomIn,
|
||||
zoomOut,
|
||||
zoomTo,
|
||||
setTransform: transform,
|
||||
project: onLoadProject(store),
|
||||
getElements: onLoadGetElements(store),
|
||||
toObject: onLoadToObject(store)
|
||||
});
|
||||
}
|
||||
const { onReady } = useZoomPanHelper();
|
||||
onReady(({ zoomIn, zoomOut, zoomTo, transform, fitView }) => {
|
||||
hooks.load.trigger({
|
||||
fitView: (params = { padding: 0.1 }) => fitView(params),
|
||||
zoomIn,
|
||||
zoomOut,
|
||||
zoomTo,
|
||||
setTransform: transform,
|
||||
project: onLoadProject(store),
|
||||
getElements: onLoadGetElements(store),
|
||||
toObject: onLoadToObject(store)
|
||||
});
|
||||
});
|
||||
|
||||
const init = () => {
|
||||
@@ -281,6 +279,4 @@ const GraphView = defineComponent({
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
export default GraphView;
|
||||
</script>
|
||||
|
||||
@@ -1,21 +1,7 @@
|
||||
<template>
|
||||
<div class="revue-flow__nodes" :style="transformStyle">
|
||||
<template v-for="(node, i) of visibleNodes" :key="`node-${i}`">
|
||||
<Node
|
||||
:node="node"
|
||||
:snap-grid="snapGrid"
|
||||
:snap-to-grid="snapToGrid"
|
||||
:select-nodes-on-drag="selectNodesOnDrag"
|
||||
:type="nodeTypes[node.type]"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { getNodesInside } from '../../utils/graph';
|
||||
import { NodeTypesType, RevueFlowStore } from '../../types';
|
||||
import { computed, defineComponent, inject, PropType } from 'vue';
|
||||
import Node from '../../components/Nodes/Node.vue';
|
||||
import { getNodesInside } from '../../utils/graph';
|
||||
import { NodeTypesType, RevueFlowStore } from '../../types';
|
||||
|
||||
interface NodeRendererProps {
|
||||
nodeTypes: NodeTypesType;
|
||||
@@ -25,7 +11,7 @@ interface NodeRendererProps {
|
||||
onlyRenderVisibleElements: boolean;
|
||||
}
|
||||
|
||||
const NodeRenderer = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'NodeRenderer',
|
||||
components: { Node },
|
||||
props: {
|
||||
@@ -79,12 +65,28 @@ const NodeRenderer = defineComponent({
|
||||
transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})`
|
||||
}));
|
||||
|
||||
return {
|
||||
transformStyle,
|
||||
visibleNodes
|
||||
};
|
||||
return () => (
|
||||
<div class="revue-flow__nodes" style={transformStyle.value}>
|
||||
{visibleNodes.value?.map((node) => {
|
||||
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}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default NodeRenderer;
|
||||
</script>
|
||||
@@ -1,39 +1,39 @@
|
||||
<template>
|
||||
<div class="revue-flow">
|
||||
<GraphView
|
||||
:nodeTypes="nodeTypesParsed"
|
||||
:edgeTypes="edgeTypesParsed"
|
||||
:connectionMode="connectionMode"
|
||||
:connectionLineType="connectionLineType"
|
||||
:connectionLineStyle="connectionLineStyle"
|
||||
:connectionLineComponent="connectionLineComponent"
|
||||
:selectionKeyCode="selectionKeyCode"
|
||||
:deleteKeyCode="deleteKeyCode"
|
||||
:multiSelectionKeyCode="multiSelectionKeyCode"
|
||||
:zoomActivationKeyCode="zoomActivationKeyCode"
|
||||
:snapToGrid="snapToGrid"
|
||||
:snapGrid="snapGrid"
|
||||
:onlyRenderVisibleElements="onlyRenderVisibleElements"
|
||||
:nodesDraggable="nodesDraggable"
|
||||
:nodesConnectable="nodesConnectable"
|
||||
:elementsSelectable="elementsSelectable"
|
||||
:selectNodesOnDrag="selectNodesOnDrag"
|
||||
:minZoom="minZoom"
|
||||
:maxZoom="maxZoom"
|
||||
:defaultZoom="defaultZoom"
|
||||
:defaultPosition="defaultPosition"
|
||||
:translateExtent="translateExtent"
|
||||
:nodeExtent="nodeExtent"
|
||||
:arrowHeadColor="arrowHeadColor"
|
||||
:markerEndId="markerEndId"
|
||||
:zoomOnScroll="zoomOnScroll"
|
||||
:zoomOnPinch="zoomOnPinch"
|
||||
:zoomOnDoubleClick="zoomOnDoubleClick"
|
||||
:panOnScroll="panOnScroll"
|
||||
:panOnScrollSpeed="panOnScrollSpeed"
|
||||
:panOnScrollMode="panOnScrollMode"
|
||||
:paneMoveable="paneMoveable"
|
||||
:edgeUpdaterRadius="edgeUpdaterRadius"
|
||||
:node-types="nodeTypesParsed"
|
||||
:edge-types="edgeTypesParsed"
|
||||
:connection-mode="connectionMode"
|
||||
:connection-line-type="connectionLineType"
|
||||
:connection-line-style="connectionLineStyle"
|
||||
:connection-line-component="connectionLineComponent"
|
||||
:selection-key-code="selectionKeyCode"
|
||||
:delete-key-code="deleteKeyCode"
|
||||
:multi-selection-key-code="multiSelectionKeyCode"
|
||||
:zoom-activation-key-code="zoomActivationKeyCode"
|
||||
:snap-to-grid="snapToGrid"
|
||||
:snap-grid="snapGrid"
|
||||
:only-render-visible-elements="onlyRenderVisibleElements"
|
||||
:nodes-draggable="nodesDraggable"
|
||||
:nodes-connectable="nodesConnectable"
|
||||
:elements-selectable="elementsSelectable"
|
||||
:select-nodes-on-drag="selectNodesOnDrag"
|
||||
:min-zoom="minZoom"
|
||||
:max-zoom="maxZoom"
|
||||
:default-zoom="defaultZoom"
|
||||
:default-position="defaultPosition"
|
||||
:translate-extent="translateExtent"
|
||||
:node-extent="nodeExtent"
|
||||
:arrow-head-color="arrowHeadColor"
|
||||
:marker-end-id="markerEndId"
|
||||
:zoom-on-scroll="zoomOnScroll"
|
||||
:zoom-on-pinch="zoomOnPinch"
|
||||
:zoom-on-double-click="zoomOnDoubleClick"
|
||||
:pan-on-scroll="panOnScroll"
|
||||
:pan-on-scroll-speed="panOnScrollSpeed"
|
||||
:pan-on-scroll-mode="panOnScrollMode"
|
||||
:pane-moveable="paneMoveable"
|
||||
:edge-updater-radius="edgeUpdaterRadius"
|
||||
/>
|
||||
<slot></slot>
|
||||
</div>
|
||||
@@ -68,8 +68,7 @@ const defaultEdgeTypes = {
|
||||
smoothstep: SmoothStepEdge
|
||||
};
|
||||
|
||||
const eventHooks = useRevueFlow();
|
||||
const RevueFlow = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'RevueFlow',
|
||||
components: { GraphView },
|
||||
props: {
|
||||
@@ -254,12 +253,12 @@ const RevueFlow = defineComponent({
|
||||
default: 10
|
||||
}
|
||||
},
|
||||
emits: Object.keys(eventHooks),
|
||||
emits: Object.keys(useRevueFlow()),
|
||||
setup(props, { emit }) {
|
||||
const store = configureStore(initialState)();
|
||||
provide<RevueFlowStore>('store', store);
|
||||
eventHooks.bind(emit);
|
||||
provide<RevueFlowHooks>('hooks', eventHooks);
|
||||
const hooks = useRevueFlow().bind(emit);
|
||||
provide<RevueFlowHooks>('hooks', hooks);
|
||||
const elements = useVModel(props, 'modelValue', emit);
|
||||
|
||||
store.setElements(elements.value);
|
||||
@@ -289,5 +288,4 @@ const RevueFlow = defineComponent({
|
||||
};
|
||||
}
|
||||
});
|
||||
export default RevueFlow;
|
||||
</script>
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { zoom, zoomIdentity } from 'd3-zoom';
|
||||
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, watchEffect } from 'vue';
|
||||
import useKeyPress from '../../hooks/useKeyPress';
|
||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
||||
import { RevueFlowHooks } from '../../hooks/RevueFlowHooks';
|
||||
import { templateRef, whenever } from '@vueuse/core';
|
||||
|
||||
interface ZoomPaneProps {
|
||||
selectionKeyPressed: boolean;
|
||||
@@ -20,38 +21,12 @@ interface ZoomPaneProps {
|
||||
defaultPosition?: [number, number];
|
||||
defaultZoom?: number;
|
||||
translateExtent?: TranslateExtent;
|
||||
onMove?: (flowTransform?: FlowTransform) => void;
|
||||
onMoveStart?: (flowTransform?: FlowTransform) => void;
|
||||
onMoveEnd?: (flowTransform?: FlowTransform) => void;
|
||||
zoomActivationKeyCode?: KeyCode;
|
||||
}
|
||||
|
||||
const viewChanged = (prevTransform: FlowTransform, eventTransform: any): boolean =>
|
||||
prevTransform.x !== eventTransform.x || prevTransform.y !== eventTransform.y || prevTransform.zoom !== eventTransform.k;
|
||||
|
||||
const eventToFlowTransform = (eventTransform: any): FlowTransform => ({
|
||||
x: eventTransform.x,
|
||||
y: eventTransform.y,
|
||||
zoom: eventTransform.k
|
||||
});
|
||||
|
||||
const ZoomPane = defineComponent({
|
||||
export default defineComponent({
|
||||
name: 'ZoomPane',
|
||||
props: {
|
||||
onMove: {
|
||||
type: Function as unknown as PropType<ZoomPaneProps['onMove']>,
|
||||
required: false,
|
||||
default: () => {}
|
||||
},
|
||||
onMoveStart: {
|
||||
type: Function as unknown as PropType<ZoomPaneProps['onMoveStart']>,
|
||||
required: false,
|
||||
default: () => {}
|
||||
},
|
||||
onMoveEnd: {
|
||||
type: Function as unknown as PropType<ZoomPaneProps['onMoveEnd']>,
|
||||
required: false,
|
||||
default: () => {}
|
||||
},
|
||||
zoomOnScroll: {
|
||||
type: Boolean as PropType<ZoomPaneProps['zoomOnScroll']>,
|
||||
required: false,
|
||||
@@ -85,12 +60,12 @@ const ZoomPane = defineComponent({
|
||||
selectionKeyPressed: {
|
||||
type: Boolean as PropType<ZoomPaneProps['selectionKeyPressed']>,
|
||||
required: false,
|
||||
default: undefined
|
||||
default: false
|
||||
},
|
||||
elementsSelectable: {
|
||||
type: Boolean as PropType<ZoomPaneProps['elementsSelectable']>,
|
||||
required: false,
|
||||
default: undefined
|
||||
default: true
|
||||
},
|
||||
paneMoveable: {
|
||||
type: Boolean as PropType<ZoomPaneProps['paneMoveable']>,
|
||||
@@ -120,16 +95,23 @@ const ZoomPane = defineComponent({
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
const store = inject<RevueFlowStore>('store')!;
|
||||
const zoomPane = ref<HTMLDivElement | null>(null);
|
||||
const hooks = inject<RevueFlowHooks>('hooks')!;
|
||||
const zoomPane = templateRef<HTMLDivElement>('zoom-pane', null);
|
||||
const prevTransform = ref<FlowTransform>({ x: 0, y: 0, zoom: 0 });
|
||||
const zoomActivationKeyPressed = useKeyPress(props.zoomActivationKeyCode);
|
||||
|
||||
watchEffect(() => {
|
||||
if (zoomPane.value) useResizeHandler(zoomPane.value, store.updateSize);
|
||||
const viewChanged = (prevTransform: FlowTransform, eventTransform: ZoomTransform): boolean =>
|
||||
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
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (zoomPane.value && props.defaultPosition && props.defaultZoom) {
|
||||
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);
|
||||
@@ -152,43 +134,41 @@ const ZoomPane = defineComponent({
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (store.d3Selection && store.d3Zoom) {
|
||||
if (props.panOnScroll && zoomActivationKeyPressed) {
|
||||
store.d3Selection
|
||||
.on('wheel', (event: any) => {
|
||||
event.preventDefault();
|
||||
event.stopImmediatePropagation();
|
||||
whenever(zoomActivationKeyPressed, () => {
|
||||
if (props.panOnScroll) {
|
||||
store.d3Selection
|
||||
?.on('wheel', (event: WheelEvent) => {
|
||||
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);
|
||||
// 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);
|
||||
if (event.ctrlKey && props.zoomOnPinch) {
|
||||
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);
|
||||
|
||||
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;
|
||||
// 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;
|
||||
|
||||
if (store.d3Selection && props.panOnScrollSpeed)
|
||||
store.d3Zoom?.translateBy(
|
||||
store.d3Selection,
|
||||
-(deltaX / currentZoom) * props.panOnScrollSpeed,
|
||||
-(deltaY / currentZoom) * props.panOnScrollSpeed
|
||||
);
|
||||
})
|
||||
.on('wheel.zoom', null);
|
||||
} else if (typeof store.d3ZoomHandler !== 'undefined') {
|
||||
store.d3Selection.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler);
|
||||
}
|
||||
if (store.d3Selection && props.panOnScrollSpeed)
|
||||
store.d3Zoom?.translateBy(
|
||||
store.d3Selection,
|
||||
-(deltaX / currentZoom) * props.panOnScrollSpeed,
|
||||
-(deltaY / currentZoom) * props.panOnScrollSpeed
|
||||
);
|
||||
})
|
||||
.on('wheel.zoom', null);
|
||||
} else if (typeof store.d3ZoomHandler !== 'undefined') {
|
||||
store.d3Selection?.on('wheel', null).on('wheel.zoom', store.d3ZoomHandler);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -197,13 +177,9 @@ const ZoomPane = defineComponent({
|
||||
if (props.selectionKeyPressed) {
|
||||
store.d3Zoom.on('zoom', null);
|
||||
} else {
|
||||
store.d3Zoom.on('zoom', (event: any) => {
|
||||
store.d3Zoom.on('zoom', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
store.transform = [event.transform.x, event.transform.y, event.transform.k];
|
||||
|
||||
if (props.onMove) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
props.onMove(flowTransform);
|
||||
}
|
||||
hooks.move.trigger(eventToFlowTransform(event.transform));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -211,41 +187,33 @@ const ZoomPane = defineComponent({
|
||||
|
||||
watchEffect(() => {
|
||||
if (store.d3Zoom) {
|
||||
if (props.onMoveStart) {
|
||||
store.d3Zoom.on('start', (event: any) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
store.d3Zoom.on('start', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
|
||||
if (props.onMoveStart) props.onMoveStart(flowTransform);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
store.d3Zoom.on('start', null);
|
||||
}
|
||||
hooks.moveStart.trigger(flowTransform);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (store.d3Zoom) {
|
||||
if (props.onMoveEnd) {
|
||||
store.d3Zoom.on('end', (event: any) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
store.d3Zoom.on('end', (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
if (viewChanged(prevTransform.value, event.transform)) {
|
||||
const flowTransform = eventToFlowTransform(event.transform);
|
||||
prevTransform.value = flowTransform;
|
||||
|
||||
if (props.onMoveEnd) props.onMoveEnd(flowTransform);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
store.d3Zoom.on('end', null);
|
||||
}
|
||||
hooks.moveEnd.trigger(flowTransform);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
watchEffect(() => {
|
||||
if (store.d3Zoom) {
|
||||
store.d3Zoom.filter((event: any) => {
|
||||
store.d3Zoom.filter((event: MouseEvent) => {
|
||||
const zoomScroll = props.zoomOnScroll;
|
||||
const pinchZoom = props.zoomOnPinch && event.ctrlKey;
|
||||
|
||||
@@ -264,20 +232,20 @@ const ZoomPane = defineComponent({
|
||||
return false;
|
||||
}
|
||||
|
||||
if (event.target.closest('.nowheel') && event.type === 'wheel') {
|
||||
if ((event.target as Element).closest('.nowheel') && event.type === 'wheel') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// when the target element is a node, we still allow zooming
|
||||
if (
|
||||
(event.target.closest('.revue-flow__node') || event.target.closest('.revue-flow__edge')) &&
|
||||
((event.target as Element).closest('.revue-flow__node') || (event.target as Element).closest('.revue-flow__edge')) &&
|
||||
event.type !== 'wheel'
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// when the target element is a node selection, we still allow zooming
|
||||
if (event.target.closest('.revue-flow__nodesselection') && event.type !== 'wheel') {
|
||||
if ((event.target as Element).closest('.revue-flow__nodesselection') && event.type !== 'wheel') {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -302,11 +270,9 @@ const ZoomPane = defineComponent({
|
||||
});
|
||||
|
||||
return () => (
|
||||
<div class="revue-flow__renderer revue-flow__zoompane" ref={zoomPane}>
|
||||
<div class="revue-flow__renderer revue-flow__zoompane" ref="zoom-pane">
|
||||
{slots.default ? slots.default() : ''}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default ZoomPane;
|
||||
|
||||
@@ -44,7 +44,7 @@ export interface RevueFlowHooks {
|
||||
edgeUpdateEnd: EventHook<{ event: MouseEvent; edge: Edge }>;
|
||||
}
|
||||
|
||||
export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string, ...args: any[]) => void) => void } => {
|
||||
export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string, ...args: any[]) => void) => RevueFlowHooks } => {
|
||||
const eventHooks: RevueFlowHooks = {
|
||||
elementClick: createEventHook<{ event: MouseEvent; element: Node | Edge }>(),
|
||||
elementsRemove: createEventHook<Elements>(),
|
||||
@@ -93,6 +93,8 @@ export const useRevueFlow = (): RevueFlowHooks & { bind: (emit: (event: string,
|
||||
emit(key, data);
|
||||
});
|
||||
}
|
||||
|
||||
return eventHooks;
|
||||
};
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import { createEventHook, MaybeRef, unrefElement, until, useEventListener, useResizeObserver } from '@vueuse/core';
|
||||
import { getDimensions } from '../utils';
|
||||
import { Dimensions } from '../types';
|
||||
import { MaybeRef, unrefElement, until, useEventListener, useResizeObserver } from '@vueuse/core';
|
||||
|
||||
export default (rendererNode: MaybeRef<HTMLDivElement>, updateSize: (size: Dimensions) => any) => {
|
||||
const resizeHandlerHook = createEventHook();
|
||||
const updateDimensions = () => {
|
||||
if (!rendererNode) {
|
||||
if (!unrefElement(rendererNode)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -23,5 +24,10 @@ export default (rendererNode: MaybeRef<HTMLDivElement>, updateSize: (size: Dimen
|
||||
updateDimensions();
|
||||
useEventListener(window, 'resize', updateDimensions);
|
||||
useResizeObserver(unrefElement(rendererNode), () => updateDimensions());
|
||||
resizeHandlerHook.trigger(true);
|
||||
});
|
||||
|
||||
return {
|
||||
onReady: resizeHandlerHook.on
|
||||
};
|
||||
};
|
||||
|
||||
@@ -65,8 +65,7 @@ const useZoomPanHelper = (): { onReady: EventHookOn<ZoomPanHelperFunctions> } =>
|
||||
project: (position: XYPosition) => {
|
||||
const { transform, snapToGrid, snapGrid } = store;
|
||||
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
||||
},
|
||||
initialized: true
|
||||
}
|
||||
});
|
||||
|
||||
until(d3Zoom)
|
||||
|
||||
15
src/shims-tsx.d.ts
vendored
Normal file
15
src/shims-tsx.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import Vue, { VNode } from 'vue';
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
// tslint:disable no-empty-interface
|
||||
interface Element extends VNode {}
|
||||
|
||||
// @ts-ignore
|
||||
interface ElementClass extends Vue {}
|
||||
|
||||
interface IntrinsicElements {
|
||||
[elem: string]: any;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -666,7 +666,6 @@ export interface ZoomPanHelperFunctions {
|
||||
setCenter: (x: number, y: number, zoom?: number) => void;
|
||||
fitBounds: (bounds: Rect, padding?: number) => void;
|
||||
project: (position: XYPosition) => XYPosition;
|
||||
initialized: boolean;
|
||||
}
|
||||
|
||||
export type OnEdgeUpdateFunc<T = any> = (oldEdge: Edge<T>, newConnection: Connection) => void;
|
||||
|
||||
@@ -262,10 +262,10 @@
|
||||
"@babel/helper-validator-identifier" "^7.14.5"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@braks/revue-draggable@^0.2.4":
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/@braks/revue-draggable/-/revue-draggable-0.2.4.tgz#a3af0c27f32a1845f4182ae579ce80cc88fd9019"
|
||||
integrity sha512-8hDMuO7fl5KCshmg87aK3bQt371f3jPOZ35ip4gdcalP2fEnusVv8+AjWP1DHR2lObDTjovn30LzCK4Ily08LA==
|
||||
"@braks/revue-draggable@^0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://registry.yarnpkg.com/@braks/revue-draggable/-/revue-draggable-0.2.5.tgz#28a94ee4633aab25bcee28877511e0c9ad262f59"
|
||||
integrity sha512-iKGLFXaurfEqoyPbkANv8ZuO1229krVvUQDDYN9le5WlVssFWcQtA3hNvBD2TPuwil226qXv9SqXGp5kgDhAsA==
|
||||
dependencies:
|
||||
"@vueuse/core" "^5.1.3"
|
||||
vue-demi latest
|
||||
|
||||
Reference in New Issue
Block a user