feat(react/svelte): controlled viewport (#3461)

* feat(react): controlled viewport

* refactor(react): cleanup viewport

* feat(svelte): two way data binding for viewport, cleanup transform <> viewport stuff

* refactor(svelte): viewport stuff
This commit is contained in:
Moritz Klack
2023-09-28 16:19:03 +02:00
committed by GitHub
parent 2215b68438
commit ae33cba887
35 changed files with 351 additions and 95 deletions
+6
View File
@@ -4,6 +4,7 @@ import { BrowserRouter, Route, Routes, useLocation, useNavigate } from 'react-ro
import Basic from '../examples/Basic';
import Backgrounds from '../examples/Backgrounds';
import ControlledUncontrolled from '../examples/ControlledUncontrolled';
import ControlledViewport from '../examples/ControlledViewport';
import CustomConnectionLine from '../examples/CustomConnectionLine';
import CustomMiniMapNode from '../examples/CustomMiniMapNode';
import CustomNode from '../examples/CustomNode';
@@ -74,6 +75,11 @@ const routes: IRoute[] = [
path: '/controlled-uncontrolled',
component: ControlledUncontrolled,
},
{
name: 'Controlled Viewport',
path: '/controlled-viewport',
component: ControlledViewport,
},
{
name: 'Custom Connection Line',
path: '/custom-connectionline',
@@ -0,0 +1,88 @@
import { MouseEvent, useCallback, useState } from 'react';
import {
ReactFlow,
addEdge,
Node,
Connection,
Edge,
useNodesState,
useEdgesState,
Viewport,
Panel,
MiniMap,
Background,
ReactFlowProvider,
useReactFlow,
Controls,
} from '@xyflow/react';
const initNodes: Node[] = [
{
id: '1a',
type: 'input',
data: { label: 'Node 1' },
position: { x: 250, y: 5 },
className: 'light',
ariaLabel: 'Input Node 1',
},
{
id: '2a',
data: { label: 'Node 2' },
position: { x: 100, y: 100 },
className: 'light',
ariaLabel: 'Default Node 2',
},
{
id: '3a',
data: { label: 'Node 3' },
position: { x: 400, y: 100 },
className: 'light',
},
{
id: '4a',
data: { label: 'Node 4' },
position: { x: 400, y: 200 },
className: 'light',
},
];
const initEdges: Edge[] = [
{ id: 'e1-2', source: '1a', target: '2a', ariaLabel: undefined },
{ id: 'e1-3', source: '1a', target: '3a' },
];
const Flow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges);
const [viewport, setViewport] = useState<Viewport>({ x: 0, y: 0, zoom: 1 });
const { fitView } = useReactFlow();
const onConnect = useCallback((params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)), [setEdges]);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
viewport={viewport}
onViewportChange={setViewport}
>
<Panel position="top-left">
<button onClick={() => setViewport((vp) => ({ ...vp, y: vp.y + 10 }))}>update viewport</button>
<button onClick={() => fitView()}>fitView</button>
</Panel>
<MiniMap />
<Background />
<Controls />
</ReactFlow>
);
};
export default () => (
<ReactFlowProvider>
<Flow />
</ReactFlowProvider>
);