feat(core,nodes): pass node intersections when dragging a single node

This commit is contained in:
braks
2022-11-04 23:44:11 +01:00
committed by Braks
parent 8a51391c31
commit 632fa93272
4 changed files with 78 additions and 70 deletions
@@ -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">
+7 -10
View File
@@ -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) => {
+1
View File
@@ -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 {
+20 -9
View File
@@ -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 */