From d17e6ebaeac3ada154e46721d6146d4151633e20 Mon Sep 17 00:00:00 2001 From: moklick Date: Fri, 11 Sep 2020 16:36:23 +0200 Subject: [PATCH] refactor(wrapNode): simplify dragging behaviour --- src/components/Nodes/wrapNode.tsx | 72 ++++++++++------------------ src/container/GraphView/index.tsx | 2 + src/container/NodeRenderer/index.tsx | 4 ++ src/types/index.ts | 2 + 4 files changed, 32 insertions(+), 48 deletions(-) diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index 4634eb90..df85651f 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -11,16 +11,12 @@ import React, { } from 'react'; import { DraggableCore } from 'react-draggable'; import cc from 'classcat'; - import { ResizeObserver } from 'resize-observer'; -import { useStoreActions } from '../../store/hooks'; +import { useStoreActions } from '../../store/hooks'; import { Provider } from '../../contexts/NodeIdContext'; import { Node, NodeComponentProps, WrapNodeProps } from '../../types'; -const getMouseEvent = (event: MouseEvent | TouchEvent) => - typeof TouchEvent !== 'undefined' && event instanceof TouchEvent ? event.touches[0] : (event as MouseEvent); - export default (NodeComponent: ComponentType) => { const NodeWrapper = ({ id, @@ -47,18 +43,20 @@ export default (NodeComponent: ComponentType) => { targetPosition, isHidden, isInitialized, + snapToGrid, + snapGrid, }: WrapNodeProps) => { const updateNodeDimensions = useStoreActions((actions) => actions.updateNodeDimensions); const setSelectedElements = useStoreActions((actions) => actions.setSelectedElements); - const updateNodePos = useStoreActions((actions) => actions.updateNodePos); + const updateNodePosDiff = useStoreActions((actions) => actions.updateNodePosDiff); const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection); const nodeElement = useRef(null); - const [offset, setOffset] = useState({ x: 0, y: 0 }); const [isDragging, setDragging] = useState(false); - const position = useMemo(() => ({ x: xPos, y: yPos }), [xPos, yPos]); - const node = useMemo(() => ({ id, type, position, data }), [id, type, position, data]); + const node = useMemo(() => ({ id, type, position: { x: xPos, y: yPos }, data }), [id, type, xPos, yPos, data]); + const grid = useMemo(() => (snapToGrid ? snapGrid : [1, 1])! as [number, number], [snapToGrid, snapGrid]); + const nodeStyle: CSSProperties = useMemo( () => ({ zIndex: selected ? 10 : 3, @@ -109,9 +107,7 @@ export default (NodeComponent: ComponentType) => { setSelectedElements({ id: node.id, type: node.type } as Node); } - if (onClick) { - onClick(event, node); - } + onClick?.(event, node); } }, [isSelectable, isDraggable, onClick, node] @@ -119,66 +115,47 @@ export default (NodeComponent: ComponentType) => { const onDragStart = useCallback( (event) => { - const startEvent = getMouseEvent(event); - - const scaledClientX = startEvent.clientX / transform[2]; - const scaledClientY = startEvent.clientY / transform[2]; - - const offsetX = scaledClientX - position.x - transform[0]; - const offsetY = scaledClientY - position.y - transform[1]; - - setOffset({ x: offsetX, y: offsetY }); - - if (onNodeDragStart) { - onNodeDragStart(event as MouseEvent, node); - } + onNodeDragStart?.(event as MouseEvent, node); if (selectNodesOnDrag && isSelectable) { unsetNodesSelection(); setSelectedElements({ id: node.id, type: node.type } as Node); } }, - [node, transform, position, selectNodesOnDrag, isSelectable, onNodeDragStart] + [node, transform, selectNodesOnDrag, isSelectable, onNodeDragStart] ); const onDrag = useCallback( - (event) => { - const dragEvent = getMouseEvent(event); - - const scaledClientX = dragEvent.clientX / transform[2]; - const scaledClientY = dragEvent.clientY / transform[2]; - + (_, data) => { setDragging(true); - updateNodePos({ + updateNodePosDiff({ id, - pos: { - x: scaledClientX - transform[0] - offset.x, - y: scaledClientY - transform[1] - offset.y, + diff: { + x: data.deltaX, + y: data.deltaY, }, }); }, - [id, transform, offset] + [id, transform] ); const onDragStop = useCallback( (event) => { + // onDragStop also gets called when user just clicks on a node. + // Because of that we set dragging to true inside the onDrag handler and handle the click here if (!isDragging) { if (isSelectable && !selectNodesOnDrag) { setSelectedElements({ id: node.id, type: node.type } as Node); } - if (onClick) { - onClick(event as MouseEvent, node); - } + onClick?.(event as MouseEvent, node); return; } setDragging(false); - if (onNodeDragStop) { - onNodeDragStop(event as MouseEvent, node); - } + onNodeDragStop?.(event as MouseEvent, node); }, [node, isDragging, isSelectable, selectNodesOnDrag, onClick, onNodeDragStop] ); @@ -187,11 +164,9 @@ export default (NodeComponent: ComponentType) => { if (nodeElement.current) { updateNodeDimensions({ id, nodeElement: nodeElement.current }); - const resizeObserver = new ResizeObserver((entries) => { - for (let _ of entries) { - if (nodeElement.current) { - updateNodeDimensions({ id, nodeElement: nodeElement.current }); - } + const resizeObserver = new ResizeObserver(() => { + if (nodeElement.current) { + updateNodeDimensions({ id, nodeElement: nodeElement.current }); } }); @@ -230,6 +205,7 @@ export default (NodeComponent: ComponentType) => { disabled={!isDraggable} cancel=".nodrag" nodeRef={nodeElement} + grid={grid} >
void; onNodeDragStop?: (event: MouseEvent, node: Node) => void; onlyRenderVisibleNodes?: boolean; + snapToGrid?: boolean; + snapGrid?: [number, number]; } function renderNode( @@ -66,6 +68,8 @@ function renderNode( selectNodesOnDrag={props.selectNodesOnDrag} isHidden={node.isHidden} isInitialized={isInitialized} + snapGrid={props.snapGrid} + snapToGrid={props.snapToGrid} /> ); } diff --git a/src/types/index.ts b/src/types/index.ts index a9471d46..ae9f53c7 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -172,6 +172,8 @@ export interface WrapNodeProps { targetPosition?: Position; isHidden?: boolean; isInitialized?: boolean; + snapToGrid?: boolean; + snapGrid?: [number, number]; } export type FitViewParams = {