refactor(nodes): use-d3drag
This commit is contained in:
@@ -23,7 +23,7 @@ const onInit = (reactFlowInstance: ReactFlowInstance) => {
|
|||||||
console.log(reactFlowInstance.getNodes());
|
console.log(reactFlowInstance.getNodes());
|
||||||
};
|
};
|
||||||
|
|
||||||
const { nodes: initialNodes, edges: initialEdges } = getNodesAndEdges(5, 5);
|
const { nodes: initialNodes, edges: initialEdges } = getNodesAndEdges(25, 25);
|
||||||
|
|
||||||
const StressFlow = () => {
|
const StressFlow = () => {
|
||||||
const [nodes, setNodes] = useState<Node[]>(initialNodes);
|
const [nodes, setNodes] = useState<Node[]>(initialNodes);
|
||||||
|
|||||||
Generated
+10
-69
@@ -11,9 +11,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.17.8",
|
"@babel/runtime": "^7.17.8",
|
||||||
"classcat": "^5.0.3",
|
"classcat": "^5.0.3",
|
||||||
|
"d3-drag": "^3.0.0",
|
||||||
"d3-selection": "^3.0.0",
|
"d3-selection": "^3.0.0",
|
||||||
"d3-zoom": "^3.0.0",
|
"d3-zoom": "^3.0.0",
|
||||||
"react-draggable": "^4.4.4",
|
|
||||||
"zustand": "^3.7.1"
|
"zustand": "^3.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -3551,14 +3551,6 @@
|
|||||||
"mimic-response": "^1.0.0"
|
"mimic-response": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/clsx": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color-convert": {
|
"node_modules/color-convert": {
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
@@ -5829,7 +5821,8 @@
|
|||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/jsbn": {
|
"node_modules/jsbn": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
@@ -6284,6 +6277,7 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
|
"dev": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
},
|
},
|
||||||
@@ -6586,6 +6580,7 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
||||||
|
"dev": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -8010,16 +8005,6 @@
|
|||||||
"node": ">=0.12"
|
"node": ">=0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prop-types": {
|
|
||||||
"version": "15.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
|
||||||
"dependencies": {
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"react-is": "^16.13.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/protocols": {
|
"node_modules/protocols": {
|
||||||
"version": "1.4.8",
|
"version": "1.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz",
|
||||||
@@ -8200,24 +8185,6 @@
|
|||||||
"react": "17.0.2"
|
"react": "17.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-draggable": {
|
|
||||||
"version": "4.4.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.4.tgz",
|
|
||||||
"integrity": "sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA==",
|
|
||||||
"dependencies": {
|
|
||||||
"clsx": "^1.1.1",
|
|
||||||
"prop-types": "^15.6.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">= 16.3.0",
|
|
||||||
"react-dom": ">= 16.3.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-is": {
|
|
||||||
"version": "16.13.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
|
||||||
},
|
|
||||||
"node_modules/read-cache": {
|
"node_modules/read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
@@ -12863,11 +12830,6 @@
|
|||||||
"mimic-response": "^1.0.0"
|
"mimic-response": "^1.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"clsx": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA=="
|
|
||||||
},
|
|
||||||
"color-convert": {
|
"color-convert": {
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
@@ -14562,7 +14524,8 @@
|
|||||||
"js-tokens": {
|
"js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"jsbn": {
|
"jsbn": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
@@ -14922,6 +14885,7 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
|
"dev": true,
|
||||||
"requires": {
|
"requires": {
|
||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
}
|
}
|
||||||
@@ -15136,7 +15100,8 @@
|
|||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
|
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=",
|
||||||
|
"dev": true
|
||||||
},
|
},
|
||||||
"object-inspect": {
|
"object-inspect": {
|
||||||
"version": "1.10.3",
|
"version": "1.10.3",
|
||||||
@@ -16121,16 +16086,6 @@
|
|||||||
"integrity": "sha1-LMfr6Vn8OmYZwEq029yeRS2GS70=",
|
"integrity": "sha1-LMfr6Vn8OmYZwEq029yeRS2GS70=",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"prop-types": {
|
|
||||||
"version": "15.8.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
|
||||||
"requires": {
|
|
||||||
"loose-envify": "^1.4.0",
|
|
||||||
"object-assign": "^4.1.1",
|
|
||||||
"react-is": "^16.13.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"protocols": {
|
"protocols": {
|
||||||
"version": "1.4.8",
|
"version": "1.4.8",
|
||||||
"resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz",
|
"resolved": "https://registry.npmjs.org/protocols/-/protocols-1.4.8.tgz",
|
||||||
@@ -16263,20 +16218,6 @@
|
|||||||
"scheduler": "^0.20.2"
|
"scheduler": "^0.20.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-draggable": {
|
|
||||||
"version": "4.4.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.4.4.tgz",
|
|
||||||
"integrity": "sha512-6e0WdcNLwpBx/YIDpoyd2Xb04PB0elrDrulKUgdrIlwuYvxh5Ok9M+F8cljm8kPXXs43PmMzek9RrB1b7mLMqA==",
|
|
||||||
"requires": {
|
|
||||||
"clsx": "^1.1.1",
|
|
||||||
"prop-types": "^15.6.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"react-is": {
|
|
||||||
"version": "16.13.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
|
||||||
},
|
|
||||||
"read-cache": {
|
"read-cache": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||||
|
|||||||
+1
-1
@@ -42,9 +42,9 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.17.8",
|
"@babel/runtime": "^7.17.8",
|
||||||
"classcat": "^5.0.3",
|
"classcat": "^5.0.3",
|
||||||
|
"d3-drag": "^3.0.0",
|
||||||
"d3-selection": "^3.0.0",
|
"d3-selection": "^3.0.0",
|
||||||
"d3-zoom": "^3.0.0",
|
"d3-zoom": "^3.0.0",
|
||||||
"react-draggable": "^4.4.4",
|
|
||||||
"zustand": "^3.7.1"
|
"zustand": "^3.7.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -50,6 +50,7 @@ export const baseConfig = ({ outputOptions = {}, injectCSS = true } = {}) => {
|
|||||||
'classcat',
|
'classcat',
|
||||||
'd3-selection',
|
'd3-selection',
|
||||||
'd3-zoom',
|
'd3-zoom',
|
||||||
|
'd3-drag',
|
||||||
'react-draggable',
|
'react-draggable',
|
||||||
'zustand',
|
'zustand',
|
||||||
'zustand/shallow',
|
'zustand/shallow',
|
||||||
@@ -109,6 +110,7 @@ export default isProd && !isTesting
|
|||||||
classcat: 'cc',
|
classcat: 'cc',
|
||||||
'd3-selection': 'd3',
|
'd3-selection': 'd3',
|
||||||
'd3-zoom': 'd3',
|
'd3-zoom': 'd3',
|
||||||
|
'd3-drag': 'd3',
|
||||||
'react-draggable': 'ReactDraggable',
|
'react-draggable': 'ReactDraggable',
|
||||||
zustand: 'zustand',
|
zustand: 'zustand',
|
||||||
'zustand/shallow': 'zustandShallow',
|
'zustand/shallow': 'zustandShallow',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useEffect, useRef, memo, ComponentType, CSSProperties, useMemo, MouseEvent, useCallback } from 'react';
|
import React, { useEffect, useRef, memo, ComponentType, CSSProperties, useMemo, MouseEvent, useCallback } from 'react';
|
||||||
import { DraggableCore, DraggableData, DraggableEvent } from 'react-draggable';
|
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import shallow from 'zustand/shallow';
|
import shallow from 'zustand/shallow';
|
||||||
|
|
||||||
@@ -7,6 +6,7 @@ import { useStore, useStoreApi } from '../../store';
|
|||||||
import { Provider } from '../../contexts/NodeIdContext';
|
import { Provider } from '../../contexts/NodeIdContext';
|
||||||
import { NodeProps, WrapNodeProps, ReactFlowState } from '../../types';
|
import { NodeProps, WrapNodeProps, ReactFlowState } from '../../types';
|
||||||
import useMemoizedMouseHandler from './useMemoizedMouseHandler';
|
import useMemoizedMouseHandler from './useMemoizedMouseHandler';
|
||||||
|
import useDrag, { UseDragEvent, UseDragData } from '../../hooks/useDrag';
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
addSelectedNodes: s.addSelectedNodes,
|
addSelectedNodes: s.addSelectedNodes,
|
||||||
@@ -20,7 +20,6 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
id,
|
id,
|
||||||
type,
|
type,
|
||||||
data,
|
data,
|
||||||
scale,
|
|
||||||
xPos,
|
xPos,
|
||||||
yPos,
|
yPos,
|
||||||
selected,
|
selected,
|
||||||
@@ -42,8 +41,6 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
sourcePosition,
|
sourcePosition,
|
||||||
targetPosition,
|
targetPosition,
|
||||||
hidden,
|
hidden,
|
||||||
snapToGrid,
|
|
||||||
snapGrid,
|
|
||||||
dragging,
|
dragging,
|
||||||
resizeObserver,
|
resizeObserver,
|
||||||
dragHandle,
|
dragHandle,
|
||||||
@@ -72,11 +69,6 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
[zIndex, xPos, yPos, hasPointerEvents, style]
|
[zIndex, xPos, yPos, hasPointerEvents, style]
|
||||||
);
|
);
|
||||||
|
|
||||||
const grid = useMemo(
|
|
||||||
() => (snapToGrid ? snapGrid : [1, 1])! as [number, number],
|
|
||||||
[snapToGrid, snapGrid?.[0], snapGrid?.[1]]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onMouseEnterHandler = useMemoizedMouseHandler(id, dragging, store.getState, onMouseEnter);
|
const onMouseEnterHandler = useMemoizedMouseHandler(id, dragging, store.getState, onMouseEnter);
|
||||||
const onMouseMoveHandler = useMemoizedMouseHandler(id, dragging, store.getState, onMouseMove);
|
const onMouseMoveHandler = useMemoizedMouseHandler(id, dragging, store.getState, onMouseMove);
|
||||||
const onMouseLeaveHandler = useMemoizedMouseHandler(id, dragging, store.getState, onMouseLeave);
|
const onMouseLeaveHandler = useMemoizedMouseHandler(id, dragging, store.getState, onMouseLeave);
|
||||||
@@ -104,7 +96,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const onDragStart = useCallback(
|
const onDragStart = useCallback(
|
||||||
(event: DraggableEvent) => {
|
(event: UseDragEvent) => {
|
||||||
if (selectNodesOnDrag && isSelectable) {
|
if (selectNodesOnDrag && isSelectable) {
|
||||||
store.setState({ nodesSelectionActive: false });
|
store.setState({ nodesSelectionActive: false });
|
||||||
|
|
||||||
@@ -123,28 +115,28 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
|
|
||||||
if (onNodeDragStart) {
|
if (onNodeDragStart) {
|
||||||
const node = store.getState().nodeInternals.get(id)!;
|
const node = store.getState().nodeInternals.get(id)!;
|
||||||
onNodeDragStart(event as MouseEvent, { ...node });
|
onNodeDragStart(event.sourceEvent as MouseEvent, { ...node });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[id, selected, selectNodesOnDrag, isSelectable, onNodeDragStart]
|
[id, selected, selectNodesOnDrag, isSelectable, onNodeDragStart]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDrag = useCallback(
|
const onDrag = useCallback(
|
||||||
(event: DraggableEvent, draggableData: DraggableData) => {
|
(event: UseDragEvent, dragPos: UseDragData) => {
|
||||||
updateNodePosition({ id, dragging: true, diff: { x: draggableData.deltaX, y: draggableData.deltaY } });
|
updateNodePosition({ id, dragging: true, diff: { x: dragPos.dx, y: dragPos.dy } });
|
||||||
|
|
||||||
if (onNodeDrag) {
|
if (onNodeDrag) {
|
||||||
const node = store.getState().nodeInternals.get(id)!;
|
const node = store.getState().nodeInternals.get(id)!;
|
||||||
onNodeDrag(event as MouseEvent, {
|
onNodeDrag(event.sourceEvent as MouseEvent, {
|
||||||
...node,
|
...node,
|
||||||
dragging: true,
|
dragging: true,
|
||||||
position: {
|
position: {
|
||||||
x: node.position.x + draggableData.deltaX,
|
x: node.position.x + dragPos.dx,
|
||||||
y: node.position.y + draggableData.deltaY,
|
y: node.position.y + dragPos.dy,
|
||||||
},
|
},
|
||||||
positionAbsolute: {
|
positionAbsolute: {
|
||||||
x: (node.positionAbsolute?.x || 0) + draggableData.deltaX,
|
x: (node.positionAbsolute?.x || 0) + dragPos.dx,
|
||||||
y: (node.positionAbsolute?.y || 0) + draggableData.deltaY,
|
y: (node.positionAbsolute?.y || 0) + dragPos.dy,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -153,7 +145,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const onDragStop = useCallback(
|
const onDragStop = useCallback(
|
||||||
(event: DraggableEvent) => {
|
(event: UseDragEvent) => {
|
||||||
// onDragStop also gets called when user just clicks on a node.
|
// 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
|
// Because of that we set dragging to true inside the onDrag handler and handle the click here
|
||||||
let node;
|
let node;
|
||||||
@@ -168,7 +160,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (onClick && node) {
|
if (onClick && node) {
|
||||||
onClick(event as MouseEvent, { ...node });
|
onClick(event.sourceEvent as MouseEvent, { ...node });
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -180,7 +172,7 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (onNodeDragStop && node) {
|
if (onNodeDragStop && node) {
|
||||||
onNodeDragStop(event as MouseEvent, { ...node, dragging: false });
|
onNodeDragStop(event.sourceEvent as MouseEvent, { ...node, dragging: false });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[id, isSelectable, selectNodesOnDrag, onClick, onNodeDragStop, dragging, selected]
|
[id, isSelectable, selectNodesOnDrag, onClick, onNodeDragStop, dragging, selected]
|
||||||
@@ -215,6 +207,16 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
}
|
}
|
||||||
}, [id, type, sourcePosition, targetPosition]);
|
}, [id, type, sourcePosition, targetPosition]);
|
||||||
|
|
||||||
|
useDrag({
|
||||||
|
onStart: onDragStart,
|
||||||
|
onDrag: onDrag,
|
||||||
|
onStop: onDragStop,
|
||||||
|
nodeRef: nodeElement,
|
||||||
|
disabled: !isDraggable,
|
||||||
|
noDragClassName,
|
||||||
|
handleSelector: dragHandle,
|
||||||
|
});
|
||||||
|
|
||||||
if (hidden) {
|
if (hidden) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -232,48 +234,35 @@ export default (NodeComponent: ComponentType<NodeProps>) => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DraggableCore
|
<div
|
||||||
onStart={onDragStart}
|
className={nodeClasses}
|
||||||
onDrag={onDrag}
|
ref={nodeElement}
|
||||||
onStop={onDragStop}
|
style={nodeStyle}
|
||||||
scale={scale}
|
onMouseEnter={onMouseEnterHandler}
|
||||||
disabled={!isDraggable}
|
onMouseMove={onMouseMoveHandler}
|
||||||
cancel={`.${noDragClassName}`}
|
onMouseLeave={onMouseLeaveHandler}
|
||||||
nodeRef={nodeElement}
|
onContextMenu={onContextMenuHandler}
|
||||||
grid={grid}
|
onClick={onSelectNodeHandler}
|
||||||
enableUserSelectHack={false}
|
onDoubleClick={onNodeDoubleClickHandler}
|
||||||
handle={dragHandle}
|
data-id={id}
|
||||||
>
|
>
|
||||||
<div
|
<Provider value={id}>
|
||||||
className={nodeClasses}
|
<NodeComponent
|
||||||
ref={nodeElement}
|
id={id}
|
||||||
style={nodeStyle}
|
data={data}
|
||||||
onMouseEnter={onMouseEnterHandler}
|
type={type}
|
||||||
onMouseMove={onMouseMoveHandler}
|
xPos={xPos}
|
||||||
onMouseLeave={onMouseLeaveHandler}
|
yPos={yPos}
|
||||||
onContextMenu={onContextMenuHandler}
|
selected={selected}
|
||||||
onClick={onSelectNodeHandler}
|
isConnectable={isConnectable}
|
||||||
onDoubleClick={onNodeDoubleClickHandler}
|
sourcePosition={sourcePosition}
|
||||||
data-id={id}
|
targetPosition={targetPosition}
|
||||||
>
|
dragging={dragging}
|
||||||
<Provider value={id}>
|
dragHandle={dragHandle}
|
||||||
<NodeComponent
|
zIndex={zIndex}
|
||||||
id={id}
|
/>
|
||||||
data={data}
|
</Provider>
|
||||||
type={type}
|
</div>
|
||||||
xPos={xPos}
|
|
||||||
yPos={yPos}
|
|
||||||
selected={selected}
|
|
||||||
isConnectable={isConnectable}
|
|
||||||
sourcePosition={sourcePosition}
|
|
||||||
targetPosition={targetPosition}
|
|
||||||
dragging={dragging}
|
|
||||||
dragHandle={dragHandle}
|
|
||||||
zIndex={zIndex}
|
|
||||||
/>
|
|
||||||
</Provider>
|
|
||||||
</div>
|
|
||||||
</DraggableCore>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -4,13 +4,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { memo, useMemo, useCallback, useRef, MouseEvent } from 'react';
|
import React, { memo, useMemo, useCallback, useRef, MouseEvent } from 'react';
|
||||||
import { DraggableCore, DraggableData } from 'react-draggable';
|
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import shallow from 'zustand/shallow';
|
import shallow from 'zustand/shallow';
|
||||||
|
|
||||||
import { useStore } from '../../store';
|
import { useStore } from '../../store';
|
||||||
import { Node, ReactFlowState } from '../../types';
|
import { Node, ReactFlowState } from '../../types';
|
||||||
import { getRectOfNodes } from '../../utils/graph';
|
import { getRectOfNodes } from '../../utils/graph';
|
||||||
|
import useDrag, { UseDragData, UseDragEvent } from '../../hooks/useDrag';
|
||||||
|
|
||||||
export interface NodesSelectionProps {
|
export interface NodesSelectionProps {
|
||||||
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void;
|
onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void;
|
||||||
@@ -27,8 +27,6 @@ const selector = (s: ReactFlowState) => ({
|
|||||||
selectedNodes: Array.from(s.nodeInternals)
|
selectedNodes: Array.from(s.nodeInternals)
|
||||||
.filter(([_, n]) => n.selected)
|
.filter(([_, n]) => n.selected)
|
||||||
.map(([_, n]) => n),
|
.map(([_, n]) => n),
|
||||||
snapToGrid: s.snapToGrid,
|
|
||||||
snapGrid: s.snapGrid,
|
|
||||||
updateNodePosition: s.updateNodePosition,
|
updateNodePosition: s.updateNodePosition,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -39,15 +37,10 @@ function NodesSelection({
|
|||||||
onSelectionContextMenu,
|
onSelectionContextMenu,
|
||||||
noPanClassName,
|
noPanClassName,
|
||||||
}: NodesSelectionProps) {
|
}: NodesSelectionProps) {
|
||||||
const { transform, userSelectionActive, selectedNodes, snapToGrid, snapGrid, updateNodePosition } = useStore(
|
const { transform, userSelectionActive, selectedNodes, updateNodePosition } = useStore(selector, shallow);
|
||||||
selector,
|
|
||||||
shallow
|
|
||||||
);
|
|
||||||
const [tX, tY, tScale] = transform;
|
const [tX, tY, tScale] = transform;
|
||||||
const nodeRef = useRef(null);
|
const nodeRef = useRef(null);
|
||||||
|
|
||||||
const grid = useMemo(() => (snapToGrid ? snapGrid : [1, 1])! as [number, number], [snapToGrid, snapGrid]);
|
|
||||||
|
|
||||||
const style = useMemo(
|
const style = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
transform: `translate(${tX}px,${tY}px) scale(${tScale})`,
|
transform: `translate(${tX}px,${tY}px) scale(${tScale})`,
|
||||||
@@ -68,34 +61,34 @@ function NodesSelection({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const onStart = useCallback(
|
const onStart = useCallback(
|
||||||
(event: MouseEvent) => {
|
(event: UseDragEvent) => {
|
||||||
onSelectionDragStart?.(event, selectedNodes);
|
onSelectionDragStart?.(event.sourceEvent, selectedNodes);
|
||||||
},
|
},
|
||||||
[onSelectionDragStart, selectedNodes]
|
[onSelectionDragStart, selectedNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDrag = useCallback(
|
const onDrag = useCallback(
|
||||||
(event: MouseEvent, data: DraggableData) => {
|
(event: UseDragEvent, data: UseDragData) => {
|
||||||
updateNodePosition({
|
updateNodePosition({
|
||||||
diff: {
|
diff: {
|
||||||
x: data.deltaX,
|
x: data.dx,
|
||||||
y: data.deltaY,
|
y: data.dy,
|
||||||
},
|
},
|
||||||
dragging: true,
|
dragging: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
onSelectionDrag?.(event, selectedNodes);
|
onSelectionDrag?.(event.sourceEvent, selectedNodes);
|
||||||
},
|
},
|
||||||
[onSelectionDrag, selectedNodes, updateNodePosition]
|
[onSelectionDrag, selectedNodes, updateNodePosition]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onStop = useCallback(
|
const onStop = useCallback(
|
||||||
(event: MouseEvent) => {
|
(event: UseDragEvent) => {
|
||||||
updateNodePosition({
|
updateNodePosition({
|
||||||
dragging: false,
|
dragging: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
onSelectionDragStop?.(event, selectedNodes);
|
onSelectionDragStop?.(event.sourceEvent, selectedNodes);
|
||||||
},
|
},
|
||||||
[selectedNodes, onSelectionDragStop]
|
[selectedNodes, onSelectionDragStop]
|
||||||
);
|
);
|
||||||
@@ -107,28 +100,20 @@ function NodesSelection({
|
|||||||
[onSelectionContextMenu, selectedNodes]
|
[onSelectionContextMenu, selectedNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
useDrag({
|
||||||
|
onStart,
|
||||||
|
onDrag,
|
||||||
|
onStop,
|
||||||
|
nodeRef,
|
||||||
|
});
|
||||||
|
|
||||||
if (!selectedNodes?.length || userSelectionActive) {
|
if (!selectedNodes?.length || userSelectionActive) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cc(['react-flow__nodesselection', 'react-flow__container', noPanClassName])} style={style}>
|
<div className={cc(['react-flow__nodesselection', 'react-flow__container', noPanClassName])} style={style}>
|
||||||
<DraggableCore
|
<div ref={nodeRef} className="react-flow__nodesselection-rect" onContextMenu={onContextMenu} style={innerStyle} />
|
||||||
scale={tScale}
|
|
||||||
grid={grid}
|
|
||||||
onStart={(event) => onStart(event as MouseEvent)}
|
|
||||||
onDrag={(event, data) => onDrag(event as MouseEvent, data)}
|
|
||||||
onStop={(event) => onStop(event as MouseEvent)}
|
|
||||||
nodeRef={nodeRef}
|
|
||||||
enableUserSelectHack={false}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
ref={nodeRef}
|
|
||||||
className="react-flow__nodesselection-rect"
|
|
||||||
onContextMenu={onContextMenu}
|
|
||||||
style={innerStyle}
|
|
||||||
/>
|
|
||||||
</DraggableCore>
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,19 +23,15 @@ interface NodeRendererProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
scale: s.transform[2],
|
|
||||||
nodesDraggable: s.nodesDraggable,
|
nodesDraggable: s.nodesDraggable,
|
||||||
nodesConnectable: s.nodesConnectable,
|
nodesConnectable: s.nodesConnectable,
|
||||||
elementsSelectable: s.elementsSelectable,
|
elementsSelectable: s.elementsSelectable,
|
||||||
updateNodeDimensions: s.updateNodeDimensions,
|
updateNodeDimensions: s.updateNodeDimensions,
|
||||||
snapGrid: s.snapGrid,
|
|
||||||
snapToGrid: s.snapToGrid,
|
|
||||||
nodeInternals: s.nodeInternals,
|
nodeInternals: s.nodeInternals,
|
||||||
});
|
});
|
||||||
|
|
||||||
const NodeRenderer = (props: NodeRendererProps) => {
|
const NodeRenderer = (props: NodeRendererProps) => {
|
||||||
const { scale, nodesDraggable, nodesConnectable, elementsSelectable, updateNodeDimensions, snapGrid, snapToGrid } =
|
const { nodesDraggable, nodesConnectable, elementsSelectable, updateNodeDimensions } = useStore(selector, shallow);
|
||||||
useStore(selector, shallow);
|
|
||||||
const nodes = useVisibleNodes(props.onlyRenderVisibleElements);
|
const nodes = useVisibleNodes(props.onlyRenderVisibleElements);
|
||||||
const resizeObserverRef = useRef<ResizeObserver>();
|
const resizeObserverRef = useRef<ResizeObserver>();
|
||||||
|
|
||||||
@@ -93,8 +89,6 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
|||||||
xPos={node.positionAbsolute?.x ?? 0}
|
xPos={node.positionAbsolute?.x ?? 0}
|
||||||
yPos={node.positionAbsolute?.y ?? 0}
|
yPos={node.positionAbsolute?.y ?? 0}
|
||||||
dragging={!!node.dragging}
|
dragging={!!node.dragging}
|
||||||
snapGrid={snapGrid}
|
|
||||||
snapToGrid={snapToGrid}
|
|
||||||
selectNodesOnDrag={props.selectNodesOnDrag}
|
selectNodesOnDrag={props.selectNodesOnDrag}
|
||||||
onClick={props.onNodeClick}
|
onClick={props.onNodeClick}
|
||||||
onMouseEnter={props.onNodeMouseEnter}
|
onMouseEnter={props.onNodeMouseEnter}
|
||||||
@@ -105,7 +99,6 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
|||||||
onNodeDragStart={props.onNodeDragStart}
|
onNodeDragStart={props.onNodeDragStart}
|
||||||
onNodeDrag={props.onNodeDrag}
|
onNodeDrag={props.onNodeDrag}
|
||||||
onNodeDragStop={props.onNodeDragStop}
|
onNodeDragStop={props.onNodeDragStop}
|
||||||
scale={scale}
|
|
||||||
selected={!!node.selected}
|
selected={!!node.selected}
|
||||||
isDraggable={isDraggable}
|
isDraggable={isDraggable}
|
||||||
isSelectable={isSelectable}
|
isSelectable={isSelectable}
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { RefObject, useEffect } from 'react';
|
||||||
|
import { D3DragEvent, drag, SubjectPosition } from 'd3-drag';
|
||||||
|
import { select } from 'd3-selection';
|
||||||
|
|
||||||
|
import { useStoreApi } from '../../store';
|
||||||
|
|
||||||
|
export type UseDragEvent = D3DragEvent<HTMLDivElement, null, SubjectPosition>;
|
||||||
|
export type UseDragData = { dx: number; dy: number };
|
||||||
|
|
||||||
|
type UseDragParams = {
|
||||||
|
onStart: (event: UseDragEvent) => void;
|
||||||
|
onDrag: (event: UseDragEvent, data: UseDragData) => void;
|
||||||
|
onStop: (event: UseDragEvent) => void;
|
||||||
|
nodeRef: RefObject<Element>;
|
||||||
|
disabled?: boolean;
|
||||||
|
noDragClassName?: string;
|
||||||
|
// @TODO: implement handleSelector functionality
|
||||||
|
handleSelector?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function useDrag({ onStart, onDrag, onStop, nodeRef, disabled = false, noDragClassName }: UseDragParams) {
|
||||||
|
const store = useStoreApi();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (nodeRef?.current) {
|
||||||
|
const selection = select(nodeRef.current);
|
||||||
|
|
||||||
|
if (disabled) {
|
||||||
|
selection.on('.drag', null);
|
||||||
|
} else {
|
||||||
|
const dragHandler = drag()
|
||||||
|
.on('start', onStart)
|
||||||
|
.on('drag', (event: UseDragEvent) => {
|
||||||
|
const [, , scale] = store.getState().transform;
|
||||||
|
|
||||||
|
// @TODO: we need to use snapGrid and snapToGrid from the store here
|
||||||
|
// @TODO: don't use event.dx but work with event.x somehow in order to prevent lagging / slower node movement than mouse movement
|
||||||
|
|
||||||
|
onDrag(event, { dx: event.dx / scale, dy: event.dy / scale });
|
||||||
|
})
|
||||||
|
.on('end', onStop)
|
||||||
|
.filter((event: any) => !event.ctrlKey && !event.button && !event.target.className.includes(noDragClassName));
|
||||||
|
|
||||||
|
selection.call(dragHandler);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
selection.on('.drag', null);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [disabled, noDragClassName]);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useDrag;
|
||||||
+7
-3
@@ -97,7 +97,7 @@ const createStore = () =>
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
updateNodePosition: ({ id, diff, dragging }: NodeDiffUpdate) => {
|
updateNodePosition: ({ id, diff, dragging }: NodeDiffUpdate) => {
|
||||||
const { onNodesChange, nodeExtent, nodeInternals, hasDefaultNodes } = get();
|
const { onNodesChange, nodeExtent, nodeInternals, hasDefaultNodes, snapGrid, snapToGrid } = get();
|
||||||
|
|
||||||
if (hasDefaultNodes || onNodesChange) {
|
if (hasDefaultNodes || onNodesChange) {
|
||||||
const changes: NodePositionChange[] = [];
|
const changes: NodePositionChange[] = [];
|
||||||
@@ -105,10 +105,14 @@ const createStore = () =>
|
|||||||
nodeInternals.forEach((node) => {
|
nodeInternals.forEach((node) => {
|
||||||
if (node.selected) {
|
if (node.selected) {
|
||||||
if (!node.parentNode || !isParentSelected(node, nodeInternals)) {
|
if (!node.parentNode || !isParentSelected(node, nodeInternals)) {
|
||||||
changes.push(createPositionChange({ node, diff, dragging, nodeExtent, nodeInternals }));
|
changes.push(
|
||||||
|
createPositionChange({ node, diff, dragging, nodeExtent, nodeInternals, snapToGrid, snapGrid })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else if (node.id === id) {
|
} else if (node.id === id) {
|
||||||
changes.push(createPositionChange({ node, diff, dragging, nodeExtent, nodeInternals }));
|
changes.push(
|
||||||
|
createPositionChange({ node, diff, dragging, nodeExtent, nodeInternals, snapToGrid, snapGrid })
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// @ts-nocheck
|
||||||
import { zoomIdentity } from 'd3-zoom';
|
import { zoomIdentity } from 'd3-zoom';
|
||||||
import { GetState } from 'zustand';
|
import { GetState } from 'zustand';
|
||||||
|
|
||||||
@@ -15,6 +16,7 @@ import {
|
|||||||
XYPosition,
|
XYPosition,
|
||||||
XYZPosition,
|
XYZPosition,
|
||||||
FitViewOptions,
|
FitViewOptions,
|
||||||
|
SnapGrid,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
type ParentNodes = Record<string, boolean>;
|
type ParentNodes = Record<string, boolean>;
|
||||||
@@ -111,6 +113,8 @@ type CreatePostionChangeParams = {
|
|||||||
nodeInternals: NodeInternals;
|
nodeInternals: NodeInternals;
|
||||||
diff?: XYPosition;
|
diff?: XYPosition;
|
||||||
dragging?: boolean;
|
dragging?: boolean;
|
||||||
|
snapToGrid?: boolean;
|
||||||
|
snapGrid?: SnapGrid;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function createPositionChange({
|
export function createPositionChange({
|
||||||
@@ -119,6 +123,8 @@ export function createPositionChange({
|
|||||||
dragging,
|
dragging,
|
||||||
nodeExtent,
|
nodeExtent,
|
||||||
nodeInternals,
|
nodeInternals,
|
||||||
|
snapToGrid,
|
||||||
|
snapGrid,
|
||||||
}: CreatePostionChangeParams): NodePositionChange {
|
}: CreatePostionChangeParams): NodePositionChange {
|
||||||
const change: NodePositionChange = {
|
const change: NodePositionChange = {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
@@ -128,6 +134,7 @@ export function createPositionChange({
|
|||||||
|
|
||||||
if (diff) {
|
if (diff) {
|
||||||
const nextPosition = { x: node.position.x + diff.x, y: node.position.y + diff.y };
|
const nextPosition = { x: node.position.x + diff.x, y: node.position.y + diff.y };
|
||||||
|
|
||||||
let currentExtent = node.extent || nodeExtent;
|
let currentExtent = node.extent || nodeExtent;
|
||||||
|
|
||||||
if (node.extent === 'parent') {
|
if (node.extent === 'parent') {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
import { CSSProperties, MouseEvent as ReactMouseEvent } from 'react';
|
||||||
|
|
||||||
import { SnapGrid } from './general';
|
|
||||||
import { XYPosition, Position, CoordinateExtent } from './utils';
|
import { XYPosition, Position, CoordinateExtent } from './utils';
|
||||||
import { HandleElement } from './handles';
|
import { HandleElement } from './handles';
|
||||||
|
|
||||||
@@ -59,7 +58,6 @@ export interface WrapNodeProps<T = any> {
|
|||||||
data: T;
|
data: T;
|
||||||
selected: boolean;
|
selected: boolean;
|
||||||
isConnectable: boolean;
|
isConnectable: boolean;
|
||||||
scale: number;
|
|
||||||
xPos: number;
|
xPos: number;
|
||||||
yPos: number;
|
yPos: number;
|
||||||
width?: number | null;
|
width?: number | null;
|
||||||
@@ -81,8 +79,6 @@ export interface WrapNodeProps<T = any> {
|
|||||||
sourcePosition: Position;
|
sourcePosition: Position;
|
||||||
targetPosition: Position;
|
targetPosition: Position;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
snapToGrid?: boolean;
|
|
||||||
snapGrid?: SnapGrid;
|
|
||||||
dragging: boolean;
|
dragging: boolean;
|
||||||
resizeObserver: ResizeObserver | null;
|
resizeObserver: ResizeObserver | null;
|
||||||
dragHandle?: string;
|
dragHandle?: string;
|
||||||
|
|||||||
Reference in New Issue
Block a user