diff --git a/examples/CustomNode/index.tsx b/examples/CustomNode/index.tsx index 05ca9a66..5ce2553a 100644 --- a/examples/CustomNode/index.tsx +++ b/examples/CustomNode/index.tsx @@ -29,7 +29,6 @@ const nodeTypes = { }; const CustomNodeFlow = defineComponent({ - components: { RevueFlow, MiniMap, Controls }, setup() { const elements = ref([]); const bgColor = ref(initBgColor); diff --git a/src/components/NodesSelection/index.tsx b/src/components/NodesSelection/index.tsx index d817c096..7cc8e644 100644 --- a/src/components/NodesSelection/index.tsx +++ b/src/components/NodesSelection/index.tsx @@ -4,7 +4,7 @@ */ import { isNode } from '../../utils/graph'; import { Node, RevueFlowStore } from '../../types'; -import { computed, inject, ref } from 'vue'; +import { computed, defineComponent, inject, PropType, ref } from 'vue'; import Draggable, { DraggableEventHandler } from '@braks/revue-draggable'; export interface NodesSelectionProps { @@ -14,89 +14,104 @@ export interface NodesSelectionProps { onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void; } -const NodesSelection = ({ - onSelectionDragStart, - onSelectionDrag, - onSelectionDragStop, - onSelectionContextMenu -}: NodesSelectionProps) => { - const store = inject('store'); - console.log('nodesSelection'); - - const nodeRef = ref(); - - const grid = computed(() => (store?.snapToGrid ? store?.snapGrid : [1, 1])! as [number, number]); - - const selectedNodes = computed(() => - store?.selectedElements - ? store?.selectedElements.filter(isNode).map((selectedNode) => { - const matchingNode = store?.nodes.find((node) => node.id === selectedNode.id); - - return { - ...matchingNode, - position: matchingNode?.__rf.position - } 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, - height: store?.selectedNodesBbox.height, - top: store?.selectedNodesBbox.y, - left: store?.selectedNodesBbox.x - })); - - const onStart: DraggableEventHandler = (event: MouseEvent) => { - onSelectionDragStart?.(event, selectedNodes.value); - }; - - const onDrag: DraggableEventHandler = (event, data) => { - if (onSelectionDrag) { - onSelectionDrag(event, selectedNodes.value); +const NodesSelection = defineComponent({ + props: { + onSelectionDragStart: { + type: Function as unknown as PropType, + required: false, + default: undefined + }, + onSelectionDrag: { + type: Function as unknown as PropType, + required: false, + default: undefined + }, + onSelectionDragStop: { + type: Function as unknown as PropType, + required: false, + default: undefined + }, + onSelectionContextMenu: { + type: Function as unknown as PropType, + required: false, + default: undefined } - console.log('dragging', data); - }; + }, + setup(props) { + const store = inject('store')!; + const nodeRef = ref(); + const grid = computed(() => (store.snapToGrid ? store.snapGrid : [1, 1])! as [number, number]); - const onStop: DraggableEventHandler = (event: MouseEvent) => { - store?.updateNodePosDiff({ - isDragging: false - }); + const selectedNodes = computed(() => + store.selectedElements + ? store.selectedElements.filter(isNode).map((selectedNode) => { + const matchingNode = store.nodes.find((node) => node.id === selectedNode.id); - onSelectionDragStop?.(event, selectedNodes.value); - }; + return { + ...matchingNode, + position: matchingNode?.__rf.position + } as Node; + }) + : [] + ); - const onContextMenu = (event: MouseEvent) => { - const selectedNodes = store?.selectedElements - ? store?.selectedElements.filter(isNode).map((selectedNode) => store?.nodes.find((node) => node.id === selectedNode.id)) - : []; + const style = computed(() => ({ + transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})` + })); - onSelectionContextMenu?.(event, selectedNodes as any); - }; + const innerStyle = computed(() => ({ + width: `${store.selectedNodesBbox.width}px`, + height: `${store.selectedNodesBbox.height}px`, + top: `${store.selectedNodesBbox.y}px`, + left: `${store.selectedNodesBbox.x}px` + })); - if (!store?.selectedElements || store?.selectionActive) { - return null; + const onStart: DraggableEventHandler = (event: MouseEvent) => { + props.onSelectionDragStart?.(event, selectedNodes.value); + }; + + const onDrag: DraggableEventHandler = (event) => { + if (props.onSelectionDrag) { + props.onSelectionDrag(event, selectedNodes.value); + } + }; + + const onStop: DraggableEventHandler = (event: MouseEvent) => { + store.updateNodePosDiff({ + isDragging: false + }); + + props.onSelectionDragStop?.(event, selectedNodes.value); + }; + + const onContextMenu = (event: MouseEvent) => { + const selectedNodes = store.selectedElements + ? store.selectedElements.filter(isNode).map((selectedNode) => store.nodes.find((node) => node.id === selectedNode.id)) + : []; + + props.onSelectionContextMenu?.(event, selectedNodes as any); + }; + + return () => { + return !store.selectedElements || store.selectionActive ? ( + '' + ) : ( +
+ +
+ +
+ ); + }; } - - return ( -
- -
- -
- ); -}; +}); export default NodesSelection; diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index ccfc16a7..78c01ab8 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -1,4 +1,4 @@ -import { defineComponent, inject, onBeforeMount, onMounted, ref, reactive, PropType, CSSProperties } from 'vue'; +import { defineComponent, inject, onBeforeMount, onMounted, ref, PropType, CSSProperties } from 'vue'; import FlowRenderer from '../FlowRenderer'; import NodeRenderer from '../NodeRenderer'; import EdgeRenderer from '../EdgeRenderer'; @@ -359,74 +359,6 @@ const GraphView = defineComponent({ } }, setup(props) { - const { - nodeTypes, - edgeTypes, - onMove, - onMoveStart, - onMoveEnd, - onLoad, - onElementClick, - onNodeDoubleClick, - onEdgeDoubleClick, - onNodeMouseEnter, - onNodeMouseMove, - onNodeMouseLeave, - onNodeContextMenu, - onNodeDragStart, - onNodeDrag, - onNodeDragStop, - onSelectionDragStart, - onSelectionDrag, - onSelectionDragStop, - onSelectionContextMenu, - connectionMode, - connectionLineType, - connectionLineStyle, - connectionLineComponent, - selectionKeyCode, - multiSelectionKeyCode, - zoomActivationKeyCode, - onElementsRemove, - deleteKeyCode, - onConnect, - onConnectStart, - onConnectStop, - onConnectEnd, - snapToGrid, - snapGrid, - onlyRenderVisibleElements, - nodesDraggable, - nodesConnectable, - elementsSelectable, - selectNodesOnDrag, - minZoom, - maxZoom, - defaultZoom, - defaultPosition, - translateExtent, - nodeExtent, - arrowHeadColor, - markerEndId, - zoomOnScroll, - zoomOnPinch, - panOnScroll, - panOnScrollSpeed, - panOnScrollMode, - zoomOnDoubleClick, - paneMoveable, - onPaneClick, - onPaneScroll, - onPaneContextMenu, - onEdgeUpdate, - onEdgeContextMenu, - onEdgeMouseEnter, - onEdgeMouseMove, - onEdgeMouseLeave, - edgeUpdaterRadius, - onEdgeUpdateStart, - onEdgeUpdateEnd - } = reactive(props); const store = inject('store'); const isInitialized = ref(false); @@ -434,8 +366,8 @@ const GraphView = defineComponent({ const { zoomIn, zoomOut, zoomTo, transform, fitView, initialized } = useZoomPanHelper(); if (!isInitialized.value && initialized) { - if (onLoad && store) { - onLoad({ + if (props.onLoad && store) { + props.onLoad({ fitView: (params = { padding: 0.1 }) => fitView(params), zoomIn, zoomOut, @@ -450,140 +382,137 @@ const GraphView = defineComponent({ } }); + onMounted(() => { + if (typeof props.nodesConnectable !== 'undefined') { + store?.setNodesConnectable(!!props.nodesConnectable); + } + if (typeof props.elementsSelectable !== 'undefined') { + store?.setElementsSelectable(!!props.elementsSelectable); + } + + if (typeof props.connectionMode !== 'undefined') { + store?.setConnectionMode(props.connectionMode); + } + }); + onBeforeMount(() => { - if (onConnect) { - store?.setOnConnect(onConnect); + if (props.onConnect) { + store?.setOnConnect(props.onConnect); } - if (onConnectStart) { - store?.setOnConnectStart(onConnectStart); + if (props.onConnectStart) { + store?.setOnConnectStart(props.onConnectStart); } - if (onConnectStop) { - store?.setOnConnectStop(onConnectStop); + if (props.onConnectStop) { + store?.setOnConnectStop(props.onConnectStop); } - if (onConnectEnd) { - store?.setOnConnectEnd(onConnectEnd); + if (props.onConnectEnd) { + store?.setOnConnectEnd(props.onConnectEnd); } - if (typeof snapToGrid !== 'undefined') { - store?.setSnapToGrid(snapToGrid); + if (typeof props.snapToGrid !== 'undefined') { + store?.setSnapToGrid(props.snapToGrid); } - if (typeof snapGrid !== 'undefined') { - store?.setSnapGrid(snapGrid); + if (typeof props.snapGrid !== 'undefined') { + store?.setSnapGrid(props.snapGrid); } - if (typeof nodesDraggable !== 'undefined') { - store?.setNodesDraggable(!!nodesDraggable); + if (typeof props.nodesDraggable !== 'undefined') { + store?.setNodesDraggable(!!props.nodesDraggable); } - if (typeof nodesConnectable !== 'undefined') { - store?.setNodesConnectable(!!nodesConnectable); + if (typeof props.nodesConnectable !== 'undefined') { + store?.setNodesConnectable(!!props.nodesConnectable); } - if (typeof elementsSelectable !== 'undefined') { - store?.setElementsSelectable(!!elementsSelectable); + if (typeof props.elementsSelectable !== 'undefined') { + store?.setElementsSelectable(!!props.elementsSelectable); } - if (typeof minZoom !== 'undefined') { - store?.setMinZoom(minZoom); + if (typeof props.minZoom !== 'undefined') { + store?.setMinZoom(props.minZoom); } - if (typeof maxZoom !== 'undefined') { - store?.setMaxZoom(maxZoom); + if (typeof props.maxZoom !== 'undefined') { + store?.setMaxZoom(props.maxZoom); } - if (typeof translateExtent !== 'undefined') { - store?.setTranslateExtent(translateExtent); + if (typeof props.translateExtent !== 'undefined') { + store?.setTranslateExtent(props.translateExtent); } - if (typeof nodeExtent !== 'undefined') { - store?.setNodeExtent(nodeExtent); - } - }); - - onMounted(() => { - if (typeof nodesConnectable !== 'undefined') { - store?.setNodesConnectable(!!nodesConnectable); - } - }); - - onMounted(() => { - if (typeof elementsSelectable !== 'undefined') { - store?.setElementsSelectable(!!elementsSelectable); - } - - if (typeof connectionMode !== 'undefined') { - store?.setConnectionMode(connectionMode); + if (typeof props.nodeExtent !== 'undefined') { + store?.setNodeExtent(props.nodeExtent); } }); return () => ( ); diff --git a/src/container/NodeRenderer/index.tsx b/src/container/NodeRenderer/index.tsx index 19e17b07..59c28ae7 100644 --- a/src/container/NodeRenderer/index.tsx +++ b/src/container/NodeRenderer/index.tsx @@ -94,27 +94,27 @@ const NodeRenderer = defineComponent({ } }, setup(props) { - const store = inject('store'); + const store = inject('store')!; const visibleNodes = computed(() => { return props.onlyRenderVisibleElements - ? store?.nodes && + ? store.nodes && getNodesInside( - store?.nodes, + store.nodes, { x: 0, y: 0, width: store?.width, height: store?.height }, - store?.transform, + store.transform, true ) - : store?.nodes; + : store.nodes; }); const transformStyle = computed(() => ({ - transform: `translate(${store?.transform[0]}px,${store?.transform[1]}px) scale(${store?.transform[2]})` + transform: `translate(${store.transform[0]}px,${store.transform[1]}px) scale(${store.transform[2]})` })); const resizeObserver = computed(() => { @@ -128,7 +128,7 @@ const NodeRenderer = defineComponent({ nodeElement: entry.target as HTMLDivElement })); - store?.updateNodeDimensions(updates); + store.updateNodeDimensions(updates); }); }); diff --git a/src/container/RevueFlow/index.tsx b/src/container/RevueFlow/index.tsx index 900dc297..ea4e06b2 100644 --- a/src/container/RevueFlow/index.tsx +++ b/src/container/RevueFlow/index.tsx @@ -1,14 +1,4 @@ -import { - computed, - CSSProperties, - defineComponent, - HTMLAttributes, - onBeforeUnmount, - PropType, - provide, - reactive, - watchEffect -} from 'vue'; +import { computed, CSSProperties, defineComponent, HTMLAttributes, onBeforeUnmount, PropType, provide, watchEffect } from 'vue'; import GraphView from '../GraphView'; import DefaultNode from '../../components/Nodes/DefaultNode'; import InputNode from '../../components/Nodes/InputNode'; @@ -548,164 +538,92 @@ const RevueFlow = defineComponent({ */ }, setup(props, { slots }) { - const { - elements = [], - nodeTypes = defaultNodeTypes, - edgeTypes = defaultEdgeTypes, - onElementClick, - onLoad, - onMove, - onMoveStart, - onMoveEnd, - onElementsRemove, - onConnect, - onConnectStart, - onConnectStop, - onConnectEnd, - onNodeMouseEnter, - onNodeMouseMove, - onNodeMouseLeave, - onNodeContextMenu, - onNodeDoubleClick, - onNodeDragStart, - onNodeDrag, - onNodeDragStop, - onSelectionChange, - onSelectionDragStart, - onSelectionDrag, - onSelectionDragStop, - onSelectionContextMenu, - connectionMode = ConnectionMode.Strict, - connectionLineType = ConnectionLineType.Bezier, - connectionLineStyle, - connectionLineComponent, - deleteKeyCode = 'Backspace', - selectionKeyCode = 'Shift', - multiSelectionKeyCode = 'Meta', - zoomActivationKeyCode = 'Meta', - snapToGrid = false, - snapGrid = [15, 15], - onlyRenderVisibleElements = false, - selectNodesOnDrag = true, - nodesDraggable, - nodesConnectable, - elementsSelectable, - minZoom, - maxZoom, - defaultZoom = 1, - defaultPosition = [0, 0], - translateExtent, - nodeExtent, - arrowHeadColor = '#b1b1b7', - markerEndId, - zoomOnScroll = true, - zoomOnPinch = true, - panOnScroll = false, - panOnScrollSpeed = 0.5, - panOnScrollMode = PanOnScrollMode.Free, - zoomOnDoubleClick = true, - paneMoveable = true, - onPaneClick, - onPaneScroll, - onPaneContextMenu, - onEdgeUpdate, - onEdgeContextMenu, - onEdgeDoubleClick, - onEdgeMouseEnter, - onEdgeMouseMove, - onEdgeMouseLeave, - onEdgeUpdateStart, - onEdgeUpdateEnd, - edgeUpdaterRadius = 10, - nodeTypesId = '1', - edgeTypesId = '1', - ...rest - } = reactive(props); const store = configureStore(initialState)(); provide('store', store); - store.setElements(elements); + store.setElements(props.elements); watchEffect(() => { - store.setElements(elements); - onSelectionChange?.(store.selectedElements); + store.setElements(props.elements); + props.onSelectionChange?.(store.selectedElements); }); + onBeforeUnmount(() => { store.$reset(); store.setElements([]); }); - const nodeTypesParsed = computed(() => nodeTypes && createNodeTypes(nodeTypes)); - const edgeTypesParsed = computed(() => edgeTypes && createEdgeTypes(edgeTypes)); + const nodeTypesParsed = computed(() => props.nodeTypes && createNodeTypes(props.nodeTypes)); + const edgeTypesParsed = computed(() => props.edgeTypes && createEdgeTypes(props.edgeTypes)); return () => ( -
+
{slots.default ? slots.default() : ''}
diff --git a/src/container/ZoomPane/index.tsx b/src/container/ZoomPane/index.tsx index f3edb571..a2ddef0d 100644 --- a/src/container/ZoomPane/index.tsx +++ b/src/container/ZoomPane/index.tsx @@ -3,7 +3,7 @@ import { select, pointer } from 'd3-selection'; import { clamp } from '../../utils'; import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode } from '../../types'; -import { defineComponent, onMounted, PropType, ref, watch, watchEffect } from 'vue'; +import { defineComponent, PropType, ref, watch, watchEffect } from 'vue'; import store from '../../store'; import useKeyPress from '../../hooks/useKeyPress'; import useResizeHandler from '../../hooks/useResizeHandler'; @@ -129,7 +129,7 @@ const ZoomPane = defineComponent({ if (zoomPane.value) useResizeHandler(zoomPane.value); }); - onMounted(() => { + watchEffect(() => { if (zoomPane.value && props.defaultPosition && props.defaultZoom) { const currentTranslateExtent = typeof props.translateExtent !== 'undefined' ? props.translateExtent : pinia.translateExtent; @@ -153,7 +153,7 @@ const ZoomPane = defineComponent({ } }); - onMounted(() => { + watchEffect(() => { if (pinia.d3Selection && pinia.d3Zoom) { if (props.panOnScroll && zoomActivationKeyPressed) { pinia.d3Selection diff --git a/src/store/configure-store.ts b/src/store/configure-store.ts index fbca1eba..5d1f4526 100644 --- a/src/store/configure-store.ts +++ b/src/store/configure-store.ts @@ -17,7 +17,7 @@ export default function configureStore( setActivePinia(pinia); return defineStore({ - id: 'revue-flow-' + pinia.state.value.length + 1, + id: 'revue-flow-' + Math.random(), state: () => preloadedState, getters: {}, actions: {