From 9c90a83d01b33c6b87be4bc2dffcdcc0f2eadaec Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 3 Aug 2020 23:44:40 +0200 Subject: [PATCH 1/8] docs(readme): use correct edges anchor --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bfdc0ce8..ec0f8c25 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ React Flow is a library for building node-based graphs. You can easily implement - [Options](#options-1) - [Node Types & Custom Nodes](#node-types--custom-nodes) - [Handle Component](#handle-component) -- [Edges](#nodes) +- [Edges](#edges) - [Options](#options) - [Edge Types & Custom Edges](#edge-types--custom-edges) - [Components](#components) From 3fe9cfbf22d7b7ef782bf7d9f53b443a975ac4c0 Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 3 Aug 2020 23:48:12 +0200 Subject: [PATCH 2/8] refactor(fitView): consider min and max zoom --- src/store/index.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/store/index.ts b/src/store/index.ts index d70802ec..b99bc1f8 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -424,19 +424,20 @@ export const storeModel: StoreModel = { fitView: action((state, payload = { padding: 0.1 }) => { const { padding } = payload; - const { nodes, width, height, d3Selection, d3Zoom } = state; + const { nodes, width, height, d3Selection, minZoom, maxZoom } = state; - if (!d3Selection || !d3Zoom || !nodes.length) { + if (!d3Selection || !nodes.length) { return; } const bounds = getRectOfNodes(nodes); const maxBoundsSize = Math.max(bounds.width, bounds.height); - const k = Math.min(width, height) / (maxBoundsSize + maxBoundsSize * padding); + const zoom = Math.min(width, height) / (maxBoundsSize + maxBoundsSize * padding); + const clampedZoom = clamp(zoom, minZoom, maxZoom); const boundsCenterX = bounds.x + bounds.width / 2; const boundsCenterY = bounds.y + bounds.height / 2; - const transform = [width / 2 - boundsCenterX * k, height / 2 - boundsCenterY * k]; - const fittedTransform = zoomIdentity.translate(transform[0], transform[1]).scale(k); + const transform = [width / 2 - boundsCenterX * clampedZoom, height / 2 - boundsCenterY * clampedZoom]; + const fittedTransform = zoomIdentity.translate(transform[0], transform[1]).scale(clampedZoom); // we need to sync the d3 zoom transform with the fitted transform d3Selection.property('__zoom', fittedTransform); From 566a52b1616b781b6d9a510fd50c6179c9049b9a Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 4 Aug 2020 00:41:25 +0200 Subject: [PATCH 3/8] refactor(cypress): dont record video while testing --- cypress.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cypress.json b/cypress.json index 7973f965..17dcce9a 100644 --- a/cypress.json +++ b/cypress.json @@ -1,5 +1,6 @@ { "baseUrl": "http://localhost:3000", "viewportWidth": 1280, - "viewportHeight": 720 -} + "viewportHeight": 720, + "video": false +} \ No newline at end of file From e53da68cb4bcd3043c53b51f4750d1af6b1d4b5b Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 4 Aug 2020 00:41:56 +0200 Subject: [PATCH 4/8] test(task): run cypress headless --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 144a97fa..5d637662 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,7 @@ "build:example": "npm install && npm run build && cd example && npm install && npm run build", "cy:open": "cypress open", "cypress": "npm run dev:wait cy:open", - "test:chrome": "cypress run --browser chrome", + "test:chrome": "cypress run --browser chrome --headless", "test:firefox": "cypress run --browser firefox", "test:all": "npm run test:chrome && npm run test:firefox", "test": "npm run dev:wait test:chrome", @@ -74,4 +74,4 @@ "files": [ "dist" ] -} +} \ No newline at end of file From 6cb3c707bf226921a0d57e9f6d5d72b3a19c898b Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 4 Aug 2020 00:42:54 +0200 Subject: [PATCH 5/8] refactor(store): rename/cleanup setNodesSection action --- src/components/Nodes/wrapNode.tsx | 4 +-- src/components/NodesSelection/index.tsx | 24 +++++++++--------- src/container/GraphView/index.tsx | 4 +-- src/hooks/useGlobalKeyHandler.ts | 8 +++--- src/store/index.ts | 33 ++++--------------------- 5 files changed, 25 insertions(+), 48 deletions(-) diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index 98d0d465..3d790c7b 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -95,8 +95,8 @@ const onDrag = ({ evt, setDragging, id, offset, transform, updateNodePos }: OnDr const dragEvt = getMouseEvent(evt); const scaledClient = { - x: dragEvt.clientX * (1 / transform[2]), - y: dragEvt.clientY * (1 / transform[2]), + x: dragEvt.clientX / transform[2], + y: dragEvt.clientY / transform[2], }; setDragging(true); diff --git a/src/components/NodesSelection/index.tsx b/src/components/NodesSelection/index.tsx index ff5aa7f3..49e07041 100644 --- a/src/components/NodesSelection/index.tsx +++ b/src/components/NodesSelection/index.tsx @@ -30,16 +30,16 @@ function getStartPositions(nodes: Node[]): StartPositions { export default () => { const [offset, setOffset] = useState({ x: 0, y: 0 }); const [startPositions, setStartPositions] = useState({}); - const [tX, tY, tScale] = useStoreState((s) => s.transform); - const selectedNodesBbox = useStoreState((s) => s.selectedNodesBbox); - const selectedElements = useStoreState((s) => s.selectedElements); - const snapToGrid = useStoreState((s) => s.snapToGrid); - const snapGrid = useStoreState((s) => s.snapGrid); - const nodes = useStoreState((s) => s.nodes); - const updateNodePos = useStoreActions((a) => a.updateNodePos); + const [tX, tY, tScale] = useStoreState((state) => state.transform); + const selectedNodesBbox = useStoreState((state) => state.selectedNodesBbox); + const selectedElements = useStoreState((state) => state.selectedElements); + const snapToGrid = useStoreState((state) => state.snapToGrid); + const snapGrid = useStoreState((state) => state.snapGrid); + const nodes = useStoreState((state) => state.nodes); + + const updateNodePos = useStoreActions((actions) => actions.updateNodePos); - const position = selectedNodesBbox; const grid = (snapToGrid ? snapGrid : [1, 1])! as [number, number]; if (!selectedElements) { @@ -51,8 +51,8 @@ export default () => { x: evt.clientX / tScale, y: evt.clientY / tScale, }; - const offsetX: number = scaledClient.x - position.x - tX; - const offsetY: number = scaledClient.y - position.y - tY; + const offsetX: number = scaledClient.x - selectedNodesBbox.x - tX; + const offsetY: number = scaledClient.y - selectedNodesBbox.y - tY; const selectedNodes = selectedElements ? selectedElements .filter(isNode) @@ -76,8 +76,8 @@ export default () => { if (selectedElements) { selectedElements.filter(isNode).forEach((node) => { const pos: XYPosition = { - x: startPositions[node.id].x + scaledClient.x - position.x - offset.x - tX, - y: startPositions[node.id].y + scaledClient.y - position.y - offset.y - tY, + x: startPositions[node.id].x + scaledClient.x - selectedNodesBbox.x - offset.x - tX, + y: startPositions[node.id].y + scaledClient.y - selectedNodesBbox.y - offset.y - tY, }; updateNodePos({ id: node.id, pos }); diff --git a/src/container/GraphView/index.tsx b/src/container/GraphView/index.tsx index 2af74166..4a1f251b 100644 --- a/src/container/GraphView/index.tsx +++ b/src/container/GraphView/index.tsx @@ -112,7 +112,7 @@ const GraphView = ({ const rendererNode = useRef(null); const d3Initialised = useStoreState((state) => state.d3Initialised); const nodesSelectionActive = useStoreState((state) => state.nodesSelectionActive); - const setNodesSelection = useStoreActions((actions) => actions.setNodesSelection); + const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection); const setOnConnect = useStoreActions((actions) => actions.setOnConnect); const setOnConnectStart = useStoreActions((actions) => actions.setOnConnectStart); const setOnConnectStop = useStoreActions((actions) => actions.setOnConnectStop); @@ -127,7 +127,7 @@ const GraphView = ({ const onZoomPaneClick = useCallback(() => { onPaneClick?.(); - setNodesSelection({ isActive: false }); + unsetNodesSelection(); }, [onPaneClick]); useResizeHandler(rendererNode); diff --git a/src/hooks/useGlobalKeyHandler.ts b/src/hooks/useGlobalKeyHandler.ts index cce41486..0fd256c8 100644 --- a/src/hooks/useGlobalKeyHandler.ts +++ b/src/hooks/useGlobalKeyHandler.ts @@ -11,10 +11,10 @@ interface HookParams { } export default ({ deleteKeyCode, onElementsRemove }: HookParams): void => { - const selectedElements = useStoreState((s) => s.selectedElements); - const edges = useStoreState((s) => s.edges); + const selectedElements = useStoreState((state) => state.selectedElements); + const edges = useStoreState((state) => state.edges); - const setNodesSelection = useStoreActions((a) => a.setNodesSelection); + const unsetNodesSelection = useStoreActions((actions) => actions.unsetNodesSelection); const deleteKeyPressed = useKeyPress(deleteKeyCode); useEffect(() => { @@ -29,7 +29,7 @@ export default ({ deleteKeyCode, onElementsRemove }: HookParams): void => { } onElementsRemove(elementsToRemove); - setNodesSelection({ isActive: false }); + unsetNodesSelection(); } }, [deleteKeyPressed]); }; diff --git a/src/store/index.ts b/src/store/index.ts index b99bc1f8..638f89b0 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -38,11 +38,6 @@ type NodeDimensionUpdate = { nodeElement: HTMLDivElement; }; -type SelectionUpdate = { - isActive: boolean; - selection?: SelectionRect; -}; - type SetMinMaxZoom = { minZoom: number; maxZoom: number; @@ -105,7 +100,7 @@ export interface StoreModel { setSelection: Action; - setNodesSelection: Action; + unsetNodesSelection: Action; setSelectedElements: Action; @@ -198,7 +193,6 @@ export const storeModel: StoreModel = { }), updateNodeDimensions: action((state, { id, nodeElement }) => { - const bounds = nodeElement.getBoundingClientRect(); const dimensions = getDimensions(nodeElement); const matchingNode = state.nodes.find((n) => n.id === id); @@ -210,6 +204,7 @@ export const storeModel: StoreModel = { return; } + const bounds = nodeElement.getBoundingClientRect(); const handleBounds = { source: getHandleBounds('.source', nodeElement, bounds, state.transform[2]), target: getHandleBounds('.target', nodeElement, bounds, state.transform[2]), @@ -315,27 +310,9 @@ export const storeModel: StoreModel = { state.selectionActive = isActive; }), - setNodesSelection: action((state, { isActive, selection }) => { - if (!isActive || typeof selection === 'undefined') { - state.nodesSelectionActive = false; - state.selectedElements = null; - - return; - } - const selectedNodes = getNodesInside(state.nodes, selection, state.transform); - - if (!selectedNodes.length) { - state.nodesSelectionActive = false; - state.selectedElements = null; - - return; - } - - const selectedNodesBbox = getRectOfNodes(selectedNodes); - - state.selection = selection; - state.nodesSelectionActive = true; - state.selectedNodesBbox = selectedNodesBbox; + unsetNodesSelection: action((state) => { + state.nodesSelectionActive = false; + state.selectedElements = null; }), setSelectedElements: action((state, elements) => { From cfaa07dc6bd0878f9d7b2190cc6f53d41fdeba09 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 4 Aug 2020 10:03:44 +0200 Subject: [PATCH 6/8] fix(edges): selected condition closes #397 --- src/container/EdgeRenderer/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx index a97731c6..925c38f3 100644 --- a/src/container/EdgeRenderer/index.tsx +++ b/src/container/EdgeRenderer/index.tsx @@ -156,7 +156,9 @@ function renderEdge( ); const isSelected = selectedElements - ? (selectedElements as Edge[]).some((elm) => isEdge(elm) && elm.source === sourceId && elm.target === targetId) + ? (selectedElements as Edge[]).some( + (elm) => isEdge(elm) && elm.source === sourceId && elm.target === targetId && elm.id === edge.id + ) : false; return ( From c59c8ee69386d894ba5e92fde4c64ca08e595bb6 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 4 Aug 2020 10:05:15 +0200 Subject: [PATCH 7/8] refactor(edges): simplify selected condition #397 --- src/container/EdgeRenderer/index.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx index 925c38f3..806b2821 100644 --- a/src/container/EdgeRenderer/index.tsx +++ b/src/container/EdgeRenderer/index.tsx @@ -155,11 +155,7 @@ function renderEdge( targetPosition ); - const isSelected = selectedElements - ? (selectedElements as Edge[]).some( - (elm) => isEdge(elm) && elm.source === sourceId && elm.target === targetId && elm.id === edge.id - ) - : false; + const isSelected = selectedElements ? selectedElements.some((elm) => isEdge(elm) && elm.id === edge.id) : false; return ( Date: Tue, 4 Aug 2020 10:45:18 +0200 Subject: [PATCH 8/8] refactor(store): get rid of unused selection --- src/store/index.ts | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/store/index.ts b/src/store/index.ts index 638f89b0..208df4ee 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -67,7 +67,6 @@ export interface StoreModel { nodesSelectionActive: boolean; selectionActive: boolean; - selection: SelectionRect | null; userSelectionRect: SelectionRect; @@ -154,7 +153,6 @@ export const storeModel: StoreModel = { nodesSelectionActive: false, selectionActive: false, - selection: null, userSelectionRect: { startX: 0, @@ -226,7 +224,6 @@ export const storeModel: StoreModel = { if (state.snapToGrid) { const [gridSizeX, gridSizeY] = state.snapGrid; - position = { x: gridSizeX * Math.round(pos.x / gridSizeX), y: gridSizeY * Math.round(pos.y / gridSizeY), @@ -276,7 +273,6 @@ export const storeModel: StoreModel = { const nextSelectedElements = [...selectedNodes, ...selectedEdges]; const selectedElementsUpdated = !isEqual(nextSelectedElements, state.selectedElements); - state.selection = nextRect; state.userSelectionRect = nextRect; if (selectedElementsUpdated) { @@ -298,7 +294,6 @@ export const storeModel: StoreModel = { const selectedNodesBbox = getRectOfNodes(selectedNodes); - state.selection = state.userSelectionRect; state.nodesSelectionActive = true; state.selectedNodesBbox = selectedNodesBbox;