Skip to content

Commit

Permalink
Merge e22c179 into c7af560
Browse files Browse the repository at this point in the history
  • Loading branch information
williaster committed Oct 30, 2020
2 parents c7af560 + e22c179 commit 61894e7
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 28 deletions.
10 changes: 6 additions & 4 deletions packages/visx-demo/src/sandboxes/visx-drag-i/Example.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ export default function DragI({ width, height }: DragIProps) {

{draggingItems.map((d, i) => (
<Drag
key={`${d.id}`}
key={`drag-${d.id}`}
width={width}
height={height}
x={d.x}
y={d.y}
onDragStart={() => {
// svg follows the painter model
// so we need to move the data item
Expand All @@ -64,11 +66,11 @@ export default function DragI({ width, height }: DragIProps) {
setDraggingItems(raise(draggingItems, i));
}}
>
{({ dragStart, dragEnd, dragMove, isDragging, dx, dy }) => (
{({ dragStart, dragEnd, dragMove, isDragging, x, y, dx, dy }) => (
<circle
key={`dot-${d.id}`}
cx={d.x}
cy={d.y}
cx={x}
cy={y}
r={isDragging ? d.radius + 4 : d.radius}
fill={isDragging ? 'url(#stroke)' : colorScale(d.id)}
transform={`translate(${dx}, ${dy})`}
Expand Down
6 changes: 5 additions & 1 deletion packages/visx-drag/src/Drag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@ export type DragProps = UseDragOptions & {
export default function Drag({
captureDragArea = true,
children,
dx,
dy,
height,
onDragEnd,
onDragMove,
onDragStart,
resetOnStart,
width,
x,
y,
}: DragProps) {
const drag = useDrag({ resetOnStart, onDragEnd, onDragMove, onDragStart });
const drag = useDrag({ resetOnStart, onDragEnd, onDragMove, onDragStart, x, y, dx, dy });

return (
<>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { useCallback, useEffect, useRef } from 'react';
import { localPoint } from '@visx/event';
import useStateWithCallback from './util/useStateWithCallback';

Expand All @@ -18,13 +18,21 @@ export type UseDragOptions = {
onDragMove?: (args: HandlerArgs) => void;
/** Optional callback invoked upon drag start. */
onDragStart?: (args: HandlerArgs) => void;
/** Optionally set the initial drag x, or override the current drag x. */
x?: number;
/** Optionally set the initial drag y, or override the current drag y. */
y?: number;
/** Optionally set the initial drag dx, or override the current drag dx. */
dx?: number;
/** Optionally set the initial drag dy, or override the current drag dy. */
dy?: number;
};

export type DragState = {
/** x position of drag at drag start time, reset to 0 if `resetOnStart=true`. */
x: number | undefined;
x?: number;
/** y position of drag at drag start time, reset to 0 if `resetOnStart=true`. */
y: number | undefined;
y?: number;
/** Change in x position since drag start, reset to 0 on drag start if `resetOnStart=true`. */
dx: number;
/** Change in y position since drag start, reset to 0 on drag start if `resetOnStart=true`. */
Expand All @@ -48,28 +56,48 @@ export default function useDrag({
onDragEnd,
onDragMove,
onDragStart,
x,
y,
dx,
dy,
}: UseDragOptions | undefined = {}): UseDrag {
// use ref to detect prop changes
const positionPropsRef = useRef({ x, y, dx, dy });

const [dragState, setDragStateWithCallback] = useStateWithCallback<DragState>({
x: undefined,
y: undefined,
dx: 0,
dy: 0,
x,
y,
dx: dx ?? 0,
dy: dy ?? 0,
isDragging: false,
});

// if prop position changes, update state
useEffect(() => {
if (
positionPropsRef.current.x !== x ||
positionPropsRef.current.y !== y ||
positionPropsRef.current.dx !== dx ||
positionPropsRef.current.dy !== dy
) {
positionPropsRef.current = { x, y, dx, dy };
setDragStateWithCallback(currState => ({ ...currState, x, y, dx: dx ?? 0, dy: dy ?? 0 }));
}
});

const handleDragStart = useCallback(
(event: MouseOrTouchEvent) => {
event.persist();

setDragStateWithCallback(
({ dx, dy }) => {
currState => {
const point = localPoint(event) || { x: 0, y: 0 };
return {
isDragging: true,
dx: resetOnStart ? 0 : dx,
dy: resetOnStart ? 0 : dy,
x: resetOnStart ? point.x : point.x - dx,
y: resetOnStart ? point.y : point.y - dy,
dx: resetOnStart ? 0 : currState.dx,
dy: resetOnStart ? 0 : currState.dy,
x: resetOnStart ? point.x : point.x - currState.dx,
y: resetOnStart ? point.y : point.y - currState.dy,
};
},
onDragStart &&
Expand All @@ -87,14 +115,13 @@ export default function useDrag({

setDragStateWithCallback(
currState => {
const { x, y, isDragging } = currState;
const point = localPoint(event) || { x: 0, y: 0 };
return isDragging
return currState.isDragging
? {
...currState,
isDragging: true,
dx: point.x - (x || 0),
dy: point.y - (y || 0),
dx: point.x - (currState.x || 0),
dy: point.y - (currState.y || 0),
}
: currState;
},
Expand Down
7 changes: 0 additions & 7 deletions packages/visx-drag/test/useDrag.test.ts

This file was deleted.

52 changes: 52 additions & 0 deletions packages/visx-drag/test/useDrag.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { useRef } from 'react';
import { mount } from 'enzyme';
import { useDrag } from '../src';
import { UseDragOptions } from '../lib/useDrag';

describe('useDrag', () => {
test('it should be defined', () => {
expect(useDrag).toBeDefined();
});
test('should provide UseDrag', () => {
expect.assertions(1);

function Consumer() {
const drag = useDrag({ x: 0, y: 0 });
expect(drag).toMatchObject({
x: 0,
y: 0,
dx: 0,
dy: 0,
isDragging: false,
dragStart: expect.any(Function),
dragMove: expect.any(Function),
dragEnd: expect.any(Function),
});

return null;
}

mount(<Consumer />);
});

test('should update drag state when useDrag options change', () => {
expect.assertions(3);

const options1 = { x: 1, y: 2, dx: 3, dy: 4 };
const options2 = { x: -1, y: -2, dx: -3, dy: -4 };

function Consumer({ x, y, dx, dy }: Pick<UseDragOptions, 'x' | 'y' | 'dx' | 'dy'>) {
const renderCount = useRef(0);
const drag = useDrag({ x, y, dx, dy });

// when this component's props change options1 => 2, it takes one
// additional render for the `useDrag` state to update to options2
expect(drag).toMatchObject(renderCount.current <= 1 ? options1 : options2);
renderCount.current += 1;
return null;
}

const wrapper = mount(<Consumer {...options1} />);
wrapper.setProps(options2);
});
});

0 comments on commit 61894e7

Please sign in to comment.