From 2cd047bc76235b01a4533234c22c50d49b4ba6df Mon Sep 17 00:00:00 2001 From: moklick Date: Sun, 28 Nov 2021 02:39:27 +0100 Subject: [PATCH] fix(child-nodes): dragging when parent is selected --- src/store/index.ts | 107 ++++++++------------------------------ src/store/initialState.ts | 44 ++++++++++++++++ src/store/utils.ts | 72 ++++++++++++++++++++++++- 3 files changed, 135 insertions(+), 88 deletions(-) create mode 100644 src/store/initialState.ts diff --git a/src/store/index.ts b/src/store/index.ts index 0305232d..d346d330 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -25,55 +25,14 @@ import { Transform, Dimensions, XYPosition, - ReactFlowStore, } from '../types'; import { getHandleBounds } from '../components/Nodes/utils'; import { createSelectionChange, getSelectionChanges } from '../utils/changes'; -import { createNodeInternals } from './utils'; +import { createNodeInternals, createPositionChange, isParentSelected } from './utils'; +import initialState from './initialState'; const { Provider, useStore, useStoreApi } = createContext(); -const infiniteExtent: CoordinateExtent = [ - [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY], - [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY], -]; - -const initialState: ReactFlowStore = { - width: 0, - height: 0, - transform: [0, 0, 1], - nodeInternals: new Map(), - edges: [], - onNodesChange: null, - onEdgesChange: null, - selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 }, - d3Zoom: null, - d3Selection: null, - d3ZoomHandler: undefined, - minZoom: 0.5, - maxZoom: 2, - translateExtent: infiniteExtent, - nodeExtent: infiniteExtent, - nodesSelectionActive: false, - userSelectionActive: false, - connectionNodeId: null, - connectionHandleId: null, - connectionHandleType: 'source', - connectionPosition: { x: 0, y: 0 }, - connectionMode: ConnectionMode.Strict, - - snapGrid: [15, 15], - snapToGrid: false, - - nodesDraggable: true, - nodesConnectable: true, - elementsSelectable: true, - - multiSelectionActive: false, - - reactFlowVersion: typeof __REACT_FLOW_VERSION__ !== 'undefined' ? __REACT_FLOW_VERSION__ : '-', -}; - const createStore = () => create((set, get) => ({ ...initialState, @@ -89,7 +48,7 @@ const createStore = () => updateNodeDimensions: (updates: NodeDimensionUpdate[]) => { const { onNodesChange, transform, nodeInternals } = get(); - const nodesToChange: NodeChange[] = updates.reduce((res, update) => { + const changes: NodeChange[] = updates.reduce((res, update) => { const node = nodeInternals.get(update.id); if (node) { @@ -108,12 +67,11 @@ const createStore = () => ...dimensions, }); - const change = { + res.push({ id: node.id, type: 'dimensions', dimensions, - } as NodeChange; - res.push(change); + } as NodeChange); } } @@ -122,53 +80,30 @@ const createStore = () => set({ nodeInternals: new Map(nodeInternals) }); - if (nodesToChange?.length > 0) { - onNodesChange?.(nodesToChange); + if (changes?.length > 0) { + onNodesChange?.(changes); } }, updateNodePosition: ({ id, diff, dragging }: NodeDiffUpdate) => { const { onNodesChange, nodeExtent, nodeInternals } = get(); if (onNodesChange) { - const nodes = Array.from(nodeInternals); - const matchingNodes = nodes.filter(([_, n]) => !!(n.selected || n.id === id)); - if (matchingNodes?.length) { - onNodesChange( - matchingNodes?.map(([_, node]) => { - const change: NodeDimensionChange = { - id: node.id, - type: 'dimensions', - dragging: !!dragging, - }; + const changes: NodeDimensionChange[] = []; - if (diff) { - let currentExtent = nodeExtent || node.extent; + nodeInternals.forEach((node) => { + if (node.selected) { + if (!node.parentNode) { + changes.push(createPositionChange({ node, diff, dragging, nodeExtent, nodeInternals })); + } else if (!isParentSelected(node, nodeInternals)) { + changes.push(createPositionChange({ node, diff, dragging, nodeExtent, nodeInternals })); + } + } else if (node.id === id) { + changes.push(createPositionChange({ node, diff, dragging, nodeExtent, nodeInternals })); + } + }); - if (node.extent === 'parent' && node.parentNode && node.width && node.height) { - const parent = nodeInternals.get(node.parentNode); - currentExtent = - parent?.width && parent?.height - ? [ - [0, 0], - [parent.width - node.width, parent.height - node.height], - ] - : currentExtent; - } - - change.position = currentExtent - ? clampPosition( - { - x: node.position.x + diff.x, - y: node.position.y + diff.y, - }, - currentExtent - ) - : { x: node.position.x + diff.x, y: node.position.y + diff.y }; - } - - return change; - }) - ); + if (changes?.length) { + onNodesChange(changes); } } }, diff --git a/src/store/initialState.ts b/src/store/initialState.ts new file mode 100644 index 00000000..9da2acfe --- /dev/null +++ b/src/store/initialState.ts @@ -0,0 +1,44 @@ +import { CoordinateExtent, ReactFlowStore, ConnectionMode } from '../types'; + +const infiniteExtent: CoordinateExtent = [ + [Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY], + [Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY], +]; + +const initialState: ReactFlowStore = { + width: 0, + height: 0, + transform: [0, 0, 1], + nodeInternals: new Map(), + edges: [], + onNodesChange: null, + onEdgesChange: null, + selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 }, + d3Zoom: null, + d3Selection: null, + d3ZoomHandler: undefined, + minZoom: 0.5, + maxZoom: 2, + translateExtent: infiniteExtent, + nodeExtent: infiniteExtent, + nodesSelectionActive: false, + userSelectionActive: false, + connectionNodeId: null, + connectionHandleId: null, + connectionHandleType: 'source', + connectionPosition: { x: 0, y: 0 }, + connectionMode: ConnectionMode.Strict, + + snapGrid: [15, 15], + snapToGrid: false, + + nodesDraggable: true, + nodesConnectable: true, + elementsSelectable: true, + + multiSelectionActive: false, + + reactFlowVersion: typeof __REACT_FLOW_VERSION__ !== 'undefined' ? __REACT_FLOW_VERSION__ : '-', +}; + +export default initialState; diff --git a/src/store/utils.ts b/src/store/utils.ts index 6d947e4d..afb3e063 100644 --- a/src/store/utils.ts +++ b/src/store/utils.ts @@ -1,5 +1,13 @@ -import { Node, NodeInternals, NodeInternalsItem, XYZPosition } from '../types'; -import { isNumeric } from '../utils'; +import { + CoordinateExtent, + Node, + NodeDimensionChange, + NodeInternals, + NodeInternalsItem, + XYPosition, + XYZPosition, +} from '../types'; +import { clampPosition, isNumeric } from '../utils'; type ParentNodes = Record; @@ -104,3 +112,63 @@ export function createNodeInternals(nodes: Node[], nodeInternals: NodeInternals) return nextNodeInternals; } + +export function isParentSelected(node: NodeInternalsItem, nodeInternals: NodeInternals): boolean { + if (!node.parentNode) { + return false; + } + + const parentNode = nodeInternals.get(node.parentNode); + + if (!parentNode) { + return false; + } + + if (parentNode.selected) { + return true; + } + + return isParentSelected(parentNode, nodeInternals); +} + +type CreatePostiionChangeParams = { + node: NodeInternalsItem; + nodeExtent: CoordinateExtent; + nodeInternals: NodeInternals; + diff?: XYPosition; + dragging?: boolean; +}; + +export function createPositionChange({ + node, + diff, + dragging, + nodeExtent, + nodeInternals, +}: CreatePostiionChangeParams): NodeDimensionChange { + const change: NodeDimensionChange = { + id: node.id, + type: 'dimensions', + dragging: !!dragging, + }; + + if (diff) { + const nextPosition = { x: node.position.x + diff.x, y: node.position.y + diff.y }; + let currentExtent = nodeExtent || node.extent; + + if (node.extent === 'parent' && node.parentNode && node.width && node.height) { + const parent = nodeInternals.get(node.parentNode); + currentExtent = + parent?.width && parent?.height + ? [ + [0, 0], + [parent.width - node.width, parent.height - node.height], + ] + : currentExtent; + } + + change.position = currentExtent ? clampPosition(nextPosition, currentExtent) : nextPosition; + } + + return change; +}