From 88c9372bb93ecdbe64c853db7da40abee7347534 Mon Sep 17 00:00:00 2001 From: Felipe Emos Date: Sat, 13 Jan 2024 00:35:26 -0300 Subject: [PATCH 01/19] fix: typesafety of Node and Edge changes On some functions and type definitions related to Change Events, the generics for the types Node and Edge were being lost. This commit fixes those: - Functions - applyNodeChanges - applyEdgeChanges - Types - NodeChange - EdgeChange - OnNodesChange - OnEdgesChange --- packages/react/src/types/changes.ts | 13 +++++++++---- packages/react/src/types/general.ts | 4 ++-- packages/react/src/utils/changes.ts | 10 ++++++++-- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/react/src/types/changes.ts b/packages/react/src/types/changes.ts index 3dfdffa2..9a5e8fe5 100644 --- a/packages/react/src/types/changes.ts +++ b/packages/react/src/types/changes.ts @@ -44,13 +44,13 @@ export type NodeReplaceChange = { * Union type of all possible node changes. * @public */ -export type NodeChange = +export type NodeChange = | NodeDimensionChange | NodePositionChange | NodeSelectionChange | NodeRemoveChange - | NodeAddChange - | NodeReplaceChange; + | NodeAddChange + | NodeReplaceChange; export type EdgeSelectionChange = NodeSelectionChange; export type EdgeRemoveChange = NodeRemoveChange; @@ -64,4 +64,9 @@ export type EdgeReplaceChange = { item: EdgeType; type: 'replace'; }; -export type EdgeChange = EdgeSelectionChange | EdgeRemoveChange | EdgeAddChange | EdgeReplaceChange; + +export type EdgeChange = + | EdgeSelectionChange + | EdgeRemoveChange + | EdgeAddChange + | EdgeReplaceChange; diff --git a/packages/react/src/types/general.ts b/packages/react/src/types/general.ts index a1d76983..82cb23ce 100644 --- a/packages/react/src/types/general.ts +++ b/packages/react/src/types/general.ts @@ -16,8 +16,8 @@ import { import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps } from '.'; import { ComponentType } from 'react'; -export type OnNodesChange = (changes: NodeChange[]) => void; -export type OnEdgesChange = (changes: EdgeChange[]) => void; +export type OnNodesChange = (changes: NodeChange[]) => void; +export type OnEdgesChange = (changes: EdgeChange[]) => void; export type OnNodesDelete = (nodes: Node[]) => void; export type OnEdgesDelete = (edges: Edge[]) => void; diff --git a/packages/react/src/utils/changes.ts b/packages/react/src/utils/changes.ts index cd148a6e..91a7a004 100644 --- a/packages/react/src/utils/changes.ts +++ b/packages/react/src/utils/changes.ts @@ -188,7 +188,10 @@ function applyChange(change: any, element: any, elements: any[] = []): any { ); */ -export function applyNodeChanges(changes: NodeChange[], nodes: NodeType[]): NodeType[] { +export function applyNodeChanges( + changes: NodeChange[], + nodes: NodeType[] +): NodeType[] { return applyChanges(changes, nodes) as NodeType[]; } @@ -212,7 +215,10 @@ export function applyNodeChanges(changes: NodeChan ); */ -export function applyEdgeChanges(changes: EdgeChange[], edges: EdgeType[]): EdgeType[] { +export function applyEdgeChanges( + changes: EdgeChange[], + edges: EdgeType[] +): EdgeType[] { return applyChanges(changes, edges) as EdgeType[]; } From 93d437edb697d5c7708b217de5778bb2e5685673 Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 15 Jan 2024 12:02:26 +0100 Subject: [PATCH 02/19] refactor(getNodesBounds): use position instead of positionAbsolute --- packages/system/src/utils/graph.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index 68024ce5..6d341e5e 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -148,7 +148,7 @@ export const getNodesBounds = (nodes: NodeBase[], nodeOrigin: NodeOrigin = [0, 0 const box = nodes.reduce( (currBox, node) => { - const { x, y } = getNodePositionWithOrigin(node, node.origin || nodeOrigin).positionAbsolute; + const { x, y } = getNodePositionWithOrigin(node, node.origin || nodeOrigin); return getBoundsOfBoxes( currBox, rectToBox({ From 0fe2f2ae5329a3336c4378a2569fbfefa1673bf8 Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 15 Jan 2024 12:20:13 +0100 Subject: [PATCH 03/19] refactor(screenToFlowPosition): add option to configure snapToGrid #3771 --- packages/react/CHANGELOG.md | 8 ++++++++ packages/react/src/hooks/useViewportHelper.ts | 6 +++--- packages/react/src/types/general.ts | 2 +- packages/svelte/CHANGELOG.md | 6 ++++++ packages/svelte/src/lib/hooks/useSvelteFlow.ts | 9 ++++++--- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 5828b7c4..951f4895 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -2,6 +2,14 @@ ## 12.0.0-next.6 +## 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 + +## 12.0.0-next.6 + ### Minor changes - fix `deleteElements` diff --git a/packages/react/src/hooks/useViewportHelper.ts b/packages/react/src/hooks/useViewportHelper.ts index 481bae15..39320459 100644 --- a/packages/react/src/hooks/useViewportHelper.ts +++ b/packages/react/src/hooks/useViewportHelper.ts @@ -86,8 +86,8 @@ const useViewportHelper = (): ViewportHelperFunctions => { panZoom?.setViewport(viewport, { duration: options?.duration }); }, - screenToFlowPosition: (position: XYPosition) => { - const { transform, snapToGrid, snapGrid, domNode } = store.getState(); + screenToFlowPosition: (position: XYPosition, options: { snapToGrid: boolean } = { snapToGrid: true }) => { + const { transform, snapGrid, domNode } = store.getState(); if (!domNode) { return position; @@ -100,7 +100,7 @@ const useViewportHelper = (): ViewportHelperFunctions => { y: position.y - domY, }; - return pointToRendererPoint(correctedPosition, transform, snapToGrid, snapGrid || [1, 1]); + return pointToRendererPoint(correctedPosition, transform, options.snapToGrid, snapGrid); }, flowToScreenPosition: (position: XYPosition) => { const { transform, domNode } = store.getState(); diff --git a/packages/react/src/types/general.ts b/packages/react/src/types/general.ts index 82cb23ce..9a36e41f 100644 --- a/packages/react/src/types/general.ts +++ b/packages/react/src/types/general.ts @@ -55,7 +55,7 @@ export type ViewportHelperFunctions = { fitView: FitView; setCenter: SetCenter; fitBounds: FitBounds; - screenToFlowPosition: (position: XYPosition) => XYPosition; + screenToFlowPosition: (position: XYPosition, options?: { snapToGrid: boolean }) => XYPosition; flowToScreenPosition: (position: XYPosition) => XYPosition; viewportInitialized: boolean; }; diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index b2fef4b0..96044f85 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -1,5 +1,11 @@ # @xyflow/svelte +## 0.0.34 + +## Minor changes + +- add second option param to `screenToFlowPosition` for configuring if `snapToGrid` should be used + ## 0.0.33 ### Bugfix diff --git a/packages/svelte/src/lib/hooks/useSvelteFlow.ts b/packages/svelte/src/lib/hooks/useSvelteFlow.ts index c4da0c2b..d47ee45b 100644 --- a/packages/svelte/src/lib/hooks/useSvelteFlow.ts +++ b/packages/svelte/src/lib/hooks/useSvelteFlow.ts @@ -53,7 +53,7 @@ export function useSvelteFlow(): { nodes?: (Node | { id: Node['id'] })[]; edges?: (Edge | { id: Edge['id'] })[]; }) => Promise<{ deletedNodes: Node[]; deletedEdges: Edge[] }>; - screenToFlowPosition: (position: XYPosition) => XYPosition; + screenToFlowPosition: (position: XYPosition, options?: { snapToGrid: boolean }) => XYPosition; flowToScreenPosition: (position: XYPosition) => XYPosition; viewport: Writable; updateNode: ( @@ -228,14 +228,17 @@ export function useSvelteFlow(): { deletedEdges: matchingEdges }; }, - screenToFlowPosition: (position: XYPosition) => { + screenToFlowPosition: ( + position: XYPosition, + options: { snapToGrid: boolean } = { snapToGrid: true } + ) => { const _domNode = get(domNode); if (!_domNode) { return position; } - const _snapGrid = get(snapGrid); + const _snapGrid = options.snapToGrid ? get(snapGrid) : false; const { x, y, zoom } = get(viewport); const { x: domX, y: domY } = _domNode.getBoundingClientRect(); const correctedPosition = { From e35e0348fe767e121830dec23da5047fcfedbc06 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 15 Jan 2024 16:53:55 +0100 Subject: [PATCH 04/19] added type docs for nodes & edges --- packages/system/src/types/edges.ts | 18 +++++++++++++++ packages/system/src/types/nodes.ts | 37 +++++++++++++++++++++++++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/packages/system/src/types/edges.ts b/packages/system/src/types/edges.ts index c2318f0c..0b461e89 100644 --- a/packages/system/src/types/edges.ts +++ b/packages/system/src/types/edges.ts @@ -2,22 +2,40 @@ import { Position } from './utils'; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type EdgeBase = { + /** unique id of an edge */ id: string; + /** type of an edge defined in edgeTypes */ type?: string; + /** id of source node */ source: string; + /** id of target node */ target: string; + /** id of source handle + * only needed if there are multiple handles per node + */ sourceHandle?: string | null; + /** id of target handle + * only needed if there are multiple handles per node + */ targetHandle?: string | null; animated?: boolean; hidden?: boolean; deletable?: boolean; selectable?: boolean; + /** arbitrary data passed to an edge */ data?: EdgeData; selected?: boolean; + /** set the marker on the beginning of an edge + * @example 'arrow', 'arrowclosed' or custom marker + */ markerStart?: EdgeMarkerType; + /** set the marker on the end of an edge + * @example 'arrow', 'arrowclosed' or custom marker + */ markerEnd?: EdgeMarkerType; zIndex?: number; ariaLabel?: string; + /** padding around the edge where interaction is still possible */ interactionWidth?: number; }; diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts index c96ee085..dd6b7ac2 100644 --- a/packages/system/src/types/nodes.ts +++ b/packages/system/src/types/nodes.ts @@ -3,16 +3,34 @@ import { internalsSymbol } from '../constants'; import type { XYPosition, Position, CoordinateExtent, HandleElement } from '.'; import { Optional } from '../utils/types'; -// this is stuff that all nodes share independent of the framework +/** + * Framework independent node data structure. + * + * @typeParam T - type of the node data + * @typeParam U - type of the node + */ export type NodeBase = { + /** unique id of a node */ id: string; + /** position of a node on the pane + * @example { x: 0, y: 0 } + */ position: XYPosition; + /** arbitrary data passed to a node */ data: T; + /** type of node defined in nodeTypes */ type?: U; + /** only relevant for default, source, target nodeType. controls source position + * @example 'right', 'left', 'top', 'bottom' + */ sourcePosition?: Position; + /** only relevant for default, source, target nodeType. controls target position + * @example 'right', 'left', 'top', 'bottom' + */ targetPosition?: Position; hidden?: boolean; selected?: boolean; + /** is node being dragged */ dragging?: boolean; draggable?: boolean; selectable?: boolean; @@ -21,11 +39,21 @@ export type NodeBase dragHandle?: string; width?: number | null; height?: number | null; + /** parent node id, used for creating sub-flows */ parentNode?: string; zIndex?: number; + /** boundary a node can be moved in + * @example 'parent' or [[0, 0], [100, 100]] + */ extent?: 'parent' | CoordinateExtent; expandParent?: boolean; ariaLabel?: string; + /** origin of the node relative to it's position + * @example + * [0.5, 0.5] // centers the node + * [0, 0] // top left + * [1, 1] // bottom right + */ origin?: NodeOrigin; handles?: NodeHandle[]; computed?: { @@ -47,7 +75,14 @@ export type NodeBase }; // props that get passed to a custom node +/** + * The node data structure that gets used for the nodes prop. + * + * @public + * @param id - The id of the node. + */ export type NodeProps = { + /** id of the node */ id: NodeBase['id']; data: T; dragHandle: NodeBase['dragHandle']; From 4d8fc79d218f19e46949e4fef558c78c816e8e7c Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 15 Jan 2024 17:00:16 +0100 Subject: [PATCH 05/19] refactor(svelte): add slot to controls, cleanup ControlButton types closes #3788 --- .../src/routes/examples/overview/+page.svelte | 10 ++++++++-- .../lib/plugins/Controls/ControlButton.svelte | 16 ++++++++++------ .../src/lib/plugins/Controls/Controls.svelte | 3 +++ .../svelte/src/lib/plugins/Controls/types.ts | 10 ++++++++++ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/examples/svelte/src/routes/examples/overview/+page.svelte b/examples/svelte/src/routes/examples/overview/+page.svelte index 44d54544..586af3b9 100644 --- a/examples/svelte/src/routes/examples/overview/+page.svelte +++ b/examples/svelte/src/routes/examples/overview/+page.svelte @@ -13,7 +13,8 @@ type Node, type Edge, ConnectionMode, - useSvelteFlow + useSvelteFlow, + ControlButton } from '@xyflow/svelte'; import CustomNode from './CustomNode.svelte'; @@ -187,7 +188,12 @@ connectionMode={ConnectionMode.Strict} attributionPosition={'top-center'} > - + + xy + console.log('control button')} + >log + diff --git a/packages/svelte/src/lib/plugins/Controls/ControlButton.svelte b/packages/svelte/src/lib/plugins/Controls/ControlButton.svelte index be64f19a..f2c05810 100644 --- a/packages/svelte/src/lib/plugins/Controls/ControlButton.svelte +++ b/packages/svelte/src/lib/plugins/Controls/ControlButton.svelte @@ -1,12 +1,16 @@ diff --git a/packages/svelte/src/lib/plugins/Controls/Controls.svelte b/packages/svelte/src/lib/plugins/Controls/Controls.svelte index a82c7c03..042b2be5 100644 --- a/packages/svelte/src/lib/plugins/Controls/Controls.svelte +++ b/packages/svelte/src/lib/plugins/Controls/Controls.svelte @@ -74,6 +74,7 @@ data-testid="svelte-flow__controls" aria-label={ariaLabel ?? 'Svelte Flow controls'} > + {#if showZoom} {:else}{/if} {/if} + + diff --git a/packages/svelte/src/lib/plugins/Controls/types.ts b/packages/svelte/src/lib/plugins/Controls/types.ts index 2aad9b39..4e0ab786 100644 --- a/packages/svelte/src/lib/plugins/Controls/types.ts +++ b/packages/svelte/src/lib/plugins/Controls/types.ts @@ -1,3 +1,4 @@ +import type { HTMLButtonAttributes } from 'svelte/elements'; import type { PanelPosition } from '@xyflow/system'; export type ControlsProps = { @@ -11,3 +12,12 @@ export type ControlsProps = { buttonColorHover?: string; 'aria-label'?: string; }; + +export type ControlButtonProps = HTMLButtonAttributes & { + class?: string; + bgColor?: string; + bgColorHover?: string; + color?: string; + colorHover?: string; + borderColor?: string; +}; From 1bc1ae855d087fd23ec980f92f2c689e205226ef Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 15 Jan 2024 17:32:30 +0100 Subject: [PATCH 06/19] refactor(util-functions): cleanup types so that node/edge types get infered --- packages/react/src/index.ts | 7 +- packages/react/src/utils/changes.ts | 2 +- packages/react/src/utils/general.ts | 64 ++----------------- packages/svelte/CHANGELOG.md | 2 + packages/svelte/src/lib/index.ts | 4 ++ .../svelte/src/lib/store/initial-store.ts | 2 +- packages/svelte/src/lib/utils/index.ts | 64 ++----------------- packages/system/src/utils/edges/general.ts | 4 +- packages/system/src/utils/graph.ts | 8 +-- 9 files changed, 30 insertions(+), 127 deletions(-) diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 20573392..3cabb813 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -29,7 +29,7 @@ export { useConnection } from './hooks/useConnection'; export { useNodeId } from './contexts/NodeIdContext'; export { applyNodeChanges, applyEdgeChanges, handleParentExpand } from './utils/changes'; -export { isNode, isEdge, getIncomers, getOutgoers, addEdge, updateEdge, getConnectedEdges } from './utils/general'; +export { isNode, isEdge } from './utils/general'; export * from './additional-components'; @@ -102,5 +102,10 @@ export { getStraightPath, getViewportForBounds, getNodesBounds, + getIncomers, + getOutgoers, + addEdge, + updateEdge, + getConnectedEdges, internalsSymbol, } from '@xyflow/system'; diff --git a/packages/react/src/utils/changes.ts b/packages/react/src/utils/changes.ts index 91a7a004..6bcddabd 100644 --- a/packages/react/src/utils/changes.ts +++ b/packages/react/src/utils/changes.ts @@ -267,7 +267,7 @@ export function getElementsDiffChanges({ lookup, }: { items: Node[] | undefined; - lookup: NodeLookup; + lookup: NodeLookup; }): NodeChange[]; export function getElementsDiffChanges({ items, diff --git a/packages/react/src/utils/general.ts b/packages/react/src/utils/general.ts index 5de3c3b2..2c2387fc 100644 --- a/packages/react/src/utils/general.ts +++ b/packages/react/src/utils/general.ts @@ -1,12 +1,4 @@ -import { - isNodeBase, - isEdgeBase, - addEdgeBase, - getOutgoersBase, - getIncomersBase, - updateEdgeBase, - getConnectedEdgesBase, -} from '@xyflow/system'; +import { isNodeBase, isEdgeBase } from '@xyflow/system'; import type { Edge, Node } from '../types'; @@ -17,7 +9,8 @@ import type { Edge, Node } from '../types'; * @param element - The element to test * @returns A boolean indicating whether the element is an Node */ -export const isNode = isNodeBase; +export const isNode = (element: unknown): element is NodeType => + isNodeBase(element); /** * Test whether an object is useable as an Edge @@ -26,52 +19,5 @@ export const isNode = isNodeBase; * @param element - The element to test * @returns A boolean indicating whether the element is an Edge */ -export const isEdge = isEdgeBase; - -/** - * Pass in a node, and get connected nodes where edge.source === node.id - * @public - * @param node - The node to get the connected nodes from - * @param nodes - The array of all nodes - * @param edges - The array of all edges - * @returns An array of nodes that are connected over eges where the source is the given node - */ -export const getOutgoers = getOutgoersBase; - -/** - * Pass in a node, and get connected nodes where edge.target === node.id - * @public - * @param node - The node to get the connected nodes from - * @param nodes - The array of all nodes - * @param edges - The array of all edges - * @returns An array of nodes that are connected over eges where the target is the given node - */ -export const getIncomers = getIncomersBase; - -/** - * This util is a convenience function to add a new Edge to an array of edges - * @remarks It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one. - * @public - * @param edgeParams - Either an Edge or a Connection you want to add - * @param edges - The array of all current edges - * @returns A new array of edges with the new edge added - */ -export const addEdge = addEdgeBase; - -/** - * A handy utility to update an existing Edge with new properties - * @param oldEdge - The edge you want to update - * @param newConnection - The new connection you want to update the edge with - * @param edges - The array of all current edges - * @param options.shouldReplaceId - should the id of the old edge be replaced with the new connection id - * @returns the updated edges array - */ -export const updateEdge = updateEdgeBase; - -/** - * Get all connecting edges for a given set of nodes - * @param nodes - Nodes you want to get the connected edges for - * @param edges - All edges - * @returns Array of edges that connect any of the given nodes with each other - */ -export const getConnectedEdges = getConnectedEdgesBase; +export const isEdge = (element: unknown): element is EdgeType => + isEdgeBase(element); diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 96044f85..23164fb0 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -5,6 +5,8 @@ ## Minor changes - add second option param to `screenToFlowPosition` for configuring if `snapToGrid` should be used +- add slot to `Controls` +- cleanup `ControlButton` types ## 0.0.33 diff --git a/packages/svelte/src/lib/index.ts b/packages/svelte/src/lib/index.ts index 9a706959..357b241a 100644 --- a/packages/svelte/src/lib/index.ts +++ b/packages/svelte/src/lib/index.ts @@ -112,5 +112,9 @@ export { getStraightPath, getViewportForBounds, getNodesBounds, + getIncomers, + getOutgoers, + getConnectedEdges, + addEdge, internalsSymbol } from '@xyflow/system'; diff --git a/packages/svelte/src/lib/store/initial-store.ts b/packages/svelte/src/lib/store/initial-store.ts index 90c59b2d..26c57c86 100644 --- a/packages/svelte/src/lib/store/initial-store.ts +++ b/packages/svelte/src/lib/store/initial-store.ts @@ -99,7 +99,7 @@ export const getInitialStore = ({ return { flowId: writable(null), nodes: createNodesStore(nextNodes, nodeLookup), - nodeLookup: readable(nodeLookup), + nodeLookup: readable>(nodeLookup), edgeLookup: readable(edgeLookup), visibleNodes: readable([]), edges: createEdgesStore(edges, connectionLookup, edgeLookup), diff --git a/packages/svelte/src/lib/utils/index.ts b/packages/svelte/src/lib/utils/index.ts index bad572dc..10f205ca 100644 --- a/packages/svelte/src/lib/utils/index.ts +++ b/packages/svelte/src/lib/utils/index.ts @@ -1,12 +1,4 @@ -import { - isNodeBase, - isEdgeBase, - addEdgeBase, - getOutgoersBase, - getIncomersBase, - updateEdgeBase, - getConnectedEdgesBase -} from '@xyflow/system'; +import { isNodeBase, isEdgeBase } from '@xyflow/system'; import type { Edge, Node } from '$lib/types'; @@ -17,7 +9,8 @@ import type { Edge, Node } from '$lib/types'; * @param element - The element to test * @returns A boolean indicating whether the element is an Node */ -export const isNode = isNodeBase; +export const isNode = (element: unknown): element is NodeType => + isNodeBase(element); /** * Test whether an object is useable as an Edge @@ -26,52 +19,5 @@ export const isNode = isNodeBase; * @param element - The element to test * @returns A boolean indicating whether the element is an Edge */ -export const isEdge = isEdgeBase; - -/** - * Pass in a node, and get connected nodes where edge.source === node.id - * @public - * @param node - The node to get the connected nodes from - * @param nodes - The array of all nodes - * @param edges - The array of all edges - * @returns An array of nodes that are connected over eges where the source is the given node - */ -export const getOutgoers = getOutgoersBase; - -/** - * Pass in a node, and get connected nodes where edge.target === node.id - * @public - * @param node - The node to get the connected nodes from - * @param nodes - The array of all nodes - * @param edges - The array of all edges - * @returns An array of nodes that are connected over eges where the target is the given node - */ -export const getIncomers = getIncomersBase; - -/** - * This util is a convenience function to add a new Edge to an array of edges - * @remarks It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one. - * @public - * @param edgeParams - Either an Edge or a Connection you want to add - * @param edges - The array of all current edges - * @returns A new array of edges with the new edge added - */ -export const addEdge = addEdgeBase; - -/** - * A handy utility to update an existing Edge with new properties - * @param oldEdge - The edge you want to update - * @param newConnection - The new connection you want to update the edge with - * @param edges - The array of all current edges - * @param options.shouldReplaceId - should the id of the old edge be replaced with the new connection id - * @returns the updated edges array - */ -export const updateEdge = updateEdgeBase; - -/** - * Get all connecting edges for a given set of nodes - * @param nodes - Nodes you want to get the connected edges for - * @param edges - All edges - * @returns Array of edges that connect any of the given nodes with each other - */ -export const getConnectedEdges = getConnectedEdgesBase; +export const isEdge = (element: unknown): element is EdgeType => + isEdgeBase(element); diff --git a/packages/system/src/utils/edges/general.ts b/packages/system/src/utils/edges/general.ts index 14e73318..f53db23a 100644 --- a/packages/system/src/utils/edges/general.ts +++ b/packages/system/src/utils/edges/general.ts @@ -98,7 +98,7 @@ const connectionExists = (edge: EdgeBase, edges: EdgeBase[]) => { * @param edges - The array of all current edges * @returns A new array of edges with the new edge added */ -export const addEdgeBase = ( +export const addEdge = ( edgeParams: EdgeType | Connection, edges: EdgeType[] ): EdgeType[] => { @@ -145,7 +145,7 @@ export type UpdateEdgeOptions = { * @param options.shouldReplaceId - should the id of the old edge be replaced with the new connection id * @returns the updated edges array */ -export const updateEdgeBase = ( +export const updateEdge = ( oldEdge: EdgeType, newConnection: Connection, edges: EdgeType[], diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index 6d341e5e..fa56a987 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -54,7 +54,7 @@ export const isNodeBase = (element: any): * @param edges - The array of all edges * @returns An array of nodes that are connected over eges where the source is the given node */ -export const getOutgoersBase = ( +export const getOutgoers = ( node: NodeType | { id: string }, nodes: NodeType[], edges: EdgeType[] @@ -81,7 +81,7 @@ export const getOutgoersBase = ( +export const getIncomers = ( node: NodeType | { id: string }, nodes: NodeType[], edges: EdgeType[] @@ -212,7 +212,7 @@ export const getNodesInside = ( * @param edges - All edges * @returns Array of edges that connect any of the given nodes with each other */ -export const getConnectedEdgesBase = ( +export const getConnectedEdges = ( nodes: NodeType[], edges: EdgeType[] ): EdgeType[] => { @@ -370,7 +370,7 @@ export async function getElementsToRemove edge.id); const deletableEdges = edges.filter((edge) => edge.deletable !== false); - const connectedEdges = getConnectedEdgesBase(matchingNodes, deletableEdges); + const connectedEdges = getConnectedEdges(matchingNodes, deletableEdges); const matchingEdges: EdgeType[] = connectedEdges; for (const edge of deletableEdges) { From 37f76275eb00127adef868fabad7286ff3a48dcb Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 15 Jan 2024 17:35:08 +0100 Subject: [PATCH 07/19] chore(libs): update changelogs --- packages/react/CHANGELOG.md | 1 + packages/svelte/CHANGELOG.md | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index 951f4895..66a7b055 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -7,6 +7,7 @@ - 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 +- infer types for `getIncomers`, `getOutgoers`, `updateEdge`, `addEdge` and `getConnectedEdges` thanks @joeyballentine ## 12.0.0-next.6 diff --git a/packages/svelte/CHANGELOG.md b/packages/svelte/CHANGELOG.md index 23164fb0..ffc72eeb 100644 --- a/packages/svelte/CHANGELOG.md +++ b/packages/svelte/CHANGELOG.md @@ -7,6 +7,7 @@ - add second option param to `screenToFlowPosition` for configuring if `snapToGrid` should be used - add slot to `Controls` - cleanup `ControlButton` types +- infer types for `getIncomers`, `getOutgoers`, `updateEdge`, `addEdge` and `getConnectedEdges` thanks @joeyballentine ## 0.0.33 From e23cf8256a624af787817fdd202bd43fcd57f153 Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 15 Jan 2024 17:37:18 +0100 Subject: [PATCH 08/19] chore(svelte): export updateEdge --- packages/svelte/src/lib/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/svelte/src/lib/index.ts b/packages/svelte/src/lib/index.ts index 357b241a..4c3737ea 100644 --- a/packages/svelte/src/lib/index.ts +++ b/packages/svelte/src/lib/index.ts @@ -116,5 +116,6 @@ export { getOutgoers, getConnectedEdges, addEdge, + updateEdge, internalsSymbol } from '@xyflow/system'; From 8c32b426f4906afa544c5e99e5df3218030f6cc8 Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 15 Jan 2024 17:38:54 +0100 Subject: [PATCH 09/19] added typedocs for plugins --- .../additional-components/Background/types.ts | 13 ++++++++++ .../additional-components/Controls/types.ts | 14 +++++++++++ .../additional-components/MiniMap/types.ts | 18 +++++++++++++ .../NodeResizer/types.ts | 25 +++++++++++++++++++ .../NodeToolbar/types.ts | 9 +++++++ .../src/lib/plugins/Background/types.ts | 11 ++++++++ .../svelte/src/lib/plugins/Controls/types.ts | 7 ++++++ .../svelte/src/lib/plugins/Minimap/types.ts | 20 +++++++++++++++ .../src/lib/plugins/NodeResizer/types.ts | 25 +++++++++++++++++++ .../src/lib/plugins/NodeToolbar/types.ts | 9 +++++++ 10 files changed, 151 insertions(+) diff --git a/packages/react/src/additional-components/Background/types.ts b/packages/react/src/additional-components/Background/types.ts index 5d21b23f..a9fca265 100644 --- a/packages/react/src/additional-components/Background/types.ts +++ b/packages/react/src/additional-components/Background/types.ts @@ -8,14 +8,27 @@ export enum BackgroundVariant { export type BackgroundProps = { id?: string; + /** color of the pattern */ color?: string; + /** color of the background */ bgColor?: string; + /** class applied to the container */ className?: string; + /** class applied to the pattern */ patternClassName?: string; + /** gap between repetitions of the pattern */ gap?: number | [number, number]; + /** size of a single pattern element */ size?: number; + /** offset of the pattern */ offset?: number; + /** line width of the Line pattern */ lineWidth?: number; + /** variant of the pattern + * @example BackgroundVariant.Lines, BackgroundVariant.Dots, BackgroundVariant.Cross + * 'lines', 'dots', 'cross' + */ variant?: BackgroundVariant; + /** style applied to the container */ style?: CSSProperties; }; diff --git a/packages/react/src/additional-components/Controls/types.ts b/packages/react/src/additional-components/Controls/types.ts index e687b09b..eb36d61c 100644 --- a/packages/react/src/additional-components/Controls/types.ts +++ b/packages/react/src/additional-components/Controls/types.ts @@ -4,17 +4,31 @@ import type { PanelPosition } from '@xyflow/system'; import type { FitViewOptions } from '../../types'; export type ControlProps = { + /** show button for zoom in/out */ showZoom?: boolean; + /** show button for fit view */ showFitView?: boolean; + /** show button for toggling interactivity */ showInteractive?: boolean; + /** options being used when fit view button is clicked */ fitViewOptions?: FitViewOptions; + /** callback when zoom in button is clicked */ onZoomIn?: () => void; + /** callback when zoom out button is clicked */ onZoomOut?: () => void; + /** callback when fit view button is clicked */ onFitView?: () => void; + /** callback when interactivity is toggled */ onInteractiveChange?: (interactiveStatus: boolean) => void; + /** position of the controls on the pane + * @example PanelPosition.TopLeft, PanelPosition.TopRight, + * PanelPosition.BottomLeft, PanelPosition.BottomRight + */ position?: PanelPosition; children?: ReactNode; + /** style applied to container */ style?: React.CSSProperties; + /**className applied to container */ className?: string; 'aria-label'?: string; }; diff --git a/packages/react/src/additional-components/MiniMap/types.ts b/packages/react/src/additional-components/MiniMap/types.ts index 3b24b08d..0a49fd07 100644 --- a/packages/react/src/additional-components/MiniMap/types.ts +++ b/packages/react/src/additional-components/MiniMap/types.ts @@ -7,23 +7,41 @@ import type { Node } from '../../types'; export type GetMiniMapNodeAttribute = (node: NodeType) => string; export type MiniMapProps = Omit, 'onClick'> & { + /** color of nodes on minimap */ nodeColor?: string | GetMiniMapNodeAttribute; + /** stroke color of nodes on minimap */ nodeStrokeColor?: string | GetMiniMapNodeAttribute; + /** className applied to nodes on minimap */ nodeClassName?: string | GetMiniMapNodeAttribute; + /** boder radius of nodes on minimap */ nodeBorderRadius?: number; + /** stroke width of nodes on minimap */ nodeStrokeWidth?: number; + /** component used to render nodes on minimap */ nodeComponent?: ComponentType; + /** color of mask representing viewport */ maskColor?: string; + /** stroke color of mask representing viewport */ maskStrokeColor?: string; + /** stroke width of mask representing viewport */ maskStrokeWidth?: number; + /** position of minimap on pane + * @example PanelPosition.TopLeft, PanelPosition.TopRight, + * PanelPosition.BottomLeft, PanelPosition.BottomRight + */ position?: PanelPosition; + /** callback caled when minimap is clicked*/ onClick?: (event: MouseEvent, position: XYPosition) => void; + /** callback called when node on minimap is clicked */ onNodeClick?: (event: MouseEvent, node: NodeType) => void; pannable?: boolean; zoomable?: boolean; ariaLabel?: string | null; + /** invert direction when panning the minimap viewport */ inversePan?: boolean; + /** step size for zooming in/out on minimap */ zoomStep?: number; + /** offset the viewport on the minmap, acts like a padding */ offsetScale?: number; }; diff --git a/packages/react/src/additional-components/NodeResizer/types.ts b/packages/react/src/additional-components/NodeResizer/types.ts index 3a414517..11a95bcf 100644 --- a/packages/react/src/additional-components/NodeResizer/types.ts +++ b/packages/react/src/additional-components/NodeResizer/types.ts @@ -10,21 +10,39 @@ import type { } from '@xyflow/system'; export type NodeResizerProps = { + /** id of the node it is resizing + * @remarks optional if used inside custom node + */ nodeId?: string; + /** color of the resize handle */ color?: string; + /** className applied to handle */ handleClassName?: string; + /** style applied to handle */ handleStyle?: CSSProperties; + /** className applied to line */ lineClassName?: string; + /** style applied to line */ lineStyle?: CSSProperties; + /** are the controls visible */ isVisible?: boolean; + /** minimum width of node */ minWidth?: number; + /** minimum height of node */ minHeight?: number; + /** maximum width of node */ maxWidth?: number; + /** maximum height of node */ maxHeight?: number; + /** keep aspect ratio when resizing */ keepAspectRatio?: boolean; + /** callback to determine if node should resize */ shouldResize?: ShouldResize; + /** callback called when resizing starts */ onResizeStart?: OnResizeStart; + /** callback called when resizing */ onResize?: OnResize; + /** callback called when resizing ends */ onResizeEnd?: OnResizeEnd; }; @@ -42,7 +60,14 @@ export type ResizeControlProps = Pick< | 'onResize' | 'onResizeEnd' > & { + /** position of the control + * @example ControlPosition.TopLeft, ControlPosition.TopRight, + * ControlPosition.BottomLeft, ControlPosition.BottomRight + */ position?: ControlPosition; + /** variant of the control + * @example ResizeControlVariant.Handle, ResizeControlVariant.Line + */ variant?: ResizeControlVariant; className?: string; style?: CSSProperties; diff --git a/packages/react/src/additional-components/NodeToolbar/types.ts b/packages/react/src/additional-components/NodeToolbar/types.ts index f08859dc..717f22ce 100644 --- a/packages/react/src/additional-components/NodeToolbar/types.ts +++ b/packages/react/src/additional-components/NodeToolbar/types.ts @@ -2,9 +2,18 @@ import type { HTMLAttributes } from 'react'; import type { Position, Align } from '@xyflow/system'; export type NodeToolbarProps = HTMLAttributes & { + /** id of the node, or array of ids the toolbar should be displayed at */ nodeId?: string | string[]; isVisible?: boolean; + /** position of the toolbar relative to the node + * @example Position.TopLeft, Position.TopRight, + * Position.BottomLeft, Position.BottomRight + */ position?: Position; + /** offset the toolbar from the node */ offset?: number; + /** align the toolbar relative to the node + * @example Align.Start, Align.Center, Align.End + */ align?: Align; }; diff --git a/packages/svelte/src/lib/plugins/Background/types.ts b/packages/svelte/src/lib/plugins/Background/types.ts index 3ca5667d..e8aaf6e8 100644 --- a/packages/svelte/src/lib/plugins/Background/types.ts +++ b/packages/svelte/src/lib/plugins/Background/types.ts @@ -6,12 +6,23 @@ export enum BackgroundVariant { export type BackgroundProps = { id?: string; + /** color of the background */ bgColor?: string; + /** color of the pattern */ patternColor?: string; + /** class applied to the pattern */ patternClass?: string; + /** class applied to the container */ class?: string; + /** gap between repetitions of the pattern */ gap?: number | [number, number]; + /** size of a single pattern element */ size?: number; + /** line width of the Line pattern */ lineWidth?: number; + /** variant of the pattern + * @example BackgroundVariant.Lines, BackgroundVariant.Dots, BackgroundVariant.Cross + * 'lines', 'dots', 'cross' + */ variant?: BackgroundVariant; }; diff --git a/packages/svelte/src/lib/plugins/Controls/types.ts b/packages/svelte/src/lib/plugins/Controls/types.ts index 2aad9b39..d3497645 100644 --- a/packages/svelte/src/lib/plugins/Controls/types.ts +++ b/packages/svelte/src/lib/plugins/Controls/types.ts @@ -1,9 +1,16 @@ import type { PanelPosition } from '@xyflow/system'; export type ControlsProps = { + /** position of the controls on the pane + * @example PanelPosition.TopLeft, PanelPosition.TopRight, + * PanelPosition.BottomLeft, PanelPosition.BottomRight + */ position?: PanelPosition; + /** show button for zoom in/out */ showZoom?: boolean; + /** show button for fit view */ showFitView?: boolean; + /** show button for toggling interactivity */ showLock?: boolean; buttonBgColor?: string; buttonBgColorHover?: string; diff --git a/packages/svelte/src/lib/plugins/Minimap/types.ts b/packages/svelte/src/lib/plugins/Minimap/types.ts index d39aab7a..9b725e45 100644 --- a/packages/svelte/src/lib/plugins/Minimap/types.ts +++ b/packages/svelte/src/lib/plugins/Minimap/types.ts @@ -5,25 +5,45 @@ import type { Node } from '$lib/types'; export type GetMiniMapNodeAttribute = (node: Node) => string; export type MiniMapProps = { + /** background color of minimap */ bgColor?: string; + /** color of nodes on the minimap */ nodeColor?: string | GetMiniMapNodeAttribute; + /** stroke color of nodes on the minimap */ nodeStrokeColor?: string | GetMiniMapNodeAttribute; + /** class applied to nodes on the minimap */ nodeClass?: string | GetMiniMapNodeAttribute; + /** border radius of nodes on the minimap */ nodeBorderRadius?: number; + /** stroke width of nodes on the minimap */ nodeStrokeWidth?: number; + /** color of the mask representing viewport */ maskColor?: string; + /** stroke color of the mask representing viewport */ maskStrokeColor?: string; + /** stroke width of the mask representing viewport */ maskStrokeWidth?: number; + /** position of the minimap on the pane + * @example PanelPosition.TopLeft, PanelPosition.TopRight, + * PanelPosition.BottomLeft, PanelPosition.BottomRight + */ position?: PanelPosition; + /** class applied to container */ class?: string; + /** style applied to container */ style?: string; + /** aria-label applied to container */ ariaLabel?: string | null; + /** width of minimap */ width?: number; + /** height of minimap */ height?: number; // onClick?: (event: MouseEvent, position: XYPosition) => void; // onNodeClick?: (event: MouseEvent, node: Node) => void; pannable?: boolean; zoomable?: boolean; + /** invert the direction when panning the minimap viewport */ inversePan?: boolean; + /** step size for zooming in/out */ zoomStep?: number; }; diff --git a/packages/svelte/src/lib/plugins/NodeResizer/types.ts b/packages/svelte/src/lib/plugins/NodeResizer/types.ts index b37e55c1..ee068275 100644 --- a/packages/svelte/src/lib/plugins/NodeResizer/types.ts +++ b/packages/svelte/src/lib/plugins/NodeResizer/types.ts @@ -8,21 +8,39 @@ import type { } from '@xyflow/system'; export type NodeResizerProps = { + /** id of the node it is resizing + * @remarks optional if used inside custom node + */ nodeId?: string; + /** color of the resize handle */ color?: string; + /** class applied to handle */ handleClass?: string; + /** style applied to handle */ handleStyle?: string; + /** class applied to line */ lineClass?: string; + /** style applied to line */ lineStyle?: string; + /** are the controls visible */ isVisible?: boolean; + /** minimum width of node */ minWidth?: number; + /** minimum height of node */ minHeight?: number; + /** maximum width of node */ maxWidth?: number; + /** maximum height of node */ maxHeight?: number; + /** keep aspect ratio when resizing */ keepAspectRatio?: boolean; + /** callback to determine if node should resize */ shouldResize?: ShouldResize; + /** callback called when resizing starts */ onResizeStart?: OnResizeStart; + /** callback called when resizing */ onResize?: OnResize; + /** callback called when resizing ends */ onResizeEnd?: OnResizeEnd; }; @@ -40,7 +58,14 @@ export type ResizeControlProps = Pick< | 'onResize' | 'onResizeEnd' > & { + /** position of control + * @example ControlPosition.TopLeft, ControlPosition.TopRight, + * ControlPosition.BottomLeft, ControlPosition.BottomRight + */ position?: ControlPosition; + /** variant of control + * @example ResizeControlVariant.Handle, ResizeControlVariant.Line + */ variant?: ResizeControlVariant; class?: string; style?: string; diff --git a/packages/svelte/src/lib/plugins/NodeToolbar/types.ts b/packages/svelte/src/lib/plugins/NodeToolbar/types.ts index 6c812de2..cc6d80a1 100644 --- a/packages/svelte/src/lib/plugins/NodeToolbar/types.ts +++ b/packages/svelte/src/lib/plugins/NodeToolbar/types.ts @@ -1,9 +1,18 @@ import type { Position, Align } from '@xyflow/system'; export type NodeToolbarProps = { + /** id of the node, or array of ids the toolbar should be displayed at */ nodeId?: string | string[]; + /** position of the toolbar relative to the node + * @example Position.TopLeft, Position.TopRight, + * Position.BottomLeft, Position.BottomRight + */ position?: Position; + /** align the toolbar relative to the node + * @example Align.Start, Align.Center, Align.End + */ align?: Align; + /** offset the toolbar from the node */ offset?: number; isVisible?: boolean; }; From f32a92ecca1abf8d9bd351c50307d4ea268f1684 Mon Sep 17 00:00:00 2001 From: moklick Date: Mon, 15 Jan 2024 17:44:29 +0100 Subject: [PATCH 10/19] refactor(utils): use utils from system package --- packages/react/src/components/Handle/index.tsx | 2 +- packages/react/src/hooks/useNodesEdgesState.ts | 14 ++++++++++---- packages/react/src/types/instance.ts | 3 --- packages/svelte/src/lib/container/Pane/Pane.svelte | 8 ++++++-- packages/svelte/src/lib/store/index.ts | 2 +- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/react/src/components/Handle/index.tsx b/packages/react/src/components/Handle/index.tsx index 41bf492c..86f8da2a 100644 --- a/packages/react/src/components/Handle/index.tsx +++ b/packages/react/src/components/Handle/index.tsx @@ -12,6 +12,7 @@ import { XYHandle, getHostForElement, isMouseEvent, + addEdge, type HandleProps, type Connection, type HandleType, @@ -19,7 +20,6 @@ import { import { useStore, useStoreApi } from '../../hooks/useStore'; import { useNodeId } from '../../contexts/NodeIdContext'; -import { addEdge } from '../../utils/'; import { type ReactFlowState } from '../../types'; export type HandleComponentProps = HandleProps & Omit, 'id'>; diff --git a/packages/react/src/hooks/useNodesEdgesState.ts b/packages/react/src/hooks/useNodesEdgesState.ts index c134472b..766ee049 100644 --- a/packages/react/src/hooks/useNodesEdgesState.ts +++ b/packages/react/src/hooks/useNodesEdgesState.ts @@ -12,9 +12,12 @@ import type { Node, NodeChange, Edge, EdgeChange } from '../types'; */ export function useNodesState( initialNodes: NodeType[] -): [NodeType[], Dispatch>, (changes: NodeChange[]) => void] { +): [NodeType[], Dispatch>, (changes: NodeChange[]) => void] { const [nodes, setNodes] = useState(initialNodes); - const onNodesChange = useCallback((changes: NodeChange[]) => setNodes((nds) => applyNodeChanges(changes, nds)), []); + const onNodesChange = useCallback( + (changes: NodeChange[]) => setNodes((nds) => applyNodeChanges(changes, nds)), + [] + ); return [nodes, setNodes, onNodesChange]; } @@ -28,9 +31,12 @@ export function useNodesState( */ export function useEdgesState( initialEdges: EdgeType[] -): [EdgeType[], Dispatch>, (changes: EdgeChange[]) => void] { +): [EdgeType[], Dispatch>, (changes: EdgeChange[]) => void] { const [edges, setEdges] = useState(initialEdges); - const onEdgesChange = useCallback((changes: EdgeChange[]) => setEdges((eds) => applyEdgeChanges(changes, eds)), []); + const onEdgesChange = useCallback( + (changes: EdgeChange[]) => setEdges((eds) => applyEdgeChanges(changes, eds)), + [] + ); return [edges, setEdges, onEdgesChange]; } diff --git a/packages/react/src/types/instance.ts b/packages/react/src/types/instance.ts index 0bf01b20..6b37fd14 100644 --- a/packages/react/src/types/instance.ts +++ b/packages/react/src/types/instance.ts @@ -45,9 +45,6 @@ export namespace Instance { area: Rect, partially?: boolean ) => boolean; - 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, diff --git a/packages/svelte/src/lib/container/Pane/Pane.svelte b/packages/svelte/src/lib/container/Pane/Pane.svelte index 21ed2a61..cce95636 100644 --- a/packages/svelte/src/lib/container/Pane/Pane.svelte +++ b/packages/svelte/src/lib/container/Pane/Pane.svelte @@ -30,10 +30,14 @@