This commit is contained in:
2025-11-02 14:08:08 +01:00
parent a30b6eaa02
commit 65495a07b2
2 changed files with 416 additions and 222 deletions
+9 -1
View File
@@ -1930,7 +1930,15 @@ const DesktopWorkspaceView = () => {
}
}}
onPointerDown={(event) => {
if (typeof onDocumentPointerSelect === 'function') {
const alreadySelected = selectedDocumentIds.includes(doc.id);
if (
typeof onDocumentPointerSelect === 'function'
&& (!alreadySelected
|| event.metaKey
|| event.ctrlKey
|| event.shiftKey
|| event.altKey)
) {
onDocumentPointerSelect(doc.id, event);
}
handlePointerDown(event, doc.id);
+407 -221
View File
@@ -14,13 +14,6 @@ const ANGULAR_DAMPING = 11;
const TORQUE_TO_ACCELERATION = 0.006;
const SETTLE_ANGULAR_VELOCITY = 1.2;
const EDGE_COLLISION_THRESHOLD = 0.5;
const EDGE_ALIGNMENT_STIFFNESS = 30;
const BASE_TORQUE_FACTOR = 0.4;
const EDGE_COLLISION_TORQUE_FACTOR = 0.08;
const EDGE_ALIGNMENT_TORQUE_MULTIPLIER = 35;
const EDGE_COLLISION_EXTRA_DAMPING = 4;
const EDGE_REST_REALIGN_RATE = 10;
const EDGE_ALIGNMENT_EPSILON = 0.15;
const useDocumentDrag = () => {
const {
@@ -39,8 +32,62 @@ const useDocumentDrag = () => {
containerRef,
onDocumentOpen,
onInspectDocument,
selectedDocumentIds,
} = useDesktopContext();
const applyTransform = useCallback(
(docId, centerX, centerY, width, height, rotation, scale = 1) => {
const node = itemRefs.current.get(docId);
if (!node) {
return;
}
node.style.transform = formatTransform(
centerX - width / 2,
centerY - height / 2,
rotation,
scale,
);
},
[itemRefs],
);
const finalizeGroupDrag = useCallback(
(dragState) => {
if (!dragState?.groupItems) {
return;
}
dragState.groupItems.forEach((item) => {
if (!item) {
return;
}
const entryItem = layoutRef.current.get(item.docId) || {};
const centerX = item.currentCenterX ?? entryItem.centerX ?? dragState.originCenterX;
const centerY = item.currentCenterY ?? entryItem.centerY ?? dragState.originCenterY;
const rotation = item.displayRotation ?? entryItem.rotation ?? 0;
layoutRef.current.set(item.docId, {
...entryItem,
centerX,
centerY,
rotation,
});
applyTransform(
item.docId,
centerX,
centerY,
item.width,
item.height,
rotation,
item.docId === dragState.docKey ? dragState.dragScale || 1 : 1,
);
});
},
[applyTransform, layoutRef],
);
const tapHandler = usePointerTap({
delay: 220,
onSingle: ({ data, event }) => {
@@ -200,11 +247,11 @@ const useDocumentDrag = () => {
);
const handlePointerDown = useCallback(
(event, docId) => {
(event, docIdInput) => {
if (debugDrag) {
console.log(
'[desk] handlePointerDown fired for doc',
docId,
docIdInput,
'button',
event.button,
'pointerType',
@@ -214,25 +261,74 @@ const useDocumentDrag = () => {
);
}
preventAll(event);
cancelInertiaAnimation(docId);
const docId = docIdInput != null ? docIdInput : null;
const docKey = docId != null ? String(docId) : null;
const doc = docKey ? documentLookup.get(docKey) : null;
const { width: docWidth, height: docHeight } = ensureDocumentSize(doc);
if (!docKey) {
return;
}
cancelInertiaAnimation(docId);
const doc = documentLookup.get(docKey);
if (!doc) {
return;
}
let selectionIds = Array.isArray(selectedDocumentIds)
? selectedDocumentIds.map((id) => String(id))
: [];
const metaOrCtrl = event.metaKey || event.ctrlKey;
if (metaOrCtrl && !selectionIds.includes(docKey)) {
selectionIds = [...selectionIds, docKey];
}
let groupDocIds = [];
if (selectionIds.includes(docKey) && selectionIds.length > 1) {
groupDocIds = selectionIds
.map((id) => String(id))
.filter((id, index, array) => array.indexOf(id) === index && documentLookup.has(id));
}
if (!groupDocIds.includes(docKey)) {
groupDocIds.unshift(docKey);
}
groupDocIds = groupDocIds.filter((id, index, array) => array.indexOf(id) === index);
if (!groupDocIds.length) {
groupDocIds = [docKey];
}
const isGroupDrag = groupDocIds.length > 1;
if (isGroupDrag) {
groupDocIds.forEach((id) => {
if (id !== docKey) {
cancelInertiaAnimation(id);
}
});
}
const sizeInfo = ensureDocumentSize(doc) || { width: 0, height: 0 };
const docWidth = sizeInfo.width || 320;
const docHeight = sizeInfo.height || 240;
const { baseScale } = resolveBaseMetrics(doc, docWidth, docHeight);
const normalizedBaseScale =
Number.isFinite(baseScale) && baseScale > 0 ? baseScale : 1;
const entry = layoutRef.current.get(docKey) || null;
const defaultCenterX = canvasPadding + docWidth / 2;
const defaultCenterY = canvasPadding + docHeight / 2;
const metaPressed = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
if (!metaPressed) {
bringToFront(docId);
}
const entry = layoutRef.current.get(docId) || null;
const centerX = typeof entry?.centerX === 'number' ? entry.centerX : defaultCenterX;
const centerY = typeof entry?.centerY === 'number' ? entry.centerY : defaultCenterY;
const modifierPressed = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
if (!modifierPressed) {
if (isGroupDrag) {
groupDocIds.forEach((id) => bringToFront(id));
} else {
bringToFront(docId);
}
}
if (entry && (entry.centerX !== centerX || entry.centerY !== centerY)) {
layoutRef.current.set(docId, { ...entry, centerX, centerY });
layoutRef.current.set(docKey, { ...entry, centerX, centerY });
}
const capturedTarget = event.currentTarget instanceof HTMLElement ? event.currentTarget : null;
@@ -245,6 +341,7 @@ const useDocumentDrag = () => {
}
}
}
const containerRect = containerRef?.current?.getBoundingClientRect?.() || null;
const containerLeft = containerRect?.left || 0;
const containerTop = containerRect?.top || 0;
@@ -258,14 +355,48 @@ const useDocumentDrag = () => {
const sinInitial = Math.sin(-initialRotationRad);
const localPointerOffsetX = pointerOffsetX * cosInitial - pointerOffsetY * sinInitial;
const localPointerOffsetY = pointerOffsetX * sinInitial + pointerOffsetY * cosInitial;
const stackRandom = () => Math.random();
const groupItems = groupDocIds.map((id, index) => {
const itemDoc = documentLookup.get(id);
const itemSize = ensureDocumentSize(itemDoc) || sizeInfo;
const itemWidth = itemSize.width || docWidth;
const itemHeight = itemSize.height || docHeight;
const itemEntry = layoutRef.current.get(id) || null;
const itemCenterX =
typeof itemEntry?.centerX === 'number' ? itemEntry.centerX : canvasPadding + itemWidth / 2;
const itemCenterY =
typeof itemEntry?.centerY === 'number' ? itemEntry.centerY : canvasPadding + itemHeight / 2;
const radius = index === 0 ? 0 : 24 + index * 8;
const offsetAngle = (index * 1.618 + stackRandom() * 0.5) * Math.PI;
const offsetX = Math.cos(offsetAngle) * radius;
const offsetY = Math.sin(offsetAngle) * radius;
const initialRotation = itemEntry?.rotation ?? 0;
const targetRotation = initialRotation;
return {
docId: id,
width: itemWidth,
height: itemHeight,
currentCenterX: itemCenterX,
currentCenterY: itemCenterY,
offsetX,
offsetY,
initialRotation,
displayRotation: initialRotation,
targetRotation,
};
});
const eventTimestamp =
typeof event.timeStamp === 'number' && Number.isFinite(event.timeStamp)
? event.timeStamp
: typeof performance !== 'undefined' && typeof performance.now === 'function'
? performance.now()
: Date.now();
dragStateRef.current = {
docId,
docKey,
pointerId: event.pointerId,
originCenterX: centerX,
originCenterY: centerY,
@@ -289,44 +420,198 @@ const useDocumentDrag = () => {
localPointerOffsetY,
containerRectLeft: containerLeft,
containerRectTop: containerTop,
};
setDraggingId(docId);
},
[
bringToFront,
isGroup: isGroupDrag,
groupDocIds,
groupItems,
groupElevated: !isGroupDrag,
};
setDraggingId(docId);
if (isGroupDrag) {
groupItems.forEach((item) => {
if (item.docId === docKey) {
return;
}
const node = itemRefs.current.get(item.docId);
if (node) {
item.displayRotation = item.initialRotation;
node.style.transform = formatTransform(
item.currentCenterX - item.width / 2,
item.currentCenterY - item.height / 2,
item.displayRotation,
1,
);
}
});
}
},
[
bringToFront,
canvasPadding,
cancelInertiaAnimation,
documentLookup,
ensureDocumentSize,
layoutRef,
resolveBaseMetrics,
setDraggingId,
debugDrag,
containerRef,
],
);
documentLookup,
ensureDocumentSize,
layoutRef,
resolveBaseMetrics,
selectedDocumentIds,
setDraggingId,
debugDrag,
itemRefs,
],
);
const handlePointerMove = useCallback(
(event) => {
const state = dragStateRef.current;
if (!state) {
if (debugDrag) {
console.log('[desk] handlePointerMove: no drag state for pointer', event.pointerId);
}
return;
if (debugDrag) {
console.log('[desk] handlePointerMove: no drag state for pointer', event.pointerId);
}
if (state.pointerId !== event.pointerId) {
if (debugDrag) {
console.log(
'[desk] handlePointerMove: pointer mismatch expected',
state.pointerId,
'got',
event.pointerId,
);
}
return;
return;
}
if (state.pointerId !== event.pointerId) {
if (debugDrag) {
console.log(
'[desk] handlePointerMove: pointer mismatch expected',
state.pointerId,
'got',
event.pointerId,
);
}
preventAll(event);
return;
}
preventAll(event);
if (state.isGroup) {
const containerRect = containerRef?.current?.getBoundingClientRect?.();
if (containerRect) {
state.containerRectLeft = containerRect.left;
state.containerRectTop = containerRect.top;
}
const pointerCanvasX = event.clientX - state.containerRectLeft;
const pointerCanvasY = event.clientY - state.containerRectTop;
const deltaX = event.clientX - state.startX;
const deltaY = event.clientY - state.startY;
if (!state.moved) {
const distanceSquared = deltaX * deltaX + deltaY * deltaY;
if (distanceSquared < DRAG_HYSTERESIS_SQUARED) {
return;
}
state.moved = true;
if (!state.groupElevated) {
state.groupDocIds.forEach((id) => {
if (id === state.docKey) {
bringToFront(state.docId);
} else {
bringToFront(id);
}
});
state.groupElevated = true;
}
}
const docWidth = state.width;
const docHeight = state.height;
const halfWidth = docWidth / 2;
const halfHeight = docHeight / 2;
const canvasWidth = canvasSize.width || defaultCanvasWidth;
const canvasHeight = canvasSize.height || defaultCanvasHeight;
const minCenterX = canvasPadding + halfWidth;
const maxCenterX = Math.max(minCenterX, canvasWidth - canvasPadding - halfWidth);
const minCenterY = canvasPadding + halfHeight;
const maxCenterY = Math.max(minCenterY, canvasHeight - canvasPadding - halfHeight);
const desiredCenterX = pointerCanvasX - state.localPointerOffsetX;
const desiredCenterY = pointerCanvasY - state.localPointerOffsetY;
const centerX = clamp(desiredCenterX, minCenterX, maxCenterX);
const centerY = clamp(desiredCenterY, minCenterY, maxCenterY);
const primaryEntry = layoutRef.current.get(state.docKey) || {};
const primaryRotation = state.rotation ?? primaryEntry.rotation ?? 0;
layoutRef.current.set(state.docKey, {
...primaryEntry,
centerX,
centerY,
});
const primaryNode = itemRefs.current.get(state.docId);
if (primaryNode) {
primaryNode.style.transform = formatTransform(
centerX - docWidth / 2,
centerY - docHeight / 2,
primaryRotation,
state.dragScale || 1,
);
}
state.groupItems.forEach((item) => {
const isPrimary = item.docId === state.docKey;
if (isPrimary) {
item.currentCenterX = centerX;
item.currentCenterY = centerY;
item.offsetX *= 0.92;
item.offsetY *= 0.92;
item.displayRotation = state.rotation ?? item.displayRotation ?? 0;
} else {
item.offsetX *= 0.92;
item.offsetY *= 0.92;
if (Math.abs(item.offsetX) < 1) item.offsetX = 0;
if (Math.abs(item.offsetY) < 1) item.offsetY = 0;
const targetX = centerX + item.offsetX;
const targetY = centerY + item.offsetY;
const smoothing = 0.18;
item.currentCenterX += (targetX - item.currentCenterX) * smoothing;
item.currentCenterY += (targetY - item.currentCenterY) * smoothing;
const halfW = item.width / 2;
const halfH = item.height / 2;
const minX = canvasPadding + halfW;
const maxX = Math.max(minX, canvasWidth - canvasPadding - halfW);
const minY = canvasPadding + halfH;
const maxY = Math.max(minY, canvasHeight - canvasPadding - halfH);
item.currentCenterX = clamp(item.currentCenterX, minX, maxX);
item.currentCenterY = clamp(item.currentCenterY, minY, maxY);
const rotationBlend = 0.16;
item.displayRotation += (item.targetRotation - item.displayRotation) * rotationBlend;
}
const entryItem = layoutRef.current.get(item.docId) || {};
layoutRef.current.set(item.docId, {
...entryItem,
centerX: item.currentCenterX,
centerY: item.currentCenterY,
rotation: item.displayRotation ?? entryItem.rotation ?? 0,
});
applyTransform(
item.docId,
item.currentCenterX,
item.currentCenterY,
item.width,
item.height,
item.displayRotation ?? entryItem.rotation ?? 0,
isPrimary ? state.dragScale || 1 : 1,
);
});
state.lastClientX = event.clientX;
state.lastClientY = event.clientY;
state.lastTimestamp =
typeof event.timeStamp === 'number' && Number.isFinite(event.timeStamp)
? event.timeStamp
: typeof performance !== 'undefined' && typeof performance.now === 'function'
? performance.now()
: Date.now();
recalcVisibleDocIds();
return;
}
if (state.locked) {
if (debugDrag) {
console.log('[desk] handlePointerMove: locked drag for doc', state.docId);
@@ -358,7 +643,7 @@ const useDocumentDrag = () => {
const pointerCanvasX = event.clientX - containerLeft;
const pointerCanvasY = event.clientY - containerTop;
const rotationDeg = state.rotation || 0;
const rotationDeg = entry?.rotation ?? 0;
const rotationRad = (rotationDeg * Math.PI) / 180;
const cosRot = Math.cos(rotationRad);
const sinRot = Math.sin(rotationRad);
@@ -420,6 +705,26 @@ const useDocumentDrag = () => {
const updated = { ...entry, centerX: currentCenterX, centerY: currentCenterY };
layoutRef.current.set(state.docId, updated);
applyTransform(
state.docId,
currentCenterX,
currentCenterY,
state.width,
state.height,
rotationDeg,
state.dragScale || 1,
);
const primaryNode = itemRefs.current.get(state.docId);
if (primaryNode) {
primaryNode.style.transform = formatTransform(
currentCenterX - state.width / 2,
currentCenterY - state.height / 2,
rotationDeg,
state.dragScale || 1,
);
}
const offsetX = pointerCanvasX - currentCenterX;
const offsetY = pointerCanvasY - currentCenterY;
const cosInverseCurrent = Math.cos(-rotationRad);
@@ -442,147 +747,10 @@ const useDocumentDrag = () => {
}
dt = clamp(dt, MIN_TIMESTEP, MAX_TIMESTEP);
const previousClientX = state.lastClientX;
const previousClientY = state.lastClientY;
const velocityX = (event.clientX - previousClientX) / dt;
const velocityY = (event.clientY - previousClientY) / dt;
const rawTorque = offsetX * velocityY - offsetY * velocityX;
const rotationBeforeIntegration = state.rotation;
let torque = rawTorque * BASE_TORQUE_FACTOR;
let dampingOverride = null;
if (collidedWithEdge) {
const currentRotation =
typeof state.rotation === 'number'
? state.rotation
: state.restRotation + state.dynamicRotation;
torque =
(rawTorque * EDGE_COLLISION_TORQUE_FACTOR + currentRotation * EDGE_ALIGNMENT_STIFFNESS) *
EDGE_ALIGNMENT_TORQUE_MULTIPLIER;
const restBlend = 1 - Math.exp(-EDGE_REST_REALIGN_RATE * dt);
if (restBlend > 0) {
const previousRest = state.restRotation;
const nextRest = previousRest + (0 - previousRest) * restBlend;
state.restRotation = nextRest;
}
dampingOverride = ANGULAR_DAMPING + EDGE_COLLISION_EXTRA_DAMPING;
if (!pointerInsideCard) {
torque = 0;
currentCenterX = entry?.centerX ?? currentCenterX;
currentCenterY = entry?.centerY ?? currentCenterY;
if (updated.centerX !== currentCenterX || updated.centerY !== currentCenterY) {
layoutRef.current.set(state.docId, { ...updated, centerX: currentCenterX, centerY: currentCenterY });
}
}
}
state.lastClientX = event.clientX;
state.lastClientY = event.clientY;
state.lastTimestamp = currentTimestamp;
integrateRotation(state, dt, torque, dampingOverride);
if (!collidedWithEdge) {
const rotationAfter = state.rotation || 0;
if (rotationAfter !== rotationBeforeIntegration) {
const rotationAfterRad = (rotationAfter * Math.PI) / 180;
const cosAfter = Math.cos(rotationAfterRad);
const sinAfter = Math.sin(rotationAfterRad);
const rotatedOffsetXAfter =
state.localPointerOffsetX * cosAfter - state.localPointerOffsetY * sinAfter;
const rotatedOffsetYAfter =
state.localPointerOffsetX * sinAfter + state.localPointerOffsetY * cosAfter;
const desiredCenterXAfter = pointerCanvasX - rotatedOffsetXAfter;
const desiredCenterYAfter = pointerCanvasY - rotatedOffsetYAfter;
const absCosAfter = Math.abs(cosAfter);
const absSinAfter = Math.abs(sinAfter);
const rotatedHalfWidthAfter = absCosAfter * halfWidth + absSinAfter * halfHeight;
const rotatedHalfHeightAfter = absSinAfter * halfWidth + absCosAfter * halfHeight;
const minCenterXAfter = canvasPadding + rotatedHalfWidthAfter;
const maxCenterXAfter = Math.max(
minCenterXAfter,
canvasWidth - canvasPadding - rotatedHalfWidthAfter,
);
const minCenterYAfter = canvasPadding + rotatedHalfHeightAfter;
const maxCenterYAfter = Math.max(
minCenterYAfter,
canvasHeight - canvasPadding - rotatedHalfHeightAfter,
);
const correctedCenterX = clamp(
desiredCenterXAfter,
minCenterXAfter,
maxCenterXAfter,
);
const correctedCenterY = clamp(
desiredCenterYAfter,
minCenterYAfter,
maxCenterYAfter,
);
if (
Math.abs(correctedCenterX - currentCenterX) > 0.01 ||
Math.abs(correctedCenterY - currentCenterY) > 0.01
) {
const entryAfter = layoutRef.current.get(state.docId);
if (entryAfter) {
const adjustedEntry = {
...entryAfter,
centerX: correctedCenterX,
centerY: correctedCenterY,
};
layoutRef.current.set(state.docId, adjustedEntry);
const nodeAfter = itemRefs.current.get(state.docId);
if (nodeAfter) {
nodeAfter.style.transform = formatTransform(
correctedCenterX - state.width / 2,
correctedCenterY - state.height / 2,
state.rotation,
state.dragScale || 1,
);
}
currentCenterX = correctedCenterX;
currentCenterY = correctedCenterY;
}
}
}
}
if (collidedWithEdge) {
const rotationAfter = state.rotation;
const crossedAlignment =
rotationBeforeIntegration > EDGE_ALIGNMENT_EPSILON && rotationAfter < 0
? rotationBeforeIntegration - rotationAfter > EDGE_ALIGNMENT_EPSILON
: rotationBeforeIntegration < -EDGE_ALIGNMENT_EPSILON && rotationAfter > 0
? rotationAfter - rotationBeforeIntegration > EDGE_ALIGNMENT_EPSILON
: false;
const nearAlignment = Math.abs(rotationAfter) <= EDGE_ALIGNMENT_EPSILON;
if (crossedAlignment || nearAlignment) {
state.restRotation = 0;
state.dynamicRotation = 0;
state.angularVelocity = 0;
state.rotation = 0;
const updatedEntry = layoutRef.current.get(state.docId);
if (updatedEntry) {
const alignedEntry = { ...updatedEntry, rotation: 0 };
layoutRef.current.set(state.docId, alignedEntry);
const node = itemRefs.current.get(state.docId);
if (node) {
node.style.transform = formatTransform(
alignedEntry.centerX - state.width / 2,
alignedEntry.centerY - state.height / 2,
0,
state.dragScale || 1,
);
}
}
}
}
const rotationForOffsetDeg = state.rotation || 0;
const rotationForOffsetRad = (rotationForOffsetDeg * Math.PI) / 180;
const cosInverse = Math.cos(-rotationForOffsetRad);
@@ -609,8 +777,9 @@ const useDocumentDrag = () => {
defaultCanvasHeight,
defaultCanvasWidth,
containerRef,
integrateRotation,
layoutRef,
itemRefs,
applyTransform,
recalcVisibleDocIds,
debugDrag,
],
@@ -619,51 +788,61 @@ const useDocumentDrag = () => {
const handlePointerUp = useCallback(
(event) => {
const state = dragStateRef.current;
if (state && state.pointerId === event.pointerId) {
if (state.moved) {
const inertiaState = {
restRotation: state.restRotation,
dynamicRotation: state.dynamicRotation,
angularVelocity: state.angularVelocity,
rotation: state.rotation,
width: state.width,
height: state.height,
dragScale: state.dragScale || 1,
};
const docId = state.docId;
finishDrag(event.pointerId);
startInertiaAnimation(docId, inertiaState);
return;
}
const docId = state.docId;
const metaPressed = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
if (!metaPressed) {
bringToFront(docId);
}
const originInfo = {
rotation: state.rotation || 0,
scale: state.baseScale || 1,
width: state.width,
height: state.height,
};
const docKey = docId != null ? String(docId) : null;
const doc = docKey ? documentLookup.get(docKey) : null;
tapHandler(event, {
docId,
originInfo,
docTitle: doc?.title || 'document',
});
if (!state || state.pointerId !== event.pointerId) {
finishDrag(event.pointerId);
return;
}
if (state.isGroup) {
finalizeGroupDrag(state);
finishDrag(event.pointerId);
recalcVisibleDocIds();
return;
}
if (state.moved) {
const inertiaState = {
restRotation: state.restRotation,
dynamicRotation: state.dynamicRotation,
angularVelocity: state.angularVelocity,
rotation: state.rotation,
width: state.width,
height: state.height,
dragScale: state.dragScale || 1,
};
const docId = state.docId;
finishDrag(event.pointerId);
startInertiaAnimation(docId, inertiaState);
return;
}
const docId = state.docId;
const metaPressed = event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;
if (!metaPressed) {
bringToFront(docId);
}
const originInfo = {
rotation: state.rotation || 0,
scale: state.baseScale || 1,
width: state.width,
height: state.height,
};
const docKey = docId != null ? String(docId) : null;
const doc = docKey ? documentLookup.get(docKey) : null;
tapHandler(event, {
docId,
originInfo,
docTitle: doc?.title || 'document',
});
finishDrag(event.pointerId);
},
[
bringToFront,
documentLookup,
finishDrag,
finalizeGroupDrag,
startInertiaAnimation,
recalcVisibleDocIds,
tapHandler,
],
);
@@ -672,6 +851,13 @@ const useDocumentDrag = () => {
(event) => {
const state = dragStateRef.current;
if (state && state.pointerId === event.pointerId && state.moved) {
if (state.isGroup) {
finalizeGroupDrag(state);
finishDrag(event.pointerId);
recalcVisibleDocIds();
return;
}
const inertiaState = {
restRotation: state.restRotation,
dynamicRotation: state.dynamicRotation,
@@ -688,7 +874,7 @@ const useDocumentDrag = () => {
}
finishDrag(event.pointerId);
},
[finishDrag, startInertiaAnimation],
[finalizeGroupDrag, finishDrag, recalcVisibleDocIds, startInertiaAnimation],
);
return {