feat(core,nodes): pass node intersections when dragging a single node
This commit is contained in:
@@ -31,6 +31,7 @@ const {
|
|||||||
addSelectedNodes,
|
addSelectedNodes,
|
||||||
updateNodeDimensions,
|
updateNodeDimensions,
|
||||||
onUpdateNodeInternals,
|
onUpdateNodeInternals,
|
||||||
|
getIntersectingNodes,
|
||||||
} = $(useVueFlow())
|
} = $(useVueFlow())
|
||||||
|
|
||||||
const node = $(useVModel(props, 'node'))
|
const node = $(useVModel(props, 'node'))
|
||||||
@@ -50,36 +51,30 @@ const dragging = useDrag({
|
|||||||
el: nodeElement,
|
el: nodeElement,
|
||||||
disabled: computed(() => !draggable),
|
disabled: computed(() => !draggable),
|
||||||
onStart(event, node, nodes) {
|
onStart(event, node, nodes) {
|
||||||
emit.dragStart({ event, node, nodes })
|
emit.dragStart({ event, node, nodes, intersections: getIntersectingNodes(node) })
|
||||||
},
|
},
|
||||||
onDrag(event, node, nodes) {
|
onDrag(event, node, nodes) {
|
||||||
emit.drag({ event, node, nodes })
|
emit.drag({ event, node, nodes, intersections: getIntersectingNodes(node) })
|
||||||
},
|
},
|
||||||
onStop(event, node, nodes) {
|
onStop(event, node, nodes) {
|
||||||
emit.dragStop({ event, node, nodes })
|
emit.dragStop({ event, node, nodes, intersections: getIntersectingNodes(node) })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const updatePosition = (nodePos: XYZPosition, parentPos?: XYZPosition) => {
|
const getClass = computed(() => (node.class instanceof Function ? node.class(node) : node.class))
|
||||||
if (parentPos) {
|
|
||||||
node.computedPosition = getXYZPos({ x: parentPos.x, y: parentPos.y, z: parentPos.z! }, nodePos)
|
|
||||||
} else {
|
|
||||||
node.computedPosition = nodePos
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateInternals = () => {
|
const getStyle = computed(() => {
|
||||||
if (nodeElement.value) updateNodeDimensions([{ id, nodeElement: nodeElement.value, forceUpdate: true }])
|
const styles = (node.style instanceof Function ? node.style(node) : node.style) || {}
|
||||||
|
|
||||||
updatePosition(
|
const width = node.width instanceof Function ? node.width(node) : node.width
|
||||||
{
|
const height = node.height instanceof Function ? node.height(node) : node.height
|
||||||
x: node.position.x,
|
|
||||||
y: node.position.y,
|
if (width) styles.width = typeof width === 'string' ? width : `${width}px`
|
||||||
z: node.computedPosition.z ? node.computedPosition.z : node.selected ? 1000 : 0,
|
|
||||||
},
|
if (height) styles.height = typeof height === 'string' ? height : `${height}px`
|
||||||
parentNode ? { ...parentNode.computedPosition } : undefined,
|
|
||||||
)
|
return styles
|
||||||
}
|
})
|
||||||
|
|
||||||
onUpdateNodeInternals((updateIds) => {
|
onUpdateNodeInternals((updateIds) => {
|
||||||
if (updateIds.includes(id)) {
|
if (updateIds.includes(id)) {
|
||||||
@@ -100,6 +95,10 @@ onMounted(() => {
|
|||||||
props.resizeObserver.observe(nodeElement.value)
|
props.resizeObserver.observe(nodeElement.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
props.resizeObserver.unobserve(nodeElement.value)
|
||||||
|
})
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[() => node.type, () => node.sourcePosition, () => node.targetPosition],
|
[() => node.type, () => node.sourcePosition, () => node.targetPosition],
|
||||||
() => {
|
() => {
|
||||||
@@ -131,60 +130,60 @@ watch(
|
|||||||
{ flush: 'post' },
|
{ flush: 'post' },
|
||||||
)
|
)
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
function updatePosition(nodePos: XYZPosition, parentPos?: XYZPosition) {
|
||||||
props.resizeObserver.unobserve(nodeElement.value)
|
if (parentPos) {
|
||||||
})
|
node.computedPosition = getXYZPos({ x: parentPos.x, y: parentPos.y, z: parentPos.z! }, nodePos)
|
||||||
|
} else {
|
||||||
|
node.computedPosition = nodePos
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const onMouseEnter = (event: MouseEvent) => {
|
function updateInternals() {
|
||||||
|
if (nodeElement.value) updateNodeDimensions([{ id, nodeElement: nodeElement.value, forceUpdate: true }])
|
||||||
|
|
||||||
|
updatePosition(
|
||||||
|
{
|
||||||
|
x: node.position.x,
|
||||||
|
y: node.position.y,
|
||||||
|
z: node.computedPosition.z ? node.computedPosition.z : node.selected ? 1000 : 0,
|
||||||
|
},
|
||||||
|
parentNode ? { ...parentNode.computedPosition } : undefined,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function onMouseEnter(event: MouseEvent) {
|
||||||
if (!dragging?.value) {
|
if (!dragging?.value) {
|
||||||
emit.mouseEnter({ event, node, connectedEdges })
|
emit.mouseEnter({ event, node, connectedEdges })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onMouseMove = (event: MouseEvent) => {
|
function onMouseMove(event: MouseEvent) {
|
||||||
if (!dragging?.value) {
|
if (!dragging?.value) {
|
||||||
emit.mouseMove({ event, node, connectedEdges })
|
emit.mouseMove({ event, node, connectedEdges })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onMouseLeave = (event: MouseEvent) => {
|
function onMouseLeave(event: MouseEvent) {
|
||||||
if (!dragging?.value) {
|
if (!dragging?.value) {
|
||||||
emit.mouseLeave({ event, node, connectedEdges })
|
emit.mouseLeave({ event, node, connectedEdges })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onContextMenu = (event: MouseEvent) => {
|
function onContextMenu(event: MouseEvent) {
|
||||||
emit.contextMenu({
|
return emit.contextMenu({ event, node, connectedEdges })
|
||||||
event,
|
|
||||||
node,
|
|
||||||
connectedEdges: getConnectedEdges([node], edges),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const onDoubleClick = (event: MouseEvent) => {
|
function onDoubleClick(event: MouseEvent) {
|
||||||
emit.doubleClick({ event, node, connectedEdges: getConnectedEdges([node], edges) })
|
return emit.doubleClick({ event, node, connectedEdges })
|
||||||
}
|
}
|
||||||
|
|
||||||
const onSelectNode = (event: MouseEvent) => {
|
function onSelectNode(event: MouseEvent) {
|
||||||
if (selectable && (!selectNodesOnDrag || !draggable)) {
|
if (selectable && (!selectNodesOnDrag || !draggable)) {
|
||||||
handleNodeClick(node, multiSelectionActive, addSelectedNodes, removeSelectedElements, $$(nodesSelectionActive))
|
handleNodeClick(node, multiSelectionActive, addSelectedNodes, removeSelectedElements, $$(nodesSelectionActive))
|
||||||
}
|
}
|
||||||
emit.click({ event, node, connectedEdges: getConnectedEdges([node], edges) })
|
|
||||||
|
emit.click({ event, node, connectedEdges })
|
||||||
}
|
}
|
||||||
|
|
||||||
const getClass = computed(() => {
|
|
||||||
return node.class instanceof Function ? node.class(node) : node.class
|
|
||||||
})
|
|
||||||
|
|
||||||
const getStyle = computed(() => {
|
|
||||||
const styles = (node.style instanceof Function ? node.style(node) : node.style) || {}
|
|
||||||
const width = node.width instanceof Function ? node.width(node) : node.width
|
|
||||||
const height = node.height instanceof Function ? node.height(node) : node.height
|
|
||||||
if (width) styles.width = typeof width === 'string' ? width : `${width}px`
|
|
||||||
if (height) styles.height = typeof height === 'string' ? height : `${height}px`
|
|
||||||
|
|
||||||
return styles
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
|||||||
@@ -467,18 +467,15 @@ export default (state: State, getters: ComputedGetters): Actions => {
|
|||||||
|
|
||||||
if (!nodeRect) return []
|
if (!nodeRect) return []
|
||||||
|
|
||||||
return (
|
return (nodes || state.nodes).filter((n) => {
|
||||||
nodes ||
|
if (!isRect && (n.id === node!.id || !n.computedPosition)) return false
|
||||||
state.nodes.filter((n) => {
|
|
||||||
if (!isRect && (n.id === node!.id || !n.computedPosition)) return false
|
|
||||||
|
|
||||||
const currNodeRect = nodeToRect(n)
|
const currNodeRect = nodeToRect(n)
|
||||||
const overlappingArea = getOverlappingArea(currNodeRect, nodeRect)
|
const overlappingArea = getOverlappingArea(currNodeRect, nodeRect)
|
||||||
const partiallyVisible = partially && overlappingArea > 0
|
const partiallyVisible = partially && overlappingArea > 0
|
||||||
|
|
||||||
return partiallyVisible || overlappingArea >= Number(nodeOrRect.width) * Number(nodeOrRect.height)
|
return partiallyVisible || overlappingArea >= Number(nodeOrRect.width) * Number(nodeOrRect.height)
|
||||||
})
|
})
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const isNodeIntersecting: Actions['isNodeIntersecting'] = (nodeOrRect, area, partially = true) => {
|
const isNodeIntersecting: Actions['isNodeIntersecting'] = (nodeOrRect, area, partially = true) => {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ export interface NodeDragEvent {
|
|||||||
event: MouseEvent
|
event: MouseEvent
|
||||||
node: GraphNode
|
node: GraphNode
|
||||||
nodes: GraphNode[]
|
nodes: GraphNode[]
|
||||||
|
intersections?: GraphNode[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EdgeMouseEvent {
|
export interface EdgeMouseEvent {
|
||||||
|
|||||||
@@ -120,40 +120,51 @@ export interface State extends Omit<FlowOptions, 'id' | 'modelValue'> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type SetElements = (elements: Elements | ((elements: FlowElements) => Elements), extent?: CoordinateExtent) => void
|
export type SetElements = (elements: Elements | ((elements: FlowElements) => Elements), extent?: CoordinateExtent) => void
|
||||||
|
|
||||||
export type SetNodes = (nodes: Node[] | ((nodes: GraphNode[]) => Node[]), extent?: CoordinateExtent) => void
|
export type SetNodes = (nodes: Node[] | ((nodes: GraphNode[]) => Node[]), extent?: CoordinateExtent) => void
|
||||||
|
|
||||||
export type SetEdges = (edges: Edge[] | ((edges: GraphEdge[]) => Edge[])) => void
|
export type SetEdges = (edges: Edge[] | ((edges: GraphEdge[]) => Edge[])) => void
|
||||||
|
|
||||||
export type AddNodes = (nodes: Node[] | ((nodes: GraphNode[]) => Node[]), extent?: CoordinateExtent) => void
|
export type AddNodes = (nodes: Node[] | ((nodes: GraphNode[]) => Node[]), extent?: CoordinateExtent) => void
|
||||||
|
|
||||||
export type RemoveNodes = (
|
export type RemoveNodes = (
|
||||||
nodes: (Node[] | string[]) | ((nodes: GraphNode[]) => Node[] | string[]),
|
nodes: (Node[] | string[]) | ((nodes: GraphNode[]) => Node[] | string[]),
|
||||||
removeConnectedEdges?: boolean,
|
removeConnectedEdges?: boolean,
|
||||||
) => void
|
) => void
|
||||||
|
|
||||||
export type RemoveEdges = (edges: (Edge[] | string[]) | ((edges: GraphEdge[]) => Edge[] | string[])) => void
|
export type RemoveEdges = (edges: (Edge[] | string[]) | ((edges: GraphEdge[]) => Edge[] | string[])) => void
|
||||||
|
|
||||||
export type AddEdges = (edgesOrConnections: (Edge | Connection)[] | ((edges: GraphEdge[]) => (Edge | Connection)[])) => void
|
export type AddEdges = (edgesOrConnections: (Edge | Connection)[] | ((edges: GraphEdge[]) => (Edge | Connection)[])) => void
|
||||||
|
|
||||||
export type UpdateEdge = (oldEdge: GraphEdge, newConnection: Connection) => GraphEdge | false
|
export type UpdateEdge = (oldEdge: GraphEdge, newConnection: Connection) => GraphEdge | false
|
||||||
|
|
||||||
export type SetState = (
|
export type SetState = (
|
||||||
state:
|
state:
|
||||||
| Partial<FlowOptions & Omit<State, 'nodes' | 'edges' | 'modelValue'>>
|
| Partial<FlowOptions & Omit<State, 'nodes' | 'edges' | 'modelValue'>>
|
||||||
| ((state: State) => Partial<FlowOptions & Omit<State, 'nodes' | 'edges' | 'modelValue'>>),
|
| ((state: State) => Partial<FlowOptions & Omit<State, 'nodes' | 'edges' | 'modelValue'>>),
|
||||||
) => void
|
) => void
|
||||||
|
|
||||||
export type UpdateNodePosition = (dragItems: NodeDragItem[], changed: boolean, dragging: boolean) => void
|
export type UpdateNodePosition = (dragItems: NodeDragItem[], changed: boolean, dragging: boolean) => void
|
||||||
|
|
||||||
export type UpdateNodeDimensions = (updates: UpdateNodeDimensionsParams[]) => void
|
export type UpdateNodeDimensions = (updates: UpdateNodeDimensionsParams[]) => void
|
||||||
|
|
||||||
export type UpdateNodeInternals = (nodeIds: string[]) => void
|
export type UpdateNodeInternals = (nodeIds: string[]) => void
|
||||||
|
|
||||||
export type FindNode = <Data = ElementData, CustomEvents extends Record<string, CustomEvent> = any>(
|
export type FindNode = <Data = ElementData, CustomEvents extends Record<string, CustomEvent> = any>(
|
||||||
id: string,
|
id: string,
|
||||||
) => GraphNode<Data, CustomEvents> | undefined
|
) => GraphNode<Data, CustomEvents> | undefined
|
||||||
|
|
||||||
export type FindEdge = <Data = ElementData, CustomEvents extends Record<string, CustomEvent> = any>(
|
export type FindEdge = <Data = ElementData, CustomEvents extends Record<string, CustomEvent> = any>(
|
||||||
id: string,
|
id: string,
|
||||||
) => GraphEdge<Data, CustomEvents> | undefined
|
) => GraphEdge<Data, CustomEvents> | undefined
|
||||||
export type GetIntersectingNodes<Data = ElementData, CustomEvents extends Record<string, CustomEvent> = any> = (
|
|
||||||
node: (Partial<Node<Data, CustomEvents>> & { id: Node['id'] }) | Rect,
|
export type GetIntersectingNodes = (
|
||||||
|
node: (Partial<Node> & { id: Node['id'] }) | Rect,
|
||||||
partially?: boolean,
|
partially?: boolean,
|
||||||
nodes?: Node<Data, CustomEvents>[],
|
nodes?: GraphNode[],
|
||||||
) => Node<Data, CustomEvents>[]
|
) => GraphNode[]
|
||||||
export type IsNodeIntersecting<Data = ElementData, CustomEvents extends Record<string, CustomEvent> = any> = (
|
|
||||||
node: (Partial<Node<Data, CustomEvents>> & { id: Node['id'] }) | Rect,
|
export type IsNodeIntersecting = (node: (Partial<Node> & { id: Node['id'] }) | Rect, area: Rect, partially?: boolean) => boolean
|
||||||
area: Rect,
|
|
||||||
partially?: boolean,
|
|
||||||
) => boolean
|
|
||||||
|
|
||||||
export interface Actions extends ViewportFunctions {
|
export interface Actions extends ViewportFunctions {
|
||||||
/** parses elements (nodes + edges) and re-sets the state */
|
/** parses elements (nodes + edges) and re-sets the state */
|
||||||
|
|||||||
Reference in New Issue
Block a user