From 13fed0a3fb3b74d3e910158574f33229202d06c9 Mon Sep 17 00:00:00 2001 From: braks <78412429+bcakmakoglu@users.noreply.github.com> Date: Mon, 10 Oct 2022 18:28:24 +0200 Subject: [PATCH] refactor(edges): move edge event handlers into Wrapper component --- examples/vite/src/Stress/StressExample.vue | 4 +- packages/core/src/components/Edges/Wrapper.ts | 203 ++++++++++-------- .../container/EdgeRenderer/EdgeRenderer.vue | 112 +--------- 3 files changed, 121 insertions(+), 198 deletions(-) diff --git a/examples/vite/src/Stress/StressExample.vue b/examples/vite/src/Stress/StressExample.vue index 4de5c430..4f7fc0c0 100644 --- a/examples/vite/src/Stress/StressExample.vue +++ b/examples/vite/src/Stress/StressExample.vue @@ -4,7 +4,7 @@ import { getElements } from './utils' const { nodes, edges } = getElements(10, 10) -const { getNode, onPaneReady } = useVueFlow({ +const { onPaneReady } = useVueFlow({ nodes, edges, }) @@ -13,6 +13,8 @@ onPaneReady((i) => { i.fitView({ padding: 0.2, }) + + console.log(i.getEdges.value) console.log(i.getElements.value) }) diff --git a/packages/core/src/components/Edges/Wrapper.ts b/packages/core/src/components/Edges/Wrapper.ts index 04165b5f..74bd7d68 100644 --- a/packages/core/src/components/Edges/Wrapper.ts +++ b/packages/core/src/components/Edges/Wrapper.ts @@ -1,111 +1,130 @@ -import type { CSSProperties, Component, VNode } from 'vue' import EdgeAnchor from './EdgeAnchor' -import type { EdgeComponent, EdgeEventsOn, EdgeMarkerType, EdgeTextProps, EdgeUpdatable, GraphNode } from '~/types' +import type { Connection, EdgeComponent, EdgeUpdatable, GraphEdge, GraphNode, HandleType } from '~/types' import { ConnectionMode, Position } from '~/types' -import { getEdgePositions, getHandle, getMarkerId } from '~/utils' +import { connectionExists, getEdgePositions, getHandle, getMarkerId } from '~/utils' import { EdgeId, EdgeRef } from '~/context' +import { useEdgeHooks, useHandle, useVueFlow } from '~/composables' interface Props { id: string type: EdgeComponent | Function | Object | false name: string - source: string - target: string - sourceNode?: GraphNode - targetNode?: GraphNode - targetHandleId?: string | null - sourceHandleId?: string | null selectable?: boolean updatable?: EdgeUpdatable - label?: string | VNode | Component | Object - data?: any - events: EdgeEventsOn - labelStyle?: CSSProperties - labelShowBg?: boolean - labelBgStyle?: any - labelBgPadding?: [number, number] - labelBgBorderRadius?: number - animated?: boolean - selected?: boolean - style: CSSProperties - markerEnd?: EdgeMarkerType - markerStart?: EdgeMarkerType - connectionMode: ConnectionMode - edgeUpdaterRadius: number - updating?: boolean + edge: GraphEdge + sourceNode: GraphNode + targetNode: GraphNode } const Wrapper = defineComponent({ - props: [ - 'name', - 'type', - 'id', - 'data', - 'events', - 'labelBgBorderRadius', - 'labelBgPadding', - 'labelBgStyle', - 'labelStyle', - 'labelShowBg', - 'style', - 'animated', - 'label', - 'updatable', - 'selectable', - 'target', - 'source', - 'sourceNode', - 'targetNode', - 'sourceHandleId', - 'targetHandleId', - 'selected', - 'markerEnd', - 'markerStart', - 'connectionMode', - 'edgeUpdaterRadius', - 'updating', - ], - emits: ['source-mousedown', 'target-mousedown'], - setup(props: Props, { emit }) { + props: ['name', 'type', 'id', 'updatable', 'selectable', 'edge', 'sourceNode', 'targetNode'], + setup(props: Props) { + const { addSelectedEdges, connectionMode, edgeUpdaterRadius, emits, nodesSelectionActive, getEdges } = useVueFlow() + + const hooks = useEdgeHooks(props.edge, emits) + + const edge = $(useVModel(props, 'edge')) + + let mouseOver = $ref(false) let updating = $ref(false) + const nodeId = ref('') + const handleId = ref(null) + const type = ref('source') + const elementEdgeUpdaterType = ref('source') + const mouseEvent = ref() + const edgeEl = ref() provide(EdgeId, props.id) provide(EdgeRef, edgeEl) - const onEdgeUpdaterMouseEnter = () => (updating = true) + const onEdgeUpdaterMouseEnter = () => (mouseOver = true) - const onEdgeUpdaterMouseOut = () => (updating = false) + const onEdgeUpdaterMouseOut = () => (mouseOver = false) - const onEdgeUpdaterSourceMouseDown = (e: MouseEvent) => { - emit('source-mousedown', e) + const onEdgeUpdate = (connection: Connection) => { + if (!connectionExists(connection, getEdges.value)) hooks.emit.update({ edge, connection }) } - const onEdgeUpdaterTargetMouseDown = (e: MouseEvent) => { - emit('target-mousedown', e) + const onEdgeUpdateEnd = () => { + if (!mouseEvent.value) return + hooks.emit.updateEnd({ event: mouseEvent.value, edge }) + updating = false + } + + const { onMouseDown } = useHandle({ + nodeId, + handleId, + type, + isValidConnection: undefined, + elementEdgeUpdaterType, + onEdgeUpdate, + onEdgeUpdateEnd, + }) + + const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => { + nodeId.value = isSourceHandle ? edge.target : edge.source + handleId.value = (isSourceHandle ? edge.targetHandle : edge.sourceHandle) ?? '' + type.value = isSourceHandle ? 'target' : 'source' + elementEdgeUpdaterType.value = type.value + mouseEvent.value = event + + hooks.emit.updateStart({ event, edge }) + + onMouseDown(event) + } + + const onEdgeClick = (event: MouseEvent) => { + const data = { event, edge } + if (props.selectable) { + nodesSelectionActive.value = false + + addSelectedEdges([edge]) + } + hooks.emit.click(data) + } + + const onEdgeContextMenu = (event: MouseEvent) => hooks.emit.contextMenu({ event, edge }) + + const onDoubleClick = (event: MouseEvent) => hooks.emit.doubleClick({ event, edge }) + + const onEdgeMouseEnter = (event: MouseEvent) => hooks.emit.mouseEnter({ event, edge }) + + const onEdgeMouseMove = (event: MouseEvent) => hooks.emit.mouseMove({ event, edge }) + + const onEdgeMouseLeave = (event: MouseEvent) => hooks.emit.mouseLeave({ event, edge }) + + const onEdgeUpdaterSourceMouseDown = (event: MouseEvent) => { + updating = true + handleEdgeUpdater(event, true) + } + + const onEdgeUpdaterTargetMouseDown = (event: MouseEvent) => { + updating = true + handleEdgeUpdater(event, false) } return () => { if (!props.sourceNode || !props.targetNode) return null let sourceNodeHandles - if (props.connectionMode === ConnectionMode.Strict) { + if (connectionMode.value === ConnectionMode.Strict) { sourceNodeHandles = props.sourceNode.handleBounds.source } else { sourceNodeHandles = props.sourceNode.handleBounds.source ?? props.sourceNode.handleBounds.target } - const sourceHandle = getHandle(sourceNodeHandles, props.sourceHandleId) + const sourceHandle = getHandle(sourceNodeHandles, edge.sourceHandle) let targetNodeHandles - if (props.connectionMode === ConnectionMode.Strict) { + if (connectionMode.value === ConnectionMode.Strict) { targetNodeHandles = props.targetNode.handleBounds.target } else { targetNodeHandles = props.targetNode.handleBounds.target ?? props.targetNode.handleBounds.source } - const targetHandle = getHandle(targetNodeHandles, props.targetHandleId) + const targetHandle = getHandle(targetNodeHandles, edge.targetHandle) const sourcePosition = sourceHandle ? sourceHandle.position : Position.Bottom @@ -129,44 +148,50 @@ const Wrapper = defineComponent({ 'vue-flow__edge', `vue-flow__edge-${props.name}`, { - updating, - selected: props.selected, - animated: props.animated, + updating: mouseOver, + selected: edge.selected, + animated: edge.animated, inactive: !props.selectable, }, ], + 'onClick': onEdgeClick, + 'onContextmenu': onEdgeContextMenu, + 'onDblclick': onDoubleClick, + 'onMouseenter': onEdgeMouseEnter, + 'onMousemove': onEdgeMouseMove, + 'onMouseleave': onEdgeMouseLeave, }, [ - props.updating + updating ? null : h(props.type as any, { id: props.id, sourceNode: props.sourceNode, targetNode: props.targetNode, - source: props.source, - target: props.target, + source: edge.source, + target: edge.target, updatable: props.updatable, - selected: props.selected, - animated: props.animated, - label: props.label, - labelStyle: props.labelStyle, - labelShowBg: props.labelShowBg, - labelBgStyle: props.labelBgStyle, - labelBgPadding: props.labelBgPadding, - labelBgBorderRadius: props.labelBgBorderRadius, - data: props.data, - events: props.events, - style: props.style, - markerStart: `url(#${getMarkerId(props.markerStart)})`, - markerEnd: `url(#${getMarkerId(props.markerEnd)})`, + selected: edge.selected, + animated: edge.animated, + label: edge.label, + labelStyle: edge.labelStyle, + labelShowBg: edge.labelShowBg, + labelBgStyle: edge.labelBgStyle, + labelBgPadding: edge.labelBgPadding, + labelBgBorderRadius: edge.labelBgBorderRadius, + data: edge.data, + events: { ...edge.events, ...hooks.on }, + style: edge.style, + markerStart: `url(#${getMarkerId(edge.markerStart)})`, + markerEnd: `url(#${getMarkerId(edge.markerEnd)})`, sourcePosition, targetPosition, sourceX, sourceY, targetX, targetY, - sourceHandleId: props.sourceHandleId, - targetHandleId: props.targetHandleId, + sourceHandleId: edge.sourceHandle, + targetHandleId: edge.targetHandle, }), [ @@ -183,7 +208,7 @@ const Wrapper = defineComponent({ 'position': sourcePosition, 'centerX': sourceX, 'centerY': sourceY, - 'radius': props.edgeUpdaterRadius, + 'radius': edgeUpdaterRadius.value, 'data-type': 'source', }), ), @@ -202,7 +227,7 @@ const Wrapper = defineComponent({ 'position': targetPosition, 'centerX': targetX, 'centerY': targetY, - 'radius': props.edgeUpdaterRadius, + 'radius': edgeUpdaterRadius.value, 'data-type': 'target', }), ), diff --git a/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue b/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue index b8ba2339..8b6d9fc4 100644 --- a/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue +++ b/packages/core/src/container/EdgeRenderer/EdgeRenderer.vue @@ -2,9 +2,9 @@ import type { CSSProperties, EffectScope } from 'vue' import EdgeWrapper from '../../components/Edges/Wrapper' import ConnectionLine from '../../components/ConnectionLine/ConnectionLine.vue' -import { useEdgeHooks, useHandle, useVueFlow } from '../../composables' -import { connectionExists, groupEdgesByZLevel } from '../../utils' -import type { Connection, EdgeComponent, EdgeUpdatable, GraphEdge, HandleType } from '../../types' +import { useVueFlow } from '../../composables' +import { groupEdgesByZLevel } from '../../utils' +import type { EdgeComponent, EdgeUpdatable, GraphEdge } from '../../types' import { Slots } from '../../context' import MarkerDefinitions from './MarkerDefinitions.vue' @@ -34,8 +34,6 @@ const selectable = (s?: boolean) => (typeof s === 'undefined' ? elementsSelectab const updatable = (u?: EdgeUpdatable) => (typeof u === 'undefined' ? edgesUpdatable : u) -const updating = ref() - const sourceNode = $( controlledComputed( () => connectionStartHandle?.nodeId, @@ -59,8 +57,6 @@ const connectionLineVisible = $( ), ) -const hooks = $ref>>({}) - let groups = $ref>([]) let scope: EffectScope | null = effectScope() @@ -72,11 +68,6 @@ onPaneReady(() => { watch( [$$(getSelectedNodes), $$(getEdges)], () => { - getEdges.forEach((edge) => { - if (hooks[edge.id]) return - hooks[edge.id] = useEdgeHooks(edge, emits) - }) - if (elevateEdgesOnSelect) { nextTick(() => (groups = groupEdgesByZLevel(getEdges, getNode))) } else { @@ -123,75 +114,6 @@ const getType = (type?: string, template?: GraphEdge['template']) => { return slot } -const onEdgeClick = (event: MouseEvent, edge: GraphEdge) => { - const data = { event, edge } - if (selectable(edge.selectable)) { - $$(nodesSelectionActive).value = false - - addSelectedEdges([edge]) - } - hooks[edge.id].emit.click(data) -} - -const onEdgeContextMenu = (event: MouseEvent, edge: GraphEdge) => hooks[edge.id].emit.contextMenu({ event, edge }) - -const onDoubleClick = (event: MouseEvent, edge: GraphEdge) => hooks[edge.id].emit.doubleClick({ event, edge }) - -const onEdgeMouseEnter = (event: MouseEvent, edge: GraphEdge) => hooks[edge.id].emit.mouseEnter({ event, edge }) - -const onEdgeMouseMove = (event: MouseEvent, edge: GraphEdge) => hooks[edge.id].emit.mouseMove({ event, edge }) - -const onEdgeMouseLeave = (event: MouseEvent, edge: GraphEdge) => hooks[edge.id].emit.mouseLeave({ event, edge }) - -const onEdgeUpdaterSourceMouseDown = (event: MouseEvent, edge: GraphEdge) => { - updating.value = edge.id - handleEdgeUpdater(event, edge, true) -} - -const onEdgeUpdaterTargetMouseDown = (event: MouseEvent, edge: GraphEdge) => { - updating.value = edge.id - handleEdgeUpdater(event, edge, false) -} - -const nodeId = ref('') -const handleId = ref(null) -const type = ref('source') -const elementEdgeUpdaterType = ref('source') -const toUpdate = ref() -const mouseEvent = ref() -const onEdgeUpdate = (connection: Connection) => { - if (!connectionExists(connection, getEdges) && toUpdate.value) - hooks[toUpdate.value.id].emit.update({ edge: toUpdate.value, connection }) -} -const onEdgeUpdateEnd = () => { - if (!toUpdate.value || !mouseEvent.value) return - hooks[toUpdate.value.id].emit.updateEnd({ event: mouseEvent.value, edge: toUpdate.value }) - updating.value = '' -} - -const { onMouseDown } = useHandle({ - nodeId, - handleId, - type, - isValidConnection: undefined, - elementEdgeUpdaterType, - onEdgeUpdate, - onEdgeUpdateEnd, -}) - -const handleEdgeUpdater = (event: MouseEvent, edge: GraphEdge, isSourceHandle: boolean) => { - nodeId.value = isSourceHandle ? edge.target : edge.source - handleId.value = (isSourceHandle ? edge.targetHandle : edge.sourceHandle) ?? '' - type.value = isSourceHandle ? 'target' : 'source' - elementEdgeUpdaterType.value = type.value - toUpdate.value = edge - mouseEvent.value = event - - hooks[edge.id].emit.updateStart({ event, edge }) - - onMouseDown(event) -} - const getClass = (edge: GraphEdge) => { const extraClass = edge.class instanceof Function ? edge.class(edge) : edge.class return [noPanClassName, extraClass] @@ -214,41 +136,15 @@ export default { v-for="edge of group.edges" :id="edge.id" :key="edge.id" + :edge="edge" :type="getType(edge.type, edge.template)" :name="edge.type || 'default'" - :source="edge.source" - :target="edge.target" - :target-handle-id="edge.targetHandle" - :source-handle-id="edge.sourceHandle" :source-node="getNode(edge.source)" :target-node="getNode(edge.target)" - :label="edge.label" - :data="edge.data" - :events="{ ...edge.events, ...hooks[edge.id].on }" - :animated="edge.animated" :selectable="selectable(edge.selectable)" - :selected="edge.selected" :updatable="updatable(edge.updatable)" - :updating="edge.id === updating" - :label-style="edge.labelStyle" - :label-show-bg="edge.labelShowBg" - :label-bg-style="edge.labelBgStyle" - :label-bg-padding="edge.labelBgPadding" - :label-bg-border-radius="edge.labelBgBorderRadius" - :connection-mode="connectionMode" - :edge-updater-radius="edgeUpdaterRadius" - :marker-end="edge.markerEnd" - :marker-start="edge.markerStart" :style="getStyle(edge)" :class="getClass(edge)" - @click="onEdgeClick($event, edge)" - @dblclick="onDoubleClick($event, edge)" - @contextmenu="onEdgeContextMenu($event, edge)" - @mouseenter="onEdgeMouseEnter($event, edge)" - @mousemove="onEdgeMouseMove($event, edge)" - @mouseleave="onEdgeMouseLeave($event, edge)" - @source-mousedown="onEdgeUpdaterSourceMouseDown($event, edge)" - @target-mousedown="onEdgeUpdaterTargetMouseDown($event, edge)" />