diff --git a/.changeset/twenty-ghosts-turn.md b/.changeset/twenty-ghosts-turn.md new file mode 100644 index 00000000..d236e126 --- /dev/null +++ b/.changeset/twenty-ghosts-turn.md @@ -0,0 +1,7 @@ +--- +'@xyflow/react': patch +'@xyflow/svelte': patch +'@xyflow/system': patch +--- + +add nodeClickDistance diff --git a/packages/react/src/components/NodeWrapper/index.tsx b/packages/react/src/components/NodeWrapper/index.tsx index 655233c9..adc6f393 100644 --- a/packages/react/src/components/NodeWrapper/index.tsx +++ b/packages/react/src/components/NodeWrapper/index.tsx @@ -39,6 +39,7 @@ export function NodeWrapper({ rfId, nodeTypes, nodeExtent, + nodeClickDistance, onError, }: NodeWrapperProps) { const { node, internals, isParent } = useStore((s) => { @@ -76,6 +77,7 @@ export function NodeWrapper({ handleSelector: node.dragHandle, nodeId: id, isSelectable, + nodeClickDistance, }); const moveSelectedNodes = useMoveSelectedNodes(); diff --git a/packages/react/src/container/FlowRenderer/index.tsx b/packages/react/src/container/FlowRenderer/index.tsx index 727970e3..f336967a 100644 --- a/packages/react/src/container/FlowRenderer/index.tsx +++ b/packages/react/src/container/FlowRenderer/index.tsx @@ -22,6 +22,7 @@ export type FlowRendererProps = Omit< | 'selectNodesOnDrag' | 'defaultMarkerColor' | 'rfId' + | 'nodeClickDistance' > & { isControlledViewport: boolean; children: ReactNode; diff --git a/packages/react/src/container/GraphView/index.tsx b/packages/react/src/container/GraphView/index.tsx index 792a570f..7356c8f4 100644 --- a/packages/react/src/container/GraphView/index.tsx +++ b/packages/react/src/container/GraphView/index.tsx @@ -33,6 +33,7 @@ export type GraphViewProps > & { rfId: string; @@ -86,6 +87,7 @@ function GraphViewComponent = Pick< | 'disableKeyboardA11y' | 'nodeExtent' | 'nodeTypes' + | 'nodeClickDistance' >; const selector = (s: ReactFlowState) => ({ @@ -86,6 +87,7 @@ function NodeRendererComponent(props: NodeRendererProps ); diff --git a/packages/react/src/container/ReactFlow/index.tsx b/packages/react/src/container/ReactFlow/index.tsx index a30442ee..d6e3e5a4 100644 --- a/packages/react/src/container/ReactFlow/index.tsx +++ b/packages/react/src/container/ReactFlow/index.tsx @@ -104,6 +104,7 @@ function ReactFlow( onPaneScroll, onPaneContextMenu, paneClickDistance = 0, + nodeClickDistance = 0, children, onReconnect, onReconnectStart, @@ -202,6 +203,7 @@ function ReactFlow( onPaneScroll={onPaneScroll} onPaneContextMenu={onPaneContextMenu} paneClickDistance={paneClickDistance} + nodeClickDistance={nodeClickDistance} onSelectionContextMenu={onSelectionContextMenu} onSelectionStart={onSelectionStart} onSelectionEnd={onSelectionEnd} diff --git a/packages/react/src/hooks/useDrag.ts b/packages/react/src/hooks/useDrag.ts index ee26cc57..f7943f98 100644 --- a/packages/react/src/hooks/useDrag.ts +++ b/packages/react/src/hooks/useDrag.ts @@ -11,6 +11,7 @@ type UseDragParams = { handleSelector?: string; nodeId?: string; isSelectable?: boolean; + nodeClickDistance?: number; }; /** @@ -25,6 +26,7 @@ export function useDrag({ handleSelector, nodeId, isSelectable, + nodeClickDistance, }: UseDragParams) { const store = useStoreApi(); const [dragging, setDragging] = useState(false); @@ -59,6 +61,7 @@ export function useDrag({ domNode: nodeRef.current, isSelectable, nodeId, + nodeClickDistance, }); return () => { xyDrag.current?.destroy(); diff --git a/packages/react/src/types/component-props.ts b/packages/react/src/types/component-props.ts index 29919931..623992b2 100644 --- a/packages/react/src/types/component-props.ts +++ b/packages/react/src/types/component-props.ts @@ -227,6 +227,10 @@ export interface ReactFlowProps; /** Custom node types to be available in a flow. diff --git a/packages/react/src/types/nodes.ts b/packages/react/src/types/nodes.ts index 71e03036..f4ad2820 100644 --- a/packages/react/src/types/nodes.ts +++ b/packages/react/src/types/nodes.ts @@ -53,6 +53,7 @@ export type NodeWrapperProps = { nodeTypes?: NodeTypes; nodeExtent?: CoordinateExtent; onError?: OnError; + nodeClickDistance?: number; }; export type BuiltInNode = Node<{ label: string }, 'input' | 'output' | 'default'>; diff --git a/packages/svelte/src/lib/actions/drag/index.ts b/packages/svelte/src/lib/actions/drag/index.ts index f5649a29..0d28f4e7 100644 --- a/packages/svelte/src/lib/actions/drag/index.ts +++ b/packages/svelte/src/lib/actions/drag/index.ts @@ -10,6 +10,7 @@ export type UseDragParams = { handleSelector?: string; nodeId?: string; isSelectable?: boolean; + nodeClickDistance?: number; onDrag?: OnDrag; onDragStart?: OnDrag; onDragStop?: OnDrag; @@ -60,7 +61,8 @@ export default function drag(domNode: Element, params: UseDragParams) { noDragClassName: params.noDragClass, handleSelector: params.handleSelector, nodeId: params.nodeId, - isSelectable: params.isSelectable + isSelectable: params.isSelectable, + nodeClickDistance: params.nodeClickDistance }); } diff --git a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte index 3c77ddef..dc571ec2 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte +++ b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte @@ -43,6 +43,7 @@ export let dragHandle: $$Props['dragHandle'] = undefined; export let initialized: $$Props['initialized'] = false; export let parentId: $$Props['parentId'] = undefined; + export let nodeClickDistance: $$Props['nodeClickDistance'] = undefined; let className: string = ''; export { className as class }; @@ -156,6 +157,7 @@ disabled: false, handleSelector: dragHandle, noDragClass: 'nodrag', + nodeClickDistance, onNodeMouseDown: handleNodeSelection, onDrag: (event, _, targetNode, nodes) => { dispatchNodeEvent('nodedrag', { event, targetNode, nodes }); diff --git a/packages/svelte/src/lib/components/NodeWrapper/types.ts b/packages/svelte/src/lib/components/NodeWrapper/types.ts index 11baf5b9..469d30f7 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/types.ts +++ b/packages/svelte/src/lib/components/NodeWrapper/types.ts @@ -34,4 +34,5 @@ export type NodeWrapperProps = Pick< zIndex: number; node: InternalNode; initialized: boolean; + nodeClickDistance?: number; }; diff --git a/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte b/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte index b20329b3..b54e15cb 100644 --- a/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte +++ b/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte @@ -4,6 +4,11 @@ import { NodeWrapper } from '$lib/components/NodeWrapper'; import { useStore } from '$lib/store'; + import type { NodeRendererProps } from './types'; + + type $$Props = NodeRendererProps; + + export let nodeClickDistance: $$Props['nodeClickDistance'] = 0; const { visibleNodes, @@ -76,6 +81,7 @@ measuredHeight={node.measured.height} parentId={node.parentId} {resizeObserver} + {nodeClickDistance} on:nodeclick on:nodemouseenter on:nodemousemove diff --git a/packages/svelte/src/lib/container/NodeRenderer/types.ts b/packages/svelte/src/lib/container/NodeRenderer/types.ts new file mode 100644 index 00000000..93c78afe --- /dev/null +++ b/packages/svelte/src/lib/container/NodeRenderer/types.ts @@ -0,0 +1,3 @@ +export type NodeRendererProps = { + nodeClickDistance?: number; +}; diff --git a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte index f19e003c..87f061d0 100644 --- a/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte +++ b/packages/svelte/src/lib/container/SvelteFlow/SvelteFlow.svelte @@ -80,6 +80,7 @@ export let oninit: $$Props['oninit'] = undefined; export let nodeOrigin: $$Props['nodeOrigin'] = undefined; export let paneClickDistance: $$Props['paneClickDistance'] = 0; + export let nodeClickDistance: $$Props['nodeClickDistance'] = 0; export let defaultMarkerColor = '#b1b1b7'; @@ -254,6 +255,7 @@
& { * @default 0 */ paneClickDistance?: number; + /** Distance that the mouse can move between mousedown/up that will trigger a click + * @default 0 + */ + nodeClickDistance?: number; /** Minimum zoom level * @default 0.5 */ diff --git a/packages/system/src/xydrag/XYDrag.ts b/packages/system/src/xydrag/XYDrag.ts index d03f729c..c4c5600f 100644 --- a/packages/system/src/xydrag/XYDrag.ts +++ b/packages/system/src/xydrag/XYDrag.ts @@ -84,6 +84,7 @@ export type DragUpdateParams = { isSelectable?: boolean; nodeId?: string; domNode: Element; + nodeClickDistance?: number; }; // eslint-disable-next-line @typescript-eslint/no-explicit-any @@ -105,7 +106,14 @@ export function XYDrag voi let abortDrag = false; // prevents unintentional dragging on multitouch // public functions - function update({ noDragClassName, handleSelector, domNode, isSelectable, nodeId }: DragUpdateParams) { + function update({ + noDragClassName, + handleSelector, + domNode, + isSelectable, + nodeId, + nodeClickDistance = 0, + }: DragUpdateParams) { d3Selection = select(domNode); function updateNodes({ x, y }: XYPosition, dragEvent: MouseEvent | null) { const { @@ -264,6 +272,7 @@ export function XYDrag voi } const d3DragInstance = drag() + .clickDistance(nodeClickDistance) .on('start', (event: UseDragEvent) => { const { domNode, nodeDragThreshold, transform, snapGrid, snapToGrid } = getStoreItems();