Merge pull request #4426 from xyflow/next

React Flow 12.0.0-next.28 and Svelte Flow 0.1.10
This commit is contained in:
Moritz Klack
2024-07-09 11:24:55 +02:00
committed by GitHub
27 changed files with 175 additions and 14 deletions
+6
View File
@@ -1,6 +1,7 @@
import Basic from '../examples/Basic'; import Basic from '../examples/Basic';
import Backgrounds from '../examples/Backgrounds'; import Backgrounds from '../examples/Backgrounds';
import ColorMode from '../examples/ColorMode'; import ColorMode from '../examples/ColorMode';
import ClickDistance from '../examples/ClickDistance';
import ControlledUncontrolled from '../examples/ControlledUncontrolled'; import ControlledUncontrolled from '../examples/ControlledUncontrolled';
import ControlledViewport from '../examples/ControlledViewport'; import ControlledViewport from '../examples/ControlledViewport';
import CustomConnectionLine from '../examples/CustomConnectionLine'; import CustomConnectionLine from '../examples/CustomConnectionLine';
@@ -85,6 +86,11 @@ const routes: IRoute[] = [
path: 'cancel-connection', path: 'cancel-connection',
component: CancelConnection, component: CancelConnection,
}, },
{
name: 'Click Distance',
path: 'click-distance',
component: ClickDistance,
},
{ {
name: 'Controlled/Uncontrolled', name: 'Controlled/Uncontrolled',
path: 'controlled-uncontrolled', path: 'controlled-uncontrolled',
@@ -0,0 +1,72 @@
import { useCallback, useState } from 'react';
import { ReactFlow, addEdge, Node, Connection, Edge, useNodesState, useEdgesState, Panel } from '@xyflow/react';
const initNodes: Node[] = [
{
id: '1a',
type: 'input',
data: { label: 'Node 1' },
position: { x: 250, y: 5 },
className: 'light',
ariaLabel: 'Input Node 1',
},
{
id: '2a',
data: { label: 'Node 2' },
position: { x: 100, y: 100 },
className: 'light',
ariaLabel: 'Default Node 2',
},
{
id: '3a',
data: { label: 'Node 3' },
position: { x: 400, y: 100 },
className: 'light',
},
{
id: '4a',
data: { label: 'Node 4' },
position: { x: 400, y: 200 },
className: 'light',
},
];
const initEdges: Edge[] = [
{ id: 'e1-2', source: '1a', target: '2a', ariaLabel: undefined },
{ id: 'e1-3', source: '1a', target: '3a' },
];
const onPaneClick = () => console.log('pane click');
const BasicFlow = () => {
const [nodes, setNodes, onNodesChange] = useNodesState(initNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initEdges);
const [paneClickDistance, setPaneClickDistance] = useState(0);
const onConnect = useCallback((params: Connection | Edge) => setEdges((eds) => addEdge(params, eds)), [setEdges]);
return (
<ReactFlow
nodes={nodes}
edges={edges}
onNodesChange={onNodesChange}
onEdgesChange={onEdgesChange}
onConnect={onConnect}
paneClickDistance={paneClickDistance}
onPaneClick={onPaneClick}
>
<Panel position="top-right">
<input
type="range"
min={0}
max={100}
value={paneClickDistance}
onChange={(evt) => setPaneClickDistance(+evt.target.value)}
/>
click distance: {paneClickDistance}
</Panel>
</ReactFlow>
);
};
export default BasicFlow;
+5
View File
@@ -1,5 +1,10 @@
# @xyflow/react # @xyflow/react
## 12.0.0-next.28
- add `paneDistanceClick` prop (max distance between mousedown/up that will trigger a click)
- returned nodes in `onNodeDragStop` are set to `dragging=false`
## 12.0.0-next.27 ## 12.0.0-next.27
- return Promises for `setViewport`, `fitView`, `fitBounds` and `zoomTo` to be able to await viewport update - return Promises for `setViewport`, `fitView`, `fitBounds` and `zoomTo` to be able to await viewport update
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@xyflow/react", "name": "@xyflow/react",
"version": "12.0.0-next.26", "version": "12.0.0-next.27",
"description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.", "description": "React Flow - A highly customizable React library for building node-based editors and interactive flow charts.",
"keywords": [ "keywords": [
"react", "react",
@@ -67,6 +67,7 @@ const reactFlowFieldsToTrack = [
'onBeforeDelete', 'onBeforeDelete',
'debug', 'debug',
'autoPanSpeed', 'autoPanSpeed',
'paneClickDistance',
] as const; ] as const;
type ReactFlowFieldsToTrack = (typeof reactFlowFieldsToTrack)[number]; type ReactFlowFieldsToTrack = (typeof reactFlowFieldsToTrack)[number];
@@ -89,6 +90,7 @@ const selector = (s: ReactFlowState) => ({
setNodeExtent: s.setNodeExtent, setNodeExtent: s.setNodeExtent,
reset: s.reset, reset: s.reset,
setDefaultNodesAndEdges: s.setDefaultNodesAndEdges, setDefaultNodesAndEdges: s.setDefaultNodesAndEdges,
setPaneClickDistance: s.setPaneClickDistance,
}); });
const initPrevValues = { const initPrevValues = {
@@ -102,6 +104,7 @@ const initPrevValues = {
elementsSelectable: true, elementsSelectable: true,
noPanClassName: 'nopan', noPanClassName: 'nopan',
rfId: '1', rfId: '1',
paneClickDistance: 0,
}; };
export function StoreUpdater<NodeType extends Node = Node, EdgeType extends Edge = Edge>( export function StoreUpdater<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
@@ -116,6 +119,7 @@ export function StoreUpdater<NodeType extends Node = Node, EdgeType extends Edge
setNodeExtent, setNodeExtent,
reset, reset,
setDefaultNodesAndEdges, setDefaultNodesAndEdges,
setPaneClickDistance,
} = useStore(selector, shallow); } = useStore(selector, shallow);
const store = useStoreApi<NodeType, EdgeType>(); const store = useStoreApi<NodeType, EdgeType>();
@@ -146,6 +150,7 @@ export function StoreUpdater<NodeType extends Node = Node, EdgeType extends Edge
else if (fieldName === 'maxZoom') setMaxZoom(fieldValue as number); else if (fieldName === 'maxZoom') setMaxZoom(fieldValue as number);
else if (fieldName === 'translateExtent') setTranslateExtent(fieldValue as CoordinateExtent); else if (fieldName === 'translateExtent') setTranslateExtent(fieldValue as CoordinateExtent);
else if (fieldName === 'nodeExtent') setNodeExtent(fieldValue as CoordinateExtent); else if (fieldName === 'nodeExtent') setNodeExtent(fieldValue as CoordinateExtent);
else if (fieldName === 'paneClickDistance') setPaneClickDistance(fieldValue as number);
// Renamed fields // Renamed fields
else if (fieldName === 'fitView') store.setState({ fitViewOnInit: fieldValue as boolean }); else if (fieldName === 'fitView') store.setState({ fitViewOnInit: fieldValue as boolean });
else if (fieldName === 'fitViewOptions') store.setState({ fitViewOnInitOptions: fieldValue as FitViewOptions }); else if (fieldName === 'fitViewOptions') store.setState({ fitViewOnInitOptions: fieldValue as FitViewOptions });
@@ -41,6 +41,7 @@ function FlowRendererComponent<NodeType extends Node = Node>({
onPaneMouseLeave, onPaneMouseLeave,
onPaneContextMenu, onPaneContextMenu,
onPaneScroll, onPaneScroll,
paneClickDistance,
deleteKeyCode, deleteKeyCode,
selectionKeyCode, selectionKeyCode,
selectionOnDrag, selectionOnDrag,
@@ -101,6 +102,7 @@ function FlowRendererComponent<NodeType extends Node = Node>({
noPanClassName={noPanClassName} noPanClassName={noPanClassName}
onViewportChange={onViewportChange} onViewportChange={onViewportChange}
isControlledViewport={isControlledViewport} isControlledViewport={isControlledViewport}
paneClickDistance={paneClickDistance}
> >
<Pane <Pane
onSelectionStart={onSelectionStart} onSelectionStart={onSelectionStart}
@@ -32,6 +32,7 @@ export type GraphViewProps<NodeType extends Node = Node, EdgeType extends Edge =
| 'noPanClassName' | 'noPanClassName'
| 'defaultViewport' | 'defaultViewport'
| 'disableKeyboardA11y' | 'disableKeyboardA11y'
| 'paneClickDistance'
> >
> & { > & {
rfId: string; rfId: string;
@@ -84,6 +85,7 @@ function GraphViewComponent<NodeType extends Node = Node, EdgeType extends Edge
onPaneMouseLeave, onPaneMouseLeave,
onPaneScroll, onPaneScroll,
onPaneContextMenu, onPaneContextMenu,
paneClickDistance,
onEdgeContextMenu, onEdgeContextMenu,
onEdgeMouseEnter, onEdgeMouseEnter,
onEdgeMouseMove, onEdgeMouseMove,
@@ -116,6 +118,7 @@ function GraphViewComponent<NodeType extends Node = Node, EdgeType extends Edge
onPaneMouseLeave={onPaneMouseLeave} onPaneMouseLeave={onPaneMouseLeave}
onPaneContextMenu={onPaneContextMenu} onPaneContextMenu={onPaneContextMenu}
onPaneScroll={onPaneScroll} onPaneScroll={onPaneScroll}
paneClickDistance={paneClickDistance}
deleteKeyCode={deleteKeyCode} deleteKeyCode={deleteKeyCode}
selectionKeyCode={selectionKeyCode} selectionKeyCode={selectionKeyCode}
selectionOnDrag={selectionOnDrag} selectionOnDrag={selectionOnDrag}
@@ -103,6 +103,7 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
onPaneMouseLeave, onPaneMouseLeave,
onPaneScroll, onPaneScroll,
onPaneContextMenu, onPaneContextMenu,
paneClickDistance = 0,
children, children,
onReconnect, onReconnect,
onReconnectStart, onReconnectStart,
@@ -200,6 +201,7 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
onPaneMouseLeave={onPaneMouseLeave} onPaneMouseLeave={onPaneMouseLeave}
onPaneScroll={onPaneScroll} onPaneScroll={onPaneScroll}
onPaneContextMenu={onPaneContextMenu} onPaneContextMenu={onPaneContextMenu}
paneClickDistance={paneClickDistance}
onSelectionContextMenu={onSelectionContextMenu} onSelectionContextMenu={onSelectionContextMenu}
onSelectionStart={onSelectionStart} onSelectionStart={onSelectionStart}
onSelectionEnd={onSelectionEnd} onSelectionEnd={onSelectionEnd}
@@ -277,6 +279,7 @@ function ReactFlow<NodeType extends Node = Node, EdgeType extends Edge = Edge>(
selectNodesOnDrag={selectNodesOnDrag} selectNodesOnDrag={selectNodesOnDrag}
nodeDragThreshold={nodeDragThreshold} nodeDragThreshold={nodeDragThreshold}
onBeforeDelete={onBeforeDelete} onBeforeDelete={onBeforeDelete}
paneClickDistance={paneClickDistance}
debug={debug} debug={debug}
/> />
<SelectionListener onSelectionChange={onSelectionChange} /> <SelectionListener onSelectionChange={onSelectionChange} />
@@ -47,6 +47,7 @@ export function ZoomPane({
noPanClassName, noPanClassName,
onViewportChange, onViewportChange,
isControlledViewport, isControlledViewport,
paneClickDistance,
}: ZoomPaneProps) { }: ZoomPaneProps) {
const store = useStoreApi(); const store = useStoreApi();
const zoomPane = useRef<HTMLDivElement>(null); const zoomPane = useRef<HTMLDivElement>(null);
@@ -64,6 +65,7 @@ export function ZoomPane({
maxZoom, maxZoom,
translateExtent, translateExtent,
viewport: defaultViewport, viewport: defaultViewport,
paneClickDistance,
onTransformChange: (transform: Transform) => { onTransformChange: (transform: Transform) => {
onViewportChange?.({ x: transform[0], y: transform[1], zoom: transform[2] }); onViewportChange?.({ x: transform[0], y: transform[1], zoom: transform[2] });
+3 -1
View File
@@ -134,7 +134,6 @@ const createStore = ({
const changes = []; const changes = [];
for (const [id, dragItem] of nodeDragItems) { for (const [id, dragItem] of nodeDragItems) {
// @todo add expandParent to drag item so that we can get rid of the look up here
const change: NodeChange = { const change: NodeChange = {
id, id,
type: 'position', type: 'position',
@@ -255,6 +254,9 @@ const createStore = ({
set({ translateExtent }); set({ translateExtent });
}, },
setPaneClickDistance: (clickDistance) => {
get().panZoom?.setClickDistance(clickDistance);
},
resetSelectedElements: () => { resetSelectedElements: () => {
const { edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get(); const { edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
@@ -223,6 +223,10 @@ export interface ReactFlowProps<NodeType extends Node = Node, EdgeType extends E
onPaneMouseMove?: (event: ReactMouseEvent) => void; onPaneMouseMove?: (event: ReactMouseEvent) => void;
/** This event handler gets called when mouse leaves the pane */ /** This event handler gets called when mouse leaves the pane */
onPaneMouseLeave?: (event: ReactMouseEvent) => void; onPaneMouseLeave?: (event: ReactMouseEvent) => void;
/** Distance that the mouse can move between mousedown/up that will trigger a click
* @default 0
*/
paneClickDistance?: number;
/** This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. */ /** This handler gets called before the user deletes nodes or edges and provides a way to abort the deletion by returning false. */
onBeforeDelete?: OnBeforeDelete<NodeType, EdgeType>; onBeforeDelete?: OnBeforeDelete<NodeType, EdgeType>;
/** Custom node types to be available in a flow. /** Custom node types to be available in a flow.
+1
View File
@@ -170,6 +170,7 @@ export type ReactFlowActions<NodeType extends Node, EdgeType extends Edge> = {
panBy: PanBy; panBy: PanBy;
fitView: (options?: FitViewOptions) => Promise<boolean>; fitView: (options?: FitViewOptions) => Promise<boolean>;
fitViewSync: (options?: FitViewOptions) => boolean; fitViewSync: (options?: FitViewOptions) => boolean;
setPaneClickDistance: (distance: number) => void;
}; };
export type ReactFlowState<NodeType extends Node = Node, EdgeType extends Edge = Edge> = ReactFlowStore< export type ReactFlowState<NodeType extends Node = Node, EdgeType extends Edge = Edge> = ReactFlowStore<
+5
View File
@@ -1,5 +1,10 @@
# @xyflow/svelte # @xyflow/svelte
## 0.1.10
- add `paneDistanceClick` prop (max distance between mousedown/up that will trigger a click)
- returned nodes in `on:nodedragstop` are set to `dragging=false`
## 0.1.9 ## 0.1.9
- return Promises for `setViewport`, `fitView`, `fitBounds` and `zoomTo` to be able to await viewport update - return Promises for `setViewport`, `fitView`, `fitBounds` and `zoomTo` to be able to await viewport update
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@xyflow/svelte", "name": "@xyflow/svelte",
"version": "0.1.8", "version": "0.1.9",
"description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.", "description": "Svelte Flow - A highly customizable Svelte library for building node-based editors, workflow systems, diagrams and more.",
"keywords": [ "keywords": [
"svelte", "svelte",
+12 -2
View File
@@ -35,11 +35,20 @@ type ZoomParams = {
noWheelClassName: string; noWheelClassName: string;
userSelectionActive: boolean; userSelectionActive: boolean;
lib: string; lib: string;
paneClickDistance: number;
}; };
export default function zoom(domNode: Element, params: ZoomParams) { export default function zoom(domNode: Element, params: ZoomParams) {
const { panZoom, minZoom, maxZoom, initialViewport, viewport, dragging, translateExtent } = const {
params; panZoom,
minZoom,
maxZoom,
initialViewport,
viewport,
dragging,
translateExtent,
paneClickDistance
} = params;
const panZoomInstance = XYPanZoom({ const panZoomInstance = XYPanZoom({
domNode, domNode,
@@ -47,6 +56,7 @@ export default function zoom(domNode: Element, params: ZoomParams) {
maxZoom, maxZoom,
translateExtent, translateExtent,
viewport: initialViewport, viewport: initialViewport,
paneClickDistance,
onTransformChange: (transform) => onTransformChange: (transform) =>
viewport.set({ x: transform[0], y: transform[1], zoom: transform[2] }), viewport.set({ x: transform[0], y: transform[1], zoom: transform[2] }),
onDraggingChange: dragging.set onDraggingChange: dragging.set
@@ -79,6 +79,7 @@
export let onbeforedelete: $$Props['onbeforedelete'] = undefined; export let onbeforedelete: $$Props['onbeforedelete'] = undefined;
export let oninit: $$Props['oninit'] = undefined; export let oninit: $$Props['oninit'] = undefined;
export let nodeOrigin: $$Props['nodeOrigin'] = undefined; export let nodeOrigin: $$Props['nodeOrigin'] = undefined;
export let paneClickDistance: $$Props['paneClickDistance'] = 0;
export let defaultMarkerColor = '#b1b1b7'; export let defaultMarkerColor = '#b1b1b7';
@@ -125,7 +126,8 @@
edgeTypes, edgeTypes,
minZoom, minZoom,
maxZoom, maxZoom,
translateExtent translateExtent,
paneClickDistance
}); });
return () => { return () => {
@@ -188,7 +190,8 @@
edgeTypes, edgeTypes,
minZoom, minZoom,
maxZoom, maxZoom,
translateExtent translateExtent,
paneClickDistance
}); });
$: colorModeClass = useColorModeClass(colorMode); $: colorModeClass = useColorModeClass(colorMode);
@@ -225,6 +228,7 @@
zoomOnPinch={zoomOnPinch === undefined ? true : zoomOnPinch} zoomOnPinch={zoomOnPinch === undefined ? true : zoomOnPinch}
panOnScroll={panOnScroll === undefined ? false : panOnScroll} panOnScroll={panOnScroll === undefined ? false : panOnScroll}
panOnDrag={panOnDrag === undefined ? true : panOnDrag} panOnDrag={panOnDrag === undefined ? true : panOnDrag}
paneClickDistance={paneClickDistance === undefined ? 0 : paneClickDistance}
> >
<Pane <Pane
on:paneclick on:paneclick
@@ -134,6 +134,10 @@ export type SvelteFlowProps = DOMAttributes<HTMLDivElement> & {
* @default 1 * @default 1
*/ */
nodeDragThreshold?: number; nodeDragThreshold?: number;
/** Distance that the mouse can move between mousedown/up that will trigger a click
* @default 0
*/
paneClickDistance?: number;
/** Minimum zoom level /** Minimum zoom level
* @default 0.5 * @default 0.5
*/ */
@@ -1,7 +1,8 @@
import type { Writable } from 'svelte/store';
import type { CoordinateExtent } from '@xyflow/system';
import type { SvelteFlowStore } from '$lib/store/types'; import type { SvelteFlowStore } from '$lib/store/types';
import type { EdgeTypes, NodeTypes } from '$lib/types'; import type { EdgeTypes, NodeTypes } from '$lib/types';
import type { CoordinateExtent } from '@xyflow/system';
import type { Writable } from 'svelte/store';
// this is helper function for updating the store // this is helper function for updating the store
// for props where we need to call a specific store action // for props where we need to call a specific store action
@@ -12,13 +13,15 @@ export function updateStore(
edgeTypes, edgeTypes,
minZoom, minZoom,
maxZoom, maxZoom,
translateExtent translateExtent,
paneClickDistance
}: { }: {
nodeTypes?: NodeTypes; nodeTypes?: NodeTypes;
edgeTypes?: EdgeTypes; edgeTypes?: EdgeTypes;
minZoom?: number; minZoom?: number;
maxZoom?: number; maxZoom?: number;
translateExtent?: CoordinateExtent; translateExtent?: CoordinateExtent;
paneClickDistance?: number;
} }
) { ) {
if (nodeTypes !== undefined) { if (nodeTypes !== undefined) {
@@ -40,6 +43,10 @@ export function updateStore(
if (translateExtent !== undefined) { if (translateExtent !== undefined) {
store.setTranslateExtent(translateExtent); store.setTranslateExtent(translateExtent);
} }
if (paneClickDistance !== undefined) {
store.setPaneClickDistance(paneClickDistance);
}
} }
const getKeys = <T extends object>(obj: T) => Object.keys(obj) as Array<keyof T>; const getKeys = <T extends object>(obj: T) => Object.keys(obj) as Array<keyof T>;
@@ -19,6 +19,7 @@
export let zoomOnPinch: $$Props['zoomOnPinch']; export let zoomOnPinch: $$Props['zoomOnPinch'];
export let panOnDrag: $$Props['panOnDrag']; export let panOnDrag: $$Props['panOnDrag'];
export let panOnScroll: $$Props['panOnScroll']; export let panOnScroll: $$Props['panOnScroll'];
export let paneClickDistance: $$Props['paneClickDistance'];
const { const {
viewport, viewport,
@@ -68,7 +69,8 @@
noWheelClassName: 'nowheel', noWheelClassName: 'nowheel',
userSelectionActive: !!$selectionRect, userSelectionActive: !!$selectionRect,
translateExtent: $translateExtent, translateExtent: $translateExtent,
lib: $lib lib: $lib,
paneClickDistance
}} }}
> >
<slot /> <slot />
@@ -12,4 +12,5 @@ export type ZoomProps = {
zoomOnPinch: boolean; zoomOnPinch: boolean;
panOnScroll: boolean; panOnScroll: boolean;
panOnDrag: boolean | number[]; panOnDrag: boolean | number[];
paneClickDistance: number;
}; };
+5
View File
@@ -248,6 +248,10 @@ export function createStore({
return elementsChanged; return elementsChanged;
} }
function setPaneClickDistance(distance: number) {
get(store.panZoom)?.setClickDistance(distance);
}
function unselectNodesAndEdges(params?: { nodes?: Node[]; edges?: Edge[] }) { function unselectNodesAndEdges(params?: { nodes?: Node[]; edges?: Edge[] }) {
const resetNodes = resetSelectedElements(params?.nodes || get(store.nodes)); const resetNodes = resetSelectedElements(params?.nodes || get(store.nodes));
if (resetNodes) store.nodes.set(get(store.nodes)); if (resetNodes) store.nodes.set(get(store.nodes));
@@ -448,6 +452,7 @@ export function createStore({
setMinZoom, setMinZoom,
setMaxZoom, setMaxZoom,
setTranslateExtent, setTranslateExtent,
setPaneClickDistance,
unselectNodesAndEdges, unselectNodesAndEdges,
addSelectedNodes, addSelectedNodes,
addSelectedEdges, addSelectedEdges,
+1
View File
@@ -25,6 +25,7 @@ export type SvelteFlowStoreActions = {
setMinZoom: (minZoom: number) => void; setMinZoom: (minZoom: number) => void;
setMaxZoom: (maxZoom: number) => void; setMaxZoom: (maxZoom: number) => void;
setTranslateExtent: (extent: CoordinateExtent) => void; setTranslateExtent: (extent: CoordinateExtent) => void;
setPaneClickDistance: (distance: number) => void;
fitView: (options?: FitViewOptions) => Promise<boolean>; fitView: (options?: FitViewOptions) => Promise<boolean>;
updateNodePositions: UpdateNodePositions; updateNodePositions: UpdateNodePositions;
updateNodeInternals: (updates: Map<string, InternalNodeUpdate>) => void; updateNodeInternals: (updates: Map<string, InternalNodeUpdate>) => void;
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@xyflow/system", "name": "@xyflow/system",
"version": "0.0.32", "version": "0.0.33",
"description": "xyflow core system that powers React Flow and Svelte Flow.", "description": "xyflow core system that powers React Flow and Svelte Flow.",
"keywords": [ "keywords": [
"node-based UI", "node-based UI",
+2
View File
@@ -9,6 +9,7 @@ export type PanZoomParams = {
domNode: Element; domNode: Element;
minZoom: number; minZoom: number;
maxZoom: number; maxZoom: number;
paneClickDistance: number;
viewport: Viewport; viewport: Viewport;
translateExtent: CoordinateExtent; translateExtent: CoordinateExtent;
onTransformChange: OnTransformChange; onTransformChange: OnTransformChange;
@@ -56,4 +57,5 @@ export type PanZoomInstance = {
scaleTo: (scale: number, options?: PanZoomTransformOptions) => Promise<boolean>; scaleTo: (scale: number, options?: PanZoomTransformOptions) => Promise<boolean>;
scaleBy: (factor: number, options?: PanZoomTransformOptions) => Promise<boolean>; scaleBy: (factor: number, options?: PanZoomTransformOptions) => Promise<boolean>;
syncViewport: (viewport: Viewport) => void; syncViewport: (viewport: Viewport) => void;
setClickDistance: (distance: number) => void;
}; };
+1
View File
@@ -332,6 +332,7 @@ export function XYDrag<OnNodeDrag extends (e: any, nodes: any, node: any) => voi
nodeId, nodeId,
dragItems, dragItems,
nodeLookup, nodeLookup,
dragging: false,
}); });
onDragStop?.(event.sourceEvent as MouseEvent, dragItems, currentNode, currentNodes); onDragStop?.(event.sourceEvent as MouseEvent, dragItems, currentNode, currentNodes);
+4
View File
@@ -81,10 +81,12 @@ export function getEventHandlerParams<NodeType extends InternalNodeBase>({
nodeId, nodeId,
dragItems, dragItems,
nodeLookup, nodeLookup,
dragging = true,
}: { }: {
nodeId?: string; nodeId?: string;
dragItems: Map<string, NodeDragItem>; dragItems: Map<string, NodeDragItem>;
nodeLookup: Map<string, NodeType>; nodeLookup: Map<string, NodeType>;
dragging?: boolean;
}): [NodeBase, NodeBase[]] { }): [NodeBase, NodeBase[]] {
const nodesFromDragItems: NodeBase[] = []; const nodesFromDragItems: NodeBase[] = [];
@@ -95,6 +97,7 @@ export function getEventHandlerParams<NodeType extends InternalNodeBase>({
nodesFromDragItems.push({ nodesFromDragItems.push({
...node, ...node,
position: dragItem.position, position: dragItem.position,
dragging,
}); });
} }
} }
@@ -109,6 +112,7 @@ export function getEventHandlerParams<NodeType extends InternalNodeBase>({
{ {
...node, ...node,
position: dragItems.get(nodeId)?.position || node.position, position: dragItems.get(nodeId)?.position || node.position,
dragging,
}, },
nodesFromDragItems, nodesFromDragItems,
]; ];
+12 -2
View File
@@ -9,7 +9,7 @@ import {
PanZoomParams, PanZoomParams,
PanZoomInstance, PanZoomInstance,
} from '../types'; } from '../types';
import { clamp } from '../utils'; import { clamp, isNumeric } from '../utils';
import { getD3Transition, viewportToTransform, wheelDelta } from './utils'; import { getD3Transition, viewportToTransform, wheelDelta } from './utils';
import { import {
createPanOnScrollHandler, createPanOnScrollHandler,
@@ -34,6 +34,7 @@ export function XYPanZoom({
domNode, domNode,
minZoom, minZoom,
maxZoom, maxZoom,
paneClickDistance,
translateExtent, translateExtent,
viewport, viewport,
onPanZoom, onPanZoom,
@@ -52,7 +53,10 @@ export function XYPanZoom({
isPanScrolling: false, isPanScrolling: false,
}; };
const bbox = domNode.getBoundingClientRect(); const bbox = domNode.getBoundingClientRect();
const d3ZoomInstance = zoom().scaleExtent([minZoom, maxZoom]).translateExtent(translateExtent); const d3ZoomInstance = zoom()
.clickDistance(!isNumeric(paneClickDistance) || paneClickDistance < 0 ? 0 : paneClickDistance)
.scaleExtent([minZoom, maxZoom])
.translateExtent(translateExtent);
const d3Selection = select(domNode).call(d3ZoomInstance); const d3Selection = select(domNode).call(d3ZoomInstance);
setViewportConstrained( setViewportConstrained(
@@ -267,6 +271,11 @@ export function XYPanZoom({
d3ZoomInstance?.translateExtent(translateExtent); d3ZoomInstance?.translateExtent(translateExtent);
} }
function setClickDistance(distance: number) {
const validDistance = !isNumeric(distance) || distance < 0 ? 0 : distance;
d3ZoomInstance?.clickDistance(validDistance);
}
return { return {
update, update,
destroy, destroy,
@@ -278,5 +287,6 @@ export function XYPanZoom({
setScaleExtent, setScaleExtent,
setTranslateExtent, setTranslateExtent,
syncViewport, syncViewport,
setClickDistance,
}; };
} }