diff --git a/examples/react/src/examples/Basic/index.tsx b/examples/react/src/examples/Basic/index.tsx index 8d9c7dfd..f454b313 100644 --- a/examples/react/src/examples/Basic/index.tsx +++ b/examples/react/src/examples/Basic/index.tsx @@ -10,9 +10,10 @@ import { Edge, useReactFlow, Panel, + OnNodeDrag, } from '@xyflow/react'; -const onNodeDrag = (_: MouseEvent, node: Node) => console.log('drag', node); +const onNodeDrag: OnNodeDrag = (_, node) => console.log('drag', node); const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); diff --git a/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx b/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx index 6857e1ac..72255e27 100644 --- a/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx +++ b/examples/react/src/examples/CustomNode/ColorSelectorNode.tsx @@ -1,6 +1,8 @@ -import React, { memo, FC, CSSProperties, useCallback, useEffect } from 'react'; +import React, { memo, FC, CSSProperties, useCallback } from 'react'; import { Handle, Position, NodeProps, Connection, Edge, useOnViewportChange, Viewport } from '@xyflow/react'; +import type { ColorSelectorNode } from '.'; + const targetHandleStyle: CSSProperties = { background: '#555' }; const sourceHandleStyleA: CSSProperties = { ...targetHandleStyle, top: 10 }; const sourceHandleStyleB: CSSProperties = { @@ -11,7 +13,7 @@ const sourceHandleStyleB: CSSProperties = { const onConnect = (params: Connection | Edge) => console.log('handle onConnect', params); -const ColorSelectorNode: FC = ({ data, isConnectable }) => { +const ColorSelectorNode: FC> = ({ data, isConnectable }) => { const onStart = useCallback((viewport: Viewport) => console.log('onStart', viewport), []); const onChange = useCallback((viewport: Viewport) => console.log('onChange', viewport), []); const onEnd = useCallback((viewport: Viewport) => console.log('onEnd', viewport), []); diff --git a/examples/react/src/examples/CustomNode/index.tsx b/examples/react/src/examples/CustomNode/index.tsx index ce7d0f62..a8c0f55d 100644 --- a/examples/react/src/examples/CustomNode/index.tsx +++ b/examples/react/src/examples/CustomNode/index.tsx @@ -5,24 +5,33 @@ import { Controls, addEdge, Node, - ReactFlowInstance, Position, SnapGrid, - Connection, - useNodesState, useEdgesState, Background, Edge, + OnNodeDrag, + OnInit, + applyNodeChanges, + OnNodesChange, + OnConnect, + OnBeforeDelete, } from '@xyflow/react'; import ColorSelectorNode from './ColorSelectorNode'; -const onInit = (reactFlowInstance: ReactFlowInstance) => { +export type ColorSelectorNode = Node< + { color: string; onChange: (event: ChangeEvent) => void }, + 'selectorNode' +>; +export type MyNode = Node | ColorSelectorNode; + +const onInit: OnInit = (reactFlowInstance) => { console.log('flow loaded:', reactFlowInstance); }; -const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node); -const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node); +const onNodeDragStop: OnNodeDrag = (_, node) => console.log('drag stop', node); +const onNodeClick = (_: MouseEvent, node: MyNode) => console.log('click', node); const initBgColor = '#1A192B'; @@ -34,7 +43,16 @@ const nodeTypes = { }; const CustomNodeFlow = () => { - const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [nodes, setNodes] = useState([]); + const onNodesChange: OnNodesChange = useCallback( + (changes) => + setNodes((nds) => { + const nextNodes = applyNodeChanges(changes, nds); + return nextNodes; + }), + [setNodes] + ); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); const [bgColor, setBgColor] = useState(initBgColor); @@ -120,12 +138,13 @@ const CustomNodeFlow = () => { ]); }, []); - const onConnect = useCallback( - (connection: Connection) => - setEdges((eds) => addEdge({ ...connection, animated: true, style: { stroke: '#fff' } }, eds)), + const onConnect: OnConnect = useCallback( + (connection) => setEdges((eds) => addEdge({ ...connection, animated: true, style: { stroke: '#fff' } }, eds)), [setEdges] ); + const onBeforeDelete: OnBeforeDelete = useCallback(async (params) => true, []); + return ( { fitView minZoom={0.3} maxZoom={2} + onBeforeDelete={onBeforeDelete} > { + nodeStrokeColor={(n: MyNode): string => { if (n.type === 'input') return '#0041d0'; if (n.type === 'selectorNode') return bgColor; if (n.type === 'output') return '#ff0072'; return '#eee'; }} - nodeColor={(n: Node): string => { + nodeColor={(n: MyNode): string => { if (n.type === 'selectorNode') return bgColor; return '#fff'; diff --git a/examples/svelte/src/routes/examples/handle-connect/MultiHandleNode.svelte b/examples/svelte/src/routes/examples/handle-connect/MultiHandleNode.svelte index ebc85c74..f411fc44 100644 --- a/examples/svelte/src/routes/examples/handle-connect/MultiHandleNode.svelte +++ b/examples/svelte/src/routes/examples/handle-connect/MultiHandleNode.svelte @@ -43,10 +43,8 @@ export let zIndex: $$Props['zIndex'] = undefined; export let dragging: $$Props['dragging'] = false; export let dragHandle: $$Props['dragHandle'] = undefined; - export let positionAbsolute: $$Props['positionAbsolute'] = { - x: 0, - y: 0 - }; + export let positionAbsoluteX: $$Props['positionAbsoluteX'] = 0; + export let positionAbsoluteY: $$Props['positionAbsoluteY'] = 0; export let isConnectable: $$Props['isConnectable'] = undefined; data; @@ -59,7 +57,8 @@ zIndex; dragging; dragHandle; - positionAbsolute; + positionAbsoluteX; + positionAbsoluteY; isConnectable; diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 35466785..9bfaa78e 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,19 +1,29 @@ # @xyflow/react -## 12.0.0-next.6 +## 12.0.0-next.8 + +### Patch changes + +- fix `OnNodeDrag` type +- refactor(handles): do not use fallback handle if an id is being used #3409 + +## 12.0.0-next.7 ## Minor changes -- pass Node/Edge types to changes thanks @FelipeEmos -- use position instead of positionAbsolute for `getNodesBounds` - add second option param to `screenToFlowPosition` for configuring if `snapToGrid` should be used + +### Patch changes + +- pass `Node`/ `Edge` types to changes thanks @FelipeEmos +- use position instead of positionAbsolute for `getNodesBounds` - infer types for `getIncomers`, `getOutgoers`, `updateEdge`, `addEdge` and `getConnectedEdges` thanks @joeyballentine - refactor handles: prefix with flow id for handling nested flows - add comments for types like `ReactFlowProps` or `Node` for a better developer experience ## 12.0.0-next.6 -### Minor changes +### Patch changes - fix `deleteElements` - refactor internal `applyChanges` diff --git a/packages/react/package.json b/packages/react/package.json index 7560e0dc..a7eb71c3 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -1,6 +1,6 @@ { "name": "@xyflow/react", - "version": "12.0.0-next.6", + "version": "12.0.0-next.7", "description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.", "keywords": [ "react", diff --git a/packages/react/src/components/EdgeWrapper/index.tsx b/packages/react/src/components/EdgeWrapper/index.tsx index cb9b9c8b..28323a19 100644 --- a/packages/react/src/components/EdgeWrapper/index.tsx +++ b/packages/react/src/components/EdgeWrapper/index.tsx @@ -186,6 +186,7 @@ export function EdgeWrapper({ animated: edge.animated, inactive: !isSelectable && !onClick, updating: updateHover, + selectable: isSelectable, }, ])} onClick={onEdgeClick} diff --git a/packages/react/src/components/Handle/index.tsx b/packages/react/src/components/Handle/index.tsx index 362027ab..36778039 100644 --- a/packages/react/src/components/Handle/index.tsx +++ b/packages/react/src/components/Handle/index.tsx @@ -22,7 +22,7 @@ import { useStore, useStoreApi } from '../../hooks/useStore'; import { useNodeId } from '../../contexts/NodeIdContext'; import { type ReactFlowState } from '../../types'; -export type HandleComponentProps = HandleProps & Omit, 'id'>; +export interface HandleComponentProps extends HandleProps, Omit, 'id'> {} const selector = (s: ReactFlowState) => ({ connectOnClick: s.connectOnClick, @@ -221,4 +221,7 @@ const HandleComponent = forwardRef( HandleComponent.displayName = 'Handle'; +/** + * The Handle component is the part of a node that can be used to connect nodes. + */ export const Handle = memo(HandleComponent); diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index 048a957b..bdd05aa0 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -203,7 +203,7 @@ export function useReactFlow, 'onError'> & { +export interface ReactFlowProps extends Omit, 'onError'> { /** An array of nodes to render in a controlled flow. * @example * const nodes = [ @@ -111,11 +111,11 @@ export type ReactFlowProps = Omit, 'onError'> & { /** This event handler is called when a user right clicks on a node */ onNodeContextMenu?: NodeMouseHandler; /** This event handler is called when a user starts to drag a node */ - onNodeDragStart?: NodeDragHandler; + onNodeDragStart?: OnNodeDrag; /** This event handler is called when a user drags a node */ - onNodeDrag?: NodeDragHandler; + onNodeDrag?: OnNodeDrag; /** This event handler is called when a user stops dragging a node */ - onNodeDragStop?: NodeDragHandler; + onNodeDragStop?: OnNodeDrag; /** This event handler is called when a user clicks on an edge */ onEdgeClick?: (event: ReactMouseEvent, edge: Edge) => void; /** This event handler is called when a user right clicks on an edge */ @@ -502,6 +502,6 @@ export type ReactFlowProps = Omit, 'onError'> & { * @example 'system' | 'light' | 'dark' */ colorMode?: ColorMode; -}; +} export type ReactFlowRefType = HTMLDivElement; diff --git a/packages/react/src/types/general.ts b/packages/react/src/types/general.ts index 9a36e41f..53d95d68 100644 --- a/packages/react/src/types/general.ts +++ b/packages/react/src/types/general.ts @@ -11,6 +11,7 @@ import { FitBounds, XYPosition, NodeProps, + OnBeforeDeleteBase, } from '@xyflow/system'; import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps } from '.'; @@ -19,7 +20,7 @@ import { ComponentType } from 'react'; export type OnNodesChange = (changes: NodeChange[]) => void; export type OnEdgesChange = (changes: EdgeChange[]) => void; -export type OnNodesDelete = (nodes: Node[]) => void; +export type OnNodesDelete = (nodes: NodeType[]) => void; export type OnEdgesDelete = (edges: Edge[]) => void; export type OnDelete = (params: { nodes: Node[]; edges: Edge[] }) => void; @@ -59,3 +60,8 @@ export type ViewportHelperFunctions = { flowToScreenPosition: (position: XYPosition) => XYPosition; viewportInitialized: boolean; }; + +export type OnBeforeDelete = OnBeforeDeleteBase< + NodeType, + EdgeType +>; diff --git a/packages/react/src/types/nodes.ts b/packages/react/src/types/nodes.ts index 71bfb685..612c1d50 100644 --- a/packages/react/src/types/nodes.ts +++ b/packages/react/src/types/nodes.ts @@ -3,11 +3,11 @@ import type { CoordinateExtent, NodeBase, NodeOrigin, OnError } from '@xyflow/sy import { NodeTypes } from './general'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any /** * The node data structure that gets used for the nodes prop. * @public */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any export type Node = NodeBase< NodeData, NodeType @@ -18,9 +18,13 @@ export type Node void; -export type NodeDragHandler = (event: ReactMouseEvent, node: Node, nodes: Node[]) => void; -export type SelectionDragHandler = (event: ReactMouseEvent, nodes: Node[]) => void; +export type NodeMouseHandler = (event: ReactMouseEvent, node: NodeType) => void; +export type SelectionDragHandler = (event: ReactMouseEvent, nodes: NodeType[]) => void; +export type OnNodeDrag = ( + event: ReactMouseEvent, + node: NodeType, + nodes: NodeType[] +) => void; export type NodeWrapperProps = { id: string; diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index b138fc9d..b3e0449e 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -17,7 +17,6 @@ import { type PanBy, type OnConnectStart, type OnConnectEnd, - type OnNodeDrag, type OnSelectionDrag, type OnMoveStart, type OnMove, @@ -27,7 +26,6 @@ import { type EdgeLookup, type ConnectionLookup, type NodeLookup, - OnBeforeDelete, } from '@xyflow/system'; import type { @@ -43,6 +41,8 @@ import type { OnSelectionChangeFunc, UnselectNodesAndEdgesParams, OnDelete, + OnNodeDrag, + OnBeforeDelete, } from '.'; export type ReactFlowStore = { diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index b7557fff..23b550ea 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,11 +1,25 @@ # @xyflow/svelte +## 0.0.35 + +## Minor changes + +- add `getNode`, `getNodes`, `getEdge` and `getEdges` to `useSvelteFlow` + +## Patch changes + +- Edge label has a default background and is clickable +- refactor(handles): do not use fallback handle if an id is being used #3409 + ## 0.0.34 ## Minor changes - add second option param to `screenToFlowPosition` for configuring if `snapToGrid` should be used - add slot to `Controls` + +## Patch changes + - cleanup `ControlButton` types - infer types for `getIncomers`, `getOutgoers`, `updateEdge`, `addEdge` and `getConnectedEdges` thanks @joeyballentine - refactor handles: prefix with flow id for handling nested flows @@ -42,7 +56,7 @@ - add `onbeforedelete` handler to prevent/ manage deletions - TSDocs for hooks and some types -### Minor changes +### Patch changes - new nodeDragThreshold default is 1 - refactor/simplify edge rendering diff --git a/packages/svelte/package.json b/packages/svelte/package.json index f47dc169..5095441e 100644 --- a/packages/svelte/package.json +++ b/packages/svelte/package.json @@ -1,6 +1,6 @@ { "name": "@xyflow/svelte", - "version": "0.0.33", + "version": "0.0.34", "description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.", "keywords": [ "svelte", diff --git a/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte b/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte index d1c60f8f..36b3763e 100644 --- a/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte +++ b/packages/svelte/src/lib/components/BaseEdge/BaseEdge.svelte @@ -1,7 +1,7 @@ @@ -43,13 +42,7 @@ {/if} {#if label} - -
- {label} -
-
+ + {label} + {/if} diff --git a/packages/svelte/src/lib/components/EdgeLabel/EdgeLabel.svelte b/packages/svelte/src/lib/components/EdgeLabel/EdgeLabel.svelte new file mode 100644 index 00000000..81039d0d --- /dev/null +++ b/packages/svelte/src/lib/components/EdgeLabel/EdgeLabel.svelte @@ -0,0 +1,28 @@ + + + +
{ + if (id) handleEdgeSelect(id); + }} + > + +
+
diff --git a/packages/svelte/src/lib/components/EdgeLabel/index.ts b/packages/svelte/src/lib/components/EdgeLabel/index.ts new file mode 100644 index 00000000..2e0a457c --- /dev/null +++ b/packages/svelte/src/lib/components/EdgeLabel/index.ts @@ -0,0 +1 @@ +export { default as EdgeLabel } from './EdgeLabel.svelte'; diff --git a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte index c68ea1d5..0d4c126b 100644 --- a/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte +++ b/packages/svelte/src/lib/components/EdgeWrapper/EdgeWrapper.svelte @@ -1,14 +1,14 @@ +
{#each $visibleEdges as edge (edge.id)} - {@const edgeType = edge.type || 'default'} - {@const selectable = !!( - edge.selectable || - ($elementsSelectable && typeof edge.selectable === 'undefined') - )} diff --git a/packages/svelte/src/lib/hooks/useHandleEdgeSelect.ts b/packages/svelte/src/lib/hooks/useHandleEdgeSelect.ts new file mode 100644 index 00000000..5e6ba5d3 --- /dev/null +++ b/packages/svelte/src/lib/hooks/useHandleEdgeSelect.ts @@ -0,0 +1,39 @@ +import { get } from 'svelte/store'; +import { errorMessages } from '@xyflow/system'; + +import { useStore } from '$lib/store'; + +export function useHandleEdgeSelect() { + const { + edgeLookup, + selectionRect, + selectionRectMode, + multiselectionKeyPressed, + addSelectedEdges, + unselectNodesAndEdges, + elementsSelectable + } = useStore(); + + return (id: string) => { + const edge = get(edgeLookup).get(id); + + if (!edge) { + console.warn('012', errorMessages['error012'](id)); + return; + } + + const selectable = + edge.selectable || (get(elementsSelectable) && typeof edge.selectable === 'undefined'); + + if (selectable) { + selectionRect.set(null); + selectionRectMode.set(null); + + if (!edge.selected) { + addSelectedEdges([id]); + } else if (edge.selected && get(multiselectionKeyPressed)) { + unselectNodesAndEdges({ nodes: [], edges: [edge] }); + } + } + }; +} diff --git a/packages/svelte/src/lib/hooks/useSvelteFlow.ts b/packages/svelte/src/lib/hooks/useSvelteFlow.ts index d47ee45b..b6bb941e 100644 --- a/packages/svelte/src/lib/hooks/useSvelteFlow.ts +++ b/packages/svelte/src/lib/hooks/useSvelteFlow.ts @@ -29,6 +29,10 @@ import { isNode } from '$lib/utils'; export function useSvelteFlow(): { zoomIn: ZoomInOut; zoomOut: ZoomInOut; + getNode: (id: string) => Node | undefined; + getNodes: (ids?: string[]) => Node[]; + getEdge: (id: string) => Edge | undefined; + getEdges: (ids?: string[]) => Edge[]; setZoom: (zoomLevel: number, options?: ViewportHelperFunctionOptions) => void; getZoom: () => number; setCenter: (x: number, y: number, options?: SetCenterOptions) => void; @@ -82,7 +86,9 @@ export function useSvelteFlow(): { panZoom, nodes, edges, - domNode + domNode, + nodeLookup, + edgeLookup } = useStore(); const getNodeRect = ( @@ -121,6 +127,10 @@ export function useSvelteFlow(): { return { zoomIn, zoomOut, + getNode: (id) => get(nodeLookup).get(id), + getNodes: (ids) => (ids === undefined ? get(nodes) : getElements(get(nodeLookup), ids)), + getEdge: (id) => get(edgeLookup).get(id), + getEdges: (ids) => (ids === undefined ? get(edges) : getElements(get(edgeLookup), ids)), setZoom: (zoomLevel, options) => { get(panZoom)?.scaleTo(zoomLevel, { duration: options?.duration }); }, @@ -295,3 +305,17 @@ export function useSvelteFlow(): { viewport }; } + +function getElements(lookup: Map, ids: string[]) { + const result = []; + + for (const id of ids) { + const element = lookup.get(id); + + if (element) { + result.push(element); + } + } + + return result; +} diff --git a/packages/svelte/src/lib/index.ts b/packages/svelte/src/lib/index.ts index 75f0092b..76394872 100644 --- a/packages/svelte/src/lib/index.ts +++ b/packages/svelte/src/lib/index.ts @@ -80,7 +80,6 @@ export { type OnError, type NodeProps, type NodeOrigin, - type OnNodeDrag, type OnSelectionDrag, Position, type XYPosition, diff --git a/packages/svelte/src/lib/store/index.ts b/packages/svelte/src/lib/store/index.ts index 6dbf84e2..7d5db531 100644 --- a/packages/svelte/src/lib/store/index.ts +++ b/packages/svelte/src/lib/store/index.ts @@ -15,7 +15,6 @@ import { type XYPosition, type CoordinateExtent, type UpdateConnection, - type NodeBase, type NodeDragItem, errorMessages } from '@xyflow/system'; @@ -67,7 +66,7 @@ export function createStore({ const updateNodePositions: UpdateNodePositions = (nodeDragItems, dragging = false) => { store.nodes.update((nds) => { return nds.map((node) => { - const nodeDragItem = (nodeDragItems as Array).find( + const nodeDragItem = (nodeDragItems as Array).find( (ndi) => ndi.id === node.id ); diff --git a/packages/svelte/src/lib/store/initial-store.ts b/packages/svelte/src/lib/store/initial-store.ts index 85d5e84b..fedaa364 100644 --- a/packages/svelte/src/lib/store/initial-store.ts +++ b/packages/svelte/src/lib/store/initial-store.ts @@ -23,7 +23,6 @@ import { type OnConnectStart, type OnConnectEnd, type NodeLookup, - type OnBeforeDelete, type EdgeLookup } from '@xyflow/system'; @@ -47,7 +46,8 @@ import type { Edge, FitViewOptions, OnDelete, - OnEdgeCreate + OnEdgeCreate, + OnBeforeDelete } from '$lib/types'; import { createNodesStore, createEdgesStore } from './utils'; import { initConnectionProps, type ConnectionProps } from './derived-connection-props'; diff --git a/packages/svelte/src/lib/types/general.ts b/packages/svelte/src/lib/types/general.ts index 4f5fa30b..e67ad88c 100644 --- a/packages/svelte/src/lib/types/general.ts +++ b/packages/svelte/src/lib/types/general.ts @@ -5,7 +5,8 @@ import type { Position, XYPosition, ConnectingHandle, - Connection + Connection, + OnBeforeDeleteBase } from '@xyflow/system'; import type { Node } from './nodes'; @@ -52,3 +53,7 @@ export type FitViewOptions = FitViewOptionsBase; export type OnDelete = (params: { nodes: Node[]; edges: Edge[] }) => void; export type OnEdgeCreate = (connection: Connection) => Edge | Connection | void; +export type OnBeforeDelete< + NodeType extends Node = Node, + EdgeType extends Edge = Edge +> = OnBeforeDeleteBase; diff --git a/packages/svelte/src/styles/base.css b/packages/svelte/src/styles/base.css index b9861c98..80bc953d 100644 --- a/packages/svelte/src/styles/base.css +++ b/packages/svelte/src/styles/base.css @@ -2,16 +2,7 @@ @import '../../../system/src/styles/init.css'; @import '../../../system/src/styles/base.css'; -.svelte-flow { - --edge-label-color-default: inherit; -} - -.svelte-flow.dark { - --edge-label-color-default: #f8f8f8; -} - .svelte-flow__edge-label { text-align: center; position: absolute; - color: var(--edge-label-color, var(--edge-label-color-default)); } diff --git a/packages/svelte/src/styles/style.css b/packages/svelte/src/styles/style.css index 308a6a1e..981bc92e 100644 --- a/packages/svelte/src/styles/style.css +++ b/packages/svelte/src/styles/style.css @@ -3,19 +3,14 @@ @import '../../../system/src/styles/style.css'; @import '../../../system/src/styles/node-resizer.css'; -.svelte-flow { - --edge-label-color-default: inherit; -} - -.svelte-flow.dark { - --edge-label-color-default: #f8f8f8; -} - .svelte-flow__edge-label { text-align: center; position: absolute; + padding: 2px; font-size: 10px; - color: var(--edge-label-color, var(--edge-label-color-default)); + cursor: pointer; + color: var(--xy-edge-label-color, var(--xy-edge-label-color-default)); + background: var(--xy-edge-label-background-color, var(--xy-edge-label-background-color-default)); } .svelte-flow__nodes { diff --git a/packages/system/package.json b/packages/system/package.json index 6cc84fd4..4eecdb57 100644 --- a/packages/system/package.json +++ b/packages/system/package.json @@ -1,6 +1,6 @@ { "name": "@xyflow/system", - "version": "0.0.14", + "version": "0.0.15", "description": "xyflow core system that powers React Flow and Svelte Flow.", "keywords": [ "node-based UI", diff --git a/packages/system/src/styles/init.css b/packages/system/src/styles/init.css index a688f6b9..14519c74 100644 --- a/packages/system/src/styles/init.css +++ b/packages/system/src/styles/init.css @@ -139,8 +139,8 @@ } &.selected .xy-flow__edge-path, - &:focus .xy-flow__edge-path, - &:focus-visible .xy-flow__edge-path { + &.selectable:focus .xy-flow__edge-path, + &.selectable:focus-visible .xy-flow__edge-path { stroke: var(--xy-edge-stroke-selected, var(--xy-edge-stroke-selected-default)); } diff --git a/packages/system/src/styles/style.css b/packages/system/src/styles/style.css index 880ebaf7..cb6f2b7c 100644 --- a/packages/system/src/styles/style.css +++ b/packages/system/src/styles/style.css @@ -19,6 +19,9 @@ --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-edge-label-background-color-default: #ffffff; + --xy-edge-label-color-default: inherit; } .xy-flow.dark { @@ -41,6 +44,9 @@ --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-edge-label-background-color-default: #141414; + --xy-edge-label-color-default: #f8f8f8; } .xy-flow__edge { diff --git a/packages/system/src/types/general.ts b/packages/system/src/types/general.ts index 5fe4a844..8ac3d6be 100644 --- a/packages/system/src/types/general.ts +++ b/packages/system/src/types/general.ts @@ -142,7 +142,7 @@ export type ColorMode = ColorModeClass | 'system'; export type ConnectionLookup = Map>; -export type OnBeforeDelete = ({ +export type OnBeforeDeleteBase = ({ nodes, edges, }: { diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts index 40aa1c16..b75e6d1b 100644 --- a/packages/system/src/types/nodes.ts +++ b/packages/system/src/types/nodes.ts @@ -94,9 +94,9 @@ export type NodeProps = { positionAbsoluteY: number; width?: number; height?: number; - dragging: boolean; - targetPosition?: Position; - sourcePosition?: Position; + dragging: NodeBase['dragging']; + sourcePosition?: NodeBase['sourcePosition']; + targetPosition?: NodeBase['targetPosition']; }; export type NodeHandleBounds = { @@ -134,8 +134,6 @@ export type NodeDragItem = { export type NodeOrigin = [number, number]; -export type OnNodeDrag = (event: MouseEvent, node: NodeBase, nodes: NodeBase[]) => void; - export type OnSelectionDrag = (event: MouseEvent, nodes: NodeBase[]) => void; export type NodeHandle = Optional; diff --git a/packages/system/src/utils/edges/positions.ts b/packages/system/src/utils/edges/positions.ts index 59b8cd42..8bb9f5df 100644 --- a/packages/system/src/utils/edges/positions.ts +++ b/packages/system/src/utils/edges/positions.ts @@ -114,11 +114,6 @@ function getHandle(bounds: HandleElement[], handleId?: string | null): HandleEle return null; } - if (bounds.length === 1 || !handleId) { - return bounds[0]; - } else if (handleId) { - return bounds.find((d) => d.id === handleId) || null; - } - - return null; + // if no handleId is given, we use the first handle, otherwise we check for the id + return (!handleId ? bounds[0] : bounds.find((d) => d.id === handleId)) || null; } diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index fa56a987..f7e177b9 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -22,7 +22,7 @@ import { NodeDragItem, CoordinateExtent, OnError, - OnBeforeDelete, + OnBeforeDeleteBase, } from '../types'; import { errorMessages } from '../constants'; @@ -347,7 +347,7 @@ export async function getElementsToRemove[]; nodes: NodeType[]; edges: EdgeType[]; - onBeforeDelete?: OnBeforeDelete; + onBeforeDelete?: OnBeforeDeleteBase; }): Promise<{ nodes: NodeType[]; edges: EdgeType[]; diff --git a/packages/system/src/xydrag/XYDrag.ts b/packages/system/src/xydrag/XYDrag.ts index 79a0823a..40d1aa4b 100644 --- a/packages/system/src/xydrag/XYDrag.ts +++ b/packages/system/src/xydrag/XYDrag.ts @@ -23,7 +23,6 @@ import type { SnapGrid, Transform, PanBy, - OnNodeDrag, OnSelectionDrag, UpdateNodePositions, Box, @@ -31,7 +30,7 @@ import type { export type OnDrag = (event: MouseEvent, dragItems: NodeDragItem[], node: NodeBase, nodes: NodeBase[]) => void; -type StoreItems = { +type StoreItems = { nodes: NodeBase[]; nodeLookup: Map; edges: EdgeBase[]; @@ -58,9 +57,9 @@ type StoreItems = { updateNodePositions: UpdateNodePositions; }; -export type XYDragParams = { +export type XYDragParams = { domNode: Element; - getStoreItems: () => StoreItems; + getStoreItems: () => StoreItems; onDragStart?: OnDrag; onDrag?: OnDrag; onDragStop?: OnDrag; @@ -80,14 +79,15 @@ export type DragUpdateParams = { domNode: Element; }; -export function XYDrag({ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export function XYDrag void | undefined>({ domNode, onNodeMouseDown, getStoreItems, onDragStart, onDrag, onDragStop, -}: XYDragParams): XYDragInstance { +}: XYDragParams): XYDragInstance { let lastPos: { x: number | null; y: number | null } = { x: null, y: null }; let autoPanId = 0; let dragItems: NodeDragItem[] = []; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 888ca119..dfddf7d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -205,9 +205,6 @@ importers: packages/react: dependencies: - '@types/d3': - specifier: ^7.4.0 - version: 7.4.0 '@xyflow/system': specifier: workspace:* version: link:../system @@ -287,7 +284,7 @@ importers: version: 2.1.0(@sveltejs/kit@1.22.6) '@sveltejs/kit': specifier: ^1.22.6 - version: 1.22.6(svelte@4.2.1)(vite@4.5.1) + version: 1.22.6(svelte@4.2.1)(vite@4.5.0) '@sveltejs/package': specifier: ^2.2.1 version: 2.2.1(svelte@4.2.1)(typescript@5.1.3) @@ -1632,7 +1629,7 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: ajv: 6.12.6 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) espree: 9.6.1 globals: 13.23.0 ignore: 5.2.4 @@ -1690,7 +1687,7 @@ packages: engines: {node: '>=10.10.0'} dependencies: '@humanwhocodes/object-schema': 2.0.1 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) minimatch: 3.1.2 transitivePeerDependencies: - supports-color @@ -1956,7 +1953,7 @@ packages: peerDependencies: '@sveltejs/kit': ^1.0.0 dependencies: - '@sveltejs/kit': 1.22.6(svelte@4.2.1)(vite@4.5.1) + '@sveltejs/kit': 1.22.6(svelte@4.2.1)(vite@4.5.0) import-meta-resolve: 3.0.0 dev: true @@ -1969,7 +1966,7 @@ packages: import-meta-resolve: 4.0.0 dev: true - /@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.1): + /@sveltejs/kit@1.22.6(svelte@4.2.1)(vite@4.5.0): resolution: {integrity: sha512-SDKxI/QpsReCwIn5czjT53fKlPBybbmMk67d317gUqfeORroBAFN1Z6s/x0E1JYi+04i7kKllS+Sz9wVfmUkAQ==} engines: {node: ^16.14 || >=18} hasBin: true @@ -1978,7 +1975,7 @@ packages: svelte: ^3.54.0 || ^4.0.0-next.0 vite: ^4.0.0 dependencies: - '@sveltejs/vite-plugin-svelte': 2.4.6(svelte@4.2.1)(vite@4.5.1) + '@sveltejs/vite-plugin-svelte': 2.4.6(svelte@4.2.1)(vite@4.5.0) '@types/cookie': 0.5.3 cookie: 0.5.0 devalue: 4.3.2 @@ -1991,7 +1988,7 @@ packages: sirv: 2.0.3 svelte: 4.2.1 undici: 5.23.0 - vite: 4.5.1 + vite: 4.5.0(@types/node@18.7.16) transitivePeerDependencies: - supports-color dev: true @@ -2055,23 +2052,6 @@ packages: vite: 4.5.0(@types/node@18.7.16) transitivePeerDependencies: - supports-color - dev: false - - /@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==} - engines: {node: ^14.18.0 || >= 16} - peerDependencies: - '@sveltejs/vite-plugin-svelte': ^2.2.0 - svelte: ^3.54.0 || ^4.0.0 - vite: ^4.0.0 - dependencies: - '@sveltejs/vite-plugin-svelte': 2.4.6(svelte@4.2.1)(vite@4.5.1) - debug: 4.3.4 - svelte: 4.2.1 - vite: 4.5.1 - transitivePeerDependencies: - - supports-color - dev: true /@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.2)(vite@4.5.0): resolution: {integrity: sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==} @@ -2107,27 +2087,6 @@ packages: vitefu: 0.2.5(vite@4.5.0) transitivePeerDependencies: - supports-color - dev: false - - /@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.1)(vite@4.5.1): - resolution: {integrity: sha512-zO79p0+DZnXPnF0ltIigWDx/ux7Ni+HRaFOw720Qeivc1azFUrJxTl0OryXVibYNx1hCboGia1NRV3x8RNv4cA==} - engines: {node: ^14.18.0 || >= 16} - peerDependencies: - svelte: ^3.54.0 || ^4.0.0 - vite: ^4.0.0 - dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 1.0.4(@sveltejs/vite-plugin-svelte@2.4.6)(svelte@4.2.1)(vite@4.5.1) - debug: 4.3.4 - deepmerge: 4.3.1 - kleur: 4.1.5 - magic-string: 0.30.5 - svelte: 4.2.1 - svelte-hmr: 0.15.3(svelte@4.2.1) - vite: 4.5.1 - vitefu: 0.2.5(vite@4.5.1) - transitivePeerDependencies: - - supports-color - dev: true /@sveltejs/vite-plugin-svelte@2.4.6(svelte@4.2.2)(vite@4.5.0): resolution: {integrity: sha512-zO79p0+DZnXPnF0ltIigWDx/ux7Ni+HRaFOw720Qeivc1azFUrJxTl0OryXVibYNx1hCboGia1NRV3x8RNv4cA==} @@ -2705,7 +2664,7 @@ packages: '@typescript-eslint/scope-manager': 5.60.0 '@typescript-eslint/type-utils': 5.60.0(eslint@8.43.0)(typescript@5.1.3) '@typescript-eslint/utils': 5.60.0(eslint@8.43.0)(typescript@5.1.3) - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) eslint: 8.43.0 grapheme-splitter: 1.0.4 ignore: 5.2.4 @@ -2788,7 +2747,7 @@ packages: '@typescript-eslint/scope-manager': 5.60.0 '@typescript-eslint/types': 5.60.0 '@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3) - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) eslint: 8.43.0 typescript: 5.1.3 transitivePeerDependencies: @@ -2873,7 +2832,7 @@ packages: dependencies: '@typescript-eslint/typescript-estree': 5.60.0(typescript@5.1.3) '@typescript-eslint/utils': 5.60.0(eslint@8.43.0)(typescript@5.1.3) - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) eslint: 8.43.0 tsutils: 3.21.0(typescript@5.1.3) typescript: 5.1.3 @@ -2947,7 +2906,7 @@ packages: dependencies: '@typescript-eslint/types': 5.60.0 '@typescript-eslint/visitor-keys': 5.60.0 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) globby: 11.1.0 is-glob: 4.0.3 semver: 7.5.4 @@ -4274,18 +4233,6 @@ packages: ms: 2.1.3 supports-color: 8.1.1 - /debug@4.3.4: - resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - dependencies: - ms: 2.1.2 - dev: true - /debug@4.3.4(supports-color@8.1.1): resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -4794,7 +4741,7 @@ packages: dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.43.0) '@jridgewell/sourcemap-codec': 1.4.15 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) eslint: 8.43.0 esutils: 2.0.3 known-css-properties: 0.27.0 @@ -4884,7 +4831,7 @@ packages: ajv: 6.12.6 chalk: 4.1.2 cross-spawn: 7.0.3 - debug: 4.3.4 + debug: 4.3.4(supports-color@8.1.1) doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -7898,6 +7845,7 @@ packages: nanoid: 3.3.7 picocolors: 1.0.0 source-map-js: 1.0.2 + dev: false /prebuild-install@7.1.1: resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==} @@ -9968,41 +9916,6 @@ packages: optionalDependencies: fsevents: 2.3.3 - /vite@4.5.1: - resolution: {integrity: sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==} - 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: - esbuild: 0.18.20 - postcss: 8.4.32 - rollup: 3.29.4 - optionalDependencies: - fsevents: 2.3.3 - dev: true - /vite@4.5.1(@types/node@18.7.16): resolution: {integrity: sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==} engines: {node: ^14.18.0 || >=16.0.0} @@ -10049,17 +9962,6 @@ packages: dependencies: vite: 4.5.0(@types/node@18.7.16) - /vitefu@0.2.5(vite@4.5.1): - resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} - peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - vite: - optional: true - dependencies: - vite: 4.5.1 - dev: true - /vscode-oniguruma@1.7.0: resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==} dev: false