Merge branch 'main' into migrate/svelte5

This commit is contained in:
peterkogo
2024-12-09 15:59:30 +01:00
21 changed files with 159 additions and 64 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@xyflow/react': patch
---
Make it possible to use expandParent with immer and other immutable helpers
+6
View File
@@ -0,0 +1,6 @@
---
'@xyflow/react': patch
'@xyflow/svelte': patch
---
Add group node to BuiltInNode type
+5
View File
@@ -0,0 +1,5 @@
---
'@xyflow/react': patch
---
Allow custom data-testid for ReactFlow component
+5
View File
@@ -0,0 +1,5 @@
---
'@xyflow/react': patch
---
Type isValidConnection prop correctly by passing EdgeType
+5
View File
@@ -0,0 +1,5 @@
---
'@xyflow/react': patch
---
Support passing `path` element attributes to `BaseEdge` component.
+5
View File
@@ -0,0 +1,5 @@
---
'@xyflow/react': patch
---
Prevent default scrolling behavior when nodes or a selection is moved with an arrow key press.
+5
View File
@@ -0,0 +1,5 @@
---
'@xyflow/react': patch
---
Forward ref of the div inside Panel components.
+1 -1
View File
@@ -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,
+17 -14
View File
@@ -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
+10 -6
View File
@@ -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);
+1 -1
View File
@@ -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.
+13 -25
View File
@@ -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
+3 -1
View File
@@ -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>;
+3 -1
View File
@@ -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 = {