Merge pull request #3613 from xyflow/next
Svelte Flow Release 0.0.27 and e2e tests
This commit is contained in:
@@ -1,8 +0,0 @@
|
|||||||
# Changesets
|
|
||||||
|
|
||||||
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
||||||
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
||||||
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
|
||||||
|
|
||||||
We have a quick list of common questions to get you started engaging with this project in
|
|
||||||
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://unpkg.com/@changesets/config@2.1.1/schema.json",
|
|
||||||
"changelog": ["@changesets/changelog-github", { "repo": "wbkd/react-flow" }],
|
|
||||||
"commit": false,
|
|
||||||
"fixed": [],
|
|
||||||
"linked": [],
|
|
||||||
"access": "public",
|
|
||||||
"baseBranch": "main",
|
|
||||||
"updateInternalDependencies": "patch",
|
|
||||||
"ignore": []
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@ import { defineConfig } from 'cypress';
|
|||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
e2e: {
|
e2e: {
|
||||||
baseUrl: 'http://localhost:3000',
|
baseUrl: 'http://localhost:3000/examples',
|
||||||
viewportWidth: 1280,
|
viewportWidth: 1280,
|
||||||
viewportHeight: 720,
|
viewportHeight: 720,
|
||||||
video: false,
|
video: false,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>React Flow Examples</title>
|
<title>React Flow Examples</title>
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
+13
-13
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "@xyflow/react-examples",
|
"name": "react-examples",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -15,25 +15,25 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@xyflow/react": "workspace:*",
|
"@xyflow/react": "workspace:*",
|
||||||
"classcat": "^5.0.3",
|
"classcat": "^5.0.4",
|
||||||
"dagre": "^0.8.5",
|
"dagre": "^0.8.5",
|
||||||
"localforage": "^1.10.0",
|
"localforage": "^1.10.0",
|
||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-router-dom": "^6.3.0",
|
"react-router-dom": "^6.18.0",
|
||||||
"zustand": "^4.4.1"
|
"zustand": "^4.4.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cypress/skip-test": "^2.6.1",
|
"@cypress/skip-test": "^2.6.1",
|
||||||
"@types/dagre": "^0.7.48",
|
"@types/dagre": "^0.7.52",
|
||||||
"@types/react": "^18.0.17",
|
"@types/react": "^18.2.36",
|
||||||
"@types/react-dom": "^18.0.6",
|
"@types/react-dom": "^18.2.14",
|
||||||
"@vitejs/plugin-react": "4.0.4",
|
"@vitejs/plugin-react": "4.1.1",
|
||||||
"@vitejs/plugin-react-swc": "^3.3.2",
|
"@vitejs/plugin-react-swc": "^3.4.1",
|
||||||
"cypress": "12.17.3",
|
"cypress": "12.17.3",
|
||||||
"cypress-real-events": "1.10.0",
|
"cypress-real-events": "1.11.0",
|
||||||
"start-server-and-test": "^1.14.0",
|
"start-server-and-test": "^2.0.2",
|
||||||
"typescript": "5.1.3",
|
"typescript": "5.2.2",
|
||||||
"vite": "4.4.9"
|
"vite": "4.5.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
@@ -1 +0,0 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,34 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import { Outlet, useLocation, useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
import routes from './routes';
|
||||||
|
|
||||||
|
export default function Header() {
|
||||||
|
const location = useLocation();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [currentPath, setCurrentPath] = useState(location.pathname);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const name = routes.find((route) => route.path === currentPath)?.name;
|
||||||
|
document.title = `React Flow Examples${name ? ' - ' + name : ''}`;
|
||||||
|
navigate(currentPath);
|
||||||
|
}, [currentPath]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<header>
|
||||||
|
<a className="logo" href="https://github.com/xyflow/xyflow">
|
||||||
|
React Flow Dev
|
||||||
|
</a>
|
||||||
|
<select value={currentPath} onChange={(event) => setCurrentPath(event.target.value)}>
|
||||||
|
{routes.map((route) => (
|
||||||
|
<option value={route.path} key={route.path}>
|
||||||
|
{route.name}
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</header>
|
||||||
|
<Outlet />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,326 +1,21 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom';
|
||||||
import { BrowserRouter, Route, Routes, useLocation, useNavigate } from 'react-router-dom';
|
|
||||||
|
|
||||||
|
import routes from './routes';
|
||||||
|
import GenericTestRoutes from '../generic-tests';
|
||||||
import Basic from '../examples/Basic';
|
import Basic from '../examples/Basic';
|
||||||
import Backgrounds from '../examples/Backgrounds';
|
import Header from './header';
|
||||||
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';
|
|
||||||
import DefaultNodes from '../examples/DefaultNodes';
|
|
||||||
import DragHandle from '../examples/DragHandle';
|
|
||||||
import DragNDrop from '../examples/DragNDrop';
|
|
||||||
import EasyConnect from '../examples/EasyConnect';
|
|
||||||
import Edges from '../examples/Edges';
|
|
||||||
import EdgeRenderer from '../examples/EdgeRenderer';
|
|
||||||
import EdgeTypes from '../examples/EdgeTypes';
|
|
||||||
import Empty from '../examples/Empty';
|
|
||||||
import Figma from '../examples/Figma';
|
|
||||||
import FloatingEdges from '../examples/FloatingEdges';
|
|
||||||
import Hidden from '../examples/Hidden';
|
|
||||||
import Interaction from '../examples/Interaction';
|
|
||||||
import Intersection from '../examples/Intersection';
|
|
||||||
import Layouting from '../examples/Layouting';
|
|
||||||
import MultiFlows from '../examples/MultiFlows';
|
|
||||||
import NestedNodes from '../examples/NestedNodes';
|
|
||||||
import NodeResizer from '../examples/NodeResizer';
|
|
||||||
import NodeTypeChange from '../examples/NodeTypeChange';
|
|
||||||
import NodeTypesObjectChange from '../examples/NodeTypesObjectChange';
|
|
||||||
import Overview from '../examples/Overview';
|
|
||||||
import Provider from '../examples/Provider';
|
|
||||||
import SaveRestore from '../examples/SaveRestore';
|
|
||||||
import Stress from '../examples/Stress';
|
|
||||||
import Subflow from '../examples/Subflow';
|
|
||||||
import SwitchFlow from '../examples/Switch';
|
|
||||||
import TouchDevice from '../examples/TouchDevice';
|
|
||||||
import Undirectional from '../examples/Undirectional';
|
|
||||||
import UpdatableEdge from '../examples/UpdatableEdge';
|
|
||||||
import UpdateNode from '../examples/UpdateNode';
|
|
||||||
import UseUpdateNodeInternals from '../examples/UseUpdateNodeInternals';
|
|
||||||
import UseReactFlow from '../examples/UseReactFlow';
|
|
||||||
import Validation from '../examples/Validation';
|
|
||||||
import UseKeyPress from '../examples/UseKeyPress';
|
|
||||||
import EdgeRouting from '../examples/EdgeRouting';
|
|
||||||
import CancelConnection from '../examples/CancelConnection';
|
|
||||||
import InteractiveMinimap from '../examples/InteractiveMinimap';
|
|
||||||
import UseOnSelectionChange from '../examples/UseOnSelectionChange';
|
|
||||||
import NodeToolbar from '../examples/NodeToolbar';
|
|
||||||
import useNodesInitialized from '../examples/UseNodesInit';
|
|
||||||
|
|
||||||
interface IRoute {
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
component: React.ComponentType;
|
|
||||||
}
|
|
||||||
|
|
||||||
const routes: IRoute[] = [
|
|
||||||
{
|
|
||||||
name: 'Basic',
|
|
||||||
path: '/',
|
|
||||||
component: Basic,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Backgrounds',
|
|
||||||
path: '/backgrounds',
|
|
||||||
component: Backgrounds,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Cancel Connection',
|
|
||||||
path: '/cancel-connection',
|
|
||||||
component: CancelConnection,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Controlled/Uncontrolled',
|
|
||||||
path: '/controlled-uncontrolled',
|
|
||||||
component: ControlledUncontrolled,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Controlled Viewport',
|
|
||||||
path: '/controlled-viewport',
|
|
||||||
component: ControlledViewport,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Custom Connection Line',
|
|
||||||
path: '/custom-connectionline',
|
|
||||||
component: CustomConnectionLine,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Custom Minimap Node',
|
|
||||||
path: '/custom-minimap-node',
|
|
||||||
component: CustomMiniMapNode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Custom Node',
|
|
||||||
path: '/custom-node',
|
|
||||||
component: CustomNode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Default Nodes',
|
|
||||||
path: '/default-nodes',
|
|
||||||
component: DefaultNodes,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Drag Handle',
|
|
||||||
path: '/draghandle',
|
|
||||||
component: DragHandle,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Drag and Drop',
|
|
||||||
path: '/dragndrop',
|
|
||||||
component: DragNDrop,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'EasyConnect',
|
|
||||||
path: '/easy-connect',
|
|
||||||
component: EasyConnect,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Edges',
|
|
||||||
path: '/edges',
|
|
||||||
component: Edges,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Edge Renderer',
|
|
||||||
path: '/edge-renderer',
|
|
||||||
component: EdgeRenderer,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Edge Types',
|
|
||||||
path: '/edge-types',
|
|
||||||
component: EdgeTypes,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Edge Routing',
|
|
||||||
path: '/edge-routing',
|
|
||||||
component: EdgeRouting,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Empty',
|
|
||||||
path: '/empty',
|
|
||||||
component: Empty,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Figma',
|
|
||||||
path: '/figma',
|
|
||||||
component: Figma,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Floating Edges',
|
|
||||||
path: '/floating-edges',
|
|
||||||
component: FloatingEdges,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Hidden',
|
|
||||||
path: '/hidden',
|
|
||||||
component: Hidden,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Interaction',
|
|
||||||
path: '/interaction',
|
|
||||||
component: Interaction,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Intersection',
|
|
||||||
path: '/intersection',
|
|
||||||
component: Intersection,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Interactive Minimap',
|
|
||||||
path: '/interactive-minimap',
|
|
||||||
component: InteractiveMinimap,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Layouting',
|
|
||||||
path: '/layouting',
|
|
||||||
component: Layouting,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Multi Flows',
|
|
||||||
path: '/multiflows',
|
|
||||||
component: MultiFlows,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Nested Nodes',
|
|
||||||
path: '/nested-nodes',
|
|
||||||
component: NestedNodes,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Node Type Change',
|
|
||||||
path: '/nodetype-change',
|
|
||||||
component: NodeTypeChange,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'nodeTypes Object Change',
|
|
||||||
path: '/nodetypesobject-change',
|
|
||||||
component: NodeTypesObjectChange,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'NodeToolbar',
|
|
||||||
path: '/node-toolbar',
|
|
||||||
component: NodeToolbar,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'NodeResizer',
|
|
||||||
path: '/node-resizer',
|
|
||||||
component: NodeResizer,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Overview',
|
|
||||||
path: '/overview',
|
|
||||||
component: Overview,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Provider',
|
|
||||||
path: '/provider',
|
|
||||||
component: Provider,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Save/Restore',
|
|
||||||
path: '/save-restore',
|
|
||||||
component: SaveRestore,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Stress',
|
|
||||||
path: '/stress',
|
|
||||||
component: Stress,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Subflow',
|
|
||||||
path: '/subflow',
|
|
||||||
component: Subflow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Switch Flow',
|
|
||||||
path: '/switch',
|
|
||||||
component: SwitchFlow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Touch Device',
|
|
||||||
path: '/touch-device',
|
|
||||||
component: TouchDevice,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Undirectional',
|
|
||||||
path: '/undirectional',
|
|
||||||
component: Undirectional,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Updatable Edge',
|
|
||||||
path: '/updatable-edge',
|
|
||||||
component: UpdatableEdge,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Update Node',
|
|
||||||
path: '/update-node',
|
|
||||||
component: UpdateNode,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'useNodesInitialized',
|
|
||||||
path: '/use-nodes-initialized',
|
|
||||||
component: useNodesInitialized,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'useOnSelectionChange',
|
|
||||||
path: '/use-on-selection-change',
|
|
||||||
component: UseOnSelectionChange,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'useReactFlow',
|
|
||||||
path: '/usereactflow',
|
|
||||||
component: UseReactFlow,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'useUpdateNodeInternals',
|
|
||||||
path: '/useupdatenodeinternals',
|
|
||||||
component: UseUpdateNodeInternals,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Validation',
|
|
||||||
path: '/validation',
|
|
||||||
component: Validation,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'useKeyPress',
|
|
||||||
path: '/use-key-press',
|
|
||||||
component: UseKeyPress,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const Header = () => {
|
|
||||||
const location = useLocation();
|
|
||||||
const navigate = useNavigate();
|
|
||||||
const [currentPath, setCurrentPath] = useState(location.pathname);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const name = routes.find((route) => route.path === currentPath)?.name;
|
|
||||||
document.title = `React Flow Examples${name ? ' - ' + name : ''}`;
|
|
||||||
navigate(currentPath);
|
|
||||||
}, [currentPath]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<header>
|
|
||||||
<a className="logo" href="https://github.com/wbkd/react-flow">
|
|
||||||
React Flow Dev
|
|
||||||
</a>
|
|
||||||
<select value={currentPath} onChange={(event) => setCurrentPath(event.target.value)}>
|
|
||||||
{routes.map((route) => (
|
|
||||||
<option value={route.path} key={route.path}>
|
|
||||||
{route.name}
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</header>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default () => (
|
export default () => (
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Header />
|
|
||||||
<Routes>
|
<Routes>
|
||||||
{routes.map((route) => (
|
<Route path="/" element={<Navigate to="/examples" />} />
|
||||||
<Route path={route.path} key={route.path} element={<route.component />} />
|
<Route path="examples" element={<Header />}>
|
||||||
))}
|
<Route index element={<Basic />} />
|
||||||
|
{routes.map((route) => (
|
||||||
|
<Route path={route.path} key={route.path} element={<route.component />} />
|
||||||
|
))}
|
||||||
|
</Route>
|
||||||
|
<Route path="tests/generic/*" element={<GenericTestRoutes />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,287 @@
|
|||||||
|
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';
|
||||||
|
import DefaultNodes from '../examples/DefaultNodes';
|
||||||
|
import DragHandle from '../examples/DragHandle';
|
||||||
|
import DragNDrop from '../examples/DragNDrop';
|
||||||
|
import EasyConnect from '../examples/EasyConnect';
|
||||||
|
import Edges from '../examples/Edges';
|
||||||
|
import EdgeRenderer from '../examples/EdgeRenderer';
|
||||||
|
import EdgeTypes from '../examples/EdgeTypes';
|
||||||
|
import Empty from '../examples/Empty';
|
||||||
|
import Figma from '../examples/Figma';
|
||||||
|
import FloatingEdges from '../examples/FloatingEdges';
|
||||||
|
import Hidden from '../examples/Hidden';
|
||||||
|
import Interaction from '../examples/Interaction';
|
||||||
|
import Intersection from '../examples/Intersection';
|
||||||
|
import Layouting from '../examples/Layouting';
|
||||||
|
import MultiFlows from '../examples/MultiFlows';
|
||||||
|
import NestedNodes from '../examples/NestedNodes';
|
||||||
|
import NodeResizer from '../examples/NodeResizer';
|
||||||
|
import NodeTypeChange from '../examples/NodeTypeChange';
|
||||||
|
import NodeTypesObjectChange from '../examples/NodeTypesObjectChange';
|
||||||
|
import Overview from '../examples/Overview';
|
||||||
|
import Provider from '../examples/Provider';
|
||||||
|
import SaveRestore from '../examples/SaveRestore';
|
||||||
|
import Stress from '../examples/Stress';
|
||||||
|
import Subflow from '../examples/Subflow';
|
||||||
|
import SwitchFlow from '../examples/Switch';
|
||||||
|
import TouchDevice from '../examples/TouchDevice';
|
||||||
|
import Undirectional from '../examples/Undirectional';
|
||||||
|
import UpdatableEdge from '../examples/UpdatableEdge';
|
||||||
|
import UpdateNode from '../examples/UpdateNode';
|
||||||
|
import UseUpdateNodeInternals from '../examples/UseUpdateNodeInternals';
|
||||||
|
import UseReactFlow from '../examples/UseReactFlow';
|
||||||
|
import Validation from '../examples/Validation';
|
||||||
|
import UseKeyPress from '../examples/UseKeyPress';
|
||||||
|
import EdgeRouting from '../examples/EdgeRouting';
|
||||||
|
import CancelConnection from '../examples/CancelConnection';
|
||||||
|
import InteractiveMinimap from '../examples/InteractiveMinimap';
|
||||||
|
import UseOnSelectionChange from '../examples/UseOnSelectionChange';
|
||||||
|
import NodeToolbar from '../examples/NodeToolbar';
|
||||||
|
import useNodesInitialized from '../examples/UseNodesInit';
|
||||||
|
|
||||||
|
export interface IRoute {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
component: React.ComponentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
const routes: IRoute[] = [
|
||||||
|
{
|
||||||
|
name: 'Basic',
|
||||||
|
path: 'basic',
|
||||||
|
component: Basic,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Backgrounds',
|
||||||
|
path: 'backgrounds',
|
||||||
|
component: Backgrounds,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Cancel Connection',
|
||||||
|
path: 'cancel-connection',
|
||||||
|
component: CancelConnection,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Controlled/Uncontrolled',
|
||||||
|
path: 'controlled-uncontrolled',
|
||||||
|
component: ControlledUncontrolled,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Controlled Viewport',
|
||||||
|
path: 'controlled-viewport',
|
||||||
|
component: ControlledViewport,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Custom Connection Line',
|
||||||
|
path: 'custom-connectionline',
|
||||||
|
component: CustomConnectionLine,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Custom Minimap Node',
|
||||||
|
path: 'custom-minimap-node',
|
||||||
|
component: CustomMiniMapNode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Custom Node',
|
||||||
|
path: 'custom-node',
|
||||||
|
component: CustomNode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Default Nodes',
|
||||||
|
path: 'default-nodes',
|
||||||
|
component: DefaultNodes,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Drag Handle',
|
||||||
|
path: 'draghandle',
|
||||||
|
component: DragHandle,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Drag and Drop',
|
||||||
|
path: 'dragndrop',
|
||||||
|
component: DragNDrop,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'EasyConnect',
|
||||||
|
path: 'easy-connect',
|
||||||
|
component: EasyConnect,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Edges',
|
||||||
|
path: 'edges',
|
||||||
|
component: Edges,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Edge Renderer',
|
||||||
|
path: 'edge-renderer',
|
||||||
|
component: EdgeRenderer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Edge Types',
|
||||||
|
path: 'edge-types',
|
||||||
|
component: EdgeTypes,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Edge Routing',
|
||||||
|
path: 'edge-routing',
|
||||||
|
component: EdgeRouting,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Empty',
|
||||||
|
path: 'empty',
|
||||||
|
component: Empty,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Figma',
|
||||||
|
path: 'figma',
|
||||||
|
component: Figma,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Floating Edges',
|
||||||
|
path: 'floating-edges',
|
||||||
|
component: FloatingEdges,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Hidden',
|
||||||
|
path: 'hidden',
|
||||||
|
component: Hidden,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Interaction',
|
||||||
|
path: 'interaction',
|
||||||
|
component: Interaction,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Intersection',
|
||||||
|
path: 'intersection',
|
||||||
|
component: Intersection,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Interactive Minimap',
|
||||||
|
path: 'interactive-minimap',
|
||||||
|
component: InteractiveMinimap,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Layouting',
|
||||||
|
path: 'layouting',
|
||||||
|
component: Layouting,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Multi Flows',
|
||||||
|
path: 'multiflows',
|
||||||
|
component: MultiFlows,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Nested Nodes',
|
||||||
|
path: 'nested-nodes',
|
||||||
|
component: NestedNodes,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Node Type Change',
|
||||||
|
path: 'nodetype-change',
|
||||||
|
component: NodeTypeChange,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'nodeTypes Object Change',
|
||||||
|
path: 'nodetypesobject-change',
|
||||||
|
component: NodeTypesObjectChange,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NodeToolbar',
|
||||||
|
path: 'node-toolbar',
|
||||||
|
component: NodeToolbar,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'NodeResizer',
|
||||||
|
path: 'node-resizer',
|
||||||
|
component: NodeResizer,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Overview',
|
||||||
|
path: 'overview',
|
||||||
|
component: Overview,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Provider',
|
||||||
|
path: 'provider',
|
||||||
|
component: Provider,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Save/Restore',
|
||||||
|
path: 'save-restore',
|
||||||
|
component: SaveRestore,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Stress',
|
||||||
|
path: 'stress',
|
||||||
|
component: Stress,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Subflow',
|
||||||
|
path: 'subflow',
|
||||||
|
component: Subflow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Switch Flow',
|
||||||
|
path: 'switch',
|
||||||
|
component: SwitchFlow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Touch Device',
|
||||||
|
path: 'touch-device',
|
||||||
|
component: TouchDevice,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Undirectional',
|
||||||
|
path: 'undirectional',
|
||||||
|
component: Undirectional,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Updatable Edge',
|
||||||
|
path: 'updatable-edge',
|
||||||
|
component: UpdatableEdge,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Update Node',
|
||||||
|
path: 'update-node',
|
||||||
|
component: UpdateNode,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'useNodesInitialized',
|
||||||
|
path: 'use-nodes-initialized',
|
||||||
|
component: useNodesInitialized,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'useOnSelectionChange',
|
||||||
|
path: 'use-on-selection-change',
|
||||||
|
component: UseOnSelectionChange,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'useReactFlow',
|
||||||
|
path: 'usereactflow',
|
||||||
|
component: UseReactFlow,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'useUpdateNodeInternals',
|
||||||
|
path: 'useupdatenodeinternals',
|
||||||
|
component: UseUpdateNodeInternals,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Validation',
|
||||||
|
path: 'validation',
|
||||||
|
component: Validation,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'useKeyPress',
|
||||||
|
path: 'use-key-press',
|
||||||
|
component: UseKeyPress,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default routes;
|
||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
import { BackgroundProps, ControlProps, Edge, MiniMapProps, Node, PanelProps, ReactFlowProps } from '@xyflow/react';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface FlowConfig {
|
||||||
|
flowProps: Omit<ReactFlowProps, 'nodes' | 'edges'> & { nodes: Node[]; edges: Edge[] };
|
||||||
|
panelProps: PanelProps;
|
||||||
|
backgroundProps: BackgroundProps;
|
||||||
|
controlsProps: ControlProps;
|
||||||
|
minimapProps: MiniMapProps;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -41,6 +41,7 @@ const initialNodes: Node[] = [
|
|||||||
data: { label: 'Node 4' },
|
data: { label: 'Node 4' },
|
||||||
position: { x: 400, y: 200 },
|
position: { x: 400, y: 200 },
|
||||||
className: 'light',
|
className: 'light',
|
||||||
|
connectable: false,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ const getId = () => `${id++}`;
|
|||||||
const UpdateNodeInternalsFlow = () => {
|
const UpdateNodeInternalsFlow = () => {
|
||||||
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 { project } = useReactFlow();
|
const { screenToFlowPosition } = useReactFlow();
|
||||||
|
|
||||||
const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
||||||
const onEdgeUpdate = useCallback(
|
const onEdgeUpdate = useCallback(
|
||||||
@@ -196,12 +196,12 @@ const UpdateNodeInternalsFlow = () => {
|
|||||||
setNodes((nds) =>
|
setNodes((nds) =>
|
||||||
nds.concat({
|
nds.concat({
|
||||||
id: getId(),
|
id: getId(),
|
||||||
position: project({ x: evt.clientX, y: evt.clientY - 40 }),
|
position: screenToFlowPosition({ x: evt.clientX, y: evt.clientY }),
|
||||||
type: 'custom',
|
type: 'custom',
|
||||||
data: null,
|
data: null,
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
[project, setNodes]
|
[screenToFlowPosition, setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import {
|
import {
|
||||||
ReactFlow,
|
ReactFlow,
|
||||||
addEdge,
|
addEdge,
|
||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
useEdgesState,
|
useEdgesState,
|
||||||
useOnSelectionChange,
|
useOnSelectionChange,
|
||||||
OnSelectionChangeParams,
|
OnSelectionChangeParams,
|
||||||
|
OnSelectionChangeFunc,
|
||||||
} from '@xyflow/react';
|
} from '@xyflow/react';
|
||||||
|
|
||||||
const initialNodes: Node[] = [
|
const initialNodes: Node[] = [
|
||||||
@@ -35,9 +36,9 @@ const initialEdges: Edge[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const SelectionLogger = () => {
|
const SelectionLogger = ({ id }: { id: string }) => {
|
||||||
const onChange = useCallback(({ nodes, edges }: OnSelectionChangeParams) => {
|
const onChange = useCallback(({ nodes, edges }: OnSelectionChangeParams) => {
|
||||||
console.log(nodes, edges);
|
console.log(id, nodes, edges);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useOnSelectionChange({
|
useOnSelectionChange({
|
||||||
@@ -63,14 +64,23 @@ const Flow = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const WrappedFlow = () => (
|
const WrappedFlow = () => {
|
||||||
<ReactFlowProvider>
|
const [secondLoggerActive, setSecondLoggerActive] = useState<boolean>(true);
|
||||||
<Flow />
|
|
||||||
<SelectionLogger />
|
const toggleSecondLogger = () => {
|
||||||
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
setSecondLoggerActive(!secondLoggerActive);
|
||||||
<input type={'text'} placeholder={'name'} />
|
};
|
||||||
</div>
|
|
||||||
</ReactFlowProvider>
|
return (
|
||||||
);
|
<ReactFlowProvider>
|
||||||
|
<Flow />
|
||||||
|
<SelectionLogger id="Logger 1" />
|
||||||
|
{secondLoggerActive && <SelectionLogger id="Logger 2" />}
|
||||||
|
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
||||||
|
<button onClick={toggleSecondLogger}>{secondLoggerActive ? 'Disable' : 'Enable'} Logger 2</button>
|
||||||
|
</div>
|
||||||
|
</ReactFlowProvider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default WrappedFlow;
|
export default WrappedFlow;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
|
||||||
const onConnect = (params: Connection | Edge) => setEdges((eds) => addEdge(params, eds));
|
const onConnect = (params: Connection | Edge) => setEdges((eds) => addEdge(params, eds));
|
||||||
const {
|
const {
|
||||||
project,
|
screenToFlowPosition,
|
||||||
setCenter,
|
setCenter,
|
||||||
zoomIn,
|
zoomIn,
|
||||||
zoomOut,
|
zoomOut,
|
||||||
@@ -72,9 +72,9 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
|
|
||||||
const onPaneClick = useCallback(
|
const onPaneClick = useCallback(
|
||||||
(evt: MouseEvent) => {
|
(evt: MouseEvent) => {
|
||||||
const projectedPosition = project({
|
const projectedPosition = screenToFlowPosition({
|
||||||
x: evt.clientX,
|
x: evt.clientX,
|
||||||
y: evt.clientY - 40,
|
y: evt.clientY,
|
||||||
});
|
});
|
||||||
|
|
||||||
setNodes((nds) =>
|
setNodes((nds) =>
|
||||||
@@ -87,7 +87,7 @@ const UseZoomPanHelperFlow = () => {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[project, setNodes]
|
[screenToFlowPosition, setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onNodeClick = useCallback(
|
const onNodeClick = useCallback(
|
||||||
|
|||||||
@@ -36,20 +36,20 @@ const UpdateNodeInternalsFlow = () => {
|
|||||||
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
|
||||||
const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
const onConnect = useCallback((params: Edge | Connection) => setEdges((els) => addEdge(params, els)), [setEdges]);
|
||||||
|
|
||||||
const { project } = useReactFlow();
|
const { screenToFlowPosition } = useReactFlow();
|
||||||
|
|
||||||
const onPaneClick = useCallback(
|
const onPaneClick = useCallback(
|
||||||
(evt: MouseEvent) =>
|
(evt: MouseEvent) =>
|
||||||
setNodes((nds) =>
|
setNodes((nds) =>
|
||||||
nds.concat({
|
nds.concat({
|
||||||
id: getId(),
|
id: getId(),
|
||||||
position: project({ x: evt.clientX, y: evt.clientY - 40 }),
|
position: screenToFlowPosition({ x: evt.clientX, y: evt.clientY }),
|
||||||
data: { label: 'new node' },
|
data: { label: 'new node' },
|
||||||
targetPosition: Position.Left,
|
targetPosition: Position.Left,
|
||||||
sourcePosition: Position.Right,
|
sourcePosition: Position.Right,
|
||||||
})
|
})
|
||||||
),
|
),
|
||||||
[project, setNodes]
|
[screenToFlowPosition, setNodes]
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { useState, useCallback } from 'react';
|
||||||
|
import {
|
||||||
|
ReactFlow,
|
||||||
|
applyNodeChanges,
|
||||||
|
applyEdgeChanges,
|
||||||
|
addEdge,
|
||||||
|
OnNodesChange,
|
||||||
|
OnEdgesChange,
|
||||||
|
OnConnect,
|
||||||
|
Controls,
|
||||||
|
Panel,
|
||||||
|
MiniMap,
|
||||||
|
Background,
|
||||||
|
} from '@xyflow/react';
|
||||||
|
|
||||||
|
type FlowProps = {
|
||||||
|
flowConfig: FlowConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ({ flowConfig }: FlowProps) => {
|
||||||
|
const [nodes, setNodes] = useState(flowConfig.flowProps.nodes);
|
||||||
|
const [edges, setEdges] = useState(flowConfig.flowProps.edges);
|
||||||
|
const props = { ...flowConfig.flowProps, nodes, edges };
|
||||||
|
|
||||||
|
const onNodesChange: OnNodesChange = useCallback((changes) => setNodes((nds) => applyNodeChanges(changes, nds)), []);
|
||||||
|
const onEdgesChange: OnEdgesChange = useCallback((changes) => setEdges((eds) => applyEdgeChanges(changes, eds)), []);
|
||||||
|
const onConnect: OnConnect = useCallback((params) => setEdges((eds) => addEdge(params, eds)), []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div style={{ height: '100%' }}>
|
||||||
|
<ReactFlow {...props} onNodesChange={onNodesChange} onEdgesChange={onEdgesChange} onConnect={onConnect}>
|
||||||
|
{flowConfig.controlsProps && <Controls {...flowConfig.controlsProps} />}
|
||||||
|
{flowConfig.panelProps && <Panel {...flowConfig.panelProps} />}
|
||||||
|
{flowConfig.minimapProps && <MiniMap {...flowConfig.minimapProps} />}
|
||||||
|
{flowConfig.backgroundProps && <Background {...flowConfig.backgroundProps} />}
|
||||||
|
</ReactFlow>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
import { MarkerType } from '@xyflow/react';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
flowProps: {
|
||||||
|
fitView: true,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
data: { label: '1' },
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
data: { label: '2' },
|
||||||
|
position: { x: -100, y: 100 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
data: { label: '3' },
|
||||||
|
position: { x: 100, y: 100 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
data: { label: '4' },
|
||||||
|
position: { x: -100, y: 200 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
data: { label: '5' },
|
||||||
|
position: { x: 100, y: 200 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
data: { label: '6' },
|
||||||
|
position: { x: -100, y: 300 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
data: { label: '7' },
|
||||||
|
position: { x: 100, y: 300 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
data: { label: '8' },
|
||||||
|
position: { x: -100, y: 400 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9',
|
||||||
|
data: { label: '9' },
|
||||||
|
position: { x: 100, y: 400 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '10',
|
||||||
|
data: { label: '10' },
|
||||||
|
position: { x: -100, y: 500 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '11',
|
||||||
|
data: { label: '11' },
|
||||||
|
position: { x: 100, y: 500 },
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// id: '12',
|
||||||
|
// data: { label: '12' },
|
||||||
|
// position: { x: -100, y: 600 }
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// id: '13',
|
||||||
|
// data: { label: '13' },
|
||||||
|
// position: { x: 100, y: 600 }
|
||||||
|
// }
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'edge-with-class',
|
||||||
|
source: '1',
|
||||||
|
target: '2',
|
||||||
|
className: 'edge-class-test',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'edge-with-style',
|
||||||
|
source: '1',
|
||||||
|
target: '3',
|
||||||
|
style: {
|
||||||
|
stroke: 'red',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hidden-edge',
|
||||||
|
source: '2',
|
||||||
|
target: '4',
|
||||||
|
label: 'hidden',
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'animated-edge',
|
||||||
|
source: '3',
|
||||||
|
target: '5',
|
||||||
|
label: 'animated',
|
||||||
|
animated: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'not-selectable-edge',
|
||||||
|
source: '4',
|
||||||
|
target: '6',
|
||||||
|
label: 'not-selectable',
|
||||||
|
selectable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'not-deletable',
|
||||||
|
source: '5',
|
||||||
|
target: '7',
|
||||||
|
label: 'not-deletable',
|
||||||
|
deletable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'z-index',
|
||||||
|
source: '6',
|
||||||
|
target: '8',
|
||||||
|
label: 'z-index',
|
||||||
|
zIndex: 3141592,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'aria-label',
|
||||||
|
source: '7',
|
||||||
|
target: '9',
|
||||||
|
label: 'aria-label',
|
||||||
|
ariaLabel: 'aria-label-test',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'interaction-width',
|
||||||
|
source: '8',
|
||||||
|
target: '10',
|
||||||
|
label: 'interaction-width',
|
||||||
|
interactionWidth: 42,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'markers',
|
||||||
|
source: '9',
|
||||||
|
target: '11',
|
||||||
|
label: 'markers',
|
||||||
|
markerEnd: { type: MarkerType.Arrow },
|
||||||
|
markerStart: { type: MarkerType.ArrowClosed },
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// id: 'updatable',
|
||||||
|
// source: '9',
|
||||||
|
// target: '11',
|
||||||
|
// label: 'focusable',
|
||||||
|
// updatable: true
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// id: 'not-focusable',
|
||||||
|
//
|
||||||
|
// source: '5',
|
||||||
|
// target: '7',
|
||||||
|
// label: 'not-focusable',
|
||||||
|
// focusable: false
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
} satisfies FlowConfig;
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
import Flow from './Flow';
|
||||||
|
|
||||||
|
const flowConfigs = import.meta.glob<FlowConfig>('./**/*.ts', { eager: true, import: 'default' });
|
||||||
|
|
||||||
|
export default () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const path = `.${location.pathname.replace('/tests/generic', '')}.ts`;
|
||||||
|
const flowConfig = flowConfigs[path];
|
||||||
|
|
||||||
|
if (!flowConfig) {
|
||||||
|
return `404: This route doesn't exists.`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <Flow flowConfig={flowConfig} />;
|
||||||
|
};
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
export default () => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="container"
|
||||||
|
style={{
|
||||||
|
width: '100px',
|
||||||
|
height: '50px',
|
||||||
|
background: 'red',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="drag-handle custom-drag-handle"
|
||||||
|
style={{
|
||||||
|
display: 'inline-block',
|
||||||
|
width: '25px',
|
||||||
|
height: '25px',
|
||||||
|
backgroundColor: 'green',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import DragHandleNode from './components/DragHandleNode';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
flowProps: {
|
||||||
|
fitView: true,
|
||||||
|
nodeTypes: {
|
||||||
|
DragHandleNode,
|
||||||
|
},
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'Node-1',
|
||||||
|
data: { label: 'Node-1' },
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
type: 'input',
|
||||||
|
className: 'playwright-test-class-123',
|
||||||
|
style: { backgroundColor: 'red' },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Node-2',
|
||||||
|
type: 'output',
|
||||||
|
data: { label: 'Node-2' },
|
||||||
|
position: { x: -100, y: 100 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Node-3',
|
||||||
|
data: { label: 'Node-3' },
|
||||||
|
position: { x: 100, y: 100 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Node-4',
|
||||||
|
data: { label: 'Node-4' },
|
||||||
|
position: { x: 0, y: 200 },
|
||||||
|
type: 'output',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'drag-handle',
|
||||||
|
data: { label: 'Drag Handle' },
|
||||||
|
position: { x: 200, y: 0 },
|
||||||
|
type: 'DragHandleNode',
|
||||||
|
dragHandle: '.custom-drag-handle',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'notConnectable',
|
||||||
|
type: 'output',
|
||||||
|
data: { label: 'notConnectable' },
|
||||||
|
position: { x: 0, y: 300 },
|
||||||
|
connectable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'notDraggable',
|
||||||
|
data: { label: 'notDraggable' },
|
||||||
|
position: { x: 0, y: 400 },
|
||||||
|
draggable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'notSelectable',
|
||||||
|
data: { label: 'notSelectable' },
|
||||||
|
position: { x: 0, y: 500 },
|
||||||
|
selectable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'notDeletable',
|
||||||
|
data: { label: 'notDeletable' },
|
||||||
|
position: { x: 0, y: 600 },
|
||||||
|
deletable: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hidden',
|
||||||
|
data: { label: 'hidden' },
|
||||||
|
position: { x: 0, y: 700 },
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: '1-2',
|
||||||
|
type: 'default',
|
||||||
|
source: 'Node-1',
|
||||||
|
target: 'Node-2',
|
||||||
|
label: 'edge',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '1-3',
|
||||||
|
type: 'default',
|
||||||
|
source: 'Node-1',
|
||||||
|
target: 'Node-3',
|
||||||
|
label: 'edge',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
} satisfies FlowConfig;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
export default {
|
||||||
|
flowProps: {
|
||||||
|
minZoom: 0.25,
|
||||||
|
maxZoom: 4,
|
||||||
|
fitView: true,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
data: { label: '1' },
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
data: { label: '2' },
|
||||||
|
position: { x: -100, y: 100 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
data: { label: '3' },
|
||||||
|
position: { x: 100, y: 100 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'first-edge',
|
||||||
|
source: '1',
|
||||||
|
target: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'second-edge',
|
||||||
|
source: '1',
|
||||||
|
target: '3',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
} satisfies FlowConfig;
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
export default {
|
||||||
|
flowProps: {
|
||||||
|
panOnScroll: true,
|
||||||
|
defaultViewport: { x: 1.23, y: 9.87, zoom: 1.234 },
|
||||||
|
autoPanOnConnect: false,
|
||||||
|
autoPanOnNodeDrag: false,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
data: { label: '1' },
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
type: 'input',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
data: { label: '2' },
|
||||||
|
position: { x: -100, y: 100 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
data: { label: '3' },
|
||||||
|
position: { x: 100, y: 100 },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'first-edge',
|
||||||
|
source: '1',
|
||||||
|
target: '2',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'second-edge',
|
||||||
|
source: '1',
|
||||||
|
target: '3',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
} satisfies FlowConfig;
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
import { StrictMode } from 'react';
|
import { StrictMode } from 'react';
|
||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
import App from './App';
|
import App from './App';
|
||||||
|
|
||||||
import './index.css';
|
|
||||||
import '@xyflow/react/dist/style.css';
|
import '@xyflow/react/dist/style.css';
|
||||||
|
import './index.css';
|
||||||
|
|
||||||
createRoot(document.getElementById('root') as HTMLElement).render(
|
createRoot(document.getElementById('root') as HTMLElement).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "svelte",
|
"name": "svelte-examples",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -12,20 +12,20 @@
|
|||||||
"format": "prettier --plugin-search-dir . --write ."
|
"format": "prettier --plugin-search-dir . --write ."
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/adapter-auto": "^2.0.0",
|
"@sveltejs/adapter-auto": "^2.1.1",
|
||||||
"@sveltejs/kit": "^1.20.4",
|
"@sveltejs/kit": "^1.27.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||||
"@typescript-eslint/parser": "^5.45.0",
|
"@typescript-eslint/parser": "^6.10.0",
|
||||||
"eslint": "^8.28.0",
|
"eslint": "^8.53.0",
|
||||||
"eslint-config-prettier": "^8.5.0",
|
"eslint-config-prettier": "^9.0.0",
|
||||||
"eslint-plugin-svelte": "^2.30.0",
|
"eslint-plugin-svelte": "^2.35.0",
|
||||||
"prettier": "^2.8.0",
|
"prettier": "^3.0.3",
|
||||||
"prettier-plugin-svelte": "^2.10.1",
|
"prettier-plugin-svelte": "^3.0.3",
|
||||||
"svelte": "^4.0.5",
|
"svelte": "^4.2.2",
|
||||||
"svelte-check": "^3.4.3",
|
"svelte-check": "^3.5.2",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.6.2",
|
||||||
"typescript": "^5.0.0",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^4.4.2"
|
"vite": "^4.5.0"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
Vendored
+19
@@ -1,4 +1,15 @@
|
|||||||
// See https://kit.svelte.dev/docs/types#app
|
// See https://kit.svelte.dev/docs/types#app
|
||||||
|
|
||||||
|
import type {
|
||||||
|
BackgroundProps,
|
||||||
|
Edge,
|
||||||
|
MiniMap,
|
||||||
|
MiniMapProps,
|
||||||
|
Node,
|
||||||
|
PanelProps,
|
||||||
|
SvelteFlowProps
|
||||||
|
} from '@xyflow/svelte';
|
||||||
|
|
||||||
// for information about these interfaces
|
// for information about these interfaces
|
||||||
declare global {
|
declare global {
|
||||||
namespace App {
|
namespace App {
|
||||||
@@ -7,6 +18,14 @@ declare global {
|
|||||||
// interface PageData {}
|
// interface PageData {}
|
||||||
// interface Platform {}
|
// interface Platform {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FlowConfig {
|
||||||
|
flowProps: Omit<SvelteFlowProps, 'nodes' | 'edges'> & { nodes: Node[]; edges: Edge[] };
|
||||||
|
panelProps: PanelProps;
|
||||||
|
backgroundProps: BackgroundProps;
|
||||||
|
controlsProps: ControlsProps;
|
||||||
|
minimapProps: MiniMapProps;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
body,
|
body,
|
||||||
|
|||||||
@@ -31,7 +31,7 @@
|
|||||||
<div class="logo">Svelte Flow</div>
|
<div class="logo">Svelte Flow</div>
|
||||||
<select on:change={onChange} value={$page.route.id}>
|
<select on:change={onChange} value={$page.route.id}>
|
||||||
{#each routes as route}
|
{#each routes as route}
|
||||||
<option value={`/${route}`}>{route}</option>
|
<option value={`/examples/${route}`}>{route}</option>
|
||||||
{/each}
|
{/each}
|
||||||
</select>
|
</select>
|
||||||
</header>
|
</header>
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
import { MarkerType } from '@xyflow/svelte';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
flowProps: {
|
||||||
|
fitView: true,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
data: { label: '1' },
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
type: 'input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
data: { label: '2' },
|
||||||
|
position: { x: -100, y: 100 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
data: { label: '3' },
|
||||||
|
position: { x: 100, y: 100 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
data: { label: '4' },
|
||||||
|
position: { x: -100, y: 200 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
data: { label: '5' },
|
||||||
|
position: { x: 100, y: 200 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
data: { label: '6' },
|
||||||
|
position: { x: -100, y: 300 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
data: { label: '7' },
|
||||||
|
position: { x: 100, y: 300 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
data: { label: '8' },
|
||||||
|
position: { x: -100, y: 400 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9',
|
||||||
|
data: { label: '9' },
|
||||||
|
position: { x: 100, y: 400 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '10',
|
||||||
|
data: { label: '10' },
|
||||||
|
position: { x: -100, y: 500 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '11',
|
||||||
|
data: { label: '11' },
|
||||||
|
position: { x: 100, y: 500 }
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// id: '12',
|
||||||
|
// data: { label: '12' },
|
||||||
|
// position: { x: -100, y: 600 }
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// id: '13',
|
||||||
|
// data: { label: '13' },
|
||||||
|
// position: { x: 100, y: 600 }
|
||||||
|
// }
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'edge-with-class',
|
||||||
|
source: '1',
|
||||||
|
target: '2',
|
||||||
|
class: 'edge-class-test'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'edge-with-style',
|
||||||
|
source: '1',
|
||||||
|
target: '3',
|
||||||
|
style: 'stroke: red;'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hidden-edge',
|
||||||
|
source: '2',
|
||||||
|
target: '4',
|
||||||
|
label: 'hidden',
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'animated-edge',
|
||||||
|
source: '3',
|
||||||
|
target: '5',
|
||||||
|
label: 'animated',
|
||||||
|
animated: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'not-selectable-edge',
|
||||||
|
source: '4',
|
||||||
|
target: '6',
|
||||||
|
label: 'not-selectable',
|
||||||
|
selectable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'not-deletable',
|
||||||
|
source: '5',
|
||||||
|
target: '7',
|
||||||
|
label: 'not-deletable',
|
||||||
|
deletable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'z-index',
|
||||||
|
source: '6',
|
||||||
|
target: '8',
|
||||||
|
label: 'z-index',
|
||||||
|
zIndex: 3141592
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'aria-label',
|
||||||
|
source: '7',
|
||||||
|
target: '9',
|
||||||
|
label: 'aria-label',
|
||||||
|
ariaLabel: 'aria-label-test'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'interaction-width',
|
||||||
|
source: '8',
|
||||||
|
target: '10',
|
||||||
|
label: 'interaction-width',
|
||||||
|
interactionWidth: 42
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'markers',
|
||||||
|
source: '9',
|
||||||
|
target: '11',
|
||||||
|
label: 'markers',
|
||||||
|
markerEnd: { type: MarkerType.Arrow },
|
||||||
|
markerStart: { type: MarkerType.ArrowClosed }
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// id: 'updatable',
|
||||||
|
// source: '9',
|
||||||
|
// target: '11',
|
||||||
|
// label: 'focusable',
|
||||||
|
// updatable: true
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// id: 'not-focusable',
|
||||||
|
//
|
||||||
|
// source: '5',
|
||||||
|
// target: '7',
|
||||||
|
// label: 'not-focusable',
|
||||||
|
// focusable: false
|
||||||
|
// },
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} satisfies FlowConfig;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { NodeProps } from '@xyflow/svelte';
|
||||||
|
|
||||||
|
type $$Props = NodeProps;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
<div class="drag-handle custom-drag-handle" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.container {
|
||||||
|
width: 100px;
|
||||||
|
height: 50px;
|
||||||
|
background: red;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.drag-handle {
|
||||||
|
display: inline-block;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
background-color: green;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
import DragHandleNode from './components/DragHandleNode.svelte';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
flowProps: {
|
||||||
|
fitView: true,
|
||||||
|
nodeTypes: {
|
||||||
|
DragHandleNode
|
||||||
|
},
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: 'Node-1',
|
||||||
|
data: { label: 'Node-1' },
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
type: 'input',
|
||||||
|
class: 'playwright-test-class-123',
|
||||||
|
style: 'background-color: red;'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Node-2',
|
||||||
|
type: 'output',
|
||||||
|
data: { label: 'Node-2' },
|
||||||
|
position: { x: -100, y: 100 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Node-3',
|
||||||
|
data: { label: 'Node-3' },
|
||||||
|
position: { x: 100, y: 100 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'Node-4',
|
||||||
|
data: { label: 'Node-4' },
|
||||||
|
position: { x: 0, y: 200 },
|
||||||
|
type: 'output'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'drag-handle',
|
||||||
|
data: { label: 'Drag Handle' },
|
||||||
|
position: { x: 200, y: 0 },
|
||||||
|
type: 'DragHandleNode',
|
||||||
|
dragHandle: '.custom-drag-handle'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'notConnectable',
|
||||||
|
type: 'output',
|
||||||
|
data: { label: 'notConnectable' },
|
||||||
|
position: { x: 0, y: 300 },
|
||||||
|
connectable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'notDraggable',
|
||||||
|
data: { label: 'notDraggable' },
|
||||||
|
position: { x: 0, y: 400 },
|
||||||
|
draggable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'notSelectable',
|
||||||
|
data: { label: 'notSelectable' },
|
||||||
|
position: { x: 0, y: 500 },
|
||||||
|
selectable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'notDeletable',
|
||||||
|
data: { label: 'notDeletable' },
|
||||||
|
position: { x: 0, y: 600 },
|
||||||
|
deletable: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hidden',
|
||||||
|
data: { label: 'hidden' },
|
||||||
|
position: { x: 0, y: 700 },
|
||||||
|
hidden: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: '1-2',
|
||||||
|
type: 'default',
|
||||||
|
source: 'Node-1',
|
||||||
|
target: 'Node-2',
|
||||||
|
label: 'edge'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '1-3',
|
||||||
|
type: 'default',
|
||||||
|
source: 'Node-1',
|
||||||
|
target: 'Node-3',
|
||||||
|
label: 'edge'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} satisfies FlowConfig;
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
export default {
|
||||||
|
flowProps: {
|
||||||
|
zoomOnScroll: false,
|
||||||
|
// zoomActivationKey: 'Space',
|
||||||
|
// panOnDrag: false,
|
||||||
|
// panOnScroll: false,
|
||||||
|
panActivationKey: 'Space',
|
||||||
|
fitView: true,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
data: { label: '1' },
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
type: 'input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
data: { label: '2' },
|
||||||
|
position: { x: -100, y: 100 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
data: { label: '3' },
|
||||||
|
position: { x: 100, y: 100 }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'first-edge',
|
||||||
|
source: '1',
|
||||||
|
target: '2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'second-edge',
|
||||||
|
source: '1',
|
||||||
|
target: '3'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} satisfies FlowConfig;
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
export default {
|
||||||
|
flowProps: {
|
||||||
|
minZoom: 0.25,
|
||||||
|
maxZoom: 4,
|
||||||
|
fitView: true,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
data: { label: '1' },
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
type: 'input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
data: { label: '2' },
|
||||||
|
position: { x: -100, y: 100 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
data: { label: '3' },
|
||||||
|
position: { x: 100, y: 100 }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'first-edge',
|
||||||
|
source: '1',
|
||||||
|
target: '2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'second-edge',
|
||||||
|
source: '1',
|
||||||
|
target: '3'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} satisfies FlowConfig;
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
export default {
|
||||||
|
flowProps: {
|
||||||
|
panOnScroll: true,
|
||||||
|
initialViewport: { x: 1.23, y: 9.87, zoom: 1.234 },
|
||||||
|
autoPanOnConnect: false,
|
||||||
|
autoPanOnNodeDrag: false,
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
data: { label: '1' },
|
||||||
|
position: { x: 0, y: 0 },
|
||||||
|
type: 'input'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
data: { label: '2' },
|
||||||
|
position: { x: -100, y: 100 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
data: { label: '3' },
|
||||||
|
position: { x: 100, y: 100 }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
edges: [
|
||||||
|
{
|
||||||
|
id: 'first-edge',
|
||||||
|
source: '1',
|
||||||
|
target: '2'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'second-edge',
|
||||||
|
source: '1',
|
||||||
|
target: '3'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
} satisfies FlowConfig;
|
||||||
@@ -2,7 +2,7 @@ import { redirect } from '@sveltejs/kit';
|
|||||||
|
|
||||||
/** @type {import('./$types').LayoutServerLoad} */
|
/** @type {import('./$types').LayoutServerLoad} */
|
||||||
export function load({ route }) {
|
export function load({ route }) {
|
||||||
if (route.id === '/') {
|
if (route.id === '/') {
|
||||||
throw redirect(307, '/overview');
|
throw redirect(307, '/examples/overview');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Header } from '../components/Header';
|
import { Header } from '$components/Header';
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="app">
|
<div class="app">
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<div>this redirects to /overview</div>
|
||||||
+4
-4
@@ -69,10 +69,10 @@
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const type = event.dataTransfer.getData('application/svelteflow');
|
const type = event.dataTransfer.getData('application/svelteflow') || 'default';
|
||||||
const position = svelteFlow.project({
|
const position = svelteFlow.screenToFlowPosition({
|
||||||
x: event.clientX,
|
x: event.clientX,
|
||||||
y: event.clientY - 40
|
y: event.clientY
|
||||||
});
|
});
|
||||||
const newNode: Node = {
|
const newNode: Node = {
|
||||||
id: `${Math.random()}`,
|
id: `${Math.random()}`,
|
||||||
@@ -81,7 +81,7 @@
|
|||||||
data: { label: `${type} node` }
|
data: { label: `${type} node` }
|
||||||
};
|
};
|
||||||
|
|
||||||
svelteFlow.nodes.update((nds) => nds.concat(newNode));
|
nodes.update((nds) => nds.concat(newNode));
|
||||||
};
|
};
|
||||||
|
|
||||||
$: {
|
$: {
|
||||||
+1
@@ -45,6 +45,7 @@
|
|||||||
{edges}
|
{edges}
|
||||||
fitView
|
fitView
|
||||||
selectionMode={SelectionMode.Partial}
|
selectionMode={SelectionMode.Partial}
|
||||||
|
selectionOnDrag
|
||||||
panOnScroll
|
panOnScroll
|
||||||
{panOnDrag}
|
{panOnDrag}
|
||||||
{onMoveStart}
|
{onMoveStart}
|
||||||
+13
@@ -83,6 +83,12 @@
|
|||||||
type: 'custom',
|
type: 'custom',
|
||||||
data: { label: 'Custom Node' },
|
data: { label: 'Custom Node' },
|
||||||
position: { x: 150, y: 300 }
|
position: { x: 150, y: 300 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'hideunhide',
|
||||||
|
data: { label: 'HIDE ME' },
|
||||||
|
position: { x: 300, y: 75 },
|
||||||
|
hidden: true
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -179,6 +185,13 @@
|
|||||||
<Panel position="top-right">
|
<Panel position="top-right">
|
||||||
<button on:click={updateNode}>update node pos</button>
|
<button on:click={updateNode}>update node pos</button>
|
||||||
<button on:click={updateEdge}>update edge type</button>
|
<button on:click={updateEdge}>update edge type</button>
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
console.log($nodes, $nodes.length);
|
||||||
|
$nodes[$nodes.length - 1].hidden = !$nodes[$nodes.length - 1].hidden;
|
||||||
|
$nodes = $nodes;
|
||||||
|
}}>hide/unhide</button
|
||||||
|
>
|
||||||
</Panel>
|
</Panel>
|
||||||
</SvelteFlow>
|
</SvelteFlow>
|
||||||
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { SvelteFlowProvider } from '@xyflow/svelte';
|
||||||
|
|
||||||
|
import Flow from './Flow.svelte';
|
||||||
|
|
||||||
|
export let data: { flowConfig: FlowConfig };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SvelteFlowProvider>
|
||||||
|
<Flow flowConfig={data.flowConfig} />
|
||||||
|
</SvelteFlowProvider>
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { error } from '@sveltejs/kit';
|
||||||
|
|
||||||
|
const flowConfigs = import.meta.glob<FlowConfig>('/src/generic-tests/**/*.ts', {
|
||||||
|
eager: true,
|
||||||
|
import: 'default'
|
||||||
|
});
|
||||||
|
|
||||||
|
/** @type {import('./$types').PageLoad} */
|
||||||
|
export function load({ params }) {
|
||||||
|
const flowConfig = flowConfigs[`/src/generic-tests/${params.topic}/${params.example}.ts`];
|
||||||
|
|
||||||
|
if (!flowConfig) {
|
||||||
|
throw error(404, 'Not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
flowConfig
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { writable } from 'svelte/store';
|
||||||
|
import { Background, Controls, MiniMap, Panel, SvelteFlow } from '@xyflow/svelte';
|
||||||
|
|
||||||
|
import '@xyflow/svelte/dist/style.css';
|
||||||
|
|
||||||
|
export let flowConfig: FlowConfig;
|
||||||
|
|
||||||
|
// Create writables here so it is easier to create test cases
|
||||||
|
const nodes = writable(flowConfig.flowProps.nodes);
|
||||||
|
const edges = writable(flowConfig.flowProps.edges);
|
||||||
|
|
||||||
|
const flowProps = { ...flowConfig.flowProps, nodes, edges };
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<SvelteFlow {...flowProps}>
|
||||||
|
{#if flowConfig.panelProps}
|
||||||
|
<Panel {...flowConfig.panelProps} />
|
||||||
|
{/if}
|
||||||
|
{#if flowConfig.minimapProps}
|
||||||
|
<MiniMap {...flowConfig.minimapProps} />
|
||||||
|
{/if}
|
||||||
|
{#if flowConfig.controlsProps}
|
||||||
|
<Controls {...flowConfig.controlsProps} />
|
||||||
|
{/if}
|
||||||
|
{#if flowConfig.backgroundProps}
|
||||||
|
<Background {...flowConfig.backgroundProps} />
|
||||||
|
{/if}
|
||||||
|
</SvelteFlow>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 111 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -11,7 +11,10 @@ const config = {
|
|||||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||||
adapter: adapter()
|
adapter: adapter(),
|
||||||
|
alias: {
|
||||||
|
$components: './src/components'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,10 @@
|
|||||||
"dev": "turbo run dev --parallel --concurrency 12",
|
"dev": "turbo run dev --parallel --concurrency 12",
|
||||||
"dev:svelte": "turbo run dev --filter=svelte --filter=system",
|
"dev:svelte": "turbo run dev --filter=svelte --filter=system",
|
||||||
"dev:react": "turbo run dev --filter=react",
|
"dev:react": "turbo run dev --filter=react",
|
||||||
|
"test:svelte": "pnpm --filter=playwright run test:svelte",
|
||||||
|
"test:svelte:ui": "pnpm --filter=playwright run test:svelte:ui",
|
||||||
|
"test:react": "pnpm --filter=playwright run test:react",
|
||||||
|
"test:react:ui": "pnpm --filter=playwright run test:react:ui",
|
||||||
"build": "turbo run build",
|
"build": "turbo run build",
|
||||||
"test": "turbo run test",
|
"test": "turbo run test",
|
||||||
"lint": "turbo run lint",
|
"lint": "turbo run lint",
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { memo, useEffect, useRef, type MouseEvent, useCallback } from 'react';
|
import { memo, useEffect, useRef, type MouseEvent, useCallback } from 'react';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import { getRectOfNodes, getBoundsOfRects, XYMinimap, type Rect, type XYMinimapInstance } from '@xyflow/system';
|
import { getNodesBounds, getBoundsOfRects, XYMinimap, type Rect, type XYMinimapInstance } from '@xyflow/system';
|
||||||
|
|
||||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||||
import Panel from '../../components/Panel';
|
import Panel from '../../components/Panel';
|
||||||
@@ -25,7 +25,7 @@ const selector = (s: ReactFlowState) => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
viewBB,
|
viewBB,
|
||||||
boundingRect: s.nodes.length > 0 ? getBoundsOfRects(getRectOfNodes(s.nodes, s.nodeOrigin), viewBB) : viewBB,
|
boundingRect: s.nodes.length > 0 ? getBoundsOfRects(getNodesBounds(s.nodes, s.nodeOrigin), viewBB) : viewBB,
|
||||||
rfId: s.rfId,
|
rfId: s.rfId,
|
||||||
nodeOrigin: s.nodeOrigin,
|
nodeOrigin: s.nodeOrigin,
|
||||||
panZoom: s.panZoom,
|
panZoom: s.panZoom,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, CSSProperties } from 'react';
|
import { useCallback, CSSProperties } from 'react';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import { getRectOfNodes, Transform, Rect, Position, internalsSymbol } from '@xyflow/system';
|
import { getNodesBounds, Transform, Rect, Position, internalsSymbol } from '@xyflow/system';
|
||||||
|
|
||||||
import { Node, ReactFlowState } from '../../types';
|
import { Node, ReactFlowState } from '../../types';
|
||||||
import { useStore } from '../../hooks/useStore';
|
import { useStore } from '../../hooks/useStore';
|
||||||
@@ -105,7 +105,7 @@ function NodeToolbar({
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const nodeRect: Rect = getRectOfNodes(nodes, nodeOrigin);
|
const nodeRect: Rect = getNodesBounds(nodes, nodeOrigin);
|
||||||
const zIndex: number = Math.max(...nodes.map((node) => (node[internalsSymbol]?.z || 1) + 1));
|
const zIndex: number = Math.max(...nodes.map((node) => (node[internalsSymbol]?.z || 1) + 1));
|
||||||
|
|
||||||
const wrapperStyle: CSSProperties = {
|
const wrapperStyle: CSSProperties = {
|
||||||
|
|||||||
@@ -213,6 +213,7 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
|
|||||||
onKeyDown={isFocusable ? onKeyDown : undefined}
|
onKeyDown={isFocusable ? onKeyDown : undefined}
|
||||||
tabIndex={isFocusable ? 0 : undefined}
|
tabIndex={isFocusable ? 0 : undefined}
|
||||||
role={isFocusable ? 'button' : 'img'}
|
role={isFocusable ? 'button' : 'img'}
|
||||||
|
data-id={id}
|
||||||
data-testid={`rf__edge-${id}`}
|
data-testid={`rf__edge-${id}`}
|
||||||
aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`}
|
aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`}
|
||||||
aria-describedby={isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined}
|
aria-describedby={isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import { memo, useRef, useEffect, type MouseEvent, type KeyboardEvent } from 'react';
|
import { memo, useRef, useEffect, type MouseEvent, type KeyboardEvent } from 'react';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import { getRectOfNodes } from '@xyflow/system';
|
import { getNodesBounds } from '@xyflow/system';
|
||||||
|
|
||||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||||
import useDrag from '../../hooks/useDrag';
|
import useDrag from '../../hooks/useDrag';
|
||||||
@@ -22,7 +22,7 @@ export type NodesSelectionProps = {
|
|||||||
|
|
||||||
const selector = (s: ReactFlowState) => {
|
const selector = (s: ReactFlowState) => {
|
||||||
const selectedNodes = s.nodes.filter((n) => n.selected);
|
const selectedNodes = s.nodes.filter((n) => n.selected);
|
||||||
const { width, height, x, y } = getRectOfNodes(selectedNodes, s.nodeOrigin);
|
const { width, height, x, y } = getNodesBounds(selectedNodes, s.nodeOrigin);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
width,
|
width,
|
||||||
|
|||||||
@@ -32,8 +32,9 @@ const SelectionListener = memo(({ onSelectionChange }: SelectionListenerProps) =
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const params = { nodes: selectedNodes, edges: selectedEdges };
|
const params = { nodes: selectedNodes, edges: selectedEdges };
|
||||||
|
|
||||||
onSelectionChange?.(params);
|
onSelectionChange?.(params);
|
||||||
store.getState().onSelectionChange?.(params);
|
store.getState().onSelectionChangeHandlers.forEach((fn) => fn(params));
|
||||||
}, [selectedNodes, selectedEdges, onSelectionChange]);
|
}, [selectedNodes, selectedEdges, onSelectionChange]);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@@ -41,12 +42,12 @@ const SelectionListener = memo(({ onSelectionChange }: SelectionListenerProps) =
|
|||||||
|
|
||||||
SelectionListener.displayName = 'SelectionListener';
|
SelectionListener.displayName = 'SelectionListener';
|
||||||
|
|
||||||
const changeSelector = (s: ReactFlowState) => !!s.onSelectionChange;
|
const changeSelector = (s: ReactFlowState) => !!s.onSelectionChangeHandlers;
|
||||||
|
|
||||||
function Wrapper({ onSelectionChange }: SelectionListenerProps) {
|
function Wrapper({ onSelectionChange }: SelectionListenerProps) {
|
||||||
const storeHasSelectionChange = useStore(changeSelector);
|
const storeHasSelectionChangeHandlers = useStore(changeSelector);
|
||||||
|
|
||||||
if (onSelectionChange || storeHasSelectionChange) {
|
if (onSelectionChange || storeHasSelectionChangeHandlers) {
|
||||||
return <SelectionListener onSelectionChange={onSelectionChange} />;
|
return <SelectionListener onSelectionChange={onSelectionChange} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ const FlowRenderer = ({
|
|||||||
elementsSelectable,
|
elementsSelectable,
|
||||||
zoomOnScroll,
|
zoomOnScroll,
|
||||||
zoomOnPinch,
|
zoomOnPinch,
|
||||||
panOnScroll,
|
panOnScroll: _panOnScroll,
|
||||||
panOnScrollSpeed,
|
panOnScrollSpeed,
|
||||||
panOnScrollMode,
|
panOnScrollMode,
|
||||||
zoomOnDoubleClick,
|
zoomOnDoubleClick,
|
||||||
@@ -72,6 +72,7 @@ const FlowRenderer = ({
|
|||||||
const panActivationKeyPressed = useKeyPress(panActivationKeyCode);
|
const panActivationKeyPressed = useKeyPress(panActivationKeyCode);
|
||||||
|
|
||||||
const panOnDrag = panActivationKeyPressed || _panOnDrag;
|
const panOnDrag = panActivationKeyPressed || _panOnDrag;
|
||||||
|
const panOnScroll = panActivationKeyPressed || _panOnScroll;
|
||||||
const isSelecting = selectionKeyPressed || (selectionOnDrag && panOnDrag !== true);
|
const isSelecting = selectionKeyPressed || (selectionOnDrag && panOnDrag !== true);
|
||||||
|
|
||||||
useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode });
|
useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode });
|
||||||
|
|||||||
@@ -4,14 +4,20 @@ import { useStoreApi } from './useStore';
|
|||||||
import type { OnSelectionChangeFunc } from '../types';
|
import type { OnSelectionChangeFunc } from '../types';
|
||||||
|
|
||||||
export type UseOnSelectionChangeOptions = {
|
export type UseOnSelectionChangeOptions = {
|
||||||
onChange?: OnSelectionChangeFunc;
|
onChange: OnSelectionChangeFunc;
|
||||||
};
|
};
|
||||||
|
|
||||||
function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
|
function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
store.setState({ onSelectionChange: onChange });
|
const nextOnSelectionChangeHandlers = [...store.getState().onSelectionChangeHandlers, onChange];
|
||||||
|
store.setState({ onSelectionChangeHandlers: nextOnSelectionChangeHandlers });
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
const nextHandlers = store.getState().onSelectionChangeHandlers.filter((fn) => fn !== onChange);
|
||||||
|
store.setState({ onSelectionChangeHandlers: nextHandlers });
|
||||||
|
};
|
||||||
}, [onChange]);
|
}, [onChange]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import {
|
import {
|
||||||
pointToRendererPoint,
|
pointToRendererPoint,
|
||||||
getTransformForBounds,
|
getViewportForBounds,
|
||||||
fitView,
|
fitView,
|
||||||
type XYPosition,
|
type XYPosition,
|
||||||
rendererPointToPoint,
|
rendererPointToPoint,
|
||||||
@@ -76,50 +76,40 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
|||||||
},
|
},
|
||||||
fitBounds: (bounds, options) => {
|
fitBounds: (bounds, options) => {
|
||||||
const { width, height, minZoom, maxZoom, panZoom } = store.getState();
|
const { width, height, minZoom, maxZoom, panZoom } = store.getState();
|
||||||
const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom, maxZoom, options?.padding ?? 0.1);
|
const viewport = getViewportForBounds(bounds, width, height, minZoom, maxZoom, options?.padding ?? 0.1);
|
||||||
|
|
||||||
panZoom?.setViewport(
|
panZoom?.setViewport(viewport, { duration: options?.duration });
|
||||||
{
|
|
||||||
x,
|
|
||||||
y,
|
|
||||||
zoom,
|
|
||||||
},
|
|
||||||
{ duration: options?.duration }
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
project: (position: XYPosition) => {
|
screenToFlowPosition: (position: XYPosition) => {
|
||||||
const { transform, snapToGrid, snapGrid } = store.getState();
|
|
||||||
return pointToRendererPoint(position, transform, snapToGrid, snapGrid);
|
|
||||||
},
|
|
||||||
screenToFlowCoordinate: (position: XYPosition) => {
|
|
||||||
const { transform, snapToGrid, snapGrid, domNode } = store.getState();
|
const { transform, snapToGrid, snapGrid, domNode } = store.getState();
|
||||||
if (domNode) {
|
|
||||||
const { x: domX, y: domY } = domNode.getBoundingClientRect();
|
|
||||||
|
|
||||||
const correctedPosition = {
|
if (!domNode) {
|
||||||
x: position.x - domX,
|
return position;
|
||||||
y: position.y - domY,
|
|
||||||
};
|
|
||||||
|
|
||||||
return pointToRendererPoint(correctedPosition, transform, snapToGrid, snapGrid || [1, 1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { x: 0, y: 0 };
|
const { x: domX, y: domY } = domNode.getBoundingClientRect();
|
||||||
|
|
||||||
|
const correctedPosition = {
|
||||||
|
x: position.x - domX,
|
||||||
|
y: position.y - domY,
|
||||||
|
};
|
||||||
|
|
||||||
|
return pointToRendererPoint(correctedPosition, transform, snapToGrid, snapGrid || [1, 1]);
|
||||||
},
|
},
|
||||||
flowToScreenCoordinate: (position: XYPosition) => {
|
flowToScreenPosition: (position: XYPosition) => {
|
||||||
const { transform, domNode } = store.getState();
|
const { transform, domNode } = store.getState();
|
||||||
if (domNode) {
|
|
||||||
const { x: domX, y: domY } = domNode.getBoundingClientRect();
|
|
||||||
|
|
||||||
const rendererPosition = rendererPointToPoint(position, transform);
|
if (!domNode) {
|
||||||
|
return position;
|
||||||
return {
|
|
||||||
x: rendererPosition.x + domX,
|
|
||||||
y: rendererPosition.y + domY,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return { x: 0, y: 0 };
|
const { x: domX, y: domY } = domNode.getBoundingClientRect();
|
||||||
|
const rendererPosition = rendererPointToPoint(position, transform);
|
||||||
|
|
||||||
|
return {
|
||||||
|
x: rendererPosition.x + domX,
|
||||||
|
y: rendererPosition.y + domY,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
viewportInitialized: panZoomInitialized,
|
viewportInitialized: panZoomInitialized,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export { default as SimpleBezierEdge, getSimpleBezierPath } from './components/E
|
|||||||
export { default as SmoothStepEdge } from './components/Edges/SmoothStepEdge';
|
export { default as SmoothStepEdge } from './components/Edges/SmoothStepEdge';
|
||||||
export { default as BaseEdge } from './components/Edges/BaseEdge';
|
export { default as BaseEdge } from './components/Edges/BaseEdge';
|
||||||
export { default as ReactFlowProvider } from './components/ReactFlowProvider';
|
export { default as ReactFlowProvider } from './components/ReactFlowProvider';
|
||||||
export { default as Panel } from './components/Panel';
|
export { default as Panel, type PanelProps } from './components/Panel';
|
||||||
export { default as EdgeLabelRenderer } from './components/EdgeLabelRenderer';
|
export { default as EdgeLabelRenderer } from './components/EdgeLabelRenderer';
|
||||||
|
|
||||||
export { default as useReactFlow } from './hooks/useReactFlow';
|
export { default as useReactFlow } from './hooks/useReactFlow';
|
||||||
@@ -85,6 +85,6 @@ export {
|
|||||||
getSmoothStepPath,
|
getSmoothStepPath,
|
||||||
type GetStraightPathParams,
|
type GetStraightPathParams,
|
||||||
getStraightPath,
|
getStraightPath,
|
||||||
getTransformForBounds,
|
getViewportForBounds,
|
||||||
getRectOfNodes,
|
getNodesBounds,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import {
|
|||||||
infiniteExtent,
|
infiniteExtent,
|
||||||
ConnectionMode,
|
ConnectionMode,
|
||||||
updateNodes,
|
updateNodes,
|
||||||
getRectOfNodes,
|
getNodesBounds,
|
||||||
getTransformForBounds,
|
getViewportForBounds,
|
||||||
Transform,
|
Transform,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
@@ -32,8 +32,9 @@ const getInitialState = ({
|
|||||||
width: node.size?.width,
|
width: node.size?.width,
|
||||||
height: node.size?.height,
|
height: node.size?.height,
|
||||||
}));
|
}));
|
||||||
const bounds = getRectOfNodes(nodesWithDimensions, [0, 0]);
|
const bounds = getNodesBounds(nodesWithDimensions, [0, 0]);
|
||||||
transform = getTransformForBounds(bounds, width, height, 0.5, 2, 0.1);
|
const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
||||||
|
transform = [x, y, zoom];
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -92,6 +93,7 @@ const getInitialState = ({
|
|||||||
connectionRadius: 20,
|
connectionRadius: 20,
|
||||||
onError: () => null,
|
onError: () => null,
|
||||||
isValidConnection: undefined,
|
isValidConnection: undefined,
|
||||||
|
onSelectionChangeHandlers: [],
|
||||||
|
|
||||||
lib: 'react',
|
lib: 'react',
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import {
|
|||||||
GetViewport,
|
GetViewport,
|
||||||
SetCenter,
|
SetCenter,
|
||||||
FitBounds,
|
FitBounds,
|
||||||
Project,
|
XYPosition,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import type { NodeChange, EdgeChange, Node, WrapNodeProps, Edge, EdgeProps, WrapEdgeProps, ReactFlowInstance } from '.';
|
import type { NodeChange, EdgeChange, Node, WrapNodeProps, Edge, EdgeProps, WrapEdgeProps, ReactFlowInstance } from '.';
|
||||||
@@ -54,8 +54,7 @@ export type ViewportHelperFunctions = {
|
|||||||
fitView: FitView;
|
fitView: FitView;
|
||||||
setCenter: SetCenter;
|
setCenter: SetCenter;
|
||||||
fitBounds: FitBounds;
|
fitBounds: FitBounds;
|
||||||
project: Project;
|
screenToFlowPosition: (position: XYPosition) => XYPosition;
|
||||||
screenToFlowCoordinate: Project;
|
flowToScreenPosition: (position: XYPosition) => XYPosition;
|
||||||
flowToScreenCoordinate: Project;
|
|
||||||
viewportInitialized: boolean;
|
viewportInitialized: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export type ReactFlowStore = {
|
|||||||
onViewportChange?: OnViewportChange;
|
onViewportChange?: OnViewportChange;
|
||||||
onViewportChangeEnd?: OnViewportChange;
|
onViewportChangeEnd?: OnViewportChange;
|
||||||
|
|
||||||
onSelectionChange?: OnSelectionChangeFunc;
|
onSelectionChangeHandlers: OnSelectionChangeFunc[];
|
||||||
|
|
||||||
ariaLiveMessage: string;
|
ariaLiveMessage: string;
|
||||||
autoPanOnConnect: boolean;
|
autoPanOnConnect: boolean;
|
||||||
|
|||||||
@@ -1,3 +1,17 @@
|
|||||||
|
## 0.0.27
|
||||||
|
|
||||||
|
- add `selectionOnDrag` prop - can be used to create figma-like controls in combination with `panOnDrag={false}` / `panOnDrag={[1, 2]}` + `panOnScroll={true}`
|
||||||
|
- ⚠️ rename `screenToFlowCoordinate` to `screenToFlowPosition`
|
||||||
|
- ⚠️ rename `flowToScreenCoordinate` to `flowToScreenPosition`
|
||||||
|
- ⚠️ rename `getTransformForBounds` to `getViewportForBounds` (return `{ x: number, y: number, zoom: number }` instead of `[number, number, number]`)
|
||||||
|
- ⚠️ rename `getRectOfNodes` to `getNodesBounds`
|
||||||
|
- simplify handle default styles, so that it's easier to override them
|
||||||
|
- added e2e tests
|
||||||
|
|
||||||
|
## 0.0.26
|
||||||
|
|
||||||
|
- fixes broken version on npm
|
||||||
|
|
||||||
## 0.0.25
|
## 0.0.25
|
||||||
|
|
||||||
- add `toObject` to `useSvelteFlow` hook
|
- add `toObject` to `useSvelteFlow` hook
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@xyflow/svelte",
|
"name": "@xyflow/svelte",
|
||||||
"version": "0.0.26",
|
"version": "0.0.27",
|
||||||
"description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.",
|
"description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"svelte",
|
"svelte",
|
||||||
|
|||||||
@@ -24,7 +24,6 @@
|
|||||||
|
|
||||||
<path
|
<path
|
||||||
d={path}
|
d={path}
|
||||||
{id}
|
|
||||||
class={cc(['svelte-flow__edge-path', className])}
|
class={cc(['svelte-flow__edge-path', className])}
|
||||||
marker-start={markerStart}
|
marker-start={markerStart}
|
||||||
marker-end={markerEnd}
|
marker-end={markerEnd}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getRectOfNodes } from '@xyflow/system';
|
import { getNodesBounds } from '@xyflow/system';
|
||||||
|
|
||||||
import { useStore } from '$lib/store';
|
import { useStore } from '$lib/store';
|
||||||
import { Selection } from '$lib/components/Selection';
|
import { Selection } from '$lib/components/Selection';
|
||||||
@@ -9,13 +9,13 @@
|
|||||||
const { selectionRectMode, nodes } = store;
|
const { selectionRectMode, nodes } = store;
|
||||||
|
|
||||||
$: selectedNodes = $nodes.filter((n) => n.selected);
|
$: selectedNodes = $nodes.filter((n) => n.selected);
|
||||||
$: rect = getRectOfNodes(selectedNodes);
|
$: bounds = getNodesBounds(selectedNodes);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if selectedNodes && $selectionRectMode === 'nodes'}
|
{#if selectedNodes && $selectionRectMode === 'nodes'}
|
||||||
<div
|
<div
|
||||||
class="selection-wrapper nopan"
|
class="selection-wrapper nopan"
|
||||||
style="width: {rect.width}px; height: {rect.height}px; transform: translate({rect.x}px, {rect.y}px)"
|
style="width: {bounds.width}px; height: {bounds.height}px; transform: translate({bounds.x}px, {bounds.y}px)"
|
||||||
use:drag={{ disabled: false, store }}
|
use:drag={{ disabled: false, store }}
|
||||||
>
|
>
|
||||||
<Selection width="100%" height="100%" x={0} y={0} />
|
<Selection width="100%" height="100%" x={0} y={0} />
|
||||||
|
|||||||
@@ -1,13 +1,7 @@
|
|||||||
<svelte:options immutable />
|
<svelte:options immutable />
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {
|
import { createEventDispatcher, setContext, SvelteComponent, type ComponentType } from 'svelte';
|
||||||
createEventDispatcher,
|
|
||||||
onMount,
|
|
||||||
setContext,
|
|
||||||
SvelteComponent,
|
|
||||||
type ComponentType
|
|
||||||
} from 'svelte';
|
|
||||||
import { get, writable } from 'svelte/store';
|
import { get, writable } from 'svelte/store';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import { errorMessages, Position, type NodeProps } from '@xyflow/system';
|
import { errorMessages, Position, type NodeProps } from '@xyflow/system';
|
||||||
@@ -54,6 +48,8 @@
|
|||||||
const nodeType = type || 'default';
|
const nodeType = type || 'default';
|
||||||
|
|
||||||
let nodeRef: HTMLDivElement;
|
let nodeRef: HTMLDivElement;
|
||||||
|
let prevNodeRef: HTMLDivElement;
|
||||||
|
|
||||||
const nodeTypeValid = !!$nodeTypes[nodeType];
|
const nodeTypeValid = !!$nodeTypes[nodeType];
|
||||||
|
|
||||||
if (!nodeTypeValid) {
|
if (!nodeTypeValid) {
|
||||||
@@ -109,13 +105,15 @@
|
|||||||
setContext('svelteflow__node_id', id);
|
setContext('svelteflow__node_id', id);
|
||||||
setContext('svelteflow__node_connectable', connectableStore);
|
setContext('svelteflow__node_connectable', connectableStore);
|
||||||
|
|
||||||
onMount(() => {
|
$: {
|
||||||
resizeObserver?.observe(nodeRef);
|
// hiding the noder removes html element is removed from the dom
|
||||||
|
if (nodeRef) {
|
||||||
return () => {
|
resizeObserver?.observe(nodeRef);
|
||||||
resizeObserver?.unobserve(nodeRef);
|
prevNodeRef = nodeRef;
|
||||||
};
|
} else if (prevNodeRef) {
|
||||||
});
|
resizeObserver?.unobserve(prevNodeRef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onSelectNodeHandler(event: MouseEvent | TouchEvent) {
|
function onSelectNodeHandler(event: MouseEvent | TouchEvent) {
|
||||||
if (selectable && (!get(selectNodesOnDrag) || !draggable || get(nodeDragThreshold) > 0)) {
|
if (selectable && (!get(selectNodesOnDrag) || !draggable || get(nodeDragThreshold) > 0)) {
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
type $$Props = PaneProps;
|
type $$Props = PaneProps;
|
||||||
|
|
||||||
export let panOnDrag: $$Props['panOnDrag'] = undefined;
|
export let panOnDrag: $$Props['panOnDrag'] = undefined;
|
||||||
|
export let selectionOnDrag: $$Props['selectionOnDrag'] = undefined;
|
||||||
|
|
||||||
const dispatch = createEventDispatcher<{
|
const dispatch = createEventDispatcher<{
|
||||||
paneclick: {
|
paneclick: {
|
||||||
@@ -67,9 +68,9 @@
|
|||||||
let containerBounds: DOMRect | null = null;
|
let containerBounds: DOMRect | null = null;
|
||||||
let selectedNodes: Node[] = [];
|
let selectedNodes: Node[] = [];
|
||||||
|
|
||||||
$: isSelecting = $selectionKeyPressed;
|
|
||||||
$: hasActiveSelection = $elementsSelectable && (isSelecting || $selectionRectMode === 'user');
|
|
||||||
$: _panOnDrag = $panActivationKeyPressed || panOnDrag;
|
$: _panOnDrag = $panActivationKeyPressed || panOnDrag;
|
||||||
|
$: isSelecting = $selectionKeyPressed || (selectionOnDrag && _panOnDrag !== true);
|
||||||
|
$: hasActiveSelection = $elementsSelectable && (isSelecting || $selectionRectMode === 'user');
|
||||||
|
|
||||||
function onClick(event: MouseEvent | TouchEvent) {
|
function onClick(event: MouseEvent | TouchEvent) {
|
||||||
dispatch('paneclick', { event });
|
dispatch('paneclick', { event });
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
export type PaneProps = {
|
export type PaneProps = {
|
||||||
panOnDrag?: boolean | number[];
|
panOnDrag?: boolean | number[];
|
||||||
|
selectionOnDrag?: boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,6 +58,7 @@
|
|||||||
export let zoomOnPinch: $$Props['zoomOnPinch'] = true;
|
export let zoomOnPinch: $$Props['zoomOnPinch'] = true;
|
||||||
export let panOnScroll: $$Props['panOnScroll'] = false;
|
export let panOnScroll: $$Props['panOnScroll'] = false;
|
||||||
export let panOnDrag: $$Props['panOnDrag'] = true;
|
export let panOnDrag: $$Props['panOnDrag'] = true;
|
||||||
|
export let selectionOnDrag: $$Props['selectionOnDrag'] = undefined;
|
||||||
export let autoPanOnConnect: $$Props['autoPanOnConnect'] = true;
|
export let autoPanOnConnect: $$Props['autoPanOnConnect'] = true;
|
||||||
export let autoPanOnNodeDrag: $$Props['autoPanOnNodeDrag'] = true;
|
export let autoPanOnNodeDrag: $$Props['autoPanOnNodeDrag'] = true;
|
||||||
export let onError: $$Props['onError'] = undefined;
|
export let onError: $$Props['onError'] = undefined;
|
||||||
@@ -179,7 +180,7 @@
|
|||||||
panOnScroll={panOnScroll === undefined ? false : panOnScroll}
|
panOnScroll={panOnScroll === undefined ? false : panOnScroll}
|
||||||
panOnDrag={panOnDrag === undefined ? true : panOnDrag}
|
panOnDrag={panOnDrag === undefined ? true : panOnDrag}
|
||||||
>
|
>
|
||||||
<Pane on:paneclick panOnDrag={panOnDrag === undefined ? true : panOnDrag}>
|
<Pane on:paneclick panOnDrag={panOnDrag === undefined ? true : panOnDrag} {selectionOnDrag}>
|
||||||
<ViewportComponent>
|
<ViewportComponent>
|
||||||
<EdgeRenderer on:edgeclick on:edgecontextmenu {defaultEdgeOptions} />
|
<EdgeRenderer on:edgeclick on:edgecontextmenu {defaultEdgeOptions} />
|
||||||
<ConnectionLine
|
<ConnectionLine
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user