Merge pull request #3773 from xyflow/next

Next
This commit is contained in:
Moritz Klack
2024-01-08 13:33:06 +01:00
committed by GitHub
84 changed files with 539 additions and 617 deletions
@@ -11,7 +11,6 @@ import {
EdgeChange,
Controls,
Background,
MiniMap,
Panel,
} from '@xyflow/react';
+31 -8
View File
@@ -1,5 +1,25 @@
# @xyflow/react
## 12.0.0-next.4
### Minor changes
- fix applyChanges: handle empty flows + addNodes/addEdges closes
- cleanup exports
## 12.0.0-next.3
### Minor changes
- fix edges styles when using base.css
## 12.0.0-next.2
### Minor changes
- fix connection line rendering
- fix multi handle
## 12.0.0-next.1
### Minor changes
@@ -29,17 +49,21 @@ Before you can try out the new features, you need to do some minor updates:
- **Updating nodes:** We are not supporting node updates with object mutations anymore. If you want to update a certain attribute, you need to create a new node.
- v11:
```js
setNodes(nds => nds.map((node) => {
setNodes((nds) =>
nds.map((node) => {
node.hidden = true;
return node;
}));
})
);
```
- v12:
```js
setNodes(nds => nds.map((node) => ({
setNodes((nds) =>
nds.map((node) => ({
...node,
hidden: true
})));
hidden: true,
}))
);
```
- **NodeProps:** `posX`/`posY` is now called `positionAbsoluteX`/`positionAbsoluteY`
- **Typescript only:** We simplified types and fixed issues about functions where users could pass a `NodeData` generic. The new way is to define your own node type for the whole app and then only use that one. The big advantage of this is, that you can have multiple node types with different data structures and always be able to distinguish by checking the `node.type` attribute.
@@ -48,7 +72,7 @@ Before you can try out the new features, you need to do some minor updates:
- affected functions: `useNodes`, `useNodesState`, `useEdgesState`, `applyNodeChange`, `onInit`, `applyEdgeChanges` , `MiniMapProps`
- **Removal of deprecated functions:**
- `getTransformForBounds` (new name: `getViewportForBounds`),
- `getRectOfNodes` ****(new name: `getNodesBounds`)
- `getRectOfNodes` (new name: `getNodesBounds`)
- `project` (new name: `screenToFlowPosition`)
- `getMarkerEndId`
@@ -93,5 +117,4 @@ These changes are not really user-facing, but it could be important for folks wh
- We removed `connectionNodeId`, `connectionHandleId`, `connectionHandleType` from the store and added `connectionStartHandle.nodeId`, `connectionStartHandle.handleId`, …
- add `data-id` to edges
__With v12 the `reactflow` package was renamed to `@xyflow/react` - you can find the v11 source and the [`reactflow` changelog](https://github.com/xyflow/xyflow/blob/v11/packages/reactflow/CHANGELOG.md) on the v11 branch.__
**With v12 the `reactflow` package was renamed to `@xyflow/react` - you can find the v11 source and the [`reactflow` changelog](https://github.com/xyflow/xyflow/blob/v11/packages/reactflow/CHANGELOG.md) on the v11 branch.**
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@xyflow/react",
"version": "12.0.0-next.1",
"version": "12.0.0-next.4",
"description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.",
"keywords": [
"react",
@@ -3,10 +3,10 @@ import cc from 'classcat';
import { shallow } from 'zustand/shallow';
import { useStore } from '../../hooks/useStore';
import { type ReactFlowState } from '../../types';
import { BackgroundProps, BackgroundVariant } from './types';
import { DotPattern, LinePattern } from './Patterns';
import { containerStyle } from '../../styles/utils';
import { type BackgroundProps, BackgroundVariant } from './types';
import { type ReactFlowState } from '../../types';
const defaultSize = {
[BackgroundVariant.Dots]: 1,
@@ -16,7 +16,7 @@ const defaultSize = {
const selector = (s: ReactFlowState) => ({ transform: s.transform, patternId: `pattern-${s.rfId}` });
function Background({
function BackgroundComponent({
id,
variant = BackgroundVariant.Dots,
// only used for dots and cross
@@ -87,6 +87,6 @@ function Background({
);
}
Background.displayName = 'Background';
BackgroundComponent.displayName = 'Background';
export default memo(Background);
export const Background = memo(BackgroundComponent);
@@ -1,2 +1,2 @@
export { default as Background } from './Background';
export * from './types';
export { Background } from './Background';
export { BackgroundVariant, type BackgroundProps } from './types';
@@ -1,14 +1,11 @@
import type { FC, PropsWithChildren } from 'react';
import cc from 'classcat';
import type { ControlButtonProps } from './types';
const ControlButton: FC<PropsWithChildren<ControlButtonProps>> = ({ children, className, ...rest }) => (
export function ControlButton({ children, className, ...rest }: ControlButtonProps) {
return (
<button type="button" className={cc(['react-flow__controls-button', className])} {...rest}>
{children}
</button>
);
ControlButton.displayName = 'ControlButton';
export default ControlButton;
}
@@ -1,19 +1,18 @@
import { memo, type FC, type PropsWithChildren } from 'react';
import { memo } from 'react';
import cc from 'classcat';
import { shallow } from 'zustand/shallow';
import { useStore, useStoreApi } from '../../hooks/useStore';
import useReactFlow from '../../hooks/useReactFlow';
import Panel from '../../components/Panel';
import { useReactFlow } from '../../hooks/useReactFlow';
import { Panel } from '../../components/Panel';
import { type ReactFlowState } from '../../types';
import PlusIcon from './Icons/Plus';
import MinusIcon from './Icons/Minus';
import FitviewIcon from './Icons/FitView';
import LockIcon from './Icons/Lock';
import UnlockIcon from './Icons/Unlock';
import ControlButton from './ControlButton';
import { PlusIcon } from './Icons/Plus';
import { MinusIcon } from './Icons/Minus';
import { FitViewIcon } from './Icons/FitView';
import { LockIcon } from './Icons/Lock';
import { UnlockIcon } from './Icons/Unlock';
import { ControlButton } from './ControlButton';
import type { ControlProps } from './types';
const selector = (s: ReactFlowState) => ({
@@ -22,7 +21,7 @@ const selector = (s: ReactFlowState) => ({
maxZoomReached: s.transform[2] >= s.maxZoom,
});
const Controls: FC<PropsWithChildren<ControlProps>> = ({
function ControlsComponent({
style,
showZoom = true,
showFitView = true,
@@ -35,7 +34,7 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
className,
children,
position = 'bottom-left',
}) => {
}: ControlProps) {
const store = useStoreApi();
const { isInteractive, minZoomReached, maxZoomReached } = useStore(selector, shallow);
const { zoomIn, zoomOut, fitView } = useReactFlow();
@@ -101,7 +100,7 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
title="fit view"
aria-label="fit view"
>
<FitviewIcon />
<FitViewIcon />
</ControlButton>
)}
{showInteractive && (
@@ -117,8 +116,8 @@ const Controls: FC<PropsWithChildren<ControlProps>> = ({
{children}
</Panel>
);
};
}
Controls.displayName = 'Controls';
ControlsComponent.displayName = 'Controls';
export default memo(Controls);
export const Controls = memo(ControlsComponent);
@@ -1,9 +1,7 @@
function FitViewIcon() {
export function FitViewIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 30">
<path d="M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z" />
</svg>
);
}
export default FitViewIcon;
@@ -1,9 +1,7 @@
function LockIcon() {
export function LockIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32">
<path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z" />
</svg>
);
}
export default LockIcon;
@@ -1,9 +1,7 @@
function MinusIcon() {
export function MinusIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 5">
<path d="M0 0h32v4.2H0z" />
</svg>
);
}
export default MinusIcon;
@@ -1,9 +1,7 @@
function PlusIcon() {
export function PlusIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
<path d="M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z" />
</svg>
);
}
export default PlusIcon;
@@ -1,9 +1,7 @@
function UnlockIcon() {
export function UnlockIcon() {
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 32">
<path d="M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z" />
</svg>
);
}
export default UnlockIcon;
@@ -1,3 +1,3 @@
export { default as Controls } from './Controls';
export { default as ControlButton } from './ControlButton';
export * from './types';
export { Controls } from './Controls';
export { ControlButton } from './ControlButton';
export type { ControlProps, ControlButtonProps } from './types';
@@ -1,4 +1,4 @@
import type { ButtonHTMLAttributes, HTMLAttributes } from 'react';
import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react';
import type { PanelPosition } from '@xyflow/system';
import type { FitViewOptions } from '../../types';
@@ -13,6 +13,7 @@ export type ControlProps = HTMLAttributes<HTMLDivElement> & {
onFitView?: () => void;
onInteractiveChange?: (interactiveStatus: boolean) => void;
position?: PanelPosition;
children?: ReactNode;
};
export type ControlButtonProps = ButtonHTMLAttributes<HTMLButtonElement>;
@@ -6,11 +6,11 @@ import { shallow } from 'zustand/shallow';
import { getNodesBounds, getBoundsOfRects, XYMinimap, type Rect, type XYMinimapInstance } from '@xyflow/system';
import { useStore, useStoreApi } from '../../hooks/useStore';
import Panel from '../../components/Panel';
import { Panel } from '../../components/Panel';
import type { ReactFlowState } from '../../types';
import type { MiniMapProps } from './types';
import MiniMapNodes from './MiniMapNodes';
import type { MiniMapProps } from './types';
const defaultWidth = 200;
const defaultHeight = 150;
@@ -37,7 +37,7 @@ const selector = (s: ReactFlowState) => {
const ARIA_LABEL_KEY = 'react-flow__minimap-desc';
function MiniMap({
function MiniMapComponent({
style,
className,
nodeStrokeColor,
@@ -171,6 +171,6 @@ function MiniMap({
);
}
MiniMap.displayName = 'MiniMap';
MiniMapComponent.displayName = 'MiniMap';
export default memo(MiniMap);
export const MiniMap = memo(MiniMapComponent);
@@ -3,7 +3,7 @@ import cc from 'classcat';
import type { MiniMapNodeProps } from './types';
function MiniMapNode({
function MiniMapNodeComponent({
id,
x,
y,
@@ -42,4 +42,4 @@ function MiniMapNode({
);
}
export default memo(MiniMapNode);
export const MiniMapNode = memo(MiniMapNodeComponent);
@@ -5,8 +5,8 @@ import { NodeOrigin, getNodePositionWithOrigin } from '@xyflow/system';
import { shallow } from 'zustand/shallow';
import { useStore } from '../../hooks/useStore';
import { MiniMapNode } from './MiniMapNode';
import type { ReactFlowState } from '../../types';
import MiniMapNode from './MiniMapNode';
import type { MiniMapNodes as MiniMapNodesProps, GetMiniMapNodeAttribute, MiniMapNodeProps } from './types';
declare const window: any;
@@ -1,2 +1,2 @@
export { default as MiniMap } from './MiniMap';
export { MiniMap } from './MiniMap';
export * from './types';
@@ -6,8 +6,8 @@ import { getNodesBounds, Rect, Position, internalsSymbol, getNodeToolbarTransfor
import { Node, ReactFlowState } from '../../types';
import { useStore } from '../../hooks/useStore';
import { useNodeId } from '../../contexts/NodeIdContext';
import NodeToolbarPortal from './NodeToolbarPortal';
import { NodeToolbarProps } from './types';
import { NodeToolbarPortal } from './NodeToolbarPortal';
import type { NodeToolbarProps } from './types';
const nodeEqualityFn = (a?: Node, b?: Node) =>
a?.computed?.positionAbsolute?.x !== b?.computed?.positionAbsolute?.x ||
@@ -35,7 +35,7 @@ const storeSelector = (state: ReactFlowState) => ({
selectedNodesCount: state.nodes.filter((node) => node.selected).length,
});
function NodeToolbar({
export function NodeToolbar({
nodeId,
children,
className,
@@ -96,5 +96,3 @@ function NodeToolbar({
</NodeToolbarPortal>
);
}
export default NodeToolbar;
@@ -6,7 +6,7 @@ import { useStore } from '../../hooks/useStore';
const selector = (state: ReactFlowState) => state.domNode?.querySelector('.react-flow__renderer');
function NodeToolbarPortal({ children }: { children: ReactNode }) {
export function NodeToolbarPortal({ children }: { children: ReactNode }) {
const wrapperRef = useStore(selector);
if (!wrapperRef) {
@@ -15,5 +15,3 @@ function NodeToolbarPortal({ children }: { children: ReactNode }) {
return createPortal(children, wrapperRef);
}
export default NodeToolbarPortal;
@@ -1,2 +1,2 @@
export { default as NodeToolbar } from './NodeToolbar';
export * from './types';
export { NodeToolbar } from './NodeToolbar';
export type { NodeToolbarProps } from './types';
@@ -32,7 +32,7 @@ function AriaLiveMessage({ rfId }: { rfId: string }) {
);
}
function A11yDescriptions({ rfId, disableKeyboardA11y }: { rfId: string; disableKeyboardA11y: boolean }) {
export function A11yDescriptions({ rfId, disableKeyboardA11y }: { rfId: string; disableKeyboardA11y: boolean }) {
return (
<>
<div id={`${ARIA_NODE_DESC_KEY}-${rfId}`} style={style}>
@@ -47,5 +47,3 @@ function A11yDescriptions({ rfId, disableKeyboardA11y }: { rfId: string; disable
</>
);
}
export default A11yDescriptions;
@@ -1,13 +1,13 @@
import type { PanelPosition, ProOptions } from '@xyflow/system';
import Panel from '../Panel';
import { Panel } from '../Panel';
type AttributionProps = {
proOptions?: ProOptions;
position?: PanelPosition;
};
function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps) {
export function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps) {
if (proOptions?.hideAttribution) {
return null;
}
@@ -24,5 +24,3 @@ function Attribution({ proOptions, position = 'bottom-right' }: AttributionProps
</Panel>
);
}
export default Attribution;
@@ -142,7 +142,7 @@ const selector = (s: ReactFlowState) => ({
height: s.height,
});
function ConnectionLineWrapper({ containerStyle, style, type, component }: ConnectionLineWrapperProps) {
export function ConnectionLineWrapper({ containerStyle, style, type, component }: ConnectionLineWrapperProps) {
const { nodeId, handleType, nodesConnectable, width, height, connectionStatus } = useStore(selector, shallow);
const isValid = !!(nodeId && handleType && width && nodesConnectable);
@@ -155,7 +155,7 @@ function ConnectionLineWrapper({ containerStyle, style, type, component }: Conne
style={containerStyle}
width={width}
height={height}
className="react-flow__edges react-flow__connectionline react-flow__container"
className="react-flow__connectionline react-flow__container"
>
<g className={cc(['react-flow__connection', connectionStatus])}>
<ConnectionLine
@@ -170,5 +170,3 @@ function ConnectionLineWrapper({ containerStyle, style, type, component }: Conne
</svg>
);
}
export default ConnectionLineWrapper;
@@ -6,7 +6,7 @@ import type { ReactFlowState } from '../../types';
const selector = (s: ReactFlowState) => s.domNode?.querySelector('.react-flow__edgelabel-renderer');
function EdgeLabelRenderer({ children }: { children: ReactNode }) {
export function EdgeLabelRenderer({ children }: { children: ReactNode }) {
const edgeLabelRenderer = useStore(selector);
if (!edgeLabelRenderer) {
@@ -15,5 +15,3 @@ function EdgeLabelRenderer({ children }: { children: ReactNode }) {
return createPortal(children, edgeLabelRenderer);
}
export default EdgeLabelRenderer;
@@ -9,8 +9,8 @@ type EdgeUpdateAnchorsProps = {
edge: Edge;
isUpdatable: boolean | 'source' | 'target';
edgeUpdaterRadius: EdgeWrapperProps['edgeUpdaterRadius'];
sourceHandleId: EdgeWrapperProps['sourceHandleId'];
targetHandleId: EdgeWrapperProps['targetHandleId'];
sourceHandleId: Edge['sourceHandle'];
targetHandleId: Edge['targetHandle'];
onEdgeUpdate: EdgeWrapperProps['onEdgeUpdate'];
onEdgeUpdateStart: EdgeWrapperProps['onEdgeUpdateStart'];
onEdgeUpdateEnd: EdgeWrapperProps['onEdgeUpdateEnd'];
@@ -18,7 +18,7 @@ type EdgeUpdateAnchorsProps = {
setUpdating: (updating: boolean) => void;
} & EdgePosition;
function EdgeUpdateAnchors({
export function EdgeUpdateAnchors({
isUpdatable,
edgeUpdaterRadius,
edge,
@@ -133,5 +133,3 @@ function EdgeUpdateAnchors({
</>
);
}
export default EdgeUpdateAnchors;
@@ -1,4 +1,4 @@
import { memo, useState, useMemo, useRef, type KeyboardEvent, useCallback } from 'react';
import { useState, useMemo, useRef, type KeyboardEvent, useCallback } from 'react';
import cc from 'classcat';
import { shallow } from 'zustand/shallow';
import {
@@ -11,19 +11,17 @@ import {
import { useStoreApi, useStore } from '../../hooks/useStore';
import { ARIA_EDGE_DESC_KEY } from '../A11yDescriptions';
import type { EdgeWrapperProps } from '../../types';
import { builtinEdgeTypes, nullPosition } from './utils';
import EdgeUpdateAnchors from './EdgeUpdateAnchors';
import { EdgeUpdateAnchors } from './EdgeUpdateAnchors';
import type { EdgeWrapperProps } from '../../types';
function EdgeWrapper({
export function EdgeWrapper({
id,
edgesFocusable,
edgesUpdatable,
elementsSelectable,
onClick,
onDoubleClick,
sourceHandleId,
targetHandleId,
onContextMenu,
onMouseEnter,
onMouseMove,
@@ -78,8 +76,8 @@ function EdgeWrapper({
id,
sourceNode,
targetNode,
sourceHandle: sourceHandleId || null,
targetHandle: targetHandleId || null,
sourceHandle: edge.sourceHandle || null,
targetHandle: edge.targetHandle || null,
connectionMode: store.connectionMode,
onError,
});
@@ -97,7 +95,7 @@ function EdgeWrapper({
...(edgePosition || nullPosition),
};
},
[edge.source, edge.target, edge.selected, edge.zIndex]
[edge.source, edge.target, edge.sourceHandle, edge.targetHandle, edge.selected, edge.zIndex]
),
shallow
);
@@ -228,8 +226,8 @@ function EdgeWrapper({
targetPosition={targetPosition}
data={edge.data}
style={edge.style}
sourceHandleId={sourceHandleId}
targetHandleId={targetHandleId}
sourceHandleId={edge.sourceHandle}
targetHandleId={edge.targetHandle}
markerStart={markerStartUrl}
markerEnd={markerEndUrl}
pathOptions={'pathOptions' in edge ? edge.pathOptions : undefined}
@@ -252,15 +250,11 @@ function EdgeWrapper({
targetPosition={targetPosition}
setUpdateHover={setUpdateHover}
setUpdating={setUpdating}
sourceHandleId={sourceHandleId}
targetHandleId={targetHandleId}
sourceHandleId={edge.sourceHandle}
targetHandleId={edge.targetHandle}
/>
)}
</g>
</svg>
);
}
EdgeWrapper.displayName = 'EdgeWrapper';
export default memo(EdgeWrapper);
@@ -1,10 +1,10 @@
import { isNumeric } from '@xyflow/system';
import cc from 'classcat';
import { EdgeText } from './EdgeText';
import type { BaseEdgeProps } from '../../types';
import EdgeText from './EdgeText';
import classcat from 'classcat';
const BaseEdge = ({
export function BaseEdge({
id,
path,
labelX,
@@ -20,7 +20,7 @@ const BaseEdge = ({
markerStart,
className,
interactionWidth = 20,
}: BaseEdgeProps) => {
}: BaseEdgeProps) {
return (
<>
<path
@@ -28,7 +28,7 @@ const BaseEdge = ({
style={style}
d={path}
fill="none"
className={classcat(['react-flow__edge-path', className])}
className={cc(['react-flow__edge-path', className])}
markerEnd={markerEnd}
markerStart={markerStart}
/>
@@ -55,8 +55,4 @@ const BaseEdge = ({
) : null}
</>
);
};
BaseEdge.displayName = 'BaseEdge';
export default BaseEdge;
}
@@ -1,7 +1,7 @@
import { memo } from 'react';
import { Position, getBezierPath } from '@xyflow/system';
import BaseEdge from './BaseEdge';
import { BaseEdge } from './BaseEdge';
import type { BezierEdgeProps } from '../../types';
function createBezierEdge(params: { isInternal: boolean }) {
@@ -1,4 +1,4 @@
import type { FC, MouseEvent as ReactMouseEvent, SVGAttributes } from 'react';
import type { MouseEvent as ReactMouseEvent, SVGAttributes } from 'react';
import cc from 'classcat';
import { Position } from '@xyflow/system';
@@ -27,7 +27,7 @@ export interface EdgeAnchorProps extends SVGAttributes<SVGGElement> {
const EdgeUpdaterClassName = 'react-flow__edgeupdater';
export const EdgeAnchor: FC<EdgeAnchorProps> = ({
export function EdgeAnchor({
position,
centerX,
centerY,
@@ -36,7 +36,8 @@ export const EdgeAnchor: FC<EdgeAnchorProps> = ({
onMouseEnter,
onMouseOut,
type,
}: EdgeAnchorProps) => (
}: EdgeAnchorProps) {
return (
<circle
onMouseDown={onMouseDown}
onMouseEnter={onMouseEnter}
@@ -49,3 +50,4 @@ export const EdgeAnchor: FC<EdgeAnchorProps> = ({
fill="transparent"
/>
);
}
@@ -1,10 +1,10 @@
import { memo, useState, type FC, type PropsWithChildren, useCallback } from 'react';
import { memo, useState, useCallback } from 'react';
import cc from 'classcat';
import type { Rect } from '@xyflow/system';
import type { EdgeTextProps } from '../../types';
const EdgeText: FC<PropsWithChildren<EdgeTextProps>> = ({
function EdgeTextComponent({
x,
y,
label,
@@ -16,7 +16,7 @@ const EdgeText: FC<PropsWithChildren<EdgeTextProps>> = ({
children,
className,
...rest
}) => {
}: EdgeTextProps) {
const [edgeTextBbox, setEdgeTextBbox] = useState<Rect>({ x: 1, y: 0, width: 0, height: 0 });
const edgeTextClasses = cc(['react-flow__edge-textwrapper', className]);
@@ -68,5 +68,8 @@ const EdgeText: FC<PropsWithChildren<EdgeTextProps>> = ({
{children}
</g>
);
};
export default memo(EdgeText);
}
EdgeTextComponent.displayName = 'EdgeText';
export const EdgeText = memo(EdgeTextComponent);
@@ -1,7 +1,7 @@
import { memo } from 'react';
import { Position, getBezierEdgeCenter } from '@xyflow/system';
import BaseEdge from './BaseEdge';
import { BaseEdge } from './BaseEdge';
import type { SimpleBezierEdgeProps } from '../../types';
export interface GetSimpleBezierPathParams {
@@ -1,7 +1,7 @@
import { memo } from 'react';
import { Position, getSmoothStepPath } from '@xyflow/system';
import BaseEdge from './BaseEdge';
import { BaseEdge } from './BaseEdge';
import type { SmoothStepEdgeProps } from '../../types';
function createSmoothStepEdge(params: { isInternal: boolean }) {
@@ -1,7 +1,7 @@
import { memo } from 'react';
import { getStraightPath } from '@xyflow/system';
import BaseEdge from './BaseEdge';
import { BaseEdge } from './BaseEdge';
import type { StraightEdgeProps } from '../../types';
function createStraightEdge(params: { isInternal: boolean }) {
@@ -47,7 +47,7 @@ const connectingSelector =
};
};
const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
const HandleComponent = forwardRef<HTMLDivElement, HandleComponentProps>(
(
{
type = 'source',
@@ -216,6 +216,6 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
}
);
Handle.displayName = 'Handle';
HandleComponent.displayName = 'Handle';
export default memo(Handle);
export const Handle = memo(HandleComponent);
@@ -1,5 +1,6 @@
import { useEffect, useRef, memo, type MouseEvent, type KeyboardEvent } from 'react';
import { useEffect, useRef, type MouseEvent, type KeyboardEvent } from 'react';
import cc from 'classcat';
import { shallow } from 'zustand/shallow';
import {
clampPosition,
elementSelectionKeys,
@@ -12,14 +13,13 @@ import {
import { useStore, useStoreApi } from '../../hooks/useStore';
import { Provider } from '../../contexts/NodeIdContext';
import { ARIA_NODE_DESC_KEY } from '../A11yDescriptions';
import useDrag from '../../hooks/useDrag';
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
import { useDrag } from '../../hooks/useDrag';
import { useUpdateNodePositions } from '../../hooks/useUpdateNodePositions';
import { handleNodeClick } from '../Nodes/utils';
import type { NodeWrapperProps } from '../../types';
import { arrowKeyDiffs, builtinNodeTypes } from './utils';
import { shallow } from 'zustand/shallow';
import type { NodeWrapperProps } from '../../types';
const NodeWrapper = ({
export function NodeWrapper({
id,
onClick,
onMouseEnter,
@@ -40,7 +40,7 @@ const NodeWrapper = ({
nodeExtent,
nodeOrigin,
onError,
}: NodeWrapperProps) => {
}: NodeWrapperProps) {
const { node, positionAbsoluteX, positionAbsoluteY, zIndex, isParent } = useStore((s) => {
const node = s.nodeLookup.get(id)!;
@@ -257,8 +257,4 @@ const NodeWrapper = ({
</Provider>
</div>
);
};
NodeWrapper.displayName = 'NodeWrapper';
export default memo(NodeWrapper);
}
@@ -1,10 +1,10 @@
import type { ComponentType } from 'react';
import type { NodeProps, XYPosition } from '@xyflow/system';
import InputNode from '../Nodes/InputNode';
import DefaultNode from '../Nodes/DefaultNode';
import GroupNode from '../Nodes/GroupNode';
import OutputNode from '../Nodes/OutputNode';
import { InputNode } from '../Nodes/InputNode';
import { DefaultNode } from '../Nodes/DefaultNode';
import { GroupNode } from '../Nodes/GroupNode';
import { OutputNode } from '../Nodes/OutputNode';
import type { NodeTypes } from '../../types';
export const arrowKeyDiffs: Record<string, XYPosition> = {
@@ -1,14 +1,13 @@
import { memo } from 'react';
import { Position, type NodeProps } from '@xyflow/system';
import Handle from '../../components/Handle';
import { Handle } from '../../components/Handle';
const DefaultNode = ({
export function DefaultNode({
data,
isConnectable,
targetPosition = Position.Top,
sourcePosition = Position.Bottom,
}: NodeProps) => {
}: NodeProps) {
return (
<>
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
@@ -16,8 +15,4 @@ const DefaultNode = ({
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
</>
);
};
DefaultNode.displayName = 'DefaultNode';
export default memo(DefaultNode);
}
@@ -1,5 +1,3 @@
const GroupNode = () => null;
GroupNode.displayName = 'GroupNode';
export default GroupNode;
export function GroupNode() {
return null;
}
@@ -1,15 +1,12 @@
import { memo } from 'react';
import { Position, type NodeProps } from '@xyflow/system';
import Handle from '../../components/Handle';
import { Handle } from '../../components/Handle';
const InputNode = ({ data, isConnectable, sourcePosition = Position.Bottom }: NodeProps) => (
export function InputNode({ data, isConnectable, sourcePosition = Position.Bottom }: NodeProps) {
return (
<>
{data?.label}
<Handle type="source" position={sourcePosition} isConnectable={isConnectable} />
</>
);
InputNode.displayName = 'InputNode';
export default memo(InputNode);
}
@@ -1,15 +1,12 @@
import { memo } from 'react';
import { Position, type NodeProps } from '@xyflow/system';
import Handle from '../../components/Handle';
import { Handle } from '../../components/Handle';
const OutputNode = ({ data, isConnectable, targetPosition = Position.Top }: NodeProps) => (
export function OutputNode({ data, isConnectable, targetPosition = Position.Top }: NodeProps) {
return (
<>
<Handle type="target" position={targetPosition} isConnectable={isConnectable} />
{data?.label}
</>
);
OutputNode.displayName = 'OutputNode';
export default memo(OutputNode);
}
+1 -1
View File
@@ -1,8 +1,8 @@
import type { RefObject } from 'react';
import type { StoreApi } from 'zustand';
import { errorMessages } from '@xyflow/system';
import type { ReactFlowState } from '../../types';
import { errorMessages } from '@xyflow/system';
// this handler is called by
// 1. the click handler when node is not draggable or selectNodesOnDrag = false
@@ -3,16 +3,16 @@
* made a selection with on or several nodes
*/
import { memo, useRef, useEffect, type MouseEvent, type KeyboardEvent } from 'react';
import { useRef, useEffect, type MouseEvent, type KeyboardEvent } from 'react';
import cc from 'classcat';
import { shallow } from 'zustand/shallow';
import { getNodesBounds } from '@xyflow/system';
import { useStore, useStoreApi } from '../../hooks/useStore';
import useDrag from '../../hooks/useDrag';
import useUpdateNodePositions from '../../hooks/useUpdateNodePositions';
import type { Node, ReactFlowState } from '../../types';
import { useDrag } from '../../hooks/useDrag';
import { useUpdateNodePositions } from '../../hooks/useUpdateNodePositions';
import { arrowKeyDiffs } from '../NodeWrapper/utils';
import type { Node, ReactFlowState } from '../../types';
export type NodesSelectionProps = {
onSelectionContextMenu?: (event: MouseEvent, nodes: Node[]) => void;
@@ -32,7 +32,7 @@ const selector = (s: ReactFlowState) => {
};
};
function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboardA11y }: NodesSelectionProps) {
export function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboardA11y }: NodesSelectionProps) {
const store = useStoreApi();
const { width, height, transformString, userSelectionActive } = useStore(selector, shallow);
const updatePositions = useUpdateNodePositions();
@@ -93,5 +93,3 @@ function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboar
</div>
);
}
export default memo(NodesSelection);
@@ -6,13 +6,13 @@ import { useStore } from '../../hooks/useStore';
import type { ReactFlowState } from '../../types';
export type PanelProps = HTMLAttributes<HTMLDivElement> & {
position: PanelPosition;
position?: PanelPosition;
children: ReactNode;
};
const selector = (s: ReactFlowState) => (s.userSelectionActive ? 'none' : 'all');
function Panel({ position, children, className, style, ...rest }: PanelProps) {
export function Panel({ position = 'top-left', children, className, style, ...rest }: PanelProps) {
const pointerEvents = useStore(selector);
const positionClasses = `${position}`.split('-');
@@ -26,5 +26,3 @@ function Panel({ position, children, className, style, ...rest }: PanelProps) {
</div>
);
}
export default Panel;
@@ -6,7 +6,7 @@ import { Provider } from '../../contexts/RFStoreContext';
import { createRFStore } from '../../store';
import type { ReactFlowState, Node, Edge } from '../../types';
function ReactFlowProvider({
export function ReactFlowProvider({
children,
initialNodes,
initialEdges,
@@ -35,7 +35,3 @@ function ReactFlowProvider({
return <Provider value={storeRef.current}>{children}</Provider>;
}
ReactFlowProvider.displayName = 'ReactFlowProvider';
export default ReactFlowProvider;
@@ -4,7 +4,7 @@
* or is using the useOnSelectionChange hook.
* @TODO: Now that we have the onNodesChange and on EdgesChange listeners, do we still need this component?
*/
import { memo, useEffect } from 'react';
import { useEffect } from 'react';
import { shallow } from 'zustand/shallow';
import { useStore, useStoreApi } from '../../hooks/useStore';
@@ -30,7 +30,7 @@ function areEqual(a: SelectorSlice, b: SelectorSlice) {
);
}
const SelectionListener = memo(({ onSelectionChange }: SelectionListenerProps) => {
function SelectionListenerInner({ onSelectionChange }: SelectionListenerProps) {
const store = useStoreApi();
const { selectedNodes, selectedEdges } = useStore(selector, areEqual);
@@ -42,20 +42,16 @@ const SelectionListener = memo(({ onSelectionChange }: SelectionListenerProps) =
}, [selectedNodes, selectedEdges, onSelectionChange]);
return null;
});
SelectionListener.displayName = 'SelectionListener';
}
const changeSelector = (s: ReactFlowState) => !!s.onSelectionChangeHandlers;
function Wrapper({ onSelectionChange }: SelectionListenerProps) {
export function SelectionListener({ onSelectionChange }: SelectionListenerProps) {
const storeHasSelectionChangeHandlers = useStore(changeSelector);
if (onSelectionChange || storeHasSelectionChangeHandlers) {
return <SelectionListener onSelectionChange={onSelectionChange} />;
return <SelectionListenerInner onSelectionChange={onSelectionChange} />;
}
return null;
}
export default Wrapper;
@@ -84,7 +84,7 @@ const selector = (s: ReactFlowState) => ({
reset: s.reset,
});
const StoreUpdater = (props: StoreUpdaterProps) => {
export function StoreUpdater(props: StoreUpdaterProps) {
const {
setNodes,
setEdges,
@@ -149,6 +149,4 @@ const StoreUpdater = (props: StoreUpdaterProps) => {
);
return null;
};
export default StoreUpdater;
}
@@ -8,7 +8,7 @@ const selector = (s: ReactFlowState) => ({
userSelectionRect: s.userSelectionRect,
});
function UserSelection() {
export function UserSelection() {
const { userSelectionActive, userSelectionRect } = useStore(selector, shallow);
const isActive = userSelectionActive && userSelectionRect;
@@ -27,5 +27,3 @@ function UserSelection() {
/>
);
}
export default UserSelection;
@@ -6,7 +6,7 @@ import type { ReactFlowState } from '../../types';
const selector = (s: ReactFlowState) => s.domNode?.querySelector('.react-flow__viewport-portal');
function ViewportPortal({ children }: { children: ReactNode }) {
export function ViewportPortal({ children }: { children: ReactNode }) {
const viewPortalDiv = useStore(selector);
if (!viewPortalDiv) {
@@ -15,5 +15,3 @@ function ViewportPortal({ children }: { children: ReactNode }) {
return createPortal(children, viewPortalDiv);
}
export default ViewportPortal;
@@ -57,5 +57,3 @@ export function useMarkerSymbol(type: MarkerType) {
return symbol;
}
export default MarkerSymbols;
@@ -2,10 +2,10 @@ import { memo, ReactNode } from 'react';
import { shallow } from 'zustand/shallow';
import { useStore } from '../../hooks/useStore';
import useVisibleEdgeIds from '../../hooks/useVisibleEdgeIds';
import { useVisibleEdgeIds } from '../../hooks/useVisibleEdgeIds';
import MarkerDefinitions from './MarkerDefinitions';
import { GraphViewProps } from '../GraphView';
import EdgeWrapper from '../../components/EdgeWrapper';
import { EdgeWrapper } from '../../components/EdgeWrapper';
import type { ReactFlowState } from '../../types';
type EdgeRendererProps = Pick<
@@ -27,7 +27,7 @@ type EdgeRendererProps = Pick<
| 'disableKeyboardA11y'
| 'edgeTypes'
> & {
children: ReactNode;
children?: ReactNode;
};
const selector = (s: ReactFlowState) => ({
@@ -40,7 +40,7 @@ const selector = (s: ReactFlowState) => ({
onError: s.onError,
});
const EdgeRenderer = ({
function EdgeRendererComponent({
defaultMarkerColor,
onlyRenderVisibleElements,
rfId,
@@ -56,8 +56,7 @@ const EdgeRenderer = ({
onEdgeDoubleClick,
onEdgeUpdateStart,
onEdgeUpdateEnd,
children,
}: EdgeRendererProps) => {
}: EdgeRendererProps) {
const { edgesFocusable, edgesUpdatable, elementsSelectable, onError } = useStore(selector, shallow);
const edgeIds = useVisibleEdgeIds(onlyRenderVisibleElements);
@@ -90,11 +89,10 @@ const EdgeRenderer = ({
/>
);
})}
{children}
</div>
);
};
}
EdgeRenderer.displayName = 'EdgeRenderer';
EdgeRendererComponent.displayName = 'EdgeRenderer';
export default memo(EdgeRenderer);
export const EdgeRenderer = memo(EdgeRendererComponent);
@@ -1,12 +1,12 @@
import { memo, type ReactNode } from 'react';
import { useStore } from '../../hooks/useStore';
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
import useKeyPress from '../../hooks/useKeyPress';
import { useGlobalKeyHandler } from '../../hooks/useGlobalKeyHandler';
import { useKeyPress } from '../../hooks/useKeyPress';
import { GraphViewProps } from '../GraphView';
import ZoomPane from '../ZoomPane';
import Pane from '../Pane';
import NodesSelection from '../../components/NodesSelection';
import { ZoomPane } from '../ZoomPane';
import { Pane } from '../Pane';
import { NodesSelection } from '../../components/NodesSelection';
import type { ReactFlowState } from '../../types';
export type FlowRendererProps = Omit<
@@ -30,7 +30,7 @@ export type FlowRendererProps = Omit<
const selector = (s: ReactFlowState) => s.nodesSelectionActive;
const FlowRenderer = ({
const FlowRendererComponent = ({
children,
onPaneClick,
onPaneMouseEnter,
@@ -125,6 +125,6 @@ const FlowRenderer = ({
);
};
FlowRenderer.displayName = 'FlowRenderer';
FlowRendererComponent.displayName = 'FlowRenderer';
export default memo(FlowRenderer);
export const FlowRenderer = memo(FlowRendererComponent);
@@ -1,14 +1,14 @@
import { memo } from 'react';
import FlowRenderer from '../FlowRenderer';
import NodeRenderer from '../NodeRenderer';
import EdgeRenderer from '../EdgeRenderer';
import ViewportWrapper from '../Viewport';
import useOnInitHandler from '../../hooks/useOnInitHandler';
import useViewportSync from '../../hooks/useViewportSync';
import ConnectionLine from '../../components/ConnectionLine';
import { FlowRenderer } from '../FlowRenderer';
import { NodeRenderer } from '../NodeRenderer';
import { EdgeRenderer } from '../EdgeRenderer';
import { Viewport } from '../Viewport';
import { useOnInitHandler } from '../../hooks/useOnInitHandler';
import { useViewportSync } from '../../hooks/useViewportSync';
import { ConnectionLineWrapper } from '../../components/ConnectionLine';
import { useNodeOrEdgeTypesWarning } from './useNodeOrEdgeTypesWarning';
import type { ReactFlowProps } from '../../types';
import useNodeOrEdgeTypesWarning from './useNodeOrEdgeTypesWarning';
export type GraphViewProps = Omit<
ReactFlowProps,
@@ -38,7 +38,7 @@ export type GraphViewProps = Omit<
rfId: string;
};
const GraphView = ({
function GraphViewComponent({
nodeTypes,
edgeTypes,
onInit,
@@ -102,7 +102,7 @@ const GraphView = ({
rfId,
viewport,
onViewportChange,
}: GraphViewProps) => {
}: GraphViewProps) {
useNodeOrEdgeTypesWarning(nodeTypes);
useNodeOrEdgeTypesWarning(edgeTypes);
@@ -147,7 +147,7 @@ const GraphView = ({
onViewportChange={onViewportChange}
isControlledViewport={!!viewport}
>
<ViewportWrapper>
<Viewport>
<EdgeRenderer
edgeTypes={edgeTypes}
onEdgeClick={onEdgeClick}
@@ -165,14 +165,13 @@ const GraphView = ({
noPanClassName={noPanClassName}
disableKeyboardA11y={disableKeyboardA11y}
rfId={rfId}
>
<ConnectionLine
/>
<ConnectionLineWrapper
style={connectionLineStyle}
type={connectionLineType}
component={connectionLineComponent}
containerStyle={connectionLineContainerStyle}
/>
</EdgeRenderer>
<div className="react-flow__edgelabel-renderer" />
<div className="react-flow__viewport-portal" />
<NodeRenderer
@@ -191,11 +190,11 @@ const GraphView = ({
nodeExtent={nodeExtent}
rfId={rfId}
/>
</ViewportWrapper>
</Viewport>
</FlowRenderer>
);
};
}
GraphView.displayName = 'GraphView';
GraphViewComponent.displayName = 'GraphView';
export default memo(GraphView);
export const GraphView = memo(GraphViewComponent);
@@ -12,7 +12,7 @@ const emptyTypes = {};
export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes?: NodeTypes): void;
export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes?: EdgeTypes): void;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export default function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes: any = emptyTypes): any {
export function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes: any = emptyTypes): any {
const updateCount = useRef(0);
const store = useStoreApi();
@@ -1,13 +1,13 @@
import { memo } from 'react';
import { shallow } from 'zustand/shallow';
import useVisibleNodesIds from '../../hooks/useVisibleNodeIds';
import { useVisibleNodeIds } from '../../hooks/useVisibleNodeIds';
import { useStore } from '../../hooks/useStore';
import { containerStyle } from '../../styles/utils';
import { GraphViewProps } from '../GraphView';
import { useResizeObserver } from './useResizeObserver';
import { NodeWrapper } from '../../components/NodeWrapper';
import type { ReactFlowState } from '../../types';
import useResizeObserver from './useResizeObserver';
import NodeWrapper from '../../components/NodeWrapper';
export type NodeRendererProps = Pick<
GraphViewProps,
@@ -35,9 +35,9 @@ const selector = (s: ReactFlowState) => ({
onError: s.onError,
});
const NodeRenderer = (props: NodeRendererProps) => {
const NodeRendererComponent = (props: NodeRendererProps) => {
const { nodesDraggable, nodesConnectable, nodesFocusable, elementsSelectable, onError } = useStore(selector, shallow);
const nodeIds = useVisibleNodesIds(props.onlyRenderVisibleElements);
const nodeIds = useVisibleNodeIds(props.onlyRenderVisibleElements);
const resizeObserver = useResizeObserver();
return (
@@ -96,6 +96,6 @@ const NodeRenderer = (props: NodeRendererProps) => {
);
};
NodeRenderer.displayName = 'NodeRenderer';
NodeRendererComponent.displayName = 'NodeRenderer';
export default memo(NodeRenderer);
export const NodeRenderer = memo(NodeRendererComponent);
@@ -5,7 +5,7 @@ import { useStore } from '../../hooks/useStore';
const selector = (s: ReactFlowState) => s.updateNodeDimensions;
export default function useResizeObserver() {
export function useResizeObserver() {
const updateNodeDimensions = useStore(selector);
const resizeObserverRef = useRef<ResizeObserver>();
+4 -10
View File
@@ -2,12 +2,12 @@
* The user selection rectangle gets displayed when a user drags the mouse while pressing shift
*/
import { memo, useRef, type MouseEvent as ReactMouseEvent, type ReactNode } from 'react';
import { useRef, type MouseEvent as ReactMouseEvent, type ReactNode } from 'react';
import { shallow } from 'zustand/shallow';
import cc from 'classcat';
import { getNodesInside, getEventPosition, SelectionMode } from '@xyflow/system';
import UserSelection from '../../components/UserSelection';
import { UserSelection } from '../../components/UserSelection';
import { containerStyle } from '../../styles/utils';
import { useStore, useStoreApi } from '../../hooks/useStore';
import { getSelectionChanges } from '../../utils';
@@ -50,8 +50,7 @@ const selector = (s: ReactFlowState) => ({
dragging: s.paneDragging,
});
const Pane = memo(
({
export function Pane({
isSelecting,
selectionMode = SelectionMode.Full,
panOnDrag,
@@ -64,7 +63,7 @@ const Pane = memo(
onPaneMouseMove,
onPaneMouseLeave,
children,
}: PaneProps) => {
}: PaneProps) {
const container = useRef<HTMLDivElement | null>(null);
const store = useStoreApi();
const prevSelectedNodesCount = useRef<number>(0);
@@ -238,8 +237,3 @@ const Pane = memo(
</div>
);
}
);
Pane.displayName = 'Pane';
export default Pane;
@@ -1,10 +1,10 @@
import { useContext, type ReactNode } from 'react';
import StoreContext from '../../contexts/RFStoreContext';
import ReactFlowProvider from '../../components/ReactFlowProvider';
import { ReactFlowProvider } from '../../components/ReactFlowProvider';
import type { Node, Edge } from '../../types';
function Wrapper({
export function Wrapper({
children,
nodes,
edges,
@@ -39,7 +39,3 @@ function Wrapper({
</ReactFlowProvider>
);
}
Wrapper.displayName = 'ReactFlowWrapper';
export default Wrapper;
@@ -5,20 +5,19 @@ import {
PanOnScrollMode,
SelectionMode,
infiniteExtent,
isMacOs,
type NodeOrigin,
type Viewport,
isMacOs,
} from '@xyflow/system';
import Attribution from '../../components/Attribution';
import SelectionListener from '../../components/SelectionListener';
import StoreUpdater from '../../components/StoreUpdater';
import A11yDescriptions from '../../components/A11yDescriptions';
import GraphView from '../GraphView';
import Wrapper from './Wrapper';
import { A11yDescriptions } from '../../components/A11yDescriptions';
import { Attribution } from '../../components/Attribution';
import { SelectionListener } from '../../components/SelectionListener';
import { StoreUpdater } from '../../components/StoreUpdater';
import { useColorModeClass } from '../../hooks/useColorModeClass';
import { GraphView } from '../GraphView';
import { Wrapper } from './Wrapper';
import type { ReactFlowProps, ReactFlowRefType } from '../../types';
import useColorModeClass from '../../hooks/useColorModeClass';
export const initNodeOrigin: NodeOrigin = [0, 0];
const initDefaultViewport: Viewport = { x: 0, y: 0, zoom: 1 };
@@ -9,7 +9,7 @@ type ViewportProps = {
children: ReactNode;
};
export default function Viewport({ children }: ViewportProps) {
export function Viewport({ children }: ViewportProps) {
const transform = useStore(selector);
return (
@@ -3,8 +3,8 @@ import { useEffect, useRef } from 'react';
import { shallow } from 'zustand/shallow';
import { XYPanZoom, PanOnScrollMode, type Transform, type PanZoomInstance } from '@xyflow/system';
import useKeyPress from '../../hooks/useKeyPress';
import useResizeHandler from '../../hooks/useResizeHandler';
import { useKeyPress } from '../../hooks/useKeyPress';
import { useResizeHandler } from '../../hooks/useResizeHandler';
import { useStore, useStoreApi } from '../../hooks/useStore';
import { containerStyle } from '../../styles/utils';
import type { FlowRendererProps } from '../FlowRenderer';
@@ -27,7 +27,7 @@ const selector = (s: ReactFlowState) => ({
lib: s.lib,
});
const ZoomPane = ({
export function ZoomPane({
onPaneContextMenu,
zoomOnScroll = true,
zoomOnPinch = true,
@@ -47,7 +47,7 @@ const ZoomPane = ({
noPanClassName,
onViewportChange,
isControlledViewport,
}: ZoomPaneProps) => {
}: ZoomPaneProps) {
const store = useStoreApi();
const zoomPane = useRef<HTMLDivElement>(null);
const { userSelectionActive, lib } = useStore(selector, shallow);
@@ -142,6 +142,4 @@ const ZoomPane = ({
{children}
</div>
);
};
export default ZoomPane;
}
@@ -15,7 +15,7 @@ function getMediaQuery() {
* @internal
* @param colorMode - The color mode to use ('dark', 'light' or 'system')
*/
export default function useColorModeClass(colorMode: ColorMode): ColorModeClass {
export function useColorModeClass(colorMode: ColorMode): ColorModeClass {
const [colorModeClass, setColorModeClass] = useState<ColorModeClass | null>(
colorMode === 'system' ? null : colorMode
);
+8 -3
View File
@@ -18,7 +18,14 @@ type UseDragParams = {
*
* @internal
*/
function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, nodeId, isSelectable }: UseDragParams) {
export function useDrag({
nodeRef,
disabled = false,
noDragClassName,
handleSelector,
nodeId,
isSelectable,
}: UseDragParams) {
const store = useStoreApi();
const [dragging, setDragging] = useState<boolean>(false);
const xyDrag = useRef<XYDragInstance>();
@@ -64,5 +71,3 @@ function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, n
return dragging;
}
export default useDrag;
+1 -3
View File
@@ -11,10 +11,8 @@ const edgesSelector = (state: ReactFlowState) => state.edges;
* @public
* @returns An array of edges
*/
function useEdges<EdgeData>(): Edge<EdgeData>[] {
export function useEdges<EdgeData>(): Edge<EdgeData>[] {
const edges = useStore(edgesSelector, shallow);
return edges;
}
export default useEdges;
@@ -2,8 +2,8 @@ import { useEffect } from 'react';
import type { KeyCode } from '@xyflow/system';
import { useStoreApi } from '../hooks/useStore';
import useKeyPress, { UseKeyPressOptions } from './useKeyPress';
import useReactFlow from './useReactFlow';
import { useKeyPress, UseKeyPressOptions } from './useKeyPress';
import { useReactFlow } from './useReactFlow';
import { Edge, Node } from '../types';
const selected = (item: Node | Edge) => item.selected;
@@ -15,13 +15,13 @@ const deleteKeyOptions: UseKeyPressOptions = { actInsideInputWithModifier: false
*
* @internal
*/
export default ({
export function useGlobalKeyHandler({
deleteKeyCode,
multiSelectionKeyCode,
}: {
deleteKeyCode: KeyCode | null;
multiSelectionKeyCode: KeyCode | null;
}): void => {
}): void {
const store = useStoreApi();
const { deleteElements } = useReactFlow();
@@ -39,4 +39,4 @@ export default ({
useEffect(() => {
store.setState({ multiSelectionActive: multiSelectionKeyPressed });
}, [multiSelectionKeyPressed]);
};
}
+3 -3
View File
@@ -20,14 +20,14 @@ const defaultDoc = typeof document !== 'undefined' ? document : null;
* @param param.options - Options
* @returns boolean
*/
export default (
export function useKeyPress(
// the keycode can be a string 'a' or an array of strings ['a', 'a+d']
// a string means a single key 'a' or a combination when '+' is used 'a+d'
// an array means different possibilites. Explainer: ['a', 'd+s'] here the
// user can use the single key 'a' or the combination 'd' + 's'
keyCode: KeyCode | null = null,
options: UseKeyPressOptions = { target: defaultDoc, actInsideInputWithModifier: true }
): boolean => {
): boolean {
const [keyPressed, setKeyPressed] = useState(false);
// we need to remember if a modifier key is pressed in order to track it
@@ -113,7 +113,7 @@ export default (
}, [keyCode, setKeyPressed]);
return keyPressed;
};
}
// utils
+1 -3
View File
@@ -11,10 +11,8 @@ const nodesSelector = (state: ReactFlowState) => state.nodes;
* @public
* @returns An array of nodes
*/
function useNodes<NodeType extends Node = Node>(): NodeType[] {
export function useNodes<NodeType extends Node = Node>(): NodeType[] {
const nodes = useStore(nodesSelector, shallow) as NodeType[];
return nodes;
}
export default useNodes;
@@ -28,10 +28,8 @@ const defaultOptions = {
* @param options.includeHiddenNodes - defaults to false
* @returns boolean indicating whether all nodes are initialized
*/
function useNodesInitialized(options: UseNodesInitializedOptions = defaultOptions): boolean {
export function useNodesInitialized(options: UseNodesInitializedOptions = defaultOptions): boolean {
const initialized = useStore(selector(options));
return initialized;
}
export default useNodesInitialized;
+2 -4
View File
@@ -1,6 +1,6 @@
import { useEffect, useRef } from 'react';
import useReactFlow from './useReactFlow';
import { useReactFlow } from './useReactFlow';
import type { OnInit } from '../types';
/**
@@ -8,7 +8,7 @@ import type { OnInit } from '../types';
*
* @internal
*/
function useOnInitHandler(onInit: OnInit | undefined) {
export function useOnInitHandler(onInit: OnInit | undefined) {
const rfInstance = useReactFlow();
const isInitialized = useRef<boolean>(false);
@@ -19,5 +19,3 @@ function useOnInitHandler(onInit: OnInit | undefined) {
}
}, [onInit, rfInstance.viewportInitialized]);
}
export default useOnInitHandler;
@@ -13,7 +13,7 @@ export type UseOnSelectionChangeOptions = {
* @public
* @params params.onChange - The handler to register
*/
function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
export function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
const store = useStoreApi();
useEffect(() => {
@@ -26,5 +26,3 @@ function useOnSelectionChange({ onChange }: UseOnSelectionChangeOptions) {
};
}, [onChange]);
}
export default useOnSelectionChange;
@@ -17,7 +17,7 @@ export type UseOnViewportChangeOptions = {
* @param params.onChange - gets called when the viewport changes
* @param params.onEnd - gets called when the viewport stops changing
*/
function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOptions) {
export function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOptions) {
const store = useStoreApi();
useEffect(() => {
@@ -32,5 +32,3 @@ function useOnViewportChange({ onStart, onChange, onEnd }: UseOnViewportChangeOp
store.setState({ onViewportChangeEnd: onEnd });
}, [onEnd]);
}
export default useOnViewportChange;
+1 -1
View File
@@ -23,7 +23,7 @@ import { isNode } from '../utils';
* @public
* @returns ReactFlowInstance
*/
export default function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(): ReactFlowInstance<
export function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(): ReactFlowInstance<
NodeType,
EdgeType
> {
+1 -3
View File
@@ -8,7 +8,7 @@ import { useStoreApi } from '../hooks/useStore';
*
* @internal
*/
function useResizeHandler(domNode: MutableRefObject<HTMLDivElement | null>): void {
export function useResizeHandler(domNode: MutableRefObject<HTMLDivElement | null>): void {
const store = useStoreApi();
useEffect(() => {
@@ -42,5 +42,3 @@ function useResizeHandler(domNode: MutableRefObject<HTMLDivElement | null>): voi
}
}, []);
}
export default useResizeHandler;
@@ -9,7 +9,7 @@ import { useStoreApi } from '../hooks/useStore';
* @public
* @returns function for updating node internals
*/
function useUpdateNodeInternals(): UpdateNodeInternals {
export function useUpdateNodeInternals(): UpdateNodeInternals {
const store = useStoreApi();
return useCallback<UpdateNodeInternals>((id: string | string[]) => {
@@ -28,5 +28,3 @@ function useUpdateNodeInternals(): UpdateNodeInternals {
requestAnimationFrame(() => updateNodeDimensions(updates));
}, []);
}
export default useUpdateNodeInternals;
@@ -13,7 +13,7 @@ const selectedAndDraggable = (nodesDraggable: boolean) => (n: Node) =>
* @internal
* @returns function for updating node positions
*/
function useUpdateNodePositions() {
export function useUpdateNodePositions() {
const store = useStoreApi();
const updatePositions = useCallback((params: { x: number; y: number; isShiftPressed: boolean }) => {
@@ -63,5 +63,3 @@ function useUpdateNodePositions() {
return updatePositions;
}
export default useUpdateNodePositions;
+1 -3
View File
@@ -16,10 +16,8 @@ const viewportSelector = (state: ReactFlowState) => ({
* @public
* @returns The current viewport
*/
function useViewport(): Viewport {
export function useViewport(): Viewport {
const viewport = useStore(viewportSelector, shallow);
return viewport;
}
export default useViewport;
+1 -1
View File
@@ -12,7 +12,7 @@ const selector = (state: ReactFlowState) => state.panZoom?.syncViewport;
* @internal
* @param viewport
*/
export default function useViewportSync(viewport?: Viewport) {
export function useViewportSync(viewport?: Viewport) {
const syncViewport = useStore(selector);
const store = useStoreApi();
@@ -12,7 +12,7 @@ import { type ReactFlowState } from '../types';
* @param onlyRenderVisible
* @returns array with visible edge ids
*/
function useVisibleEdgeIds(onlyRenderVisible: boolean): string[] {
export function useVisibleEdgeIds(onlyRenderVisible: boolean): string[] {
const edgeIds = useStore(
useCallback(
(s: ReactFlowState) => {
@@ -52,5 +52,3 @@ function useVisibleEdgeIds(onlyRenderVisible: boolean): string[] {
return edgeIds;
}
export default useVisibleEdgeIds;
@@ -20,10 +20,8 @@ const selector = (onlyRenderVisible: boolean) => (s: ReactFlowState) => {
* @param onlyRenderVisible
* @returns array with visible node ids
*/
function useVisibleNodeIds(onlyRenderVisible: boolean) {
export function useVisibleNodeIds(onlyRenderVisible: boolean) {
const nodeIds = useStore(useCallback(selector(onlyRenderVisible), [onlyRenderVisible]), shallow);
return nodeIds;
}
export default useVisibleNodeIds;
+17 -17
View File
@@ -1,28 +1,28 @@
export { default as ReactFlow } from './container/ReactFlow';
export { default as Handle, type HandleComponentProps } from './components/Handle';
export { default as EdgeText } from './components/Edges/EdgeText';
export { Handle, type HandleComponentProps } from './components/Handle';
export { EdgeText } from './components/Edges/EdgeText';
export { StraightEdge } from './components/Edges/StraightEdge';
export { StepEdge } from './components/Edges/StepEdge';
export { BezierEdge } from './components/Edges/BezierEdge';
export { SimpleBezierEdge, getSimpleBezierPath } from './components/Edges/SimpleBezierEdge';
export { SmoothStepEdge } from './components/Edges/SmoothStepEdge';
export { default as BaseEdge } from './components/Edges/BaseEdge';
export { default as ReactFlowProvider } from './components/ReactFlowProvider';
export { default as Panel, type PanelProps } from './components/Panel';
export { default as EdgeLabelRenderer } from './components/EdgeLabelRenderer';
export { default as ViewportPortal } from './components/ViewportPortal';
export { BaseEdge } from './components/Edges/BaseEdge';
export { ReactFlowProvider } from './components/ReactFlowProvider';
export { Panel, type PanelProps } from './components/Panel';
export { EdgeLabelRenderer } from './components/EdgeLabelRenderer';
export { ViewportPortal } from './components/ViewportPortal';
export { default as useReactFlow } from './hooks/useReactFlow';
export { default as useUpdateNodeInternals } from './hooks/useUpdateNodeInternals';
export { default as useNodes } from './hooks/useNodes';
export { default as useEdges } from './hooks/useEdges';
export { default as useViewport } from './hooks/useViewport';
export { default as useKeyPress } from './hooks/useKeyPress';
export * from './hooks/useNodesEdgesState';
export { useReactFlow } from './hooks/useReactFlow';
export { useUpdateNodeInternals } from './hooks/useUpdateNodeInternals';
export { useNodes } from './hooks/useNodes';
export { useEdges } from './hooks/useEdges';
export { useViewport } from './hooks/useViewport';
export { useKeyPress } from './hooks/useKeyPress';
export { useNodesState, useEdgesState } from './hooks/useNodesEdgesState';
export { useStore, useStoreApi } from './hooks/useStore';
export { default as useOnViewportChange, type UseOnViewportChangeOptions } from './hooks/useOnViewportChange';
export { default as useOnSelectionChange, type UseOnSelectionChangeOptions } from './hooks/useOnSelectionChange';
export { default as useNodesInitialized, type UseNodesInitializedOptions } from './hooks/useNodesInitialized';
export { useOnViewportChange, type UseOnViewportChangeOptions } from './hooks/useOnViewportChange';
export { useOnSelectionChange, type UseOnSelectionChangeOptions } from './hooks/useOnSelectionChange';
export { useNodesInitialized, type UseNodesInitializedOptions } from './hooks/useNodesInitialized';
export { useHandleConnections } from './hooks/useHandleConnections';
export { useNodesData } from './hooks/useNodesData';
export { useConnection } from './hooks/useConnection';
-2
View File
@@ -69,8 +69,6 @@ export type EdgeWrapperProps = {
noPanClassName: string;
onClick?: EdgeMouseHandler;
onDoubleClick?: EdgeMouseHandler;
sourceHandleId?: string | null;
targetHandleId?: string | null;
onEdgeUpdate?: OnEdgeUpdateFunc;
onContextMenu?: EdgeMouseHandler;
onMouseEnter?: EdgeMouseHandler;
+13 -7
View File
@@ -54,20 +54,26 @@ function applyChanges(changes: any[], elements: any[]): any[] {
return changes.filter((c) => c.type === 'reset').map((c) => c.item);
}
let remainingChanges = changes;
let remainingChanges = [];
const updatedElements: any[] = [];
for (const change of changes) {
if (change.type === 'add') {
updatedElements.push(change.item);
} else {
remainingChanges.push(change);
}
}
for (const item of elements) {
const nextChanges: any[] = [];
const _remainingChanges: any[] = [];
for (const c of remainingChanges) {
if (c.type === 'add') {
updatedElements.push(c.item);
} else if (c.id === item.id) {
nextChanges.push(c);
for (const change of remainingChanges) {
if (change.id === item.id) {
nextChanges.push(change);
} else {
_remainingChanges.push(c);
_remainingChanges.push(change);
}
}
+7 -1
View File
@@ -106,6 +106,12 @@
.xy-flow__edges {
position: absolute;
svg {
overflow: visible;
position: absolute;
pointer-events: none;
}
}
.xy-flow__edge {
@@ -156,7 +162,7 @@
}
}
.xy-flow__connectionline {
svg.xy-flow__connectionline {
z-index: 1001;
overflow: visible;
position: absolute;
-6
View File
@@ -43,12 +43,6 @@
--xy-controls-box-shadow-default: 0 0 2px 1px rgba(0, 0, 0, 0.08);
}
.xy-flow__edges svg {
overflow: visible;
position: absolute;
pointer-events: none;
}
.xy-flow__edge {
&.updating {
.xy-flow__edge-path {