Skip to content

Commit

Permalink
Changed touch/drag movement UX
Browse files Browse the repository at this point in the history
Dragging past the Panel's min/max size will behave differently when dragging back. The panel won't start resizing again until the touch/mouse event returns to the min/max panel size point.
  • Loading branch information
bvaughn committed Dec 25, 2022
1 parent b718a59 commit 3993084
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 59 deletions.
13 changes: 3 additions & 10 deletions packages/react-resizable-panels/src/PanelGroup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import useUniqueId from "./hooks/useUniqueId";
import { PanelContext, PanelGroupContext } from "./PanelContexts";
import { Direction, PanelData, ResizeEvent } from "./types";
import { loadPanelLayout, savePanelGroupLayout } from "./utils/serialization";
import { Coordinates, getUpdatedCoordinates } from "./utils/coordinates";
import { getMovement } from "./utils/coordinates";
import {
adjustByDelta,
getOffset,
Expand Down Expand Up @@ -71,10 +71,6 @@ export default function PanelGroup({
width,
});

// Tracks the most recent coordinates of a touch/mouse event.
// This is needed to calculate movement (because TouchEvent doesn't support movementX and movementY).
const prevOffsetRef = useRef<number>(0);

useLayoutEffect(() => {
committedValuesRef.current.direction = direction;
committedValuesRef.current.height = height;
Expand Down Expand Up @@ -200,16 +196,14 @@ export default function PanelGroup({
return;
}

const nextCoordinates = getUpdatedCoordinates(
const movement = getMovement(
event,
prevOffsetRef.current,
handleId,
{ height, width },
direction
);
prevOffsetRef.current = nextCoordinates.offset;

const isHorizontal = direction === "horizontal";
const movement = nextCoordinates.movement;
const delta = isHorizontal ? movement / width : movement / height;

const nextSizes = adjustByDelta(
Expand Down Expand Up @@ -252,7 +246,6 @@ export default function PanelGroup({
startDragging: (id: string) => setActiveHandleId(id),
stopDragging: () => {
setActiveHandleId(null);
prevOffsetRef.current = 0;
},
unregisterPanel,
}),
Expand Down
73 changes: 24 additions & 49 deletions packages/react-resizable-panels/src/utils/coordinates.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Direction, ResizeEvent } from "../types";
import { getResizeHandle } from "./group";

export type Coordinates = {
movement: number;
Expand All @@ -14,81 +15,55 @@ const element = document.createElement("div");
element.getBoundingClientRect();

// https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/movementX
export function getUpdatedCoordinates(
export function getMovement(
event: ResizeEvent,
prevOffset: number,
handleId: string,
{ height, width }: Size,
direction: Direction
): Coordinates {
): number {
const isHorizontal = direction === "horizontal";
const size = isHorizontal ? width : height;

const getMovementBetween = (current: number, prev: number) =>
prev === 0 ? 0 : current - prev;

if (isKeyDown(event)) {
let movement = 0;

const denominator = event.shiftKey ? 10 : 100;
const delta = size / denominator;

switch (event.key) {
case "ArrowDown":
movement = delta;
break;
return delta;
case "ArrowLeft":
movement = -delta;
break;
return -delta;
case "ArrowRight":
movement = delta;
break;
return delta;
case "ArrowUp":
movement = -delta;
break;
return -delta;
case "End":
if (isHorizontal) {
movement = size;
return size;
} else {
movement = size;
return size;
}
break;
case "Home":
if (isHorizontal) {
movement = -size;
return -size;
} else {
movement = -size;
return -size;
}
break;
}

// Estimate screen X/Y to be the center of the resize handle.
// Otherwise the first mouse/touch event after a keyboard event will appear to "jump"
let offset = 0;
if (document.activeElement) {
const rect = document.activeElement.getBoundingClientRect();
offset = isHorizontal
? rect.left + rect.width / 2
: rect.top + rect.height / 2;
} else {
const handleElement = getResizeHandle(handleId)!;
const rect = handleElement.getBoundingClientRect();
const elementOffset = isHorizontal ? rect.left : rect.top;

let pointerOffset = 0;
if (isMouseMoveEvent(event)) {
pointerOffset = isHorizontal ? event.clientX : event.clientY;
} else {
const firstTouch = event.touches[0];
pointerOffset = isHorizontal ? firstTouch.screenX : firstTouch.screenY;
}

return {
movement,
offset,
};
} else if (isTouchMoveEvent(event)) {
const firstTouch = event.touches[0];

const offset = isHorizontal ? firstTouch.screenX : firstTouch.screenY;
const movement = getMovementBetween(offset, prevOffset);

return { movement, offset };
} else if (isMouseMoveEvent(event)) {
const offset = isHorizontal ? event.screenX : event.screenY;
const movement = getMovementBetween(offset, prevOffset);

return { movement, offset };
} else {
throw Error(`Unsupported event type: "${(event as any).type}"`);
return pointerOffset - elementOffset;
}
}

Expand Down

0 comments on commit 3993084

Please sign in to comment.