diff --git a/packages/visx-zoom/src/Zoom.tsx b/packages/visx-zoom/src/Zoom.tsx index cd7c51070..844436dfe 100644 --- a/packages/visx-zoom/src/Zoom.tsx +++ b/packages/visx-zoom/src/Zoom.tsx @@ -18,6 +18,7 @@ import { ScaleSignature, ProvidedZoom, PinchDelta, + CreateGestureHandlers, } from './types'; // default prop values @@ -38,6 +39,40 @@ const defaultPinchDelta: PinchDelta = ({ offset: [s], lastOffset: [lastS] }) => scaleY: s - lastS < 0 ? 0.9 : 1.1, }); +const defaultCreateGestureHandlers: CreateGestureHandlers = ({ + dragStart, + dragEnd, + dragMove, + handlePinch, + handleWheel, +}: ProvidedZoom) => ({ + onDragStart: ({ event }) => { + if (!(event instanceof KeyboardEvent)) dragStart(event); + }, + onDrag: ({ event, pinching, cancel }) => { + if (pinching) { + cancel(); + dragEnd(); + } else if (!(event instanceof KeyboardEvent)) { + dragMove(event); + } + }, + onDragEnd: dragEnd, + onPinch: handlePinch, + onWheel: ({ event, active, pinching }) => { + if ( + // Outside of Safari, the wheel event is fired together with the pinch event + pinching || + // currently onWheelEnd emits one final wheel event which causes 2x scale + // updates for the last tick. ensuring that the gesture is active avoids this + !active + ) { + return; + } + handleWheel(event); + }, +}); + export type ZoomProps = { /** Width of the zoom container. */ width: number; @@ -94,6 +129,8 @@ export type ZoomProps = { /** Initial transform matrix to apply. */ initialTransformMatrix?: TransformMatrix; children: (zoom: ProvidedZoom & ZoomState) => React.ReactElement; + /** A function that returns gesture handlers for managing UI interactions */ + createGestureHandlers?: CreateGestureHandlers; }; type ZoomState = { @@ -114,6 +151,7 @@ function Zoom({ height, constrain, children, + createGestureHandlers = defaultCreateGestureHandlers, }: ZoomProps): React.ReactElement { const containerRef = useRef(null); const matrixStateRef = useRef(initialTransformMatrix); @@ -312,37 +350,6 @@ function Zoom({ setTransformMatrix(identityMatrix()); }, [setTransformMatrix]); - useGesture( - { - onDragStart: ({ event }) => { - if (!(event instanceof KeyboardEvent)) dragStart(event); - }, - onDrag: ({ event, pinching, cancel }) => { - if (pinching) { - cancel(); - dragEnd(); - } else if (!(event instanceof KeyboardEvent)) { - dragMove(event); - } - }, - onDragEnd: dragEnd, - onPinch: handlePinch, - onWheel: ({ event, active, pinching }) => { - if ( - // Outside of Safari, the wheel event is fired together with the pinch event - pinching || - // currently onWheelEnd emits one final wheel event which causes 2x scale - // updates for the last tick. ensuring that the gesture is active avoids this - !active - ) { - return; - } - handleWheel(event); - }, - }, - { target: containerRef, eventOptions: { passive: false }, drag: { filterTaps: true } }, - ); - const zoom: ProvidedZoom & ZoomState = { initialTransformMatrix, transformMatrix, @@ -368,6 +375,12 @@ function Zoom({ containerRef, }; + useGesture(createGestureHandlers(zoom), { + target: containerRef, + eventOptions: { passive: false }, + drag: { filterTaps: true }, + }); + return <>{children(zoom)}; } diff --git a/packages/visx-zoom/src/types.ts b/packages/visx-zoom/src/types.ts index efc39574e..ce8e25d43 100644 --- a/packages/visx-zoom/src/types.ts +++ b/packages/visx-zoom/src/types.ts @@ -1,4 +1,10 @@ -import { UserHandlers, WebKitGestureEvent, Handler } from '@use-gesture/react'; +import { + UserHandlers, + WebKitGestureEvent, + Handler, + GestureHandlers, + EventTypes, +} from '@use-gesture/react'; import { RefObject, MouseEvent as ReactMouseEvent, @@ -46,6 +52,10 @@ export interface ScaleSignature { point?: Point; } +export type CreateGestureHandlers = ( + zoom: ProvidedZoom, +) => GestureHandlers; + export interface ProvidedZoom { /** Sets translateX/Y to the center defined by width and height. */ center: () => void; diff --git a/packages/visx-zoom/test/Zoom.test.tsx b/packages/visx-zoom/test/Zoom.test.tsx index b63c7e007..10a2a9e65 100644 --- a/packages/visx-zoom/test/Zoom.test.tsx +++ b/packages/visx-zoom/test/Zoom.test.tsx @@ -1,11 +1,14 @@ import React from 'react'; -import { render } from 'enzyme'; +import { render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import { Zoom, inverseMatrix } from '../src'; +import { CreateGestureHandlers } from '../lib/types'; describe('', () => { it('should be defined', () => { expect(Zoom).toBeDefined(); }); + it('should render the children and pass zoom params', () => { const initialTransform = { scaleX: 1.27, @@ -16,7 +19,7 @@ describe('', () => { skewY: 0, }; - const wrapper = render( + render( ', () => { > {({ transformMatrix }) => { const { scaleX, scaleY, translateX, translateY } = transformMatrix; - return
{[scaleX, scaleY, translateX, translateY].join(',')}
; + return ( +
{[scaleX, scaleY, translateX, translateY].join(',')}
+ ); }}
, ); + expect(screen.getByTestId('zoom-child').innerHTML).toBe('1.27,1.27,-211.62,162.59'); + }); + it('should accept custom gesture handlers', () => { + const onClick = jest.fn(); + + const createGestureHandlers: CreateGestureHandlers = () => ({ onClick }); + + render( + + width={400} + height={400} + createGestureHandlers={createGestureHandlers} + > + {({ containerRef }) =>