update: Add slots to CustomConnectionLine.vue, Edge.vue, Node.vue

This commit is contained in:
Braks
2021-10-20 22:39:54 +02:00
parent b3d276bfb8
commit 9d9a90e9a7
25 changed files with 363 additions and 391 deletions
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { CSSProperties } from 'vue'
import { ConnectionLineType, CustomConnectionLine, HandleElement, Node, Position } from '~/types'
import { ConnectionLineType, HandleElement, Node, Position } from '~/types'
import { getBezierPath, getSmoothStepPath } from '~/components/Edges/utils'
import { Store } from '~/context'
@@ -8,7 +8,6 @@ interface ConnectionLineProps {
sourceNode: Node
connectionLineType?: ConnectionLineType
connectionLineStyle?: CSSProperties
customConnectionLine?: CustomConnectionLine
}
const props = withDefaults(defineProps<ConnectionLineProps>(), {
@@ -73,20 +72,19 @@ const dAttr = computed(() => {
</script>
<template>
<g class="revue-flow__connection">
<component
:is="props.customConnectionLine"
v-if="props.customConnectionLine"
<slot
v-bind="{
sourceX: sourceX,
sourceY: sourceY,
sourceX,
sourceY,
sourcePosition: sourceHandle.position,
targetX: targetX,
targetY: targetY,
targetPosition: targetPosition,
targetX,
targetY,
targetPosition,
connectionLineType: props.connectionLineType,
connectionLineStyle: props.connectionLineStyle,
}"
/>
<path v-else :d="dAttr" class="revue-flow__connection-path" :style="props.connectionLineStyle" />
>
<path :d="dAttr" class="revue-flow__connection-path" :style="props.connectionLineStyle" />
</slot>
</g>
</template>
+38 -12
View File
@@ -2,16 +2,14 @@
import EdgeAnchor from './EdgeAnchor.vue'
import { getEdgePositions, getHandle, getSourceTargetNodes, isEdgeVisible } from '~/container/EdgeRenderer/utils'
import { isEdge } from '~/utils/graph'
import { ConnectionMode, Dimensions, Edge, EdgeType, Position, Transform } from '~/types'
import { onMouseDown } from '~/components/Handle/utils'
import { ConnectionMode, Edge, EdgeType, Position } from '~/types'
import { Hooks, Store } from '~/context'
import { useHandle } from '~/composables'
interface EdgeProps {
type: EdgeType
edge: Edge
nodes: ReturnType<typeof getSourceTargetNodes>
dimensions: Dimensions
transform: Transform
markerEndId?: string
edgeUpdaterRadius?: number
}
@@ -68,6 +66,7 @@ const onEdgeMouseMove = (event: MouseEvent) => hooks.edgeMouseMove.trigger({ eve
const onEdgeMouseLeave = (event: MouseEvent) => hooks.edgeMouseLeave.trigger({ event, edge: props.edge })
const handler = useHandle()
const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => {
const nodeId = isSourceHandle ? props.edge.target : props.edge.source
const handleId = isSourceHandle ? props.edge.targetHandle : props.edge.sourceHandle
@@ -75,8 +74,7 @@ const handleEdgeUpdater = (event: MouseEvent, isSourceHandle: boolean) => {
const isTarget = isSourceHandle
hooks.edgeUpdateStart.trigger({ event, edge: props.edge })
handleId &&
onMouseDown(event, store, hooks, handleId, nodeId, isTarget, isValidConnection, isSourceHandle ? 'target' : 'source')
handleId && handler(event, handleId, nodeId, isTarget, isValidConnection, isSourceHandle ? 'target' : 'source')
}
const onEdgeUpdaterSourceMouseDown = (event: MouseEvent) => {
@@ -95,9 +93,9 @@ const isVisible = ({ sourceX, sourceY, targetX, targetY }: ReturnType<typeof get
? isEdgeVisible({
sourcePos: { x: sourceX, y: sourceY },
targetPos: { x: targetX, y: targetY },
width: props.dimensions.width,
height: props.dimensions.height,
transform: props.transform,
width: store.dimensions.width,
height: store.dimensions.height,
transform: store.transform,
})
: true
}
@@ -125,8 +123,7 @@ const visible = computed(() => !props.edge.isHidden && isVisible(edgePos.value))
@mousemove="onEdgeMouseMove"
@mouseleave="onEdgeMouseLeave"
>
<component
:is="props.type"
<slot
v-bind="{
id: props.edge.id,
source: props.edge.source,
@@ -152,7 +149,36 @@ const visible = computed(() => !props.edge.isHidden && isVisible(edgePos.value))
sourceHandleId: props.edge.sourceHandle,
targetHandleId: props.edge.targetHandle,
}"
/>
>
<component
:is="props.type"
v-bind="{
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,
sourcePosition,
targetPosition,
sourceX: edgePos.sourceX,
sourceY: edgePos.sourceY,
targetX: edgePos.targetX,
targetY: edgePos.targetY,
markerEndId: props.markerEndId,
sourceHandleId: props.edge.sourceHandle,
targetHandleId: props.edge.targetHandle,
}"
/>
</slot>
<g @mousedown="onEdgeUpdaterSourceMouseDown" @mouseenter="onEdgeUpdaterMouseEnter" @mouseout="onEdgeUpdaterMouseOut">
<EdgeAnchor
:position="sourcePosition"
+4 -14
View File
@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { ElementId, Position } from '~/types'
import { onMouseDown, ValidConnectionFunc } from '~/components/Handle/utils'
import { ElementId, Position, ValidConnectionFunc } from '~/types'
import { Hooks, Store } from '~/context'
import { useHandle } from '~/composables'
interface HandleProps {
id?: string
@@ -22,19 +22,9 @@ const store = inject(Store)!
const hooks = inject(Hooks)!
const nodeId = inject<ElementId>('NodeIdContext')!
const handler = useHandle()
const onMouseDownHandler = (event: MouseEvent) =>
onMouseDown(
event,
store,
hooks,
props.id,
nodeId,
props.type === 'target',
props.isValidConnection ??
function () {
return true
},
)
handler(event, props.id, nodeId, props.type === 'target', props.isValidConnection)
</script>
<template>
<div
-179
View File
@@ -1,179 +0,0 @@
import { getHostForElement } from '~/utils'
import { ElementId, ConnectionMode, Connection, HandleType, FlowStore, FlowHooks } from '~/types'
export type ValidConnectionFunc = (connection: Connection) => boolean
type Result = {
elementBelow: Element | null
isValid: boolean
connection: Connection
isHoveringHandle: boolean
}
// checks if element below mouse is a handle and returns connection in form of an object { source: 123, target: 312 }
function checkElementBelowIsValid(
event: MouseEvent,
connectionMode: ConnectionMode,
isTarget: boolean,
nodeId: ElementId,
handleId: ElementId | null,
isValidConnection: ValidConnectionFunc,
doc: Document,
) {
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
const elementBelowIsTarget = elementBelow?.classList.contains('target') || false
const elementBelowIsSource = elementBelow?.classList.contains('source') || false
const result: Result = {
elementBelow,
isValid: false,
connection: { source: null, target: null, sourceHandle: null, targetHandle: null },
isHoveringHandle: false,
}
if (elementBelow && (elementBelowIsTarget || elementBelowIsSource)) {
result.isHoveringHandle = true
// in strict mode we don't allow target to target or source to source connections
const isValid =
connectionMode === ConnectionMode.Strict ? (isTarget && elementBelowIsSource) || (!isTarget && elementBelowIsTarget) : true
if (isValid) {
const elementBelowNodeId = elementBelow.getAttribute('data-nodeid')
const elementBelowHandleId = elementBelow.getAttribute('data-handleid')
const connection: Connection = isTarget
? {
source: elementBelowNodeId,
sourceHandle: elementBelowHandleId,
target: nodeId,
targetHandle: handleId,
}
: {
source: nodeId,
sourceHandle: handleId,
target: elementBelowNodeId,
targetHandle: elementBelowHandleId,
}
result.connection = connection
result.isValid = isValidConnection(connection)
}
}
return result
}
function resetRecentHandle(hoveredHandle: Element): void {
hoveredHandle?.classList.remove('revue-flow__handle-valid')
hoveredHandle?.classList.remove('revue-flow__handle-connecting')
}
export function onMouseDown(
event: MouseEvent,
store: FlowStore,
hooks: {
connectStart: FlowHooks['connectStart']
connectStop: FlowHooks['connectStop']
connectEnd: FlowHooks['connectEnd']
connect: FlowHooks['connect']
edgeUpdateEnd: FlowHooks['edgeUpdateEnd']
},
handleId: ElementId,
nodeId: ElementId,
isTarget: boolean,
isValidConnection: ValidConnectionFunc = () => {
return true
},
elementEdgeUpdaterType?: HandleType,
): void {
const revueFlowNode = (event.target as Element).closest('.revue-flow')
// when revue-flow is used inside a shadow root we can't use document
const doc = getHostForElement(event.target as HTMLElement)
if (!doc) {
return
}
const elementBelow = doc.elementFromPoint(event.clientX, event.clientY)
const elementBelowIsTarget = elementBelow?.classList.contains('target')
const elementBelowIsSource = elementBelow?.classList.contains('source')
if (!revueFlowNode || (!elementBelowIsTarget && !elementBelowIsSource && !elementEdgeUpdaterType)) {
return
}
const handleType = elementEdgeUpdaterType || (elementBelowIsTarget ? 'target' : 'source')
const containerBounds = revueFlowNode.getBoundingClientRect()
let recentHoveredHandle: Element
store.connectionPosition.x = event.clientX - containerBounds.left
store.connectionPosition.y = event.clientY - containerBounds.top
store.setConnectionNodeId({
connectionNodeId: nodeId,
connectionHandleId: handleId,
connectionHandleType: handleType,
})
hooks.connectStart.trigger({ event, params: { nodeId, handleId, handleType } })
function onMouseMove(event: MouseEvent) {
store.connectionPosition.x = event.clientX - containerBounds.left
store.connectionPosition.y = event.clientY - containerBounds.top
const { connection, elementBelow, isValid, isHoveringHandle } = checkElementBelowIsValid(
event,
store.connectionMode,
isTarget,
nodeId,
handleId,
isValidConnection,
doc,
)
if (!isHoveringHandle) {
return resetRecentHandle(recentHoveredHandle)
}
const isOwnHandle = connection.source === connection.target
if (!isOwnHandle && elementBelow) {
recentHoveredHandle = elementBelow
elementBelow.classList.add('revue-flow__handle-connecting')
elementBelow.classList.toggle('revue-flow__handle-valid', isValid)
}
}
function onMouseUp(event: MouseEvent) {
const { connection, isValid } = checkElementBelowIsValid(
event,
store.connectionMode,
isTarget,
nodeId,
handleId,
isValidConnection,
doc,
)
hooks.connectStop.trigger(event)
if (isValid) {
hooks.connect.trigger(connection)
}
hooks.connectEnd.trigger(event)
if (elementEdgeUpdaterType) {
hooks.edgeUpdateEnd.trigger({ event } as any)
}
resetRecentHandle(recentHoveredHandle)
store.setConnectionNodeId({ connectionNodeId: undefined, connectionHandleId: undefined, connectionHandleType: undefined })
store.connectionPosition = { x: NaN, y: NaN }
doc.removeEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
doc.removeEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
}
doc.addEventListener('mousemove', onMouseMove as EventListenerOrEventListenerObject)
doc.addEventListener('mouseup', onMouseUp as EventListenerOrEventListenerObject)
}
+17 -4
View File
@@ -95,7 +95,6 @@ const onDragStop: DraggableEventListener = ({ event }) => {
if (props.selectable && !props.selectNodesOnDrag && !props.selected) {
store.addSelectedElements([n])
}
hooks.nodeClick.trigger({ event, node: n })
return
@@ -163,8 +162,7 @@ onMounted(() => {
@contextmenu="onContextMenuHandler"
@click="onSelectNodeHandler"
>
<component
:is="props.type"
<slot
v-bind="{
data: props.node.data,
type: props.node.type,
@@ -176,7 +174,22 @@ onMounted(() => {
targetPosition: props.node.targetPosition,
dragging: props.node.__rf.isDragging,
}"
/>
>
<component
:is="props.type"
v-bind="{
data: props.node.data,
type: props.node.type,
xPos: props.node.__rf.position.x,
yPos: props.node.__rf.position.y,
selected: props.selected,
connectable: props.connectable,
sourcePosition: props.node.sourcePosition,
targetPosition: props.node.targetPosition,
dragging: props.node.__rf.isDragging,
}"
/>
</slot>
</div>
</DraggableCore>
</template>