Merge branch 'main' into enhance/fitView
This commit is contained in:
5
.changeset/empty-swans-exercise.md
Normal file
5
.changeset/empty-swans-exercise.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@xyflow/react": patch
|
||||
---
|
||||
|
||||
fix: improve TSDoc comments for `BackgroundProps`
|
||||
6
.changeset/famous-impalas-relate.md
Normal file
6
.changeset/famous-impalas-relate.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@xyflow/react': patch
|
||||
'@xyflow/svelte': patch
|
||||
---
|
||||
|
||||
Prevent onPaneClick when connection is in progress. Closes [#5057](https://github.com/xyflow/xyflow/issues/5057)
|
||||
5
.changeset/forty-phones-lick.md
Normal file
5
.changeset/forty-phones-lick.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@xyflow/react": patch
|
||||
---
|
||||
|
||||
fix: improve TSDoc comments for `EdgeLabelOptions` and `BaseEdgeProps`
|
||||
5
.changeset/polite-drinks-listen.md
Normal file
5
.changeset/polite-drinks-listen.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@xyflow/react': patch
|
||||
---
|
||||
|
||||
Add TSDoc annotations for exported edges
|
||||
6
.changeset/twenty-sloths-reflect.md
Normal file
6
.changeset/twenty-sloths-reflect.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
'@xyflow/react': patch
|
||||
'@xyflow/svelte': patch
|
||||
---
|
||||
|
||||
Hidden nodes are not displayed in the mini map anymore
|
||||
5
.changeset/unlucky-lobsters-refuse.md
Normal file
5
.changeset/unlucky-lobsters-refuse.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@xyflow/react': patch
|
||||
---
|
||||
|
||||
Release key even when an inout field is focused
|
||||
5
.changeset/witty-toys-wash.md
Normal file
5
.changeset/witty-toys-wash.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@xyflow/system': patch
|
||||
---
|
||||
|
||||
Add center-left and center-right as a panel position
|
||||
@@ -16,6 +16,7 @@ export enum BackgroundVariant {
|
||||
* @expand
|
||||
*/
|
||||
export type BackgroundProps = {
|
||||
/** When multiple backgrounds are present on the page, each one should have a unique id. */
|
||||
id?: string;
|
||||
/** Color of the pattern */
|
||||
color?: string;
|
||||
@@ -25,16 +26,31 @@ export type BackgroundProps = {
|
||||
className?: string;
|
||||
/** Class applied to the pattern */
|
||||
patternClassName?: string;
|
||||
/** Gap between repetitions of the pattern */
|
||||
/**
|
||||
* The gap between patterns. Passing in a tuple allows you to control the x and y gap
|
||||
* independently.
|
||||
* @default '28'
|
||||
*/
|
||||
gap?: number | [number, number];
|
||||
/** Size of a single pattern element */
|
||||
/**
|
||||
* The radius of each dot or the size of each rectangle if `BackgroundVariant.Dots` or
|
||||
* `BackgroundVariant.Cross` is used. This defaults to 1 or 6 respectively, or ignored if
|
||||
* `BackgroundVariant.Lines` is used.
|
||||
*/
|
||||
size?: number;
|
||||
/** Offset of the pattern */
|
||||
/**
|
||||
* Offset of the pattern
|
||||
* @default 2
|
||||
*/
|
||||
offset?: number | [number, number];
|
||||
/** Line width of the Line pattern */
|
||||
/**
|
||||
* The stroke thickness used when drawing the pattern.
|
||||
* @default 1
|
||||
*/
|
||||
lineWidth?: number;
|
||||
/**
|
||||
* Variant of the pattern
|
||||
* @default BackgroundVariant.Dots
|
||||
* @example BackgroundVariant.Lines, BackgroundVariant.Dots, BackgroundVariant.Cross
|
||||
* 'lines', 'dots', 'cross'
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,8 @@ import type { MiniMapProps } from './types';
|
||||
const defaultWidth = 200;
|
||||
const defaultHeight = 150;
|
||||
|
||||
const filterHidden = (node: Node) => !node.hidden;
|
||||
|
||||
const selector = (s: ReactFlowState) => {
|
||||
const viewBB: Rect = {
|
||||
x: -s.transform[0] / s.transform[2],
|
||||
@@ -25,7 +27,10 @@ const selector = (s: ReactFlowState) => {
|
||||
|
||||
return {
|
||||
viewBB,
|
||||
boundingRect: s.nodeLookup.size > 0 ? getBoundsOfRects(getInternalNodesBounds(s.nodeLookup), viewBB) : viewBB,
|
||||
boundingRect:
|
||||
s.nodeLookup.size > 0
|
||||
? getBoundsOfRects(getInternalNodesBounds(s.nodeLookup, { filter: filterHidden }), viewBB)
|
||||
: viewBB,
|
||||
rfId: s.rfId,
|
||||
panZoom: s.panZoom,
|
||||
translateExtent: s.translateExtent,
|
||||
@@ -113,16 +118,16 @@ function MiniMapComponent<NodeType extends Node = Node>({
|
||||
|
||||
const onSvgClick = onClick
|
||||
? (event: MouseEvent) => {
|
||||
const [x, y] = minimapInstance.current?.pointer(event) || [0, 0];
|
||||
onClick(event, { x, y });
|
||||
}
|
||||
const [x, y] = minimapInstance.current?.pointer(event) || [0, 0];
|
||||
onClick(event, { x, y });
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const onSvgNodeClick = onNodeClick
|
||||
? useCallback((event: MouseEvent, nodeId: string) => {
|
||||
const node = store.getState().nodeLookup.get(nodeId)!;
|
||||
onNodeClick(event, node);
|
||||
}, [])
|
||||
const node = store.getState().nodeLookup.get(nodeId)!;
|
||||
onNodeClick(event, node);
|
||||
}, [])
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
|
||||
@@ -10,37 +10,38 @@ const selector = (s: ReactFlowState) => s.domNode?.querySelector('.react-flow__e
|
||||
* Edges are SVG-based. If you want to render more complex labels you can use the
|
||||
* `<EdgeLabelRenderer />` component to access a div based renderer. This component
|
||||
* is a portal that renders the label in a `<div />` that is positioned on top of
|
||||
* the edges. You can see an example usage of the component in the [edge label renderer](/examples/edges/edge-label-renderer) example.
|
||||
* the edges. You can see an example usage of the component in the
|
||||
* [edge label renderer example](/examples/edges/edge-label-renderer).
|
||||
* @public
|
||||
*
|
||||
* @example
|
||||
*```jsx
|
||||
*import React from 'react';
|
||||
*import { getBezierPath, EdgeLabelRenderer, BaseEdge } from '@xyflow/react';
|
||||
* ```jsx
|
||||
* import React from 'react';
|
||||
* import { getBezierPath, EdgeLabelRenderer, BaseEdge } from '@xyflow/react';
|
||||
*
|
||||
*export function CustomEdge({ id, data, ...props }) {
|
||||
* const [edgePath, labelX, labelY] = getBezierPath(props);
|
||||
* export function CustomEdge({ id, data, ...props }) {
|
||||
* const [edgePath, labelX, labelY] = getBezierPath(props);
|
||||
*
|
||||
* return (
|
||||
* <>
|
||||
* <BaseEdge id={id} path={edgePath} />
|
||||
* <EdgeLabelRenderer>
|
||||
* <div
|
||||
* style={{
|
||||
* position: 'absolute',
|
||||
* transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
||||
* background: '#ffcc00',
|
||||
* padding: 10,
|
||||
* }}
|
||||
* className="nodrag nopan"
|
||||
* >
|
||||
* {data.label}
|
||||
* </div>
|
||||
* </EdgeLabelRenderer>
|
||||
* </>
|
||||
* );
|
||||
*};
|
||||
*```
|
||||
* return (
|
||||
* <>
|
||||
* <BaseEdge id={id} path={edgePath} />
|
||||
* <EdgeLabelRenderer>
|
||||
* <div
|
||||
* style={{
|
||||
* position: 'absolute',
|
||||
* transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
|
||||
* background: '#ffcc00',
|
||||
* padding: 10,
|
||||
* }}
|
||||
* className="nodrag nopan"
|
||||
* >
|
||||
* {data.label}
|
||||
* </div>
|
||||
* </EdgeLabelRenderer>
|
||||
* </>
|
||||
* );
|
||||
* };
|
||||
* ```
|
||||
*
|
||||
* @remarks The `<EdgeLabelRenderer />` has no pointer events by default. If you want to
|
||||
* add mouse interactions you need to set the style `pointerEvents: all` and add
|
||||
|
||||
@@ -61,7 +61,34 @@ function createBezierEdge(params: { isInternal: boolean }) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that can be used inside a custom edge to render a bezier curve.
|
||||
*
|
||||
* @public
|
||||
* @example
|
||||
*
|
||||
* ```tsx
|
||||
* import { BezierEdge } from '@xyflow/react';
|
||||
*
|
||||
* function CustomEdge({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }) {
|
||||
* return (
|
||||
* <BezierEdge
|
||||
* sourceX={sourceX}
|
||||
* sourceY={sourceY}
|
||||
* targetX={targetX}
|
||||
* targetY={targetY}
|
||||
* sourcePosition={sourcePosition}
|
||||
* targetPosition={targetPosition}
|
||||
* />
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
const BezierEdge = createBezierEdge({ isInternal: false });
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const BezierEdgeInternal = createBezierEdge({ isInternal: true });
|
||||
|
||||
BezierEdge.displayName = 'BezierEdge';
|
||||
|
||||
@@ -27,6 +27,9 @@ export interface EdgeAnchorProps extends SVGAttributes<SVGGElement> {
|
||||
|
||||
const EdgeUpdaterClassName = 'react-flow__edgeupdater';
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export function EdgeAnchor({
|
||||
position,
|
||||
centerX,
|
||||
|
||||
@@ -77,26 +77,26 @@ EdgeTextComponent.displayName = 'EdgeText';
|
||||
* You can use the `<EdgeText />` component as a helper component to display text
|
||||
* within your custom edges.
|
||||
*
|
||||
*@public
|
||||
* @public
|
||||
*
|
||||
*@example
|
||||
*```jsx
|
||||
*import { EdgeText } from '@xyflow/react';
|
||||
* @example
|
||||
* ```jsx
|
||||
* import { EdgeText } from '@xyflow/react';
|
||||
*
|
||||
*export function CustomEdgeLabel({ label }) {
|
||||
* return (
|
||||
* <EdgeText
|
||||
* x={100}
|
||||
* y={100}
|
||||
* label={label}
|
||||
* labelStyle={{ fill: 'white' }}
|
||||
* labelShowBg
|
||||
* labelBgStyle={{ fill: 'red' }}
|
||||
* labelBgPadding={[2, 4]}
|
||||
* labelBgBorderRadius={2}
|
||||
* />
|
||||
* );
|
||||
*}
|
||||
* export function CustomEdgeLabel({ label }) {
|
||||
* return (
|
||||
* <EdgeText
|
||||
* x={100}
|
||||
* y={100}
|
||||
* label={label}
|
||||
* labelStyle={{ fill: 'white' }}
|
||||
* labelShowBg
|
||||
* labelBgStyle={{ fill: 'red' }}
|
||||
* labelBgPadding={[2, 4]}
|
||||
* labelBgBorderRadius={2}
|
||||
* />
|
||||
* );
|
||||
* }
|
||||
*```
|
||||
*/
|
||||
export const EdgeText = memo(EdgeTextComponent);
|
||||
|
||||
@@ -62,7 +62,34 @@ function createSmoothStepEdge(params: { isInternal: boolean }) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that can be used inside a custom edge to render a smooth step edge.
|
||||
*
|
||||
* @public
|
||||
* @example
|
||||
*
|
||||
* ```tsx
|
||||
* import { SmoothStepEdge } from '@xyflow/react';
|
||||
*
|
||||
* function CustomEdge({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }) {
|
||||
* return (
|
||||
* <SmoothStepEdge
|
||||
* sourceX={sourceX}
|
||||
* sourceY={sourceY}
|
||||
* targetX={targetX}
|
||||
* targetY={targetY}
|
||||
* sourcePosition={sourcePosition}
|
||||
* targetPosition={targetPosition}
|
||||
* />
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
const SmoothStepEdge = createSmoothStepEdge({ isInternal: false });
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const SmoothStepEdgeInternal = createSmoothStepEdge({ isInternal: true });
|
||||
|
||||
SmoothStepEdge.displayName = 'SmoothStepEdge';
|
||||
|
||||
@@ -21,7 +21,34 @@ function createStepEdge(params: { isInternal: boolean }) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that can be used inside a custom edge to render a step edge.
|
||||
*
|
||||
* @public
|
||||
* @example
|
||||
*
|
||||
* ```tsx
|
||||
* import { StepEdge } from '@xyflow/react';
|
||||
*
|
||||
* function CustomEdge({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }) {
|
||||
* return (
|
||||
* <StepEdge
|
||||
* sourceX={sourceX}
|
||||
* sourceY={sourceY}
|
||||
* targetX={targetX}
|
||||
* targetY={targetY}
|
||||
* sourcePosition={sourcePosition}
|
||||
* targetPosition={targetPosition}
|
||||
* />
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
const StepEdge = createStepEdge({ isInternal: false });
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const StepEdgeInternal = createStepEdge({ isInternal: true });
|
||||
|
||||
StepEdge.displayName = 'StepEdge';
|
||||
|
||||
@@ -50,7 +50,32 @@ function createStraightEdge(params: { isInternal: boolean }) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component that can be used inside a custom edge to render a straight line.
|
||||
*
|
||||
* @public
|
||||
* @example
|
||||
*
|
||||
* ```tsx
|
||||
* import { StraightEdge } from '@xyflow/react';
|
||||
*
|
||||
* function CustomEdge({ sourceX, sourceY, targetX, targetY }) {
|
||||
* return (
|
||||
* <StraightEdge
|
||||
* sourceX={sourceX}
|
||||
* sourceY={sourceY}
|
||||
* targetX={targetX}
|
||||
* targetY={targetY}
|
||||
* />
|
||||
* );
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
const StraightEdge = createStraightEdge({ isInternal: false });
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
const StraightEdgeInternal = createStraightEdge({ isInternal: true });
|
||||
|
||||
StraightEdge.displayName = 'StraightEdge';
|
||||
|
||||
@@ -61,6 +61,7 @@ const wrapHandler = (
|
||||
const selector = (s: ReactFlowState) => ({
|
||||
userSelectionActive: s.userSelectionActive,
|
||||
elementsSelectable: s.elementsSelectable,
|
||||
connectionInProgress: s.connection.inProgress,
|
||||
dragging: s.paneDragging,
|
||||
});
|
||||
|
||||
@@ -81,7 +82,7 @@ export function Pane({
|
||||
children,
|
||||
}: PaneProps) {
|
||||
const store = useStoreApi();
|
||||
const { userSelectionActive, elementsSelectable, dragging } = useStore(selector, shallow);
|
||||
const { userSelectionActive, elementsSelectable, dragging, connectionInProgress } = useStore(selector, shallow);
|
||||
const hasActiveSelection = elementsSelectable && (isSelecting || userSelectionActive);
|
||||
|
||||
const container = useRef<HTMLDivElement | null>(null);
|
||||
@@ -96,7 +97,8 @@ export function Pane({
|
||||
|
||||
const onClick = (event: ReactMouseEvent) => {
|
||||
// We prevent click events when the user let go of the selectionKey during a selection
|
||||
if (selectionInProgress.current) {
|
||||
// We also prevent click events when a connection is in progress
|
||||
if (selectionInProgress.current || connectionInProgress) {
|
||||
selectionInProgress.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -106,13 +106,6 @@ export function useKeyPress(
|
||||
};
|
||||
|
||||
const upHandler = (event: KeyboardEvent) => {
|
||||
const preventAction =
|
||||
(!modifierPressed.current || (modifierPressed.current && !options.actInsideInputWithModifier)) &&
|
||||
isInputDOMNode(event);
|
||||
|
||||
if (preventAction) {
|
||||
return false;
|
||||
}
|
||||
const keyOrCode = useKeyOrCode(event.code, keysToWatch);
|
||||
|
||||
if (isMatchingKey(keyCodes, pressedKeys.current, true)) {
|
||||
|
||||
@@ -23,7 +23,11 @@ import { EdgeTypes, InternalNode, Node } from '.';
|
||||
* @inline
|
||||
*/
|
||||
export type EdgeLabelOptions = {
|
||||
label?: string | ReactNode;
|
||||
/**
|
||||
* The label or custom element to render along the edge. This is commonly a text label or some
|
||||
* custom controls.
|
||||
*/
|
||||
label?: ReactNode;
|
||||
labelStyle?: CSSProperties;
|
||||
labelShowBg?: boolean;
|
||||
labelBgStyle?: CSSProperties;
|
||||
@@ -136,13 +140,20 @@ export type EdgeProps<EdgeType extends Edge = Edge> = Pick<
|
||||
*/
|
||||
export type BaseEdgeProps = Omit<SVGAttributes<SVGPathElement>, 'd'> &
|
||||
EdgeLabelOptions & {
|
||||
/** Additional padding where interacting with an edge is still possible */
|
||||
/**
|
||||
* The width of the invisible area around the edge that the user can interact with. This is
|
||||
* useful for making the edge easier to click or hover over.
|
||||
*/
|
||||
interactionWidth?: number;
|
||||
/** The x position of edge label */
|
||||
labelX?: number;
|
||||
/** The y position of edge label */
|
||||
labelY?: number;
|
||||
/** SVG path of the edge */
|
||||
/**
|
||||
* The SVG path string that defines the edge. This should look something like
|
||||
* `'M 0 0 L 100 100'` for a simple line. The utility functions like `getSimpleBezierEdge` can
|
||||
* be used to generate this string for you.
|
||||
*/
|
||||
path: string;
|
||||
};
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@
|
||||
selectionKeyPressed,
|
||||
selectionMode,
|
||||
panActivationKeyPressed,
|
||||
unselectNodesAndEdges
|
||||
unselectNodesAndEdges,
|
||||
connection,
|
||||
} = useStore();
|
||||
|
||||
let container: HTMLDivElement;
|
||||
@@ -80,7 +81,8 @@
|
||||
|
||||
function onClick(event: MouseEvent | TouchEvent) {
|
||||
// We prevent click events when the user let go of the selectionKey during a selection
|
||||
if (selectionInProgress) {
|
||||
// We also prevent click events when a connection is in progress
|
||||
if (selectionInProgress || $connection.inProgress) {
|
||||
selectionInProgress = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,12 @@
|
||||
|
||||
$: {
|
||||
boundingRect =
|
||||
$nodeLookup.size > 0 ? getBoundsOfRects(getInternalNodesBounds($nodeLookup), viewBB) : viewBB;
|
||||
$nodeLookup.size > 0
|
||||
? getBoundsOfRects(
|
||||
getInternalNodesBounds($nodeLookup, { filter: (n) => !n.hidden }),
|
||||
viewBB
|
||||
)
|
||||
: viewBB;
|
||||
$nodes;
|
||||
}
|
||||
|
||||
|
||||
@@ -278,6 +278,14 @@ svg.xy-flow__connectionline {
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
&.top,
|
||||
&.bottom {
|
||||
&.center {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
|
||||
&.left {
|
||||
left: 0;
|
||||
}
|
||||
@@ -286,9 +294,12 @@ svg.xy-flow__connectionline {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
&.center {
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
&.left,
|
||||
&.right {
|
||||
&.center {
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,15 @@ export type UpdateNodeInternals = (nodeId: string | string[]) => void;
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
export type PanelPosition = 'top-left' | 'top-center' | 'top-right' | 'bottom-left' | 'bottom-center' | 'bottom-right';
|
||||
export type PanelPosition =
|
||||
| 'top-left'
|
||||
| 'top-center'
|
||||
| 'top-right'
|
||||
| 'bottom-left'
|
||||
| 'bottom-center'
|
||||
| 'bottom-right'
|
||||
| 'center-left'
|
||||
| 'center-right';
|
||||
|
||||
export type ProOptions = {
|
||||
account?: string;
|
||||
|
||||
Reference in New Issue
Block a user