Merge branch 'main' into migrate/svelte5
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Make it possible to use expandParent with immer and other immutable helpers
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': patch
|
||||||
|
'@xyflow/svelte': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add group node to BuiltInNode type
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Allow custom data-testid for ReactFlow component
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Type isValidConnection prop correctly by passing EdgeType
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Support passing `path` element attributes to `BaseEdge` component.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Prevent default scrolling behavior when nodes or a selection is moved with an arrow key press.
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@xyflow/react': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Forward ref of the div inside Panel components.
|
||||||
@@ -165,7 +165,7 @@ React Flow and Svelte Flow are maintained by the team behind [xyflow](https://xy
|
|||||||
|
|
||||||
- Christopher • [Twitter](https://twitter.com/chrtze) • [Github](https://github.com/chrtze)
|
- Christopher • [Twitter](https://twitter.com/chrtze) • [Github](https://github.com/chrtze)
|
||||||
- Hayleigh • [Twitter](https://twitter.com/hayleighdotdev) • [Github](https://github.com/hayleigh-dot-dev)
|
- Hayleigh • [Twitter](https://twitter.com/hayleighdotdev) • [Github](https://github.com/hayleigh-dot-dev)
|
||||||
- John • [Website](https://johnrobbdesign.com/) • [Mastodon](https://mastodon.social/@johnrobbjr)
|
- Abbey • [Github](https://github.com/printerscanner)
|
||||||
- Moritz • [Twitter](https://twitter.com/moklick) • [Github](https://github.com/moklick)
|
- Moritz • [Twitter](https://twitter.com/moklick) • [Github](https://github.com/moklick)
|
||||||
- Peter • [Github](https://github.com/peterkogo)
|
- Peter • [Github](https://github.com/peterkogo)
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
@keyframes react-flow-edge-dash {
|
||||||
|
from {
|
||||||
|
stroke-dashoffset: 100;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
stroke-dashoffset: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.react-flow__edge-custom3 {
|
||||||
|
stroke-dasharray: 100;
|
||||||
|
stroke-dashoffset: 100;
|
||||||
|
animation: react-flow-edge-dash 1s linear forwards;
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import { FC } from 'react';
|
||||||
|
import { BaseEdge, EdgeProps, EdgeText, getSmoothStepPath } from '@xyflow/react';
|
||||||
|
import './CustomEdge3.css';
|
||||||
|
|
||||||
|
const CustomEdge: FC<EdgeProps> = ({
|
||||||
|
id,
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
sourcePosition,
|
||||||
|
targetPosition,
|
||||||
|
data,
|
||||||
|
}) => {
|
||||||
|
const [edgePath, labelX, labelY] = getSmoothStepPath({
|
||||||
|
sourceX,
|
||||||
|
sourceY,
|
||||||
|
sourcePosition,
|
||||||
|
targetX,
|
||||||
|
targetY,
|
||||||
|
targetPosition,
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<BaseEdge id={id} path={edgePath} pathLength={100} />
|
||||||
|
<EdgeText
|
||||||
|
x={labelX}
|
||||||
|
y={labelY - 5}
|
||||||
|
label={data.text}
|
||||||
|
labelBgStyle={{ fill: 'transparent' }}
|
||||||
|
onClick={() => console.log(data)}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomEdge;
|
||||||
@@ -16,6 +16,7 @@ import {
|
|||||||
|
|
||||||
import CustomEdge from './CustomEdge';
|
import CustomEdge from './CustomEdge';
|
||||||
import CustomEdge2 from './CustomEdge2';
|
import CustomEdge2 from './CustomEdge2';
|
||||||
|
import CustomEdge3 from './CustomEdge3';
|
||||||
|
|
||||||
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
const onNodeDragStop = (_: MouseEvent, node: Node) => console.log('drag stop', node);
|
||||||
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
|
const onNodeClick = (_: MouseEvent, node: Node) => console.log('click', node);
|
||||||
@@ -63,6 +64,12 @@ const initialNodes: Node[] = [
|
|||||||
data: { label: 'Output 9' },
|
data: { label: 'Output 9' },
|
||||||
position: { x: 675, y: 500 },
|
position: { x: 675, y: 500 },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: '10',
|
||||||
|
type: 'output',
|
||||||
|
data: { label: 'Output 10' },
|
||||||
|
position: { x: 50, y: 400 },
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const initialEdges: Edge[] = [
|
const initialEdges: Edge[] = [
|
||||||
@@ -137,6 +144,13 @@ const initialEdges: Edge[] = [
|
|||||||
type: 'custom2',
|
type: 'custom2',
|
||||||
data: { text: 'custom edge 2' },
|
data: { text: 'custom edge 2' },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
id: 'e3a-10',
|
||||||
|
source: '3a',
|
||||||
|
target: '10',
|
||||||
|
type: 'custom3',
|
||||||
|
data: { text: 'custom edge 3' },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
id: 'e5-6',
|
id: 'e5-6',
|
||||||
source: '5',
|
source: '5',
|
||||||
@@ -173,6 +187,7 @@ const initialEdges: Edge[] = [
|
|||||||
const edgeTypes: EdgeTypes = {
|
const edgeTypes: EdgeTypes = {
|
||||||
custom: CustomEdge,
|
custom: CustomEdge,
|
||||||
custom2: CustomEdge2,
|
custom2: CustomEdge2,
|
||||||
|
custom3: CustomEdge3,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultEdgeOptions = {
|
const defaultEdgeOptions = {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import { EdgeText } from './EdgeText';
|
|||||||
import type { BaseEdgeProps } from '../../types';
|
import type { BaseEdgeProps } from '../../types';
|
||||||
|
|
||||||
export function BaseEdge({
|
export function BaseEdge({
|
||||||
id,
|
|
||||||
path,
|
path,
|
||||||
labelX,
|
labelX,
|
||||||
labelY,
|
labelY,
|
||||||
@@ -15,23 +14,12 @@ export function BaseEdge({
|
|||||||
labelBgStyle,
|
labelBgStyle,
|
||||||
labelBgPadding,
|
labelBgPadding,
|
||||||
labelBgBorderRadius,
|
labelBgBorderRadius,
|
||||||
style,
|
|
||||||
markerEnd,
|
|
||||||
markerStart,
|
|
||||||
className,
|
|
||||||
interactionWidth = 20,
|
interactionWidth = 20,
|
||||||
|
...props
|
||||||
}: BaseEdgeProps) {
|
}: BaseEdgeProps) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<path
|
<path {...props} d={path} fill="none" className={cc(['react-flow__edge-path', props.className])} />
|
||||||
id={id}
|
|
||||||
style={style}
|
|
||||||
d={path}
|
|
||||||
fill="none"
|
|
||||||
className={cc(['react-flow__edge-path', className])}
|
|
||||||
markerEnd={markerEnd}
|
|
||||||
markerStart={markerStart}
|
|
||||||
/>
|
|
||||||
{interactionWidth && (
|
{interactionWidth && (
|
||||||
<path
|
<path
|
||||||
d={path}
|
d={path}
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ export function NodeWrapper<NodeType extends Node>({
|
|||||||
nodeRef,
|
nodeRef,
|
||||||
});
|
});
|
||||||
} else if (isDraggable && node.selected && Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
|
} else if (isDraggable && node.selected && Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
|
||||||
|
// prevent default scrolling behavior on arrow key press when node is moved
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
store.setState({
|
store.setState({
|
||||||
ariaLiveMessage: `Moved selected node ${event.key
|
ariaLiveMessage: `Moved selected node ${event.key
|
||||||
.replace('Arrow', '')
|
.replace('Arrow', '')
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ export function NodesSelection<NodeType extends Node>({
|
|||||||
|
|
||||||
const onKeyDown = (event: KeyboardEvent) => {
|
const onKeyDown = (event: KeyboardEvent) => {
|
||||||
if (Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
|
if (Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
moveSelectedNodes({
|
moveSelectedNodes({
|
||||||
direction: arrowKeyDiffs[event.key],
|
direction: arrowKeyDiffs[event.key],
|
||||||
factor: event.shiftKey ? 4 : 1,
|
factor: event.shiftKey ? 4 : 1,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { HTMLAttributes, ReactNode } from 'react';
|
import { forwardRef, type HTMLAttributes, type ReactNode } from 'react';
|
||||||
import cc from 'classcat';
|
import cc from 'classcat';
|
||||||
import type { PanelPosition } from '@xyflow/system';
|
import type { PanelPosition } from '@xyflow/system';
|
||||||
|
|
||||||
@@ -15,17 +15,20 @@ export type PanelProps = HTMLAttributes<HTMLDivElement> & {
|
|||||||
|
|
||||||
const selector = (s: ReactFlowState) => (s.userSelectionActive ? 'none' : 'all');
|
const selector = (s: ReactFlowState) => (s.userSelectionActive ? 'none' : 'all');
|
||||||
|
|
||||||
export function Panel({ position = 'top-left', children, className, style, ...rest }: PanelProps) {
|
export const Panel = forwardRef<HTMLDivElement, PanelProps>(
|
||||||
const pointerEvents = useStore(selector);
|
({ position = 'top-left', children, className, style, ...rest }, ref) => {
|
||||||
const positionClasses = `${position}`.split('-');
|
const pointerEvents = useStore(selector);
|
||||||
|
const positionClasses = `${position}`.split('-');
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cc(['react-flow__panel', className, ...positionClasses])}
|
className={cc(['react-flow__panel', className, ...positionClasses])}
|
||||||
style={{ ...style, pointerEvents }}
|
style={{ ...style, pointerEvents }}
|
||||||
{...rest}
|
ref={ref}
|
||||||
>
|
{...rest}
|
||||||
{children}
|
>
|
||||||
</div>
|
{children}
|
||||||
);
|
</div>
|
||||||
}
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|||||||
@@ -153,11 +153,11 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
data-testid="rf__wrapper"
|
||||||
{...rest}
|
{...rest}
|
||||||
style={{ ...style, ...wrapperStyle }}
|
style={{ ...style, ...wrapperStyle }}
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cc(['react-flow', className, colorModeClassName])}
|
className={cc(['react-flow', className, colorModeClassName])}
|
||||||
data-testid="rf__wrapper"
|
|
||||||
id={id}
|
id={id}
|
||||||
>
|
>
|
||||||
<Wrapper
|
<Wrapper
|
||||||
|
|||||||
@@ -148,26 +148,30 @@ const createStore = ({
|
|||||||
const changes = [];
|
const changes = [];
|
||||||
|
|
||||||
for (const [id, dragItem] of nodeDragItems) {
|
for (const [id, dragItem] of nodeDragItems) {
|
||||||
|
const expandParent = !!(dragItem?.expandParent && dragItem?.parentId && dragItem?.position);
|
||||||
|
|
||||||
const change: NodeChange = {
|
const change: NodeChange = {
|
||||||
id,
|
id,
|
||||||
type: 'position',
|
type: 'position',
|
||||||
position: dragItem.position,
|
position: expandParent
|
||||||
|
? {
|
||||||
|
x: Math.max(0, dragItem.position.x),
|
||||||
|
y: Math.max(0, dragItem.position.y),
|
||||||
|
}
|
||||||
|
: dragItem.position,
|
||||||
dragging,
|
dragging,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (dragItem?.expandParent && dragItem?.parentId && change.position) {
|
if (expandParent) {
|
||||||
parentExpandChildren.push({
|
parentExpandChildren.push({
|
||||||
id,
|
id,
|
||||||
parentId: dragItem.parentId,
|
parentId: dragItem.parentId!,
|
||||||
rect: {
|
rect: {
|
||||||
...dragItem.internals.positionAbsolute,
|
...dragItem.internals.positionAbsolute,
|
||||||
width: dragItem.measured.width!,
|
width: dragItem.measured.width!,
|
||||||
height: dragItem.measured.height!,
|
height: dragItem.measured.height!,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
change.position.x = Math.max(0, change.position.x);
|
|
||||||
change.position.y = Math.max(0, change.position.y);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
changes.push(change);
|
changes.push(change);
|
||||||
|
|||||||
@@ -499,7 +499,7 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
|
|||||||
* If you have custom connection logic its preferred to use this callback over the isValidConnection prop on the handle component for performance reasons.
|
* If you have custom connection logic its preferred to use this callback over the isValidConnection prop on the handle component for performance reasons.
|
||||||
* @default (connection: Connection) => true
|
* @default (connection: Connection) => true
|
||||||
*/
|
*/
|
||||||
isValidConnection?: IsValidConnection;
|
isValidConnection?: IsValidConnection<EdgeType>;
|
||||||
/** With a threshold greater than zero you can control the distinction between node drag and click events.
|
/** With a threshold greater than zero you can control the distinction between node drag and click events.
|
||||||
*
|
*
|
||||||
* If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired.
|
* If threshold equals 1, you need to drag the node 1 pixel before a drag event is fired.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
import type { CSSProperties, HTMLAttributes, ReactNode, MouseEvent as ReactMouseEvent, ComponentType } from 'react';
|
import type { CSSProperties, SVGAttributes, ReactNode, MouseEvent as ReactMouseEvent, ComponentType } from 'react';
|
||||||
import type {
|
import type {
|
||||||
EdgeBase,
|
EdgeBase,
|
||||||
BezierPathOptions,
|
BezierPathOptions,
|
||||||
@@ -14,7 +14,6 @@ import type {
|
|||||||
EdgePosition,
|
EdgePosition,
|
||||||
StepPathOptions,
|
StepPathOptions,
|
||||||
OnError,
|
OnError,
|
||||||
ConnectionState,
|
|
||||||
FinalConnectionState,
|
FinalConnectionState,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
@@ -94,7 +93,7 @@ export type EdgeWrapperProps<EdgeType extends Edge = Edge> = {
|
|||||||
|
|
||||||
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
|
export type DefaultEdgeOptions = DefaultEdgeOptionsBase<Edge>;
|
||||||
|
|
||||||
export type EdgeTextProps = HTMLAttributes<SVGElement> &
|
export type EdgeTextProps = SVGAttributes<SVGElement> &
|
||||||
EdgeLabelOptions & {
|
EdgeLabelOptions & {
|
||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
@@ -123,28 +122,17 @@ export type EdgeProps<EdgeType extends Edge = Edge> = Pick<
|
|||||||
* BaseEdge component props
|
* BaseEdge component props
|
||||||
* @public
|
* @public
|
||||||
*/
|
*/
|
||||||
export type BaseEdgeProps = EdgeLabelOptions & {
|
export type BaseEdgeProps = Omit<SVGAttributes<SVGPathElement>, 'd'> &
|
||||||
/** Unique id of edge */
|
EdgeLabelOptions & {
|
||||||
id?: string;
|
/** Additional padding where interacting with an edge is still possible */
|
||||||
/** Additional padding where interacting with an edge is still possible */
|
interactionWidth?: number;
|
||||||
interactionWidth?: number;
|
/** The x position of edge label */
|
||||||
className?: string;
|
labelX?: number;
|
||||||
/** The x position of edge label */
|
/** The y position of edge label */
|
||||||
labelX?: number;
|
labelY?: number;
|
||||||
/** The y position of edge label */
|
/** SVG path of the edge */
|
||||||
labelY?: number;
|
path: string;
|
||||||
/** Marker at start of edge
|
};
|
||||||
* @example 'url(#arrow)'
|
|
||||||
*/
|
|
||||||
markerStart?: string;
|
|
||||||
/** Marker at end of edge
|
|
||||||
* @example 'url(#arrow)'
|
|
||||||
*/
|
|
||||||
markerEnd?: string;
|
|
||||||
/** SVG path of the edge */
|
|
||||||
path: string;
|
|
||||||
style?: CSSProperties;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper type for edge components that get exported by the library
|
* Helper type for edge components that get exported by the library
|
||||||
|
|||||||
@@ -56,6 +56,8 @@ export type NodeWrapperProps<NodeType extends Node> = {
|
|||||||
nodeClickDistance?: number;
|
nodeClickDistance?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>;
|
export type BuiltInNode =
|
||||||
|
| Node<{ label: string }, 'input' | 'output' | 'default'>
|
||||||
|
| Node<Record<string, never>, 'group'>;
|
||||||
|
|
||||||
export type NodeProps<NodeType extends Node = Node> = NodePropsBase<NodeType>;
|
export type NodeProps<NodeType extends Node = Node> = NodePropsBase<NodeType>;
|
||||||
|
|||||||
@@ -42,7 +42,9 @@ export type NodeTypes = Record<
|
|||||||
|
|
||||||
export type DefaultNodeOptions = Partial<Omit<Node, 'id'>>;
|
export type DefaultNodeOptions = Partial<Omit<Node, 'id'>>;
|
||||||
|
|
||||||
export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>;
|
export type BuiltInNode =
|
||||||
|
| Node<{ label: string }, 'input' | 'output' | 'default'>
|
||||||
|
| Node<Record<string, never>, 'group'>;
|
||||||
|
|
||||||
// TODO SVELTE5 remove this
|
// TODO SVELTE5 remove this
|
||||||
export type NodeEventMap = {
|
export type NodeEventMap = {
|
||||||
|
|||||||
Reference in New Issue
Block a user