feat(edges): add contextual edge events
# What's changed? * allow event handlers to be passed in initial edge definition * allow event listeners to be forwarded to edge components
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import type { CSSProperties, Component, FunctionalComponent, VNode } from 'vue'
|
||||
import EdgeAnchor from './EdgeAnchor'
|
||||
import type { EdgeComponent, EdgeMarkerType, EdgeTextProps, GraphNode } from '~/types'
|
||||
import type { EdgeComponent, EdgeEventsOn, EdgeMarkerType, EdgeTextProps, GraphNode } from '~/types'
|
||||
import { ConnectionMode, Position } from '~/types'
|
||||
import { getEdgePositions, getHandle, getMarkerId } from '~/utils'
|
||||
|
||||
@@ -18,6 +18,7 @@ interface Props {
|
||||
updatable?: boolean
|
||||
label?: string | VNode | Component<EdgeTextProps> | Object
|
||||
data?: any
|
||||
events: EdgeEventsOn
|
||||
labelStyle?: CSSProperties
|
||||
labelShowBg?: boolean
|
||||
labelBgStyle?: any
|
||||
@@ -38,6 +39,7 @@ const Wrapper: FunctionalComponent<Props> = function (
|
||||
type,
|
||||
id,
|
||||
data,
|
||||
events,
|
||||
labelBgBorderRadius,
|
||||
labelBgPadding,
|
||||
labelBgStyle,
|
||||
@@ -140,6 +142,7 @@ const Wrapper: FunctionalComponent<Props> = function (
|
||||
labelBgPadding,
|
||||
labelBgBorderRadius,
|
||||
data,
|
||||
events,
|
||||
style,
|
||||
markerStart: `url(#${getMarkerId(markerStart)})`,
|
||||
markerEnd: `url(#${getMarkerId(markerEnd)})`,
|
||||
@@ -193,6 +196,7 @@ Wrapper.props = [
|
||||
'type',
|
||||
'id',
|
||||
'data',
|
||||
'events',
|
||||
'labelBgBorderRadius',
|
||||
'labelBgPadding',
|
||||
'labelBgStyle',
|
||||
|
||||
@@ -5,3 +5,4 @@ export { default as useWindow } from './useWindow'
|
||||
export { default as useVueFlow } from './useVueFlow'
|
||||
export { default as useDrag } from './useDrag'
|
||||
export { default as useNodeHooks } from './useNodeHooks'
|
||||
export { default as useEdgeHooks } from './useEdgeHooks'
|
||||
|
||||
72
packages/vue-flow/src/composables/useEdgeHooks.ts
Normal file
72
packages/vue-flow/src/composables/useEdgeHooks.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import type { EdgeEventsEmit, EdgeEventsOn, GraphEdge, VueFlowStore } from '~/types'
|
||||
|
||||
const createEdgeHooks = () => ({
|
||||
doubleClick: createEventHook(),
|
||||
click: createEventHook(),
|
||||
mouseEnter: createEventHook(),
|
||||
mouseMove: createEventHook(),
|
||||
mouseLeave: createEventHook(),
|
||||
contextMenu: createEventHook(),
|
||||
updateStart: createEventHook(),
|
||||
update: createEventHook(),
|
||||
updateEnd: createEventHook(),
|
||||
})
|
||||
|
||||
export default function useEdgeHooks(edge: GraphEdge, emits: VueFlowStore['emits']): { emit: EdgeEventsEmit; on: EdgeEventsOn } {
|
||||
const edgeHooks = createEdgeHooks()
|
||||
|
||||
edgeHooks.doubleClick.on((event) => {
|
||||
emits.edgeDoubleClick(event)
|
||||
edge.events?.doubleClick?.(event)
|
||||
})
|
||||
|
||||
edgeHooks.click.on((event) => {
|
||||
emits.edgeClick(event)
|
||||
edge.events?.click?.(event)
|
||||
})
|
||||
|
||||
edgeHooks.mouseEnter.on((event) => {
|
||||
emits.edgeMouseEnter(event)
|
||||
edge.events?.mouseEnter?.(event)
|
||||
})
|
||||
|
||||
edgeHooks.mouseMove.on((event) => {
|
||||
emits.edgeMouseMove(event)
|
||||
edge.events?.mouseMove?.(event)
|
||||
})
|
||||
|
||||
edgeHooks.mouseLeave.on((event) => {
|
||||
emits.edgeMouseLeave(event)
|
||||
edge.events?.mouseLeave?.(event)
|
||||
})
|
||||
|
||||
edgeHooks.contextMenu.on((event) => {
|
||||
emits.edgeContextMenu(event)
|
||||
edge.events?.contextMenu?.(event)
|
||||
})
|
||||
|
||||
edgeHooks.updateStart.on((event) => {
|
||||
emits.edgeUpdateStart(event)
|
||||
edge.events?.updateStart?.(event)
|
||||
})
|
||||
|
||||
edgeHooks.update.on((event) => {
|
||||
emits.edgeUpdate(event)
|
||||
edge.events?.update?.(event)
|
||||
})
|
||||
|
||||
edgeHooks.updateEnd.on((event) => {
|
||||
emits.edgeUpdateEnd(event)
|
||||
edge.events?.updateEnd?.(event)
|
||||
})
|
||||
|
||||
return Object.entries(edgeHooks).reduce(
|
||||
(hooks, [key, value]) => {
|
||||
hooks.emit[key as keyof EdgeEventsEmit] = value.trigger
|
||||
hooks.on[key as keyof EdgeEventsOn] = value.on
|
||||
|
||||
return hooks
|
||||
},
|
||||
{ emit: {} as EdgeEventsEmit, on: {} as EdgeEventsOn },
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
import type { CSSProperties } from 'vue'
|
||||
import EdgeWrapper from '../../components/Edges/Wrapper'
|
||||
import ConnectionLine from '../../components/ConnectionLine/ConnectionLine.vue'
|
||||
import { useHandle, useVueFlow } from '../../composables'
|
||||
import { useEdgeHooks, useHandle, useVueFlow } from '../../composables'
|
||||
import { connectionExists, groupEdgesByZLevel } from '../../utils'
|
||||
import type { EdgeComponent, GraphEdge } from '../../types'
|
||||
import { Slots } from '../../context'
|
||||
@@ -55,6 +55,8 @@ const connectionLineVisible = $(
|
||||
),
|
||||
)
|
||||
|
||||
const hooks = $ref<Record<string, ReturnType<typeof useEdgeHooks>>>({})
|
||||
|
||||
let groups = $ref<ReturnType<typeof groupEdgesByZLevel>>([])
|
||||
|
||||
const scope = effectScope()
|
||||
@@ -64,6 +66,12 @@ onPaneReady(() => {
|
||||
watch(
|
||||
[$$(getSelectedNodes), $$(getEdges), $$(getNodes)],
|
||||
() => {
|
||||
getEdges.reduce((acc, edge) => {
|
||||
if (hooks[edge.id]) return acc
|
||||
hooks[edge.id] = useEdgeHooks(edge, emits)
|
||||
return acc
|
||||
}, hooks)
|
||||
|
||||
if (elevateEdgesOnSelect) {
|
||||
nextTick(() => (groups = groupEdgesByZLevel(getEdges, getNode)))
|
||||
} else {
|
||||
@@ -117,18 +125,18 @@ const onEdgeClick = (event: MouseEvent, edge: GraphEdge) => {
|
||||
|
||||
addSelectedEdges([edge])
|
||||
}
|
||||
emits.edgeClick(data)
|
||||
hooks[edge.id].emit.click(data)
|
||||
}
|
||||
|
||||
const onEdgeContextMenu = (event: MouseEvent, edge: GraphEdge) => emits.edgeContextMenu({ event, edge })
|
||||
const onEdgeContextMenu = (event: MouseEvent, edge: GraphEdge) => hooks[edge.id].emit.contextMenu({ event, edge })
|
||||
|
||||
const onDoubleClick = (event: MouseEvent, edge: GraphEdge) => emits.edgeDoubleClick({ event, edge })
|
||||
const onDoubleClick = (event: MouseEvent, edge: GraphEdge) => hooks[edge.id].emit.doubleClick({ event, edge })
|
||||
|
||||
const onEdgeMouseEnter = (event: MouseEvent, edge: GraphEdge) => emits.edgeMouseEnter({ event, edge })
|
||||
const onEdgeMouseEnter = (event: MouseEvent, edge: GraphEdge) => hooks[edge.id].emit.mouseEnter({ event, edge })
|
||||
|
||||
const onEdgeMouseMove = (event: MouseEvent, edge: GraphEdge) => emits.edgeMouseMove({ event, edge })
|
||||
const onEdgeMouseMove = (event: MouseEvent, edge: GraphEdge) => hooks[edge.id].emit.mouseMove({ event, edge })
|
||||
|
||||
const onEdgeMouseLeave = (event: MouseEvent, edge: GraphEdge) => emits.edgeMouseLeave({ event, edge })
|
||||
const onEdgeMouseLeave = (event: MouseEvent, edge: GraphEdge) => hooks[edge.id].emit.mouseLeave({ event, edge })
|
||||
|
||||
const onEdgeUpdaterSourceMouseDown = (event: MouseEvent, edge: GraphEdge) => handleEdgeUpdater(event, edge, true)
|
||||
|
||||
@@ -140,7 +148,7 @@ const handleEdgeUpdater = (event: MouseEvent, edge: GraphEdge, isSourceHandle: b
|
||||
const nodeId = isSourceHandle ? edge.target : edge.source
|
||||
const handleId = (isSourceHandle ? edge.targetHandle : edge.sourceHandle) ?? ''
|
||||
|
||||
emits.edgeUpdateStart({ event, edge })
|
||||
hooks[edge.id].emit.updateStart({ event, edge })
|
||||
|
||||
onMouseDown(
|
||||
event,
|
||||
@@ -150,9 +158,9 @@ const handleEdgeUpdater = (event: MouseEvent, edge: GraphEdge, isSourceHandle: b
|
||||
undefined,
|
||||
isSourceHandle ? 'target' : 'source',
|
||||
(connection) => {
|
||||
if (!connectionExists(connection, getEdges)) emits.edgeUpdate({ edge, connection })
|
||||
if (!connectionExists(connection, getEdges)) hooks[edge.id].emit.update({ edge, connection })
|
||||
},
|
||||
() => emits.edgeUpdateEnd({ event, edge }),
|
||||
() => hooks[edge.id].emit.updateEnd({ event, edge }),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -188,6 +196,7 @@ export default {
|
||||
: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"
|
||||
@@ -204,7 +213,7 @@ export default {
|
||||
:style="getStyle(edge)"
|
||||
:class="getClass(edge)"
|
||||
@click="onEdgeClick($event, edge)"
|
||||
@dbl-click="onDoubleClick($event, edge)"
|
||||
@dblclick="onDoubleClick($event, edge)"
|
||||
@contextmenu="onEdgeContextMenu($event, edge)"
|
||||
@mouseenter="onEdgeMouseEnter($event, edge)"
|
||||
@mousemove="onEdgeMouseMove($event, edge)"
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { CSSProperties, Component, VNode } from 'vue'
|
||||
import type { ClassFunc, ElementData, Position, StyleFunc, Styles } from './flow'
|
||||
import type { GraphNode } from './node'
|
||||
import type { DefaultEdgeTypes, EdgeComponent, EdgeTextProps } from './components'
|
||||
import type { CustomEvent, EdgeEventsHandler, EdgeEventsOn } from "./hooks";
|
||||
import type { CustomEvent, EdgeEventsHandler, EdgeEventsOn } from './hooks'
|
||||
|
||||
/** Edge markers */
|
||||
export enum MarkerType {
|
||||
|
||||
@@ -175,3 +175,23 @@ export type NodeEventsOn<CustomEvents = {}> = {
|
||||
export type NodeEventsEmit<CustomEvents = {}> = {
|
||||
[key in keyof NodeEventsHandler]: EventHookTrigger<NodeEventsHandler[key] extends (event: infer Event) => any ? Event : never>
|
||||
} & CustomEventHandlers<CustomEvents>
|
||||
|
||||
export type EdgeEventsHandler<CustomEvents = {}> = {
|
||||
doubleClick: (event: EdgeMouseEvent) => void | { off: () => void }
|
||||
click: (event: EdgeMouseEvent) => void | { off: () => void }
|
||||
mouseEnter: (event: EdgeMouseEvent) => void | { off: () => void }
|
||||
mouseMove: (event: EdgeMouseEvent) => void | { off: () => void }
|
||||
mouseLeave: (event: EdgeMouseEvent) => void | { off: () => void }
|
||||
contextMenu: (event: EdgeMouseEvent) => void | { off: () => void }
|
||||
updateStart: (event: EdgeMouseEvent) => void | { off: () => void }
|
||||
update: (event: EdgeUpdateEvent) => void | { off: () => void }
|
||||
updateEnd: (event: EdgeMouseEvent) => void | { off: () => void }
|
||||
} & CustomEventHandlers<CustomEvents>
|
||||
|
||||
export type EdgeEventsOn<CustomEvents = {}> = {
|
||||
[key in keyof EdgeEventsHandler]: EventHookOn<EdgeEventsHandler[key] extends (event: infer Event) => any ? Event : never>
|
||||
} & CustomEventHandlers<CustomEvents>
|
||||
|
||||
export type EdgeEventsEmit<CustomEvents = {}> = {
|
||||
[key in keyof EdgeEventsHandler]: EventHookTrigger<EdgeEventsHandler[key] extends (event: infer Event) => any ? Event : never>
|
||||
} & CustomEventHandlers<CustomEvents>
|
||||
|
||||
Reference in New Issue
Block a user