diff --git a/examples/react/src/App/routes.ts b/examples/react/src/App/routes.ts index 2665abfa..4bab00f5 100644 --- a/examples/react/src/App/routes.ts +++ b/examples/react/src/App/routes.ts @@ -44,7 +44,9 @@ 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'; +import UseNodesInitialized from '../examples/UseNodesInit'; +import UseNodesData from '../examples/UseNodesData'; +import UseHandleConnections from '../examples/UseHandleConnections'; export interface IRoute { name: string; @@ -261,7 +263,7 @@ const routes: IRoute[] = [ { name: 'useNodesInitialized', path: 'use-nodes-initialized', - component: useNodesInitialized, + component: UseNodesInitialized, }, { name: 'useOnSelectionChange', @@ -273,6 +275,16 @@ const routes: IRoute[] = [ path: 'usereactflow', component: UseReactFlow, }, + { + name: 'useHandleConnections', + path: 'usehandleconnections', + component: UseHandleConnections, + }, + { + name: 'useNodesData', + path: 'usenodesdata', + component: UseNodesData, + }, { name: 'useUpdateNodeInternals', path: 'useupdatenodeinternals', diff --git a/examples/react/src/examples/ColorMode/index.tsx b/examples/react/src/examples/ColorMode/index.tsx index f867c650..eae8ad86 100644 --- a/examples/react/src/examples/ColorMode/index.tsx +++ b/examples/react/src/examples/ColorMode/index.tsx @@ -15,6 +15,8 @@ import { Position, } from '@xyflow/react'; +import './style.css'; + const nodeDefaults = { sourcePosition: Position.Right, targetPosition: Position.Left, diff --git a/examples/react/src/examples/ColorMode/style.css b/examples/react/src/examples/ColorMode/style.css new file mode 100644 index 00000000..e69de29b diff --git a/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx b/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx index aebc8e0e..6857e1ac 100644 --- a/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx +++ b/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx @@ -1,4 +1,4 @@ -import React, { memo, FC, CSSProperties, useCallback } from 'react'; +import React, { memo, FC, CSSProperties, useCallback, useEffect } from 'react'; import { Handle, Position, NodeProps, Connection, Edge, useOnViewportChange, Viewport } from '@xyflow/react'; const targetHandleStyle: CSSProperties = { background: '#555' }; diff --git a/examples/react/src/examples/UpdateNode/index.tsx b/examples/react/src/examples/UpdateNode/index.tsx index 7c320606..d6e8a67a 100644 --- a/examples/react/src/examples/UpdateNode/index.tsx +++ b/examples/react/src/examples/UpdateNode/index.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { ReactFlow, Node, Edge, useNodesState, useEdgesState } from '@xyflow/react'; +import { ReactFlow, Node, Edge, useNodesState, useEdgesState, ReactFlowProvider, useReactFlow } from '@xyflow/react'; import styles from './updatenode.module.css'; @@ -13,6 +13,7 @@ const initialEdges: Edge[] = [{ id: 'e1-2', source: '1', target: '2' }]; const UpdateNode = () => { const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); + const { updateNode } = useReactFlow(); const [nodeName, setNodeName] = useState('Node 1'); const [nodeBg, setNodeBg] = useState('#eee'); @@ -80,9 +81,19 @@ const UpdateNode = () => { setNodeHidden(evt.target.checked)} /> + + ); }; -export default UpdateNode; +export default () => ( + + + +); diff --git a/examples/react/src/examples/UseHandleConnections/MultiHandleNode.tsx b/examples/react/src/examples/UseHandleConnections/MultiHandleNode.tsx new file mode 100644 index 00000000..e04f3448 --- /dev/null +++ b/examples/react/src/examples/UseHandleConnections/MultiHandleNode.tsx @@ -0,0 +1,39 @@ +import { memo, FC, useEffect, useCallback } from 'react'; +import { Handle, Position, NodeProps, useHandleConnections, Connection, HandleComponentProps } from '@xyflow/react'; + +function CustomHandle({ nodeId, ...handleProps }: HandleComponentProps & { nodeId: string }) { + const onConnect = useCallback( + (connections: Connection[]) => console.log('onConnect handler, node id:', nodeId, connections), + [nodeId] + ); + + const onDisconnect = useCallback( + (connections: Connection[]) => console.log('onDisconnect handler, node id:', nodeId, connections), + [nodeId] + ); + const connections = useHandleConnections({ + handleType: handleProps.type, + handleId: handleProps.id, + onConnect, + onDisconnect, + }); + + useEffect(() => { + console.log('useEffect, node id:', nodeId, handleProps.type, connections); + }, [connections]); + + return ; +} + +const CustomNode: FC = ({ id }) => { + return ( +
+ +
node {id}
+ + +
+ ); +}; + +export default memo(CustomNode); diff --git a/examples/react/src/examples/UseHandleConnections/SingleHandleNode.tsx b/examples/react/src/examples/UseHandleConnections/SingleHandleNode.tsx new file mode 100644 index 00000000..444499fb --- /dev/null +++ b/examples/react/src/examples/UseHandleConnections/SingleHandleNode.tsx @@ -0,0 +1,41 @@ +import { memo, FC, useEffect, useCallback } from 'react'; +import { Handle, Position, NodeProps, useHandleConnections, Connection, HandleComponentProps } from '@xyflow/react'; + +function CustomHandle({ nodeId, ...handleProps }: HandleComponentProps & { nodeId: string }) { + const onConnect = useCallback( + (connections: Connection[]) => { + console.log('onConnect handler, node id:', nodeId, connections); + }, + [nodeId] + ); + const onDisconnect = useCallback( + (connections: Connection[]) => { + console.log('onDisconnect handler, node id:', nodeId, connections); + }, + [nodeId] + ); + const connections = useHandleConnections({ + handleType: handleProps.type, + handleId: handleProps.id, + onConnect, + onDisconnect, + }); + + useEffect(() => { + console.log('useEffect, node id:', nodeId, handleProps.type, connections); + }, [connections]); + + return ; +} + +const CustomNode: FC = ({ id }) => { + return ( +
+ +
node {id}
+ +
+ ); +}; + +export default memo(CustomNode); diff --git a/examples/react/src/examples/UseHandleConnections/index.tsx b/examples/react/src/examples/UseHandleConnections/index.tsx new file mode 100644 index 00000000..c6aa4a68 --- /dev/null +++ b/examples/react/src/examples/UseHandleConnections/index.tsx @@ -0,0 +1,118 @@ +import { useCallback } from 'react'; +import { + ReactFlow, + MiniMap, + Controls, + addEdge, + Connection, + useNodesState, + useEdgesState, + Background, +} from '@xyflow/react'; + +import MultiHandleNode from './MultiHandleNode'; +import SingleHandleNode from './SingleHandleNode'; + +const nodeTypes = { + multi: MultiHandleNode, + single: SingleHandleNode, +}; + +const initNodes = [ + { + id: '1', + type: 'single', + data: {}, + position: { x: 0, y: 0 }, + }, + { + id: '2', + type: 'single', + data: {}, + position: { x: 200, y: -100 }, + }, + { + id: '3', + type: 'single', + data: {}, + position: { x: 200, y: 100 }, + }, + + { + id: '4', + type: 'multi', + data: {}, + position: { x: 400, y: 0 }, + }, + { + id: '5', + type: 'multi', + data: {}, + position: { x: 600, y: -100 }, + }, + { + id: '6', + type: 'multi', + data: {}, + position: { x: 600, y: 100 }, + }, +]; + +const initEdges = [ + { + id: 'e1-2', + source: '1', + target: '2', + }, + { + id: 'e1-3', + source: '1', + target: '3', + }, + + { + id: 'e4a-5', + source: '4', + sourceHandle: 'a', + target: '5', + }, + { + id: 'e4b-5', + source: '4', + sourceHandle: 'b', + target: '6', + }, +]; + +const defaultEdgeOptions = { + animated: true, +}; + +const CustomNodeFlow = () => { + const [nodes, setNodes, onNodesChange] = useNodesState(initNodes); + const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges); + + const onConnect = useCallback((connection: Connection) => setEdges((eds) => addEdge(connection, eds)), [setEdges]); + + return ( + + + + + + ); +}; + +export default CustomNodeFlow; diff --git a/examples/react/src/examples/UseNodesData/ResultNode.tsx b/examples/react/src/examples/UseNodesData/ResultNode.tsx new file mode 100644 index 00000000..ac9777dd --- /dev/null +++ b/examples/react/src/examples/UseNodesData/ResultNode.tsx @@ -0,0 +1,22 @@ +import { memo } from 'react'; +import { Handle, Position, useHandleConnections, useNodesData } from '@xyflow/react'; + +function ResultNode() { + const connections = useHandleConnections({ + type: 'target', + }); + const nodesData = useNodesData(connections.map((connection) => connection.source)); + + return ( +
+ +
+ incoming texts:{' '} + {nodesData?.filter((nodeData) => nodeData.text !== undefined).map(({ text }, i) =>
{text}
) || + 'none'} +
+
+ ); +} + +export default memo(ResultNode); diff --git a/examples/react/src/examples/UseNodesData/TextNode.tsx b/examples/react/src/examples/UseNodesData/TextNode.tsx new file mode 100644 index 00000000..6ca424e6 --- /dev/null +++ b/examples/react/src/examples/UseNodesData/TextNode.tsx @@ -0,0 +1,20 @@ +import { memo, ChangeEventHandler } from 'react'; +import { Position, NodeProps, Handle, useReactFlow } from '@xyflow/react'; + +function TextNode({ id, data }: NodeProps) { + const { updateNodeData } = useReactFlow(); + + const onChange: ChangeEventHandler = (evt) => updateNodeData(id, { text: evt.target.value }); + + return ( +
+
node {id}
+
+ +
+ +
+ ); +} + +export default memo(TextNode); diff --git a/examples/react/src/examples/UseNodesData/UppercaseNode.tsx b/examples/react/src/examples/UseNodesData/UppercaseNode.tsx new file mode 100644 index 00000000..699dc37e --- /dev/null +++ b/examples/react/src/examples/UseNodesData/UppercaseNode.tsx @@ -0,0 +1,24 @@ +import { memo, useEffect } from 'react'; +import { Position, NodeProps, useReactFlow, Handle, useHandleConnections, useNodesData } from '@xyflow/react'; + +function UppercaseNode({ id }: NodeProps) { + const { updateNodeData } = useReactFlow(); + const connections = useHandleConnections({ + type: 'target', + }); + const nodeData = useNodesData(connections[0]?.source); + + useEffect(() => { + updateNodeData(id, { text: nodeData?.text.toUpperCase() }); + }, [nodeData]); + + return ( +
+ +
uppercase transform
+ +
+ ); +} + +export default memo(UppercaseNode); diff --git a/examples/react/src/examples/UseNodesData/index.tsx b/examples/react/src/examples/UseNodesData/index.tsx new file mode 100644 index 00000000..7bb1a94e --- /dev/null +++ b/examples/react/src/examples/UseNodesData/index.tsx @@ -0,0 +1,102 @@ +import { useCallback } from 'react'; +import { + ReactFlow, + Controls, + addEdge, + Connection, + useNodesState, + useEdgesState, + Background, + Node, + Edge, +} from '@xyflow/react'; + +import TextNode from './TextNode'; +import ResultNode from './ResultNode'; +import UppercaseNode from './UppercaseNode'; + +export type TextNode = Node<{ text: string }, 'text'>; +export type ResultNode = Node<{}, 'result'>; +export type UppercaseNode = Node<{}, 'uppercase'>; +export type MyNode = Node<{ text: string }, 'text'> | Node<{}, 'result'> | Node<{}, 'uppercase'>; + +const nodeTypes = { + text: TextNode, + result: ResultNode, + uppercase: UppercaseNode, +}; + +const initNodes: MyNode[] = [ + { + id: '1', + type: 'text', + data: { + text: 'hello', + }, + position: { x: -100, y: -50 }, + }, + { + id: '1a', + type: 'uppercase', + data: {}, + position: { x: 100, y: 0 }, + }, + + { + id: '2', + type: 'text', + data: { + text: 'world', + }, + position: { x: 0, y: 100 }, + }, + + { + id: '3', + type: 'result', + data: {}, + position: { x: 300, y: 50 }, + }, +]; + +const initEdges: Edge[] = [ + { + id: 'e1-1a', + source: '1', + target: '1a', + }, + { + id: 'e1a-3', + source: '1a', + target: '3', + }, + { + id: 'e2-3', + source: '2', + target: '3', + }, +]; + +const CustomNodeFlow = () => { + const [nodes, setNodes, onNodesChange] = useNodesState(initNodes); + const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges); + + const onConnect = useCallback((connection: Connection) => setEdges((eds) => addEdge(connection, eds)), [setEdges]); + + return ( + + + + + ); +}; + +export default CustomNodeFlow; diff --git a/examples/svelte/src/components/Header/Header.svelte b/examples/svelte/src/components/Header/Header.svelte index 7d229796..9d67f049 100644 --- a/examples/svelte/src/components/Header/Header.svelte +++ b/examples/svelte/src/components/Header/Header.svelte @@ -4,13 +4,14 @@ const routes = [ 'add-node-on-drop', - 'colormode', + 'color-mode', 'custom-connection-line', 'customnode', 'dagre', 'drag-n-drop', 'edges', 'figma', + 'handle-connect', 'interaction', 'intersections', 'node-toolbar', @@ -18,6 +19,7 @@ 'stress', 'subflows', 'two-way-viewport', + 'usenodesdata', 'usesvelteflow', 'useupdatenodeinternals', 'validation' diff --git a/examples/svelte/src/routes/examples/add-node-on-drop/Flow.svelte b/examples/svelte/src/routes/examples/add-node-on-drop/Flow.svelte index 5556ca80..809ae82c 100644 --- a/examples/svelte/src/routes/examples/add-node-on-drop/Flow.svelte +++ b/examples/svelte/src/routes/examples/add-node-on-drop/Flow.svelte @@ -1,6 +1,12 @@ @@ -69,11 +77,11 @@ {edges} fitView fitViewOptions={{ padding: 2 }} - on:connectstart={({ detail: { nodeId } }) => { + onconnectstart={(_, { nodeId }) => { // Memorize the nodeId you start draggin a connection line from a node connectingNodeId = nodeId; }} - on:connectend={handleConnectEnd} + onconnectend={handleConnectEnd} /> diff --git a/examples/svelte/src/routes/examples/customnode/+page.svelte b/examples/svelte/src/routes/examples/customnode/+page.svelte index 14130a47..525b0706 100644 --- a/examples/svelte/src/routes/examples/customnode/+page.svelte +++ b/examples/svelte/src/routes/examples/customnode/+page.svelte @@ -85,9 +85,9 @@ {nodes} {edges} {nodeTypes} - style="--background-color: {$bgColor}" + style="--xy-background-color: {$bgColor}" fitView - on:connect={onConnect} + onconnect={onConnect} > diff --git a/examples/svelte/src/routes/examples/customnode/CustomNode.svelte b/examples/svelte/src/routes/examples/customnode/CustomNode.svelte index 25d37d32..ca01be5e 100644 --- a/examples/svelte/src/routes/examples/customnode/CustomNode.svelte +++ b/examples/svelte/src/routes/examples/customnode/CustomNode.svelte @@ -10,7 +10,7 @@
- +
Custom Color Picker Node: {$colorStore}
@@ -20,14 +20,8 @@ on:input={(evt) => colorStore.set(evt.currentTarget.value)} value={$colorStore} /> - - + +
diff --git a/examples/svelte/src/routes/examples/edges/CustomBezierEdge.svelte b/examples/svelte/src/routes/examples/edges/CustomBezierEdge.svelte new file mode 100644 index 00000000..48d86c3d --- /dev/null +++ b/examples/svelte/src/routes/examples/edges/CustomBezierEdge.svelte @@ -0,0 +1,55 @@ + + + diff --git a/examples/svelte/src/routes/examples/handle-connect/+page.svelte b/examples/svelte/src/routes/examples/handle-connect/+page.svelte new file mode 100644 index 00000000..4f121c83 --- /dev/null +++ b/examples/svelte/src/routes/examples/handle-connect/+page.svelte @@ -0,0 +1,94 @@ + + + + + + + diff --git a/examples/svelte/src/routes/examples/handle-connect/MultiHandleNode.svelte b/examples/svelte/src/routes/examples/handle-connect/MultiHandleNode.svelte new file mode 100644 index 00000000..ebc85c74 --- /dev/null +++ b/examples/svelte/src/routes/examples/handle-connect/MultiHandleNode.svelte @@ -0,0 +1,110 @@ + + +
+ +
node {id}
+ onConnectSource('a', connections)} + ondisconnect={(connections) => onDisconnectSource('a', connections)} + class="source-a" + /> + onConnectSource('b', connections)} + ondisconnect={(connections) => onDisconnectSource('b', connections)} + class="source-b" + /> +
+ + diff --git a/examples/svelte/src/routes/examples/handle-connect/SingleHandleNode.svelte b/examples/svelte/src/routes/examples/handle-connect/SingleHandleNode.svelte new file mode 100644 index 00000000..7c1e026d --- /dev/null +++ b/examples/svelte/src/routes/examples/handle-connect/SingleHandleNode.svelte @@ -0,0 +1,77 @@ + + +
+ +
node {id}
+ +
+ + diff --git a/examples/svelte/src/routes/examples/overview/+page.svelte b/examples/svelte/src/routes/examples/overview/+page.svelte index 68fd4aaa..d8293666 100644 --- a/examples/svelte/src/routes/examples/overview/+page.svelte +++ b/examples/svelte/src/routes/examples/overview/+page.svelte @@ -12,7 +12,8 @@ type EdgeTypes, type Node, type Edge, - ConnectionMode + ConnectionMode, + useSvelteFlow } from '@xyflow/svelte'; import CustomNode from './CustomNode.svelte'; @@ -150,9 +151,9 @@ on:nodemouseenter={(event) => console.log('on node enter', event)} on:nodemouseleave={(event) => console.log('on node leave', event)} on:edgeclick={(event) => console.log('edge click', event)} - on:connectstart={(event) => console.log('on connect start', event)} - on:connect={(event) => console.log('on connect', event)} - on:connectend={(event) => console.log('on connect end', event)} + onconnectstart={(event) => console.log('on connect start', event)} + onconnect={(event) => console.log('on connect', event)} + onconnectend={(event) => console.log('on connect end', event)} on:paneclick={(event) => console.log('on pane click', event)} on:panecontextmenu={(event) => { console.log('on pane contextmenu', event); diff --git a/examples/svelte/src/routes/examples/usenodesdata/+page.svelte b/examples/svelte/src/routes/examples/usenodesdata/+page.svelte new file mode 100644 index 00000000..7b2d2204 --- /dev/null +++ b/examples/svelte/src/routes/examples/usenodesdata/+page.svelte @@ -0,0 +1,80 @@ + + + + + + + diff --git a/examples/svelte/src/routes/examples/usenodesdata/ResultNode.svelte b/examples/svelte/src/routes/examples/usenodesdata/ResultNode.svelte new file mode 100644 index 00000000..9dd98173 --- /dev/null +++ b/examples/svelte/src/routes/examples/usenodesdata/ResultNode.svelte @@ -0,0 +1,37 @@ + + +
+ +
incoming texts:
+ + {#each $nodeData as data} +
{data.text}
+ {/each} +
+ + diff --git a/examples/svelte/src/routes/examples/usenodesdata/TextNode.svelte b/examples/svelte/src/routes/examples/usenodesdata/TextNode.svelte new file mode 100644 index 00000000..c1642da2 --- /dev/null +++ b/examples/svelte/src/routes/examples/usenodesdata/TextNode.svelte @@ -0,0 +1,29 @@ + + +
+
node {id}
+
+ updateNodeData(id, { text: evt.currentTarget.value })} + /> +
+ +
+ + diff --git a/examples/svelte/src/routes/examples/usenodesdata/UppercaseNode.svelte b/examples/svelte/src/routes/examples/usenodesdata/UppercaseNode.svelte new file mode 100644 index 00000000..2dcf3bde --- /dev/null +++ b/examples/svelte/src/routes/examples/usenodesdata/UppercaseNode.svelte @@ -0,0 +1,40 @@ + + +
+ +
uppercase transform
+ +
+ + diff --git a/examples/svelte/src/routes/examples/useupdatenodeinternals/CustomNode.svelte b/examples/svelte/src/routes/examples/useupdatenodeinternals/CustomNode.svelte index f4d306ac..fbe9c8d7 100644 --- a/examples/svelte/src/routes/examples/useupdatenodeinternals/CustomNode.svelte +++ b/examples/svelte/src/routes/examples/useupdatenodeinternals/CustomNode.svelte @@ -15,15 +15,9 @@ }; - + {#each Array.from({ length: handleCount }) as handle, i} - + {/each} diff --git a/examples/svelte/src/routes/examples/useupdatenodeinternals/Flow.svelte b/examples/svelte/src/routes/examples/useupdatenodeinternals/Flow.svelte index 13b48cb3..42c76cd0 100644 --- a/examples/svelte/src/routes/examples/useupdatenodeinternals/Flow.svelte +++ b/examples/svelte/src/routes/examples/useupdatenodeinternals/Flow.svelte @@ -6,7 +6,9 @@ Background, BackgroundVariant, MiniMap, - type NodeTypes + type NodeTypes, + useSvelteFlow, + Panel } from '@xyflow/svelte'; import CustomNode from './CustomNode.svelte'; @@ -50,6 +52,12 @@ target: '3' } ]); + + const { updateNode } = useSvelteFlow(); + + const updateNodePosition = () => { + updateNode('1', (node) => ({ position: { x: node.position.x + 10, y: node.position.y } })); + };
@@ -57,6 +65,8 @@ + +
diff --git a/packages/react/src/additional-components/Background/Background.tsx b/packages/react/src/additional-components/Background/Background.tsx index 843daf19..3e71d723 100644 --- a/packages/react/src/additional-components/Background/Background.tsx +++ b/packages/react/src/additional-components/Background/Background.tsx @@ -55,8 +55,8 @@ function Background({ { ...style, ...containerStyle, - '--background-color-props': bgColor, - '--background-pattern-color-props': color, + '--xy-background-color-props': bgColor, + '--xy-background-pattern-color-props': color, } as CSSProperties } ref={ref} diff --git a/packages/react/src/additional-components/MiniMap/MiniMap.tsx b/packages/react/src/additional-components/MiniMap/MiniMap.tsx index 33848836..8d1f0538 100644 --- a/packages/react/src/additional-components/MiniMap/MiniMap.tsx +++ b/packages/react/src/additional-components/MiniMap/MiniMap.tsx @@ -129,10 +129,10 @@ function MiniMap({ style={ { ...style, - '--minimap-mask-color-props': typeof maskColor === 'string' ? maskColor : undefined, - '--minimap-node-background-color-props': typeof nodeColor === 'string' ? nodeColor : undefined, - '--minimap-node-stroke-color-props': typeof nodeStrokeColor === 'string' ? nodeStrokeColor : undefined, - '--minimap-node-stroke-width-props': typeof nodeStrokeWidth === 'string' ? nodeStrokeWidth : undefined, + '--xy-minimap-mask-color-props': typeof maskColor === 'string' ? maskColor : undefined, + '--xy-minimap-node-background-color-props': typeof nodeColor === 'string' ? nodeColor : undefined, + '--xy-minimap-node-stroke-color-props': typeof nodeStrokeColor === 'string' ? nodeStrokeColor : undefined, + '--xy-minimap-node-stroke-width-props': typeof nodeStrokeWidth === 'string' ? nodeStrokeWidth : undefined, } as CSSProperties } className={cc(['react-flow__minimap', className])} diff --git a/packages/react/src/components/Edges/BezierEdge.tsx b/packages/react/src/components/Edges/BezierEdge.tsx index 8f446393..2a0b54a2 100644 --- a/packages/react/src/components/Edges/BezierEdge.tsx +++ b/packages/react/src/components/Edges/BezierEdge.tsx @@ -4,56 +4,67 @@ import { Position, getBezierPath } from '@xyflow/system'; import BaseEdge from './BaseEdge'; import type { BezierEdgeProps } from '../../types'; -const BezierEdge = memo( - ({ - sourceX, - sourceY, - targetX, - targetY, - sourcePosition = Position.Bottom, - targetPosition = Position.Top, - label, - labelStyle, - labelShowBg, - labelBgStyle, - labelBgPadding, - labelBgBorderRadius, - style, - markerEnd, - markerStart, - pathOptions, - interactionWidth, - }: BezierEdgeProps) => { - const [path, labelX, labelY] = getBezierPath({ +function createBezierEdge(params: { isInternal: boolean }) { + // eslint-disable-next-line react/display-name + return memo( + ({ + id, sourceX, sourceY, - sourcePosition, targetX, targetY, - targetPosition, - curvature: pathOptions?.curvature, - }); + sourcePosition = Position.Bottom, + targetPosition = Position.Top, + label, + labelStyle, + labelShowBg, + labelBgStyle, + labelBgPadding, + labelBgBorderRadius, + style, + markerEnd, + markerStart, + pathOptions, + interactionWidth, + }: BezierEdgeProps) => { + const [path, labelX, labelY] = getBezierPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + curvature: pathOptions?.curvature, + }); - return ( - - ); - } -); + const _id = params.isInternal ? undefined : id; + + return ( + + ); + } + ); +} + +const BezierEdge = createBezierEdge({ isInternal: false }); +const BezierEdgeInternal = createBezierEdge({ isInternal: true }); BezierEdge.displayName = 'BezierEdge'; +BezierEdgeInternal.displayName = 'BezierEdgeInternal'; -export default BezierEdge; +export { BezierEdge, BezierEdgeInternal }; diff --git a/packages/react/src/components/Edges/SimpleBezierEdge.tsx b/packages/react/src/components/Edges/SimpleBezierEdge.tsx index d9fa50c4..09c7a752 100644 --- a/packages/react/src/components/Edges/SimpleBezierEdge.tsx +++ b/packages/react/src/components/Edges/SimpleBezierEdge.tsx @@ -2,7 +2,7 @@ import { memo } from 'react'; import { Position, getBezierEdgeCenter } from '@xyflow/system'; import BaseEdge from './BaseEdge'; -import type { EdgeProps } from '../../types'; +import type { SimpleBezierEdgeProps } from '../../types'; export interface GetSimpleBezierPathParams { sourceX: number; @@ -71,54 +71,65 @@ export function getSimpleBezierPath({ ]; } -const SimpleBezierEdge = memo( - ({ - sourceX, - sourceY, - targetX, - targetY, - sourcePosition = Position.Bottom, - targetPosition = Position.Top, - label, - labelStyle, - labelShowBg, - labelBgStyle, - labelBgPadding, - labelBgBorderRadius, - style, - markerEnd, - markerStart, - interactionWidth, - }: EdgeProps) => { - const [path, labelX, labelY] = getSimpleBezierPath({ +function createSimpleBezierEdge(params: { isInternal: boolean }) { + // eslint-disable-next-line react/display-name + return memo( + ({ + id, sourceX, sourceY, - sourcePosition, targetX, targetY, - targetPosition, - }); + sourcePosition = Position.Bottom, + targetPosition = Position.Top, + label, + labelStyle, + labelShowBg, + labelBgStyle, + labelBgPadding, + labelBgBorderRadius, + style, + markerEnd, + markerStart, + interactionWidth, + }: SimpleBezierEdgeProps) => { + const [path, labelX, labelY] = getSimpleBezierPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); - return ( - - ); - } -); + const _id = params.isInternal ? undefined : id; + + return ( + + ); + } + ); +} + +const SimpleBezierEdge = createSimpleBezierEdge({ isInternal: false }); +const SimpleBezierEdgeInternal = createSimpleBezierEdge({ isInternal: true }); SimpleBezierEdge.displayName = 'SimpleBezierEdge'; +SimpleBezierEdgeInternal.displayName = 'SimpleBezierEdgeInternal'; -export default SimpleBezierEdge; +export { SimpleBezierEdge, SimpleBezierEdgeInternal }; diff --git a/packages/react/src/components/Edges/SmoothStepEdge.tsx b/packages/react/src/components/Edges/SmoothStepEdge.tsx index be635a4e..8eb10d89 100644 --- a/packages/react/src/components/Edges/SmoothStepEdge.tsx +++ b/packages/react/src/components/Edges/SmoothStepEdge.tsx @@ -4,57 +4,68 @@ import { Position, getSmoothStepPath } from '@xyflow/system'; import BaseEdge from './BaseEdge'; import type { SmoothStepEdgeProps } from '../../types'; -const SmoothStepEdge = memo( - ({ - sourceX, - sourceY, - targetX, - targetY, - label, - labelStyle, - labelShowBg, - labelBgStyle, - labelBgPadding, - labelBgBorderRadius, - style, - sourcePosition = Position.Bottom, - targetPosition = Position.Top, - markerEnd, - markerStart, - pathOptions, - interactionWidth, - }: SmoothStepEdgeProps) => { - const [path, labelX, labelY] = getSmoothStepPath({ +function createSmoothStepEdge(params: { isInternal: boolean }) { + // eslint-disable-next-line react/display-name + return memo( + ({ + id, sourceX, sourceY, - sourcePosition, targetX, targetY, - targetPosition, - borderRadius: pathOptions?.borderRadius, - offset: pathOptions?.offset, - }); + label, + labelStyle, + labelShowBg, + labelBgStyle, + labelBgPadding, + labelBgBorderRadius, + style, + sourcePosition = Position.Bottom, + targetPosition = Position.Top, + markerEnd, + markerStart, + pathOptions, + interactionWidth, + }: SmoothStepEdgeProps) => { + const [path, labelX, labelY] = getSmoothStepPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + borderRadius: pathOptions?.borderRadius, + offset: pathOptions?.offset, + }); - return ( - - ); - } -); + const _id = params.isInternal ? undefined : id; + + return ( + + ); + } + ); +} + +const SmoothStepEdge = createSmoothStepEdge({ isInternal: false }); +const SmoothStepEdgeInternal = createSmoothStepEdge({ isInternal: true }); SmoothStepEdge.displayName = 'SmoothStepEdge'; +SmoothStepEdgeInternal.displayName = 'SmoothStepEdgeInternal'; -export default SmoothStepEdge; +export { SmoothStepEdge, SmoothStepEdgeInternal }; diff --git a/packages/react/src/components/Edges/StepEdge.tsx b/packages/react/src/components/Edges/StepEdge.tsx index df2646a7..6385e8f0 100644 --- a/packages/react/src/components/Edges/StepEdge.tsx +++ b/packages/react/src/components/Edges/StepEdge.tsx @@ -1,15 +1,30 @@ import { memo, useMemo } from 'react'; -import SmoothStepEdge from './SmoothStepEdge'; -import type { SmoothStepEdgeProps } from '../../types'; +import { SmoothStepEdge } from './SmoothStepEdge'; +import type { StepEdgeProps } from '../../types'; -const StepEdge = memo((props: SmoothStepEdgeProps) => ( - ({ borderRadius: 0, offset: props.pathOptions?.offset }), [props.pathOptions?.offset])} - /> -)); +function createStepEdge(params: { isInternal: boolean }) { + // eslint-disable-next-line react/display-name + return memo(({ id, ...props }: StepEdgeProps) => { + const _id = params.isInternal ? undefined : id; + + return ( + ({ borderRadius: 0, offset: props.pathOptions?.offset }), + [props.pathOptions?.offset] + )} + /> + ); + }); +} + +const StepEdge = createStepEdge({ isInternal: false }); +const StepEdgeInternal = createStepEdge({ isInternal: true }); StepEdge.displayName = 'StepEdge'; +StepEdgeInternal.displayName = 'StepEdgeInternal'; -export default StepEdge; +export { StepEdge, StepEdgeInternal }; diff --git a/packages/react/src/components/Edges/StraightEdge.tsx b/packages/react/src/components/Edges/StraightEdge.tsx index 06f4a161..5a3c824d 100644 --- a/packages/react/src/components/Edges/StraightEdge.tsx +++ b/packages/react/src/components/Edges/StraightEdge.tsx @@ -2,47 +2,58 @@ import { memo } from 'react'; import { getStraightPath } from '@xyflow/system'; import BaseEdge from './BaseEdge'; -import type { EdgeProps } from '../../types'; +import type { StraightEdgeProps } from '../../types'; -const StraightEdge = memo( - ({ - sourceX, - sourceY, - targetX, - targetY, - label, - labelStyle, - labelShowBg, - labelBgStyle, - labelBgPadding, - labelBgBorderRadius, - style, - markerEnd, - markerStart, - interactionWidth, - }: EdgeProps) => { - const [path, labelX, labelY] = getStraightPath({ sourceX, sourceY, targetX, targetY }); +function createStraightEdge(params: { isInternal: boolean }) { + // eslint-disable-next-line react/display-name + return memo( + ({ + id, + sourceX, + sourceY, + targetX, + targetY, + label, + labelStyle, + labelShowBg, + labelBgStyle, + labelBgPadding, + labelBgBorderRadius, + style, + markerEnd, + markerStart, + interactionWidth, + }: StraightEdgeProps) => { + const [path, labelX, labelY] = getStraightPath({ sourceX, sourceY, targetX, targetY }); - return ( - - ); - } -); + const _id = params.isInternal ? undefined : id; + + return ( + + ); + } + ); +} + +const StraightEdge = createStraightEdge({ isInternal: false }); +const StraightEdgeInternal = createStraightEdge({ isInternal: true }); StraightEdge.displayName = 'StraightEdge'; +StraightEdgeInternal.displayName = 'StraightEdgeInternal'; -export default StraightEdge; +export { StraightEdge, StraightEdgeInternal }; diff --git a/packages/react/src/components/Edges/index.ts b/packages/react/src/components/Edges/index.ts index e4d30109..21af9667 100644 --- a/packages/react/src/components/Edges/index.ts +++ b/packages/react/src/components/Edges/index.ts @@ -1,5 +1,9 @@ -export { default as SimpleBezierEdge } from './SimpleBezierEdge'; -export { default as SmoothStepEdge } from './SmoothStepEdge'; -export { default as StepEdge } from './StepEdge'; -export { default as StraightEdge } from './StraightEdge'; -export { default as BezierEdge } from './BezierEdge'; +// We distinguish between internal and exported edges +// The internal edges are used directly like custom edges and always get an id, source and target props +// If you import an edge from the library, the id is optional and source and target are not used at all + +export { SimpleBezierEdge, SimpleBezierEdgeInternal } from './SimpleBezierEdge'; +export { SmoothStepEdge, SmoothStepEdgeInternal } from './SmoothStepEdge'; +export { StepEdge, StepEdgeInternal } from './StepEdge'; +export { StraightEdge, StraightEdgeInternal } from './StraightEdge'; +export { BezierEdge, BezierEdgeInternal } from './BezierEdge'; diff --git a/packages/react/src/components/Handle/index.tsx b/packages/react/src/components/Handle/index.tsx index 29231b71..a2c0101f 100644 --- a/packages/react/src/components/Handle/index.tsx +++ b/packages/react/src/components/Handle/index.tsx @@ -171,7 +171,7 @@ const Handle = forwardRef( lib, }); - if (isValid) { + if (isValid && connection) { onConnectExtended(connection); } diff --git a/packages/react/src/container/EdgeRenderer/utils.ts b/packages/react/src/container/EdgeRenderer/utils.ts index 0e8f39d3..433bb0f0 100644 --- a/packages/react/src/container/EdgeRenderer/utils.ts +++ b/packages/react/src/container/EdgeRenderer/utils.ts @@ -1,6 +1,12 @@ import type { ComponentType } from 'react'; -import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges'; +import { + BezierEdgeInternal, + SmoothStepEdgeInternal, + StepEdgeInternal, + StraightEdgeInternal, + SimpleBezierEdgeInternal, +} from '../../components/Edges'; import wrapEdge from '../../components/Edges/wrapEdge'; import type { EdgeProps, EdgeTypes, EdgeTypesWrapped } from '../../types'; @@ -8,18 +14,18 @@ export type CreateEdgeTypes = (edgeTypes: EdgeTypes) => EdgeTypesWrapped; export function createEdgeTypes(edgeTypes: EdgeTypes): EdgeTypesWrapped { const standardTypes: EdgeTypesWrapped = { - default: wrapEdge((edgeTypes.default || BezierEdge) as ComponentType), - straight: wrapEdge((edgeTypes.bezier || StraightEdge) as ComponentType), - step: wrapEdge((edgeTypes.step || StepEdge) as ComponentType), - smoothstep: wrapEdge((edgeTypes.step || SmoothStepEdge) as ComponentType), - simplebezier: wrapEdge((edgeTypes.simplebezier || SimpleBezierEdge) as ComponentType), + default: wrapEdge((edgeTypes.default || BezierEdgeInternal) as ComponentType), + straight: wrapEdge((edgeTypes.bezier || StraightEdgeInternal) as ComponentType), + step: wrapEdge((edgeTypes.step || StepEdgeInternal) as ComponentType), + smoothstep: wrapEdge((edgeTypes.step || SmoothStepEdgeInternal) as ComponentType), + simplebezier: wrapEdge((edgeTypes.simplebezier || SimpleBezierEdgeInternal) as ComponentType), }; const wrappedTypes = {} as EdgeTypesWrapped; const specialTypes: EdgeTypesWrapped = Object.keys(edgeTypes) .filter((k) => !['default', 'bezier'].includes(k)) .reduce((res, key) => { - res[key] = wrapEdge((edgeTypes[key] || BezierEdge) as ComponentType); + res[key] = wrapEdge((edgeTypes[key] || BezierEdgeInternal) as ComponentType); return res; }, wrappedTypes); diff --git a/packages/react/src/container/ReactFlow/index.tsx b/packages/react/src/container/ReactFlow/index.tsx index 1b3dc796..a7360a46 100644 --- a/packages/react/src/container/ReactFlow/index.tsx +++ b/packages/react/src/container/ReactFlow/index.tsx @@ -12,7 +12,13 @@ import { } from '@xyflow/system'; import Attribution from '../../components/Attribution'; -import { BezierEdge, SmoothStepEdge, StepEdge, StraightEdge, SimpleBezierEdge } from '../../components/Edges'; +import { + BezierEdgeInternal, + SmoothStepEdgeInternal, + StepEdgeInternal, + StraightEdgeInternal, + SimpleBezierEdgeInternal, +} from '../../components/Edges'; import DefaultNode from '../../components/Nodes/DefaultNode'; import InputNode from '../../components/Nodes/InputNode'; import OutputNode from '../../components/Nodes/OutputNode'; @@ -33,11 +39,11 @@ const defaultNodeTypes: NodeTypes = { }; const defaultEdgeTypes: EdgeTypes = { - default: BezierEdge, - straight: StraightEdge, - step: StepEdge, - smoothstep: SmoothStepEdge, - simplebezier: SimpleBezierEdge, + default: BezierEdgeInternal, + straight: StraightEdgeInternal, + step: StepEdgeInternal, + smoothstep: SmoothStepEdgeInternal, + simplebezier: SimpleBezierEdgeInternal, }; const initNodeOrigin: NodeOrigin = [0, 0]; diff --git a/packages/react/src/hooks/useHandleConnections.ts b/packages/react/src/hooks/useHandleConnections.ts new file mode 100644 index 00000000..c9a026d9 --- /dev/null +++ b/packages/react/src/hooks/useHandleConnections.ts @@ -0,0 +1,54 @@ +import { useEffect, useMemo, useRef } from 'react'; +import { Connection, HandleType, areConnectionMapsEqual, handleConnectionChange } from '@xyflow/system'; + +import { useStore } from './useStore'; +import { useNodeId } from '../contexts/NodeIdContext'; + +type useHandleConnectionsParams = { + type: HandleType; + id?: string | null; + nodeId?: string; + onConnect?: (connections: Connection[]) => void; + onDisconnect?: (connections: Connection[]) => void; +}; + +/** + * Hook to check if a is connected to another and get the connections. + * + * @public + * @param param.type - handle type 'source' or 'target' + * @param param.id - the handle id (this is only needed if the node has multiple handles of the same type) + * @param param.nodeId - node id - if not provided, the node id from the NodeIdContext is used + * @param param.onConnect - gets called when a connection is established + * @param param.onDisconnect - gets called when a connection is removed + * @returns an array with connections + */ +export function useHandleConnections({ + type, + id = null, + nodeId, + onConnect, + onDisconnect, +}: useHandleConnectionsParams): Connection[] { + const _nodeId = useNodeId(); + const prevConnections = useRef | null>(null); + const currentNodeId = nodeId || _nodeId; + + const connections = useStore( + (state) => state.connectionLookup.get(`${currentNodeId}-${type}-${id}`), + areConnectionMapsEqual + ); + + useEffect(() => { + // @todo dicuss if onConnect/onDisconnect should be called when the component mounts/unmounts + if (prevConnections.current && prevConnections.current !== connections) { + const _connections = connections ?? new Map(); + handleConnectionChange(prevConnections.current, _connections, onDisconnect); + handleConnectionChange(_connections, prevConnections.current, onConnect); + } + + prevConnections.current = connections ?? new Map(); + }, [connections, onConnect, onDisconnect]); + + return useMemo(() => Array.from(connections?.values() ?? []), [connections]); +} diff --git a/packages/react/src/hooks/useNodesData.ts b/packages/react/src/hooks/useNodesData.ts new file mode 100644 index 00000000..4c242ad3 --- /dev/null +++ b/packages/react/src/hooks/useNodesData.ts @@ -0,0 +1,40 @@ +import { useCallback } from 'react'; +import { shallow } from 'zustand/shallow'; + +import { useStore } from '../hooks/useStore'; +import type { Node } from '../types'; + +export function useNodesData(nodeId: string): NodeType['data'] | null; +export function useNodesData(nodeIds: string[]): NodeType['data'][]; +export function useNodesData( + nodeIds: string[], + guard: (node: Node) => node is NodeType +): NodeType['data'][]; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function useNodesData(nodeIds: any): any { + const nodesData = useStore( + useCallback( + (s) => { + if (!Array.isArray(nodeIds)) { + return s.nodeLookup.get(nodeIds)?.data || null; + } + + const data = []; + + for (const nodeId of nodeIds) { + const nodeData = s.nodeLookup.get(nodeId)?.data; + + if (nodeData) { + data.push(nodeData); + } + } + + return data; + }, + [nodeIds] + ), + shallow + ); + + return nodesData; +} diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index 44e29c14..9bb17eb9 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -10,7 +10,7 @@ import { } from '@xyflow/system'; import useViewportHelper from './useViewportHelper'; -import { useStoreApi } from '../hooks/useStore'; +import { useStoreApi } from './useStore'; import type { ReactFlowInstance, Instance, @@ -24,6 +24,7 @@ import type { Node, Edge, } from '../types'; +import { isNode } from '../utils'; /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ export default function useReactFlow(): ReactFlowInstance { @@ -271,6 +272,36 @@ export default function useReactFlow(): ReactFlo return getOutgoersBase(node, nodes, edges); }, []); + const updateNode = useCallback( + (id, nodeUpdate, options = { replace: true }) => { + setNodes((prevNodes) => + prevNodes.map((node) => { + if (node.id === id) { + const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node as Node) : nodeUpdate; + return options.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode }; + } + + return node; + }) + ); + }, + [setNodes] + ); + + const updateNodeData = useCallback( + (id, dataUpdate, options = { replace: false }) => { + updateNode( + id, + (node) => { + const nextData = typeof dataUpdate === 'function' ? dataUpdate(node) : dataUpdate; + return options.replace ? { ...node, data: nextData } : { ...node, data: { ...node.data, ...nextData } }; + }, + options + ); + }, + [updateNode] + ); + return useMemo(() => { return { ...viewportHelper, @@ -289,6 +320,8 @@ export default function useReactFlow(): ReactFlo getConnectedEdges, getIncomers, getOutgoers, + updateNode, + updateNodeData, }; }, [ viewportHelper, diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 4fe922e5..ae82e78b 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -1,11 +1,11 @@ export { default as ReactFlow } from './container/ReactFlow'; -export { default as Handle } from './components/Handle'; +export { default as Handle, type HandleComponentProps } from './components/Handle'; export { default as EdgeText } from './components/Edges/EdgeText'; -export { default as StraightEdge } from './components/Edges/StraightEdge'; -export { default as StepEdge } from './components/Edges/StepEdge'; -export { default as BezierEdge } from './components/Edges/BezierEdge'; -export { default as SimpleBezierEdge, getSimpleBezierPath } from './components/Edges/SimpleBezierEdge'; -export { default as SmoothStepEdge } from './components/Edges/SmoothStepEdge'; +export { StraightEdge } from './components/Edges/StraightEdge'; +export { StepEdge } from './components/Edges/StepEdge'; +export { BezierEdge } from './components/Edges/BezierEdge'; +export { SimpleBezierEdge, getSimpleBezierPath } from './components/Edges/SimpleBezierEdge'; +export { SmoothStepEdge } from './components/Edges/SmoothStepEdge'; export { default as BaseEdge } from './components/Edges/BaseEdge'; export { default as ReactFlowProvider } from './components/ReactFlowProvider'; export { default as Panel, type PanelProps } from './components/Panel'; @@ -22,6 +22,8 @@ export { useStore, useStoreApi } from './hooks/useStore'; export { default as useOnViewportChange, type UseOnViewportChangeOptions } from './hooks/useOnViewportChange'; export { default as useOnSelectionChange, type UseOnSelectionChangeOptions } from './hooks/useOnSelectionChange'; export { default as useNodesInitialized, type UseNodesInitializedOptions } from './hooks/useNodesInitialized'; +export { useHandleConnections } from './hooks/useHandleConnections'; +export { useNodesData } from './hooks/useNodesData'; export { useNodeId } from './contexts/NodeIdContext'; export { applyNodeChanges, applyEdgeChanges, handleParentExpand } from './utils/changes'; diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index 0f50f385..c5c21d7a 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -7,6 +7,7 @@ import { panBy as panBySystem, Dimensions, updateNodeDimensions as updateNodeDimensionsSystem, + updateConnectionLookup, } from '@xyflow/system'; import { applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes'; @@ -49,8 +50,12 @@ const createRFStore = ({ set({ nodes: nextNodes }); }, setEdges: (edges: Edge[]) => { - const { defaultEdgeOptions = {} } = get(); - set({ edges: edges.map((e) => ({ ...defaultEdgeOptions, ...e })) }); + const { defaultEdgeOptions = {}, connectionLookup } = get(); + const nextEdges = edges.map((e) => ({ ...defaultEdgeOptions, ...e })); + + updateConnectionLookup(connectionLookup, nextEdges); + + set({ edges: nextEdges }); }, // when the user works with an uncontrolled flow, // we set a flag `hasDefaultNodes` / `hasDefaultEdges` @@ -326,6 +331,7 @@ const createRFStore = ({ set(currentConnection); }, + reset: () => { // @todo: what should we do about this? Do we still need it? // if you are on a SPA with multiple flows, we want to make sure that the store gets resetted diff --git a/packages/react/src/store/initialState.ts b/packages/react/src/store/initialState.ts index 48e5434f..8d07617a 100644 --- a/packages/react/src/store/initialState.ts +++ b/packages/react/src/store/initialState.ts @@ -5,6 +5,7 @@ import { getNodesBounds, getViewportForBounds, Transform, + updateConnectionLookup, } from '@xyflow/system'; import type { Edge, Node, ReactFlowStore } from '../types'; @@ -22,7 +23,8 @@ const getInitialState = ({ height?: number; fitView?: boolean; } = {}): ReactFlowStore => { - const nodeLookup = new Map(); + const nodeLookup = new Map(); + const connectionLookup = updateConnectionLookup(new Map(), edges); const nextNodes = updateNodes(nodes, nodeLookup, { nodeOrigin: [0, 0], elevateNodesOnSelect: false }); let transform: Transform = [0, 0, 1]; @@ -42,6 +44,7 @@ const getInitialState = ({ nodes: nextNodes, nodeLookup, edges: edges, + connectionLookup, onNodesChange: null, onEdgesChange: null, hasDefaultNodes: false, diff --git a/packages/react/src/types/edges.ts b/packages/react/src/types/edges.ts index cd29a85f..287c277d 100644 --- a/packages/react/src/types/edges.ts +++ b/packages/react/src/types/edges.ts @@ -13,6 +13,8 @@ import type { HandleElement, ConnectionStatus, EdgePosition, + Optional, + StepPathOptions, } from '@xyflow/system'; import { Node } from '.'; @@ -46,7 +48,12 @@ type BezierEdgeType = DefaultEdge & { pathOptions?: BezierPathOptions; }; -export type Edge = DefaultEdge | SmoothStepEdgeType | BezierEdgeType; +type StepEdgeType = DefaultEdge & { + type: 'step'; + pathOptions?: StepPathOptions; +}; + +export type Edge = DefaultEdge | SmoothStepEdgeType | BezierEdgeType | StepEdgeType; export type EdgeMouseHandler = (event: ReactMouseEvent, edge: Edge) => void; @@ -100,14 +107,24 @@ export type BaseEdgeProps = Pick = EdgeProps & { +export type EdgeComponentProps = Optional, 'source' | 'target'>, 'id'>; + +export type StraightEdgeProps = Omit, 'sourcePosition' | 'targetPosition'>; + +export type SmoothStepEdgeProps = EdgeComponentProps & { pathOptions?: SmoothStepPathOptions; }; -export type BezierEdgeProps = EdgeProps & { +export type BezierEdgeProps = EdgeComponentProps & { pathOptions?: BezierPathOptions; }; +export type StepEdgeProps = EdgeComponentProps & { + pathOptions?: StepPathOptions; +}; + +export type SimpleBezierEdgeProps = EdgeComponentProps; + export type OnEdgeUpdateFunc = (oldEdge: Edge, newConnection: Connection) => void; export type ConnectionLineComponentProps = { diff --git a/packages/react/src/types/instance.ts b/packages/react/src/types/instance.ts index f229ec87..a1c822bb 100644 --- a/packages/react/src/types/instance.ts +++ b/packages/react/src/types/instance.ts @@ -45,6 +45,17 @@ export namespace Instance { export type getConnectedEdges = (id: string | (Node | { id: Node['id'] })[]) => Edge[]; export type getIncomers = (node: string | Node | { id: Node['id'] }) => Node[]; export type getOutgoers = (node: string | Node | { id: Node['id'] }) => Node[]; + + export type UpdateNode = ( + id: string, + dataUpdate: Partial | ((node: Node) => Partial), + options?: { replace: boolean } + ) => void; + export type UpdateNodeData = ( + id: string, + dataUpdate: object | ((node: Node) => object), + options?: { replace: boolean } + ) => void; } export type ReactFlowInstance = { @@ -60,5 +71,7 @@ export type ReactFlowInstance = { deleteElements: Instance.DeleteElements; getIntersectingNodes: Instance.GetIntersectingNodes; isNodeIntersecting: Instance.IsNodeIntersecting; + updateNode: Instance.UpdateNode; + updateNodeData: Instance.UpdateNodeData; viewportInitialized: boolean; } & Omit; diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index c473cbd5..5aebb99d 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -24,6 +24,7 @@ import { type OnMoveEnd, type IsValidConnection, type UpdateConnection, + Connection, } from '@xyflow/system'; import type { @@ -49,6 +50,8 @@ export type ReactFlowStore = { nodes: Node[]; nodeLookup: Map; edges: Edge[]; + connectionLookup: Map>; + onNodesChange: OnNodesChange | null; onEdgesChange: OnEdgesChange | null; hasDefaultNodes: boolean; diff --git a/packages/react/src/utils/general.ts b/packages/react/src/utils/general.ts index e13247c1..a106e182 100644 --- a/packages/react/src/utils/general.ts +++ b/packages/react/src/utils/general.ts @@ -10,8 +10,8 @@ import { import type { Edge, Node } from '../types'; -export const isNode = isNodeBase; -export const isEdge = isEdgeBase; +export const isNode = isNodeBase; +export const isEdge = isEdgeBase; export const getOutgoers = getOutgoersBase; export const getIncomers = getIncomersBase; export const addEdge = addEdgeBase; diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 12086a6a..1a602f2f 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,3 +1,28 @@ +## 0.0.29 + +Another huge update for Svelte Flow 🙏 Handling data flows will be way easier with the new hooks and functions. You can now subscribe to connected nodes, receive data and update nodes more easily. We fix a big issue about the `` component. No more `on:connect` that only worked for target `` components but `onconnect` and `ondisconnect` that works for every ``. + +### Features + +- add `useHandleConnections` hook for receiving connected node and handle ids for a specific handle +- add `useNodesData(ids: string | string[])` hook for receiving data from other nodes +- export `updateNode` and `updateNodeData` from `useSvelteFlow` to update a node or the data object +- add `onedgecreate` function for passing a certain id or other attributes to a newly created edge + +### ⚠️ Breaking + +- replace `on:connect`, `on:connectstart` and `on:connectend` with `onconnect`, `onconnectstart` and `onconnectend`, no need to forward `on:connect..` anymore + +### Fixes and minor changes + +- `onconnect` and `ondisconnect` callback work for `` component +- don't delete a node when user presses Backspace inside an input/textarea/.nokey element +- `bgColor` prop for Background didn't work +- prefix css vars with "xy-" +- don't update nodes and edges on pane click if not necessary +- cleaner types for exported edges +- fix `getIntersectingNodes` bug when passing `Rect` + ## 0.0.28 This is a huge update! We added a new `` component and a new `colorMode` ('light' | 'dark' | 'system') prop for toggling dark/light mode. @@ -7,7 +32,7 @@ There are also some breaking changes again (sorry!) but we are very close to the - add `` component - add `on:selectionclick` and `on:selectioncontextmenu` event handlers - add `ondelete({ nodes, edges })` handler -- add `zoomActivationKey` prop +- add `zoomActivationKey` prop - add `width` and `height` prop to custom `NodeProps` type - add `colorMode` prop ('light' | 'dark' | 'system') - ⚠️ replace `xPos` and `yPos` with `positionAbsolute` prop to custom `NodeProps` type @@ -18,7 +43,7 @@ There are also some breaking changes again (sorry!) but we are very close to the ## 0.0.27 -- add `selectionOnDrag` prop - can be used to create figma-like controls in combination with `panOnDrag={false}` / `panOnDrag={[1, 2]}` + `panOnScroll={true}` +- 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]`) @@ -67,7 +92,7 @@ There are also some breaking changes again (sorry!) but we are very close to the ## 0.0.22 -- add `connectionLine` slot for rendering a custom connection line +- add `connectionLine` slot for rendering a custom connection line - add `connectionLineStyle` and `connectionLineContainerStyle` props - add `useConnection` hook - add `nodeDragThreshold` prop @@ -100,7 +125,7 @@ There are also some breaking changes again (sorry!) but we are very close to the ## 0.0.18 -- add `nodesDraggable` prop +- add `nodesDraggable` prop - minimap: add default background ## 0.0.17 @@ -113,7 +138,7 @@ There are also some breaking changes again (sorry!) but we are very close to the ## 0.0.15 -- fix wrongly displayed connectionline +- fix wrongly displayed connectionline ## 0.0.14 @@ -152,14 +177,14 @@ this version is broken because of a wrong path in the package.json - add `connectionRadius` -## 0.0.1 +## 0.0.1 -Svelte Flow alpha is here 🔥 You can expect some changes until we reach 1.0.0 but we try to stick as close as possible to the React Flow API. There are no docs yet, but we are working on it! For now the easiest way is to use the autocomplete of your IDE, lookup the props in the SvelteFlow component or check out the React Flow docs. +Svelte Flow alpha is here 🔥 You can expect some changes until we reach 1.0.0 but we try to stick as close as possible to the React Flow API. There are no docs yet, but we are working on it! For now the easiest way is to use the autocomplete of your IDE, lookup the props in the SvelteFlow component or check out the React Flow docs. This very first release comes with lots of features already: -- pass `nodes` and `edges` as writables -- draggable, selectable and deletable nodes +- pass `nodes` and `edges` as writables +- draggable, selectable and deletable nodes - support for custom `nodeTypes` and `edgeTypes` - basic viewport settings like `fitView`, `minZoom` and `maxZoom` - additional components: ``, `` & `` diff --git a/packages/svelte/package.json b/packages/svelte/package.json index 5ed9f8e9..887b9084 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,6 +1,6 @@ { "name": "@xyflow/svelte", - "version": "0.0.28", + "version": "0.0.29", "description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.", "keywords": [ "svelte", @@ -41,7 +41,7 @@ "access": "public" }, "dependencies": { - "@svelte-put/shortcut": "^3.0.0", + "@svelte-put/shortcut": "^3.1.0", "@xyflow/system": "workspace:*", "classcat": "^5.0.4" }, diff --git a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte index 7a8df68c..aad6d2b1 100644 --- a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte +++ b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte @@ -6,7 +6,7 @@ import { errorMessages, getMarkerId } from '@xyflow/system'; import { useStore } from '$lib/store'; - import BezierEdge from '$lib/components/edges/BezierEdge.svelte'; + import { BezierEdgeInternal } from '$lib/components/edges'; import type { EdgeLayouted, Edge } from '$lib/types'; import { get } from 'svelte/store'; @@ -57,7 +57,7 @@ edgecontextmenu: { edge: Edge; event: MouseEvent }; }>(); - $: edgeComponent = $edgeTypes[type!] || BezierEdge; + $: edgeComponent = $edgeTypes[type!] || BezierEdgeInternal; $: markerStartUrl = markerStart ? `url(#${getMarkerId(markerStart, $flowId)})` : undefined; $: markerEndUrl = markerEnd ? `url(#${getMarkerId(markerEnd, $flowId)})` : undefined; diff --git a/packages/svelte/src/lib/components/Handle/Handle.svelte b/packages/svelte/src/lib/components/Handle/Handle.svelte index 655bcc6b..807594e0 100644 --- a/packages/svelte/src/lib/components/Handle/Handle.svelte +++ b/packages/svelte/src/lib/components/Handle/Handle.svelte @@ -1,17 +1,18 @@ diff --git a/packages/svelte/src/lib/components/KeyHandler/KeyHandler.svelte b/packages/svelte/src/lib/components/KeyHandler/KeyHandler.svelte index f059ff8e..f6697cb2 100644 --- a/packages/svelte/src/lib/components/KeyHandler/KeyHandler.svelte +++ b/packages/svelte/src/lib/components/KeyHandler/KeyHandler.svelte @@ -1,10 +1,10 @@ diff --git a/packages/svelte/src/lib/components/edges/BezierEdgeInternal.svelte b/packages/svelte/src/lib/components/edges/BezierEdgeInternal.svelte new file mode 100644 index 00000000..9431a05d --- /dev/null +++ b/packages/svelte/src/lib/components/edges/BezierEdgeInternal.svelte @@ -0,0 +1,63 @@ + + + diff --git a/packages/svelte/src/lib/components/edges/SmoothStepEdge.svelte b/packages/svelte/src/lib/components/edges/SmoothStepEdge.svelte index 30872b68..3e27368f 100644 --- a/packages/svelte/src/lib/components/edges/SmoothStepEdge.svelte +++ b/packages/svelte/src/lib/components/edges/SmoothStepEdge.svelte @@ -1,30 +1,49 @@ diff --git a/packages/svelte/src/lib/components/edges/SmoothStepEdgeInternal.svelte b/packages/svelte/src/lib/components/edges/SmoothStepEdgeInternal.svelte new file mode 100644 index 00000000..28e87269 --- /dev/null +++ b/packages/svelte/src/lib/components/edges/SmoothStepEdgeInternal.svelte @@ -0,0 +1,63 @@ + + + diff --git a/packages/svelte/src/lib/components/edges/StepEdge.svelte b/packages/svelte/src/lib/components/edges/StepEdge.svelte index 65012595..5ca6dc00 100644 --- a/packages/svelte/src/lib/components/edges/StepEdge.svelte +++ b/packages/svelte/src/lib/components/edges/StepEdge.svelte @@ -1,31 +1,49 @@ diff --git a/packages/svelte/src/lib/components/edges/StepEdgeInternal.svelte b/packages/svelte/src/lib/components/edges/StepEdgeInternal.svelte new file mode 100644 index 00000000..248e5912 --- /dev/null +++ b/packages/svelte/src/lib/components/edges/StepEdgeInternal.svelte @@ -0,0 +1,64 @@ + + + diff --git a/packages/svelte/src/lib/components/edges/StraightEdge.svelte b/packages/svelte/src/lib/components/edges/StraightEdge.svelte index 50f282ff..c868752f 100644 --- a/packages/svelte/src/lib/components/edges/StraightEdge.svelte +++ b/packages/svelte/src/lib/components/edges/StraightEdge.svelte @@ -1,28 +1,42 @@ diff --git a/packages/svelte/src/lib/components/edges/StraightEdgeInternal.svelte b/packages/svelte/src/lib/components/edges/StraightEdgeInternal.svelte new file mode 100644 index 00000000..0d436d52 --- /dev/null +++ b/packages/svelte/src/lib/components/edges/StraightEdgeInternal.svelte @@ -0,0 +1,63 @@ + + + diff --git a/packages/svelte/src/lib/components/edges/index.ts b/packages/svelte/src/lib/components/edges/index.ts index 1f1567b6..4be51363 100644 --- a/packages/svelte/src/lib/components/edges/index.ts +++ b/packages/svelte/src/lib/components/edges/index.ts @@ -1,4 +1,17 @@ +// We distinguish between internal and exported edges +// The internal edges are used directly like custom edges and always get an id, source and target props +// If you import an edge from the library, the id is optional and source and target are not used at all + +// @todo: how can we prevent this duplication in ...Edge/ ...EdgeInternal? +// both are quite similar, it's just about 1-2 props that are different export { default as BezierEdge } from './BezierEdge.svelte'; -export { default as StraightEdge } from './StraightEdge.svelte'; +export { default as BezierEdgeInternal } from './BezierEdgeInternal.svelte'; + export { default as SmoothStepEdge } from './SmoothStepEdge.svelte'; +export { default as SmoothStepEdgeInternal } from './SmoothStepEdgeInternal.svelte'; + +export { default as StraightEdge } from './StraightEdge.svelte'; +export { default as StraightEdgeInternal } from './StraightEdgeInternal.svelte'; + export { default as StepEdge } from './StepEdge.svelte'; +export { default as StepEdgeInternal } from './StepEdgeInternal.svelte'; diff --git a/packages/svelte/src/lib/components/nodes/DefaultNode.svelte b/packages/svelte/src/lib/components/nodes/DefaultNode.svelte index f4fc7270..a0d2a15c 100644 --- a/packages/svelte/src/lib/components/nodes/DefaultNode.svelte +++ b/packages/svelte/src/lib/components/nodes/DefaultNode.svelte @@ -37,6 +37,6 @@ isConnectable; - + {data?.label} - + diff --git a/packages/svelte/src/lib/components/nodes/InputNode.svelte b/packages/svelte/src/lib/components/nodes/InputNode.svelte index aa911d1c..a8a25eb8 100644 --- a/packages/svelte/src/lib/components/nodes/InputNode.svelte +++ b/packages/svelte/src/lib/components/nodes/InputNode.svelte @@ -39,4 +39,4 @@ {data?.label} - + diff --git a/packages/svelte/src/lib/components/nodes/OutputNode.svelte b/packages/svelte/src/lib/components/nodes/OutputNode.svelte index b5ebc2b1..7fff504f 100644 --- a/packages/svelte/src/lib/components/nodes/OutputNode.svelte +++ b/packages/svelte/src/lib/components/nodes/OutputNode.svelte @@ -36,4 +36,4 @@ {data?.label} - + diff --git a/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte b/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte index b4b05a1b..df399a7d 100644 --- a/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte +++ b/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte @@ -83,9 +83,6 @@ on:nodemouseenter on:nodemousemove on:nodemouseleave - on:connectstart - on:connect - on:connectend on:nodedrag on:nodedragstart on:nodedragstop diff --git a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte index 59bff1f4..81aa81bd 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte +++ b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte @@ -71,12 +71,16 @@ export let autoPanOnNodeDrag: $$Props['autoPanOnNodeDrag'] = true; export let onerror: $$Props['onerror'] = undefined; export let ondelete: $$Props['ondelete'] = undefined; + export let onedgecreate: $$Props['onedgecreate'] = undefined; export let attributionPosition: $$Props['attributionPosition'] = undefined; export let proOptions: $$Props['proOptions'] = undefined; export let defaultEdgeOptions: $$Props['defaultEdgeOptions'] = undefined; export let width: $$Props['width'] = undefined; export let height: $$Props['height'] = undefined; export let colorMode: $$Props['colorMode'] = 'light'; + export let onconnect: $$Props['onconnect'] = undefined; + export let onconnectstart: $$Props['onconnectstart'] = undefined; + export let onconnectend: $$Props['onconnectend'] = undefined; export let defaultMarkerColor = '#b1b1b7'; @@ -149,8 +153,12 @@ autoPanOnNodeDrag, onerror, ondelete, + onedgecreate, connectionMode, - nodeDragThreshold + nodeDragThreshold, + onconnect, + onconnectstart, + onconnectend }; updateStoreByKeys(store, updatableProps); @@ -215,9 +223,6 @@ on:nodemouseenter on:nodemousemove on:nodemouseleave - on:connectstart - on:connect - on:connectend on:nodedragstart on:nodedrag on:nodedragstop diff --git a/packages/svelte/src/lib/container/SvelteFlow/types.ts b/packages/svelte/src/lib/container/SvelteFlow/types.ts index daab48e9..99361624 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/types.ts +++ b/packages/svelte/src/lib/container/SvelteFlow/types.ts @@ -15,7 +15,10 @@ import type { ConnectionMode, PanelPosition, ProOptions, - ColorMode + ColorMode, + OnConnect, + OnConnectStart, + OnConnectEnd } from '@xyflow/system'; import type { @@ -26,7 +29,8 @@ import type { EdgeTypes, DefaultEdgeOptions, FitViewOptions, - OnDelete + OnDelete, + OnEdgeCreate } from '$lib/types'; import type { Writable } from 'svelte/store'; @@ -89,4 +93,10 @@ export type SvelteFlowProps = DOMAttributes & { onMoveEnd?: OnMoveEnd; onerror?: OnError; ondelete?: OnDelete; + + onedgecreate?: OnEdgeCreate; + + onconnect?: OnConnect; + onconnectstart?: OnConnectStart; + onconnectend?: OnConnectEnd; }; diff --git a/packages/svelte/src/lib/container/SvelteFlow/utils.ts b/packages/svelte/src/lib/container/SvelteFlow/utils.ts index 9abdb3e9..497ccd14 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/utils.ts +++ b/packages/svelte/src/lib/container/SvelteFlow/utils.ts @@ -64,7 +64,11 @@ export type UpdatableStoreProps = { connectionMode?: UnwrapWritable; onerror?: UnwrapWritable; ondelete?: UnwrapWritable; + onedgecreate?: UnwrapWritable; nodeDragThreshold?: UnwrapWritable; + onconnect?: UnwrapWritable; + onconnectstart?: UnwrapWritable; + onconnectend?: UnwrapWritable; }; export function updateStoreByKeys(store: SvelteFlowStore, keys: UpdatableStoreProps) { diff --git a/packages/svelte/src/lib/hooks/useHandleConnections.ts b/packages/svelte/src/lib/hooks/useHandleConnections.ts new file mode 100644 index 00000000..c8df0dae --- /dev/null +++ b/packages/svelte/src/lib/hooks/useHandleConnections.ts @@ -0,0 +1,30 @@ +import { derived } from 'svelte/store'; +import { areConnectionMapsEqual, type Connection, type HandleType } from '@xyflow/system'; + +import { useStore } from '$lib/store'; + +export type useHandleConnectionsParams = { + nodeId: string; + type: HandleType; + id?: string | null; +}; + +const initialConnections: Connection[] = []; + +export function useHandleConnections({ nodeId, type, id = null }: useHandleConnectionsParams) { + const { edges, connectionLookup } = useStore(); + let prevConnections: Map | undefined = undefined; + + return derived( + [edges, connectionLookup], + ([, connectionLookup], set) => { + const nextConnections = connectionLookup.get(`${nodeId}-${type}-${id || null}`); + + if (!areConnectionMapsEqual(nextConnections, prevConnections)) { + prevConnections = nextConnections; + set(Array.from(prevConnections?.values() || [])); + } + }, + initialConnections + ); +} diff --git a/packages/svelte/src/lib/hooks/useNodesData.ts b/packages/svelte/src/lib/hooks/useNodesData.ts new file mode 100644 index 00000000..f50bc8b8 --- /dev/null +++ b/packages/svelte/src/lib/hooks/useNodesData.ts @@ -0,0 +1,64 @@ +import { derived, type Readable } from 'svelte/store'; + +import type { Node } from '$lib/types'; +import { useStore } from '$lib/store'; + +function areNodesDataEqual(a: Node['data'][] | null, b: Node['data'][] | null) { + if ((!a && !b) || (!a?.length && !b?.length)) { + true; + } + + if (!a || !b || a.length !== b.length) { + return false; + } + + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + return false; + } + } + + return true; +} + +export function useNodesData( + nodeId: string +): Readable; +export function useNodesData( + nodeIds: string[] +): Readable; +export function useNodesData( + nodeIds: string[], + guard: (node: Node) => node is NodeType +): Readable; +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function useNodesData(nodeIds: any): any { + const { nodes, nodeLookup } = useStore(); + let prevNodesData: (Node['data'] | null)[] | null = null; + + return derived([nodes, nodeLookup], ([, nodeLookup], set) => { + let nextNodesData: (Node['data'] | null)[] | null = null; + const nodeIdArray = Array.isArray(nodeIds); + + if (!nodeIdArray) { + nextNodesData = [nodeLookup.get(nodeIds)?.data || null]; + } else { + const data = []; + + for (const nodeId of nodeIds) { + const nodeData = nodeLookup.get(nodeId)?.data; + + if (nodeData) { + data.push(nodeData); + } + } + + nextNodesData = data; + } + + if (!areNodesDataEqual(nextNodesData, prevNodesData)) { + prevNodesData = nextNodesData; + set(nodeIdArray ? nextNodesData : nextNodesData[0]); + } + }); +} diff --git a/packages/svelte/src/lib/hooks/useSvelteFlow.ts b/packages/svelte/src/lib/hooks/useSvelteFlow.ts index fb1419ec..6e66a998 100644 --- a/packages/svelte/src/lib/hooks/useSvelteFlow.ts +++ b/packages/svelte/src/lib/hooks/useSvelteFlow.ts @@ -1,7 +1,5 @@ import { get, type Writable } from 'svelte/store'; import { - getIncomersBase, - getOutgoersBase, getOverlappingArea, isRectObject, nodeToRect, @@ -20,6 +18,7 @@ import { import { useStore } from '$lib/store'; import type { Edge, FitViewOptions, Node } from '$lib/types'; +import { isNode } from '$lib/utils'; export function useSvelteFlow(): { zoomIn: ZoomInOut; @@ -48,9 +47,16 @@ export function useSvelteFlow(): { screenToFlowPosition: (position: XYPosition) => XYPosition; flowToScreenPosition: (position: XYPosition) => XYPosition; viewport: Writable; - getConnectedEdges: (id: string | (Node | { id: Node['id'] })[]) => Edge[]; - getIncomers: (node: string | Node | { id: Node['id'] }) => Node[]; - getOutgoers: (node: string | Node | { id: Node['id'] }) => Node[]; + updateNode: ( + id: string, + nodeUpdate: Partial | ((node: Node) => Partial), + options?: { replace: boolean } + ) => void; + updateNodeData: ( + id: string, + dataUpdate: object | ((node: Node) => object), + options?: { replace: boolean } + ) => void; toObject: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport }; } { const { @@ -84,6 +90,24 @@ export function useSvelteFlow(): { return [nodeRect, node, isRect]; }; + const updateNode = ( + id: string, + nodeUpdate: Partial | ((node: Node) => Partial), + options: { replace: boolean } = { replace: false } + ) => { + nodes.update((nds) => + nds.map((node) => { + if (node.id === id) { + const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node as Node) : nodeUpdate; + + return options.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode }; + } + + return node; + }) + ); + }; + return { zoomIn, zoomOut, @@ -136,12 +160,12 @@ export function useSvelteFlow(): { ) => { const [nodeRect, node, isRect] = getNodeRect(nodeOrRect); - if (!nodeRect || !node) { + if (!nodeRect) { return []; } return (nodesToIntersect || get(nodes)).filter((n) => { - if (!isRect && (n.id === node.id || !n.computed?.positionAbsolute)) { + if (!isRect && (n.id === node!.id || !n.computed?.positionAbsolute)) { return false; } @@ -232,29 +256,6 @@ export function useSvelteFlow(): { y: rendererPosition.y + domY }; }, - getConnectedEdges: (node) => { - const nodeIds = new Set(); - - if (typeof node === 'string') { - nodeIds.add(node); - } else if (node.length >= 1) { - node.forEach((n) => { - nodeIds.add(n.id); - }); - } - - return get(edges).filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target)); - }, - getIncomers: (node) => { - const _node = typeof node === 'string' ? { id: node } : node; - - return getIncomersBase(_node, get(nodes), get(edges)); - }, - getOutgoers: (node) => { - const _node = typeof node === 'string' ? { id: node } : node; - - return getOutgoersBase(_node, get(nodes), get(edges)); - }, toObject: () => { return { nodes: get(nodes).map((node) => ({ @@ -268,6 +269,16 @@ export function useSvelteFlow(): { viewport: { ...get(viewport) } }; }, + updateNode, + updateNodeData: (id, dataUpdate, options) => { + updateNode(id, (node) => { + const nextData = typeof dataUpdate === 'function' ? dataUpdate(node) : dataUpdate; + + return options?.replace + ? { ...node, data: nextData } + : { ...node, data: { ...node.data, ...nextData } }; + }); + }, viewport }; } diff --git a/packages/svelte/src/lib/index.ts b/packages/svelte/src/lib/index.ts index d9e0885f..895793d2 100644 --- a/packages/svelte/src/lib/index.ts +++ b/packages/svelte/src/lib/index.ts @@ -7,7 +7,7 @@ export * from '$lib/container/Panel'; export * from '$lib/components/SvelteFlowProvider'; export * from '$lib/components/EdgeLabelRenderer'; export * from '$lib/components/BaseEdge'; -export * from '$lib/components/edges'; +export { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '$lib/components/edges'; export * from '$lib/components/Handle'; // plugins @@ -27,9 +27,20 @@ export * from '$lib/hooks/useSvelteFlow'; export * from '$lib/hooks/useUpdateNodeInternals'; export * from '$lib/hooks/useConnection'; export * from '$lib/hooks/useNodesEdges'; +export * from '$lib/hooks/useHandleConnections'; +export * from '$lib/hooks/useNodesData'; // types -export type { Edge, EdgeProps, EdgeTypes, DefaultEdgeOptions } from '$lib/types/edges'; +export type { + Edge, + EdgeProps, + BezierEdgeProps, + SmoothStepEdgeProps, + StepEdgeProps, + StraightEdgeProps, + EdgeTypes, + DefaultEdgeOptions +} from '$lib/types/edges'; export type { HandleComponentProps, FitViewOptions } from '$lib/types/general'; export type { Node, NodeTypes, DefaultNodeOptions } from '$lib/types/nodes'; export type { SvelteFlowStore } from '$lib/store/types'; diff --git a/packages/svelte/src/lib/plugins/Background/Background.svelte b/packages/svelte/src/lib/plugins/Background/Background.svelte index 67689ab9..c6a67d48 100644 --- a/packages/svelte/src/lib/plugins/Background/Background.svelte +++ b/packages/svelte/src/lib/plugins/Background/Background.svelte @@ -45,8 +45,8 @@ diff --git a/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte b/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte index 67f93698..7b2896d8 100644 --- a/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte +++ b/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte @@ -94,10 +94,10 @@ viewBox="{x} {y} {viewboxWidth} {viewboxHeight}" role="img" aria-labelledby={labelledBy} - style:--minimap-background-color-props={bgColor} - style:--minimap-mask-color-props={maskColor} - style:--minimap-mask-stroke-color-props={maskStrokeColor} - style:--minimap-mask-stroke-width-props={maskStrokeWidth} + style:--xy-minimap-background-color-props={bgColor} + style:--xy-minimap-mask-color-props={maskColor} + style:--xy-minimap-mask-stroke-color-props={maskStrokeColor} + style:--xy-minimap-mask-stroke-width-props={maskStrokeWidth} use:interactive={{ panZoom: $panZoom, viewport, diff --git a/packages/svelte/src/lib/store/index.ts b/packages/svelte/src/lib/store/index.ts index ec771416..873d9b6f 100644 --- a/packages/svelte/src/lib/store/index.ts +++ b/packages/svelte/src/lib/store/index.ts @@ -192,15 +192,20 @@ export function createStore({ } function unselectNodesAndEdges(params?: { nodes?: Node[]; edges?: Edge[] }) { - const nodeIdsToUnselect = (params?.nodes ? params.nodes : get(store.nodes)).map( - (item) => item.id - ); - const edgeIdsToUnselect = (params?.edges ? params.edges : get(store.edges)).map( - (item) => item.id - ); + const selectedNodeIds = (params?.nodes ? params.nodes : get(store.nodes)) + .filter((node) => node.selected) + .map((node) => node.id); + const selectedEdgeIds = (params?.edges ? params.edges : get(store.edges)) + .filter((edge) => edge.selected) + .map((edge) => edge.id); - store.nodes.update((ns) => ns.map(resetSelectedItem(nodeIdsToUnselect))); - store.edges.update((es) => es.map(resetSelectedItem(edgeIdsToUnselect))); + if (selectedNodeIds.length) { + store.nodes.update((ns) => ns.map(resetSelectedItem(selectedNodeIds))); + } + + if (selectedEdgeIds.length) { + store.edges.update((es) => es.map(resetSelectedItem(selectedEdgeIds))); + } } store.deleteKeyPressed.subscribe((deleteKeyPressed) => { diff --git a/packages/svelte/src/lib/store/initial-store.ts b/packages/svelte/src/lib/store/initial-store.ts index dbbb17fc..0691c202 100644 --- a/packages/svelte/src/lib/store/initial-store.ts +++ b/packages/svelte/src/lib/store/initial-store.ts @@ -17,17 +17,26 @@ import { type Viewport, updateNodes, getNodesBounds, - getViewportForBounds + getViewportForBounds, + updateConnectionLookup, + type ConnectionLookup, + type OnConnect, + type OnConnectStart, + type OnConnectEnd } from '@xyflow/system'; import DefaultNode from '$lib/components/nodes/DefaultNode.svelte'; import InputNode from '$lib/components/nodes/InputNode.svelte'; import OutputNode from '$lib/components/nodes/OutputNode.svelte'; import GroupNode from '$lib/components/nodes/GroupNode.svelte'; -import BezierEdge from '$lib/components/edges/BezierEdge.svelte'; -import StraightEdge from '$lib/components/edges/StraightEdge.svelte'; -import SmoothStepEdge from '$lib/components/edges/SmoothStepEdge.svelte'; -import StepEdge from '$lib/components/edges/StepEdge.svelte'; + +import { + BezierEdgeInternal, + SmoothStepEdgeInternal, + StraightEdgeInternal, + StepEdgeInternal +} from '$lib/components/edges'; + import type { NodeTypes, EdgeTypes, @@ -35,7 +44,8 @@ import type { Node, Edge, FitViewOptions, - OnDelete + OnDelete, + OnEdgeCreate } from '$lib/types'; import { createNodesStore, createEdgesStore } from './utils'; import { initConnectionProps, type ConnectionProps } from './derived-connection-props'; @@ -48,10 +58,10 @@ export const initialNodeTypes = { }; export const initialEdgeTypes = { - straight: StraightEdge, - smoothstep: SmoothStepEdge, - default: BezierEdge, - step: StepEdge + straight: StraightEdgeInternal, + smoothstep: SmoothStepEdgeInternal, + default: BezierEdgeInternal, + step: StepEdgeInternal }; export const getInitialStore = ({ @@ -67,11 +77,12 @@ export const getInitialStore = ({ height?: number; fitView?: boolean; }) => { - const nodeLookup = new Map(); + const nodeLookup = new Map(); const nextNodes = updateNodes(nodes, nodeLookup, { nodeOrigin: [0, 0], elevateNodesOnSelect: false }); + const connectionLookup = updateConnectionLookup(new Map(), edges); let viewport: Viewport = { x: 0, y: 0, zoom: 1 }; @@ -86,8 +97,9 @@ export const getInitialStore = ({ nodes: createNodesStore(nextNodes, nodeLookup), nodeLookup: readable>(nodeLookup), visibleNodes: readable([]), - edges: createEdgesStore(edges), + edges: createEdgesStore(edges, connectionLookup), edgeTree: readable[]>([]), + connectionLookup: readable(connectionLookup), height: writable(500), width: writable(500), minZoom: writable(0.5), @@ -130,6 +142,10 @@ export const getInitialStore = ({ lib: readable('svelte'), onlyRenderVisibleElements: writable(false), onerror: writable(devWarn), - ondelete: writable(undefined) + ondelete: writable(undefined), + onedgecreate: writable(undefined), + onconnect: writable(undefined), + onconnectstart: writable(undefined), + onconnectend: writable(undefined) }; }; diff --git a/packages/svelte/src/lib/store/utils.ts b/packages/svelte/src/lib/store/utils.ts index a11bf1f6..b9292ca7 100644 --- a/packages/svelte/src/lib/store/utils.ts +++ b/packages/svelte/src/lib/store/utils.ts @@ -6,7 +6,13 @@ import { type Writable, get } from 'svelte/store'; -import { updateNodes, type Viewport, type PanZoomInstance } from '@xyflow/system'; +import { + updateNodes, + type Viewport, + type PanZoomInstance, + type ConnectionLookup, + updateConnectionLookup +} from '@xyflow/system'; import type { DefaultEdgeOptions, DefaultNodeOptions, Edge, Node } from '$lib/types'; @@ -168,6 +174,7 @@ export const createNodesStore = ( export const createEdgesStore = ( edges: Edge[], + connectionLookup: ConnectionLookup, defaultOptions?: DefaultEdgeOptions ): Writable & { setDefaultOptions: (opts: DefaultEdgeOptions) => void } => { const { subscribe, set, update } = writable([]); @@ -176,6 +183,9 @@ export const createEdgesStore = ( const _set: typeof set = (eds: Edge[]) => { const nextEdges = defaults ? eds.map((edge) => ({ ...defaults, ...edge })) : eds; + + updateConnectionLookup(connectionLookup, nextEdges); + value = nextEdges; set(value); }; diff --git a/packages/svelte/src/lib/types/edges.ts b/packages/svelte/src/lib/types/edges.ts index 33f0d908..3f1f0e8d 100644 --- a/packages/svelte/src/lib/types/edges.ts +++ b/packages/svelte/src/lib/types/edges.ts @@ -5,7 +5,9 @@ import type { BezierPathOptions, DefaultEdgeOptionsBase, EdgePosition, - SmoothStepPathOptions + SmoothStepPathOptions, + Optional, + StepPathOptions } from '@xyflow/system'; import type { Node } from '$lib/types'; @@ -29,6 +31,7 @@ type BezierEdgeType = DefaultEdge & { type StepEdgeType = DefaultEdge & { type: 'step'; + pathOptions?: StepPathOptions; }; export type Edge = @@ -37,7 +40,7 @@ export type Edge = | BezierEdgeType | StepEdgeType; -export type EdgeProps = Omit & +export type EdgeProps = Omit, 'sourceHandle' | 'targetHandle' | 'type'> & EdgePosition & { markerStart?: string; markerEnd?: string; @@ -45,6 +48,31 @@ export type EdgeProps = Omit & targetHandleId?: string | null; }; +export type EdgeComponentProps = Optional< + Omit< + EdgeProps, + 'source' | 'target' | 'sourceHandleId' | 'targetHandleId' | 'animated' | 'selected' | 'data' + >, + 'id' +>; + +export type BezierEdgeProps = EdgeComponentProps & { + pathOptions?: BezierPathOptions; +}; + +export type SmoothStepEdgeProps = EdgeComponentProps & { + pathOptions?: SmoothStepPathOptions; +}; + +export type StepEdgeProps = EdgeComponentProps & { + pathOptions?: StepPathOptions; +}; + +export type StraightEdgeProps = Omit< + EdgeComponentProps, + 'sourcePosition' | 'targetPosition' +>; + export type EdgeTypes = Record>>; export type DefaultEdgeOptions = Omit, 'focusable'>; diff --git a/packages/svelte/src/lib/types/general.ts b/packages/svelte/src/lib/types/general.ts index 14764318..5d33a97c 100644 --- a/packages/svelte/src/lib/types/general.ts +++ b/packages/svelte/src/lib/types/general.ts @@ -4,7 +4,8 @@ import type { HandleType, Position, XYPosition, - ConnectingHandle + ConnectingHandle, + Connection } from '@xyflow/system'; import type { Node } from './nodes'; @@ -30,8 +31,11 @@ export type HandleComponentProps = { isConnectable?: boolean; isConnectableStart?: boolean; isConnectableEnd?: boolean; + onconnect?: (connections: Connection[]) => void; + ondisconnect?: (connections: Connection[]) => void; }; export type FitViewOptions = FitViewOptionsBase; export type OnDelete = (params: { nodes: Node[]; edges: Edge[] }) => void; +export type OnEdgeCreate = (connection: Connection) => Edge | Connection | void; diff --git a/packages/svelte/src/lib/utils/index.ts b/packages/svelte/src/lib/utils/index.ts index 23b29448..09769c62 100644 --- a/packages/svelte/src/lib/utils/index.ts +++ b/packages/svelte/src/lib/utils/index.ts @@ -10,8 +10,8 @@ import { import type { Edge, Node } from '$lib/types'; -export const isNode = isNodeBase; -export const isEdge = isEdgeBase; +export const isNode = isNodeBase; +export const isEdge = isEdgeBase; export const getOutgoers = getOutgoersBase; export const getIncomers = getIncomersBase; export const addEdge = addEdgeBase; diff --git a/packages/svelte/tsconfig.json b/packages/svelte/tsconfig.json index 8fbe9e82..d9cfe5d6 100644 --- a/packages/svelte/tsconfig.json +++ b/packages/svelte/tsconfig.json @@ -8,7 +8,8 @@ "resolveJsonModule": true, "skipLibCheck": true, "sourceMap": true, - "strict": true + "strict": true, + "noErrorTruncation": true } // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias diff --git a/packages/system/package.json b/packages/system/package.json index 10729670..9eff995d 100644 --- a/packages/system/package.json +++ b/packages/system/package.json @@ -1,6 +1,6 @@ { "name": "@xyflow/system", - "version": "0.0.11", + "version": "0.0.12", "description": "xyflow core system that powers React Flow and Svelte Flow.", "keywords": [ "node-based UI", diff --git a/packages/system/src/styles/base.css b/packages/system/src/styles/base.css index 1cb026b6..3a5afe6c 100644 --- a/packages/system/src/styles/base.css +++ b/packages/system/src/styles/base.css @@ -1,38 +1,38 @@ .xy-flow { - --node-border-default: 1px solid #bbb; - --node-border-selected-default: 1px solid #555; + --xy-node-border-default: 1px solid #bbb; + --xy-node-border-selected-default: 1px solid #555; - --handle-background-color-default: #333; + --xy-handle-background-color-default: #333; - --selection-background-color-default: rgba(150, 150, 180, 0.1); - --selection-border-default: 1px dotted rgba(155, 155, 155, 0.8); + --xy-selection-background-color-default: rgba(150, 150, 180, 0.1); + --xy-selection-border-default: 1px dotted rgba(155, 155, 155, 0.8); } .xy-flow.dark { - --node-color-default: #f8f8f8; + --xy-node-color-default: #f8f8f8; } .xy-flow__handle { - background-color: var(--handle-background-color, var(--handle-background-color-default)); + background-color: var(--xy-handle-background-color, var(--xy-handle-background-color-default)); } .xy-flow__node-input, .xy-flow__node-default, .xy-flow__node-output, .xy-flow__node-group { - border: var(--node-border, var(--node-border-default)); - color: var(--node-color, var(--node-color-default)); + border: var(--xy-node-border, var(--xy-node-border-default)); + color: var(--xy-node-color, var(--xy-node-color-default)); &.selected, &:focus, &:focus-visible { outline: none; - border: var(--node-border-selected, var(--node-border-selected-default)); + border: var(--xy-node-border-selected, var(--xy-node-border-selected-default)); } } .xy-flow__nodesselection-rect, .xy-flow__selection { - background: var(--selection-background-color, var(--selection-background-color-default)); - border: var(--selection-border, var(--selection-border-default)); + background: var(--xy-selection-background-color, var(--xy-selection-background-color-default)); + border: var(--xy-selection-border, var(--xy-selection-border-default)); } diff --git a/packages/system/src/styles/init.css b/packages/system/src/styles/init.css index 474b1322..7b716a9d 100644 --- a/packages/system/src/styles/init.css +++ b/packages/system/src/styles/init.css @@ -1,51 +1,55 @@ /* these are the necessary styles for React/Svelte Flow, they get used by base.css and style.css */ .xy-flow { - --edge-stroke-default: #b1b1b7; - --edge-stroke-width-default: 1; - --edge-stroke-selected-default: #555; + --xy-edge-stroke-default: #b1b1b7; + --xy-edge-stroke-width-default: 1; + --xy-edge-stroke-selected-default: #555; - --connectionline-stroke-default: #b1b1b7; - --connectionline-stroke-width-default: 1; + --xy-connectionline-stroke-default: #b1b1b7; + --xy-connectionline-stroke-width-default: 1; - --attribution-background-color-default: rgba(255, 255, 255, 0.5); + --xy-attribution-background-color-default: rgba(255, 255, 255, 0.5); - --minimap-background-color-default: #fff; - --minimap-mask-background-color-default: rgb(240, 240, 240, 0.6); - --minimap-node-background-color-default: #e2e2e2; - --minimap-node-stroke-color-default: transparent; - --minimap-node-stroke-width-default: 2; + --xy-minimap-background-color-default: #fff; + --xy-minimap-mask-background-color-default: rgb(240, 240, 240, 0.6); + --xy-minimap-node-background-color-default: #e2e2e2; + --xy-minimap-node-stroke-color-default: transparent; + --xy-minimap-node-stroke-width-default: 2; - --background-color-default: transparent; - --background-pattern-dots-color-default: #91919a; - --background-pattern-lines-color-default: #eee; - --background-pattern-cross-color-default: #e2e2e2; + --xy-background-color-default: transparent; + --xy-background-pattern-dots-color-default: #91919a; + --xy-background-pattern-lines-color-default: #eee; + --xy-background-pattern-cross-color-default: #e2e2e2; } .xy-flow.dark { - --edge-stroke-default: #3c3c3c; - --edge-stroke-width-default: 1; - --edge-stroke-selected-default: #727272; + --xy-edge-stroke-default: #3e3e3e; + --xy-edge-stroke-width-default: 1; + --xy-edge-stroke-selected-default: #727272; - --connectionline-stroke-default: #b1b1b7; - --connectionline-stroke-width-default: 1; + --xy-connectionline-stroke-default: #b1b1b7; + --xy-connectionline-stroke-width-default: 1; - --attribution-background-color-default: rgba(150, 150, 150, 0.25); + --xy-attribution-background-color-default: rgba(150, 150, 150, 0.25); - --minimap-background-color-default: #141414; - --minimap-mask-background-color-default: rgb(60, 60, 60, 0.6); - --minimap-node-background-color-default: #2b2b2b; - --minimap-node-stroke-color-default: transparent; - --minimap-node-stroke-width-default: 2; + --xy-minimap-background-color-default: #141414; + --xy-minimap-mask-background-color-default: rgb(60, 60, 60, 0.6); + --xy-minimap-node-background-color-default: #2b2b2b; + --xy-minimap-node-stroke-color-default: transparent; + --xy-minimap-node-stroke-width-default: 2; - --background-color-default: #141414; - --background-pattern-dots-color-default: #777; - --background-pattern-lines-color-default: #777; - --background-pattern-cross-color-default: #777; + --xy-background-color-default: #141414; + --xy-background-pattern-dots-color-default: #777; + --xy-background-pattern-lines-color-default: #777; + --xy-background-pattern-cross-color-default: #777; } .xy-flow { - background-color: var(--background-color-props, var(--background-color-default, 'transparent')); + background-color: var(--xy-background-color, var(--xy-background-color-default)); +} + +.xy-flow__background { + background-color: var(--xy-background-color, var(--xy-background-color-props, var(--xy-background-color-default))); } .xy-flow__container { @@ -94,14 +98,14 @@ } .xy-flow__edge-path { - stroke: var(--edge-stroke, var(--edge-stroke-default)); - stroke-width: var(--edge-stroke-width, var(--edge-stroke-width-default)); + stroke: var(--xy-edge-stroke, var(--xy-edge-stroke-default)); + stroke-width: var(--xy-edge-stroke-width, var(--xy-edge-stroke-width-default)); fill: none; } .xy-flow__connection-path { - stroke: var(--connectionline-stroke, var(--connectionline-stroke-default)); - stroke-width: var(--connectionline-stroke-width, var(--connectionline-stroke-width-default)); + stroke: var(--xy-connectionline-stroke, var(--xy-connectionline-stroke-default)); + stroke-width: var(--xy-connectionline-stroke-width, var(--xy-connectionline-stroke-width-default)); fill: none; } @@ -132,7 +136,7 @@ &.selected .xy-flow__edge-path, &:focus .xy-flow__edge-path, &:focus-visible .xy-flow__edge-path { - stroke: var(--edge-stroke-selected, var(--edge-stroke-selected-default)); + stroke: var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default)); } &-textwrapper { @@ -266,7 +270,7 @@ .xy-flow__attribution { font-size: 10px; - background: var(--attribution-background-color, var(--attribution-background-color-default)); + background: var(--xy-attribution-background-color, var(--xy-attribution-background-color-default)); padding: 2px 3px; margin: 0; @@ -293,27 +297,27 @@ } .xy-flow__minimap { - background: var(--minimap-background-color, var(--minimap-background-color-default)); + background: var(--xy-minimap-background-color, var(--xy-minimap-background-color-default)); &-mask { fill: var( - --minimap-mask-background-color-props, - var(--minimap-mask-background-color, var(--minimap-mask-background-color-default)) + --xy-minimap-mask-background-color-props, + var(--xy-minimap-mask-background-color, var(--xy-minimap-mask-background-color-default)) ); } &-node { fill: var( - --minimap-node-background-color-props, - var(--minimap-node-background-color, var(--minimap-node-background-color-default)) + --xy-minimap-node-background-color-props, + var(--xy-minimap-node-background-color, var(--xy-minimap-node-background-color-default)) ); stroke: var( - --minimap-node-stroke-color-props, - var(--minimap-node-stroke-color, var(--minimap-node-stroke-color-default)) + --xy-minimap-node-stroke-color-props, + var(--xy-minimap-node-stroke-color, var(--xy-minimap-node-stroke-color-default)) ); stroke-width: var( - --minimap-node-stroke-width-props, - var(--minimap-node-stroke-width, var(--minimap-node-stroke-width-default)) + --xy-minimap-node-stroke-width-props, + var(--xy-minimap-node-stroke-width, var(--xy-minimap-node-stroke-width-default)) ); } } @@ -326,22 +330,22 @@ .xy-flow__background-pattern { &.dots { fill: var( - --background-pattern-color-props, - var(--background-pattern-color, var(--background-pattern-dots-color-default)) + --xy-background-pattern-color-props, + var(--xy-background-pattern-color, var(--xy-background-pattern-dots-color-default)) ); } &.lines { stroke: var( - --background-pattern-color-props, - var(--background-pattern-color, var(--background-pattern-lines-color-default)) + --xy-background-pattern-color-props, + var(--xy-background-pattern-color, var(--xy-background-pattern-lines-color-default)) ); } &.cross { stroke: var( - --background-pattern-color-props, - var(--background-pattern-color, var(--background-pattern-cross-color-default)) + --xy-background-pattern-color-props, + var(--xy-background-pattern-color, var(--xy-background-pattern-cross-color-default)) ); } } diff --git a/packages/system/src/styles/style.css b/packages/system/src/styles/style.css index e88cc3f1..880ebaf7 100644 --- a/packages/system/src/styles/style.css +++ b/packages/system/src/styles/style.css @@ -1,46 +1,46 @@ .xy-flow { - --node-color-default: inherit; - --node-border-default: 1px solid #1a192b; - --node-background-color-default: #fff; - --node-group-background-color-default: rgba(240, 240, 240, 0.25); - --node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, 0.08); - --node-boxshadow-selected-default: 0 0 0 0.5px #1a192b; - --node-border-radius-default: 3px; + --xy-node-color-default: inherit; + --xy-node-border-default: 1px solid #1a192b; + --xy-node-background-color-default: #fff; + --xy-node-group-background-color-default: rgba(240, 240, 240, 0.25); + --xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(0, 0, 0, 0.08); + --xy-node-boxshadow-selected-default: 0 0 0 0.5px #1a192b; + --xy-node-border-radius-default: 3px; - --handle-background-color-default: #1a192b; - --handle-border-color-default: #fff; + --xy-handle-background-color-default: #1a192b; + --xy-handle-border-color-default: #fff; - --selection-background-color-default: rgba(0, 89, 220, 0.08); - --selection-border-default: 1px dotted rgba(0, 89, 220, 0.8); + --xy-selection-background-color-default: rgba(0, 89, 220, 0.08); + --xy-selection-border-default: 1px dotted rgba(0, 89, 220, 0.8); - --controls-button-background-color-default: #fefefe; - --controls-button-background-color-hover-default: #f4f4f4; - --controls-button-color-default: inherit; - --controls-button-color-hover-default: inherit; - --controls-button-border-color-default: #eee; - --controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08); + --xy-controls-button-background-color-default: #fefefe; + --xy-controls-button-background-color-hover-default: #f4f4f4; + --xy-controls-button-color-default: inherit; + --xy-controls-button-color-hover-default: inherit; + --xy-controls-button-border-color-default: #eee; + --xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08); } .xy-flow.dark { - --node-color-default: #f8f8f8; - --node-border-default: 1px solid #3c3c3c; - --node-background-color-default: #1e1e1e; - --node-group-background-color-default: rgba(240, 240, 240, 0.25); - --node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, 0.08); - --node-boxshadow-selected-default: 0 0 0 0.5px #999; + --xy-node-color-default: #f8f8f8; + --xy-node-border-default: 1px solid #3c3c3c; + --xy-node-background-color-default: #1e1e1e; + --xy-node-group-background-color-default: rgba(240, 240, 240, 0.25); + --xy-node-boxshadow-hover-default: 0 1px 4px 1px rgba(255, 255, 255, 0.08); + --xy-node-boxshadow-selected-default: 0 0 0 0.5px #999; - --handle-background-color-default: #bebebe; - --handle-border-color-default: #1e1e1e; + --xy-handle-background-color-default: #bebebe; + --xy-handle-border-color-default: #1e1e1e; - --selection-background-color-default: rgba(200, 200, 220, 0.08); - --selection-border-default: 1px dotted rgba(200, 200, 220, 0.8); + --xy-selection-background-color-default: rgba(200, 200, 220, 0.08); + --xy-selection-border-default: 1px dotted rgba(200, 200, 220, 0.8); - --controls-button-background-color-default: #2b2b2b; - --controls-button-background-color-hover-default: #3e3e3e; - --controls-button-color-default: #f8f8f8; - --controls-button-color-hover-default: #fff; - --controls-button-border-color-default: #5b5b5b; - --controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08); + --xy-controls-button-background-color-default: #2b2b2b; + --xy-controls-button-background-color-hover-default: #3e3e3e; + --xy-controls-button-color-default: #f8f8f8; + --xy-controls-button-color-hover-default: #fff; + --xy-controls-button-border-color-default: #5b5b5b; + --xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08); } .xy-flow__edge { @@ -67,35 +67,35 @@ .xy-flow__node-output, .xy-flow__node-group { padding: 10px; - border-radius: var(--node-border-radius, var(--node-border-radius-default)); + border-radius: var(--xy-node-border-radius, var(--xy-node-border-radius-default)); width: 150px; font-size: 12px; - color: var(--node-color, var(--node-color-default)); + color: var(--xy-node-color, var(--xy-node-color-default)); text-align: center; - border: var(--node-border, var(--node-border-default)); - background-color: var(--node-background-color, var(--node-background-color-default)); + border: var(--xy-node-border, var(--xy-node-border-default)); + background-color: var(--xy-node-background-color, var(--xy-node-background-color-default)); &.selectable { &:hover { - box-shadow: var(--node-boxshadow-hover, var(--node-boxshadow-hover-default)); + box-shadow: var(--xy-node-boxshadow-hover, var(--xy-node-boxshadow-hover-default)); } &.selected, &:focus, &:focus-visible { - box-shadow: var(--node-boxshadow-selected, var(--node-boxshadow-selected-default)); + box-shadow: var(--xy-node-boxshadow-selected, var(--xy-node-boxshadow-selected-default)); } } } .xy-flow__node-group { - background-color: var(--node-group-background-color, var(--node-group-background-color-default)); + background-color: var(--xy-node-group-background-color, var(--xy-node-group-background-color-default)); } .xy-flow__nodesselection-rect, .xy-flow__selection { - background: var(--selection-background-color, var(--selection-background-color-default)); - border: var(--selection-border, var(--selection-border-default)); + background: var(--xy-selection-background-color, var(--xy-selection-background-color-default)); + border: var(--xy-selection-border, var(--xy-selection-border-default)); &:focus, &:focus-visible { @@ -106,34 +106,37 @@ .xy-flow__handle { width: 6px; height: 6px; - background-color: var(--handle-background-color, var(--handle-background-color-default)); - border: 1px solid var(--handle-border-color, var(--handle-border-color-default)); + background-color: var(--xy-handle-background-color, var(--xy-handle-background-color-default)); + border: 1px solid var(--xy-handle-border-color, var(--xy-handle-border-color-default)); border-radius: 100%; } .xy-flow__controls { - box-shadow: var(--controls-box-shadow, var(--controls-box-shadow-default)); + box-shadow: var(--xy-controls-box-shadow, var(--xy-controls-box-shadow-default)); &-button { border: none; - background: var(--controls-button-background-color, var(--controls-button-background-color-default)); + background: var(--xy-controls-button-background-color, var(--xy-controls-button-background-color-default)); border-bottom: 1px solid var( - --controls-button-border-color-props, - var(--controls-button-border-color, var(--controls-button-border-color-default)) + --xy-controls-button-border-color-props, + var(--xy-controls-button-border-color, var(--xy-controls-button-border-color-default)) ); - color: var(--controls-button-color-props, var(--controls-button-color, var(--controls-button-color-default))); + color: var( + --xy-controls-button-color-props, + var(--xy-controls-button-color, var(--xy-controls-button-color-default)) + ); cursor: pointer; user-select: none; &:hover { background: var( - --controls-button-background-color-hover-props, - var(--controls-button-background-color-hover, var(--controls-button-background-color-hover-default)) + --xy-controls-button-background-color-hover-props, + var(--xy-controls-button-background-color-hover, var(--xy-controls-button-background-color-hover-default)) ); color: var( - --controls-button-color-hover-props, - var(--controls-button-color-hover, var(--controls-button-color-hover-default)) + --xy-controls-button-color-hover-props, + var(--xy-controls-button-color-hover, var(--xy-controls-button-color-hover-default)) ); } diff --git a/packages/system/src/types/edges.ts b/packages/system/src/types/edges.ts index 855682d2..65c8e641 100644 --- a/packages/system/src/types/edges.ts +++ b/packages/system/src/types/edges.ts @@ -27,6 +27,10 @@ export type SmoothStepPathOptions = { borderRadius?: number; }; +export type StepPathOptions = { + offset?: number; +}; + export type BezierPathOptions = { curvature?: number; }; diff --git a/packages/system/src/types/general.ts b/packages/system/src/types/general.ts index 22ad889e..1591cca4 100644 --- a/packages/system/src/types/general.ts +++ b/packages/system/src/types/general.ts @@ -22,8 +22,8 @@ export type SetCenter = (x: number, y: number, options?: SetCenterOptions) => vo export type FitBounds = (bounds: Rect, options?: FitBoundsOptions) => void; export type Connection = { - source: string | null; - target: string | null; + source: string; + target: string; sourceHandle: string | null; targetHandle: string | null; }; @@ -139,3 +139,5 @@ export type UpdateConnection = (params: { export type ColorModeClass = 'light' | 'dark'; export type ColorMode = ColorModeClass | 'system'; + +export type ConnectionLookup = Map>; diff --git a/packages/system/src/types/utils.ts b/packages/system/src/types/utils.ts index 908e95f1..6ebb0744 100644 --- a/packages/system/src/types/utils.ts +++ b/packages/system/src/types/utils.ts @@ -1,3 +1,5 @@ +import type { Optional } from '../utils/types'; + export enum Position { Left = 'left', Top = 'top', diff --git a/packages/system/src/utils/connections.ts b/packages/system/src/utils/connections.ts new file mode 100644 index 00000000..ded405c3 --- /dev/null +++ b/packages/system/src/utils/connections.ts @@ -0,0 +1,53 @@ +import { Connection } from '../types'; + +/** + * @internal + */ +export function areConnectionMapsEqual(a?: Map, b?: Map) { + if (!a && !b) { + return true; + } + + if (!a || !b || a.size !== b.size) { + return false; + } + + if (!a.size && !b.size) { + return true; + } + + for (const key of a.keys()) { + if (!b.has(key)) { + return false; + } + } + + return true; +} + +/** + * We call the callback for all connections in a that are not in b + * + * @internal + */ +export function handleConnectionChange( + a: Map, + b: Map, + cb?: (diff: Connection[]) => void +) { + if (!cb) { + return; + } + + const diff: Connection[] = []; + + a.forEach((connection, key) => { + if (!b?.has(key)) { + diff.push(connection); + } + }); + + if (diff.length) { + cb(diff); + } +} diff --git a/packages/system/src/utils/edges/general.ts b/packages/system/src/utils/edges/general.ts index bdffbbd8..9ffd8e24 100644 --- a/packages/system/src/utils/edges/general.ts +++ b/packages/system/src/utils/edges/general.ts @@ -112,7 +112,7 @@ export function isEdgeVisible({ sourceNode, targetNode, width, height, transform } const getEdgeId = ({ source, sourceHandle, target, targetHandle }: Connection | EdgeBase): string => - `xyflow__edge-${source}${sourceHandle || ''}-${target}${targetHandle || ''}`; + `xy-edge__${source}${sourceHandle || ''}-${target}${targetHandle || ''}`; const connectionExists = (edge: EdgeBase, edges: EdgeBase[]) => { return edges.some( @@ -148,6 +148,14 @@ export const addEdgeBase = ( return edges; } + if (edge.sourceHandle === null) { + delete edge.sourceHandle; + } + + if (edge.targetHandle === null) { + delete edge.targetHandle; + } + return edges.concat(edge); }; diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index 6a946a10..df815510 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -11,7 +11,6 @@ import { getViewportForBounds, } from './general'; import { - type Connection, type Transform, type XYPosition, type Rect, @@ -26,13 +25,11 @@ import { } from '../types'; import { errorMessages } from '../constants'; -export const isEdgeBase = ( - element: NodeType | Connection | EdgeType -): element is EdgeType => 'id' in element && 'source' in element && 'target' in element; +export const isEdgeBase = (element: any): element is EdgeType => + 'id' in element && 'source' in element && 'target' in element; -export const isNodeBase = ( - element: NodeType | Connection | EdgeType -): element is NodeType => 'id' in element && !('source' in element) && !('target' in element); +export const isNodeBase = (element: any): element is NodeType => + 'id' in element && !('source' in element) && !('target' in element); export const getOutgoersBase = ( node: NodeType | { id: string }, diff --git a/packages/system/src/utils/index.ts b/packages/system/src/utils/index.ts index 380b7658..cd66e951 100644 --- a/packages/system/src/utils/index.ts +++ b/packages/system/src/utils/index.ts @@ -1,3 +1,4 @@ +export * from './connections'; export * from './dom'; export * from './edges'; export * from './graph'; @@ -5,3 +6,4 @@ export * from './general'; export * from './marker'; export * from './node-toolbar'; export * from './store'; +export * from './types'; diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index eca6b8de..86434a72 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -9,6 +9,8 @@ import { Transform, XYPosition, XYZPosition, + ConnectionLookup, + EdgeBase, } from '../types'; import { getDimensions, getHandleBounds } from './dom'; import { isNumeric } from './general'; @@ -71,11 +73,13 @@ export function updateNodes( defaults: {}, } ): NodeType[] { + const tmpLookup = new Map(nodeLookup); + nodeLookup.clear(); const parentNodes: ParentNodes = {}; const selectedNodeZ: number = options?.elevateNodesOnSelect ? 1000 : 0; const nextNodes = nodes.map((n) => { - const currentStoreNode = nodeLookup.get(n.id); + const currentStoreNode = tmpLookup.get(n.id); const node: NodeType = { ...options.defaults, ...n, @@ -233,3 +237,23 @@ export function panBy({ return transformChanged; } + +export function updateConnectionLookup(lookup: ConnectionLookup, edges: EdgeBase[]) { + lookup.clear(); + + edges.forEach(({ source, target, sourceHandle = null, targetHandle = null }) => { + if (source && target) { + const sourceKey = `${source}-source-${sourceHandle}`; + const targetKey = `${target}-target-${targetHandle}`; + + const prevSource = lookup.get(sourceKey) || new Map(); + const prevTarget = lookup.get(targetKey) || new Map(); + const connection = { source, target, sourceHandle, targetHandle }; + + lookup.set(sourceKey, prevSource.set(`${target}-${targetHandle}`, connection)); + lookup.set(targetKey, prevTarget.set(`${source}-${sourceHandle}`, connection)); + } + }); + + return lookup; +} diff --git a/packages/system/src/xyhandle/XYHandle.ts b/packages/system/src/xyhandle/XYHandle.ts index 6ec86528..826a1c80 100644 --- a/packages/system/src/xyhandle/XYHandle.ts +++ b/packages/system/src/xyhandle/XYHandle.ts @@ -58,12 +58,10 @@ export type XYHandleInstance = { type Result = { handleDomNode: Element | null; isValid: boolean; - connection: Connection; + connection: Connection | null; endHandle: ConnectingHandle | null; }; -const nullConnection: Connection = { source: null, target: null, sourceHandle: null, targetHandle: null }; - const alwaysValid = () => true; let connectionStartHandle: ConnectingHandle | null = null; @@ -197,7 +195,7 @@ function onPointerDown( return resetRecentHandle(prevActiveHandle, lib); } - if (connection.source !== connection.target && handleDomNode) { + if (connection?.source !== connection?.target && handleDomNode) { resetRecentHandle(prevActiveHandle, lib); prevActiveHandle = handleDomNode; handleDomNode.classList.add('connecting', `${lib}-flow__handle-connecting`); @@ -269,7 +267,7 @@ function isValidHandle( const result: Result = { handleDomNode: handleToCheck, isValid: false, - connection: nullConnection, + connection: null, endHandle: null, }; @@ -280,6 +278,10 @@ function isValidHandle( const connectable = handleToCheck.classList.contains('connectable'); const connectableEnd = handleToCheck.classList.contains('connectableend'); + if (!handleNodeId) { + return result; + } + const connection: Connection = { source: isTarget ? handleNodeId : fromNodeId, sourceHandle: isTarget ? handleId : fromHandleId, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05a5637d..0286010e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,10 +61,10 @@ importers: dependencies: '@astrojs/react': specifier: ^3.0.2 - version: registry.npmjs.org/@astrojs/react@3.0.2(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0)(vite@4.5.0) + version: registry.npmjs.org/@astrojs/react@3.0.2(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0)(vite@4.5.1) '@astrojs/svelte': specifier: ^4.0.2 - version: registry.npmjs.org/@astrojs/svelte@4.0.2(astro@3.2.2)(svelte@4.2.1)(typescript@5.2.2)(vite@4.5.0) + version: registry.npmjs.org/@astrojs/svelte@4.0.2(astro@3.2.2)(svelte@4.2.1)(typescript@5.3.3)(vite@4.5.1) '@types/react': specifier: ^18.2.24 version: registry.npmjs.org/@types/react@18.2.24 @@ -291,8 +291,8 @@ importers: packages/svelte: dependencies: '@svelte-put/shortcut': - specifier: ^3.0.0 - version: registry.npmjs.org/@svelte-put/shortcut@3.0.0 + specifier: ^3.1.0 + version: registry.npmjs.org/@svelte-put/shortcut@3.1.0 '@xyflow/system': specifier: workspace:* version: link:../system @@ -305,7 +305,7 @@ importers: version: registry.npmjs.org/@sveltejs/adapter-auto@2.1.0(@sveltejs/kit@1.22.6) '@sveltejs/kit': specifier: ^1.22.6 - version: registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.0) + version: registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.1) '@sveltejs/package': specifier: ^2.2.1 version: registry.npmjs.org/@sveltejs/package@2.2.1(svelte@4.2.1)(typescript@5.1.3) @@ -417,7 +417,7 @@ importers: dependencies: '@playwright/experimental-ct-react': specifier: ^1.39.0 - version: registry.npmjs.org/@playwright/experimental-ct-react@1.39.0(@types/node@18.7.16)(vite@4.5.0) + version: registry.npmjs.org/@playwright/experimental-ct-react@1.39.0(@types/node@18.7.16)(vite@4.5.1) '@types/react': specifier: ^18.2.31 version: registry.npmjs.org/@types/react@18.2.31 @@ -551,7 +551,7 @@ packages: prismjs: registry.npmjs.org/prismjs@1.29.0 dev: false - registry.npmjs.org/@astrojs/react@3.0.2(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0)(vite@4.5.0): + registry.npmjs.org/@astrojs/react@3.0.2(@types/react-dom@18.2.8)(@types/react@18.2.24)(react-dom@18.2.0)(react@18.2.0)(vite@4.5.1): resolution: {integrity: sha512-aooNIuQxTg+IWGMZPuIEwBeBi4/TCPCMsr3714zuLjAjukVd5ZrX/bCNxJqDWU4HNwUm4XFU1OhcEvYOHa5uMQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@astrojs/react/-/react-3.0.2.tgz} id: registry.npmjs.org/@astrojs/react/3.0.2 name: '@astrojs/react' @@ -565,7 +565,7 @@ packages: dependencies: '@types/react': registry.npmjs.org/@types/react@18.2.24 '@types/react-dom': registry.npmjs.org/@types/react-dom@18.2.8 - '@vitejs/plugin-react': registry.npmjs.org/@vitejs/plugin-react@4.1.1(vite@4.5.0) + '@vitejs/plugin-react': registry.npmjs.org/@vitejs/plugin-react@4.1.1(vite@4.5.1) react: registry.npmjs.org/react@18.2.0 react-dom: registry.npmjs.org/react-dom@18.2.0(react@18.2.0) ultrahtml: registry.npmjs.org/ultrahtml@1.5.2 @@ -574,7 +574,7 @@ packages: - vite dev: false - registry.npmjs.org/@astrojs/svelte@4.0.2(astro@3.2.2)(svelte@4.2.1)(typescript@5.2.2)(vite@4.5.0): + registry.npmjs.org/@astrojs/svelte@4.0.2(astro@3.2.2)(svelte@4.2.1)(typescript@5.3.3)(vite@4.5.1): resolution: {integrity: sha512-XB9Sexq+iW5aUpctDk6zuKHbWIAuPlAnZKbfgaS9VOaOUtx1t12crP9YoKEVcTifXYgoRuWWfeEAm2I8pYlLIQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@astrojs/svelte/-/svelte-4.0.2.tgz} id: registry.npmjs.org/@astrojs/svelte/4.0.2 name: '@astrojs/svelte' @@ -584,10 +584,10 @@ packages: astro: ^3.0.11 svelte: ^3.55.0 || ^4.0.0 dependencies: - '@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.0) + '@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.1) astro: registry.npmjs.org/astro@3.2.2 svelte: registry.npmjs.org/svelte@4.2.1 - svelte2tsx: registry.npmjs.org/svelte2tsx@0.6.23(svelte@4.2.1)(typescript@5.2.2) + svelte2tsx: registry.npmjs.org/svelte2tsx@0.6.23(svelte@4.2.1)(typescript@5.3.3) transitivePeerDependencies: - supports-color - typescript @@ -1938,7 +1938,7 @@ packages: - terser dev: false - registry.npmjs.org/@playwright/experimental-ct-react@1.39.0(@types/node@18.7.16)(vite@4.5.0): + registry.npmjs.org/@playwright/experimental-ct-react@1.39.0(@types/node@18.7.16)(vite@4.5.1): resolution: {integrity: sha512-6GaDVVo6x8P9Jdw3yPbVaEP59nesRsETn3kznHAy+2493VsP3IaVATat2G28z0Sivf/K9Tkh5xRYjiawN0ebgw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@playwright/experimental-ct-react/-/experimental-ct-react-1.39.0.tgz} id: registry.npmjs.org/@playwright/experimental-ct-react/1.39.0 name: '@playwright/experimental-ct-react' @@ -1947,7 +1947,7 @@ packages: hasBin: true dependencies: '@playwright/experimental-ct-core': registry.npmjs.org/@playwright/experimental-ct-core@1.39.0(@types/node@18.7.16) - '@vitejs/plugin-react': registry.npmjs.org/@vitejs/plugin-react@4.1.1(vite@4.5.0) + '@vitejs/plugin-react': registry.npmjs.org/@vitejs/plugin-react@4.1.1(vite@4.5.1) transitivePeerDependencies: - '@types/node' - less @@ -2120,10 +2120,10 @@ packages: version: 2.0.0 dev: true - registry.npmjs.org/@svelte-put/shortcut@3.0.0: - resolution: {integrity: sha512-nZg3pwpTi9wUsvQPlqOzEsxZcF2jmY5j+VBq/20IUjjd2OpM92XqZAga0PCCjE6OuEobOt58UMnC2QZgOvk0tQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@svelte-put/shortcut/-/shortcut-3.0.0.tgz} + registry.npmjs.org/@svelte-put/shortcut@3.1.0: + resolution: {integrity: sha512-EWMEDkZ0+O3yMhb9yrqe5UYisV9CNRKX6Pl/JW3x62t74CiN+3COu1L9NzZUG0omagc2Z3J14PZNYxs77IC9NA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@svelte-put/shortcut/-/shortcut-3.1.0.tgz} name: '@svelte-put/shortcut' - version: 3.0.0 + version: 3.1.0 dev: false registry.npmjs.org/@sveltejs/adapter-auto@2.1.0(@sveltejs/kit@1.22.6): @@ -2134,7 +2134,7 @@ packages: peerDependencies: '@sveltejs/kit': ^1.0.0 dependencies: - '@sveltejs/kit': registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.0) + '@sveltejs/kit': registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.1) import-meta-resolve: registry.npmjs.org/import-meta-resolve@3.0.0 dev: true @@ -2150,7 +2150,7 @@ packages: import-meta-resolve: registry.npmjs.org/import-meta-resolve@4.0.0 dev: true - registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.0): + registry.npmjs.org/@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.1): resolution: {integrity: sha512-SDKxI/QpsReCwIn5czjT53fKlPBybbmMk67d317gUqfeORroBAFN1Z6s/x0E1JYi+04i7kKllS+Sz9wVfmUkAQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@sveltejs/kit/-/kit-1.22.6.tgz} id: registry.npmjs.org/@sveltejs/kit/1.22.6 name: '@sveltejs/kit' @@ -2162,7 +2162,7 @@ packages: svelte: ^3.54.0 || ^4.0.0-next.0 vite: ^4.0.0 dependencies: - '@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.0) + '@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.1) '@types/cookie': registry.npmjs.org/@types/cookie@0.5.3 cookie: registry.npmjs.org/cookie@0.5.0 devalue: registry.npmjs.org/devalue@4.3.2 @@ -2175,7 +2175,7 @@ packages: sirv: registry.npmjs.org/sirv@2.0.3 svelte: registry.npmjs.org/svelte@4.2.1 undici: registry.npmjs.org/undici@5.23.0 - vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16) + vite: registry.npmjs.org/vite@4.5.1(@types/node@18.7.16) transitivePeerDependencies: - supports-color dev: true @@ -2231,7 +2231,7 @@ packages: - typescript dev: true - registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.5.0): + registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.5.1): resolution: {integrity: sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/-/vite-plugin-svelte-inspector-1.0.4.tgz} id: registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector/1.0.4 name: '@sveltejs/vite-plugin-svelte-inspector' @@ -2242,10 +2242,10 @@ packages: svelte: ^3.54.0 || ^4.0.0 vite: ^4.0.0 dependencies: - '@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.0) + '@sveltejs/vite-plugin-svelte': registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.1) debug: registry.npmjs.org/debug@4.3.4(supports-color@8.1.1) svelte: registry.npmjs.org/svelte@4.2.1 - vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16) + vite: registry.npmjs.org/vite@4.5.1(@types/node@18.7.16) transitivePeerDependencies: - supports-color @@ -2268,7 +2268,7 @@ packages: - supports-color dev: true - registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.0): + registry.npmjs.org/@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.1): resolution: {integrity: sha512-zO79p0+DZnXPnF0ltIigWDx/ux7Ni+HRaFOw720Qeivc1azFUrJxTl0OryXVibYNx1hCboGia1NRV3x8RNv4cA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.4.6.tgz} id: registry.npmjs.org/@sveltejs/vite-plugin-svelte/2.4.6 name: '@sveltejs/vite-plugin-svelte' @@ -2278,15 +2278,15 @@ packages: svelte: ^3.54.0 || ^4.0.0 vite: ^4.0.0 dependencies: - '@sveltejs/vite-plugin-svelte-inspector': registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.5.0) + '@sveltejs/vite-plugin-svelte-inspector': registry.npmjs.org/@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.5.1) debug: registry.npmjs.org/debug@4.3.4(supports-color@8.1.1) deepmerge: registry.npmjs.org/deepmerge@4.3.1 kleur: registry.npmjs.org/kleur@4.1.5 magic-string: registry.npmjs.org/magic-string@0.30.5 svelte: registry.npmjs.org/svelte@4.2.1 svelte-hmr: registry.npmjs.org/svelte-hmr@0.15.3(svelte@4.2.1) - vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16) - vitefu: registry.npmjs.org/vitefu@0.2.5(vite@4.5.0) + vite: registry.npmjs.org/vite@4.5.1(@types/node@18.7.16) + vitefu: registry.npmjs.org/vitefu@0.2.5(vite@4.5.1) transitivePeerDependencies: - supports-color @@ -2972,6 +2972,12 @@ packages: version: 7.5.4 dev: true + registry.npmjs.org/@types/semver@7.5.6: + resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/semver/-/semver-7.5.6.tgz} + name: '@types/semver' + version: 7.5.6 + dev: true + registry.npmjs.org/@types/sinonjs__fake-timers@8.1.1: resolution: {integrity: sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz} name: '@types/sinonjs__fake-timers' @@ -2996,10 +3002,10 @@ packages: version: 3.0.1 dev: false - registry.npmjs.org/@types/yauzl@2.10.2: - resolution: {integrity: sha512-Km7XAtUIduROw7QPgvcft0lIupeG8a8rdKL8RiSyKvlE7dYY31fEn41HVuQsRFDuROA8tA4K2UVL+WdfFmErBA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.2.tgz} + registry.npmjs.org/@types/yauzl@2.10.3: + resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz} name: '@types/yauzl' - version: 2.10.2 + version: 2.10.3 requiresBuild: true dependencies: '@types/node': registry.npmjs.org/@types/node@18.7.16 @@ -3092,7 +3098,7 @@ packages: debug: registry.npmjs.org/debug@4.3.4(supports-color@8.1.1) eslint: registry.npmjs.org/eslint@8.43.0 graphemer: registry.npmjs.org/graphemer@1.4.0 - ignore: registry.npmjs.org/ignore@5.2.4 + ignore: registry.npmjs.org/ignore@5.3.0 natural-compare: registry.npmjs.org/natural-compare@1.4.0 semver: registry.npmjs.org/semver@7.5.4 ts-api-utils: registry.npmjs.org/ts-api-utils@1.0.3(typescript@5.1.3) @@ -3398,7 +3404,7 @@ packages: dependencies: '@eslint-community/eslint-utils': registry.npmjs.org/@eslint-community/eslint-utils@4.4.0(eslint@8.53.0) '@types/json-schema': registry.npmjs.org/@types/json-schema@7.0.15 - '@types/semver': registry.npmjs.org/@types/semver@7.5.4 + '@types/semver': registry.npmjs.org/@types/semver@7.5.6 '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager@6.10.0 '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types@6.10.0 '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree@6.10.0(typescript@5.2.2) @@ -3420,7 +3426,7 @@ packages: dependencies: '@eslint-community/eslint-utils': registry.npmjs.org/@eslint-community/eslint-utils@4.4.0(eslint@8.43.0) '@types/json-schema': registry.npmjs.org/@types/json-schema@7.0.15 - '@types/semver': registry.npmjs.org/@types/semver@7.5.4 + '@types/semver': registry.npmjs.org/@types/semver@7.5.6 '@typescript-eslint/scope-manager': registry.npmjs.org/@typescript-eslint/scope-manager@6.8.0 '@typescript-eslint/types': registry.npmjs.org/@typescript-eslint/types@6.8.0 '@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree@6.8.0(typescript@5.1.3) @@ -3498,6 +3504,26 @@ packages: vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16) transitivePeerDependencies: - supports-color + dev: true + + registry.npmjs.org/@vitejs/plugin-react@4.1.1(vite@4.5.1): + resolution: {integrity: sha512-Jie2HERK+uh27e+ORXXwEP5h0Y2lS9T2PRGbfebiHGlwzDO0dEnd2aNtOR/qjBlPb1YgxwAONeblL1xqLikLag==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.1.1.tgz} + id: registry.npmjs.org/@vitejs/plugin-react/4.1.1 + name: '@vitejs/plugin-react' + version: 4.1.1 + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 + dependencies: + '@babel/core': registry.npmjs.org/@babel/core@7.23.2 + '@babel/plugin-transform-react-jsx-self': registry.npmjs.org/@babel/plugin-transform-react-jsx-self@7.22.5(@babel/core@7.23.2) + '@babel/plugin-transform-react-jsx-source': registry.npmjs.org/@babel/plugin-transform-react-jsx-source@7.22.5(@babel/core@7.23.2) + '@types/babel__core': registry.npmjs.org/@types/babel__core@7.20.4 + react-refresh: registry.npmjs.org/react-refresh@0.14.0 + vite: registry.npmjs.org/vite@4.5.1(@types/node@18.7.16) + transitivePeerDependencies: + - supports-color + dev: false registry.npmjs.org/acorn-jsx@5.3.2(acorn@8.10.0): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz} @@ -5313,7 +5339,7 @@ packages: is-weakref: registry.npmjs.org/is-weakref@1.0.2 object-inspect: registry.npmjs.org/object-inspect@1.13.1 object-keys: registry.npmjs.org/object-keys@1.1.1 - object.assign: registry.npmjs.org/object.assign@4.1.4 + object.assign: registry.npmjs.org/object.assign@4.1.5 regexp.prototype.flags: registry.npmjs.org/regexp.prototype.flags@1.5.1 safe-array-concat: registry.npmjs.org/safe-array-concat@1.0.1 safe-regex-test: registry.npmjs.org/safe-regex-test@1.0.0 @@ -5985,7 +6011,7 @@ packages: get-stream: registry.npmjs.org/get-stream@5.2.0 yauzl: registry.npmjs.org/yauzl@2.10.0 optionalDependencies: - '@types/yauzl': registry.npmjs.org/@types/yauzl@2.10.2 + '@types/yauzl': registry.npmjs.org/@types/yauzl@2.10.3 transitivePeerDependencies: - supports-color dev: true @@ -6487,7 +6513,7 @@ packages: array-union: registry.npmjs.org/array-union@2.1.0 dir-glob: registry.npmjs.org/dir-glob@3.0.1 fast-glob: registry.npmjs.org/fast-glob@3.3.2 - ignore: registry.npmjs.org/ignore@5.2.4 + ignore: registry.npmjs.org/ignore@5.3.0 merge2: registry.npmjs.org/merge2@1.4.1 slash: registry.npmjs.org/slash@3.0.0 dev: true @@ -6500,7 +6526,7 @@ packages: dependencies: dir-glob: registry.npmjs.org/dir-glob@3.0.1 fast-glob: registry.npmjs.org/fast-glob@3.3.2 - ignore: registry.npmjs.org/ignore@5.2.4 + ignore: registry.npmjs.org/ignore@5.3.0 merge2: registry.npmjs.org/merge2@1.4.1 slash: registry.npmjs.org/slash@4.0.0 dev: true @@ -6791,6 +6817,13 @@ packages: engines: {node: '>= 4'} dev: true + registry.npmjs.org/ignore@5.3.0: + resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz} + name: ignore + version: 5.3.0 + engines: {node: '>= 4'} + dev: true + registry.npmjs.org/immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz} name: immediate @@ -7431,7 +7464,7 @@ packages: dependencies: array-includes: registry.npmjs.org/array-includes@3.1.7 array.prototype.flat: registry.npmjs.org/array.prototype.flat@1.3.2 - object.assign: registry.npmjs.org/object.assign@4.1.4 + object.assign: registry.npmjs.org/object.assign@4.1.5 object.values: registry.npmjs.org/object.values@1.1.7 dev: true @@ -8560,10 +8593,10 @@ packages: engines: {node: '>= 0.4'} dev: true - registry.npmjs.org/object.assign@4.1.4: - resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz} + registry.npmjs.org/object.assign@4.1.5: + resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz} name: object.assign - version: 4.1.4 + version: 4.1.5 engines: {node: '>= 0.4'} dependencies: call-bind: registry.npmjs.org/call-bind@1.0.5 @@ -9487,6 +9520,16 @@ packages: picocolors: registry.npmjs.org/picocolors@1.0.0 source-map-js: registry.npmjs.org/source-map-js@1.0.2 + registry.npmjs.org/postcss@8.4.32: + resolution: {integrity: sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz} + name: postcss + version: 8.4.32 + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: registry.npmjs.org/nanoid@3.3.7 + picocolors: registry.npmjs.org/picocolors@1.0.0 + source-map-js: registry.npmjs.org/source-map-js@1.0.2 + registry.npmjs.org/prebuild-install@7.1.1: resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz} name: prebuild-install @@ -11206,7 +11249,7 @@ packages: typescript: registry.npmjs.org/typescript@5.1.3 dev: true - registry.npmjs.org/svelte2tsx@0.6.23(svelte@4.2.1)(typescript@5.2.2): + registry.npmjs.org/svelte2tsx@0.6.23(svelte@4.2.1)(typescript@5.3.3): resolution: {integrity: sha512-3bwd1PuWUA3oEXy8+85zrLDnmJOsVpShpKVAehGWeYsz/66zMihTpRpUN97VVAKTZbO5tP4wnchHUXYs0zOwdw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/svelte2tsx/-/svelte2tsx-0.6.23.tgz} id: registry.npmjs.org/svelte2tsx/0.6.23 name: svelte2tsx @@ -11218,7 +11261,7 @@ packages: dedent-js: registry.npmjs.org/dedent-js@1.0.1 pascal-case: registry.npmjs.org/pascal-case@3.1.2 svelte: registry.npmjs.org/svelte@4.2.1 - typescript: registry.npmjs.org/typescript@5.2.2 + typescript: registry.npmjs.org/typescript@5.3.3 dev: false registry.npmjs.org/svelte@4.2.1: @@ -11755,6 +11798,14 @@ packages: engines: {node: '>=14.17'} hasBin: true + registry.npmjs.org/typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz} + name: typescript + version: 5.3.3 + engines: {node: '>=14.17'} + hasBin: true + dev: false + registry.npmjs.org/ultrahtml@1.5.2: resolution: {integrity: sha512-qh4mBffhlkiXwDAOxvSGxhL0QEQsTbnP9BozOK3OYPEGvPvdWzvAUaXNtUSMdNsKDtuyjEbyVUPFZ52SSLhLqw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.5.2.tgz} name: ultrahtml @@ -12090,6 +12141,44 @@ packages: optionalDependencies: fsevents: registry.npmjs.org/fsevents@2.3.3 + registry.npmjs.org/vite@4.5.1(@types/node@18.7.16): + resolution: {integrity: sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/vite/-/vite-4.5.1.tgz} + id: registry.npmjs.org/vite/4.5.1 + name: vite + version: 4.5.1 + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': registry.npmjs.org/@types/node@18.7.16 + esbuild: registry.npmjs.org/esbuild@0.18.20 + postcss: registry.npmjs.org/postcss@8.4.32 + rollup: registry.npmjs.org/rollup@3.29.4 + optionalDependencies: + fsevents: registry.npmjs.org/fsevents@2.3.3 + registry.npmjs.org/vitefu@0.2.5(vite@4.5.0): resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz} id: registry.npmjs.org/vitefu/0.2.5 @@ -12103,6 +12192,19 @@ packages: dependencies: vite: registry.npmjs.org/vite@4.5.0(@types/node@18.7.16) + registry.npmjs.org/vitefu@0.2.5(vite@4.5.1): + resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/vitefu/-/vitefu-0.2.5.tgz} + id: registry.npmjs.org/vitefu/0.2.5 + name: vitefu + version: 0.2.5 + peerDependencies: + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + vite: + optional: true + dependencies: + vite: registry.npmjs.org/vite@4.5.1(@types/node@18.7.16) + registry.npmjs.org/vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz} name: vscode-oniguruma diff --git a/tooling/tsconfig/react.json b/tooling/tsconfig/react.json index e3cb9853..33c4fc1f 100644 --- a/tooling/tsconfig/react.json +++ b/tooling/tsconfig/react.json @@ -6,6 +6,7 @@ "lib": ["dom", "esnext"], "module": "esnext", "target": "esnext", - "outDir": "dist" + "outDir": "dist", + "noErrorTruncation": true } }