Action pane use Gesture
This commit is contained in:
parent
d6926dbca0
commit
59e802fc14
3 changed files with 48 additions and 105 deletions
|
@ -10,6 +10,8 @@ import { useItems } from "./Items";
|
|||
import { useSetRecoilState, useRecoilCallback } from "recoil";
|
||||
import { insideClass, hasClass } from "../../utils";
|
||||
|
||||
import Gesture from "./Gesture";
|
||||
|
||||
const ActionPane = ({ children }) => {
|
||||
const { putItemsOnTop, moveItems } = useItems();
|
||||
|
||||
|
@ -20,76 +22,38 @@ const ActionPane = ({ children }) => {
|
|||
const actionRef = React.useRef({});
|
||||
|
||||
const onMouseDown = useRecoilCallback(
|
||||
({ snapshot }) => async (e) => {
|
||||
if (!e.isPrimary) {
|
||||
return;
|
||||
}
|
||||
const {
|
||||
target,
|
||||
pointerId,
|
||||
currentTarget,
|
||||
ctrlKey,
|
||||
metaKey,
|
||||
clientX,
|
||||
clientY,
|
||||
button,
|
||||
} = e;
|
||||
if (button === 0 && !e.altKey) {
|
||||
// Allow text selection instead of moving
|
||||
if (["INPUT", "TEXTAREA"].includes(target.tagName)) return;
|
||||
({ snapshot }) => async ({ target, ctrlKey, metaKey, event }) => {
|
||||
// Allow text selection instead of moving
|
||||
if (["INPUT", "TEXTAREA"].includes(target.tagName)) return;
|
||||
|
||||
const { top, left } = currentTarget.getBoundingClientRect();
|
||||
const foundElement = insideClass(target, "item");
|
||||
|
||||
const foundElement = insideClass(target, "item");
|
||||
if (foundElement && !hasClass(foundElement, "locked")) {
|
||||
event.stopPropagation();
|
||||
const selectedItems = await snapshot.getPromise(selectedItemsAtom);
|
||||
|
||||
if (foundElement && !hasClass(foundElement, "locked")) {
|
||||
e.stopPropagation();
|
||||
const panZoomRotate = await snapshot.getPromise(PanZoomRotateAtom);
|
||||
const selectedItems = await snapshot.getPromise(selectedItemsAtom);
|
||||
|
||||
const point = {
|
||||
x: (clientX - left) / panZoomRotate.scale,
|
||||
y: (clientY - top) / panZoomRotate.scale,
|
||||
};
|
||||
|
||||
if (!selectedItems.includes(foundElement.id)) {
|
||||
if (ctrlKey || metaKey) {
|
||||
setSelectedItems((prev) => [...prev, foundElement.id]);
|
||||
} else {
|
||||
setSelectedItems([foundElement.id]);
|
||||
}
|
||||
}
|
||||
|
||||
actionRef.current.moving = true;
|
||||
actionRef.current.onTop = false;
|
||||
actionRef.current.startX = point.x;
|
||||
actionRef.current.startY = point.y;
|
||||
actionRef.current.prevX = point.x;
|
||||
actionRef.current.prevY = point.y;
|
||||
actionRef.current.itemId = foundElement.id;
|
||||
|
||||
wrapperRef.current.style.cursor = "move";
|
||||
try {
|
||||
currentTarget.setPointerCapture(pointerId);
|
||||
} catch (e) {
|
||||
console.log("Fail to capture pointer", e);
|
||||
if (!selectedItems.includes(foundElement.id)) {
|
||||
if (ctrlKey || metaKey) {
|
||||
setSelectedItems((prev) => [...prev, foundElement.id]);
|
||||
} else {
|
||||
setSelectedItems([foundElement.id]);
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(actionRef.current, {
|
||||
moving: true,
|
||||
onTop: false,
|
||||
remainX: 0,
|
||||
remainY: 0,
|
||||
});
|
||||
}
|
||||
},
|
||||
[setSelectedItems]
|
||||
);
|
||||
|
||||
const onMouseMove = useRecoilCallback(
|
||||
({ snapshot }) => async (e) => {
|
||||
({ snapshot }) => async ({ deltaX, deltaY }) => {
|
||||
if (actionRef.current.moving) {
|
||||
if (!e.isPrimary) {
|
||||
return;
|
||||
}
|
||||
const { clientX, clientY, currentTarget } = e;
|
||||
|
||||
const { top, left } = currentTarget.getBoundingClientRect();
|
||||
|
||||
const panZoomRotate = await snapshot.getPromise(PanZoomRotateAtom);
|
||||
const selectedItems = await snapshot.getPromise(selectedItemsAtom);
|
||||
const { gridSize: boardGridSize = 1 } = await snapshot.getPromise(
|
||||
|
@ -97,15 +61,10 @@ const ActionPane = ({ children }) => {
|
|||
);
|
||||
const gridSize = boardGridSize || 1; // avoid 0 grid size
|
||||
|
||||
const currentX = (clientX - left) / panZoomRotate.scale;
|
||||
const currentY = (clientY - top) / panZoomRotate.scale;
|
||||
|
||||
let realMoveX =
|
||||
Math.round((currentX - actionRef.current.prevX) / gridSize) *
|
||||
gridSize;
|
||||
let realMoveY =
|
||||
Math.round((currentY - actionRef.current.prevY) / gridSize) *
|
||||
gridSize;
|
||||
const moveX = actionRef.current.remainX + deltaX / panZoomRotate.scale;
|
||||
const moveY = actionRef.current.remainY + deltaY / panZoomRotate.scale;
|
||||
const realMoveX = Math.round(moveX / gridSize) * gridSize;
|
||||
const realMoveY = Math.round(moveY / gridSize) * gridSize;
|
||||
|
||||
if (realMoveX || realMoveY) {
|
||||
// Put items on top of others on first move
|
||||
|
@ -124,41 +83,32 @@ const ActionPane = ({ children }) => {
|
|||
gridSize
|
||||
);
|
||||
|
||||
actionRef.current.prevX += realMoveX;
|
||||
actionRef.current.prevY += realMoveY;
|
||||
setBoardState((prev) =>
|
||||
!prev.movingItems ? { ...prev, movingItems: true } : prev
|
||||
);
|
||||
}
|
||||
actionRef.current.remainX = moveX - realMoveX;
|
||||
actionRef.current.remainY = moveY - realMoveY;
|
||||
}
|
||||
},
|
||||
[moveItems, putItemsOnTop, setBoardState]
|
||||
);
|
||||
|
||||
const onMouseUp = React.useCallback(
|
||||
(e) => {
|
||||
if (!e.isPrimary) {
|
||||
return;
|
||||
}
|
||||
if (actionRef.current.moving) {
|
||||
actionRef.current = { moving: false };
|
||||
wrapperRef.current.style.cursor = "auto";
|
||||
setBoardState((prev) => ({ ...prev, movingItems: false }));
|
||||
}
|
||||
},
|
||||
[setBoardState]
|
||||
);
|
||||
const onMouseUp = React.useCallback(() => {
|
||||
if (actionRef.current.moving) {
|
||||
actionRef.current = { moving: false };
|
||||
setBoardState((prev) => ({ ...prev, movingItems: false }));
|
||||
}
|
||||
}, [setBoardState]);
|
||||
|
||||
return (
|
||||
<div
|
||||
onPointerDown={onMouseDown}
|
||||
onPointerMove={onMouseMove}
|
||||
onPointerUp={onMouseUp}
|
||||
style={{ touchAction: "none" }}
|
||||
ref={wrapperRef}
|
||||
<Gesture
|
||||
onDragStart={onMouseDown}
|
||||
onDrag={onMouseMove}
|
||||
onDragEnd={onMouseUp}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<div ref={wrapperRef}>{children}</div>
|
||||
</Gesture>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -174,8 +174,7 @@ const PanZoomRotate = ({ children, moveFirst }) => {
|
|||
};
|
||||
}, []);
|
||||
|
||||
const onZoom = (e) => {
|
||||
const { clientX, clientY, scale } = e;
|
||||
const onZoom = ({ clientX, clientY, scale }) => {
|
||||
setScale((prevScale) => {
|
||||
let newScale = prevScale.scale * scale;
|
||||
if (newScale > 8) {
|
||||
|
@ -193,8 +192,7 @@ const PanZoomRotate = ({ children, moveFirst }) => {
|
|||
});
|
||||
};
|
||||
|
||||
const onPan = (e) => {
|
||||
const { deltaX, deltaY } = e;
|
||||
const onPan = ({ deltaX, deltaY }) => {
|
||||
setDim((prevDim) => {
|
||||
return {
|
||||
...prevDim,
|
||||
|
@ -204,14 +202,14 @@ const PanZoomRotate = ({ children, moveFirst }) => {
|
|||
});
|
||||
};
|
||||
|
||||
const onDrag = (e) => {
|
||||
const { target } = e;
|
||||
const onDrag = (state) => {
|
||||
const { target } = state;
|
||||
|
||||
const outsideItem =
|
||||
!insideClass(target, "item") || insideClass(target, "locked");
|
||||
|
||||
if (moveFirst && outsideItem) {
|
||||
onPan(e);
|
||||
onPan(state);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -113,9 +113,7 @@ const Selector = ({ children, moveFirst }) => {
|
|||
};
|
||||
}, [setSelected, emptySelection]);
|
||||
|
||||
const onDragStart = (e) => {
|
||||
const { button, altKey, ctrlKey, metaKey, target } = e;
|
||||
|
||||
const onDragStart = ({ button, altKey, ctrlKey, metaKey, target }) => {
|
||||
const outsideItem =
|
||||
!insideClass(target, "item") || insideClass(target, "locked");
|
||||
|
||||
|
@ -133,9 +131,7 @@ const Selector = ({ children, moveFirst }) => {
|
|||
};
|
||||
|
||||
const onDrag = useRecoilCallback(
|
||||
({ snapshot }) => async (e) => {
|
||||
const { distanceY, distanceX, startX, startY } = e;
|
||||
|
||||
({ snapshot }) => async ({ distanceY, distanceX, startX, startY }) => {
|
||||
if (stateRef.current.moving) {
|
||||
const { top, left } = wrapperRef.current.getBoundingClientRect();
|
||||
|
||||
|
@ -178,8 +174,7 @@ const Selector = ({ children, moveFirst }) => {
|
|||
};
|
||||
|
||||
const onTap = React.useCallback(
|
||||
(e) => {
|
||||
const { target } = e;
|
||||
({ target }) => {
|
||||
if (
|
||||
(!insideClass(target, "item") || insideClass(target, "locked")) &&
|
||||
insideClass(target, "board")
|
||||
|
|
Loading…
Reference in a new issue