Refactor: CSS handling (#2344)

* refactor(css): only load base styles, add css task, cleanup exports
* style(base): add edge label bg
* refactor(css): use css-utils
* feat(core): add Panel component
* refactor(background): cleanup
* refactor(css-handling): cleanup
This commit is contained in:
Moritz Klack
2022-08-05 18:36:32 +02:00
committed by GitHub
parent af28431990
commit 97c22ace71
89 changed files with 1853 additions and 20334 deletions
+4
View File
@@ -1 +1,5 @@
nodeLinker: node-modules nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
-1
View File
@@ -4,5 +4,4 @@ module.exports = {
'@babel/preset-typescript', '@babel/preset-typescript',
'@babel/preset-env', '@babel/preset-env',
], ],
plugins: ['./tooling/inject-css-babel-plugin'],
}; };
+6 -2
View File
@@ -3,7 +3,8 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "yarn run copystyles && next dev",
"copystyles": "cp ../../packages/core/src/styles/theme-default.css ./styles/theme-default.css",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint" "lint": "next lint"
@@ -23,6 +24,9 @@
"devDependencies": { "devDependencies": {
"@types/dagre": "^0.7.47", "@types/dagre": "^0.7.47",
"eslint": "8.19.0", "eslint": "8.19.0",
"eslint-config-next": "12.2.2" "eslint-config-next": "12.2.2",
"postcss-flexbugs-fixes": "^5.0.2",
"postcss-nested": "^5.0.6",
"postcss-preset-env": "^7.7.2"
} }
} }
+4 -2
View File
@@ -1,12 +1,14 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import ReactFlow, { import {
ReactFlow,
Node, Node,
ReactFlowProvider, ReactFlowProvider,
useNodesState, useNodesState,
} from '@react-flow/core'; } from '@react-flow/core';
import Background, { import {
Background,
BackgroundProps, BackgroundProps,
BackgroundVariant, BackgroundVariant,
} from '@react-flow/background'; } from '@react-flow/background';
@@ -1,6 +1,6 @@
import React from 'react'; import React from 'react';
import {
import ReactFlow, { ReactFlow,
useReactFlow, useReactFlow,
Node, Node,
Edge, Edge,
@@ -8,8 +8,7 @@ import ReactFlow, {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/core';
import { Background, BackgroundVariant } from '@react-flow/background';
import Background, { BackgroundVariant } from '@react-flow/background';
const defaultNodes: Node[] = [ const defaultNodes: Node[] = [
{ {
@@ -1,5 +1,6 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
Node, Node,
addEdge, addEdge,
Connection, Connection,
@@ -7,8 +8,7 @@ import ReactFlow, {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/core';
import { Background, BackgroundVariant } from '@react-flow/background';
import Background, { BackgroundVariant } from '@react-flow/background';
import ConnectionLine from './ConnectionLine'; import ConnectionLine from './ConnectionLine';
@@ -1,12 +1,11 @@
import React, { memo, FC, CSSProperties } from 'react'; import React, { memo, FC, CSSProperties } from 'react';
import { import {
Handle, Handle,
Position, Position,
NodeProps, NodeProps,
Connection, Connection,
Edge, Edge,
} from '@react-flow/core'; } from '@react-flow/renderer';
const targetHandleStyle: CSSProperties = { background: '#555' }; const targetHandleStyle: CSSProperties = { background: '#555' };
const sourceHandleStyleA: CSSProperties = { ...targetHandleStyle, top: 10 }; const sourceHandleStyleA: CSSProperties = { ...targetHandleStyle, top: 10 };
@@ -23,7 +22,7 @@ const ColorSelectorNode: FC<NodeProps> = ({ data, isConnectable }) => {
return ( return (
<> <>
<Handle <Handle
type='target' type="target"
position={Position.Left} position={Position.Left}
style={targetHandleStyle} style={targetHandleStyle}
onConnect={onConnect} onConnect={onConnect}
@@ -32,15 +31,15 @@ const ColorSelectorNode: FC<NodeProps> = ({ data, isConnectable }) => {
Custom Color Picker Node: <strong>{data.color}</strong> Custom Color Picker Node: <strong>{data.color}</strong>
</div> </div>
<input <input
className='nodrag' className="nodrag"
type='color' type="color"
onChange={data.onChange} onChange={data.onChange}
defaultValue={data.color} defaultValue={data.color}
/> />
<Handle <Handle
type='source' type="source"
position={Position.Right} position={Position.Right}
id='a' id="a"
style={sourceHandleStyleA} style={sourceHandleStyleA}
isConnectable={isConnectable} isConnectable={isConnectable}
onMouseDown={(e) => { onMouseDown={(e) => {
@@ -48,9 +47,9 @@ const ColorSelectorNode: FC<NodeProps> = ({ data, isConnectable }) => {
}} }}
/> />
<Handle <Handle
type='source' type="source"
position={Position.Right} position={Position.Right}
id='b' id="b"
style={sourceHandleStyleB} style={sourceHandleStyleB}
isConnectable={isConnectable} isConnectable={isConnectable}
/> />
+6 -8
View File
@@ -1,7 +1,8 @@
import React, { useState, useEffect, MouseEvent } from 'react'; import React, { useState, useEffect, MouseEvent, ChangeEvent } from 'react';
import { ChangeEvent } from 'react'; import {
ReactFlow,
import ReactFlow, { MiniMap,
Controls,
addEdge, addEdge,
Node, Node,
ReactFlowInstance, ReactFlowInstance,
@@ -10,10 +11,7 @@ import ReactFlow, {
Connection, Connection,
useNodesState, useNodesState,
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/renderer';
import MiniMap from '@react-flow/minimap';
import Controls from '@react-flow/controls';
import ColorSelectorNode from './ColorSelectorNode'; import ColorSelectorNode from './ColorSelectorNode';
+3 -4
View File
@@ -1,13 +1,12 @@
import React from 'react'; import React from 'react';
import {
import ReactFlow, { ReactFlow,
useReactFlow, useReactFlow,
Node, Node,
Edge, Edge,
ReactFlowProvider, ReactFlowProvider,
} from '@react-flow/core'; } from '@react-flow/core';
import { Background, BackgroundVariant } from '@react-flow/background';
import Background, { BackgroundVariant } from '@react-flow/background';
const defaultNodes: Node[] = [ const defaultNodes: Node[] = [
{ {
+2 -1
View File
@@ -1,5 +1,6 @@
import React, { MouseEvent } from 'react'; import React, { MouseEvent } from 'react';
import ReactFlow, { import {
ReactFlow,
Node, Node,
Edge, Edge,
useNodesState, useNodesState,
+3 -2
View File
@@ -1,5 +1,6 @@
import React, { useState, DragEvent } from 'react'; import React, { useState, DragEvent } from 'react';
import ReactFlow, { import {
ReactFlow,
ReactFlowProvider, ReactFlowProvider,
addEdge, addEdge,
ReactFlowInstance, ReactFlowInstance,
@@ -9,7 +10,7 @@ import ReactFlow, {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/core';
import Controls from '@react-flow/controls'; import { Controls } from '@react-flow/controls';
import Sidebar from './Sidebar'; import Sidebar from './Sidebar';
+8 -8
View File
@@ -3,18 +3,18 @@
*/ */
import React from 'react'; import React from 'react';
import ReactFlow, { import {
ReactFlow,
Background,
Controls,
MiniMap,
addEdge, addEdge,
ReactFlowInstance, ReactFlowInstance,
Connection, Connection,
Edge, Edge,
useNodesState, useNodesState,
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/renderer';
import Background from '@react-flow/background';
import Controls from '@react-flow/controls';
import MiniMap from '@react-flow/minimap';
import { getElements } from './utils'; import { getElements } from './utils';
@@ -44,10 +44,10 @@ const EdgeTypesFlow = () => {
onConnect={onConnect} onConnect={onConnect}
minZoom={0.2} minZoom={0.2}
zoomOnScroll={false} zoomOnScroll={false}
selectionKeyCode='a+s' selectionKeyCode="a+s"
multiSelectionKeyCode={multiSelectionKeyCode} multiSelectionKeyCode={multiSelectionKeyCode}
deleteKeyCode={deleteKeyCode} deleteKeyCode={deleteKeyCode}
zoomActivationKeyCode='z' zoomActivationKeyCode="z"
> >
<MiniMap /> <MiniMap />
<Controls /> <Controls />
+6 -7
View File
@@ -1,19 +1,18 @@
import React, { MouseEvent, useCallback } from 'react'; import React, { MouseEvent, useCallback } from 'react';
import ReactFlow, { import {
Controls,
Background,
ReactFlow,
MiniMap,
addEdge, addEdge,
Connection, Connection,
Edge, Edge,
EdgeTypes, EdgeTypes,
MarkerType, MarkerType,
Node, Node,
ReactFlowInstance,
useEdgesState, useEdgesState,
useNodesState, useNodesState,
} from '@react-flow/core'; } from '@react-flow/renderer';
import Controls from '@react-flow/controls';
import Background from '@react-flow/background';
import MiniMap from '@react-flow/minimap';
import CustomEdge from './CustomEdge'; import CustomEdge from './CustomEdge';
import CustomEdge2 from './CustomEdge2'; import CustomEdge2 from './CustomEdge2';
+12 -9
View File
@@ -1,6 +1,10 @@
import React, { MouseEvent, CSSProperties } from 'react'; import React, { MouseEvent, CSSProperties, useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
Background,
BackgroundVariant,
Controls,
addEdge, addEdge,
Node, Node,
Connection, Connection,
@@ -8,10 +12,7 @@ import ReactFlow, {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
ReactFlowInstance, ReactFlowInstance,
} from '@react-flow/core'; } from '@react-flow/renderer';
import Controls from '@react-flow/controls';
import Background, { BackgroundVariant } from '@react-flow/background';
const onInit = (reactFlowInstance: ReactFlowInstance) => const onInit = (reactFlowInstance: ReactFlowInstance) =>
console.log('flow loaded:', reactFlowInstance); console.log('flow loaded:', reactFlowInstance);
@@ -30,8 +31,10 @@ const EmptyFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState([]); const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const onConnect = (params: Connection | Edge) => const onConnect = useCallback(
setEdges((els) => addEdge(params, els)); (params: Connection | Edge) => setEdges((els) => addEdge(params, els)),
[setEdges]
);
const addRandomNode = () => { const addRandomNode = () => {
const nodeId = (nodes.length + 1).toString(); const nodeId = (nodes.length + 1).toString();
const newNode: Node = { const newNode: Node = {
@@ -60,7 +63,7 @@ const EmptyFlow = () => {
<Controls /> <Controls />
<Background variant={BackgroundVariant.Lines} /> <Background variant={BackgroundVariant.Lines} />
<button type='button' onClick={addRandomNode} style={buttonStyle}> <button type="button" onClick={addRandomNode} style={buttonStyle}>
add node add node
</button> </button>
</ReactFlow> </ReactFlow>
@@ -1,15 +1,15 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
Background,
addEdge, addEdge,
ReactFlowInstance, ReactFlowInstance,
EdgeTypes, EdgeTypes,
Connection, Connection,
useNodesState, useNodesState,
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/renderer';
import Background from '@react-flow/background';
import styles from './style.module.css'; import styles from './style.module.css';
+8 -7
View File
@@ -1,6 +1,7 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
addEdge, addEdge,
Connection, Connection,
Edge, Edge,
@@ -9,8 +10,8 @@ import ReactFlow, {
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/core';
import Controls from '@react-flow/controls'; import { Controls } from '@react-flow/controls';
import MiniMap from '@react-flow/minimap'; import { MiniMap } from '@react-flow/minimap';
const initialNodes: Node[] = [ const initialNodes: Node[] = [
{ {
@@ -85,14 +86,14 @@ const HiddenFlow = () => {
<div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}> <div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
<div> <div>
<label htmlFor='ishidden'> <label htmlFor="ishidden">
isHidden isHidden
<input <input
id='ishidden' id="ishidden"
type='checkbox' type="checkbox"
checked={isHidden} checked={isHidden}
onChange={(event) => setIsHidden(event.target.checked)} onChange={(event) => setIsHidden(event.target.checked)}
className='react-flow__ishidden' className="react-flow__ishidden"
/> />
</label> </label>
</div> </div>
+54 -53
View File
@@ -3,7 +3,8 @@ import React, {
MouseEvent as ReactMouseEvent, MouseEvent as ReactMouseEvent,
WheelEvent, WheelEvent,
} from 'react'; } from 'react';
import ReactFlow, { import {
ReactFlow,
addEdge, addEdge,
Node, Node,
Connection, Connection,
@@ -14,8 +15,8 @@ import ReactFlow, {
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/core';
import Controls from '@react-flow/controls'; import { Controls } from '@react-flow/controls';
import MiniMap from '@react-flow/minimap'; import { MiniMap } from '@react-flow/minimap';
const initialNodes: Node[] = [ const initialNodes: Node[] = [
{ {
@@ -102,151 +103,151 @@ const InteractionFlow = () => {
<div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}> <div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
<div> <div>
<label htmlFor='draggable'> <label htmlFor="draggable">
nodesDraggable nodesDraggable
<input <input
id='draggable' id="draggable"
type='checkbox' type="checkbox"
checked={isDraggable} checked={isDraggable}
onChange={(event) => setIsDraggable(event.target.checked)} onChange={(event) => setIsDraggable(event.target.checked)}
className='react-flow__draggable' className="react-flow__draggable"
/> />
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='connectable'> <label htmlFor="connectable">
nodesConnectable nodesConnectable
<input <input
id='connectable' id="connectable"
type='checkbox' type="checkbox"
checked={isConnectable} checked={isConnectable}
onChange={(event) => setIsConnectable(event.target.checked)} onChange={(event) => setIsConnectable(event.target.checked)}
className='react-flow__connectable' className="react-flow__connectable"
/> />
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='selectable'> <label htmlFor="selectable">
elementsSelectable elementsSelectable
<input <input
id='selectable' id="selectable"
type='checkbox' type="checkbox"
checked={isSelectable} checked={isSelectable}
onChange={(event) => setIsSelectable(event.target.checked)} onChange={(event) => setIsSelectable(event.target.checked)}
className='react-flow__selectable' className="react-flow__selectable"
/> />
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='zoomonscroll'> <label htmlFor="zoomonscroll">
zoomOnScroll zoomOnScroll
<input <input
id='zoomonscroll' id="zoomonscroll"
type='checkbox' type="checkbox"
checked={zoomOnScroll} checked={zoomOnScroll}
onChange={(event) => setZoomOnScroll(event.target.checked)} onChange={(event) => setZoomOnScroll(event.target.checked)}
className='react-flow__zoomonscroll' className="react-flow__zoomonscroll"
/> />
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='zoomonpinch'> <label htmlFor="zoomonpinch">
zoomOnPinch zoomOnPinch
<input <input
id='zoomonpinch' id="zoomonpinch"
type='checkbox' type="checkbox"
checked={zoomOnPinch} checked={zoomOnPinch}
onChange={(event) => setZoomOnPinch(event.target.checked)} onChange={(event) => setZoomOnPinch(event.target.checked)}
className='react-flow__zoomonpinch' className="react-flow__zoomonpinch"
/> />
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='panonscroll'> <label htmlFor="panonscroll">
panOnScroll panOnScroll
<input <input
id='panonscroll' id="panonscroll"
type='checkbox' type="checkbox"
checked={panOnScroll} checked={panOnScroll}
onChange={(event) => setPanOnScroll(event.target.checked)} onChange={(event) => setPanOnScroll(event.target.checked)}
className='react-flow__panonscroll' className="react-flow__panonscroll"
/> />
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='panonscrollmode'> <label htmlFor="panonscrollmode">
panOnScrollMode panOnScrollMode
<select <select
id='panonscrollmode' id="panonscrollmode"
value={panOnScrollMode} value={panOnScrollMode}
onChange={(event) => onChange={(event) =>
setPanOnScrollMode(event.target.value as PanOnScrollMode) setPanOnScrollMode(event.target.value as PanOnScrollMode)
} }
className='react-flow__panonscrollmode' className="react-flow__panonscrollmode"
> >
<option value='free'>free</option> <option value="free">free</option>
<option value='horizontal'>horizontal</option> <option value="horizontal">horizontal</option>
<option value='vertical'>vertical</option> <option value="vertical">vertical</option>
</select> </select>
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='zoomondbl'> <label htmlFor="zoomondbl">
zoomOnDoubleClick zoomOnDoubleClick
<input <input
id='zoomondbl' id="zoomondbl"
type='checkbox' type="checkbox"
checked={zoomOnDoubleClick} checked={zoomOnDoubleClick}
onChange={(event) => setZoomOnDoubleClick(event.target.checked)} onChange={(event) => setZoomOnDoubleClick(event.target.checked)}
className='react-flow__zoomondbl' className="react-flow__zoomondbl"
/> />
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='panondrag'> <label htmlFor="panondrag">
panOnDrag panOnDrag
<input <input
id='panondrag' id="panondrag"
type='checkbox' type="checkbox"
checked={panOnDrag} checked={panOnDrag}
onChange={(event) => setPanOnDrag(event.target.checked)} onChange={(event) => setPanOnDrag(event.target.checked)}
className='react-flow__panondrag' className="react-flow__panondrag"
/> />
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='capturezoompaneclick'> <label htmlFor="capturezoompaneclick">
capture onPaneClick capture onPaneClick
<input <input
id='capturezoompaneclick' id="capturezoompaneclick"
type='checkbox' type="checkbox"
checked={captureZoomClick} checked={captureZoomClick}
onChange={(event) => setCaptureZoomClick(event.target.checked)} onChange={(event) => setCaptureZoomClick(event.target.checked)}
className='react-flow__capturezoompaneclick' className="react-flow__capturezoompaneclick"
/> />
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='capturezoompanescroll'> <label htmlFor="capturezoompanescroll">
capture onPaneScroll capture onPaneScroll
<input <input
id='capturezoompanescroll' id="capturezoompanescroll"
type='checkbox' type="checkbox"
checked={captureZoomScroll} checked={captureZoomScroll}
onChange={(event) => setCaptureZoomScroll(event.target.checked)} onChange={(event) => setCaptureZoomScroll(event.target.checked)}
className='react-flow__capturezoompanescroll' className="react-flow__capturezoompanescroll"
/> />
</label> </label>
</div> </div>
<div> <div>
<label htmlFor='captureelementclick'> <label htmlFor="captureelementclick">
capture onElementClick capture onElementClick
<input <input
id='captureelementclick' id="captureelementclick"
type='checkbox' type="checkbox"
checked={captureElementClick} checked={captureElementClick}
onChange={(event) => setCaptureElementClick(event.target.checked)} onChange={(event) => setCaptureElementClick(event.target.checked)}
className='react-flow__captureelementclick' className="react-flow__captureelementclick"
/> />
</label> </label>
</div> </div>
+4 -3
View File
@@ -1,5 +1,7 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import ReactFlow, { import {
Controls,
ReactFlow,
ReactFlowProvider, ReactFlowProvider,
addEdge, addEdge,
Connection, Connection,
@@ -9,9 +11,8 @@ import ReactFlow, {
useEdgesState, useEdgesState,
MarkerType, MarkerType,
EdgeMarker, EdgeMarker,
} from '@react-flow/core'; } from '@react-flow/renderer';
import Controls from '@react-flow/controls';
import dagre from 'dagre'; import dagre from 'dagre';
import initialItems from './initial-elements'; import initialItems from './initial-elements';
+6 -6
View File
@@ -1,6 +1,8 @@
import React, { FC } from 'react'; import React, { FC } from 'react';
import ReactFlow, { import {
ReactFlow,
Background,
addEdge, addEdge,
Node, Node,
Edge, Edge,
@@ -9,9 +11,7 @@ import ReactFlow, {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
MarkerType, MarkerType,
} from '@react-flow/core'; } from '@react-flow/renderer';
import Background from '@react-flow/background';
import styles from './multiflows.module.css'; import styles from './multiflows.module.css';
@@ -79,8 +79,8 @@ const Flow: FC<{ id: string }> = ({ id }) => {
const MultiFlows: FC = () => ( const MultiFlows: FC = () => (
<div className={styles.multiflows}> <div className={styles.multiflows}>
<Flow id='flow-a' /> <Flow id="flow-a" />
<Flow id='flow-b' /> <Flow id="flow-b" />
</div> </div>
); );
+12 -10
View File
@@ -1,6 +1,9 @@
import React, { useState, MouseEvent, useCallback } from 'react'; import React, { useState, MouseEvent, useCallback } from 'react';
import {
import ReactFlow, { Controls,
MiniMap,
Background,
ReactFlow,
addEdge, addEdge,
useNodesState, useNodesState,
useEdgesState, useEdgesState,
@@ -8,11 +11,7 @@ import ReactFlow, {
Edge, Edge,
ReactFlowInstance, ReactFlowInstance,
Connection, Connection,
} from '@react-flow/core'; } from '@react-flow/renderer';
import Controls from '@react-flow/controls';
import MiniMap from '@react-flow/minimap';
import Background from '@react-flow/background';
const onNodeDragStop = (_: MouseEvent, node: Node) => const onNodeDragStop = (_: MouseEvent, node: Node) =>
console.log('drag stop', node); console.log('drag stop', node);
@@ -105,9 +104,12 @@ const NestedFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback((connection: Connection) => { const onConnect = useCallback(
setEdges((eds) => addEdge(connection, eds)); (connection: Connection) => {
}, []); setEdges((eds) => addEdge(connection, eds));
},
[setEdges]
);
const onInit = useCallback( const onInit = useCallback(
(reactFlowInstance: ReactFlowInstance) => setRfInstance(reactFlowInstance), (reactFlowInstance: ReactFlowInstance) => setRfInstance(reactFlowInstance),
[] []
@@ -1,6 +1,7 @@
import React, { CSSProperties } from 'react'; import React, { CSSProperties, useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
addEdge, addEdge,
Node, Node,
Position, Position,
@@ -42,8 +43,10 @@ const buttonStyle: CSSProperties = {
const NodeTypeChangeFlow = () => { const NodeTypeChangeFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = (params: Connection | Edge) => const onConnect = useCallback(
setEdges((eds) => addEdge(params, eds)); (params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)),
[setEdges]
);
const changeType = () => { const changeType = () => {
setNodes((nds) => setNodes((nds) =>
nds.map((node) => { nds.map((node) => {
@@ -1,6 +1,7 @@
import React, { useState, CSSProperties, FC } from 'react'; import React, { useState, CSSProperties, FC, useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
addEdge, addEdge,
Node, Node,
Position, Position,
@@ -65,10 +66,12 @@ const nodeTypesObjects: NodeTypesObject = {
const NodeTypeChangeFlow = () => { const NodeTypeChangeFlow = () => {
const [nodeTypesId, setNodeTypesId] = useState<string>('a'); const [nodeTypesId, setNodeTypesId] = useState<string>('a');
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const onConnect = (params: Connection | Edge) => const onConnect = useCallback(
setEdges((eds) => addEdge(params, eds)); (params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)),
[setEdges]
);
const changeType = () => setNodeTypesId((nt) => (nt === 'a' ? 'b' : 'a')); const changeType = () => setNodeTypesId((nt) => (nt === 'a' ? 'b' : 'a'));
return ( return (
+5 -4
View File
@@ -4,7 +4,8 @@ import React, {
useCallback, useCallback,
} from 'react'; } from 'react';
import ReactFlow, { import {
ReactFlow,
addEdge, addEdge,
Node, Node,
Viewport, Viewport,
@@ -17,9 +18,9 @@ import ReactFlow, {
OnSelectionChangeParams, OnSelectionChangeParams,
} from '@react-flow/core'; } from '@react-flow/core';
import Controls from '@react-flow/controls'; import { Controls } from '@react-flow/controls';
import Background from '@react-flow/background'; import { Background } from '@react-flow/background';
import MiniMap from '@react-flow/minimap'; import { MiniMap } from '@react-flow/minimap';
const onNodeDragStart = (_: ReactMouseEvent, node: Node, nodes: Node[]) => const onNodeDragStart = (_: ReactMouseEvent, node: Node, nodes: Node[]) =>
console.log('drag start', node, nodes); console.log('drag start', node, nodes);
+9 -6
View File
@@ -1,5 +1,7 @@
import React, { MouseEvent } from 'react'; import React, { MouseEvent, useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
Controls,
ReactFlowProvider, ReactFlowProvider,
addEdge, addEdge,
Node, Node,
@@ -9,8 +11,7 @@ import ReactFlow, {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
ReactFlowInstance, ReactFlowInstance,
} from '@react-flow/core'; } from '@react-flow/renderer';
import Controls from '@react-flow/controls';
import Sidebar from './Sidebar'; import Sidebar from './Sidebar';
@@ -40,8 +41,10 @@ const initialEdges: Edge[] = [
const ProviderFlow = () => { const ProviderFlow = () => {
const [nodes, , onNodesChange] = useNodesState(initialNodes); const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = (params: Edge | Connection) => const onConnect = useCallback(
setEdges((els) => addEdge(params, els)); (params: Edge | Connection) => setEdges((els) => addEdge(params, els)),
[setEdges]
);
return ( return (
<div className={styles.providerflow}> <div className={styles.providerflow}>
+2 -2
View File
@@ -1,5 +1,6 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
ReactFlowProvider, ReactFlowProvider,
Node, Node,
addEdge, addEdge,
@@ -8,7 +9,6 @@ import ReactFlow, {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/core';
import Controls from './Controls'; import Controls from './Controls';
const initialNodes: Node[] = [ const initialNodes: Node[] = [
+5 -5
View File
@@ -1,5 +1,6 @@
import React, { useState, CSSProperties, useCallback } from 'react'; import React, { useState, CSSProperties, useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
ReactFlowInstance, ReactFlowInstance,
Edge, Edge,
Node, Node,
@@ -10,10 +11,9 @@ import ReactFlow, {
applyEdgeChanges, applyEdgeChanges,
EdgeChange, EdgeChange,
} from '@react-flow/core'; } from '@react-flow/core';
import { Controls } from '@react-flow/controls';
import Controls from '@react-flow/controls'; import { Background } from '@react-flow/background';
import Background from '@react-flow/background'; import { MiniMap } from '@react-flow/minimap';
import MiniMap from '@react-flow/minimap';
import { getNodesAndEdges } from './utils'; import { getNodesAndEdges } from './utils';
+5 -6
View File
@@ -1,6 +1,6 @@
import React, { useState, MouseEvent, useCallback } from 'react'; import React, { useState, MouseEvent, useCallback } from 'react';
import {
import ReactFlow, { ReactFlow,
addEdge, addEdge,
Node, Node,
Edge, Edge,
@@ -10,10 +10,9 @@ import ReactFlow, {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/core';
import { Controls } from '@react-flow/controls';
import Controls from '@react-flow/controls'; import { Background } from '@react-flow/background';
import Background from '@react-flow/background'; import { MiniMap } from '@react-flow/minimap';
import MiniMap from '@react-flow/minimap';
import DebugNode from './DebugNode'; import DebugNode from './DebugNode';
+2 -1
View File
@@ -1,5 +1,6 @@
import React, { MouseEvent, useCallback } from 'react'; import React, { MouseEvent, useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
addEdge, addEdge,
Node, Node,
Connection, Connection,
+2 -1
View File
@@ -1,5 +1,6 @@
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
Node, Node,
Edge, Edge,
useNodesState, useNodesState,
@@ -1,6 +1,7 @@
import React, { MouseEvent, useCallback } from 'react'; import React, { MouseEvent, useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
useReactFlow, useReactFlow,
NodeTypes, NodeTypes,
addEdge, addEdge,
@@ -1,5 +1,6 @@
import React, { useState, useCallback } from 'react'; import React, { useState, useCallback } from 'react';
import ReactFlow, { import {
ReactFlow,
updateEdge, updateEdge,
addEdge, addEdge,
applyNodeChanges, applyNodeChanges,
@@ -13,7 +14,7 @@ import ReactFlow, {
HandleType, HandleType,
} from '@react-flow/core'; } from '@react-flow/core';
import Controls from '@react-flow/controls'; import { Controls } from '@react-flow/controls';
const initialNodes: Node[] = [ const initialNodes: Node[] = [
{ {
+2 -1
View File
@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import ReactFlow, { import {
ReactFlow,
Node, Node,
Edge, Edge,
useNodesState, useNodesState,
+5 -6
View File
@@ -1,6 +1,8 @@
import React, { useCallback, MouseEvent, useEffect } from 'react'; import React, { useCallback, MouseEvent, useEffect } from 'react';
import {
import ReactFlow, { Background,
MiniMap,
ReactFlow,
Node, Node,
addEdge, addEdge,
useReactFlow, useReactFlow,
@@ -9,10 +11,7 @@ import ReactFlow, {
Edge, Edge,
useNodesState, useNodesState,
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/renderer';
import Background from '@react-flow/background';
import MiniMap from '@react-flow/minimap';
const initialNodes: Node[] = [ const initialNodes: Node[] = [
{ {
@@ -1,6 +1,6 @@
import React, { useCallback, CSSProperties, MouseEvent } from 'react'; import React, { useCallback, CSSProperties, MouseEvent } from 'react';
import {
import ReactFlow, { ReactFlow,
NodeTypes, NodeTypes,
addEdge, addEdge,
useReactFlow, useReactFlow,
@@ -13,6 +13,7 @@ import ReactFlow, {
useNodesState, useNodesState,
useEdgesState, useEdgesState,
} from '@react-flow/core'; } from '@react-flow/core';
import CustomNode from './CustomNode'; import CustomNode from './CustomNode';
const initialHandleCount = 1; const initialHandleCount = 1;
@@ -47,8 +48,10 @@ const getId = (): string => `${id++}`;
const UpdateNodeInternalsFlow = () => { const UpdateNodeInternalsFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState([]); const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const onConnect = (params: Edge | Connection) => const onConnect = useCallback(
setEdges((els) => addEdge(params, els)); (params: Edge | Connection) => setEdges((els) => addEdge(params, els)),
[setEdges]
);
const updateNodeInternals = useUpdateNodeInternals(); const updateNodeInternals = useUpdateNodeInternals();
const { project } = useReactFlow(); const { project } = useReactFlow();
+5 -4
View File
@@ -1,5 +1,6 @@
import React, { FC, useCallback, useState } from 'react'; import React, { FC, useCallback, useState } from 'react';
import ReactFlow, { import {
ReactFlow,
addEdge, addEdge,
Handle, Handle,
Connection, Connection,
@@ -28,7 +29,7 @@ const CustomInput: FC<NodeProps> = () => (
<> <>
<div>Only connectable with B</div> <div>Only connectable with B</div>
<Handle <Handle
type='source' type="source"
position={Position.Right} position={Position.Right}
isValidConnection={isValidConnection} isValidConnection={isValidConnection}
/> />
@@ -38,13 +39,13 @@ const CustomInput: FC<NodeProps> = () => (
const CustomNode: FC<NodeProps> = ({ id }) => ( const CustomNode: FC<NodeProps> = ({ id }) => (
<> <>
<Handle <Handle
type='target' type="target"
position={Position.Left} position={Position.Left}
isValidConnection={isValidConnection} isValidConnection={isValidConnection}
/> />
<div>{id}</div> <div>{id}</div>
<Handle <Handle
type='source' type="source"
position={Position.Right} position={Position.Right}
isValidConnection={isValidConnection} isValidConnection={isValidConnection}
/> />
+7
View File
@@ -2,6 +2,13 @@ import React from 'react';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import '../styles/globals.css'; import '../styles/globals.css';
// Unfortunately this doesn't work because preconsruct clears the dist folder and there is
// no way to hook into the watch process to copy the files back to the dist folder
// import '@react-flow/core/dist/theme-default.css';
// this is a workaround for testing the theme. See explanation above.
// import '../styles/theme-default.css';
const routes = [ const routes = [
'/', '/',
'/Backgrounds', '/Backgrounds',
+6 -5
View File
@@ -1,15 +1,16 @@
import React, { MouseEvent } from 'react'; import React, { MouseEvent } from 'react';
import ReactFlow, { import {
ReactFlow,
ReactFlowProvider, ReactFlowProvider,
Node, Node,
Edge, Edge,
useReactFlow, useReactFlow,
} from '@react-flow/core'; } from '@react-flow/core';
import Minimap from '@react-flow/minimap'; import { MiniMap } from '@react-flow/minimap';
import Background, { BackgroundVariant } from '@react-flow/background'; import { Background, BackgroundVariant } from '@react-flow/background';
import Controls from '@react-flow/controls'; import { Controls } from '@react-flow/controls';
const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node); const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node);
const onNodeDragStop = (_: MouseEvent, node: Node) => const onNodeDragStop = (_: MouseEvent, node: Node) =>
@@ -95,7 +96,7 @@ const BasicFlow = () => {
selectNodesOnDrag={false} selectNodesOnDrag={false}
> >
<Background variant={BackgroundVariant.Lines} /> <Background variant={BackgroundVariant.Lines} />
<Minimap /> <MiniMap />
<Controls /> <Controls />
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}> <div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
@@ -3,6 +3,7 @@
&:focus, &:focus,
&:focus-visible { &:focus-visible {
outline: none; outline: none;
.react-flow__edge-path { .react-flow__edge-path {
stroke: #555; stroke: #555;
} }
@@ -29,10 +30,6 @@
font-size: 10px; font-size: 10px;
} }
.react-flow__edge-textbg {
fill: white;
}
.react-flow__connection-path { .react-flow__connection-path {
stroke: #b1b1b7; stroke: #b1b1b7;
stroke-width: 1; stroke-width: 1;
+18
View File
@@ -0,0 +1,18 @@
{
"plugins": [
"postcss-nested",
"postcss-flexbugs-fixes",
[
"postcss-preset-env",
{
"autoprefixer": {
"flexbox": "no-2009"
},
"stage": 3,
"features": {
"custom-properties": false
}
}
]
]
}
+106
View File
@@ -0,0 +1,106 @@
.react-flow__edge {
&.selected,
&:focus,
&:focus-visible {
outline: none;
.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__connection-path {
stroke: #b1b1b7;
stroke-width: 1;
}
.react-flow__node-default,
.react-flow__node-input,
.react-flow__node-output,
.react-flow__node-group {
padding: 10px;
border-radius: 3px;
width: 150px;
font-size: 12px;
color: #222;
text-align: center;
border-width: 1px;
border-style: solid;
border-color: #1a192b;
&.selected,
&:focus,
&:focus-visible {
box-shadow: 0 0 0 0.5px #1a192b;
outline: none;
}
.react-flow__handle {
background: #1a192b;
}
}
.react-flow__node-default.selectable,
.react-flow__node-input.selectable,
.react-flow__node-output.selectable,
.react-flow__node-group.selectable {
&:hover {
box-shadow: 0 1px 4px 1px rgba(0, 0, 0, 0.08);
}
&.selected,
&:focus,
&:focus-visible {
outline: none;
box-shadow: 0 0 0 0.5px #1a192b;
}
}
.react-flow__node-group {
background: rgba(240, 240, 240, 0.25);
border-color: #1a192b;
}
.react-flow__nodesselection-rect,
.react-flow__selection {
background: rgba(0, 89, 220, 0.08);
border: 1px dotted rgba(0, 89, 220, 0.8);
&:focus,
&:focus-visible {
outline: none;
}
}
.react-flow__handle {
width: 6px;
height: 6px;
background: #555;
border: 1px solid white;
border-radius: 100%;
&.connectable {
cursor: crosshair;
}
}
+3 -5
View File
@@ -10,10 +10,11 @@
], ],
"private": true, "private": true,
"scripts": { "scripts": {
"postinstall": "preconstruct dev && yarn run packages",
"dev": "preconstruct watch", "dev": "preconstruct watch",
"dev:example": "cd examples/nextjs && yarn dev", "dev:example": "cd examples/nextjs && yarn dev",
"postinstall": "preconstruct dev", "build": "preconstruct build && yarn run packages",
"build": "preconstruct build", "packages": "yarn workspaces foreach --include '@react-flow/**' run build",
"test:all": "yarn test:chrome && yarn test:firefox", "test:all": "yarn test:chrome && yarn test:firefox",
"test:chrome": "cypress run --browser chrome", "test:chrome": "cypress run --browser chrome",
"test:firefox": "cypress run --browser firefox", "test:firefox": "cypress run --browser firefox",
@@ -29,10 +30,7 @@
"@babel/preset-react": "^7.17.12", "@babel/preset-react": "^7.17.12",
"@babel/preset-typescript": "^7.17.12", "@babel/preset-typescript": "^7.17.12",
"@preconstruct/cli": "^2.1.8", "@preconstruct/cli": "^2.1.8",
"autoprefixer": "^10.4.7",
"cypress": "^10.3.1", "cypress": "^10.3.1",
"postcss": "^8.4.14",
"postcss-nested": "^5.0.6",
"start-server-and-test": "^1.14.0", "start-server-and-test": "^1.14.0",
"typescript": "^4.7.4" "typescript": "^4.7.4"
}, },
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "@react-flow/background", "name": "@react-flow/background",
"version": "11.0.0", "version": "11.0.0",
"description": "A background component for React Flow", "description": "Background component with different variants for React Flow",
"main": "dist/react-flow-background.cjs.js", "main": "dist/react-flow-background.cjs.js",
"module": "dist/react-flow-background.esm.js", "module": "dist/react-flow-background.esm.js",
"license": "MIT", "license": "MIT",
+9 -10
View File
@@ -1,11 +1,11 @@
import React, { memo, FC, useRef, useId } from 'react'; import React, { memo, useRef, useId } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import { useStore, ReactFlowState } from '@react-flow/core'; import { useStore, ReactFlowState } from '@react-flow/core';
import { BackgroundProps, BackgroundVariant } from './types'; import { BackgroundProps, BackgroundVariant } from './types';
import { DotPattern, LinePattern } from './utils'; import { DotPattern, LinePattern } from './Patterns';
const defaultColors = { const defaultColor = {
[BackgroundVariant.Dots]: '#91919a', [BackgroundVariant.Dots]: '#91919a',
[BackgroundVariant.Lines]: '#eee', [BackgroundVariant.Lines]: '#eee',
[BackgroundVariant.Cross]: '#e2e2e2', [BackgroundVariant.Cross]: '#e2e2e2',
@@ -34,8 +34,8 @@ function Background({
const patternId = useId(); const patternId = useId();
const [tX, tY, tScale] = useStore(transformSelector); const [tX, tY, tScale] = useStore(transformSelector);
const patternColor = color ? color : defaultColors[variant]; const patternColor = color || defaultColor[variant];
const patternSize = size ? size : defaultSize[variant]; const patternSize = size || defaultSize[variant];
const isDots = variant === BackgroundVariant.Dots; const isDots = variant === BackgroundVariant.Dots;
const isCross = variant === BackgroundVariant.Cross; const isCross = variant === BackgroundVariant.Cross;
const gapXY: [number, number] = Array.isArray(gap) ? gap : [gap, gap]; const gapXY: [number, number] = Array.isArray(gap) ? gap : [gap, gap];
@@ -55,15 +55,14 @@ function Background({
return ( return (
<svg <svg
className={cc([ className={cc(['react-flow__background', className])}
'react-flow__background',
'react-flow__container',
className,
])}
style={{ style={{
...style, ...style,
position: 'absolute',
width: '100%', width: '100%',
height: '100%', height: '100%',
top: 0,
left: 0,
}} }}
ref={ref} ref={ref}
> >
+1 -1
View File
@@ -1,2 +1,2 @@
export { default as Background, default } from './Background'; export { default as Background } from './Background';
export * from './types'; export * from './types';
+3 -2
View File
@@ -1,12 +1,13 @@
{ {
"name": "@react-flow/controls", "name": "@react-flow/controls",
"version": "11.0.0", "version": "11.0.0",
"description": "A component that controls the viewport of React Flow", "description": "A component to control the viewport of a React Flow instance",
"main": "dist/react-flow-controls.cjs.js", "main": "dist/react-flow-controls.cjs.js",
"module": "dist/react-flow-controls.esm.js", "module": "dist/react-flow-controls.esm.js",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@react-flow/core": "11.0.0", "@react-flow/core": "^11.0.0",
"@react-flow/css-utils": "^11.0.0",
"classcat": "^5.0.3" "classcat": "^5.0.3"
}, },
"peerDependencies": { "peerDependencies": {
+22
View File
@@ -0,0 +1,22 @@
import React, { FC, PropsWithChildren } from 'react';
import cc from 'classcat';
import { ControlButtonProps } from './types';
const ControlButton: FC<PropsWithChildren<ControlButtonProps>> = ({
children,
className,
...rest
}) => (
<button
type="button"
className={cc(['react-flow__controls-button', className])}
{...rest}
>
{children}
</button>
);
ControlButton.displayName = 'ControlButton';
export default ControlButton;
+25 -30
View File
@@ -5,31 +5,21 @@ import {
useStoreApi, useStoreApi,
useReactFlow, useReactFlow,
ReactFlowState, ReactFlowState,
Panel,
} from '@react-flow/core'; } from '@react-flow/core';
import { injectStyle } from '@react-flow/css-utils';
import PlusIcon from './Icons/Plus'; import PlusIcon from './Icons/Plus';
import MinusIcon from './Icons/Minus'; import MinusIcon from './Icons/Minus';
import FitviewIcon from './Icons/FitView'; import FitviewIcon from './Icons/FitView';
import LockIcon from './Icons/Lock'; import LockIcon from './Icons/Lock';
import UnlockIcon from './Icons/Unlock'; import UnlockIcon from './Icons/Unlock';
import ControlButton from './ControlButton';
import baseStyle from './style';
import { ControlProps, ControlButtonProps } from './types'; import { ControlProps } from './types';
import './style.css'; injectStyle(baseStyle);
export const ControlButton: FC<PropsWithChildren<ControlButtonProps>> = ({
children,
className,
...rest
}) => (
<button
type='button'
className={cc(['react-flow__controls-button', className])}
{...rest}
>
{children}
</button>
);
const isInteractiveSelector = (s: ReactFlowState) => const isInteractiveSelector = (s: ReactFlowState) =>
s.nodesDraggable && s.nodesConnectable && s.elementsSelectable; s.nodesDraggable && s.nodesConnectable && s.elementsSelectable;
@@ -46,6 +36,7 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
onInteractiveChange, onInteractiveChange,
className, className,
children, children,
position = 'bottom-left',
}) => { }) => {
const store = useStoreApi(); const store = useStoreApi();
const [isVisible, setIsVisible] = useState<boolean>(false); const [isVisible, setIsVisible] = useState<boolean>(false);
@@ -86,22 +77,26 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
}; };
return ( return (
<div className={cc(['react-flow__controls', className])} style={style}> <Panel
className={cc(['react-flow__controls', className])}
position={position}
style={style}
>
{showZoom && ( {showZoom && (
<> <>
<ControlButton <ControlButton
onClick={onZoomInHandler} onClick={onZoomInHandler}
className='react-flow__controls-zoomin' className="react-flow__controls-zoomin"
title='zoom in' title="zoom in"
aria-label='zoom in' aria-label="zoom in"
> >
<PlusIcon /> <PlusIcon />
</ControlButton> </ControlButton>
<ControlButton <ControlButton
onClick={onZoomOutHandler} onClick={onZoomOutHandler}
className='react-flow__controls-zoomout' className="react-flow__controls-zoomout"
title='zoom out' title="zoom out"
aria-label='zoom out' aria-label="zoom out"
> >
<MinusIcon /> <MinusIcon />
</ControlButton> </ControlButton>
@@ -109,26 +104,26 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
)} )}
{showFitView && ( {showFitView && (
<ControlButton <ControlButton
className='react-flow__controls-fitview' className="react-flow__controls-fitview"
onClick={onFitViewHandler} onClick={onFitViewHandler}
title='fit view' title="fit view"
aria-label='fit view' aria-label="fit view"
> >
<FitviewIcon /> <FitviewIcon />
</ControlButton> </ControlButton>
)} )}
{showInteractive && ( {showInteractive && (
<ControlButton <ControlButton
className='react-flow__controls-interactive' className="react-flow__controls-interactive"
onClick={onToggleInteractivity} onClick={onToggleInteractivity}
title='toggle interactivity' title="toggle interactivity"
aria-label='toggle interactivity' aria-label="toggle interactivity"
> >
{isInteractive ? <UnlockIcon /> : <LockIcon />} {isInteractive ? <UnlockIcon /> : <LockIcon />}
</ControlButton> </ControlButton>
)} )}
{children} {children}
</div> </Panel>
); );
}; };
+2 -2
View File
@@ -1,3 +1,3 @@
export { default as default } from './Controls'; export { default as Controls } from './Controls';
export { default as ControlButton } from './ControlButton';
export * from './types'; export * from './types';
export * from './Controls';
-32
View File
@@ -1,32 +0,0 @@
.react-flow__controls {
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.08);
position: absolute;
z-index: 5;
bottom: 20px;
left: 15px;
&-button {
border: none;
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%;
max-width: 12px;
max-height: 12px;
}
&:hover {
background: #f4f4f4;
}
}
}
+31
View File
@@ -0,0 +1,31 @@
const style = `
.react-flow__controls {
box-shadow: 0 0 2px 1px rgba(0, 0, 0, 0.08);
}
.react-flow__controls-button {
border: none;
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;
}
.react-flow__controls-button svg {
width: 100%;
max-width: 12px;
max-height: 12px;
}
.react-flow__controls-button:hover {
background: #f4f4f4;
}`;
export default style;
+2 -1
View File
@@ -1,5 +1,5 @@
import { ButtonHTMLAttributes, HTMLAttributes } from 'react'; import { ButtonHTMLAttributes, HTMLAttributes } from 'react';
import { FitViewOptions } from '@react-flow/core'; import { FitViewOptions, PanelPosition } from '@react-flow/core';
export interface ControlProps extends HTMLAttributes<HTMLDivElement> { export interface ControlProps extends HTMLAttributes<HTMLDivElement> {
showZoom?: boolean; showZoom?: boolean;
@@ -10,6 +10,7 @@ export interface ControlProps extends HTMLAttributes<HTMLDivElement> {
onZoomOut?: () => void; onZoomOut?: () => void;
onFitView?: () => void; onFitView?: () => void;
onInteractiveChange?: (interactiveStatus: boolean) => void; onInteractiveChange?: (interactiveStatus: boolean) => void;
position?: PanelPosition;
} }
export interface ControlButtonProps export interface ControlButtonProps
-3
View File
@@ -1,3 +0,0 @@
<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: 463 B

-3
View File
@@ -1,3 +0,0 @@
<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: 530 B

-3
View File
@@ -1,3 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 5">
<path d="M0 0h32v4.2H0z"/>
</svg>

Before

Width:  |  Height:  |  Size: 96 B

-3
View File
@@ -1,3 +0,0 @@
<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: 152 B

-3
View File
@@ -1,3 +0,0 @@
<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: 472 B

-19621
View File
File diff suppressed because it is too large Load Diff
+10 -1
View File
@@ -12,8 +12,12 @@
"type": "git", "type": "git",
"url": "https://github.com/wbkd/react-flow.git" "url": "https://github.com/wbkd/react-flow.git"
}, },
"scripts": {
"build": "postcss src/styles/*.css --config ../../tooling/postcss.config.json --dir dist"
},
"dependencies": { "dependencies": {
"@babel/runtime": "^7.18.0", "@babel/runtime": "^7.18.0",
"@react-flow/css-utils": "^11.0.0",
"classcat": "^5.0.3", "classcat": "^5.0.3",
"d3-drag": "^3.0.0", "d3-drag": "^3.0.0",
"d3-selection": "^3.0.0", "d3-selection": "^3.0.0",
@@ -30,6 +34,11 @@
], ],
"devDependencies": { "devDependencies": {
"@types/d3": "^7.4.0", "@types/d3": "^7.4.0",
"@types/react": "^18.0.15" "@types/react": "^18.0.15",
"autoprefixer": "^10.4.8",
"postcss": "^8.4.14",
"postcss-cli": "^10.0.0",
"postcss-import": "^14.1.0",
"postcss-nested": "^5.0.6"
} }
} }
@@ -1,11 +1,11 @@
import React from 'react'; import React from 'react';
import cc from 'classcat';
import { AttributionPosition, ProOptions } from '../../types'; import Panel from '../Panel';
import { PanelPosition, ProOptions } from '../../types';
type AttributionProps = { type AttributionProps = {
proOptions?: ProOptions; proOptions?: ProOptions;
position?: AttributionPosition; position?: PanelPosition;
}; };
function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps) { function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps) {
@@ -13,17 +13,16 @@ function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps
return null; return null;
} }
const positionClasses = `${position}`.split('-');
return ( return (
<div <Panel
className={cc(['react-flow__attribution', ...positionClasses])} position={position}
className="react-flow__attribution"
data-message="Please only hide this attribution when you are subscribed to React Flow Pro: https://pro.reactflow.dev" data-message="Please only hide this attribution when you are subscribed to React Flow Pro: https://pro.reactflow.dev"
> >
<a href="https://reactflow.dev" target="_blank" rel="noopener noreferrer" aria-label="React Flow attribution"> <a href="https://reactflow.dev" target="_blank" rel="noopener noreferrer" aria-label="React Flow attribution">
React Flow React Flow
</a> </a>
</div> </Panel>
); );
} }
@@ -113,7 +113,7 @@ export default ({
return ( return (
<g className="react-flow__connection"> <g className="react-flow__connection">
<path d={dAttr} className="react-flow__connection-path" style={connectionLineStyle} /> <path d={dAttr} fill="none" className="react-flow__connection-path" style={connectionLineStyle} />
</g> </g>
); );
}; };
@@ -32,7 +32,14 @@ export default ({
return ( return (
<> <>
<path style={style} d={path} className="react-flow__edge-path" markerEnd={markerEnd} markerStart={markerStart} /> <path
style={style}
d={path}
fill="none"
className="react-flow__edge-path"
markerEnd={markerEnd}
markerStart={markerStart}
/>
{text} {text}
</> </>
); );
@@ -0,0 +1,21 @@
import React, { HTMLAttributes, ReactNode } from 'react';
import cc from 'classcat';
import { PanelPosition } from '../../types';
export type PanelProps = HTMLAttributes<HTMLDivElement> & {
position: PanelPosition;
children: ReactNode;
};
function Panel({ position, children, className, ...rest }: PanelProps) {
const positionClasses = `${position}`.split('-');
return (
<div className={cc(['react-flow__panel', className, ...positionClasses])} {...rest}>
{children}
</div>
);
}
export default Panel;
@@ -9,6 +9,7 @@ import UserSelection from '../../components/UserSelection';
import NodesSelection from '../../components/NodesSelection'; import NodesSelection from '../../components/NodesSelection';
import { ReactFlowState } from '../../types'; import { ReactFlowState } from '../../types';
import { containerStyle } from '../../styles';
export type FlowRendererProps = Omit< export type FlowRendererProps = Omit<
GraphViewProps, GraphViewProps,
@@ -104,13 +105,14 @@ const FlowRenderer = ({
/> />
)} )}
<div <div
className="react-flow__pane react-flow__container" className="react-flow__pane"
onClick={onClick} onClick={onClick}
onMouseEnter={onPaneMouseEnter} onMouseEnter={onPaneMouseEnter}
onMouseMove={onPaneMouseMove} onMouseMove={onPaneMouseMove}
onMouseLeave={onPaneMouseLeave} onMouseLeave={onPaneMouseLeave}
onContextMenu={onContextMenu} onContextMenu={onContextMenu}
onWheel={onWheel} onWheel={onWheel}
style={containerStyle}
/> />
</ZoomPane> </ZoomPane>
); );
@@ -3,6 +3,7 @@ import shallow from 'zustand/shallow';
import useVisibleNodes from '../../hooks/useVisibleNodes'; import useVisibleNodes from '../../hooks/useVisibleNodes';
import { useStore } from '../../store'; import { useStore } from '../../store';
import { containerStyle } from '../../styles';
import { NodeMouseHandler, NodeTypesWrapped, Position, ReactFlowState, WrapNodeProps } from '../../types'; import { NodeMouseHandler, NodeTypesWrapped, Position, ReactFlowState, WrapNodeProps } from '../../types';
import { internalsSymbol } from '../../utils'; import { internalsSymbol } from '../../utils';
@@ -61,7 +62,7 @@ const NodeRenderer = (props: NodeRendererProps) => {
}, []); }, []);
return ( return (
<div className="react-flow__nodes react-flow__container"> <div className="react-flow__nodes" style={containerStyle}>
{nodes.map((node) => { {nodes.map((node) => {
let nodeType = node.type || 'default'; let nodeType = node.type || 'default';
@@ -1,5 +1,6 @@
import React, { forwardRef, useId } from 'react'; import React, { CSSProperties, forwardRef, useId } from 'react';
import cc from 'classcat'; import cc from 'classcat';
import { injectStyle } from '@react-flow/css-utils';
import Attribution from '../../components/Attribution'; import Attribution from '../../components/Attribution';
import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges'; import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges';
@@ -8,9 +9,9 @@ import InputNode from '../../components/Nodes/InputNode';
import OutputNode from '../../components/Nodes/OutputNode'; import OutputNode from '../../components/Nodes/OutputNode';
import SelectionListener from '../../components/SelectionListener'; import SelectionListener from '../../components/SelectionListener';
import StoreUpdater from '../../components/StoreUpdater'; import StoreUpdater from '../../components/StoreUpdater';
import baseStyle from '../../styles/base';
import '../../style.css'; injectStyle(baseStyle);
import '../../theme-default.css';
import { import {
ConnectionLineType, ConnectionLineType,
@@ -48,6 +49,13 @@ const defaultEdgeTypes: EdgeTypes = {
const initSnapGrid: [number, number] = [15, 15]; const initSnapGrid: [number, number] = [15, 15];
const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 }; const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 };
const wrapperStyle: CSSProperties = {
width: '100%',
height: '100%',
position: 'relative',
overflow: 'hidden',
};
const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>( const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
( (
{ {
@@ -143,6 +151,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
defaultEdgeOptions, defaultEdgeOptions,
elevateEdgesOnSelect = false, elevateEdgesOnSelect = false,
disableKeyboardA11y = false, disableKeyboardA11y = false,
style,
...rest ...rest
}, },
ref ref
@@ -152,7 +161,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
const rfId = useId(); const rfId = useId();
return ( return (
<div {...rest} ref={ref} className={cc(['react-flow', className])}> <div {...rest} style={{ ...style, ...wrapperStyle }} ref={ref} className={cc(['react-flow', className])}>
<Wrapper> <Wrapper>
<GraphView <GraphView
onInit={onInit} onInit={onInit}
@@ -9,6 +9,7 @@ import useResizeHandler from '../../hooks/useResizeHandler';
import { useStore, useStoreApi } from '../../store'; import { useStore, useStoreApi } from '../../store';
import { Viewport, PanOnScrollMode, ReactFlowState } from '../../types'; import { Viewport, PanOnScrollMode, ReactFlowState } from '../../types';
import { FlowRendererProps } from '../FlowRenderer'; import { FlowRendererProps } from '../FlowRenderer';
import { containerStyle } from '../../styles';
type ZoomPaneProps = Omit< type ZoomPaneProps = Omit<
FlowRendererProps, FlowRendererProps,
@@ -253,7 +254,7 @@ const ZoomPane = ({
]); ]);
return ( return (
<div className="react-flow__renderer react-flow__container" ref={zoomPane}> <div className="react-flow__renderer" ref={zoomPane} style={containerStyle}>
{children} {children}
</div> </div>
); );
+3 -5
View File
@@ -1,7 +1,4 @@
import ReactFlow from './container/ReactFlow'; export { default as ReactFlow } from './container/ReactFlow';
export default ReactFlow;
export { default as Handle } from './components/Handle'; export { default as Handle } from './components/Handle';
export { default as EdgeText } from './components/Edges/EdgeText'; export { default as EdgeText } from './components/Edges/EdgeText';
export { default as StraightEdge } from './components/Edges/StraightEdge'; export { default as StraightEdge } from './components/Edges/StraightEdge';
@@ -18,7 +15,7 @@ export {
} from './components/Edges/SimpleBezierEdge'; } from './components/Edges/SimpleBezierEdge';
export { default as SmoothStepEdge, getSmoothStepPath } from './components/Edges/SmoothStepEdge'; export { default as SmoothStepEdge, getSmoothStepPath } from './components/Edges/SmoothStepEdge';
export { internalsSymbol } from './utils'; export { internalsSymbol, rectToBox, boxToRect, getBoundsOfRects } from './utils';
export { export {
isNode, isNode,
isEdge, isEdge,
@@ -33,6 +30,7 @@ export {
export { applyNodeChanges, applyEdgeChanges } from './utils/changes'; export { applyNodeChanges, applyEdgeChanges } from './utils/changes';
export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils'; export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils';
export { default as ReactFlowProvider } from './components/ReactFlowProvider'; export { default as ReactFlowProvider } from './components/ReactFlowProvider';
export { default as Panel } from './components/Panel';
export { default as useReactFlow } from './hooks/useReactFlow'; export { default as useReactFlow } from './hooks/useReactFlow';
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals'; export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals';
-180
View File
@@ -1,180 +0,0 @@
.react-flow {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
}
.react-flow__container {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.react-flow__pane {
z-index: 1;
}
.react-flow__viewport {
transform-origin: 0 0;
z-index: 2;
pointer-events: none;
}
.react-flow__renderer {
z-index: 4;
}
.react-flow__selectionpane {
z-index: 5;
}
.react-flow .react-flow__edges {
pointer-events: none;
overflow: visible;
}
.react-flow .react-flow__connectionline {
z-index: 1001;
}
.react-flow__edge {
pointer-events: visibleStroke;
&.inactive {
pointer-events: none;
}
}
@keyframes dashdraw {
from {
stroke-dashoffset: 10;
}
}
.react-flow__edge-path {
fill: none;
}
.react-flow__edge-textwrapper {
pointer-events: all;
}
.react-flow__edge-text {
pointer-events: none;
user-select: none;
}
.react-flow__connection {
pointer-events: none;
.animated {
stroke-dasharray: 5;
animation: dashdraw 0.5s linear infinite;
}
}
.react-flow__connection-path {
fill: none;
}
.react-flow__nodes {
pointer-events: none;
transform-origin: 0 0;
}
.react-flow__node {
position: absolute;
user-select: none;
pointer-events: all;
transform-origin: 0 0;
box-sizing: border-box;
}
.react-flow__nodesselection {
z-index: 3;
transform-origin: left top;
pointer-events: none;
&-rect {
position: absolute;
pointer-events: all;
cursor: grab;
}
}
.react-flow__handle {
position: absolute;
pointer-events: none;
&.connectable {
pointer-events: all;
}
}
.react-flow__handle-bottom {
top: auto;
left: 50%;
bottom: -4px;
transform: translate(-50%, 0);
}
.react-flow__handle-top {
left: 50%;
top: -4px;
transform: translate(-50%, 0);
}
.react-flow__handle-left {
top: 50%;
left: -4px;
transform: translate(0, -50%);
}
.react-flow__handle-right {
right: -4px;
top: 50%;
transform: translate(0, -50%);
}
.react-flow__edgeupdater {
cursor: move;
pointer-events: all;
}
.react-flow__attribution {
font-size: 10px;
position: absolute;
z-index: 1000;
background: rgba(255, 255, 255, 0.5);
padding: 2px 3px;
color: #999;
a {
color: #555;
text-decoration: none;
}
&.top {
top: 0;
}
&.bottom {
bottom: 0;
}
&.left {
left: 0;
}
&.right {
right: 0;
}
&.center {
left: 50%;
transform: translateX(-50%);
}
}
+226
View File
@@ -0,0 +1,226 @@
const style = `
.react-flow__container {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.react-flow__pane {
z-index: 1;
}
.react-flow__viewport {
transform-origin: 0 0;
z-index: 2;
pointer-events: none;
}
.react-flow__renderer {
z-index: 4;
}
.react-flow__selectionpane {
z-index: 5;
}
.react-flow__nodesselection-rect,
.react-flow__selection {
background: rgba(150, 150, 180, 0.1);
border: 1px dotted rgba(155, 155, 155, 0.8);
}
.react-flow__nodesselection-rect:focus,
.react-flow__nodesselection-rect:focus-visible {
outline: none;
}
.react-flow .react-flow__edges {
pointer-events: none;
overflow: visible;
}
.react-flow__connection {
pointer-events: none;
}
.react-flow__connection.animated {
stroke-dasharray: 5;
animation: dashdraw 0.5s linear infinite;
}
.react-flow .react-flow__connectionline {
z-index: 1001;
}
.react-flow__connectionline path, .react-flow__edge path {
fill: none;
}
.react-flow__edge-path, .react-flow__connection-path {
stroke: #b1b1b7;
stroke-width: 1;
}
.react-flow__edge {
pointer-events: visibleStroke;
}
.react-flow__edge.animated path {
stroke-dasharray: 5;
animation: dashdraw 0.5s linear infinite;
}
.react-flow__edge.inactive {
pointer-events: none;
}
.react-flow__edge.selected, .react-flow__edge:focus, .react-flow__edge:focus-visible {
outline: none;
}
.react-flow__edge.selected .react-flow__edge-path,
.react-flow__edge:focus .react-flow__edge-path,
.react-flow__edge:focus-visible .react-flow__edge-path {
stroke: #555;
}
@keyframes dashdraw {
from {
stroke-dashoffset: 10;
}
}
.react-flow__edge-textwrapper {
pointer-events: all;
}
.react-flow__edge-text {
pointer-events: none;
user-select: none;
}
.react-flow__edge-textbg {
fill: white;
}
.react-flow__nodes {
pointer-events: none;
transform-origin: 0 0;
}
.react-flow__node {
position: absolute;
user-select: none;
pointer-events: all;
transform-origin: 0 0;
box-sizing: border-box;
background-color: white;
cursor: grab;
border: 1px solid #bbb;
}
.react-flow__node.selected,
.react-flow__node:focus,
.react-flow__node:focus-visible {
outline: none;
border: 1px solid #555;
}
.react-flow__nodesselection {
z-index: 3;
transform-origin: left top;
pointer-events: none;
}
.react-flow__nodesselection-rect {
position: absolute;
pointer-events: all;
cursor: grab;
}
.react-flow__handle {
position: absolute;
pointer-events: none;
min-width: 5px;
min-height: 5px;
background-color: #333;
}
.react-flow__handle.connectable {
pointer-events: all;
}
.react-flow__handle-bottom {
top: auto;
left: 50%;
bottom: -4px;
transform: translate(-50%, 0);
}
.react-flow__handle-top {
left: 50%;
top: -4px;
transform: translate(-50%, 0);
}
.react-flow__handle-left {
top: 50%;
left: -4px;
transform: translate(0, -50%);
}
.react-flow__handle-right {
right: -4px;
top: 50%;
transform: translate(0, -50%);
}
.react-flow__edgeupdater {
cursor: move;
pointer-events: all;
}
.react-flow__panel {
position: absolute;
z-index: 1000;
margin: 15px;
}
.react-flow__panel.top {
top: 0;
}
.react-flow__panel.bottom {
bottom: 0;
}
.react-flow__panel.left {
left: 0;
}
.react-flow__panel.right {
right: 0;
}
.react-flow__panel.center {
left: 50%;
transform: translateX(-50%);
}
.react-flow__attribution {
font-size: 10px;
background: rgba(255, 255, 255, 0.5);
padding: 2px 3px;
color: #999;
margin: 0;
}
.react-flow__attribution a {
color: #555;
text-decoration: none;
}
`;
export default style;
+9
View File
@@ -0,0 +1,9 @@
import { CSSProperties } from 'react';
export const containerStyle: CSSProperties = {
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
left: 0,
};
@@ -0,0 +1,83 @@
.react-flow__edge {
&.updating {
.react-flow__edge-path {
stroke: #777;
}
}
}
.react-flow__edge-text {
font-size: 10px;
}
.react-flow__node-default,
.react-flow__node-input,
.react-flow__node-output,
.react-flow__node-group {
padding: 10px;
border-radius: 3px;
width: 150px;
font-size: 12px;
color: #222;
text-align: center;
border-width: 1px;
border-style: solid;
border-color: #1a192b;
&.selected,
&:focus,
&:focus-visible {
border: none;
box-shadow: 0 0 0 0.5px #1a192b;
outline: none;
}
.react-flow__handle {
background: #1a192b;
}
}
.react-flow__node-default.selectable,
.react-flow__node-input.selectable,
.react-flow__node-output.selectable,
.react-flow__node-group.selectable {
&:hover {
box-shadow: 0 1px 4px 1px rgba(0, 0, 0, 0.08);
}
&.selected,
&:focus,
&:focus-visible {
border: none;
box-shadow: 0 0 0 0.5px #1a192b;
outline: none;
}
}
.react-flow__node-group {
background: rgba(240, 240, 240, 0.25);
border-color: #1a192b;
}
.react-flow__nodesselection-rect,
.react-flow__selection {
background: rgba(0, 89, 220, 0.08);
border: 1px dotted rgba(0, 89, 220, 0.8);
&:focus,
&:focus-visible {
outline: none;
}
}
.react-flow__handle {
width: 6px;
height: 6px;
background: #555;
border: 1px solid white;
border-radius: 100%;
&.connectable {
cursor: crosshair;
}
}
+1 -7
View File
@@ -221,13 +221,7 @@ export type OnSelectionChangeParams = {
export type OnSelectionChangeFunc = (params: OnSelectionChangeParams) => void; export type OnSelectionChangeFunc = (params: OnSelectionChangeParams) => void;
export type AttributionPosition = export type PanelPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
| 'top-left'
| 'top-center'
| 'top-right'
| 'bottom-left'
| 'bottom-center'
| 'bottom-right';
export type ProOptions = { export type ProOptions = {
hideAttribution: boolean; hideAttribution: boolean;
+1 -1
View File
@@ -36,7 +36,7 @@ export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
height: y2 - y, height: y2 - y,
}); });
export const getBoundsofRects = (rect1: Rect, rect2: Rect): Rect => export const getBoundsOfRects = (rect1: Rect, rect2: Rect): Rect =>
boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2))); boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)));
export const isNumeric = (n: any): n is number => !isNaN(n) && isFinite(n); export const isNumeric = (n: any): n is number => !isNaN(n) && isFinite(n);
+8
View File
@@ -0,0 +1,8 @@
{
"name": "@react-flow/css-utils",
"version": "11.0.0",
"description": "Utils for handling styles.",
"main": "dist/react-flow-css-utils.cjs.js",
"module": "dist/react-flow-css-utils.esm.js",
"license": "MIT"
}
+10
View File
@@ -0,0 +1,10 @@
export function injectStyle(css: string) {
if (typeof document === 'undefined') return;
const head = document.head || document.getElementsByTagName('head')[0];
const style = document.createElement('style');
head.prepend(style);
style.appendChild(document.createTextNode(css));
}
+2 -7
View File
@@ -6,18 +6,13 @@
"module": "dist/react-flow-minimap.esm.js", "module": "dist/react-flow-minimap.esm.js",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@react-flow/core": "11.0.0", "@react-flow/core": "^11.0.0",
"@react-flow/css-utils": "^11.0.0",
"classcat": "^5.0.3", "classcat": "^5.0.3",
"d3-drag": "^3.0.0",
"d3-selection": "^3.0.0",
"zustand": "^3.7.2" "zustand": "^3.7.2"
}, },
"peerDependencies": { "peerDependencies": {
"react": "^18.1.0", "react": "^18.1.0",
"react-dom": ">=18" "react-dom": ">=18"
},
"devDependencies": {
"@types/d3-drag": "^3.0.1",
"@types/d3-selection": "^3.0.2"
} }
} }
+49 -72
View File
@@ -4,16 +4,18 @@ import shallow from 'zustand/shallow';
import { import {
useStore, useStore,
getRectOfNodes, getRectOfNodes,
Box,
ReactFlowState, ReactFlowState,
Rect, Rect,
Panel,
getBoundsOfRects,
} from '@react-flow/core'; } from '@react-flow/core';
import { injectStyle } from '@react-flow/css-utils';
import MiniMapNode from './MiniMapNode'; import MiniMapNode from './MiniMapNode';
import MiniMapDrag from './MiniMapDrag';
import { MiniMapProps, GetMiniMapNodeAttribute } from './types'; import { MiniMapProps, GetMiniMapNodeAttribute } from './types';
import baseStyle from './style';
import './style.css'; injectStyle(baseStyle);
declare const window: any; declare const window: any;
@@ -30,30 +32,6 @@ const selector = (s: ReactFlowState) => ({
const getAttrFunction = (func: any): GetMiniMapNodeAttribute => const getAttrFunction = (func: any): GetMiniMapNodeAttribute =>
func instanceof Function ? func : () => func; func instanceof Function ? func : () => func;
export const getBoundsOfBoxes = (box1: Box, box2: Box): Box => ({
x: Math.min(box1.x, box2.x),
y: Math.min(box1.y, box2.y),
x2: Math.max(box1.x2, box2.x2),
y2: Math.max(box1.y2, box2.y2),
});
export const rectToBox = ({ x, y, width, height }: Rect): Box => ({
x,
y,
x2: x + width,
y2: y + height,
});
export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
x,
y,
width: x2 - x,
height: y2 - y,
});
export const getBoundsofRects = (rect1: Rect, rect2: Rect): Rect =>
boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)));
const ARIA_LABEL_KEY = 'react-flow__minimap-desc'; const ARIA_LABEL_KEY = 'react-flow__minimap-desc';
function MiniMap({ function MiniMap({
@@ -65,6 +43,7 @@ function MiniMap({
nodeBorderRadius = 5, nodeBorderRadius = 5,
nodeStrokeWidth = 2, nodeStrokeWidth = 2,
maskColor = 'rgb(240, 242, 243, 0.7)', maskColor = 'rgb(240, 242, 243, 0.7)',
position = 'bottom-right',
}: MiniMapProps) { }: MiniMapProps) {
const minimapId = useId(); const minimapId = useId();
const { const {
@@ -85,7 +64,7 @@ function MiniMap({
height: containerHeight / transform[2], height: containerHeight / transform[2],
}; };
const boundingRect = const boundingRect =
nodes.length > 0 ? getBoundsofRects(getRectOfNodes(nodes), viewBB) : viewBB; nodes.length > 0 ? getBoundsOfRects(getRectOfNodes(nodes), viewBB) : viewBB;
const scaledWidth = boundingRect.width / elementWidth; const scaledWidth = boundingRect.width / elementWidth;
const scaledHeight = boundingRect.height / elementHeight; const scaledHeight = boundingRect.height / elementHeight;
const viewScale = Math.max(scaledWidth, scaledHeight); const viewScale = Math.max(scaledWidth, scaledHeight);
@@ -103,54 +82,52 @@ function MiniMap({
const labelledBy = `${ARIA_LABEL_KEY}-${minimapId}`; const labelledBy = `${ARIA_LABEL_KEY}-${minimapId}`;
return ( return (
<svg <Panel
width={elementWidth} position={position}
height={elementHeight}
viewBox={`${x} ${y} ${width} ${height}`}
style={style} style={style}
className={cc(['react-flow__minimap', className])} className={cc(['react-flow__minimap', className])}
role="img"
aria-labelledby={labelledBy}
> >
<title id={labelledBy}>React Flow mini map</title> <svg
{nodes width={elementWidth}
.filter((node) => !node.hidden && node.width && node.height) height={elementHeight}
.map((node) => { viewBox={`${x} ${y} ${width} ${height}`}
return ( role="img"
<MiniMapNode aria-labelledby={labelledBy}
key={node.id} >
x={node.positionAbsolute?.x ?? 0} <title id={labelledBy}>React Flow mini map</title>
y={node.positionAbsolute?.y ?? 0} {nodes
width={node.width!} .filter((node) => !node.hidden && node.width && node.height)
height={node.height!} .map((node) => {
style={node.style} return (
className={nodeClassNameFunc(node)} <MiniMapNode
color={nodeColorFunc(node)} key={node.id}
borderRadius={nodeBorderRadius} x={node.positionAbsolute?.x ?? 0}
strokeColor={nodeStrokeColorFunc(node)} y={node.positionAbsolute?.y ?? 0}
strokeWidth={nodeStrokeWidth} width={node.width!}
shapeRendering={shapeRendering} height={node.height!}
/> style={node.style}
); className={nodeClassNameFunc(node)}
})} color={nodeColorFunc(node)}
<MiniMapDrag borderRadius={nodeBorderRadius}
x={viewBB.x} strokeColor={nodeStrokeColorFunc(node)}
y={viewBB.y} strokeWidth={nodeStrokeWidth}
width={viewBB.width} shapeRendering={shapeRendering}
height={viewBB.height} />
/> );
<path })}
className="react-flow__minimap-mask" <path
d={`M${x - offset},${y - offset}h${width + offset * 2}v${ className="react-flow__minimap-mask"
height + offset * 2 d={`M${x - offset},${y - offset}h${width + offset * 2}v${
}h${-width - offset * 2}z height + offset * 2
}h${-width - offset * 2}z
M${viewBB.x},${viewBB.y}h${viewBB.width}v${ M${viewBB.x},${viewBB.y}h${viewBB.width}v${
viewBB.height viewBB.height
}h${-viewBB.width}z`} }h${-viewBB.width}z`}
fill={maskColor} fill={maskColor}
fillRule="evenodd" fillRule="evenodd"
/> />
</svg> </svg>
</Panel>
); );
} }
-42
View File
@@ -1,42 +0,0 @@
import React, { useEffect, useRef } from 'react';
import { drag, D3DragEvent, SubjectPosition } from 'd3-drag';
import { select } from 'd3-selection';
import { useReactFlow } from '@react-flow/core';
function MiniMapDrag({ x = 0, y = 0, width = 0, height = 0 }) {
const dragRef = useRef<SVGRectElement>(null);
const { getViewport, setViewport } = useReactFlow();
useEffect(() => {
if (dragRef.current) {
const onDrag = (
evt: D3DragEvent<SVGRectElement, null, SubjectPosition>
) => {
const { x, y, zoom } = getViewport();
setViewport({ x: x - evt.dx * zoom, y: y - evt.dy * zoom, zoom });
};
const selection = select(dragRef.current as Element);
const dragBehaviour = drag().on('drag', onDrag);
selection.call(dragBehaviour);
return () => {
selection.on('.drag', null);
};
}
}, [getViewport, setViewport]);
return (
<rect
ref={dragRef}
fill='rgba(255, 0,0,.5)'
x={x}
y={y}
width={width}
height={height}
/>
);
}
export default MiniMapDrag;
+1 -1
View File
@@ -1,2 +1,2 @@
export * from './types'; export * from './types';
export { default as MiniMap, default } from './MiniMap'; export { default as MiniMap } from './MiniMap';
-7
View File
@@ -1,7 +0,0 @@
.react-flow__minimap {
background-color: #fff;
position: absolute;
z-index: 5;
bottom: 20px;
right: 15px;
}
+6
View File
@@ -0,0 +1,6 @@
const style = `
.react-flow__minimap {
background-color: #fff;
}`;
export default style;
+2 -1
View File
@@ -1,5 +1,5 @@
import { HTMLAttributes } from 'react'; import { HTMLAttributes } from 'react';
import { Node } from '@react-flow/core'; import { Node, PanelPosition } from '@react-flow/core';
export type GetMiniMapNodeAttribute<NodeData = any> = ( export type GetMiniMapNodeAttribute<NodeData = any> = (
node: Node<NodeData> node: Node<NodeData>
@@ -13,4 +13,5 @@ export interface MiniMapProps<NodeData = any>
nodeBorderRadius?: number; nodeBorderRadius?: number;
nodeStrokeWidth?: number; nodeStrokeWidth?: number;
maskColor?: string; maskColor?: string;
position?: PanelPosition;
} }
+28
View File
@@ -0,0 +1,28 @@
{
"name": "@react-flow/renderer",
"version": "11.0.0",
"description": "The main React Flow package that comes with all the essentials.",
"main": "dist/react-flow-renderer.cjs.js",
"module": "dist/react-flow-renderer.esm.js",
"license": "MIT",
"scripts": {
"build": "postcss src/*.css --config ../../tooling/postcss.config.json --dir dist"
},
"dependencies": {
"@react-flow/background": "11.0.0",
"@react-flow/controls": "11.0.0",
"@react-flow/core": "11.0.0",
"@react-flow/minimap": "11.0.0"
},
"peerDependencies": {
"react": "^18.1.0",
"react-dom": ">=18"
},
"devDependencies": {
"autoprefixer": "^10.4.8",
"postcss": "^8.4.14",
"postcss-cli": "^10.0.0",
"postcss-import": "^14.1.0",
"postcss-nested": "^5.0.6"
}
}
+4
View File
@@ -0,0 +1,4 @@
export * from '@react-flow/core';
export * from '@react-flow/minimap';
export * from '@react-flow/controls';
export * from '@react-flow/background';
+1
View File
@@ -0,0 +1 @@
@import '@react-flow/core/dist/theme-default.css';
-45
View File
@@ -1,45 +0,0 @@
const fs = require('fs');
const path = require('path');
const postcss = require('postcss');
const autoprefixer = require('autoprefixer');
const nested = require('postcss-nested');
function getStyleInject(cssString) {
return `
(function(css) {
if (!css || typeof document === 'undefined') return;
const head = document.head || document.getElementsByTagName('head')[0];
const style = document.createElement('style');
head.appendChild(style);
style.appendChild(document.createTextNode(css));
})("${cssString}")
`;
}
module.exports = function (babel) {
return {
name: 'inject-css-babel-plugin',
visitor: {
ImportDeclaration(item, state) {
if (!item.node.source.value.includes('.css')) {
return;
}
const importFilePath = path.resolve(
state.file.opts.filename,
'..',
item.node.source.value
);
const css = fs.readFileSync(importFilePath).toString();
const result = postcss([autoprefixer, nested]).process(css);
const minified = result.css.replace(/\n/g, '');
item.replaceWithSourceString(getStyleInject(minified));
},
},
};
};
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
plugins: [
require('postcss-nested'),
require('autoprefixer'),
require('postcss-import'),
],
};
+883 -24
View File
File diff suppressed because it is too large Load Diff