fix(move-events): fire for panOnScroll
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-router-dom": "^6.3.0",
|
||||
"zustand": "^4.3.1"
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cypress/skip-test": "^2.6.1",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactFlow, Background, BackgroundVariant, Node, Edge, SelectionMode } from '@xyflow/react';
|
||||
import { ReactFlow, Background, BackgroundVariant, Node, Edge, SelectionMode, Controls, Panel } from '@xyflow/react';
|
||||
|
||||
const MULTI_SELECT_KEY = ['Meta', 'Shift'];
|
||||
|
||||
@@ -21,6 +21,10 @@ const onPaneContextMenu = (e: any) => {
|
||||
|
||||
const panOnDrag = [1, 2];
|
||||
|
||||
const onMoveStart = (e: any) => console.log('move start', e);
|
||||
const onMove = (e: any) => console.log('move', e);
|
||||
const onMoveEnd = (e: any) => console.log('move end', e);
|
||||
|
||||
const BasicFlow = () => {
|
||||
return (
|
||||
<ReactFlow
|
||||
@@ -36,11 +40,15 @@ const BasicFlow = () => {
|
||||
fitView
|
||||
selectNodesOnDrag={false}
|
||||
onSelectionContextMenu={onPaneContextMenu}
|
||||
onMoveStart={onMoveStart}
|
||||
onMove={onMove}
|
||||
onMoveEnd={onMoveEnd}
|
||||
>
|
||||
<Background variant={BackgroundVariant.Cross} />
|
||||
<div style={{ position: 'absolute', right: 10, top: 10, zIndex: 4 }}>
|
||||
<Controls />
|
||||
<Panel position="top-right">
|
||||
<input type={'text'} placeholder={'name'} />
|
||||
</div>
|
||||
</Panel>
|
||||
</ReactFlow>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -26,6 +26,8 @@ export type ZoomPanValues = {
|
||||
prevViewport: Viewport;
|
||||
mouseButton: number;
|
||||
timerId: ReturnType<typeof setTimeout> | undefined;
|
||||
panScrollTimeout: ReturnType<typeof setTimeout> | undefined;
|
||||
isPanScrolling: boolean;
|
||||
};
|
||||
|
||||
export function XYPanZoom({
|
||||
@@ -46,6 +48,8 @@ export function XYPanZoom({
|
||||
prevViewport: { x: 0, y: 0, zoom: 0 },
|
||||
mouseButton: 0,
|
||||
timerId: undefined,
|
||||
panScrollTimeout: undefined,
|
||||
isPanScrolling: false,
|
||||
};
|
||||
const bbox = domNode.getBoundingClientRect();
|
||||
const d3ZoomInstance = zoom().scaleExtent([minZoom, maxZoom]).translateExtent(translateExtent);
|
||||
@@ -98,12 +102,16 @@ export function XYPanZoom({
|
||||
|
||||
const wheelHandler = isPanOnScroll
|
||||
? createPanOnScrollHandler({
|
||||
zoomPanValues,
|
||||
noWheelClassName,
|
||||
d3Selection,
|
||||
d3Zoom: d3ZoomInstance,
|
||||
panOnScrollMode,
|
||||
panOnScrollSpeed,
|
||||
zoomOnPinch,
|
||||
onPanZoomStart,
|
||||
onPanZoom,
|
||||
onPanZoomEnd,
|
||||
})
|
||||
: createZoomOnScrollHandler({
|
||||
noWheelClassName,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/ban-ts-comment */
|
||||
import type { D3ZoomEvent } from 'd3-zoom';
|
||||
import { pointer } from 'd3-selection';
|
||||
@@ -8,28 +9,24 @@ import {
|
||||
type D3ZoomHandler,
|
||||
type D3ZoomInstance,
|
||||
type OnPanZoom,
|
||||
type Viewport,
|
||||
type OnDraggingChange,
|
||||
type OnTransformChange,
|
||||
} from '../types';
|
||||
import { isRightClickPan, isWrappedWithClass, transformToViewport, viewChanged, wheelDelta } from './utils';
|
||||
import { clamp, isMacOs } from '../utils';
|
||||
|
||||
export type ZoomPanValues = {
|
||||
isZoomingOrPanning: boolean;
|
||||
usedRightMouseButton: boolean;
|
||||
prevViewport: Viewport;
|
||||
mouseButton: number;
|
||||
timerId: ReturnType<typeof setTimeout> | undefined;
|
||||
};
|
||||
import { isMacOs } from '../utils';
|
||||
import { ZoomPanValues } from './XYPanZoom';
|
||||
|
||||
export type PanOnScrollParams = {
|
||||
zoomPanValues: ZoomPanValues;
|
||||
noWheelClassName: string;
|
||||
d3Selection: D3SelectionInstance;
|
||||
d3Zoom: D3ZoomInstance;
|
||||
panOnScrollMode: PanOnScrollMode;
|
||||
panOnScrollSpeed: number;
|
||||
zoomOnPinch: boolean;
|
||||
onPanZoomStart?: OnPanZoom;
|
||||
onPanZoom?: OnPanZoom;
|
||||
onPanZoomEnd?: OnPanZoom;
|
||||
};
|
||||
|
||||
export type ZoomOnScrollParams = {
|
||||
@@ -62,12 +59,16 @@ export type PanZoomEndParams = {
|
||||
};
|
||||
|
||||
export function createPanOnScrollHandler({
|
||||
zoomPanValues,
|
||||
noWheelClassName,
|
||||
d3Selection,
|
||||
d3Zoom,
|
||||
panOnScrollMode,
|
||||
panOnScrollSpeed,
|
||||
zoomOnPinch,
|
||||
onPanZoomStart,
|
||||
onPanZoom,
|
||||
onPanZoomEnd,
|
||||
}: PanOnScrollParams) {
|
||||
return (event: any) => {
|
||||
if (isWrappedWithClass(event, noWheelClassName)) {
|
||||
@@ -105,8 +106,33 @@ export function createPanOnScrollHandler({
|
||||
d3Zoom.translateBy(
|
||||
d3Selection,
|
||||
-(deltaX / currentZoom) * panOnScrollSpeed,
|
||||
-(deltaY / currentZoom) * panOnScrollSpeed
|
||||
-(deltaY / currentZoom) * panOnScrollSpeed,
|
||||
// @ts-ignore
|
||||
{ internal: true }
|
||||
);
|
||||
|
||||
const nextViewport = transformToViewport(d3Selection.property('__zoom'));
|
||||
|
||||
clearTimeout(zoomPanValues.panScrollTimeout);
|
||||
|
||||
// for pan on scroll we need to handle the event calls on our own
|
||||
// we can't use the start, zoom and end events from d3-zoom
|
||||
// because start and move gets called on every scroll event and not once at the beginning
|
||||
if (!zoomPanValues.isPanScrolling) {
|
||||
zoomPanValues.isPanScrolling = true;
|
||||
|
||||
onPanZoomStart?.(event, nextViewport);
|
||||
}
|
||||
|
||||
if (zoomPanValues.isPanScrolling) {
|
||||
onPanZoom?.(event, nextViewport);
|
||||
|
||||
zoomPanValues.panScrollTimeout = setTimeout(() => {
|
||||
onPanZoomEnd?.(event, nextViewport);
|
||||
|
||||
zoomPanValues.isPanScrolling = false;
|
||||
}, 150);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -124,6 +150,10 @@ export function createZoomOnScrollHandler({ noWheelClassName, preventScrolling,
|
||||
|
||||
export function createPanZoomStartHandler({ zoomPanValues, onDraggingChange, onPanZoomStart }: PanZoomStartParams) {
|
||||
return (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
if (event.sourceEvent?.internal) {
|
||||
return;
|
||||
}
|
||||
|
||||
const viewport = transformToViewport(event.transform);
|
||||
|
||||
// we need to remember it here, because it's always 0 in the "zoom" event
|
||||
@@ -155,7 +185,7 @@ export function createPanZoomHandler({
|
||||
|
||||
onTransformChange([event.transform.x, event.transform.y, event.transform.k]);
|
||||
|
||||
if (onPanZoom) {
|
||||
if (onPanZoom && !event.sourceEvent?.internal) {
|
||||
onPanZoom?.(event.sourceEvent as MouseEvent | TouchEvent, transformToViewport(event.transform));
|
||||
}
|
||||
};
|
||||
@@ -170,6 +200,9 @@ export function createPanZoomEndHandler({
|
||||
onPaneContextMenu,
|
||||
}: PanZoomEndParams) {
|
||||
return (event: D3ZoomEvent<HTMLDivElement, any>) => {
|
||||
if (event.sourceEvent?.internal) {
|
||||
return;
|
||||
}
|
||||
zoomPanValues.isZoomingOrPanning = false;
|
||||
|
||||
if (
|
||||
|
||||
60
pnpm-lock.yaml
generated
60
pnpm-lock.yaml
generated
@@ -99,8 +99,8 @@ importers:
|
||||
specifier: ^6.3.0
|
||||
version: registry.npmjs.org/react-router-dom@6.12.1(react-dom@18.2.0)(react@18.2.0)
|
||||
zustand:
|
||||
specifier: ^4.3.1
|
||||
version: registry.npmjs.org/zustand@4.3.8(react@18.2.0)
|
||||
specifier: ^4.4.1
|
||||
version: registry.npmjs.org/zustand@4.4.1(@types/react@18.2.12)(react@18.2.0)
|
||||
devDependencies:
|
||||
'@cypress/skip-test':
|
||||
specifier: ^2.6.1
|
||||
@@ -2421,7 +2421,7 @@ packages:
|
||||
'@typescript-eslint/typescript-estree': registry.npmjs.org/@typescript-eslint/typescript-estree@5.60.0(typescript@5.1.3)
|
||||
eslint: registry.npmjs.org/eslint@8.43.0
|
||||
eslint-scope: registry.npmjs.org/eslint-scope@5.1.1
|
||||
semver: registry.npmjs.org/semver@7.5.1
|
||||
semver: registry.npmjs.org/semver@7.5.4
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
@@ -4140,7 +4140,7 @@ packages:
|
||||
dependencies:
|
||||
acorn: registry.npmjs.org/acorn@8.8.2
|
||||
acorn-jsx: registry.npmjs.org/acorn-jsx@5.3.2(acorn@8.8.2)
|
||||
eslint-visitor-keys: registry.npmjs.org/eslint-visitor-keys@3.4.1
|
||||
eslint-visitor-keys: registry.npmjs.org/eslint-visitor-keys@3.4.3
|
||||
dev: true
|
||||
|
||||
registry.npmjs.org/esprima@4.0.1:
|
||||
@@ -5076,14 +5076,6 @@ packages:
|
||||
ci-info: registry.npmjs.org/ci-info@3.8.0
|
||||
dev: true
|
||||
|
||||
registry.npmjs.org/is-core-module@2.12.1:
|
||||
resolution: {integrity: sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/is-core-module/-/is-core-module-2.12.1.tgz}
|
||||
name: is-core-module
|
||||
version: 2.12.1
|
||||
dependencies:
|
||||
has: registry.npmjs.org/has@1.0.3
|
||||
dev: true
|
||||
|
||||
registry.npmjs.org/is-core-module@2.13.0:
|
||||
resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz}
|
||||
name: is-core-module
|
||||
@@ -6772,7 +6764,7 @@ packages:
|
||||
version: 1.22.2
|
||||
hasBin: true
|
||||
dependencies:
|
||||
is-core-module: registry.npmjs.org/is-core-module@2.12.1
|
||||
is-core-module: registry.npmjs.org/is-core-module@2.13.0
|
||||
path-parse: registry.npmjs.org/path-parse@1.0.7
|
||||
supports-preserve-symlinks-flag: registry.npmjs.org/supports-preserve-symlinks-flag@1.0.0
|
||||
dev: true
|
||||
@@ -8338,25 +8330,6 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
dev: true
|
||||
|
||||
registry.npmjs.org/zustand@4.3.8(react@18.2.0):
|
||||
resolution: {integrity: sha512-4h28KCkHg5ii/wcFFJ5Fp+k1J3gJoasaIbppdgZFO4BPJnsNxL0mQXBSFgOgAdCdBj35aDTPvdAJReTMntFPGg==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/zustand/-/zustand-4.3.8.tgz}
|
||||
id: registry.npmjs.org/zustand/4.3.8
|
||||
name: zustand
|
||||
version: 4.3.8
|
||||
engines: {node: '>=12.7.0'}
|
||||
peerDependencies:
|
||||
immer: '>=9.0'
|
||||
react: '>=16.8'
|
||||
peerDependenciesMeta:
|
||||
immer:
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
react: registry.npmjs.org/react@18.2.0
|
||||
use-sync-external-store: registry.npmjs.org/use-sync-external-store@1.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/zustand@4.4.0(@types/react@18.2.12)(react@18.2.0):
|
||||
resolution: {integrity: sha512-2dq6wq4dSxbiPTamGar0NlIG/av0wpyWZJGeQYtUOLegIUvhM2Bf86ekPlmgpUtS5uR7HyetSiktYrGsdsyZgQ==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/zustand/-/zustand-4.4.0.tgz}
|
||||
id: registry.npmjs.org/zustand/4.4.0
|
||||
@@ -8379,3 +8352,26 @@ packages:
|
||||
react: registry.npmjs.org/react@18.2.0
|
||||
use-sync-external-store: registry.npmjs.org/use-sync-external-store@1.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
registry.npmjs.org/zustand@4.4.1(@types/react@18.2.12)(react@18.2.0):
|
||||
resolution: {integrity: sha512-QCPfstAS4EBiTQzlaGP1gmorkh/UL1Leaj2tdj+zZCZ/9bm0WS7sI2wnfD5lpOszFqWJ1DcPnGoY8RDL61uokw==, registry: https://registry.npmjs.com/, tarball: https://registry.npmjs.org/zustand/-/zustand-4.4.1.tgz}
|
||||
id: registry.npmjs.org/zustand/4.4.1
|
||||
name: zustand
|
||||
version: 4.4.1
|
||||
engines: {node: '>=12.7.0'}
|
||||
peerDependencies:
|
||||
'@types/react': '>=16.8'
|
||||
immer: '>=9.0'
|
||||
react: '>=16.8'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
immer:
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@types/react': registry.npmjs.org/@types/react@18.2.12
|
||||
react: registry.npmjs.org/react@18.2.0
|
||||
use-sync-external-store: registry.npmjs.org/use-sync-external-store@1.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
Reference in New Issue
Block a user