Merge branch 'main' into refactor/redux
@@ -1 +1,3 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M96 124.2c0-6.9 5.2-12.2 12.2-12.2H176V64h-66.8C75.7 64 48 90.7 48 124.2V192h48v-67.8zM403.6 64H336v48h67.2c6.9 0 12.8 5.2 12.8 12.2V192h48v-67.8c0-33.5-27-60.2-60.4-60.2zM416 386.8c0 6.9-5.2 12.2-12.2 12.2H336v49h67.8c33.5 0 60.2-27.7 60.2-61.2V320h-48v66.8zM108.2 399c-6.9 0-12.2-5.2-12.2-12.2V320H48v66.8c0 33.5 27.7 61.2 61.2 61.2H176v-49h-67.8z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 30">
|
||||||
|
<path d="M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z" />
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 429 B After Width: | Height: | Size: 463 B |
@@ -1 +1,3 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M376 186h-20v-40c0-55-45-100-100-100S156 91 156 146v40h-20c-22.002 0-40 17.998-40 40v200c0 22.002 17.998 40 40 40h240c22.002 0 40-17.998 40-40V226c0-22.002-17.998-40-40-40zM256 368c-22.002 0-40-17.998-40-40s17.998-40 40-40 40 17.998 40 40-17.998 40-40 40zm62.002-182H193.998v-40c0-34.004 28.003-62.002 62.002-62.002 34.004 0 62.002 27.998 62.002 62.002v40z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32">
|
||||||
|
<path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z" />
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 437 B After Width: | Height: | Size: 530 B |
@@ -1 +1,3 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M96 235h320v42H96z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 5">
|
||||||
|
<path d="M0 0h32v4.2H0z"/>
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 98 B After Width: | Height: | Size: 96 B |
@@ -1 +1,3 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M416 277.333H277.333V416h-42.666V277.333H96v-42.666h138.667V96h42.666v138.667H416v42.666z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
|
||||||
|
<path d="M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"/>
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 169 B After Width: | Height: | Size: 152 B |
@@ -1 +1,3 @@
|
|||||||
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"><path d="m376 187h-20v-40c0-55-45-100-100-100-54.002 23.996-17.999 27.998 4 31.998s58.002 33.998 58.002 68.002h-.002v40h-182c-22.002 0-40 17.998-40 40v200c0 22.002 17.998 40 40 40h240c22.002 0 40-17.998 40-40v-200c0-22.002-17.998-40-40-40zm-120 182c-22.002 0-40-17.998-40-40s17.998-40 40-40 40 17.998 40 40-17.998 40-40 40z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32">
|
||||||
|
<path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z" />
|
||||||
|
</svg>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 394 B After Width: | Height: | Size: 472 B |
@@ -27,6 +27,7 @@ const InteractionFlow = () => {
|
|||||||
const [isDraggable, setIsDraggable] = useState(false);
|
const [isDraggable, setIsDraggable] = useState(false);
|
||||||
const [isConnectable, setIsConnectable] = useState(false);
|
const [isConnectable, setIsConnectable] = useState(false);
|
||||||
const [zoomOnScroll, setZoomOnScroll] = useState(false);
|
const [zoomOnScroll, setZoomOnScroll] = useState(false);
|
||||||
|
const [zoomOnPinch, setZoomOnPinch] = useState(false);
|
||||||
const [panOnScroll, setPanOnScroll] = useState(false);
|
const [panOnScroll, setPanOnScroll] = useState(false);
|
||||||
const [panOnScrollMode, setPanOnScrollMode] = useState('free');
|
const [panOnScrollMode, setPanOnScrollMode] = useState('free');
|
||||||
const [zoomOnDoubleClick, setZoomOnDoubleClick] = useState(false);
|
const [zoomOnDoubleClick, setZoomOnDoubleClick] = useState(false);
|
||||||
@@ -42,6 +43,7 @@ const InteractionFlow = () => {
|
|||||||
nodesConnectable={isConnectable}
|
nodesConnectable={isConnectable}
|
||||||
nodesDraggable={isDraggable}
|
nodesDraggable={isDraggable}
|
||||||
zoomOnScroll={zoomOnScroll}
|
zoomOnScroll={zoomOnScroll}
|
||||||
|
zoomOnPinch={zoomOnPinch}
|
||||||
panOnScroll={panOnScroll}
|
panOnScroll={panOnScroll}
|
||||||
panOnScrollMode={panOnScrollMode}
|
panOnScrollMode={panOnScrollMode}
|
||||||
zoomOnDoubleClick={zoomOnDoubleClick}
|
zoomOnDoubleClick={zoomOnDoubleClick}
|
||||||
@@ -107,6 +109,18 @@ const InteractionFlow = () => {
|
|||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="zoomonpinch">
|
||||||
|
zoomOnPinch
|
||||||
|
<input
|
||||||
|
id="zoomonpinch"
|
||||||
|
type="checkbox"
|
||||||
|
checked={zoomOnPinch}
|
||||||
|
onChange={(event) => setZoomOnPinch(event.target.checked)}
|
||||||
|
className="react-flow__zoomonpinch"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="panonscroll">
|
<label htmlFor="panonscroll">
|
||||||
panOnScroll
|
panOnScroll
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import ReactFlow, { ReactFlowProvider, addEdge, removeElements, Controls, isNode } from 'react-flow-renderer';
|
import ReactFlow, { ReactFlowProvider, addEdge, removeElements, Controls, isNode } from 'react-flow-renderer';
|
||||||
import dagre from 'dagre';
|
import dagre from 'dagre';
|
||||||
|
|
||||||
@@ -9,6 +9,11 @@ import './layouting.css';
|
|||||||
const dagreGraph = new dagre.graphlib.Graph();
|
const dagreGraph = new dagre.graphlib.Graph();
|
||||||
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
dagreGraph.setDefaultEdgeLabel(() => ({}));
|
||||||
|
|
||||||
|
const nodeExtent = [
|
||||||
|
[0, 0],
|
||||||
|
[1000, 1000],
|
||||||
|
];
|
||||||
|
|
||||||
const LayoutFlow = () => {
|
const LayoutFlow = () => {
|
||||||
const [elements, setElements] = useState(initialElements);
|
const [elements, setElements] = useState(initialElements);
|
||||||
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
const onConnect = (params) => setElements((els) => addEdge(params, els));
|
||||||
@@ -44,14 +49,16 @@ const LayoutFlow = () => {
|
|||||||
setElements(layoutedElements);
|
setElements(layoutedElements);
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
onLayout('TB');
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="layoutflow">
|
<div className="layoutflow">
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
<ReactFlow elements={elements} onConnect={onConnect} onElementsRemove={onElementsRemove}>
|
<ReactFlow
|
||||||
|
elements={elements}
|
||||||
|
onConnect={onConnect}
|
||||||
|
onElementsRemove={onElementsRemove}
|
||||||
|
nodeExtent={nodeExtent}
|
||||||
|
onLoad={() => onLayout('TB')}
|
||||||
|
>
|
||||||
<Controls />
|
<Controls />
|
||||||
</ReactFlow>
|
</ReactFlow>
|
||||||
<div className="controls">
|
<div className="controls">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import React, { useState } from 'react';
|
|||||||
import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background, isNode } from 'react-flow-renderer';
|
import ReactFlow, { removeElements, addEdge, MiniMap, Controls, Background, isNode } from 'react-flow-renderer';
|
||||||
|
|
||||||
const onNodeDragStart = (event, node) => console.log('drag start', node);
|
const onNodeDragStart = (event, node) => console.log('drag start', node);
|
||||||
|
const onNodeDrag = (event, node) => console.log('drag', node);
|
||||||
const onNodeDragStop = (event, node) => console.log('drag stop', node);
|
const onNodeDragStop = (event, node) => console.log('drag stop', node);
|
||||||
const onPaneClick = (event) => console.log('pane click', event);
|
const onPaneClick = (event) => console.log('pane click', event);
|
||||||
const onPaneScroll = (event) => console.log('pane scroll', event);
|
const onPaneScroll = (event) => console.log('pane scroll', event);
|
||||||
@@ -132,6 +133,7 @@ const OverviewFlow = () => {
|
|||||||
onPaneScroll={onPaneScroll}
|
onPaneScroll={onPaneScroll}
|
||||||
onPaneContextMenu={onPaneContextMenu}
|
onPaneContextMenu={onPaneContextMenu}
|
||||||
onNodeDragStart={onNodeDragStart}
|
onNodeDragStart={onNodeDragStart}
|
||||||
|
onNodeDrag={onNodeDrag}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
onSelectionDragStart={onSelectionDragStart}
|
onSelectionDragStart={onSelectionDragStart}
|
||||||
onSelectionDrag={onSelectionDrag}
|
onSelectionDrag={onSelectionDrag}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"internal": true,
|
||||||
|
"main": "../dist/nocss/ReactFlow-nocss.js",
|
||||||
|
"module": "../dist/nocss/ReactFlow-nocss.esm.js",
|
||||||
|
"types": "../dist/nocss/index.d.ts"
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "react-flow-renderer",
|
"name": "react-flow-renderer",
|
||||||
"version": "8.3.7",
|
"version": "8.8.0",
|
||||||
"main": "dist/ReactFlow.js",
|
"main": "dist/ReactFlow.js",
|
||||||
"module": "dist/ReactFlow.esm.js",
|
"module": "dist/ReactFlow.esm.js",
|
||||||
"types": "dist/index.d.ts",
|
"types": "dist/index.d.ts",
|
||||||
@@ -10,12 +10,12 @@
|
|||||||
"url": "https://github.com/wbkd/react-flow.git"
|
"url": "https://github.com/wbkd/react-flow.git"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c --environment NODE_ENV:production",
|
"build": "rollup -c --environment NODE_ENV:production && postcss src/*.css --dir dist",
|
||||||
"start": "rollup -w -c",
|
"start": "rollup -w -c",
|
||||||
"start:examples": "rollup -c rollup.examples.js -w",
|
"start:examples": "rollup -c rollup.examples.js -w",
|
||||||
"build:examples": "rollup -c rollup.examples.js --environment NODE_ENV:production",
|
"build:examples": "rollup -c rollup.examples.js --environment NODE_ENV:production",
|
||||||
"dev": "npm run start:examples",
|
"dev": "npm run start:examples",
|
||||||
"start:testserver": "npm run build && rollup -c rollup.examples.js -w --environment NODE_ENV:testing",
|
"start:testserver": "rollup -c --environment NODE_ENV:testing && rollup -c rollup.examples.js -w --environment NODE_ENV:testing",
|
||||||
"build:dev": "npm install && npm run build:examples",
|
"build:dev": "npm install && npm run build:examples",
|
||||||
"dev:wait": "start-server-and-test start:testserver http-get://localhost:3000",
|
"dev:wait": "start-server-and-test start:testserver http-get://localhost:3000",
|
||||||
"test": "npm run dev:wait test:chrome",
|
"test": "npm run dev:wait test:chrome",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"release:next": "release-it --preRelease=next"
|
"release:next": "release-it --preRelease=next"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/runtime": "^7.12.5",
|
"@babel/runtime": "^7.12.13",
|
||||||
"classcat": "^4.1.0",
|
"classcat": "^4.1.0",
|
||||||
"d3-selection": "^2.0.0",
|
"d3-selection": "^2.0.0",
|
||||||
"d3-zoom": "^2.0.0",
|
"d3-zoom": "^2.0.0",
|
||||||
@@ -41,50 +41,51 @@
|
|||||||
"redux-thunk": "^2.3.0"
|
"redux-thunk": "^2.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.10",
|
"@babel/core": "^7.12.13",
|
||||||
"@babel/plugin-transform-runtime": "^7.12.10",
|
"@babel/plugin-transform-runtime": "^7.12.15",
|
||||||
"@babel/preset-env": "^7.12.11",
|
"@babel/preset-env": "^7.12.13",
|
||||||
"@babel/preset-react": "^7.12.10",
|
"@babel/preset-react": "^7.12.13",
|
||||||
"@rollup/plugin-alias": "^3.1.1",
|
"@rollup/plugin-alias": "^3.1.2",
|
||||||
"@rollup/plugin-babel": "^5.2.2",
|
"@rollup/plugin-babel": "^5.2.3",
|
||||||
"@rollup/plugin-commonjs": "^17.0.0",
|
"@rollup/plugin-commonjs": "^17.1.0",
|
||||||
"@rollup/plugin-node-resolve": "^11.1.0",
|
"@rollup/plugin-node-resolve": "^11.1.1",
|
||||||
"@rollup/plugin-replace": "^2.3.4",
|
"@rollup/plugin-replace": "^2.3.4",
|
||||||
"@svgr/rollup": "^5.5.0",
|
"@svgr/rollup": "^5.5.0",
|
||||||
"@types/classnames": "^2.2.11",
|
"@types/classnames": "^2.2.11",
|
||||||
"@types/d3": "^6.2.0",
|
"@types/d3": "^6.3.0",
|
||||||
"@types/react": "^17.0.0",
|
"@types/react": "^17.0.1",
|
||||||
"@types/react-dom": "^17.0.0",
|
"@types/react-dom": "^17.0.0",
|
||||||
"@types/react-redux": "^7.1.15",
|
"@types/react-redux": "^7.1.15",
|
||||||
"@types/redux": "^3.6.0",
|
"@types/redux": "^3.6.0",
|
||||||
"@types/resize-observer-browser": "^0.1.5",
|
"@types/resize-observer-browser": "^0.1.5",
|
||||||
"autoprefixer": "^10.2.1",
|
"autoprefixer": "^10.2.4",
|
||||||
"babel-preset-react-app": "^10.0.0",
|
"babel-preset-react-app": "^10.0.0",
|
||||||
"cypress": "^6.2.1",
|
"cypress": "^6.4.0",
|
||||||
"dagre": "^0.8.5",
|
"dagre": "^0.8.5",
|
||||||
"localforage": "^1.9.0",
|
"localforage": "^1.9.0",
|
||||||
"postcss": "^8.2.4",
|
"postcss": "^8.2.5",
|
||||||
|
"postcss-cli": "^8.3.1",
|
||||||
"postcss-nested": "^5.0.3",
|
"postcss-nested": "^5.0.3",
|
||||||
"prettier": "2.2.1",
|
"prettier": "2.2.1",
|
||||||
"prop-types": "^15.7.2",
|
"prop-types": "^15.7.2",
|
||||||
"react": "^17.0.1",
|
"react": "^17.0.1",
|
||||||
"react-dom": "^17.0.1",
|
"react-dom": "^17.0.1",
|
||||||
"react-flow-renderer": "^8.3.2",
|
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
"release-it": "^14.2.2",
|
"release-it": "^14.3.0",
|
||||||
"rollup": "^2.36.2",
|
"rollup": "^2.38.5",
|
||||||
"rollup-plugin-bundle-size": "^1.0.3",
|
"rollup-plugin-bundle-size": "^1.0.3",
|
||||||
"rollup-plugin-livereload": "^2.0.0",
|
"rollup-plugin-livereload": "^2.0.0",
|
||||||
"rollup-plugin-postcss": "^4.0.0",
|
"rollup-plugin-postcss": "^4.0.0",
|
||||||
"rollup-plugin-serve": "^1.1.0",
|
"rollup-plugin-serve": "^1.1.0",
|
||||||
"rollup-plugin-typescript2": "^0.29.0",
|
"rollup-plugin-typescript2": "^0.29.0",
|
||||||
"start-server-and-test": "^1.11.7",
|
"start-server-and-test": "^1.12.0",
|
||||||
"typescript": "^4.1.3"
|
"typescript": "^4.1.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": "16 || 17"
|
"react": "16 || 17"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist",
|
||||||
|
"nocss"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,9 +10,10 @@ import typescript from 'rollup-plugin-typescript2';
|
|||||||
import pkg from './package.json';
|
import pkg from './package.json';
|
||||||
|
|
||||||
const isProd = process.env.NODE_ENV === 'production';
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
const processEnv = isProd ? 'production' : 'development';
|
const isTesting = process.env.NODE_ENV === 'testing';
|
||||||
|
const processEnv = isProd || isTesting ? 'production' : 'development';
|
||||||
|
|
||||||
export default {
|
export const baseConfig = ({ mainFile = pkg.main, moduleFile = pkg.module, injectCSS = true } = {}) => ({
|
||||||
input: 'src/index.ts',
|
input: 'src/index.ts',
|
||||||
external: ['react', 'react-dom', /@babel\/runtime/],
|
external: ['react', 'react-dom', /@babel\/runtime/],
|
||||||
onwarn(warning, rollupWarn) {
|
onwarn(warning, rollupWarn) {
|
||||||
@@ -22,13 +23,13 @@ export default {
|
|||||||
},
|
},
|
||||||
output: [
|
output: [
|
||||||
{
|
{
|
||||||
file: pkg.main,
|
file: mainFile,
|
||||||
format: 'cjs',
|
format: 'cjs',
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
exports: 'named',
|
exports: 'named',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
file: pkg.module,
|
file: moduleFile,
|
||||||
format: 'esm',
|
format: 'esm',
|
||||||
sourcemap: true,
|
sourcemap: true,
|
||||||
exports: 'named',
|
exports: 'named',
|
||||||
@@ -38,10 +39,13 @@ export default {
|
|||||||
replace({
|
replace({
|
||||||
__ENV__: JSON.stringify(processEnv),
|
__ENV__: JSON.stringify(processEnv),
|
||||||
__REACT_FLOW_VERSION__: JSON.stringify(pkg.version),
|
__REACT_FLOW_VERSION__: JSON.stringify(pkg.version),
|
||||||
|
// this comes from the easy-peasy dependency
|
||||||
|
'process.env.FORCE_SIMILAR_INSTEAD_OF_MAP': false,
|
||||||
}),
|
}),
|
||||||
bundleSize(),
|
bundleSize(),
|
||||||
postcss({
|
postcss({
|
||||||
minimize: isProd,
|
minimize: isProd,
|
||||||
|
inject: injectCSS,
|
||||||
}),
|
}),
|
||||||
babel({
|
babel({
|
||||||
exclude: 'node_modules/**',
|
exclude: 'node_modules/**',
|
||||||
@@ -56,4 +60,15 @@ export default {
|
|||||||
include: 'node_modules/**',
|
include: 'node_modules/**',
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
};
|
});
|
||||||
|
|
||||||
|
export default isProd && !isTesting
|
||||||
|
? [
|
||||||
|
baseConfig(),
|
||||||
|
baseConfig({
|
||||||
|
mainFile: 'dist/nocss/ReactFlow-nocss.js',
|
||||||
|
moduleFile: 'dist/nocss/ReactFlow-nocss.esm.js',
|
||||||
|
injectCSS: false,
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: baseConfig();
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import postcss from 'rollup-plugin-postcss';
|
|||||||
import livereload from 'rollup-plugin-livereload';
|
import livereload from 'rollup-plugin-livereload';
|
||||||
import serve from 'rollup-plugin-serve';
|
import serve from 'rollup-plugin-serve';
|
||||||
|
|
||||||
import libraryRollupConfig from './rollup.config.js';
|
import { baseConfig } from './rollup.config.js';
|
||||||
|
|
||||||
const isProd = process.env.NODE_ENV === 'production';
|
const isProd = process.env.NODE_ENV === 'production';
|
||||||
const isTesting = process.env.NODE_ENV === 'testing';
|
const isTesting = process.env.NODE_ENV === 'testing';
|
||||||
@@ -16,8 +16,8 @@ const isTesting = process.env.NODE_ENV === 'testing';
|
|||||||
const serveFiles = !isProd || isTesting;
|
const serveFiles = !isProd || isTesting;
|
||||||
const doLiveReload = !isProd && !isTesting;
|
const doLiveReload = !isProd && !isTesting;
|
||||||
|
|
||||||
const rollupConfig = {
|
const rollupExamplesConfig = {
|
||||||
input: ['example/src/index.js'],
|
input: 'example/src/index.js',
|
||||||
output: [
|
output: [
|
||||||
{
|
{
|
||||||
dir: 'example/public/dist',
|
dir: 'example/public/dist',
|
||||||
@@ -28,7 +28,7 @@ const rollupConfig = {
|
|||||||
plugins: [
|
plugins: [
|
||||||
replace({
|
replace({
|
||||||
'process.env.NODE_ENV': isProd ? JSON.stringify('production') : JSON.stringify('development'),
|
'process.env.NODE_ENV': isProd ? JSON.stringify('production') : JSON.stringify('development'),
|
||||||
'process.env.FORCE_SIMILAR_INSTEAD_OF_MAP': 'false',
|
'process.env.FORCE_SIMILAR_INSTEAD_OF_MAP': false,
|
||||||
}),
|
}),
|
||||||
postcss({
|
postcss({
|
||||||
modules: false,
|
modules: false,
|
||||||
@@ -55,4 +55,4 @@ const rollupConfig = {
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default isTesting ? rollupConfig : [libraryRollupConfig, rollupConfig];
|
export default isTesting ? rollupExamplesConfig : [baseConfig(), rollupExamplesConfig];
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ import { useStoreState } from '../../store/hooks';
|
|||||||
import { BackgroundVariant } from '../../types';
|
import { BackgroundVariant } from '../../types';
|
||||||
import { createGridLinesPath, createGridDotsPath } from './utils';
|
import { createGridLinesPath, createGridDotsPath } from './utils';
|
||||||
|
|
||||||
import './style.css';
|
|
||||||
|
|
||||||
export interface BackgroundProps extends HTMLAttributes<SVGElement> {
|
export interface BackgroundProps extends HTMLAttributes<SVGElement> {
|
||||||
variant?: BackgroundVariant;
|
variant?: BackgroundVariant;
|
||||||
gap?: number;
|
gap?: number;
|
||||||
@@ -34,26 +32,25 @@ const Background = ({
|
|||||||
const xOffset = x % scaledGap;
|
const xOffset = x % scaledGap;
|
||||||
const yOffset = y % scaledGap;
|
const yOffset = y % scaledGap;
|
||||||
|
|
||||||
|
const isLines = variant === BackgroundVariant.Lines;
|
||||||
const isLines = variant === BackgroundVariant.Lines;
|
const bgColor = color ? color : defaultColors[variant];
|
||||||
const bgColor = color ? color : defaultColors[variant];
|
const path = isLines ? createGridLinesPath(scaledGap, size, bgColor) : createGridDotsPath(size, bgColor);
|
||||||
const path = isLines ? createGridLinesPath(scaledGap, size, bgColor) : createGridDotsPath(size, bgColor);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
className={bgClasses}
|
className={bgClasses}
|
||||||
style={{
|
style={{
|
||||||
...style,
|
...style,
|
||||||
width: "100%",
|
width: '100%',
|
||||||
height: "100%"
|
height: '100%',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<pattern id="pattern" x={xOffset} y={yOffset} width={scaledGap} height={scaledGap} patternUnits="userSpaceOnUse">
|
<pattern id="pattern" x={xOffset} y={yOffset} width={scaledGap} height={scaledGap} patternUnits="userSpaceOnUse">
|
||||||
{path}
|
{path}
|
||||||
</pattern>
|
</pattern>
|
||||||
|
|
||||||
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern)"></rect>
|
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern)"></rect>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
.react-flow__background {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
@@ -9,13 +9,14 @@ import FitviewIcon from '../../../assets/icons/fitview.svg';
|
|||||||
import LockIcon from '../../../assets/icons/lock.svg';
|
import LockIcon from '../../../assets/icons/lock.svg';
|
||||||
import UnlockIcon from '../../../assets/icons/unlock.svg';
|
import UnlockIcon from '../../../assets/icons/unlock.svg';
|
||||||
|
|
||||||
import './style.css';
|
|
||||||
import useZoomPanHelper from '../../hooks/useZoomPanHelper';
|
import useZoomPanHelper from '../../hooks/useZoomPanHelper';
|
||||||
|
import { FitViewParams } from '../../types';
|
||||||
|
|
||||||
export interface ControlProps extends React.HTMLAttributes<HTMLDivElement> {
|
export interface ControlProps extends React.HTMLAttributes<HTMLDivElement> {
|
||||||
showZoom?: boolean;
|
showZoom?: boolean;
|
||||||
showFitView?: boolean;
|
showFitView?: boolean;
|
||||||
showInteractive?: boolean;
|
showInteractive?: boolean;
|
||||||
|
fitViewParams?: FitViewParams;
|
||||||
onZoomIn?: () => void;
|
onZoomIn?: () => void;
|
||||||
onZoomOut?: () => void;
|
onZoomOut?: () => void;
|
||||||
onFitView?: () => void;
|
onFitView?: () => void;
|
||||||
@@ -27,6 +28,7 @@ const Controls = ({
|
|||||||
showZoom = true,
|
showZoom = true,
|
||||||
showFitView = true,
|
showFitView = true,
|
||||||
showInteractive = true,
|
showInteractive = true,
|
||||||
|
fitViewParams,
|
||||||
onZoomIn,
|
onZoomIn,
|
||||||
onZoomOut,
|
onZoomOut,
|
||||||
onFitView,
|
onFitView,
|
||||||
@@ -50,9 +52,9 @@ const Controls = ({
|
|||||||
}, [zoomOut, onZoomOut]);
|
}, [zoomOut, onZoomOut]);
|
||||||
|
|
||||||
const onFitViewHandler = useCallback(() => {
|
const onFitViewHandler = useCallback(() => {
|
||||||
fitView?.();
|
fitView?.(fitViewParams);
|
||||||
onFitView?.();
|
onFitView?.();
|
||||||
}, [fitView, onFitView]);
|
}, [fitView, fitViewParams, onFitView]);
|
||||||
|
|
||||||
const onInteractiveChangeHandler = useCallback(() => {
|
const onInteractiveChangeHandler = useCallback(() => {
|
||||||
setInteractive?.(!isInteractive);
|
setInteractive?.(!isInteractive);
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
.react-flow__controls {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 5;
|
|
||||||
bottom: 10px;
|
|
||||||
left: 10px;
|
|
||||||
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.08);
|
|
||||||
|
|
||||||
&-button {
|
|
||||||
background: #fefefe;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
box-sizing: content-box;
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
user-select: none;
|
|
||||||
padding: 5px;
|
|
||||||
|
|
||||||
svg {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #f4f4f4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,10 +10,22 @@ interface MiniMapNodeProps {
|
|||||||
className: string;
|
className: string;
|
||||||
color: string;
|
color: string;
|
||||||
strokeColor: string;
|
strokeColor: string;
|
||||||
|
strokeWidth: number;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MiniMapNode = ({ x, y, width, height, style, color, strokeColor, className, borderRadius }: MiniMapNodeProps) => {
|
const MiniMapNode = ({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
style,
|
||||||
|
color,
|
||||||
|
strokeColor,
|
||||||
|
strokeWidth,
|
||||||
|
className,
|
||||||
|
borderRadius,
|
||||||
|
}: MiniMapNodeProps) => {
|
||||||
const { background, backgroundColor } = style || {};
|
const { background, backgroundColor } = style || {};
|
||||||
const fill = (color || background || backgroundColor) as string;
|
const fill = (color || background || backgroundColor) as string;
|
||||||
|
|
||||||
@@ -28,7 +40,7 @@ const MiniMapNode = ({ x, y, width, height, style, color, strokeColor, className
|
|||||||
height={height}
|
height={height}
|
||||||
fill={fill}
|
fill={fill}
|
||||||
stroke={strokeColor}
|
stroke={strokeColor}
|
||||||
strokeWidth={2}
|
strokeWidth={strokeWidth}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import { getRectOfNodes, getBoundsofRects, isNode } from '../../utils/graph';
|
|||||||
import { Node, Rect } from '../../types';
|
import { Node, Rect } from '../../types';
|
||||||
import MiniMapNode from './MiniMapNode';
|
import MiniMapNode from './MiniMapNode';
|
||||||
|
|
||||||
import './style.css';
|
|
||||||
|
|
||||||
type StringFunc = (node: Node) => string;
|
type StringFunc = (node: Node) => string;
|
||||||
|
|
||||||
export interface MiniMapProps extends React.HTMLAttributes<SVGSVGElement> {
|
export interface MiniMapProps extends React.HTMLAttributes<SVGSVGElement> {
|
||||||
@@ -15,6 +13,7 @@ export interface MiniMapProps extends React.HTMLAttributes<SVGSVGElement> {
|
|||||||
nodeStrokeColor?: string | StringFunc;
|
nodeStrokeColor?: string | StringFunc;
|
||||||
nodeClassName?: string | StringFunc;
|
nodeClassName?: string | StringFunc;
|
||||||
nodeBorderRadius?: number;
|
nodeBorderRadius?: number;
|
||||||
|
nodeStrokeWidth?: number;
|
||||||
maskColor?: string;
|
maskColor?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +27,7 @@ const MiniMap = ({
|
|||||||
nodeColor = '#fff',
|
nodeColor = '#fff',
|
||||||
nodeClassName = '',
|
nodeClassName = '',
|
||||||
nodeBorderRadius = 5,
|
nodeBorderRadius = 5,
|
||||||
|
nodeStrokeWidth = 2,
|
||||||
maskColor = 'rgb(240, 242, 243, 0.7)',
|
maskColor = 'rgb(240, 242, 243, 0.7)',
|
||||||
}: MiniMapProps) => {
|
}: MiniMapProps) => {
|
||||||
const containerWidth = useStoreState((s) => s.width);
|
const containerWidth = useStoreState((s) => s.width);
|
||||||
@@ -86,6 +86,7 @@ const MiniMap = ({
|
|||||||
color={nodeColorFunc(node)}
|
color={nodeColorFunc(node)}
|
||||||
borderRadius={nodeBorderRadius}
|
borderRadius={nodeBorderRadius}
|
||||||
strokeColor={nodeStrokeColorFunc(node)}
|
strokeColor={nodeStrokeColorFunc(node)}
|
||||||
|
strokeWidth={nodeStrokeWidth}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<path
|
<path
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
.react-flow__minimap {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 5;
|
|
||||||
bottom: 10px;
|
|
||||||
right: 10px;
|
|
||||||
background-color: #fff;
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
// These components are not used by React Flow directly
|
// These components are not used by React Flow directly
|
||||||
// but the user can add them as a child of a React Flow component
|
// but the user can add them as children of a React Flow component
|
||||||
|
|
||||||
export { default as MiniMap } from './MiniMap';
|
export { default as MiniMap } from './MiniMap';
|
||||||
export { default as Controls } from './Controls';
|
export { default as Controls } from './Controls';
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
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 } from 'react-draggable';
|
import { DraggableCore, DraggableData, DraggableEvent } from 'react-draggable';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
|
|
||||||
import { useStoreActions } from '../../store/hooks';
|
import { useStoreActions } from '../../store/hooks';
|
||||||
import { Provider } from '../../contexts/NodeIdContext';
|
import { Provider } from '../../contexts/NodeIdContext';
|
||||||
import { Node, NodeComponentProps, WrapNodeProps } from '../../types';
|
import { NodeComponentProps, WrapNodeProps } from '../../types';
|
||||||
|
|
||||||
export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
||||||
const NodeWrapper = ({
|
const NodeWrapper = ({
|
||||||
@@ -21,6 +21,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
onMouseLeave,
|
onMouseLeave,
|
||||||
onContextMenu,
|
onContextMenu,
|
||||||
onNodeDragStart,
|
onNodeDragStart,
|
||||||
|
onNodeDrag,
|
||||||
onNodeDragStop,
|
onNodeDragStop,
|
||||||
style,
|
style,
|
||||||
className,
|
className,
|
||||||
@@ -96,7 +97,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
unsetNodesSelection();
|
unsetNodesSelection();
|
||||||
|
|
||||||
if (!selected) {
|
if (!selected) {
|
||||||
addSelectedElements({ id: node.id, type: node.type } as Node);
|
addSelectedElements(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,14 +108,14 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const onDragStart = useCallback(
|
const onDragStart = useCallback(
|
||||||
(event) => {
|
(event: DraggableEvent) => {
|
||||||
onNodeDragStart?.(event as MouseEvent, node);
|
onNodeDragStart?.(event as MouseEvent, node);
|
||||||
|
|
||||||
if (selectNodesOnDrag && isSelectable) {
|
if (selectNodesOnDrag && isSelectable) {
|
||||||
unsetNodesSelection();
|
unsetNodesSelection();
|
||||||
|
|
||||||
if (!selected) {
|
if (!selected) {
|
||||||
addSelectedElements({ id: node.id, type: node.type } as Node);
|
addSelectedElements(node);
|
||||||
}
|
}
|
||||||
} else if (!selectNodesOnDrag && !selected && isSelectable) {
|
} else if (!selectNodesOnDrag && !selected && isSelectable) {
|
||||||
unsetNodesSelection();
|
unsetNodesSelection();
|
||||||
@@ -125,25 +126,31 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const onDrag = useCallback(
|
const onDrag = useCallback(
|
||||||
(_, data) => {
|
(event: DraggableEvent, draggableData: DraggableData) => {
|
||||||
|
if (onNodeDrag) {
|
||||||
|
node.position.x += draggableData.deltaX;
|
||||||
|
node.position.y += draggableData.deltaY;
|
||||||
|
onNodeDrag(event as MouseEvent, node);
|
||||||
|
}
|
||||||
|
|
||||||
updateNodePosDiff({
|
updateNodePosDiff({
|
||||||
id,
|
id,
|
||||||
diff: {
|
diff: {
|
||||||
x: data.deltaX,
|
x: draggableData.deltaX,
|
||||||
y: data.deltaY,
|
y: draggableData.deltaY,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
[id]
|
[id, node, onNodeDrag]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onDragStop = useCallback(
|
const onDragStop = useCallback(
|
||||||
(event) => {
|
(event: DraggableEvent) => {
|
||||||
// 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
|
||||||
if (!isDragging) {
|
if (!isDragging) {
|
||||||
if (isSelectable && !selectNodesOnDrag && !selected) {
|
if (isSelectable && !selectNodesOnDrag && !selected) {
|
||||||
addSelectedElements({ id: node.id, type: node.type } as Node);
|
addSelectedElements(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick?.(event as MouseEvent, node);
|
onClick?.(event as MouseEvent, node);
|
||||||
|
|||||||
@@ -37,8 +37,10 @@ const FlowRenderer = ({
|
|||||||
onMoveEnd,
|
onMoveEnd,
|
||||||
selectionKeyCode,
|
selectionKeyCode,
|
||||||
multiSelectionKeyCode,
|
multiSelectionKeyCode,
|
||||||
|
zoomActivationKeyCode,
|
||||||
elementsSelectable,
|
elementsSelectable,
|
||||||
zoomOnScroll,
|
zoomOnScroll,
|
||||||
|
zoomOnPinch,
|
||||||
panOnScroll,
|
panOnScroll,
|
||||||
panOnScrollSpeed,
|
panOnScrollSpeed,
|
||||||
panOnScrollMode,
|
panOnScrollMode,
|
||||||
@@ -91,6 +93,7 @@ const FlowRenderer = ({
|
|||||||
selectionKeyPressed={selectionKeyPressed}
|
selectionKeyPressed={selectionKeyPressed}
|
||||||
elementsSelectable={elementsSelectable}
|
elementsSelectable={elementsSelectable}
|
||||||
zoomOnScroll={zoomOnScroll}
|
zoomOnScroll={zoomOnScroll}
|
||||||
|
zoomOnPinch={zoomOnPinch}
|
||||||
panOnScroll={panOnScroll}
|
panOnScroll={panOnScroll}
|
||||||
panOnScrollSpeed={panOnScrollSpeed}
|
panOnScrollSpeed={panOnScrollSpeed}
|
||||||
panOnScrollMode={panOnScrollMode}
|
panOnScrollMode={panOnScrollMode}
|
||||||
@@ -99,6 +102,7 @@ const FlowRenderer = ({
|
|||||||
defaultPosition={defaultPosition}
|
defaultPosition={defaultPosition}
|
||||||
defaultZoom={defaultZoom}
|
defaultZoom={defaultZoom}
|
||||||
translateExtent={translateExtent}
|
translateExtent={translateExtent}
|
||||||
|
zoomActivationKeyCode={zoomActivationKeyCode}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
<UserSelection selectionKeyPressed={selectionKeyPressed} />
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ const GraphView = ({
|
|||||||
onNodeMouseLeave,
|
onNodeMouseLeave,
|
||||||
onNodeContextMenu,
|
onNodeContextMenu,
|
||||||
onNodeDragStart,
|
onNodeDragStart,
|
||||||
|
onNodeDrag,
|
||||||
onNodeDragStop,
|
onNodeDragStop,
|
||||||
onSelectionDragStart,
|
onSelectionDragStart,
|
||||||
onSelectionDrag,
|
onSelectionDrag,
|
||||||
@@ -51,6 +52,7 @@ const GraphView = ({
|
|||||||
connectionLineComponent,
|
connectionLineComponent,
|
||||||
selectionKeyCode,
|
selectionKeyCode,
|
||||||
multiSelectionKeyCode,
|
multiSelectionKeyCode,
|
||||||
|
zoomActivationKeyCode,
|
||||||
onElementsRemove,
|
onElementsRemove,
|
||||||
deleteKeyCode,
|
deleteKeyCode,
|
||||||
onConnect,
|
onConnect,
|
||||||
@@ -69,9 +71,11 @@ const GraphView = ({
|
|||||||
defaultZoom,
|
defaultZoom,
|
||||||
defaultPosition,
|
defaultPosition,
|
||||||
translateExtent,
|
translateExtent,
|
||||||
|
nodeExtent,
|
||||||
arrowHeadColor,
|
arrowHeadColor,
|
||||||
markerEndId,
|
markerEndId,
|
||||||
zoomOnScroll,
|
zoomOnScroll,
|
||||||
|
zoomOnPinch,
|
||||||
panOnScroll,
|
panOnScroll,
|
||||||
panOnScrollSpeed,
|
panOnScrollSpeed,
|
||||||
panOnScrollMode,
|
panOnScrollMode,
|
||||||
@@ -95,6 +99,7 @@ const GraphView = ({
|
|||||||
const setMinZoom = useStoreActions((actions) => actions.setMinZoom);
|
const setMinZoom = useStoreActions((actions) => actions.setMinZoom);
|
||||||
const setMaxZoom = useStoreActions((actions) => actions.setMaxZoom);
|
const setMaxZoom = useStoreActions((actions) => actions.setMaxZoom);
|
||||||
const setTranslateExtent = useStoreActions((actions) => actions.setTranslateExtent);
|
const setTranslateExtent = useStoreActions((actions) => actions.setTranslateExtent);
|
||||||
|
const setNodeExtent = useStoreActions((actions) => actions.setNodeExtent);
|
||||||
const setConnectionMode = useStoreActions((actions) => actions.setConnectionMode);
|
const setConnectionMode = useStoreActions((actions) => actions.setConnectionMode);
|
||||||
const currentStore = useStore();
|
const currentStore = useStore();
|
||||||
const { zoomIn, zoomOut, zoomTo, transform, fitView, initialized } = useZoomPanHelper();
|
const { zoomIn, zoomOut, zoomTo, transform, fitView, initialized } = useZoomPanHelper();
|
||||||
@@ -190,6 +195,12 @@ const GraphView = ({
|
|||||||
}
|
}
|
||||||
}, [translateExtent]);
|
}, [translateExtent]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (typeof nodeExtent !== 'undefined') {
|
||||||
|
setNodeExtent(nodeExtent);
|
||||||
|
}
|
||||||
|
}, [nodeExtent]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (typeof connectionMode !== 'undefined') {
|
if (typeof connectionMode !== 'undefined') {
|
||||||
setConnectionMode(connectionMode);
|
setConnectionMode(connectionMode);
|
||||||
@@ -205,11 +216,13 @@ const GraphView = ({
|
|||||||
deleteKeyCode={deleteKeyCode}
|
deleteKeyCode={deleteKeyCode}
|
||||||
selectionKeyCode={selectionKeyCode}
|
selectionKeyCode={selectionKeyCode}
|
||||||
multiSelectionKeyCode={multiSelectionKeyCode}
|
multiSelectionKeyCode={multiSelectionKeyCode}
|
||||||
|
zoomActivationKeyCode={zoomActivationKeyCode}
|
||||||
elementsSelectable={elementsSelectable}
|
elementsSelectable={elementsSelectable}
|
||||||
onMove={onMove}
|
onMove={onMove}
|
||||||
onMoveStart={onMoveStart}
|
onMoveStart={onMoveStart}
|
||||||
onMoveEnd={onMoveEnd}
|
onMoveEnd={onMoveEnd}
|
||||||
zoomOnScroll={zoomOnScroll}
|
zoomOnScroll={zoomOnScroll}
|
||||||
|
zoomOnPinch={zoomOnPinch}
|
||||||
zoomOnDoubleClick={zoomOnDoubleClick}
|
zoomOnDoubleClick={zoomOnDoubleClick}
|
||||||
panOnScroll={panOnScroll}
|
panOnScroll={panOnScroll}
|
||||||
panOnScrollSpeed={panOnScrollSpeed}
|
panOnScrollSpeed={panOnScrollSpeed}
|
||||||
@@ -231,6 +244,7 @@ const GraphView = ({
|
|||||||
onNodeMouseLeave={onNodeMouseLeave}
|
onNodeMouseLeave={onNodeMouseLeave}
|
||||||
onNodeContextMenu={onNodeContextMenu}
|
onNodeContextMenu={onNodeContextMenu}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
|
onNodeDrag={onNodeDrag}
|
||||||
onNodeDragStart={onNodeDragStart}
|
onNodeDragStart={onNodeDragStart}
|
||||||
selectNodesOnDrag={selectNodesOnDrag}
|
selectNodesOnDrag={selectNodesOnDrag}
|
||||||
snapToGrid={snapToGrid}
|
snapToGrid={snapToGrid}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import React, { memo, useMemo, ComponentType, MouseEvent } from 'react';
|
|||||||
import { getNodesInside, isNode } from '../../utils/graph';
|
import { getNodesInside, isNode } from '../../utils/graph';
|
||||||
import { useStoreState, useStoreActions } from '../../store/hooks';
|
import { useStoreState, useStoreActions } from '../../store/hooks';
|
||||||
import { Node, NodeTypesType, WrapNodeProps, Edge } from '../../types';
|
import { Node, NodeTypesType, WrapNodeProps, Edge } from '../../types';
|
||||||
|
|
||||||
interface NodeRendererProps {
|
interface NodeRendererProps {
|
||||||
nodeTypes: NodeTypesType;
|
nodeTypes: NodeTypesType;
|
||||||
selectNodesOnDrag: boolean;
|
selectNodesOnDrag: boolean;
|
||||||
@@ -13,6 +12,7 @@ interface NodeRendererProps {
|
|||||||
onNodeMouseLeave?: (event: MouseEvent, node: Node) => void;
|
onNodeMouseLeave?: (event: MouseEvent, node: Node) => void;
|
||||||
onNodeContextMenu?: (event: MouseEvent, node: Node) => void;
|
onNodeContextMenu?: (event: MouseEvent, node: Node) => void;
|
||||||
onNodeDragStart?: (event: MouseEvent, node: Node) => void;
|
onNodeDragStart?: (event: MouseEvent, node: Node) => void;
|
||||||
|
onNodeDrag?: (event: MouseEvent, node: Node) => void;
|
||||||
onNodeDragStop?: (event: MouseEvent, node: Node) => void;
|
onNodeDragStop?: (event: MouseEvent, node: Node) => void;
|
||||||
snapToGrid: boolean;
|
snapToGrid: boolean;
|
||||||
snapGrid: [number, number];
|
snapGrid: [number, number];
|
||||||
@@ -94,6 +94,7 @@ const NodeRenderer = (props: NodeRendererProps) => {
|
|||||||
onMouseLeave={props.onNodeMouseLeave}
|
onMouseLeave={props.onNodeMouseLeave}
|
||||||
onContextMenu={props.onNodeContextMenu}
|
onContextMenu={props.onNodeContextMenu}
|
||||||
onNodeDragStart={props.onNodeDragStart}
|
onNodeDragStart={props.onNodeDragStart}
|
||||||
|
onNodeDrag={props.onNodeDrag}
|
||||||
onNodeDragStop={props.onNodeDragStop}
|
onNodeDragStop={props.onNodeDragStop}
|
||||||
scale={transform[2]}
|
scale={transform[2]}
|
||||||
selected={selectedElements?.some(({ id }) => id === node.id) || false}
|
selected={selectedElements?.some(({ id }) => id === node.id) || false}
|
||||||
|
|||||||
@@ -30,9 +30,11 @@ import {
|
|||||||
KeyCode,
|
KeyCode,
|
||||||
PanOnScrollMode,
|
PanOnScrollMode,
|
||||||
OnEdgeUpdateFunc,
|
OnEdgeUpdateFunc,
|
||||||
|
NodeExtent,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
import '../../style.css';
|
import '../../style.css';
|
||||||
|
import '../../theme-default.css';
|
||||||
|
|
||||||
const defaultNodeTypes = {
|
const defaultNodeTypes = {
|
||||||
input: InputNode,
|
input: InputNode,
|
||||||
@@ -56,6 +58,7 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
|
|||||||
onNodeMouseLeave?: (event: MouseEvent, node: Node) => void;
|
onNodeMouseLeave?: (event: MouseEvent, node: Node) => void;
|
||||||
onNodeContextMenu?: (event: MouseEvent, node: Node) => void;
|
onNodeContextMenu?: (event: MouseEvent, node: Node) => void;
|
||||||
onNodeDragStart?: (event: MouseEvent, node: Node) => void;
|
onNodeDragStart?: (event: MouseEvent, node: Node) => void;
|
||||||
|
onNodeDrag?: (event: MouseEvent, node: Node) => void;
|
||||||
onNodeDragStop?: (event: MouseEvent, node: Node) => void;
|
onNodeDragStop?: (event: MouseEvent, node: Node) => void;
|
||||||
onConnect?: (connection: Edge | Connection) => void;
|
onConnect?: (connection: Edge | Connection) => void;
|
||||||
onConnectStart?: OnConnectStartFunc;
|
onConnectStart?: OnConnectStartFunc;
|
||||||
@@ -82,6 +85,7 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
|
|||||||
deleteKeyCode?: KeyCode;
|
deleteKeyCode?: KeyCode;
|
||||||
selectionKeyCode?: KeyCode;
|
selectionKeyCode?: KeyCode;
|
||||||
multiSelectionKeyCode?: KeyCode;
|
multiSelectionKeyCode?: KeyCode;
|
||||||
|
zoomActivationKeyCode?: KeyCode;
|
||||||
snapToGrid?: boolean;
|
snapToGrid?: boolean;
|
||||||
snapGrid?: [number, number];
|
snapGrid?: [number, number];
|
||||||
onlyRenderVisibleElements?: boolean;
|
onlyRenderVisibleElements?: boolean;
|
||||||
@@ -95,9 +99,11 @@ export interface ReactFlowProps extends Omit<HTMLAttributes<HTMLDivElement>, 'on
|
|||||||
defaultZoom?: number;
|
defaultZoom?: number;
|
||||||
defaultPosition?: [number, number];
|
defaultPosition?: [number, number];
|
||||||
translateExtent?: TranslateExtent;
|
translateExtent?: TranslateExtent;
|
||||||
|
nodeExtent?: NodeExtent;
|
||||||
arrowHeadColor?: string;
|
arrowHeadColor?: string;
|
||||||
markerEndId?: string;
|
markerEndId?: string;
|
||||||
zoomOnScroll?: boolean;
|
zoomOnScroll?: boolean;
|
||||||
|
zoomOnPinch?: boolean;
|
||||||
panOnScroll?: boolean;
|
panOnScroll?: boolean;
|
||||||
panOnScrollSpeed?: number;
|
panOnScrollSpeed?: number;
|
||||||
panOnScrollMode?: PanOnScrollMode;
|
panOnScrollMode?: PanOnScrollMode;
|
||||||
@@ -125,6 +131,7 @@ const ReactFlow = ({
|
|||||||
onNodeMouseLeave,
|
onNodeMouseLeave,
|
||||||
onNodeContextMenu,
|
onNodeContextMenu,
|
||||||
onNodeDragStart,
|
onNodeDragStart,
|
||||||
|
onNodeDrag,
|
||||||
onNodeDragStop,
|
onNodeDragStop,
|
||||||
onSelectionChange,
|
onSelectionChange,
|
||||||
onSelectionDragStart,
|
onSelectionDragStart,
|
||||||
@@ -138,6 +145,7 @@ const ReactFlow = ({
|
|||||||
deleteKeyCode = 'Backspace',
|
deleteKeyCode = 'Backspace',
|
||||||
selectionKeyCode = 'Shift',
|
selectionKeyCode = 'Shift',
|
||||||
multiSelectionKeyCode = 'Meta',
|
multiSelectionKeyCode = 'Meta',
|
||||||
|
zoomActivationKeyCode = 'Meta',
|
||||||
snapToGrid = false,
|
snapToGrid = false,
|
||||||
snapGrid = [15, 15],
|
snapGrid = [15, 15],
|
||||||
onlyRenderVisibleElements = true,
|
onlyRenderVisibleElements = true,
|
||||||
@@ -150,9 +158,11 @@ const ReactFlow = ({
|
|||||||
defaultZoom = 1,
|
defaultZoom = 1,
|
||||||
defaultPosition = [0, 0],
|
defaultPosition = [0, 0],
|
||||||
translateExtent,
|
translateExtent,
|
||||||
|
nodeExtent,
|
||||||
arrowHeadColor = '#b1b1b7',
|
arrowHeadColor = '#b1b1b7',
|
||||||
markerEndId,
|
markerEndId,
|
||||||
zoomOnScroll = true,
|
zoomOnScroll = true,
|
||||||
|
zoomOnPinch = true,
|
||||||
panOnScroll = false,
|
panOnScroll = false,
|
||||||
panOnScrollSpeed = 0.5,
|
panOnScrollSpeed = 0.5,
|
||||||
panOnScrollMode = PanOnScrollMode.Free,
|
panOnScrollMode = PanOnScrollMode.Free,
|
||||||
@@ -183,6 +193,7 @@ const ReactFlow = ({
|
|||||||
onNodeMouseLeave={onNodeMouseLeave}
|
onNodeMouseLeave={onNodeMouseLeave}
|
||||||
onNodeContextMenu={onNodeContextMenu}
|
onNodeContextMenu={onNodeContextMenu}
|
||||||
onNodeDragStart={onNodeDragStart}
|
onNodeDragStart={onNodeDragStart}
|
||||||
|
onNodeDrag={onNodeDrag}
|
||||||
onNodeDragStop={onNodeDragStop}
|
onNodeDragStop={onNodeDragStop}
|
||||||
nodeTypes={nodeTypesParsed}
|
nodeTypes={nodeTypesParsed}
|
||||||
edgeTypes={edgeTypesParsed}
|
edgeTypes={edgeTypesParsed}
|
||||||
@@ -194,6 +205,7 @@ const ReactFlow = ({
|
|||||||
onElementsRemove={onElementsRemove}
|
onElementsRemove={onElementsRemove}
|
||||||
deleteKeyCode={deleteKeyCode}
|
deleteKeyCode={deleteKeyCode}
|
||||||
multiSelectionKeyCode={multiSelectionKeyCode}
|
multiSelectionKeyCode={multiSelectionKeyCode}
|
||||||
|
zoomActivationKeyCode={zoomActivationKeyCode}
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onConnectStart={onConnectStart}
|
onConnectStart={onConnectStart}
|
||||||
onConnectStop={onConnectStop}
|
onConnectStop={onConnectStop}
|
||||||
@@ -210,9 +222,11 @@ const ReactFlow = ({
|
|||||||
defaultZoom={defaultZoom}
|
defaultZoom={defaultZoom}
|
||||||
defaultPosition={defaultPosition}
|
defaultPosition={defaultPosition}
|
||||||
translateExtent={translateExtent}
|
translateExtent={translateExtent}
|
||||||
|
nodeExtent={nodeExtent}
|
||||||
arrowHeadColor={arrowHeadColor}
|
arrowHeadColor={arrowHeadColor}
|
||||||
markerEndId={markerEndId}
|
markerEndId={markerEndId}
|
||||||
zoomOnScroll={zoomOnScroll}
|
zoomOnScroll={zoomOnScroll}
|
||||||
|
zoomOnPinch={zoomOnPinch}
|
||||||
zoomOnDoubleClick={zoomOnDoubleClick}
|
zoomOnDoubleClick={zoomOnDoubleClick}
|
||||||
panOnScroll={panOnScroll}
|
panOnScroll={panOnScroll}
|
||||||
panOnScrollSpeed={panOnScrollSpeed}
|
panOnScrollSpeed={panOnScrollSpeed}
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import React, { useEffect, useRef, ReactNode } from 'react';
|
import React, { useEffect, useRef, ReactNode } from 'react';
|
||||||
|
|
||||||
import { zoom, zoomIdentity } from 'd3-zoom';
|
import { zoom, zoomIdentity } from 'd3-zoom';
|
||||||
import { select } from 'd3-selection';
|
import { select, pointer } from 'd3-selection';
|
||||||
import { clamp } from '../../utils';
|
|
||||||
|
|
||||||
|
import { clamp } from '../../utils';
|
||||||
|
import useKeyPress from '../../hooks/useKeyPress';
|
||||||
import useResizeHandler from '../../hooks/useResizeHandler';
|
import useResizeHandler from '../../hooks/useResizeHandler';
|
||||||
import { useStoreState, useStoreActions, useStore } from '../../store/hooks';
|
import { useStoreState, useStoreActions, useStore } from '../../store/hooks';
|
||||||
import { FlowTransform, TranslateExtent, PanOnScrollMode } from '../../types';
|
import { FlowTransform, TranslateExtent, PanOnScrollMode, KeyCode } from '../../types';
|
||||||
|
|
||||||
interface ZoomPaneProps {
|
interface ZoomPaneProps {
|
||||||
selectionKeyPressed: boolean;
|
selectionKeyPressed: boolean;
|
||||||
elementsSelectable?: boolean;
|
elementsSelectable?: boolean;
|
||||||
zoomOnScroll?: boolean;
|
zoomOnScroll?: boolean;
|
||||||
|
zoomOnPinch?: boolean;
|
||||||
panOnScroll?: boolean;
|
panOnScroll?: boolean;
|
||||||
panOnScrollSpeed?: number;
|
panOnScrollSpeed?: number;
|
||||||
panOnScrollMode?: PanOnScrollMode;
|
panOnScrollMode?: PanOnScrollMode;
|
||||||
@@ -23,6 +24,7 @@ interface ZoomPaneProps {
|
|||||||
onMove?: (flowTransform?: FlowTransform) => void;
|
onMove?: (flowTransform?: FlowTransform) => void;
|
||||||
onMoveStart?: (flowTransform?: FlowTransform) => void;
|
onMoveStart?: (flowTransform?: FlowTransform) => void;
|
||||||
onMoveEnd?: (flowTransform?: FlowTransform) => void;
|
onMoveEnd?: (flowTransform?: FlowTransform) => void;
|
||||||
|
zoomActivationKeyCode?: KeyCode;
|
||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +44,7 @@ const ZoomPane = ({
|
|||||||
onMoveStart,
|
onMoveStart,
|
||||||
onMoveEnd,
|
onMoveEnd,
|
||||||
zoomOnScroll = true,
|
zoomOnScroll = true,
|
||||||
|
zoomOnPinch = true,
|
||||||
panOnScroll = false,
|
panOnScroll = false,
|
||||||
panOnScrollSpeed = 0.5,
|
panOnScrollSpeed = 0.5,
|
||||||
panOnScrollMode = PanOnScrollMode.Free,
|
panOnScrollMode = PanOnScrollMode.Free,
|
||||||
@@ -52,6 +55,7 @@ const ZoomPane = ({
|
|||||||
defaultPosition = [0, 0],
|
defaultPosition = [0, 0],
|
||||||
defaultZoom = 1,
|
defaultZoom = 1,
|
||||||
translateExtent,
|
translateExtent,
|
||||||
|
zoomActivationKeyCode,
|
||||||
children,
|
children,
|
||||||
}: ZoomPaneProps) => {
|
}: ZoomPaneProps) => {
|
||||||
const zoomPane = useRef<HTMLDivElement>(null);
|
const zoomPane = useRef<HTMLDivElement>(null);
|
||||||
@@ -65,6 +69,8 @@ const ZoomPane = ({
|
|||||||
const initD3Zoom = useStoreActions((actions) => actions.initD3Zoom);
|
const initD3Zoom = useStoreActions((actions) => actions.initD3Zoom);
|
||||||
const updateTransform = useStoreActions((actions) => actions.updateTransform);
|
const updateTransform = useStoreActions((actions) => actions.updateTransform);
|
||||||
|
|
||||||
|
const zoomActivationKeyPressed = useKeyPress(zoomActivationKeyCode);
|
||||||
|
|
||||||
useResizeHandler(zoomPane);
|
useResizeHandler(zoomPane);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -93,13 +99,24 @@ const ZoomPane = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (d3Selection && d3Zoom) {
|
if (d3Selection && d3Zoom) {
|
||||||
if (panOnScroll) {
|
if (panOnScroll && !zoomActivationKeyPressed) {
|
||||||
d3Selection
|
d3Selection
|
||||||
.on('wheel', (event: any) => {
|
.on('wheel', (event: any) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopImmediatePropagation();
|
event.stopImmediatePropagation();
|
||||||
|
|
||||||
const currentZoom = d3Selection.property('__zoom').k || 1;
|
const currentZoom = d3Selection.property('__zoom').k || 1;
|
||||||
|
|
||||||
|
if (event.ctrlKey && zoomOnPinch) {
|
||||||
|
const point = pointer(event);
|
||||||
|
// taken from https://github.com/d3/d3-zoom/blob/master/src/zoom.js
|
||||||
|
const pinchDelta = -event.deltaY * (event.deltaMode === 1 ? 0.05 : event.deltaMode ? 1 : 0.002) * 10;
|
||||||
|
const zoom = currentZoom * Math.pow(2, pinchDelta);
|
||||||
|
d3Zoom.scaleTo(d3Selection, zoom, point);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// increase scroll speed in firefox
|
// increase scroll speed in firefox
|
||||||
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
// firefox: deltaMode === 1; chrome: deltaMode === 0
|
||||||
const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
|
const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
|
||||||
@@ -117,7 +134,7 @@ const ZoomPane = ({
|
|||||||
d3Selection.on('wheel', null).on('wheel.zoom', d3ZoomHandler);
|
d3Selection.on('wheel', null).on('wheel.zoom', d3ZoomHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [panOnScroll, panOnScrollMode, d3Selection, d3Zoom, d3ZoomHandler]);
|
}, [panOnScroll, panOnScrollMode, d3Selection, d3Zoom, d3ZoomHandler, zoomActivationKeyPressed, zoomOnPinch]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (d3Zoom) {
|
if (d3Zoom) {
|
||||||
@@ -173,8 +190,11 @@ const ZoomPane = ({
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (d3Zoom) {
|
if (d3Zoom) {
|
||||||
d3Zoom.filter((event: any) => {
|
d3Zoom.filter((event: any) => {
|
||||||
|
const zoomScroll = zoomActivationKeyPressed || zoomOnScroll;
|
||||||
|
const pinchZoom = zoomOnPinch && event.ctrlKey;
|
||||||
|
|
||||||
// if all interactions are disabled, we prevent all zoom events
|
// if all interactions are disabled, we prevent all zoom events
|
||||||
if (!paneMoveable && !zoomOnScroll && !panOnScroll && !zoomOnDoubleClick) {
|
if (!paneMoveable && !zoomScroll && !panOnScroll && !zoomOnDoubleClick && !zoomOnPinch) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,8 +225,12 @@ const ZoomPane = ({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!zoomOnPinch && event.ctrlKey && event.type === 'wheel') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// when there is no scroll handling enabled, we prevent all wheel events
|
// when there is no scroll handling enabled, we prevent all wheel events
|
||||||
if (!zoomOnScroll && !panOnScroll && event.type === 'wheel') {
|
if (!zoomScroll && !panOnScroll && !pinchZoom && event.type === 'wheel') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +243,17 @@ const ZoomPane = ({
|
|||||||
return (!event.ctrlKey || event.type === 'wheel') && !event.button;
|
return (!event.ctrlKey || event.type === 'wheel') && !event.button;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [d3Zoom, zoomOnScroll, panOnScroll, zoomOnDoubleClick, paneMoveable, selectionKeyPressed, elementsSelectable]);
|
}, [
|
||||||
|
d3Zoom,
|
||||||
|
zoomOnScroll,
|
||||||
|
zoomOnPinch,
|
||||||
|
panOnScroll,
|
||||||
|
zoomOnDoubleClick,
|
||||||
|
paneMoveable,
|
||||||
|
selectionKeyPressed,
|
||||||
|
elementsSelectable,
|
||||||
|
zoomActivationKeyPressed,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="react-flow__renderer react-flow__zoompane" ref={zoomPane}>
|
<div className="react-flow__renderer react-flow__zoompane" ref={zoomPane}>
|
||||||
|
|||||||
@@ -3,35 +3,37 @@ import { useState, useEffect } from 'react';
|
|||||||
import { isInputDOMNode } from '../utils';
|
import { isInputDOMNode } from '../utils';
|
||||||
import { KeyCode } from '../types';
|
import { KeyCode } from '../types';
|
||||||
|
|
||||||
export default (keyCode: KeyCode): boolean => {
|
export default (keyCode?: KeyCode): boolean => {
|
||||||
const [keyPressed, setKeyPressed] = useState(false);
|
const [keyPressed, setKeyPressed] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const downHandler = (event: KeyboardEvent) => {
|
if (typeof keyCode !== 'undefined') {
|
||||||
if (!isInputDOMNode(event) && (event.key === keyCode || event.keyCode === keyCode)) {
|
const downHandler = (event: KeyboardEvent) => {
|
||||||
event.preventDefault();
|
if (!isInputDOMNode(event) && (event.key === keyCode || event.keyCode === keyCode)) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
setKeyPressed(true);
|
setKeyPressed(true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const upHandler = (event: KeyboardEvent) => {
|
const upHandler = (event: KeyboardEvent) => {
|
||||||
if (!isInputDOMNode(event) && (event.key === keyCode || event.keyCode === keyCode)) {
|
if (!isInputDOMNode(event) && (event.key === keyCode || event.keyCode === keyCode)) {
|
||||||
setKeyPressed(false);
|
setKeyPressed(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const resetHandler = () => setKeyPressed(false);
|
const resetHandler = () => setKeyPressed(false);
|
||||||
|
|
||||||
window.addEventListener('keydown', downHandler);
|
window.addEventListener('keydown', downHandler);
|
||||||
window.addEventListener('keyup', upHandler);
|
window.addEventListener('keyup', upHandler);
|
||||||
window.addEventListener('blur', resetHandler);
|
window.addEventListener('blur', resetHandler);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener('keydown', downHandler);
|
window.removeEventListener('keydown', downHandler);
|
||||||
window.removeEventListener('keyup', upHandler);
|
window.removeEventListener('keyup', upHandler);
|
||||||
window.removeEventListener('blur', resetHandler);
|
window.removeEventListener('blur', resetHandler);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}, [keyCode, setKeyPressed]);
|
}, [keyCode, setKeyPressed]);
|
||||||
|
|
||||||
return keyPressed;
|
return keyPressed;
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ import { clamp } from '../utils';
|
|||||||
import { getRectOfNodes, isNode } from '../utils/graph';
|
import { getRectOfNodes, isNode } from '../utils/graph';
|
||||||
import { FitViewParams, FlowTransform, ZoomPanHelperFunctions, Rect, Transform } from '../types';
|
import { FitViewParams, FlowTransform, ZoomPanHelperFunctions, Rect, Transform } from '../types';
|
||||||
|
|
||||||
|
const DEFAULT_PADDING = 0.1;
|
||||||
|
|
||||||
const initialZoomPanHelper: ZoomPanHelperFunctions = {
|
const initialZoomPanHelper: ZoomPanHelperFunctions = {
|
||||||
zoomIn: () => {},
|
zoomIn: () => {},
|
||||||
zoomOut: () => {},
|
zoomOut: () => {},
|
||||||
zoomTo: (_: number) => {},
|
zoomTo: (_: number) => {},
|
||||||
transform: (_: FlowTransform) => {},
|
transform: (_: FlowTransform) => {},
|
||||||
fitView: (_: FitViewParams = { padding: 0.1 }) => {},
|
fitView: (_: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {},
|
||||||
setCenter: (_: number, __: number) => {},
|
setCenter: (_: number, __: number) => {},
|
||||||
fitBounds: (_: Rect) => {},
|
fitBounds: (_: Rect) => {},
|
||||||
initialized: false,
|
initialized: false,
|
||||||
@@ -23,7 +25,7 @@ const getTransformForBounds = (
|
|||||||
height: number,
|
height: number,
|
||||||
minZoom: number,
|
minZoom: number,
|
||||||
maxZoom: number,
|
maxZoom: number,
|
||||||
padding = 0.1
|
padding = DEFAULT_PADDING
|
||||||
): Transform => {
|
): Transform => {
|
||||||
const xZoom = width / (bounds.width * (1 + padding));
|
const xZoom = width / (bounds.width * (1 + padding));
|
||||||
const yZoom = height / (bounds.height * (1 + padding));
|
const yZoom = height / (bounds.height * (1 + padding));
|
||||||
@@ -37,7 +39,7 @@ const getTransformForBounds = (
|
|||||||
return [x, y, clampedZoom];
|
return [x, y, clampedZoom];
|
||||||
};
|
};
|
||||||
|
|
||||||
const usePanZoomHelper = (): ZoomPanHelperFunctions => {
|
const useZoomPanHelper = (): ZoomPanHelperFunctions => {
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
const d3Zoom = useStoreState((s) => s.d3Zoom);
|
const d3Zoom = useStoreState((s) => s.d3Zoom);
|
||||||
const d3Selection = useStoreState((s) => s.d3Selection);
|
const d3Selection = useStoreState((s) => s.d3Selection);
|
||||||
@@ -53,7 +55,7 @@ const usePanZoomHelper = (): ZoomPanHelperFunctions => {
|
|||||||
|
|
||||||
d3Zoom.transform(d3Selection, nextTransform);
|
d3Zoom.transform(d3Selection, nextTransform);
|
||||||
},
|
},
|
||||||
fitView: (options: FitViewParams = { padding: 0.1 }) => {
|
fitView: (options: FitViewParams = { padding: DEFAULT_PADDING, includeHiddenNodes: false }) => {
|
||||||
const { elements, width, height, minZoom, maxZoom } = store.getState();
|
const { elements, width, height, minZoom, maxZoom } = store.getState();
|
||||||
const nodes = elements.filter(isNode);
|
const nodes = elements.filter(isNode);
|
||||||
|
|
||||||
@@ -61,8 +63,9 @@ const usePanZoomHelper = (): ZoomPanHelperFunctions => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bounds = getRectOfNodes(nodes);
|
const bounds = getRectOfNodes(options.includeHiddenNodes ? nodes : nodes.filter((node) => !node.isHidden));
|
||||||
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, options.padding);
|
const padding = options.padding ?? DEFAULT_PADDING;
|
||||||
|
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding);
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
||||||
|
|
||||||
d3Zoom.transform(d3Selection, transform);
|
d3Zoom.transform(d3Selection, transform);
|
||||||
@@ -77,7 +80,7 @@ const usePanZoomHelper = (): ZoomPanHelperFunctions => {
|
|||||||
|
|
||||||
d3Zoom.transform(d3Selection, transform);
|
d3Zoom.transform(d3Selection, transform);
|
||||||
},
|
},
|
||||||
fitBounds: (bounds: Rect, padding = 0.1) => {
|
fitBounds: (bounds: Rect, padding = DEFAULT_PADDING) => {
|
||||||
const { width, height, minZoom, maxZoom } = store.getState();
|
const { width, height, minZoom, maxZoom } = store.getState();
|
||||||
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding);
|
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, padding);
|
||||||
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
const transform = zoomIdentity.translate(x, y).scale(zoom);
|
||||||
@@ -94,4 +97,4 @@ const usePanZoomHelper = (): ZoomPanHelperFunctions => {
|
|||||||
return zoomPanHelperFunctions;
|
return zoomPanHelperFunctions;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default usePanZoomHelper;
|
export default useZoomPanHelper;
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import {
|
|||||||
SetConnectionId,
|
SetConnectionId,
|
||||||
SnapGrid,
|
SnapGrid,
|
||||||
ConnectionMode,
|
ConnectionMode,
|
||||||
|
NodeExtent,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -53,6 +54,7 @@ import {
|
|||||||
SET_ELEMENTS_SELECTABLE,
|
SET_ELEMENTS_SELECTABLE,
|
||||||
SET_MULTI_SELECTION_ACTIVE,
|
SET_MULTI_SELECTION_ACTIVE,
|
||||||
SET_CONNECTION_MODE,
|
SET_CONNECTION_MODE,
|
||||||
|
SET_NODE_EXTENT,
|
||||||
} from './contants';
|
} from './contants';
|
||||||
|
|
||||||
export const setOnConnect = (onConnect: OnConnectFunc) =>
|
export const setOnConnect = (onConnect: OnConnectFunc) =>
|
||||||
@@ -158,6 +160,8 @@ export const setMultiSelectionActive = (multiSelectionActive: boolean) =>
|
|||||||
export const setConnectionMode = (connectionMode: ConnectionMode) =>
|
export const setConnectionMode = (connectionMode: ConnectionMode) =>
|
||||||
createAction(SET_CONNECTION_MODE, { connectionMode });
|
createAction(SET_CONNECTION_MODE, { connectionMode });
|
||||||
|
|
||||||
|
export const setNodeExtent = (nodeExtent: NodeExtent) => createAction(SET_NODE_EXTENT, { nodeExtent });
|
||||||
|
|
||||||
export type ReactFlowAction = ReturnType<
|
export type ReactFlowAction = ReturnType<
|
||||||
| typeof setOnConnect
|
| typeof setOnConnect
|
||||||
| typeof setOnConnectStart
|
| typeof setOnConnectStart
|
||||||
@@ -192,4 +196,5 @@ export type ReactFlowAction = ReturnType<
|
|||||||
| typeof setElementsSelectable
|
| typeof setElementsSelectable
|
||||||
| typeof setMultiSelectionActive
|
| typeof setMultiSelectionActive
|
||||||
| typeof setConnectionMode
|
| typeof setConnectionMode
|
||||||
|
| typeof setNodeExtent
|
||||||
>;
|
>;
|
||||||
|
|||||||
@@ -31,3 +31,4 @@ export const SET_NODES_CONNECTABLE = 'SET_NODES_CONNECTABLE';
|
|||||||
export const SET_ELEMENTS_SELECTABLE = 'SET_ELEMENTS_SELECTABLE';
|
export const SET_ELEMENTS_SELECTABLE = 'SET_ELEMENTS_SELECTABLE';
|
||||||
export const SET_MULTI_SELECTION_ACTIVE = 'SET_MULTI_SELECTION_ACTIVE';
|
export const SET_MULTI_SELECTION_ACTIVE = 'SET_MULTI_SELECTION_ACTIVE';
|
||||||
export const SET_CONNECTION_MODE = 'SET_CONNECTION_MODE';
|
export const SET_CONNECTION_MODE = 'SET_CONNECTION_MODE';
|
||||||
|
export const SET_NODE_EXTENT = 'SET_NODE_EXTENT';
|
||||||
|
|||||||
@@ -23,6 +23,11 @@ export const initialState: ReactFlowState = {
|
|||||||
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
|
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
|
||||||
],
|
],
|
||||||
|
|
||||||
|
nodeExtent: [
|
||||||
|
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
||||||
|
[Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY],
|
||||||
|
],
|
||||||
|
|
||||||
nodesSelectionActive: false,
|
nodesSelectionActive: false,
|
||||||
selectionActive: false,
|
selectionActive: false,
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
SET_MULTI_SELECTION_ACTIVE,
|
SET_MULTI_SELECTION_ACTIVE,
|
||||||
SET_NODES_CONNECTABLE,
|
SET_NODES_CONNECTABLE,
|
||||||
SET_NODES_DRAGGABLE,
|
SET_NODES_DRAGGABLE,
|
||||||
|
SET_NODE_EXTENT,
|
||||||
SET_ON_CONNECT,
|
SET_ON_CONNECT,
|
||||||
SET_ON_CONNECT_END,
|
SET_ON_CONNECT_END,
|
||||||
SET_ON_CONNECT_START,
|
SET_ON_CONNECT_START,
|
||||||
@@ -82,7 +83,7 @@ export default function reactFlowReducer(state = initialState, action: ReactFlow
|
|||||||
return storeElement;
|
return storeElement;
|
||||||
} else {
|
} else {
|
||||||
// add new element
|
// add new element
|
||||||
return parseElement(el);
|
return parseElement(el, state.nodeExtent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -389,6 +390,7 @@ export default function reactFlowReducer(state = initialState, action: ReactFlow
|
|||||||
case SET_ELEMENTS_SELECTABLE:
|
case SET_ELEMENTS_SELECTABLE:
|
||||||
case SET_MULTI_SELECTION_ACTIVE:
|
case SET_MULTI_SELECTION_ACTIVE:
|
||||||
case SET_CONNECTION_MODE:
|
case SET_CONNECTION_MODE:
|
||||||
|
case SET_NODE_EXTENT:
|
||||||
return { ...state, ...action.payload };
|
return { ...state, ...action.payload };
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
|
|||||||
@@ -31,8 +31,6 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
background: rgba(0, 89, 220, 0.08);
|
|
||||||
border: 1px dotted rgba(0, 89, 220, 0.8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-flow__edges {
|
.react-flow__edges {
|
||||||
@@ -49,23 +47,6 @@
|
|||||||
&.inactive {
|
&.inactive {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.selected {
|
|
||||||
.react-flow__edge-path {
|
|
||||||
stroke: #555;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.animated path {
|
|
||||||
stroke-dasharray: 5;
|
|
||||||
animation: dashdraw 0.5s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.updating {
|
|
||||||
.react-flow__edge-path {
|
|
||||||
stroke: #777;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes dashdraw {
|
@keyframes dashdraw {
|
||||||
@@ -76,20 +57,13 @@
|
|||||||
|
|
||||||
.react-flow__edge-path {
|
.react-flow__edge-path {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke: #b1b1b7;
|
|
||||||
stroke-width: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-flow__edge-text {
|
.react-flow__edge-text {
|
||||||
font-size: 10px;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-flow__edge-textbg {
|
|
||||||
fill: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__connection {
|
.react-flow__connection {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
|
||||||
@@ -101,8 +75,6 @@
|
|||||||
|
|
||||||
.react-flow__connection-path {
|
.react-flow__connection-path {
|
||||||
fill: none;
|
fill: none;
|
||||||
stroke: #b1b1b7;
|
|
||||||
stroke-width: 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-flow__nodes {
|
.react-flow__nodes {
|
||||||
@@ -119,70 +91,6 @@
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
transform-origin: 0 0;
|
transform-origin: 0 0;
|
||||||
cursor: grab;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__node-default,
|
|
||||||
.react-flow__node-input,
|
|
||||||
.react-flow__node-output {
|
|
||||||
padding: 10px;
|
|
||||||
border-radius: 3px;
|
|
||||||
width: 150px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: #222;
|
|
||||||
text-align: center;
|
|
||||||
border-width: 1px;
|
|
||||||
border-style: solid;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__node-default.selectable,
|
|
||||||
.react-flow__node-input.selectable,
|
|
||||||
.react-flow__node-output.selectable {
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 1px 4px 1px rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__node-input {
|
|
||||||
background: #fff;
|
|
||||||
border-color: #0041d0;
|
|
||||||
|
|
||||||
&.selected,
|
|
||||||
&.selected:hover {
|
|
||||||
box-shadow: 0 0 0 0.5px #0041d0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__handle {
|
|
||||||
background: #0041d0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__node-default {
|
|
||||||
background: #fff;
|
|
||||||
border-color: #1a192b;
|
|
||||||
|
|
||||||
&.selected,
|
|
||||||
&.selected:hover {
|
|
||||||
box-shadow: 0 0 0 0.5px #1a192b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__handle {
|
|
||||||
background: #1a192b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__node-output {
|
|
||||||
background: #fff;
|
|
||||||
border-color: #ff0072;
|
|
||||||
|
|
||||||
&.selected,
|
|
||||||
&.selected:hover {
|
|
||||||
box-shadow: 0 0 0 0.5px #ff0072;
|
|
||||||
}
|
|
||||||
|
|
||||||
.react-flow__handle {
|
|
||||||
background: #ff0072;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-flow__nodesselection {
|
.react-flow__nodesselection {
|
||||||
@@ -197,25 +105,16 @@
|
|||||||
|
|
||||||
&-rect {
|
&-rect {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
background: rgba(0, 89, 220, 0.08);
|
|
||||||
border: 1px dotted rgba(0, 89, 220, 0.8);
|
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
cursor: grab;
|
cursor: grab;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.react-flow__handle {
|
.react-flow__handle {
|
||||||
position: absolute;
|
|
||||||
width: 6px;
|
|
||||||
height: 6px;
|
|
||||||
background: #555;
|
|
||||||
border: 1px solid white;
|
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
border-radius: 100%;
|
|
||||||
|
|
||||||
&.connectable {
|
&.connectable {
|
||||||
pointer-events: all;
|
pointer-events: all;
|
||||||
cursor: crosshair;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,3 +146,40 @@
|
|||||||
.react-flow__edgeupdater {
|
.react-flow__edgeupdater {
|
||||||
cursor: move;
|
cursor: move;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* additional components */
|
||||||
|
|
||||||
|
.react-flow__background {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__controls {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 5;
|
||||||
|
bottom: 10px;
|
||||||
|
left: 10px;
|
||||||
|
|
||||||
|
&-button {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__minimap {
|
||||||
|
position: absolute;
|
||||||
|
z-index: 5;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
|
||||||
|
&-node {
|
||||||
|
shape-rendering: crispedges;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,157 @@
|
|||||||
|
.react-flow__selection {
|
||||||
|
background: rgba(0, 89, 220, 0.08);
|
||||||
|
border: 1px dotted rgba(0, 89, 220, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__edge {
|
||||||
|
&.selected {
|
||||||
|
.react-flow__edge-path {
|
||||||
|
stroke: #555;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.animated path {
|
||||||
|
stroke-dasharray: 5;
|
||||||
|
animation: dashdraw 0.5s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.updating {
|
||||||
|
.react-flow__edge-path {
|
||||||
|
stroke: #777;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__edge-path {
|
||||||
|
stroke: #b1b1b7;
|
||||||
|
stroke-width: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__edge-text {
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__edge-textbg {
|
||||||
|
fill: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__connection-path {
|
||||||
|
stroke: #b1b1b7;
|
||||||
|
stroke-width: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node {
|
||||||
|
cursor: grab;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node-default,
|
||||||
|
.react-flow__node-input,
|
||||||
|
.react-flow__node-output {
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 3px;
|
||||||
|
width: 150px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #222;
|
||||||
|
text-align: center;
|
||||||
|
border-width: 1px;
|
||||||
|
border-style: solid;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node-default.selectable,
|
||||||
|
.react-flow__node-input.selectable,
|
||||||
|
.react-flow__node-output.selectable {
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 1px 4px 1px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node-input {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #0041d0;
|
||||||
|
|
||||||
|
&.selected,
|
||||||
|
&.selected:hover {
|
||||||
|
box-shadow: 0 0 0 0.5px #0041d0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__handle {
|
||||||
|
background: #0041d0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node-default {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #1a192b;
|
||||||
|
|
||||||
|
&.selected,
|
||||||
|
&.selected:hover {
|
||||||
|
box-shadow: 0 0 0 0.5px #1a192b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__handle {
|
||||||
|
background: #1a192b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__node-output {
|
||||||
|
background: #fff;
|
||||||
|
border-color: #ff0072;
|
||||||
|
|
||||||
|
&.selected,
|
||||||
|
&.selected:hover {
|
||||||
|
box-shadow: 0 0 0 0.5px #ff0072;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__handle {
|
||||||
|
background: #ff0072;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__nodesselection-rect {
|
||||||
|
background: rgba(0, 89, 220, 0.08);
|
||||||
|
border: 1px dotted rgba(0, 89, 220, 0.8);
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__handle {
|
||||||
|
position: absolute;
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
background: #555;
|
||||||
|
border: 1px solid white;
|
||||||
|
border-radius: 100%;
|
||||||
|
|
||||||
|
&.connectable {
|
||||||
|
cursor: crosshair;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__minimap {
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__controls {
|
||||||
|
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
&-button {
|
||||||
|
background: #fefefe;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
box-sizing: content-box;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
max-width: 12px;
|
||||||
|
max-height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #f4f4f4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -197,6 +197,7 @@ export interface NodeComponentProps<T = any> {
|
|||||||
onMouseLeave?: (node: Node) => void;
|
onMouseLeave?: (node: Node) => void;
|
||||||
onContextMenu?: (node: Node) => void;
|
onContextMenu?: (node: Node) => void;
|
||||||
onNodeDragStart?: (node: Node) => void;
|
onNodeDragStart?: (node: Node) => void;
|
||||||
|
onNodeDrag?: (node: Node) => void;
|
||||||
onNodeDragStop?: (node: Node) => void;
|
onNodeDragStop?: (node: Node) => void;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
isDragging?: boolean;
|
isDragging?: boolean;
|
||||||
@@ -220,6 +221,7 @@ export interface WrapNodeProps<T = any> {
|
|||||||
onMouseLeave?: (event: ReactMouseEvent, node: Node) => void;
|
onMouseLeave?: (event: ReactMouseEvent, node: Node) => void;
|
||||||
onContextMenu?: (event: ReactMouseEvent, node: Node) => void;
|
onContextMenu?: (event: ReactMouseEvent, node: Node) => void;
|
||||||
onNodeDragStart?: (event: ReactMouseEvent, node: Node) => void;
|
onNodeDragStart?: (event: ReactMouseEvent, node: Node) => void;
|
||||||
|
onNodeDrag?: (event: ReactMouseEvent, node: Node) => void;
|
||||||
onNodeDragStop?: (event: ReactMouseEvent, node: Node) => void;
|
onNodeDragStop?: (event: ReactMouseEvent, node: Node) => void;
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
className?: string;
|
className?: string;
|
||||||
@@ -234,7 +236,8 @@ export interface WrapNodeProps<T = any> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type FitViewParams = {
|
export type FitViewParams = {
|
||||||
padding: number;
|
padding?: number;
|
||||||
|
includeHiddenNodes?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FlowExportObject<T = any> = {
|
export type FlowExportObject<T = any> = {
|
||||||
@@ -340,6 +343,7 @@ export type FlowTransform = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type TranslateExtent = [[number, number], [number, number]];
|
export type TranslateExtent = [[number, number], [number, number]];
|
||||||
|
export type NodeExtent = TranslateExtent;
|
||||||
|
|
||||||
export type KeyCode = number | string;
|
export type KeyCode = number | string;
|
||||||
|
|
||||||
@@ -354,7 +358,7 @@ export interface ZoomPanHelperFunctions {
|
|||||||
zoomOut: () => void;
|
zoomOut: () => void;
|
||||||
zoomTo: (zoomLevel: number) => void;
|
zoomTo: (zoomLevel: number) => void;
|
||||||
transform: (transform: FlowTransform) => void;
|
transform: (transform: FlowTransform) => void;
|
||||||
fitView: (params?: FitViewParams) => void;
|
fitView: FitViewFunc;
|
||||||
setCenter: (x: number, y: number, zoom?: number) => void;
|
setCenter: (x: number, y: number, zoom?: number) => void;
|
||||||
fitBounds: (bounds: Rect, padding?: number) => void;
|
fitBounds: (bounds: Rect, padding?: number) => void;
|
||||||
initialized: boolean;
|
initialized: boolean;
|
||||||
@@ -391,6 +395,7 @@ export interface ReactFlowState {
|
|||||||
minZoom: number;
|
minZoom: number;
|
||||||
maxZoom: number;
|
maxZoom: number;
|
||||||
translateExtent: TranslateExtent;
|
translateExtent: TranslateExtent;
|
||||||
|
nodeExtent: NodeExtent;
|
||||||
|
|
||||||
nodesSelectionActive: boolean;
|
nodesSelectionActive: boolean;
|
||||||
selectionActive: boolean;
|
selectionActive: boolean;
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import { Store } from 'redux';
|
import { Store } from 'redux';
|
||||||
|
|
||||||
|
import { clampPosition } from '../utils';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ElementId,
|
ElementId,
|
||||||
Node,
|
Node,
|
||||||
@@ -11,6 +14,7 @@ import {
|
|||||||
Connection,
|
Connection,
|
||||||
FlowExportObject,
|
FlowExportObject,
|
||||||
ReactFlowState,
|
ReactFlowState,
|
||||||
|
NodeExtent,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
|
|
||||||
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
|
export const isEdge = (element: Node | Connection | Edge): element is Edge =>
|
||||||
@@ -142,7 +146,7 @@ export const onLoadProject = (currentStore: Store<ReactFlowState>) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const parseElement = (element: Node | Edge): Node | Edge => {
|
export const parseElement = (element: Node | Edge, nodeExtent: NodeExtent): Node | Edge => {
|
||||||
if (!element.id) {
|
if (!element.id) {
|
||||||
throw new Error('All nodes and edges need to have an id.');
|
throw new Error('All nodes and edges need to have an id.');
|
||||||
}
|
}
|
||||||
@@ -164,7 +168,7 @@ export const parseElement = (element: Node | Edge): Node | Edge => {
|
|||||||
id: element.id.toString(),
|
id: element.id.toString(),
|
||||||
type: element.type || 'default',
|
type: element.type || 'default',
|
||||||
__rf: {
|
__rf: {
|
||||||
position: element.position,
|
position: clampPosition(element.position, nodeExtent),
|
||||||
width: null,
|
width: null,
|
||||||
height: null,
|
height: null,
|
||||||
handleBounds: {},
|
handleBounds: {},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { DraggableEvent } from 'react-draggable';
|
import { DraggableEvent } from 'react-draggable';
|
||||||
import { MouseEvent as ReactMouseEvent } from 'react';
|
import { MouseEvent as ReactMouseEvent } from 'react';
|
||||||
|
|
||||||
import { Dimensions } from '../types';
|
import { Dimensions, XYPosition, NodeExtent } from '../types';
|
||||||
|
|
||||||
export const isInputDOMNode = (e: ReactMouseEvent | DraggableEvent | KeyboardEvent) => {
|
export const isInputDOMNode = (e: ReactMouseEvent | DraggableEvent | KeyboardEvent) => {
|
||||||
const target = e?.target as HTMLElement;
|
const target = e?.target as HTMLElement;
|
||||||
@@ -17,3 +17,8 @@ export const getDimensions = (node: HTMLDivElement): Dimensions => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const clamp = (val: number, min: number = 0, max: number = 1): number => Math.min(Math.max(val, min), max);
|
export const clamp = (val: number, min: number = 0, max: number = 1): number => Math.min(Math.max(val, min), max);
|
||||||
|
|
||||||
|
export const clampPosition = (position: XYPosition, extent: NodeExtent) => ({
|
||||||
|
x: clamp(position.x, extent[0][0], extent[1][0]),
|
||||||
|
y: clamp(position.y, extent[0][1], extent[1][1]),
|
||||||
|
});
|
||||||
|
|||||||
@@ -7,17 +7,17 @@
|
|||||||
"jsx": "react",
|
"jsx": "react",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
"strict": false,
|
"strict": true,
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": true,
|
||||||
"strictNullChecks": false,
|
"strictNullChecks": true,
|
||||||
"strictFunctionTypes": false,
|
"strictFunctionTypes": true,
|
||||||
"strictPropertyInitialization": false,
|
"strictPropertyInitialization": true,
|
||||||
"noImplicitThis": false,
|
"noImplicitThis": true,
|
||||||
"alwaysStrict": false,
|
"alwaysStrict": true,
|
||||||
"noUnusedLocals": false,
|
"noUnusedLocals": true,
|
||||||
"noUnusedParameters": false,
|
"noUnusedParameters": true,
|
||||||
"noImplicitReturns": false,
|
"noImplicitReturns": false,
|
||||||
"noFallthroughCasesInSwitch": false,
|
"noFallthroughCasesInSwitch": true,
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"esModuleInterop": true
|
"esModuleInterop": true
|
||||||
},
|
},
|
||||||
|
|||||||