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

View File

@@ -1 +1,5 @@
nodeLinker: node-modules
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"

View File

@@ -4,5 +4,4 @@ module.exports = {
'@babel/preset-typescript',
'@babel/preset-env',
],
plugins: ['./tooling/inject-css-babel-plugin'],
};

View File

@@ -3,7 +3,8 @@
"version": "0.1.0",
"private": true,
"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",
"start": "next start",
"lint": "next lint"
@@ -23,6 +24,9 @@
"devDependencies": {
"@types/dagre": "^0.7.47",
"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"
}
}

View File

@@ -1,12 +1,14 @@
import React, { FC } from 'react';
import ReactFlow, {
import {
ReactFlow,
Node,
ReactFlowProvider,
useNodesState,
} from '@react-flow/core';
import Background, {
import {
Background,
BackgroundProps,
BackgroundVariant,
} from '@react-flow/background';

View File

@@ -1,6 +1,6 @@
import React from 'react';
import ReactFlow, {
import {
ReactFlow,
useReactFlow,
Node,
Edge,
@@ -8,8 +8,7 @@ import ReactFlow, {
useNodesState,
useEdgesState,
} from '@react-flow/core';
import Background, { BackgroundVariant } from '@react-flow/background';
import { Background, BackgroundVariant } from '@react-flow/background';
const defaultNodes: Node[] = [
{

View File

@@ -1,5 +1,6 @@
import React, { useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
Node,
addEdge,
Connection,
@@ -7,8 +8,7 @@ import ReactFlow, {
useNodesState,
useEdgesState,
} from '@react-flow/core';
import Background, { BackgroundVariant } from '@react-flow/background';
import { Background, BackgroundVariant } from '@react-flow/background';
import ConnectionLine from './ConnectionLine';

View File

@@ -1,12 +1,11 @@
import React, { memo, FC, CSSProperties } from 'react';
import {
Handle,
Position,
NodeProps,
Connection,
Edge,
} from '@react-flow/core';
} from '@react-flow/renderer';
const targetHandleStyle: CSSProperties = { background: '#555' };
const sourceHandleStyleA: CSSProperties = { ...targetHandleStyle, top: 10 };
@@ -23,7 +22,7 @@ const ColorSelectorNode: FC<NodeProps> = ({ data, isConnectable }) => {
return (
<>
<Handle
type='target'
type="target"
position={Position.Left}
style={targetHandleStyle}
onConnect={onConnect}
@@ -32,15 +31,15 @@ const ColorSelectorNode: FC<NodeProps> = ({ data, isConnectable }) => {
Custom Color Picker Node: <strong>{data.color}</strong>
</div>
<input
className='nodrag'
type='color'
className="nodrag"
type="color"
onChange={data.onChange}
defaultValue={data.color}
/>
<Handle
type='source'
type="source"
position={Position.Right}
id='a'
id="a"
style={sourceHandleStyleA}
isConnectable={isConnectable}
onMouseDown={(e) => {
@@ -48,9 +47,9 @@ const ColorSelectorNode: FC<NodeProps> = ({ data, isConnectable }) => {
}}
/>
<Handle
type='source'
type="source"
position={Position.Right}
id='b'
id="b"
style={sourceHandleStyleB}
isConnectable={isConnectable}
/>

View File

@@ -1,7 +1,8 @@
import React, { useState, useEffect, MouseEvent } from 'react';
import { ChangeEvent } from 'react';
import ReactFlow, {
import React, { useState, useEffect, MouseEvent, ChangeEvent } from 'react';
import {
ReactFlow,
MiniMap,
Controls,
addEdge,
Node,
ReactFlowInstance,
@@ -10,10 +11,7 @@ import ReactFlow, {
Connection,
useNodesState,
useEdgesState,
} from '@react-flow/core';
import MiniMap from '@react-flow/minimap';
import Controls from '@react-flow/controls';
} from '@react-flow/renderer';
import ColorSelectorNode from './ColorSelectorNode';

View File

@@ -1,13 +1,12 @@
import React from 'react';
import ReactFlow, {
import {
ReactFlow,
useReactFlow,
Node,
Edge,
ReactFlowProvider,
} from '@react-flow/core';
import Background, { BackgroundVariant } from '@react-flow/background';
import { Background, BackgroundVariant } from '@react-flow/background';
const defaultNodes: Node[] = [
{

View File

@@ -1,5 +1,6 @@
import React, { MouseEvent } from 'react';
import ReactFlow, {
import {
ReactFlow,
Node,
Edge,
useNodesState,

View File

@@ -1,5 +1,6 @@
import React, { useState, DragEvent } from 'react';
import ReactFlow, {
import {
ReactFlow,
ReactFlowProvider,
addEdge,
ReactFlowInstance,
@@ -9,7 +10,7 @@ import ReactFlow, {
useNodesState,
useEdgesState,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
import { Controls } from '@react-flow/controls';
import Sidebar from './Sidebar';

View File

@@ -3,18 +3,18 @@
*/
import React from 'react';
import ReactFlow, {
import {
ReactFlow,
Background,
Controls,
MiniMap,
addEdge,
ReactFlowInstance,
Connection,
Edge,
useNodesState,
useEdgesState,
} from '@react-flow/core';
import Background from '@react-flow/background';
import Controls from '@react-flow/controls';
import MiniMap from '@react-flow/minimap';
} from '@react-flow/renderer';
import { getElements } from './utils';
@@ -44,10 +44,10 @@ const EdgeTypesFlow = () => {
onConnect={onConnect}
minZoom={0.2}
zoomOnScroll={false}
selectionKeyCode='a+s'
selectionKeyCode="a+s"
multiSelectionKeyCode={multiSelectionKeyCode}
deleteKeyCode={deleteKeyCode}
zoomActivationKeyCode='z'
zoomActivationKeyCode="z"
>
<MiniMap />
<Controls />

View File

@@ -1,19 +1,18 @@
import React, { MouseEvent, useCallback } from 'react';
import ReactFlow, {
import {
Controls,
Background,
ReactFlow,
MiniMap,
addEdge,
Connection,
Edge,
EdgeTypes,
MarkerType,
Node,
ReactFlowInstance,
useEdgesState,
useNodesState,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
import Background from '@react-flow/background';
import MiniMap from '@react-flow/minimap';
} from '@react-flow/renderer';
import CustomEdge from './CustomEdge';
import CustomEdge2 from './CustomEdge2';

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,
Node,
Connection,
@@ -8,10 +12,7 @@ import ReactFlow, {
useNodesState,
useEdgesState,
ReactFlowInstance,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
import Background, { BackgroundVariant } from '@react-flow/background';
} from '@react-flow/renderer';
const onInit = (reactFlowInstance: ReactFlowInstance) =>
console.log('flow loaded:', reactFlowInstance);
@@ -30,8 +31,10 @@ const EmptyFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const onConnect = (params: Connection | Edge) =>
setEdges((els) => addEdge(params, els));
const onConnect = useCallback(
(params: Connection | Edge) => setEdges((els) => addEdge(params, els)),
[setEdges]
);
const addRandomNode = () => {
const nodeId = (nodes.length + 1).toString();
const newNode: Node = {
@@ -60,7 +63,7 @@ const EmptyFlow = () => {
<Controls />
<Background variant={BackgroundVariant.Lines} />
<button type='button' onClick={addRandomNode} style={buttonStyle}>
<button type="button" onClick={addRandomNode} style={buttonStyle}>
add node
</button>
</ReactFlow>

View File

@@ -1,15 +1,15 @@
import React, { useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
Background,
addEdge,
ReactFlowInstance,
EdgeTypes,
Connection,
useNodesState,
useEdgesState,
} from '@react-flow/core';
import Background from '@react-flow/background';
} from '@react-flow/renderer';
import styles from './style.module.css';

View File

@@ -1,6 +1,7 @@
import React, { useState, useEffect, useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
addEdge,
Connection,
Edge,
@@ -9,8 +10,8 @@ import ReactFlow, {
useEdgesState,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
import MiniMap from '@react-flow/minimap';
import { Controls } from '@react-flow/controls';
import { MiniMap } from '@react-flow/minimap';
const initialNodes: Node[] = [
{
@@ -85,14 +86,14 @@ const HiddenFlow = () => {
<div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
<div>
<label htmlFor='ishidden'>
<label htmlFor="ishidden">
isHidden
<input
id='ishidden'
type='checkbox'
id="ishidden"
type="checkbox"
checked={isHidden}
onChange={(event) => setIsHidden(event.target.checked)}
className='react-flow__ishidden'
className="react-flow__ishidden"
/>
</label>
</div>

View File

@@ -3,7 +3,8 @@ import React, {
MouseEvent as ReactMouseEvent,
WheelEvent,
} from 'react';
import ReactFlow, {
import {
ReactFlow,
addEdge,
Node,
Connection,
@@ -14,8 +15,8 @@ import ReactFlow, {
useEdgesState,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
import MiniMap from '@react-flow/minimap';
import { Controls } from '@react-flow/controls';
import { MiniMap } from '@react-flow/minimap';
const initialNodes: Node[] = [
{
@@ -102,151 +103,151 @@ const InteractionFlow = () => {
<div style={{ position: 'absolute', left: 10, top: 10, zIndex: 4 }}>
<div>
<label htmlFor='draggable'>
<label htmlFor="draggable">
nodesDraggable
<input
id='draggable'
type='checkbox'
id="draggable"
type="checkbox"
checked={isDraggable}
onChange={(event) => setIsDraggable(event.target.checked)}
className='react-flow__draggable'
className="react-flow__draggable"
/>
</label>
</div>
<div>
<label htmlFor='connectable'>
<label htmlFor="connectable">
nodesConnectable
<input
id='connectable'
type='checkbox'
id="connectable"
type="checkbox"
checked={isConnectable}
onChange={(event) => setIsConnectable(event.target.checked)}
className='react-flow__connectable'
className="react-flow__connectable"
/>
</label>
</div>
<div>
<label htmlFor='selectable'>
<label htmlFor="selectable">
elementsSelectable
<input
id='selectable'
type='checkbox'
id="selectable"
type="checkbox"
checked={isSelectable}
onChange={(event) => setIsSelectable(event.target.checked)}
className='react-flow__selectable'
className="react-flow__selectable"
/>
</label>
</div>
<div>
<label htmlFor='zoomonscroll'>
<label htmlFor="zoomonscroll">
zoomOnScroll
<input
id='zoomonscroll'
type='checkbox'
id="zoomonscroll"
type="checkbox"
checked={zoomOnScroll}
onChange={(event) => setZoomOnScroll(event.target.checked)}
className='react-flow__zoomonscroll'
className="react-flow__zoomonscroll"
/>
</label>
</div>
<div>
<label htmlFor='zoomonpinch'>
<label htmlFor="zoomonpinch">
zoomOnPinch
<input
id='zoomonpinch'
type='checkbox'
id="zoomonpinch"
type="checkbox"
checked={zoomOnPinch}
onChange={(event) => setZoomOnPinch(event.target.checked)}
className='react-flow__zoomonpinch'
className="react-flow__zoomonpinch"
/>
</label>
</div>
<div>
<label htmlFor='panonscroll'>
<label htmlFor="panonscroll">
panOnScroll
<input
id='panonscroll'
type='checkbox'
id="panonscroll"
type="checkbox"
checked={panOnScroll}
onChange={(event) => setPanOnScroll(event.target.checked)}
className='react-flow__panonscroll'
className="react-flow__panonscroll"
/>
</label>
</div>
<div>
<label htmlFor='panonscrollmode'>
<label htmlFor="panonscrollmode">
panOnScrollMode
<select
id='panonscrollmode'
id="panonscrollmode"
value={panOnScrollMode}
onChange={(event) =>
setPanOnScrollMode(event.target.value as PanOnScrollMode)
}
className='react-flow__panonscrollmode'
className="react-flow__panonscrollmode"
>
<option value='free'>free</option>
<option value='horizontal'>horizontal</option>
<option value='vertical'>vertical</option>
<option value="free">free</option>
<option value="horizontal">horizontal</option>
<option value="vertical">vertical</option>
</select>
</label>
</div>
<div>
<label htmlFor='zoomondbl'>
<label htmlFor="zoomondbl">
zoomOnDoubleClick
<input
id='zoomondbl'
type='checkbox'
id="zoomondbl"
type="checkbox"
checked={zoomOnDoubleClick}
onChange={(event) => setZoomOnDoubleClick(event.target.checked)}
className='react-flow__zoomondbl'
className="react-flow__zoomondbl"
/>
</label>
</div>
<div>
<label htmlFor='panondrag'>
<label htmlFor="panondrag">
panOnDrag
<input
id='panondrag'
type='checkbox'
id="panondrag"
type="checkbox"
checked={panOnDrag}
onChange={(event) => setPanOnDrag(event.target.checked)}
className='react-flow__panondrag'
className="react-flow__panondrag"
/>
</label>
</div>
<div>
<label htmlFor='capturezoompaneclick'>
<label htmlFor="capturezoompaneclick">
capture onPaneClick
<input
id='capturezoompaneclick'
type='checkbox'
id="capturezoompaneclick"
type="checkbox"
checked={captureZoomClick}
onChange={(event) => setCaptureZoomClick(event.target.checked)}
className='react-flow__capturezoompaneclick'
className="react-flow__capturezoompaneclick"
/>
</label>
</div>
<div>
<label htmlFor='capturezoompanescroll'>
<label htmlFor="capturezoompanescroll">
capture onPaneScroll
<input
id='capturezoompanescroll'
type='checkbox'
id="capturezoompanescroll"
type="checkbox"
checked={captureZoomScroll}
onChange={(event) => setCaptureZoomScroll(event.target.checked)}
className='react-flow__capturezoompanescroll'
className="react-flow__capturezoompanescroll"
/>
</label>
</div>
<div>
<label htmlFor='captureelementclick'>
<label htmlFor="captureelementclick">
capture onElementClick
<input
id='captureelementclick'
type='checkbox'
id="captureelementclick"
type="checkbox"
checked={captureElementClick}
onChange={(event) => setCaptureElementClick(event.target.checked)}
className='react-flow__captureelementclick'
className="react-flow__captureelementclick"
/>
</label>
</div>

View File

@@ -1,5 +1,7 @@
import React, { useCallback } from 'react';
import ReactFlow, {
import {
Controls,
ReactFlow,
ReactFlowProvider,
addEdge,
Connection,
@@ -9,9 +11,8 @@ import ReactFlow, {
useEdgesState,
MarkerType,
EdgeMarker,
} from '@react-flow/core';
} from '@react-flow/renderer';
import Controls from '@react-flow/controls';
import dagre from 'dagre';
import initialItems from './initial-elements';

View File

@@ -1,6 +1,8 @@
import React, { FC } from 'react';
import ReactFlow, {
import {
ReactFlow,
Background,
addEdge,
Node,
Edge,
@@ -9,9 +11,7 @@ import ReactFlow, {
useNodesState,
useEdgesState,
MarkerType,
} from '@react-flow/core';
import Background from '@react-flow/background';
} from '@react-flow/renderer';
import styles from './multiflows.module.css';
@@ -79,8 +79,8 @@ const Flow: FC<{ id: string }> = ({ id }) => {
const MultiFlows: FC = () => (
<div className={styles.multiflows}>
<Flow id='flow-a' />
<Flow id='flow-b' />
<Flow id="flow-a" />
<Flow id="flow-b" />
</div>
);

View File

@@ -1,6 +1,9 @@
import React, { useState, MouseEvent, useCallback } from 'react';
import ReactFlow, {
import {
Controls,
MiniMap,
Background,
ReactFlow,
addEdge,
useNodesState,
useEdgesState,
@@ -8,11 +11,7 @@ import ReactFlow, {
Edge,
ReactFlowInstance,
Connection,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
import MiniMap from '@react-flow/minimap';
import Background from '@react-flow/background';
} from '@react-flow/renderer';
const onNodeDragStop = (_: MouseEvent, node: Node) =>
console.log('drag stop', node);
@@ -105,9 +104,12 @@ const NestedFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = useCallback((connection: Connection) => {
setEdges((eds) => addEdge(connection, eds));
}, []);
const onConnect = useCallback(
(connection: Connection) => {
setEdges((eds) => addEdge(connection, eds));
},
[setEdges]
);
const onInit = useCallback(
(reactFlowInstance: ReactFlowInstance) => setRfInstance(reactFlowInstance),
[]

View File

@@ -1,6 +1,7 @@
import React, { CSSProperties } from 'react';
import React, { CSSProperties, useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
addEdge,
Node,
Position,
@@ -42,8 +43,10 @@ const buttonStyle: CSSProperties = {
const NodeTypeChangeFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = (params: Connection | Edge) =>
setEdges((eds) => addEdge(params, eds));
const onConnect = useCallback(
(params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)),
[setEdges]
);
const changeType = () => {
setNodes((nds) =>
nds.map((node) => {

View File

@@ -1,6 +1,7 @@
import React, { useState, CSSProperties, FC } from 'react';
import React, { useState, CSSProperties, FC, useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
addEdge,
Node,
Position,
@@ -65,10 +66,12 @@ const nodeTypesObjects: NodeTypesObject = {
const NodeTypeChangeFlow = () => {
const [nodeTypesId, setNodeTypesId] = useState<string>('a');
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const onConnect = (params: Connection | Edge) =>
setEdges((eds) => addEdge(params, eds));
const onConnect = useCallback(
(params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)),
[setEdges]
);
const changeType = () => setNodeTypesId((nt) => (nt === 'a' ? 'b' : 'a'));
return (

View File

@@ -4,7 +4,8 @@ import React, {
useCallback,
} from 'react';
import ReactFlow, {
import {
ReactFlow,
addEdge,
Node,
Viewport,
@@ -17,9 +18,9 @@ import ReactFlow, {
OnSelectionChangeParams,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
import Background from '@react-flow/background';
import MiniMap from '@react-flow/minimap';
import { Controls } from '@react-flow/controls';
import { Background } from '@react-flow/background';
import { MiniMap } from '@react-flow/minimap';
const onNodeDragStart = (_: ReactMouseEvent, node: Node, nodes: Node[]) =>
console.log('drag start', node, nodes);

View File

@@ -1,5 +1,7 @@
import React, { MouseEvent } from 'react';
import ReactFlow, {
import React, { MouseEvent, useCallback } from 'react';
import {
ReactFlow,
Controls,
ReactFlowProvider,
addEdge,
Node,
@@ -9,8 +11,7 @@ import ReactFlow, {
useNodesState,
useEdgesState,
ReactFlowInstance,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
} from '@react-flow/renderer';
import Sidebar from './Sidebar';
@@ -40,8 +41,10 @@ const initialEdges: Edge[] = [
const ProviderFlow = () => {
const [nodes, , onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onConnect = (params: Edge | Connection) =>
setEdges((els) => addEdge(params, els));
const onConnect = useCallback(
(params: Edge | Connection) => setEdges((els) => addEdge(params, els)),
[setEdges]
);
return (
<div className={styles.providerflow}>

View File

@@ -1,5 +1,6 @@
import React, { useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
ReactFlowProvider,
Node,
addEdge,
@@ -8,7 +9,6 @@ import ReactFlow, {
useNodesState,
useEdgesState,
} from '@react-flow/core';
import Controls from './Controls';
const initialNodes: Node[] = [

View File

@@ -1,5 +1,6 @@
import React, { useState, CSSProperties, useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
ReactFlowInstance,
Edge,
Node,
@@ -10,10 +11,9 @@ import ReactFlow, {
applyEdgeChanges,
EdgeChange,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
import Background from '@react-flow/background';
import MiniMap from '@react-flow/minimap';
import { Controls } from '@react-flow/controls';
import { Background } from '@react-flow/background';
import { MiniMap } from '@react-flow/minimap';
import { getNodesAndEdges } from './utils';

View File

@@ -1,6 +1,6 @@
import React, { useState, MouseEvent, useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
addEdge,
Node,
Edge,
@@ -10,10 +10,9 @@ import ReactFlow, {
useNodesState,
useEdgesState,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
import Background from '@react-flow/background';
import MiniMap from '@react-flow/minimap';
import { Controls } from '@react-flow/controls';
import { Background } from '@react-flow/background';
import { MiniMap } from '@react-flow/minimap';
import DebugNode from './DebugNode';

View File

@@ -1,5 +1,6 @@
import React, { MouseEvent, useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
addEdge,
Node,
Connection,

View File

@@ -1,5 +1,6 @@
import React, { useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
Node,
Edge,
useNodesState,

View File

@@ -1,6 +1,7 @@
import React, { MouseEvent, useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
useReactFlow,
NodeTypes,
addEdge,

View File

@@ -1,5 +1,6 @@
import React, { useState, useCallback } from 'react';
import ReactFlow, {
import {
ReactFlow,
updateEdge,
addEdge,
applyNodeChanges,
@@ -13,7 +14,7 @@ import ReactFlow, {
HandleType,
} from '@react-flow/core';
import Controls from '@react-flow/controls';
import { Controls } from '@react-flow/controls';
const initialNodes: Node[] = [
{

View File

@@ -1,5 +1,6 @@
import React, { useEffect, useState } from 'react';
import ReactFlow, {
import {
ReactFlow,
Node,
Edge,
useNodesState,

View File

@@ -1,6 +1,8 @@
import React, { useCallback, MouseEvent, useEffect } from 'react';
import ReactFlow, {
import {
Background,
MiniMap,
ReactFlow,
Node,
addEdge,
useReactFlow,
@@ -9,10 +11,7 @@ import ReactFlow, {
Edge,
useNodesState,
useEdgesState,
} from '@react-flow/core';
import Background from '@react-flow/background';
import MiniMap from '@react-flow/minimap';
} from '@react-flow/renderer';
const initialNodes: Node[] = [
{

View File

@@ -1,6 +1,6 @@
import React, { useCallback, CSSProperties, MouseEvent } from 'react';
import ReactFlow, {
import {
ReactFlow,
NodeTypes,
addEdge,
useReactFlow,
@@ -13,6 +13,7 @@ import ReactFlow, {
useNodesState,
useEdgesState,
} from '@react-flow/core';
import CustomNode from './CustomNode';
const initialHandleCount = 1;
@@ -47,8 +48,10 @@ const getId = (): string => `${id++}`;
const UpdateNodeInternalsFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const onConnect = (params: Edge | Connection) =>
setEdges((els) => addEdge(params, els));
const onConnect = useCallback(
(params: Edge | Connection) => setEdges((els) => addEdge(params, els)),
[setEdges]
);
const updateNodeInternals = useUpdateNodeInternals();
const { project } = useReactFlow();

View File

@@ -1,5 +1,6 @@
import React, { FC, useCallback, useState } from 'react';
import ReactFlow, {
import {
ReactFlow,
addEdge,
Handle,
Connection,
@@ -28,7 +29,7 @@ const CustomInput: FC<NodeProps> = () => (
<>
<div>Only connectable with B</div>
<Handle
type='source'
type="source"
position={Position.Right}
isValidConnection={isValidConnection}
/>
@@ -38,13 +39,13 @@ const CustomInput: FC<NodeProps> = () => (
const CustomNode: FC<NodeProps> = ({ id }) => (
<>
<Handle
type='target'
type="target"
position={Position.Left}
isValidConnection={isValidConnection}
/>
<div>{id}</div>
<Handle
type='source'
type="source"
position={Position.Right}
isValidConnection={isValidConnection}
/>

View File

@@ -2,6 +2,13 @@ import React from 'react';
import { useRouter } from 'next/router';
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 = [
'/',
'/Backgrounds',

View File

@@ -1,15 +1,16 @@
import React, { MouseEvent } from 'react';
import ReactFlow, {
import {
ReactFlow,
ReactFlowProvider,
Node,
Edge,
useReactFlow,
} from '@react-flow/core';
import Minimap from '@react-flow/minimap';
import Background, { BackgroundVariant } from '@react-flow/background';
import Controls from '@react-flow/controls';
import { MiniMap } from '@react-flow/minimap';
import { Background, BackgroundVariant } from '@react-flow/background';
import { Controls } from '@react-flow/controls';
const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node);
const onNodeDragStop = (_: MouseEvent, node: Node) =>
@@ -95,7 +96,7 @@ const BasicFlow = () => {
selectNodesOnDrag={false}
>
<Background variant={BackgroundVariant.Lines} />
<Minimap />
<MiniMap />
<Controls />
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>

View File

@@ -3,6 +3,7 @@
&:focus,
&:focus-visible {
outline: none;
.react-flow__edge-path {
stroke: #555;
}
@@ -29,10 +30,6 @@
font-size: 10px;
}
.react-flow__edge-textbg {
fill: white;
}
.react-flow__connection-path {
stroke: #b1b1b7;
stroke-width: 1;

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
}
}
]
]
}

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;
}
}

View File

@@ -10,10 +10,11 @@
],
"private": true,
"scripts": {
"postinstall": "preconstruct dev && yarn run packages",
"dev": "preconstruct watch",
"dev:example": "cd examples/nextjs && yarn dev",
"postinstall": "preconstruct dev",
"build": "preconstruct build",
"build": "preconstruct build && yarn run packages",
"packages": "yarn workspaces foreach --include '@react-flow/**' run build",
"test:all": "yarn test:chrome && yarn test:firefox",
"test:chrome": "cypress run --browser chrome",
"test:firefox": "cypress run --browser firefox",
@@ -29,10 +30,7 @@
"@babel/preset-react": "^7.17.12",
"@babel/preset-typescript": "^7.17.12",
"@preconstruct/cli": "^2.1.8",
"autoprefixer": "^10.4.7",
"cypress": "^10.3.1",
"postcss": "^8.4.14",
"postcss-nested": "^5.0.6",
"start-server-and-test": "^1.14.0",
"typescript": "^4.7.4"
},

View File

@@ -1,7 +1,7 @@
{
"name": "@react-flow/background",
"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",
"module": "dist/react-flow-background.esm.js",
"license": "MIT",

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 { useStore, ReactFlowState } from '@react-flow/core';
import { BackgroundProps, BackgroundVariant } from './types';
import { DotPattern, LinePattern } from './utils';
import { DotPattern, LinePattern } from './Patterns';
const defaultColors = {
const defaultColor = {
[BackgroundVariant.Dots]: '#91919a',
[BackgroundVariant.Lines]: '#eee',
[BackgroundVariant.Cross]: '#e2e2e2',
@@ -34,8 +34,8 @@ function Background({
const patternId = useId();
const [tX, tY, tScale] = useStore(transformSelector);
const patternColor = color ? color : defaultColors[variant];
const patternSize = size ? size : defaultSize[variant];
const patternColor = color || defaultColor[variant];
const patternSize = size || defaultSize[variant];
const isDots = variant === BackgroundVariant.Dots;
const isCross = variant === BackgroundVariant.Cross;
const gapXY: [number, number] = Array.isArray(gap) ? gap : [gap, gap];
@@ -55,15 +55,14 @@ function Background({
return (
<svg
className={cc([
'react-flow__background',
'react-flow__container',
className,
])}
className={cc(['react-flow__background', className])}
style={{
...style,
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
left: 0,
}}
ref={ref}
>

View File

@@ -1,2 +1,2 @@
export { default as Background, default } from './Background';
export { default as Background } from './Background';
export * from './types';

View File

@@ -1,12 +1,13 @@
{
"name": "@react-flow/controls",
"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",
"module": "dist/react-flow-controls.esm.js",
"license": "MIT",
"dependencies": {
"@react-flow/core": "11.0.0",
"@react-flow/core": "^11.0.0",
"@react-flow/css-utils": "^11.0.0",
"classcat": "^5.0.3"
},
"peerDependencies": {

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;

View File

@@ -5,31 +5,21 @@ import {
useStoreApi,
useReactFlow,
ReactFlowState,
Panel,
} from '@react-flow/core';
import { injectStyle } from '@react-flow/css-utils';
import PlusIcon from './Icons/Plus';
import MinusIcon from './Icons/Minus';
import FitviewIcon from './Icons/FitView';
import LockIcon from './Icons/Lock';
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';
export const ControlButton: FC<PropsWithChildren<ControlButtonProps>> = ({
children,
className,
...rest
}) => (
<button
type='button'
className={cc(['react-flow__controls-button', className])}
{...rest}
>
{children}
</button>
);
injectStyle(baseStyle);
const isInteractiveSelector = (s: ReactFlowState) =>
s.nodesDraggable && s.nodesConnectable && s.elementsSelectable;
@@ -46,6 +36,7 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
onInteractiveChange,
className,
children,
position = 'bottom-left',
}) => {
const store = useStoreApi();
const [isVisible, setIsVisible] = useState<boolean>(false);
@@ -86,22 +77,26 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
};
return (
<div className={cc(['react-flow__controls', className])} style={style}>
<Panel
className={cc(['react-flow__controls', className])}
position={position}
style={style}
>
{showZoom && (
<>
<ControlButton
onClick={onZoomInHandler}
className='react-flow__controls-zoomin'
title='zoom in'
aria-label='zoom in'
className="react-flow__controls-zoomin"
title="zoom in"
aria-label="zoom in"
>
<PlusIcon />
</ControlButton>
<ControlButton
onClick={onZoomOutHandler}
className='react-flow__controls-zoomout'
title='zoom out'
aria-label='zoom out'
className="react-flow__controls-zoomout"
title="zoom out"
aria-label="zoom out"
>
<MinusIcon />
</ControlButton>
@@ -109,26 +104,26 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
)}
{showFitView && (
<ControlButton
className='react-flow__controls-fitview'
className="react-flow__controls-fitview"
onClick={onFitViewHandler}
title='fit view'
aria-label='fit view'
title="fit view"
aria-label="fit view"
>
<FitviewIcon />
</ControlButton>
)}
{showInteractive && (
<ControlButton
className='react-flow__controls-interactive'
className="react-flow__controls-interactive"
onClick={onToggleInteractivity}
title='toggle interactivity'
aria-label='toggle interactivity'
title="toggle interactivity"
aria-label="toggle interactivity"
>
{isInteractive ? <UnlockIcon /> : <LockIcon />}
</ControlButton>
)}
{children}
</div>
</Panel>
);
};

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 './Controls';

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;
}
}
}

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;

View File

@@ -1,5 +1,5 @@
import { ButtonHTMLAttributes, HTMLAttributes } from 'react';
import { FitViewOptions } from '@react-flow/core';
import { FitViewOptions, PanelPosition } from '@react-flow/core';
export interface ControlProps extends HTMLAttributes<HTMLDivElement> {
showZoom?: boolean;
@@ -10,6 +10,7 @@ export interface ControlProps extends HTMLAttributes<HTMLDivElement> {
onZoomOut?: () => void;
onFitView?: () => void;
onInteractiveChange?: (interactiveStatus: boolean) => void;
position?: PanelPosition;
}
export interface ControlButtonProps

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

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

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

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

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

File diff suppressed because it is too large Load Diff

View File

@@ -12,8 +12,12 @@
"type": "git",
"url": "https://github.com/wbkd/react-flow.git"
},
"scripts": {
"build": "postcss src/styles/*.css --config ../../tooling/postcss.config.json --dir dist"
},
"dependencies": {
"@babel/runtime": "^7.18.0",
"@react-flow/css-utils": "^11.0.0",
"classcat": "^5.0.3",
"d3-drag": "^3.0.0",
"d3-selection": "^3.0.0",
@@ -30,6 +34,11 @@
],
"devDependencies": {
"@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"
}
}

View File

@@ -1,11 +1,11 @@
import React from 'react';
import cc from 'classcat';
import { AttributionPosition, ProOptions } from '../../types';
import Panel from '../Panel';
import { PanelPosition, ProOptions } from '../../types';
type AttributionProps = {
proOptions?: ProOptions;
position?: AttributionPosition;
position?: PanelPosition;
};
function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps) {
@@ -13,17 +13,16 @@ function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps
return null;
}
const positionClasses = `${position}`.split('-');
return (
<div
className={cc(['react-flow__attribution', ...positionClasses])}
<Panel
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"
>
<a href="https://reactflow.dev" target="_blank" rel="noopener noreferrer" aria-label="React Flow attribution">
React Flow
</a>
</div>
</Panel>
);
}

View File

@@ -113,7 +113,7 @@ export default ({
return (
<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>
);
};

View File

@@ -32,7 +32,14 @@ export default ({
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}
</>
);

View File

@@ -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;

View File

@@ -9,6 +9,7 @@ import UserSelection from '../../components/UserSelection';
import NodesSelection from '../../components/NodesSelection';
import { ReactFlowState } from '../../types';
import { containerStyle } from '../../styles';
export type FlowRendererProps = Omit<
GraphViewProps,
@@ -104,13 +105,14 @@ const FlowRenderer = ({
/>
)}
<div
className="react-flow__pane react-flow__container"
className="react-flow__pane"
onClick={onClick}
onMouseEnter={onPaneMouseEnter}
onMouseMove={onPaneMouseMove}
onMouseLeave={onPaneMouseLeave}
onContextMenu={onContextMenu}
onWheel={onWheel}
style={containerStyle}
/>
</ZoomPane>
);

View File

@@ -3,6 +3,7 @@ import shallow from 'zustand/shallow';
import useVisibleNodes from '../../hooks/useVisibleNodes';
import { useStore } from '../../store';
import { containerStyle } from '../../styles';
import { NodeMouseHandler, NodeTypesWrapped, Position, ReactFlowState, WrapNodeProps } from '../../types';
import { internalsSymbol } from '../../utils';
@@ -61,7 +62,7 @@ const NodeRenderer = (props: NodeRendererProps) => {
}, []);
return (
<div className="react-flow__nodes react-flow__container">
<div className="react-flow__nodes" style={containerStyle}>
{nodes.map((node) => {
let nodeType = node.type || 'default';

View File

@@ -1,5 +1,6 @@
import React, { forwardRef, useId } from 'react';
import React, { CSSProperties, forwardRef, useId } from 'react';
import cc from 'classcat';
import { injectStyle } from '@react-flow/css-utils';
import Attribution from '../../components/Attribution';
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 SelectionListener from '../../components/SelectionListener';
import StoreUpdater from '../../components/StoreUpdater';
import baseStyle from '../../styles/base';
import '../../style.css';
import '../../theme-default.css';
injectStyle(baseStyle);
import {
ConnectionLineType,
@@ -48,6 +49,13 @@ const defaultEdgeTypes: EdgeTypes = {
const initSnapGrid: [number, number] = [15, 15];
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>(
(
{
@@ -143,6 +151,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
defaultEdgeOptions,
elevateEdgesOnSelect = false,
disableKeyboardA11y = false,
style,
...rest
},
ref
@@ -152,7 +161,7 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
const rfId = useId();
return (
<div {...rest} ref={ref} className={cc(['react-flow', className])}>
<div {...rest} style={{ ...style, ...wrapperStyle }} ref={ref} className={cc(['react-flow', className])}>
<Wrapper>
<GraphView
onInit={onInit}

View File

@@ -9,6 +9,7 @@ import useResizeHandler from '../../hooks/useResizeHandler';
import { useStore, useStoreApi } from '../../store';
import { Viewport, PanOnScrollMode, ReactFlowState } from '../../types';
import { FlowRendererProps } from '../FlowRenderer';
import { containerStyle } from '../../styles';
type ZoomPaneProps = Omit<
FlowRendererProps,
@@ -253,7 +254,7 @@ const ZoomPane = ({
]);
return (
<div className="react-flow__renderer react-flow__container" ref={zoomPane}>
<div className="react-flow__renderer" ref={zoomPane} style={containerStyle}>
{children}
</div>
);

View File

@@ -1,7 +1,4 @@
import ReactFlow from './container/ReactFlow';
export default ReactFlow;
export { default as ReactFlow } from './container/ReactFlow';
export { default as Handle } from './components/Handle';
export { default as EdgeText } from './components/Edges/EdgeText';
export { default as StraightEdge } from './components/Edges/StraightEdge';
@@ -18,7 +15,7 @@ export {
} from './components/Edges/SimpleBezierEdge';
export { default as SmoothStepEdge, getSmoothStepPath } from './components/Edges/SmoothStepEdge';
export { internalsSymbol } from './utils';
export { internalsSymbol, rectToBox, boxToRect, getBoundsOfRects } from './utils';
export {
isNode,
isEdge,
@@ -33,6 +30,7 @@ export {
export { applyNodeChanges, applyEdgeChanges } from './utils/changes';
export { getMarkerEnd, getCenter as getEdgeCenter } from './components/Edges/utils';
export { default as ReactFlowProvider } from './components/ReactFlowProvider';
export { default as Panel } from './components/Panel';
export { default as useReactFlow } from './hooks/useReactFlow';
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals';

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%);
}
}

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;

View File

@@ -0,0 +1,9 @@
import { CSSProperties } from 'react';
export const containerStyle: CSSProperties = {
position: 'absolute',
width: '100%',
height: '100%',
top: 0,
left: 0,
};

View File

@@ -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;
}
}

View File

@@ -221,13 +221,7 @@ export type OnSelectionChangeParams = {
export type OnSelectionChangeFunc = (params: OnSelectionChangeParams) => void;
export type AttributionPosition =
| 'top-left'
| 'top-center'
| 'top-right'
| 'bottom-left'
| 'bottom-center'
| 'bottom-right';
export type PanelPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
export type ProOptions = {
hideAttribution: boolean;

View File

@@ -36,7 +36,7 @@ export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({
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)));
export const isNumeric = (n: any): n is number => !isNaN(n) && isFinite(n);

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"
}

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));
}

View File

@@ -6,18 +6,13 @@
"module": "dist/react-flow-minimap.esm.js",
"license": "MIT",
"dependencies": {
"@react-flow/core": "11.0.0",
"@react-flow/core": "^11.0.0",
"@react-flow/css-utils": "^11.0.0",
"classcat": "^5.0.3",
"d3-drag": "^3.0.0",
"d3-selection": "^3.0.0",
"zustand": "^3.7.2"
},
"peerDependencies": {
"react": "^18.1.0",
"react-dom": ">=18"
},
"devDependencies": {
"@types/d3-drag": "^3.0.1",
"@types/d3-selection": "^3.0.2"
}
}

View File

@@ -4,16 +4,18 @@ import shallow from 'zustand/shallow';
import {
useStore,
getRectOfNodes,
Box,
ReactFlowState,
Rect,
Panel,
getBoundsOfRects,
} from '@react-flow/core';
import { injectStyle } from '@react-flow/css-utils';
import MiniMapNode from './MiniMapNode';
import MiniMapDrag from './MiniMapDrag';
import { MiniMapProps, GetMiniMapNodeAttribute } from './types';
import baseStyle from './style';
import './style.css';
injectStyle(baseStyle);
declare const window: any;
@@ -30,30 +32,6 @@ const selector = (s: ReactFlowState) => ({
const getAttrFunction = (func: any): GetMiniMapNodeAttribute =>
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';
function MiniMap({
@@ -65,6 +43,7 @@ function MiniMap({
nodeBorderRadius = 5,
nodeStrokeWidth = 2,
maskColor = 'rgb(240, 242, 243, 0.7)',
position = 'bottom-right',
}: MiniMapProps) {
const minimapId = useId();
const {
@@ -85,7 +64,7 @@ function MiniMap({
height: containerHeight / transform[2],
};
const boundingRect =
nodes.length > 0 ? getBoundsofRects(getRectOfNodes(nodes), viewBB) : viewBB;
nodes.length > 0 ? getBoundsOfRects(getRectOfNodes(nodes), viewBB) : viewBB;
const scaledWidth = boundingRect.width / elementWidth;
const scaledHeight = boundingRect.height / elementHeight;
const viewScale = Math.max(scaledWidth, scaledHeight);
@@ -103,54 +82,52 @@ function MiniMap({
const labelledBy = `${ARIA_LABEL_KEY}-${minimapId}`;
return (
<svg
width={elementWidth}
height={elementHeight}
viewBox={`${x} ${y} ${width} ${height}`}
<Panel
position={position}
style={style}
className={cc(['react-flow__minimap', className])}
role="img"
aria-labelledby={labelledBy}
>
<title id={labelledBy}>React Flow mini map</title>
{nodes
.filter((node) => !node.hidden && node.width && node.height)
.map((node) => {
return (
<MiniMapNode
key={node.id}
x={node.positionAbsolute?.x ?? 0}
y={node.positionAbsolute?.y ?? 0}
width={node.width!}
height={node.height!}
style={node.style}
className={nodeClassNameFunc(node)}
color={nodeColorFunc(node)}
borderRadius={nodeBorderRadius}
strokeColor={nodeStrokeColorFunc(node)}
strokeWidth={nodeStrokeWidth}
shapeRendering={shapeRendering}
/>
);
})}
<MiniMapDrag
x={viewBB.x}
y={viewBB.y}
width={viewBB.width}
height={viewBB.height}
/>
<path
className="react-flow__minimap-mask"
d={`M${x - offset},${y - offset}h${width + offset * 2}v${
height + offset * 2
}h${-width - offset * 2}z
<svg
width={elementWidth}
height={elementHeight}
viewBox={`${x} ${y} ${width} ${height}`}
role="img"
aria-labelledby={labelledBy}
>
<title id={labelledBy}>React Flow mini map</title>
{nodes
.filter((node) => !node.hidden && node.width && node.height)
.map((node) => {
return (
<MiniMapNode
key={node.id}
x={node.positionAbsolute?.x ?? 0}
y={node.positionAbsolute?.y ?? 0}
width={node.width!}
height={node.height!}
style={node.style}
className={nodeClassNameFunc(node)}
color={nodeColorFunc(node)}
borderRadius={nodeBorderRadius}
strokeColor={nodeStrokeColorFunc(node)}
strokeWidth={nodeStrokeWidth}
shapeRendering={shapeRendering}
/>
);
})}
<path
className="react-flow__minimap-mask"
d={`M${x - offset},${y - offset}h${width + offset * 2}v${
height + offset * 2
}h${-width - offset * 2}z
M${viewBB.x},${viewBB.y}h${viewBB.width}v${
viewBB.height
}h${-viewBB.width}z`}
fill={maskColor}
fillRule="evenodd"
/>
</svg>
viewBB.height
}h${-viewBB.width}z`}
fill={maskColor}
fillRule="evenodd"
/>
</svg>
</Panel>
);
}

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;

View File

@@ -1,2 +1,2 @@
export * from './types';
export { default as MiniMap, default } from './MiniMap';
export { default as MiniMap } from './MiniMap';

View File

@@ -1,7 +0,0 @@
.react-flow__minimap {
background-color: #fff;
position: absolute;
z-index: 5;
bottom: 20px;
right: 15px;
}

View File

@@ -0,0 +1,6 @@
const style = `
.react-flow__minimap {
background-color: #fff;
}`;
export default style;

View File

@@ -1,5 +1,5 @@
import { HTMLAttributes } from 'react';
import { Node } from '@react-flow/core';
import { Node, PanelPosition } from '@react-flow/core';
export type GetMiniMapNodeAttribute<NodeData = any> = (
node: Node<NodeData>
@@ -13,4 +13,5 @@ export interface MiniMapProps<NodeData = any>
nodeBorderRadius?: number;
nodeStrokeWidth?: number;
maskColor?: string;
position?: PanelPosition;
}

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"
}
}

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';

View File

@@ -0,0 +1 @@
@import '@react-flow/core/dist/theme-default.css';

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));
},
},
};
};

View File

@@ -0,0 +1,7 @@
module.exports = {
plugins: [
require('postcss-nested'),
require('autoprefixer'),
require('postcss-import'),
],
};

907
yarn.lock

File diff suppressed because it is too large Load Diff