diff --git a/examples/react/cypress.config.ts b/examples/react/cypress.config.ts index b46eaa76..027bd690 100644 --- a/examples/react/cypress.config.ts +++ b/examples/react/cypress.config.ts @@ -2,7 +2,7 @@ import { defineConfig } from 'cypress'; export default defineConfig({ e2e: { - baseUrl: 'http://localhost:3000', + baseUrl: 'http://localhost:3000/examples', viewportWidth: 1280, viewportHeight: 720, video: false, diff --git a/examples/react/index.html b/examples/react/index.html index f1cba741..828516b9 100644 --- a/examples/react/index.html +++ b/examples/react/index.html @@ -2,7 +2,7 @@ - + React Flow Examples diff --git a/examples/react/package.json b/examples/react/package.json index 73d91382..36dd09a6 100644 --- a/examples/react/package.json +++ b/examples/react/package.json @@ -1,5 +1,5 @@ { - "name": "@xyflow/react-examples", + "name": "react-examples", "private": true, "version": "0.0.0", "type": "module", @@ -15,25 +15,25 @@ }, "dependencies": { "@xyflow/react": "workspace:*", - "classcat": "^5.0.3", + "classcat": "^5.0.4", "dagre": "^0.8.5", "localforage": "^1.10.0", "react": "^18.2.0", "react-dom": "^18.2.0", - "react-router-dom": "^6.3.0", - "zustand": "^4.4.1" + "react-router-dom": "^6.18.0", + "zustand": "^4.4.6" }, "devDependencies": { "@cypress/skip-test": "^2.6.1", - "@types/dagre": "^0.7.48", - "@types/react": "^18.0.17", - "@types/react-dom": "^18.0.6", - "@vitejs/plugin-react": "4.0.4", - "@vitejs/plugin-react-swc": "^3.3.2", + "@types/dagre": "^0.7.52", + "@types/react": "^18.2.36", + "@types/react-dom": "^18.2.14", + "@vitejs/plugin-react": "4.1.1", + "@vitejs/plugin-react-swc": "^3.4.1", "cypress": "12.17.3", - "cypress-real-events": "1.10.0", - "start-server-and-test": "^1.14.0", - "typescript": "5.1.3", - "vite": "4.4.9" + "cypress-real-events": "1.11.0", + "start-server-and-test": "^2.0.2", + "typescript": "5.2.2", + "vite": "4.5.0" } } diff --git a/examples/react/public/favicon.ico b/examples/react/public/favicon.ico new file mode 100644 index 00000000..9ef0fbfd Binary files /dev/null and b/examples/react/public/favicon.ico differ diff --git a/examples/react/public/vite.svg b/examples/react/public/vite.svg deleted file mode 100644 index e7b8dfb1..00000000 --- a/examples/react/public/vite.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/examples/react/src/App/header.tsx b/examples/react/src/App/header.tsx new file mode 100644 index 00000000..37e57910 --- /dev/null +++ b/examples/react/src/App/header.tsx @@ -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 ( + <> +
+ + React Flow Dev + + +
+ + + ); +} diff --git a/examples/react/src/App/index.tsx b/examples/react/src/App/index.tsx index b6bc5be7..98b70cf8 100644 --- a/examples/react/src/App/index.tsx +++ b/examples/react/src/App/index.tsx @@ -1,326 +1,21 @@ -import { useEffect, useState } from 'react'; -import { BrowserRouter, Route, Routes, useLocation, useNavigate } from 'react-router-dom'; +import { BrowserRouter, Navigate, Route, Routes } from 'react-router-dom'; +import routes from './routes'; +import GenericTestRoutes from '../generic-tests'; 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'; - -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 ( -
- - React Flow Dev - - -
- ); -}; +import Header from './header'; export default () => ( -
- {routes.map((route) => ( - } /> - ))} + } /> + }> + } /> + {routes.map((route) => ( + } /> + ))} + + } /> ); diff --git a/examples/react/src/App/routes.ts b/examples/react/src/App/routes.ts new file mode 100644 index 00000000..6c6b596c --- /dev/null +++ b/examples/react/src/App/routes.ts @@ -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; diff --git a/examples/react/src/app.d.ts b/examples/react/src/app.d.ts new file mode 100644 index 00000000..da7d9f29 --- /dev/null +++ b/examples/react/src/app.d.ts @@ -0,0 +1,7 @@ +import { Edge, Node, ReactFlowProps } from '@xyflow/react'; + +declare global { + interface FlowConfig { + flowProps: Omit & { nodes: Node[]; edges: Edge[] }; + } +} diff --git a/examples/react/src/examples/Basic/index.tsx b/examples/react/src/examples/Basic/index.tsx index 21f48ec5..4f1a51d2 100644 --- a/examples/react/src/examples/Basic/index.tsx +++ b/examples/react/src/examples/Basic/index.tsx @@ -41,6 +41,7 @@ const initialNodes: Node[] = [ data: { label: 'Node 4' }, position: { x: 400, y: 200 }, className: 'light', + connectable: false, }, ]; diff --git a/examples/react/src/examples/UseOnSelectionChange/index.tsx b/examples/react/src/examples/UseOnSelectionChange/index.tsx index f1d8ffeb..b9795951 100644 --- a/examples/react/src/examples/UseOnSelectionChange/index.tsx +++ b/examples/react/src/examples/UseOnSelectionChange/index.tsx @@ -1,5 +1,5 @@ import { useCallback, useState } from 'react'; -import { +import { ReactFlow, addEdge, ReactFlowProvider, @@ -10,6 +10,7 @@ import { useEdgesState, useOnSelectionChange, OnSelectionChangeParams, + OnSelectionChangeFunc, } from '@xyflow/react'; const initialNodes: Node[] = [ @@ -64,7 +65,7 @@ const Flow = () => { }; const WrappedFlow = () => { - const [secondLoggerActive, setSecondLoggerActive] = useState(false); + const [secondLoggerActive, setSecondLoggerActive] = useState(true); const toggleSecondLogger = () => { setSecondLoggerActive(!secondLoggerActive); diff --git a/examples/react/src/generic-tests/Flow.tsx b/examples/react/src/generic-tests/Flow.tsx new file mode 100644 index 00000000..e5c9cd77 --- /dev/null +++ b/examples/react/src/generic-tests/Flow.tsx @@ -0,0 +1,30 @@ +import { useState, useCallback } from 'react'; +import { + ReactFlow, + applyNodeChanges, + applyEdgeChanges, + addEdge, + OnNodesChange, + OnEdgesChange, + OnConnect, +} 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 ( +
+ +
+ ); +}; diff --git a/examples/react/src/generic-tests/edges/general.ts b/examples/react/src/generic-tests/edges/general.ts new file mode 100644 index 00000000..bb768b09 --- /dev/null +++ b/examples/react/src/generic-tests/edges/general.ts @@ -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; diff --git a/examples/react/src/generic-tests/index.tsx b/examples/react/src/generic-tests/index.tsx new file mode 100644 index 00000000..1a595581 --- /dev/null +++ b/examples/react/src/generic-tests/index.tsx @@ -0,0 +1,17 @@ +import { useLocation } from 'react-router-dom'; + +import Flow from './Flow'; + +const flowConfigs = import.meta.glob('./**/*.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 ; +}; diff --git a/examples/react/src/generic-tests/nodes/components/DragHandleNode.tsx b/examples/react/src/generic-tests/nodes/components/DragHandleNode.tsx new file mode 100644 index 00000000..48037f61 --- /dev/null +++ b/examples/react/src/generic-tests/nodes/components/DragHandleNode.tsx @@ -0,0 +1,25 @@ +export default () => { + return ( +
+
+
+ ); +}; diff --git a/examples/react/src/generic-tests/nodes/general.ts b/examples/react/src/generic-tests/nodes/general.ts new file mode 100644 index 00000000..0fcb975c --- /dev/null +++ b/examples/react/src/generic-tests/nodes/general.ts @@ -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; diff --git a/examples/react/src/generic-tests/pane/general.ts b/examples/react/src/generic-tests/pane/general.ts new file mode 100644 index 00000000..54c1d193 --- /dev/null +++ b/examples/react/src/generic-tests/pane/general.ts @@ -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; diff --git a/examples/react/src/generic-tests/pane/non-defaults.ts b/examples/react/src/generic-tests/pane/non-defaults.ts new file mode 100644 index 00000000..1da85cdd --- /dev/null +++ b/examples/react/src/generic-tests/pane/non-defaults.ts @@ -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; diff --git a/examples/react/src/main.tsx b/examples/react/src/main.tsx index 34599efc..663a29b5 100644 --- a/examples/react/src/main.tsx +++ b/examples/react/src/main.tsx @@ -1,9 +1,10 @@ import { StrictMode } from 'react'; import { createRoot } from 'react-dom/client'; + import App from './App'; -import './index.css'; import '@xyflow/react/dist/style.css'; +import './index.css'; createRoot(document.getElementById('root') as HTMLElement).render( diff --git a/examples/svelte/package.json b/examples/svelte/package.json index 8cd6a14e..2679983a 100644 --- a/examples/svelte/package.json +++ b/examples/svelte/package.json @@ -1,5 +1,5 @@ { - "name": "svelte", + "name": "svelte-examples", "version": "0.0.1", "private": true, "scripts": { @@ -12,20 +12,20 @@ "format": "prettier --plugin-search-dir . --write ." }, "devDependencies": { - "@sveltejs/adapter-auto": "^2.0.0", - "@sveltejs/kit": "^1.20.4", - "@typescript-eslint/eslint-plugin": "^5.45.0", - "@typescript-eslint/parser": "^5.45.0", - "eslint": "^8.28.0", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-svelte": "^2.30.0", - "prettier": "^2.8.0", - "prettier-plugin-svelte": "^2.10.1", - "svelte": "^4.0.5", - "svelte-check": "^3.4.3", - "tslib": "^2.4.1", - "typescript": "^5.0.0", - "vite": "^4.4.2" + "@sveltejs/adapter-auto": "^2.1.1", + "@sveltejs/kit": "^1.27.3", + "@typescript-eslint/eslint-plugin": "^6.10.0", + "@typescript-eslint/parser": "^6.10.0", + "eslint": "^8.53.0", + "eslint-config-prettier": "^9.0.0", + "eslint-plugin-svelte": "^2.35.0", + "prettier": "^3.0.3", + "prettier-plugin-svelte": "^3.0.3", + "svelte": "^4.2.2", + "svelte-check": "^3.5.2", + "tslib": "^2.6.2", + "typescript": "^5.2.2", + "vite": "^4.5.0" }, "type": "module", "dependencies": { diff --git a/examples/svelte/src/app.d.ts b/examples/svelte/src/app.d.ts index f59b884c..f1252a6d 100644 --- a/examples/svelte/src/app.d.ts +++ b/examples/svelte/src/app.d.ts @@ -1,4 +1,7 @@ // See https://kit.svelte.dev/docs/types#app + +import type { Edge, Node, SvelteFlowProps } from '@xyflow/svelte'; + // for information about these interfaces declare global { namespace App { @@ -7,6 +10,10 @@ declare global { // interface PageData {} // interface Platform {} } + + interface FlowConfig { + flowProps: Omit & { nodes: Node[]; edges: Edge[] }; + } } export {}; diff --git a/examples/svelte/src/app.html b/examples/svelte/src/app.html index 68f1308a..a9d5b2f0 100644 --- a/examples/svelte/src/app.html +++ b/examples/svelte/src/app.html @@ -1,8 +1,9 @@ - + + diff --git a/examples/svelte/src/generic-tests/nodes/general.ts b/examples/svelte/src/generic-tests/nodes/general.ts new file mode 100644 index 00000000..4e5abc91 --- /dev/null +++ b/examples/svelte/src/generic-tests/nodes/general.ts @@ -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; diff --git a/examples/svelte/src/generic-tests/pane/activation-keys.ts b/examples/svelte/src/generic-tests/pane/activation-keys.ts new file mode 100644 index 00000000..ca16c47b --- /dev/null +++ b/examples/svelte/src/generic-tests/pane/activation-keys.ts @@ -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; diff --git a/examples/svelte/src/generic-tests/pane/general.ts b/examples/svelte/src/generic-tests/pane/general.ts new file mode 100644 index 00000000..9b9c9a5c --- /dev/null +++ b/examples/svelte/src/generic-tests/pane/general.ts @@ -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; diff --git a/examples/svelte/src/generic-tests/pane/non-defaults.ts b/examples/svelte/src/generic-tests/pane/non-defaults.ts new file mode 100644 index 00000000..d3aa8e27 --- /dev/null +++ b/examples/svelte/src/generic-tests/pane/non-defaults.ts @@ -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; diff --git a/examples/svelte/src/routes/+layout.server.ts b/examples/svelte/src/routes/+layout.server.ts index b64f1170..c968a610 100644 --- a/examples/svelte/src/routes/+layout.server.ts +++ b/examples/svelte/src/routes/+layout.server.ts @@ -2,7 +2,7 @@ import { redirect } from '@sveltejs/kit'; /** @type {import('./$types').LayoutServerLoad} */ export function load({ route }) { - if (route.id === '/') { - throw redirect(307, '/overview'); - } + if (route.id === '/') { + throw redirect(307, '/examples/overview'); + } } diff --git a/examples/svelte/src/routes/+layout.svelte b/examples/svelte/src/routes/examples/+layout.svelte similarity index 81% rename from examples/svelte/src/routes/+layout.svelte rename to examples/svelte/src/routes/examples/+layout.svelte index 84598fec..cf9f655f 100644 --- a/examples/svelte/src/routes/+layout.svelte +++ b/examples/svelte/src/routes/examples/+layout.svelte @@ -1,5 +1,5 @@
diff --git a/examples/svelte/src/routes/examples/+page.svelte b/examples/svelte/src/routes/examples/+page.svelte new file mode 100644 index 00000000..45db2228 --- /dev/null +++ b/examples/svelte/src/routes/examples/+page.svelte @@ -0,0 +1 @@ +
this redirects to /overview
diff --git a/examples/svelte/src/routes/add-node-on-drop/+page.svelte b/examples/svelte/src/routes/examples/add-node-on-drop/+page.svelte similarity index 100% rename from examples/svelte/src/routes/add-node-on-drop/+page.svelte rename to examples/svelte/src/routes/examples/add-node-on-drop/+page.svelte diff --git a/examples/svelte/src/routes/add-node-on-drop/Flow.svelte b/examples/svelte/src/routes/examples/add-node-on-drop/Flow.svelte similarity index 100% rename from examples/svelte/src/routes/add-node-on-drop/Flow.svelte rename to examples/svelte/src/routes/examples/add-node-on-drop/Flow.svelte diff --git a/examples/svelte/src/routes/custom-connection-line/+page.svelte b/examples/svelte/src/routes/examples/custom-connection-line/+page.svelte similarity index 100% rename from examples/svelte/src/routes/custom-connection-line/+page.svelte rename to examples/svelte/src/routes/examples/custom-connection-line/+page.svelte diff --git a/examples/svelte/src/routes/custom-connection-line/ConnectionLine.svelte b/examples/svelte/src/routes/examples/custom-connection-line/ConnectionLine.svelte similarity index 100% rename from examples/svelte/src/routes/custom-connection-line/ConnectionLine.svelte rename to examples/svelte/src/routes/examples/custom-connection-line/ConnectionLine.svelte diff --git a/examples/svelte/src/routes/custom-connection-line/CustomNode.svelte b/examples/svelte/src/routes/examples/custom-connection-line/CustomNode.svelte similarity index 100% rename from examples/svelte/src/routes/custom-connection-line/CustomNode.svelte rename to examples/svelte/src/routes/examples/custom-connection-line/CustomNode.svelte diff --git a/examples/svelte/src/routes/customnode/+page.svelte b/examples/svelte/src/routes/examples/customnode/+page.svelte similarity index 100% rename from examples/svelte/src/routes/customnode/+page.svelte rename to examples/svelte/src/routes/examples/customnode/+page.svelte diff --git a/examples/svelte/src/routes/customnode/CustomNode.svelte b/examples/svelte/src/routes/examples/customnode/CustomNode.svelte similarity index 100% rename from examples/svelte/src/routes/customnode/CustomNode.svelte rename to examples/svelte/src/routes/examples/customnode/CustomNode.svelte diff --git a/examples/svelte/src/routes/dagre/+page.svelte b/examples/svelte/src/routes/examples/dagre/+page.svelte similarity index 100% rename from examples/svelte/src/routes/dagre/+page.svelte rename to examples/svelte/src/routes/examples/dagre/+page.svelte diff --git a/examples/svelte/src/routes/dagre/nodes-and-edges.ts b/examples/svelte/src/routes/examples/dagre/nodes-and-edges.ts similarity index 100% rename from examples/svelte/src/routes/dagre/nodes-and-edges.ts rename to examples/svelte/src/routes/examples/dagre/nodes-and-edges.ts diff --git a/examples/svelte/src/routes/drag-n-drop/+page.svelte b/examples/svelte/src/routes/examples/drag-n-drop/+page.svelte similarity index 100% rename from examples/svelte/src/routes/drag-n-drop/+page.svelte rename to examples/svelte/src/routes/examples/drag-n-drop/+page.svelte diff --git a/examples/svelte/src/routes/drag-n-drop/Flow.svelte b/examples/svelte/src/routes/examples/drag-n-drop/Flow.svelte similarity index 100% rename from examples/svelte/src/routes/drag-n-drop/Flow.svelte rename to examples/svelte/src/routes/examples/drag-n-drop/Flow.svelte diff --git a/examples/svelte/src/routes/drag-n-drop/Sidebar.svelte b/examples/svelte/src/routes/examples/drag-n-drop/Sidebar.svelte similarity index 100% rename from examples/svelte/src/routes/drag-n-drop/Sidebar.svelte rename to examples/svelte/src/routes/examples/drag-n-drop/Sidebar.svelte diff --git a/examples/svelte/src/routes/edges/+page.svelte b/examples/svelte/src/routes/examples/edges/+page.svelte similarity index 100% rename from examples/svelte/src/routes/edges/+page.svelte rename to examples/svelte/src/routes/examples/edges/+page.svelte diff --git a/examples/svelte/src/routes/figma/+page.svelte b/examples/svelte/src/routes/examples/figma/+page.svelte similarity index 100% rename from examples/svelte/src/routes/figma/+page.svelte rename to examples/svelte/src/routes/examples/figma/+page.svelte diff --git a/examples/svelte/src/routes/interaction/+page.svelte b/examples/svelte/src/routes/examples/interaction/+page.svelte similarity index 100% rename from examples/svelte/src/routes/interaction/+page.svelte rename to examples/svelte/src/routes/examples/interaction/+page.svelte diff --git a/examples/svelte/src/routes/intersections/+page.svelte b/examples/svelte/src/routes/examples/intersections/+page.svelte similarity index 100% rename from examples/svelte/src/routes/intersections/+page.svelte rename to examples/svelte/src/routes/examples/intersections/+page.svelte diff --git a/examples/svelte/src/routes/intersections/+server.ts b/examples/svelte/src/routes/examples/intersections/+server.ts similarity index 100% rename from examples/svelte/src/routes/intersections/+server.ts rename to examples/svelte/src/routes/examples/intersections/+server.ts diff --git a/examples/svelte/src/routes/intersections/Flow.svelte b/examples/svelte/src/routes/examples/intersections/Flow.svelte similarity index 100% rename from examples/svelte/src/routes/intersections/Flow.svelte rename to examples/svelte/src/routes/examples/intersections/Flow.svelte diff --git a/examples/svelte/src/routes/intersections/nodes-and-edges.ts b/examples/svelte/src/routes/examples/intersections/nodes-and-edges.ts similarity index 100% rename from examples/svelte/src/routes/intersections/nodes-and-edges.ts rename to examples/svelte/src/routes/examples/intersections/nodes-and-edges.ts diff --git a/examples/svelte/src/routes/overview/+page.svelte b/examples/svelte/src/routes/examples/overview/+page.svelte similarity index 93% rename from examples/svelte/src/routes/overview/+page.svelte rename to examples/svelte/src/routes/examples/overview/+page.svelte index 32bef5d0..dddbf398 100644 --- a/examples/svelte/src/routes/overview/+page.svelte +++ b/examples/svelte/src/routes/examples/overview/+page.svelte @@ -83,6 +83,12 @@ type: 'custom', data: { label: 'Custom Node' }, position: { x: 150, y: 300 } + }, + { + id: 'hideunhide', + data: { label: 'HIDE ME' }, + position: { x: 300, y: 75 }, + hidden: true } ]); @@ -179,6 +185,13 @@ + diff --git a/examples/svelte/src/routes/overview/CustomEdge.svelte b/examples/svelte/src/routes/examples/overview/CustomEdge.svelte similarity index 100% rename from examples/svelte/src/routes/overview/CustomEdge.svelte rename to examples/svelte/src/routes/examples/overview/CustomEdge.svelte diff --git a/examples/svelte/src/routes/overview/CustomNode.svelte b/examples/svelte/src/routes/examples/overview/CustomNode.svelte similarity index 100% rename from examples/svelte/src/routes/overview/CustomNode.svelte rename to examples/svelte/src/routes/examples/overview/CustomNode.svelte diff --git a/examples/svelte/src/routes/overview/CustomNodeDragHandle.svelte b/examples/svelte/src/routes/examples/overview/CustomNodeDragHandle.svelte similarity index 100% rename from examples/svelte/src/routes/overview/CustomNodeDragHandle.svelte rename to examples/svelte/src/routes/examples/overview/CustomNodeDragHandle.svelte diff --git a/examples/svelte/src/routes/stress/+page.svelte b/examples/svelte/src/routes/examples/stress/+page.svelte similarity index 100% rename from examples/svelte/src/routes/stress/+page.svelte rename to examples/svelte/src/routes/examples/stress/+page.svelte diff --git a/examples/svelte/src/routes/subflows/+page.svelte b/examples/svelte/src/routes/examples/subflows/+page.svelte similarity index 100% rename from examples/svelte/src/routes/subflows/+page.svelte rename to examples/svelte/src/routes/examples/subflows/+page.svelte diff --git a/examples/svelte/src/routes/subflows/DebugNode.svelte b/examples/svelte/src/routes/examples/subflows/DebugNode.svelte similarity index 100% rename from examples/svelte/src/routes/subflows/DebugNode.svelte rename to examples/svelte/src/routes/examples/subflows/DebugNode.svelte diff --git a/examples/svelte/src/routes/two-way-viewport/+page.svelte b/examples/svelte/src/routes/examples/two-way-viewport/+page.svelte similarity index 100% rename from examples/svelte/src/routes/two-way-viewport/+page.svelte rename to examples/svelte/src/routes/examples/two-way-viewport/+page.svelte diff --git a/examples/svelte/src/routes/two-way-viewport/Flow.svelte b/examples/svelte/src/routes/examples/two-way-viewport/Flow.svelte similarity index 100% rename from examples/svelte/src/routes/two-way-viewport/Flow.svelte rename to examples/svelte/src/routes/examples/two-way-viewport/Flow.svelte diff --git a/examples/svelte/src/routes/usesvelteflow/+page.svelte b/examples/svelte/src/routes/examples/usesvelteflow/+page.svelte similarity index 100% rename from examples/svelte/src/routes/usesvelteflow/+page.svelte rename to examples/svelte/src/routes/examples/usesvelteflow/+page.svelte diff --git a/examples/svelte/src/routes/usesvelteflow/Flow.svelte b/examples/svelte/src/routes/examples/usesvelteflow/Flow.svelte similarity index 100% rename from examples/svelte/src/routes/usesvelteflow/Flow.svelte rename to examples/svelte/src/routes/examples/usesvelteflow/Flow.svelte diff --git a/examples/svelte/src/routes/usesvelteflow/Sidebar.svelte b/examples/svelte/src/routes/examples/usesvelteflow/Sidebar.svelte similarity index 100% rename from examples/svelte/src/routes/usesvelteflow/Sidebar.svelte rename to examples/svelte/src/routes/examples/usesvelteflow/Sidebar.svelte diff --git a/examples/svelte/src/routes/useupdatenodeinternals/+page.svelte b/examples/svelte/src/routes/examples/useupdatenodeinternals/+page.svelte similarity index 100% rename from examples/svelte/src/routes/useupdatenodeinternals/+page.svelte rename to examples/svelte/src/routes/examples/useupdatenodeinternals/+page.svelte diff --git a/examples/svelte/src/routes/useupdatenodeinternals/CustomNode.svelte b/examples/svelte/src/routes/examples/useupdatenodeinternals/CustomNode.svelte similarity index 100% rename from examples/svelte/src/routes/useupdatenodeinternals/CustomNode.svelte rename to examples/svelte/src/routes/examples/useupdatenodeinternals/CustomNode.svelte diff --git a/examples/svelte/src/routes/useupdatenodeinternals/Flow.svelte b/examples/svelte/src/routes/examples/useupdatenodeinternals/Flow.svelte similarity index 100% rename from examples/svelte/src/routes/useupdatenodeinternals/Flow.svelte rename to examples/svelte/src/routes/examples/useupdatenodeinternals/Flow.svelte diff --git a/examples/svelte/src/routes/validation/+page.svelte b/examples/svelte/src/routes/examples/validation/+page.svelte similarity index 100% rename from examples/svelte/src/routes/validation/+page.svelte rename to examples/svelte/src/routes/examples/validation/+page.svelte diff --git a/examples/svelte/src/routes/validation/style.css b/examples/svelte/src/routes/examples/validation/style.css similarity index 100% rename from examples/svelte/src/routes/validation/style.css rename to examples/svelte/src/routes/examples/validation/style.css diff --git a/examples/svelte/src/routes/tests/generic/[topic]/[example]/+page.svelte b/examples/svelte/src/routes/tests/generic/[topic]/[example]/+page.svelte new file mode 100644 index 00000000..8d75afb8 --- /dev/null +++ b/examples/svelte/src/routes/tests/generic/[topic]/[example]/+page.svelte @@ -0,0 +1,11 @@ + + + + + diff --git a/examples/svelte/src/routes/tests/generic/[topic]/[example]/+page.ts b/examples/svelte/src/routes/tests/generic/[topic]/[example]/+page.ts new file mode 100644 index 00000000..193516c1 --- /dev/null +++ b/examples/svelte/src/routes/tests/generic/[topic]/[example]/+page.ts @@ -0,0 +1,19 @@ +import { error } from '@sveltejs/kit'; + +const flowConfigs = import.meta.glob('/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 + }; +} diff --git a/examples/svelte/src/routes/tests/generic/[topic]/[example]/Flow.svelte b/examples/svelte/src/routes/tests/generic/[topic]/[example]/Flow.svelte new file mode 100644 index 00000000..44965e2e --- /dev/null +++ b/examples/svelte/src/routes/tests/generic/[topic]/[example]/Flow.svelte @@ -0,0 +1,16 @@ + + + diff --git a/examples/svelte/static/favicon.ico b/examples/svelte/static/favicon.ico new file mode 100644 index 00000000..9ef0fbfd Binary files /dev/null and b/examples/svelte/static/favicon.ico differ diff --git a/examples/svelte/static/favicon.png b/examples/svelte/static/favicon.png deleted file mode 100644 index 825b9e65..00000000 Binary files a/examples/svelte/static/favicon.png and /dev/null differ diff --git a/examples/svelte/svelte.config.js b/examples/svelte/svelte.config.js index 1cf26a00..80719d79 100644 --- a/examples/svelte/svelte.config.js +++ b/examples/svelte/svelte.config.js @@ -11,7 +11,10 @@ const config = { // 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. // See https://kit.svelte.dev/docs/adapters for more information about adapters. - adapter: adapter() + adapter: adapter(), + alias: { + $components: './src/components' + } } }; diff --git a/package.json b/package.json index f0e22afe..4411e137 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,10 @@ "dev": "turbo run dev --parallel --concurrency 12", "dev:svelte": "turbo run dev --filter=svelte --filter=system", "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", "test": "turbo run test", "lint": "turbo run lint", diff --git a/packages/react/src/components/Edges/wrapEdge.tsx b/packages/react/src/components/Edges/wrapEdge.tsx index ad1d8f2a..750254ff 100644 --- a/packages/react/src/components/Edges/wrapEdge.tsx +++ b/packages/react/src/components/Edges/wrapEdge.tsx @@ -213,6 +213,7 @@ export default (EdgeComponent: ComponentType) => { onKeyDown={isFocusable ? onKeyDown : undefined} tabIndex={isFocusable ? 0 : undefined} role={isFocusable ? 'button' : 'img'} + data-id={id} data-testid={`rf__edge-${id}`} aria-label={ariaLabel === null ? undefined : ariaLabel ? ariaLabel : `Edge from ${source} to ${target}`} aria-describedby={isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined} diff --git a/packages/react/src/components/SelectionListener/index.tsx b/packages/react/src/components/SelectionListener/index.tsx index c26888b8..b71231f6 100644 --- a/packages/react/src/components/SelectionListener/index.tsx +++ b/packages/react/src/components/SelectionListener/index.tsx @@ -32,8 +32,9 @@ const SelectionListener = memo(({ onSelectionChange }: SelectionListenerProps) = useEffect(() => { const params = { nodes: selectedNodes, edges: selectedEdges }; + onSelectionChange?.(params); - store.getState().onSelectionChange.forEach((fn) => fn(params)); + store.getState().onSelectionChangeHandlers.forEach((fn) => fn(params)); }, [selectedNodes, selectedEdges, onSelectionChange]); return null; @@ -41,12 +42,12 @@ const SelectionListener = memo(({ onSelectionChange }: SelectionListenerProps) = SelectionListener.displayName = 'SelectionListener'; -const changeSelector = (s: ReactFlowState) => !!s.onSelectionChange; +const changeSelector = (s: ReactFlowState) => !!s.onSelectionChangeHandlers; function Wrapper({ onSelectionChange }: SelectionListenerProps) { - const storeHasSelectionChange = useStore(changeSelector); + const storeHasSelectionChangeHandlers = useStore(changeSelector); - if (onSelectionChange || storeHasSelectionChange) { + if (onSelectionChange || storeHasSelectionChangeHandlers) { return ; } diff --git a/packages/react/src/hooks/useOnSelectionChange.ts b/packages/react/src/hooks/useOnSelectionChange.ts index 8ac71464..51df3ddb 100644 --- a/packages/react/src/hooks/useOnSelectionChange.ts +++ b/packages/react/src/hooks/useOnSelectionChange.ts @@ -11,11 +11,12 @@ function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) { const store = useStoreApi(); useEffect(() => { - const addedOnSelectionChange = [...store.getState().onSelectionChange, onChange]; - store.setState({ onSelectionChange: addedOnSelectionChange }); + const nextOnSelectionChangeHandlers = [...store.getState().onSelectionChangeHandlers, onChange]; + store.setState({ onSelectionChangeHandlers: nextOnSelectionChangeHandlers }); + return () => { - const removedOnSelectionChange = store.getState().onSelectionChange.filter((fn) => fn !== onChange); - store.setState({ onSelectionChange: removedOnSelectionChange }); + const nextHandlers = store.getState().onSelectionChangeHandlers.filter((fn) => fn !== onChange); + store.setState({ onSelectionChangeHandlers: nextHandlers }); }; }, [onChange]); } diff --git a/packages/react/src/store/initialState.ts b/packages/react/src/store/initialState.ts index 3bbd8053..156d8d76 100644 --- a/packages/react/src/store/initialState.ts +++ b/packages/react/src/store/initialState.ts @@ -92,7 +92,7 @@ const getInitialState = ({ connectionRadius: 20, onError: () => null, isValidConnection: undefined, - onSelectionChange: [], + onSelectionChangeHandlers: [], lib: 'react', }; diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index 47ffd28b..3662f862 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -123,8 +123,8 @@ export type ReactFlowStore = { onViewportChangeStart?: OnViewportChange; onViewportChange?: OnViewportChange; onViewportChangeEnd?: OnViewportChange; - - onSelectionChange: OnSelectionChangeFunc[]; + + onSelectionChangeHandlers: OnSelectionChangeFunc[]; ariaLiveMessage: string; autoPanOnConnect: boolean; diff --git a/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte b/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte index e53bd7c1..4e5b39d1 100644 --- a/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte +++ b/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte @@ -24,7 +24,6 @@