merged next
This commit is contained in:
@@ -1,5 +1,12 @@
|
||||
# @xyflow/react
|
||||
|
||||
## 12.0.0-next.12
|
||||
|
||||
## Patch changes
|
||||
|
||||
- fix useNodesData: handle invalid node id thanks @saswatax
|
||||
- fix forwardRef: use custom fixForwardRef function
|
||||
|
||||
## 12.0.0-next.11
|
||||
|
||||
## Patch changes
|
||||
|
||||
@@ -96,6 +96,7 @@ export function EdgeUpdateAnchors<EdgeType extends Edge = Edge>({
|
||||
onEdgeUpdateEnd: _onEdgeUpdateEnd,
|
||||
updateConnection,
|
||||
getTransform: () => store.getState().transform,
|
||||
getConnectionStartHandle: () => store.getState().connectionStartHandle,
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
type TouchEvent as ReactTouchEvent,
|
||||
type ForwardedRef,
|
||||
memo,
|
||||
forwardRef,
|
||||
} from 'react';
|
||||
import cc from 'classcat';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
@@ -24,6 +23,7 @@ import {
|
||||
import { useStore, useStoreApi } from '../../hooks/useStore';
|
||||
import { useNodeId } from '../../contexts/NodeIdContext';
|
||||
import { type ReactFlowState } from '../../types';
|
||||
import { fixedForwardRef } from '../../utils';
|
||||
|
||||
export interface HandleComponentProps extends HandleProps, Omit<HTMLAttributes<HTMLDivElement>, 'id'> {}
|
||||
|
||||
@@ -140,6 +140,7 @@ function HandleComponent(
|
||||
onConnect: onConnectExtended,
|
||||
isValidConnection: isValidConnection || currentStore.isValidConnection,
|
||||
getTransform: () => store.getState().transform,
|
||||
getConnectionStartHandle: () => store.getState().connectionStartHandle,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -242,4 +243,4 @@ function HandleComponent(
|
||||
/**
|
||||
* The Handle component is a UI element that is used to connect nodes.
|
||||
*/
|
||||
export const Handle = memo(forwardRef(HandleComponent));
|
||||
export const Handle = memo(fixedForwardRef(HandleComponent));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ForwardedRef, forwardRef, type CSSProperties } from 'react';
|
||||
import { ForwardedRef, type CSSProperties } from 'react';
|
||||
import cc from 'classcat';
|
||||
import { ConnectionLineType, PanOnScrollMode, SelectionMode, infiniteExtent, isMacOs } from '@xyflow/system';
|
||||
|
||||
@@ -11,6 +11,7 @@ import { GraphView } from '../GraphView';
|
||||
import { Wrapper } from './Wrapper';
|
||||
import type { Edge, Node, ReactFlowProps } from '../../types';
|
||||
import { defaultViewport as initViewport, defaultNodeOrigin } from './init-values';
|
||||
import { fixedForwardRef } from '../../utils/general';
|
||||
|
||||
const wrapperStyle: CSSProperties = {
|
||||
width: '100%',
|
||||
@@ -286,4 +287,4 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
|
||||
);
|
||||
}
|
||||
|
||||
export default forwardRef(ReactFlow);
|
||||
export default fixedForwardRef(ReactFlow);
|
||||
|
||||
@@ -16,14 +16,6 @@ import {
|
||||
|
||||
import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps, NodeProps } from '.';
|
||||
|
||||
// this is needed, to use generics + forwardRef
|
||||
declare module 'react' {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
function forwardRef<T, P = {}>(
|
||||
render: (props: P, ref: React.Ref<T>) => React.ReactElement | null
|
||||
): (props: P & React.RefAttributes<T>) => React.ReactElement | null;
|
||||
}
|
||||
|
||||
export type OnNodesChange<NodeType extends Node = Node> = (changes: NodeChange<NodeType>[]) => void;
|
||||
export type OnEdgesChange<EdgeType extends Edge = Edge> = (changes: EdgeChange<EdgeType>[]) => void;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { isNodeBase, isEdgeBase } from '@xyflow/system';
|
||||
|
||||
import type { Edge, Node } from '../types';
|
||||
import React from 'react';
|
||||
|
||||
/**
|
||||
* Test whether an object is useable as a Node
|
||||
@@ -21,3 +22,11 @@ export const isNode = <NodeType extends Node = Node>(element: unknown): element
|
||||
*/
|
||||
export const isEdge = <EdgeType extends Edge = Edge>(element: unknown): element is EdgeType =>
|
||||
isEdgeBase<EdgeType>(element);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export function fixedForwardRef<T, P = {}>(
|
||||
render: (props: P, ref: React.Ref<T>) => React.ReactNode
|
||||
): (props: P & React.RefAttributes<T>) => React.ReactNode {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
return React.forwardRef(render) as any;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @xyflow/svelte
|
||||
|
||||
## 0.0.39
|
||||
|
||||
## ⚠️ Breaking changes
|
||||
|
||||
- `on:nodedragstart`, `on:nodedrag` and `on:nodedragstop` are also getting fired if a selection bix is being dragged. We renamed the `node` param to `targetNode` and set it to `null` if a selection is being dragged.
|
||||
|
||||
|
||||
## Patch changes
|
||||
|
||||
- export `EdgeLabel` component
|
||||
- add "connectionindicator" class for `Handle` component
|
||||
|
||||
## 0.0.38
|
||||
|
||||
## ⚠️ Breaking changes
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
isMouseEvent,
|
||||
type HandleConnection,
|
||||
areConnectionMapsEqual,
|
||||
handleConnectionChange
|
||||
handleConnectionChange,
|
||||
ConnectionMode
|
||||
} from '@xyflow/system';
|
||||
|
||||
import { useStore } from '$lib/store';
|
||||
@@ -23,6 +24,7 @@
|
||||
export let isConnectable: $$Props['isConnectable'] = undefined;
|
||||
export let onconnect: $$Props['onconnect'] = undefined;
|
||||
export let ondisconnect: $$Props['ondisconnect'] = undefined;
|
||||
// @todo implement connectablestart, connectableend
|
||||
// export let isConnectableStart: $$Props['isConnectableStart'] = undefined;
|
||||
// export let isConnectableEnd: $$Props['isConnectableEnd'] = undefined;
|
||||
|
||||
@@ -32,7 +34,7 @@
|
||||
const isTarget = type === 'target';
|
||||
const nodeId = getContext<string>('svelteflow__node_id');
|
||||
const connectable = getContext<Writable<boolean>>('svelteflow__node_connectable');
|
||||
$: handleConnectable = isConnectable !== undefined ? isConnectable : $connectable;
|
||||
$: isConnectable = isConnectable !== undefined ? isConnectable : $connectable;
|
||||
|
||||
const handleId = id || null;
|
||||
|
||||
@@ -99,7 +101,8 @@
|
||||
onConnectEnd: (event) => {
|
||||
$onConnectEndAction?.(event);
|
||||
},
|
||||
getTransform: () => [$viewport.x, $viewport.y, $viewport.zoom]
|
||||
getTransform: () => [$viewport.x, $viewport.y, $viewport.zoom],
|
||||
getConnectionStartHandle: () => $connection.startHandle
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -124,6 +127,7 @@
|
||||
prevConnections = connections ?? new Map();
|
||||
}
|
||||
|
||||
$: connectionInProcess = !!$connection.startHandle;
|
||||
$: connectingFrom =
|
||||
$connection.startHandle?.nodeId === nodeId &&
|
||||
$connection.startHandle?.type === type &&
|
||||
@@ -132,9 +136,12 @@
|
||||
$connection.endHandle?.nodeId === nodeId &&
|
||||
$connection.endHandle?.type === type &&
|
||||
$connection.endHandle?.handleId === handleId;
|
||||
$: isPossibleEndHandle =
|
||||
$connectionMode === ConnectionMode.Strict
|
||||
? $connection.startHandle?.type !== type
|
||||
: nodeId !== $connection.startHandle?.nodeId ||
|
||||
handleId !== $connection.startHandle?.handleId;
|
||||
$: valid = connectingTo && $connection.status === 'valid';
|
||||
|
||||
// @todo implement connectablestart, connectableend
|
||||
</script>
|
||||
|
||||
<!--
|
||||
@@ -152,19 +159,17 @@ The Handle component is the part of a node that can be used to connect nodes.
|
||||
'nodrag',
|
||||
'nopan',
|
||||
position,
|
||||
{
|
||||
valid,
|
||||
connectingto: connectingTo,
|
||||
connectingfrom: connectingFrom
|
||||
},
|
||||
className
|
||||
])}
|
||||
class:valid
|
||||
class:connectingto={connectingTo}
|
||||
class:connectingfrom={connectingFrom}
|
||||
class:source={!isTarget}
|
||||
class:target={isTarget}
|
||||
class:connectablestart={handleConnectable}
|
||||
class:connectableend={handleConnectable}
|
||||
class:connectable={handleConnectable}
|
||||
class:connectionindicator={handleConnectable}
|
||||
class:connectablestart={isConnectable}
|
||||
class:connectableend={isConnectable}
|
||||
class:connectable={isConnectable}
|
||||
class:connectionindicator={isConnectable && (!connectionInProcess || isPossibleEndHandle)}
|
||||
on:mousedown={onPointerDown}
|
||||
on:touchstart={onPointerDown}
|
||||
{style}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { Selection } from '$lib/components/Selection';
|
||||
import drag from '$lib/actions/drag';
|
||||
import type { Node } from '$lib/types';
|
||||
import { createNodeEventDispatcher } from '$lib';
|
||||
|
||||
const store = useStore();
|
||||
const { selectionRectMode, nodes } = store;
|
||||
@@ -14,6 +15,7 @@
|
||||
selectioncontextmenu: { nodes: Node[]; event: MouseEvent | TouchEvent };
|
||||
selectionclick: { nodes: Node[]; event: MouseEvent | TouchEvent };
|
||||
}>();
|
||||
const dispatchNodeEvent = createNodeEventDispatcher();
|
||||
|
||||
$: selectedNodes = $nodes.filter((n) => n.selected);
|
||||
$: bounds = getNodesBounds(selectedNodes);
|
||||
@@ -31,7 +33,19 @@
|
||||
<div
|
||||
class="selection-wrapper nopan"
|
||||
style="width: {bounds.width}px; height: {bounds.height}px; transform: translate({bounds.x}px, {bounds.y}px)"
|
||||
use:drag={{ disabled: false, store }}
|
||||
use:drag={{
|
||||
disabled: false,
|
||||
store,
|
||||
onDrag: (event, _, __, nodes) => {
|
||||
dispatchNodeEvent('nodedrag', { event, targetNode: null, nodes });
|
||||
},
|
||||
onDragStart: (event, _, __, nodes) => {
|
||||
dispatchNodeEvent('nodedragstart', { event, targetNode: null, nodes });
|
||||
},
|
||||
onDragStop: (event, _, __, nodes) => {
|
||||
dispatchNodeEvent('nodedragstop', { event, targetNode: null, nodes });
|
||||
}
|
||||
}}
|
||||
on:contextmenu={onContextMenu}
|
||||
on:click={onClick}
|
||||
role="button"
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import type { NodeWrapperProps } from './types';
|
||||
import type { Node } from '$lib/types';
|
||||
import { getNodeInlineStyleDimensions } from './utils';
|
||||
import { createNodeEventDispatcher } from '$lib';
|
||||
|
||||
interface $$Props extends NodeWrapperProps {}
|
||||
|
||||
@@ -66,16 +67,7 @@
|
||||
}
|
||||
|
||||
const nodeComponent = $nodeTypes[nodeType] || DefaultNode;
|
||||
const dispatch = createEventDispatcher<{
|
||||
nodeclick: { node: Node; event: MouseEvent | TouchEvent };
|
||||
nodecontextmenu: { node: Node; event: MouseEvent | TouchEvent };
|
||||
nodedrag: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent };
|
||||
nodedragstart: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent };
|
||||
nodedragstop: { node: Node; nodes: Node[]; event: MouseEvent | TouchEvent };
|
||||
nodemouseenter: { node: Node; event: MouseEvent | TouchEvent };
|
||||
nodemouseleave: { node: Node; event: MouseEvent | TouchEvent };
|
||||
nodemousemove: { node: Node; event: MouseEvent | TouchEvent };
|
||||
}>();
|
||||
const dispatchNodeEvent = createNodeEventDispatcher();
|
||||
const connectableStore = writable(connectable);
|
||||
let prevType: string | undefined = undefined;
|
||||
let prevSourcePosition: Position | undefined = undefined;
|
||||
@@ -151,7 +143,7 @@
|
||||
handleNodeSelection(id);
|
||||
}
|
||||
|
||||
dispatch('nodeclick', { node, event });
|
||||
dispatchNodeEvent('nodeclick', { node, event });
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -166,14 +158,14 @@
|
||||
handleSelector: dragHandle,
|
||||
noDragClass: 'nodrag',
|
||||
onNodeMouseDown: handleNodeSelection,
|
||||
onDrag: (event, _, node, nodes) => {
|
||||
dispatch('nodedrag', { event, node, nodes });
|
||||
onDrag: (event, _, targetNode, nodes) => {
|
||||
dispatchNodeEvent('nodedrag', { event, targetNode, nodes });
|
||||
},
|
||||
onDragStart: (event, _, node, nodes) => {
|
||||
dispatch('nodedragstart', { event, node, nodes });
|
||||
onDragStart: (event, _, targetNode, nodes) => {
|
||||
dispatchNodeEvent('nodedragstart', { event, targetNode, nodes });
|
||||
},
|
||||
onDragStop: (event, _, node, nodes) => {
|
||||
dispatch('nodedragstop', { event, node, nodes });
|
||||
onDragStop: (event, _, targetNode, nodes) => {
|
||||
dispatchNodeEvent('nodedragstop', { event, targetNode, nodes });
|
||||
},
|
||||
store
|
||||
}}
|
||||
@@ -192,10 +184,10 @@
|
||||
style:visibility={initialized ? 'visible' : 'hidden'}
|
||||
style="{style ?? ''};{inlineStyleDimensions.width}{inlineStyleDimensions.height}"
|
||||
on:click={onSelectNodeHandler}
|
||||
on:mouseenter={(event) => dispatch('nodemouseenter', { node, event })}
|
||||
on:mouseleave={(event) => dispatch('nodemouseleave', { node, event })}
|
||||
on:mousemove={(event) => dispatch('nodemousemove', { node, event })}
|
||||
on:contextmenu={(event) => dispatch('nodecontextmenu', { node, event })}
|
||||
on:mouseenter={(event) => dispatchNodeEvent('nodemouseenter', { node, event })}
|
||||
on:mouseleave={(event) => dispatchNodeEvent('nodemouseleave', { node, event })}
|
||||
on:mousemove={(event) => dispatchNodeEvent('nodemousemove', { node, event })}
|
||||
on:contextmenu={(event) => dispatchNodeEvent('nodecontextmenu', { node, event })}
|
||||
>
|
||||
<svelte:component
|
||||
this={nodeComponent}
|
||||
|
||||
@@ -242,7 +242,13 @@
|
||||
on:nodedragstop
|
||||
on:nodecontextmenu
|
||||
/>
|
||||
<NodeSelection on:selectionclick on:selectioncontextmenu />
|
||||
<NodeSelection
|
||||
on:selectionclick
|
||||
on:selectioncontextmenu
|
||||
on:nodedragstart
|
||||
on:nodedrag
|
||||
on:nodedragstop
|
||||
/>
|
||||
</ViewportComponent>
|
||||
<UserSelection />
|
||||
</Pane>
|
||||
|
||||
@@ -10,6 +10,7 @@ export * from '$lib/components/ViewportPortal';
|
||||
export * from '$lib/components/BaseEdge';
|
||||
export { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '$lib/components/edges';
|
||||
export * from '$lib/components/Handle';
|
||||
export * from '$lib/components/EdgeLabel';
|
||||
|
||||
// plugins
|
||||
export * from '$lib/plugins/Controls';
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { isNodeBase, isEdgeBase } from '@xyflow/system';
|
||||
|
||||
import type { Edge, Node } from '$lib/types';
|
||||
@@ -21,3 +22,15 @@ export const isNode = <NodeType extends Node = Node>(element: unknown): element
|
||||
*/
|
||||
export const isEdge = <EdgeType extends Edge = Edge>(element: unknown): element is EdgeType =>
|
||||
isEdgeBase<EdgeType>(element);
|
||||
|
||||
export const createNodeEventDispatcher = () =>
|
||||
createEventDispatcher<{
|
||||
nodeclick: { node: Node; event: MouseEvent | TouchEvent };
|
||||
nodecontextmenu: { node: Node; event: MouseEvent | TouchEvent };
|
||||
nodedrag: { targetNode: Node | null; nodes: Node[]; event: MouseEvent | TouchEvent };
|
||||
nodedragstart: { targetNode: Node | null; nodes: Node[]; event: MouseEvent | TouchEvent };
|
||||
nodedragstop: { targetNode: Node | null; nodes: Node[]; event: MouseEvent | TouchEvent };
|
||||
nodemouseenter: { node: Node; event: MouseEvent | TouchEvent };
|
||||
nodemouseleave: { node: Node; event: MouseEvent | TouchEvent };
|
||||
nodemousemove: { node: Node; event: MouseEvent | TouchEvent };
|
||||
}>();
|
||||
|
||||
@@ -2,7 +2,11 @@ import { NodeBase } from '../types';
|
||||
|
||||
type NodeData = Pick<NodeBase, 'id' | 'type' | 'data'>;
|
||||
|
||||
export function shallowNodeData(a: NodeData | NodeData[], b: NodeData | NodeData[]) {
|
||||
export function shallowNodeData(a: NodeData | NodeData[] | null, b: NodeData | NodeData[] | null) {
|
||||
if (a === null || b === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const _a = Array.isArray(a) ? a : [a];
|
||||
const _b = Array.isArray(b) ? b : [b];
|
||||
|
||||
|
||||
@@ -38,6 +38,7 @@ export type OnPointerDownParams = {
|
||||
isValidConnection?: IsValidConnection;
|
||||
onEdgeUpdateEnd?: (evt: MouseEvent | TouchEvent) => void;
|
||||
getTransform: () => Transform;
|
||||
getConnectionStartHandle: () => ConnectingHandle | null;
|
||||
};
|
||||
|
||||
export type IsValidParams = {
|
||||
@@ -91,6 +92,7 @@ function onPointerDown(
|
||||
onEdgeUpdateEnd,
|
||||
updateConnection,
|
||||
getTransform,
|
||||
getConnectionStartHandle,
|
||||
}: OnPointerDownParams
|
||||
) {
|
||||
// when xyflow is used inside a shadow root we can't use document
|
||||
@@ -149,6 +151,10 @@ function onPointerDown(
|
||||
onConnectStart?.(event, { nodeId, handleId, handleType });
|
||||
|
||||
function onPointerMove(event: MouseEvent | TouchEvent) {
|
||||
if (!getConnectionStartHandle()) {
|
||||
onPointerUp(event);
|
||||
}
|
||||
|
||||
const transform = getTransform();
|
||||
connectionPosition = getEventPosition(event, containerBounds);
|
||||
closestHandle = getClosestHandle(
|
||||
|
||||
Reference in New Issue
Block a user