Merge pull request #4258 from xyflow/next

React Flow 12.0.0-next.18
This commit is contained in:
Moritz Klack
2024-05-08 22:49:13 +02:00
committed by GitHub
22 changed files with 113 additions and 92 deletions
@@ -12,7 +12,6 @@ import {
Node, Node,
Edge, Edge,
} from '@xyflow/react'; } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
const initialNodes: Node[] = [ const initialNodes: Node[] = [
{ {
@@ -1,5 +1,4 @@
import { ReactFlow } from '@xyflow/react'; import { ReactFlow } from '@xyflow/react';
import '@xyflow/react/dist/style.css';
import { useDispatch, useSelector, Provider } from 'react-redux'; import { useDispatch, useSelector, Provider } from 'react-redux';
@@ -1,7 +1,7 @@
import { memo, FC, useEffect, useCallback } from 'react'; import { memo, FC, useEffect, useCallback } from 'react';
import { Handle, Position, NodeProps, useHandleConnections, Connection, HandleComponentProps } from '@xyflow/react'; import { Handle, Position, NodeProps, useHandleConnections, Connection, HandleProps } from '@xyflow/react';
function CustomHandle({ nodeId, ...handleProps }: HandleComponentProps & { nodeId: string }) { function CustomHandle({ nodeId, ...handleProps }: HandleProps & { nodeId: string }) {
const onConnect = useCallback( const onConnect = useCallback(
(connections: Connection[]) => console.log('onConnect handler, node id:', nodeId, connections), (connections: Connection[]) => console.log('onConnect handler, node id:', nodeId, connections),
[nodeId] [nodeId]
@@ -1,7 +1,7 @@
import { memo, FC, useEffect, useCallback } from 'react'; import { memo, FC, useEffect, useCallback } from 'react';
import { Handle, Position, NodeProps, useHandleConnections, Connection, HandleComponentProps } from '@xyflow/react'; import { Handle, Position, NodeProps, useHandleConnections, Connection, HandleProps } from '@xyflow/react';
function CustomHandle({ nodeId, ...handleProps }: HandleComponentProps & { nodeId: string }) { function CustomHandle({ nodeId, ...handleProps }: HandleProps & { nodeId: string }) {
const onConnect = useCallback( const onConnect = useCallback(
(connections: Connection[]) => { (connections: Connection[]) => {
console.log('onConnect handler, node id:', nodeId, connections); console.log('onConnect handler, node id:', nodeId, connections);
+8
View File
@@ -1,5 +1,12 @@
# @xyflow/react # @xyflow/react
## 12.0.0-next.18
- don't show nodeTypes warning if not necessary you've created a new nodeTypes or edgeTypes
- add node resizer styles to base.css
- remove `HandleComponentProps` type, only export `HandleProps` type
- add warning when styles not loaded
## 12.0.0-next.17 ## 12.0.0-next.17
- fix broken `defaultNodes` - fix broken `defaultNodes`
@@ -9,6 +16,7 @@
- return user node in node event handlers - return user node in node event handlers
- cleanup `useReactFlow` - cleanup `useReactFlow`
- export `KeyCode` and `Align` type - export `KeyCode` and `Align` type
- remove `Instance` in favour of `ReactFlowInstance` type
## 12.0.0-next.16 ## 12.0.0-next.16
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@xyflow/react", "name": "@xyflow/react",
"version": "12.0.0-next.17", "version": "12.0.0-next.18",
"description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.", "description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.",
"keywords": [ "keywords": [
"react", "react",
@@ -14,10 +14,11 @@ import {
getHostForElement, getHostForElement,
isMouseEvent, isMouseEvent,
addEdge, addEdge,
type HandleProps, type HandleProps as HandlePropsSystem,
type Connection, type Connection,
type HandleType, type HandleType,
ConnectionMode, ConnectionMode,
OnConnect,
} from '@xyflow/system'; } from '@xyflow/system';
import { useStore, useStoreApi } from '../../hooks/useStore'; import { useStore, useStoreApi } from '../../hooks/useStore';
@@ -25,7 +26,10 @@ import { useNodeId } from '../../contexts/NodeIdContext';
import { type ReactFlowState } from '../../types'; import { type ReactFlowState } from '../../types';
import { fixedForwardRef } from '../../utils'; import { fixedForwardRef } from '../../utils';
export interface HandleComponentProps extends HandleProps, Omit<HTMLAttributes<HTMLDivElement>, 'id'> {} export interface HandleProps extends HandlePropsSystem, Omit<HTMLAttributes<HTMLDivElement>, 'id'> {
/** Callback called when connection is made */
onConnect?: OnConnect;
}
const selector = (s: ReactFlowState) => ({ const selector = (s: ReactFlowState) => ({
connectOnClick: s.connectOnClick, connectOnClick: s.connectOnClick,
@@ -75,7 +79,7 @@ function HandleComponent(
onMouseDown, onMouseDown,
onTouchStart, onTouchStart,
...rest ...rest
}: HandleComponentProps, }: HandleProps,
ref: ForwardedRef<HTMLDivElement> ref: ForwardedRef<HTMLDivElement>
) { ) {
const handleId = id || null; const handleId = id || null;
@@ -9,6 +9,7 @@ import { useViewportSync } from '../../hooks/useViewportSync';
import { ConnectionLineWrapper } from '../../components/ConnectionLine'; import { ConnectionLineWrapper } from '../../components/ConnectionLine';
import { useNodeOrEdgeTypesWarning } from './useNodeOrEdgeTypesWarning'; import { useNodeOrEdgeTypesWarning } from './useNodeOrEdgeTypesWarning';
import type { Edge, Node, ReactFlowProps } from '../../types'; import type { Edge, Node, ReactFlowProps } from '../../types';
import { useStylesLoadedWarning } from './useStylesLoadedWarning';
export type GraphViewProps<NodeType extends Node = Node, EdgeType extends Edge = Edge> = Omit< export type GraphViewProps<NodeType extends Node = Node, EdgeType extends Edge = Edge> = Omit<
ReactFlowProps<NodeType, EdgeType>, ReactFlowProps<NodeType, EdgeType>,
@@ -104,6 +105,7 @@ function GraphViewComponent<NodeType extends Node = Node, EdgeType extends Edge
}: GraphViewProps<NodeType, EdgeType>) { }: GraphViewProps<NodeType, EdgeType>) {
useNodeOrEdgeTypesWarning(nodeTypes); useNodeOrEdgeTypesWarning(nodeTypes);
useNodeOrEdgeTypesWarning(edgeTypes); useNodeOrEdgeTypesWarning(edgeTypes);
useStylesLoadedWarning();
useOnInitHandler(onInit); useOnInitHandler(onInit);
useViewportSync(viewport); useViewportSync(viewport);
@@ -1,27 +1,36 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { errorMessages } from '@xyflow/system'; import { errorMessages } from '@xyflow/system';
import type { EdgeTypes, NodeTypes } from '../../types';
import { useStoreApi } from '../../hooks/useStore'; import { useStoreApi } from '../../hooks/useStore';
import type { EdgeTypes, NodeTypes } from '../../types';
const emptyTypes = {}; const emptyTypes = {};
/* /**
* This hook warns the user if node or edgeTypes change. * This hook warns the user if nodeTypes or edgeTypes changed.
* It is only used in development mode.
*
* @internal
*/ */
export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes?: NodeTypes): void; export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes?: NodeTypes): void;
export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes?: EdgeTypes): void; export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes?: EdgeTypes): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes: any = emptyTypes): any { export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes: any = emptyTypes): any {
const updateCount = useRef(0); const typesRef = useRef(nodeOrEdgeTypes);
const store = useStoreApi(); const store = useStoreApi();
useEffect(() => { useEffect(() => {
if (process.env.NODE_ENV === 'development') { if (process.env.NODE_ENV === 'development') {
if (updateCount.current > 1) { const usedKeys = new Set([...Object.keys(typesRef.current), ...Object.keys(nodeOrEdgeTypes)]);
store.getState().onError?.('002', errorMessages['error002']());
for (const key of usedKeys) {
if (typesRef.current[key] !== nodeOrEdgeTypes[key]) {
store.getState().onError?.('002', errorMessages['error002']());
break;
}
} }
updateCount.current += 1;
typesRef.current = nodeOrEdgeTypes;
} }
}, [nodeOrEdgeTypes]); }, [nodeOrEdgeTypes]);
} }
@@ -0,0 +1,23 @@
import { useEffect, useRef } from 'react';
import { errorMessages } from '@xyflow/system';
import { useStoreApi } from '../../hooks/useStore';
export function useStylesLoadedWarning() {
const store = useStoreApi();
const checked = useRef(false);
useEffect(() => {
if (process.env.NODE_ENV === 'development') {
if (!checked.current) {
const pane = document.querySelector('.react-flow__pane');
if (pane && !(window.getComputedStyle(pane).zIndex === '1')) {
store.getState().onError?.('013', errorMessages['error013']('react'));
}
checked.current = true;
}
}
}, []);
}
+1 -1
View File
@@ -1,5 +1,5 @@
export { default as ReactFlow } from './container/ReactFlow'; export { default as ReactFlow } from './container/ReactFlow';
export { Handle, type HandleComponentProps } from './components/Handle'; export { Handle, type HandleProps } from './components/Handle';
export { EdgeText } from './components/Edges/EdgeText'; export { EdgeText } from './components/Edges/EdgeText';
export { StraightEdge } from './components/Edges/StraightEdge'; export { StraightEdge } from './components/Edges/StraightEdge';
export { StepEdge } from './components/Edges/StepEdge'; export { StepEdge } from './components/Edges/StepEdge';
+3 -3
View File
@@ -1,4 +1,4 @@
import { ReactNode, Ref, RefAttributes, forwardRef } from 'react'; import { type Ref, type RefAttributes, forwardRef } from 'react';
import { isNodeBase, isEdgeBase } from '@xyflow/system'; import { isNodeBase, isEdgeBase } from '@xyflow/system';
import type { Edge, Node } from '../types'; import type { Edge, Node } from '../types';
@@ -25,8 +25,8 @@ export const isEdge = <EdgeType extends Edge = Edge>(element: unknown): element
// eslint-disable-next-line @typescript-eslint/ban-types // eslint-disable-next-line @typescript-eslint/ban-types
export function fixedForwardRef<T, P = {}>( export function fixedForwardRef<T, P = {}>(
render: (props: P, ref: Ref<T>) => ReactNode render: (props: P, ref: Ref<T>) => JSX.Element
): (props: P & RefAttributes<T>) => ReactNode { ): (props: P & RefAttributes<T>) => JSX.Element {
// eslint-disable-next-line @typescript-eslint/no-explicit-any // eslint-disable-next-line @typescript-eslint/no-explicit-any
return forwardRef(render) as any; return forwardRef(render) as any;
} }
+12
View File
@@ -1,5 +1,17 @@
# @xyflow/svelte # @xyflow/svelte
## 0.1.3
- fix `NodeToolbar` for subflows
## 0.1.2
- export `InternalNode` type
## 0.1.1
- export `useInternalNode` hook
## 0.1.0 ## 0.1.0
This is a bigger update for Svelte Flow to keep up with the latest changes we made for React Flow and the Svelte5 rewrite. The biggest change is the separation of user nodes (type `Node`) and internal nodes (type `InternalNode`), which includes a renaming of the `node.computed` attribute to `node.measured`. In the previous versions, we stored internals in `node[internalsSymbol]`. This doesn't exist anymore, but we only add it to our internal nodes in `node.internals.`. This is a bigger update for Svelte Flow to keep up with the latest changes we made for React Flow and the Svelte5 rewrite. The biggest change is the separation of user nodes (type `Node`) and internal nodes (type `InternalNode`), which includes a renaming of the `node.computed` attribute to `node.measured`. In the previous versions, we stored internals in `node[internalsSymbol]`. This doesn't exist anymore, but we only add it to our internal nodes in `node.internals.`.
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@xyflow/svelte", "name": "@xyflow/svelte",
"version": "0.1.0", "version": "0.1.3",
"description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.", "description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.",
"keywords": [ "keywords": [
"svelte", "svelte",
@@ -13,9 +13,9 @@
} from '@xyflow/system'; } from '@xyflow/system';
import { useStore } from '$lib/store'; import { useStore } from '$lib/store';
import type { HandleComponentProps } from '$lib/types'; import type { HandleProps } from '$lib/types';
type $$Props = HandleComponentProps; type $$Props = HandleProps;
export let id: $$Props['id'] = undefined; export let id: $$Props['id'] = undefined;
export let type: $$Props['type'] = 'source'; export let type: $$Props['type'] = 'source';
+10 -2
View File
@@ -32,6 +32,7 @@ export * from '$lib/hooks/useConnection';
export * from '$lib/hooks/useNodesEdges'; export * from '$lib/hooks/useNodesEdges';
export * from '$lib/hooks/useHandleConnections'; export * from '$lib/hooks/useHandleConnections';
export * from '$lib/hooks/useNodesData'; export * from '$lib/hooks/useNodesData';
export * from '$lib/hooks/useInternalNode';
export { useInitialized, useNodesInitialized } from '$lib/hooks/useInitialized'; export { useInitialized, useNodesInitialized } from '$lib/hooks/useInitialized';
// types // types
@@ -45,8 +46,15 @@ export type {
EdgeTypes, EdgeTypes,
DefaultEdgeOptions DefaultEdgeOptions
} from '$lib/types/edges'; } from '$lib/types/edges';
export type { HandleComponentProps, FitViewOptions } from '$lib/types/general'; export type { HandleProps, FitViewOptions } from '$lib/types/general';
export type { Node, NodeTypes, DefaultNodeOptions, BuiltInNode, NodeProps } from '$lib/types/nodes'; export type {
Node,
NodeTypes,
DefaultNodeOptions,
BuiltInNode,
NodeProps,
InternalNode
} from '$lib/types/nodes';
export type { SvelteFlowStore } from '$lib/store/types'; export type { SvelteFlowStore } from '$lib/store/types';
// system types // system types
@@ -42,18 +42,7 @@
} }
$: { $: {
let nodeRect: Rect | undefined = undefined; const nodeRect = getNodesBounds(toolbarNodes, { nodeOrigin: $nodeOrigin });
if (toolbarNodes.length === 1) {
const toolbarNode = toolbarNodes[0];
nodeRect = {
...toolbarNode.position,
width: toolbarNode.measured.width ?? toolbarNode.width ?? 0,
height: toolbarNode.measured.height ?? toolbarNode.height ?? 0
};
} else if (toolbarNodes.length > 1) {
nodeRect = getNodesBounds(toolbarNodes, { nodeOrigin: $nodeOrigin });
}
if (nodeRect) { if (nodeRect) {
transform = getNodeToolbarTransform(nodeRect, $viewport, _position, _offset, _align); transform = getNodeToolbarTransform(nodeRect, $viewport, _position, _offset, _align);
+3 -27
View File
@@ -1,12 +1,11 @@
import type { ShortcutModifierDefinition } from '@svelte-put/shortcut'; import type { ShortcutModifierDefinition } from '@svelte-put/shortcut';
import type { import type {
FitViewOptionsBase, FitViewOptionsBase,
HandleType,
Position,
XYPosition, XYPosition,
ConnectingHandle, ConnectingHandle,
Connection, Connection,
OnBeforeDeleteBase OnBeforeDeleteBase,
HandleProps as HandlePropsSystem
} from '@xyflow/system'; } from '@xyflow/system';
import type { Node } from './nodes'; import type { Node } from './nodes';
@@ -23,32 +22,9 @@ export type ConnectionData = {
connectionStatus: string | null; connectionStatus: string | null;
}; };
export type HandleComponentProps = { export type HandleProps = HandlePropsSystem & {
/** Type of the handle
* @example HandleType.Source, HandleType.Target
*/
type: HandleType;
/** Position of the handle
* @example Position.TopLeft, Position.TopRight,
* Position.BottomLeft, Position.BottomRight
*/
position?: Position;
/** Id of the handle
* @remarks optional if there is only one handle of this type
*/
id?: string;
class?: string; class?: string;
style?: string; style?: string;
/** Should you be able to connect from/to this handle */
isConnectable?: boolean;
/** Shoould you be able to connect from this handle */
isConnectableStart?: boolean;
/** Should you be able to connect to this handle */
isConnectableEnd?: boolean;
/** Function that is called when checking if connection is valid.
* Overrides the isValidConnection on the Flow component.
*/
isValidConnection?: IsValidConnection;
onconnect?: (connections: Connection[]) => void; onconnect?: (connections: Connection[]) => void;
ondisconnect?: (connections: Connection[]) => void; ondisconnect?: (connections: Connection[]) => void;
}; };
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@xyflow/system", "name": "@xyflow/system",
"version": "0.0.25", "version": "0.0.26",
"description": "xyflow core system that powers React Flow and Svelte Flow.", "description": "xyflow core system that powers React Flow and Svelte Flow.",
"keywords": [ "keywords": [
"node-based UI", "node-based UI",
+2 -1
View File
@@ -1,6 +1,5 @@
import { CoordinateExtent, HandleType } from './types'; import { CoordinateExtent, HandleType } from './types';
// @todo: update URLs to xyflow
export const errorMessages = { export const errorMessages = {
error001: () => error001: () =>
'[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001', '[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001',
@@ -23,6 +22,8 @@ export const errorMessages = {
error011: (edgeType: string) => `Edge type "${edgeType}" not found. Using fallback type "default".`, error011: (edgeType: string) => `Edge type "${edgeType}" not found. Using fallback type "default".`,
error012: (id: string) => error012: (id: string) =>
`Node with id "${id}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`, `Node with id "${id}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`,
error013: (lib: string = 'react') =>
`It seems that you haven't loaded the styles. Please import '@xyflow/${lib}/dist/style.css' or base.css to make sure everything is working properly.`,
}; };
export const infiniteExtent: CoordinateExtent = [ export const infiniteExtent: CoordinateExtent = [
+1 -3
View File
@@ -1,4 +1,4 @@
import type { Position, OnConnect, IsValidConnection } from '.'; import type { Position, IsValidConnection } from '.';
export type HandleType = 'source' | 'target'; export type HandleType = 'source' | 'target';
@@ -42,8 +42,6 @@ export type HandleProps = {
isConnectableStart?: boolean; isConnectableStart?: boolean;
/** Should you be able to connect to this handle */ /** Should you be able to connect to this handle */
isConnectableEnd?: boolean; isConnectableEnd?: boolean;
/** Callback called when connection is made */
onConnect?: OnConnect;
/** Callback if connection is valid /** Callback if connection is valid
* @remarks connection becomes an edge if isValidConnection returns true * @remarks connection becomes an edge if isValidConnection returns true
*/ */
+15 -22
View File
@@ -254,13 +254,10 @@ export function updateNodeInternals<NodeType extends InternalNodeBase>(
const node = nodeLookup.get(update.id); const node = nodeLookup.get(update.id);
if (node?.hidden) { if (node?.hidden) {
nodeLookup.set(node.id, { node.internals = {
...node, ...node.internals,
internals: { handleBounds: undefined,
...node.internals, };
handleBounds: undefined,
},
});
updatedInternals = true; updatedInternals = true;
} else if (node) { } else if (node) {
const dimensions = getDimensions(update.nodeElement); const dimensions = getDimensions(update.nodeElement);
@@ -272,33 +269,29 @@ export function updateNodeInternals<NodeType extends InternalNodeBase>(
); );
if (doUpdate) { if (doUpdate) {
const newNode = { node.measured = dimensions;
...node, node.internals = {
measured: dimensions, ...node.internals,
internals: { handleBounds: {
...node.internals, source: getHandleBounds('.source', update.nodeElement, zoom, node.origin || nodeOrigin),
handleBounds: { target: getHandleBounds('.target', update.nodeElement, zoom, node.origin || nodeOrigin),
source: getHandleBounds('.source', update.nodeElement, zoom, node.origin || nodeOrigin),
target: getHandleBounds('.target', update.nodeElement, zoom, node.origin || nodeOrigin),
},
}, },
}; };
nodeLookup.set(node.id, newNode);
updatedInternals = true; updatedInternals = true;
if (dimensionChanged) { if (dimensionChanged) {
changes.push({ changes.push({
id: newNode.id, id: node.id,
type: 'dimensions', type: 'dimensions',
dimensions, dimensions,
}); });
if (newNode.expandParent && newNode.parentId) { if (node.expandParent && node.parentId) {
parentExpandChildren.push({ parentExpandChildren.push({
id: newNode.id, id: node.id,
parentId: newNode.parentId, parentId: node.parentId,
rect: nodeToRect(newNode, newNode.origin || nodeOrigin), rect: nodeToRect(node, nodeOrigin),
}); });
} }
} }