fix
This commit is contained in:
@@ -35,9 +35,6 @@ const CARD_MIN = 240;
|
|||||||
const CARD_MAX = 340;
|
const CARD_MAX = 340;
|
||||||
const TAG_REMOVE_DISTANCE = 160;
|
const TAG_REMOVE_DISTANCE = 160;
|
||||||
const STACK_HIT_EPSILON = 4;
|
const STACK_HIT_EPSILON = 4;
|
||||||
const STACK_CENTER_TOLERANCE = 0.35;
|
|
||||||
const STACK_CENTER_MIN = 32;
|
|
||||||
const STACK_ROTATION_TOLERANCE = 15;
|
|
||||||
|
|
||||||
const DEBUG_DRAG = false;
|
const DEBUG_DRAG = false;
|
||||||
const DEBUG_FOCUS = true;
|
const DEBUG_FOCUS = true;
|
||||||
@@ -1953,7 +1950,6 @@ const DesktopWorkspaceView = () => {
|
|||||||
onClearSelection,
|
onClearSelection,
|
||||||
detailPanelOpen,
|
detailPanelOpen,
|
||||||
onCloseDetailPanel,
|
onCloseDetailPanel,
|
||||||
documentLookup,
|
|
||||||
} = useDesktopContext();
|
} = useDesktopContext();
|
||||||
|
|
||||||
const { handlePointerDown, handlePointerMove, handlePointerUp, handlePointerCancel } =
|
const { handlePointerDown, handlePointerMove, handlePointerUp, handlePointerCancel } =
|
||||||
@@ -1974,7 +1970,8 @@ const DesktopWorkspaceView = () => {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const hits = [];
|
const candidates = [];
|
||||||
|
|
||||||
items.forEach((doc) => {
|
items.forEach((doc) => {
|
||||||
if (!doc?.id) {
|
if (!doc?.id) {
|
||||||
return;
|
return;
|
||||||
@@ -1983,6 +1980,7 @@ const DesktopWorkspaceView = () => {
|
|||||||
if (!layout) {
|
if (!layout) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const sizeInfo = ensureDocumentSize(doc);
|
const sizeInfo = ensureDocumentSize(doc);
|
||||||
if (!sizeInfo) {
|
if (!sizeInfo) {
|
||||||
return;
|
return;
|
||||||
@@ -2018,122 +2016,66 @@ const DesktopWorkspaceView = () => {
|
|||||||
const halfWidth = width / 2;
|
const halfWidth = width / 2;
|
||||||
const halfHeight = height / 2;
|
const halfHeight = height / 2;
|
||||||
|
|
||||||
if (
|
const containsPointer =
|
||||||
Math.abs(localX) <= halfWidth + STACK_HIT_EPSILON
|
Math.abs(localX) <= halfWidth + STACK_HIT_EPSILON
|
||||||
&& Math.abs(localY) <= halfHeight + STACK_HIT_EPSILON
|
&& Math.abs(localY) <= halfHeight + STACK_HIT_EPSILON;
|
||||||
) {
|
|
||||||
const docKey = String(doc.id);
|
candidates.push({
|
||||||
if (!hits.some((entry) => entry.id === docKey)) {
|
id: String(doc.id),
|
||||||
hits.push({
|
z: Number.isFinite(layout.z) ? layout.z : 0,
|
||||||
id: docKey,
|
centerX,
|
||||||
z: Number.isFinite(layout.z) ? layout.z : 0,
|
centerY,
|
||||||
});
|
rotationDeg,
|
||||||
}
|
width,
|
||||||
}
|
height,
|
||||||
|
halfWidth,
|
||||||
|
halfHeight,
|
||||||
|
localX,
|
||||||
|
localY,
|
||||||
|
marginX: halfWidth - Math.abs(localX),
|
||||||
|
marginY: halfHeight - Math.abs(localY),
|
||||||
|
containsPointer,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!hits.length) {
|
const pointerCandidates = candidates.filter((candidate) => candidate.containsPointer);
|
||||||
|
if (!pointerCandidates.length) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
hits.sort((a, b) => (b.z ?? 0) - (a.z ?? 0));
|
const sortedByZ = [...pointerCandidates].sort((a, b) => (b.z ?? 0) - (a.z ?? 0));
|
||||||
|
const targetKey = targetDocId != null ? String(targetDocId) : sortedByZ[0].id;
|
||||||
|
|
||||||
const targetKey = targetDocId != null ? String(targetDocId) : hits[0].id;
|
const primary = sortedByZ.find((candidate) => candidate.id === targetKey) || sortedByZ[0];
|
||||||
const orderedIds = hits.map((entry) => entry.id);
|
if (!primary) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const radius = Math.max(Math.min(primary.halfWidth, primary.halfHeight) * 1.2, 6);
|
||||||
|
const radiusSquared = radius * radius;
|
||||||
|
|
||||||
|
const selected = candidates
|
||||||
|
.filter((candidate) => {
|
||||||
|
if (!candidate?.id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const dx = candidate.centerX - primary.centerX;
|
||||||
|
const dy = candidate.centerY - primary.centerY;
|
||||||
|
return dx * dx + dy * dy <= radiusSquared + 1e-4;
|
||||||
|
})
|
||||||
|
.sort((a, b) => (b.z ?? 0) - (a.z ?? 0));
|
||||||
|
|
||||||
if (targetKey) {
|
if (targetKey) {
|
||||||
const targetIndex = orderedIds.indexOf(targetKey);
|
const targetIndex = selected.findIndex((entry) => entry.id === targetKey);
|
||||||
if (targetIndex > 0) {
|
if (targetIndex > 0) {
|
||||||
const [targetEntry] = orderedIds.splice(targetIndex, 1);
|
const [targetEntry] = selected.splice(targetIndex, 1);
|
||||||
orderedIds.unshift(targetEntry);
|
selected.unshift(targetEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryKey = orderedIds[0];
|
return selected
|
||||||
if (!primaryKey) {
|
.map((candidate) => candidate.id)
|
||||||
return orderedIds;
|
.filter((id, index, array) => array.indexOf(id) === index);
|
||||||
}
|
|
||||||
|
|
||||||
const primaryDoc = documentLookup.get(primaryKey) || null;
|
|
||||||
const primaryLayout = primaryDoc
|
|
||||||
? layoutSnapshot.get(primaryDoc.id) ?? layoutRef.current.get(primaryKey)
|
|
||||||
: null;
|
|
||||||
const primarySize = primaryDoc ? ensureDocumentSize(primaryDoc) : null;
|
|
||||||
|
|
||||||
if (!primaryLayout || !primarySize) {
|
|
||||||
return orderedIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
const primaryCenterX = Number(primaryLayout.centerX);
|
|
||||||
const primaryCenterY = Number(primaryLayout.centerY);
|
|
||||||
const primaryRotation = Number(primaryLayout.rotation) || 0;
|
|
||||||
if (!Number.isFinite(primaryCenterX) || !Number.isFinite(primaryCenterY)) {
|
|
||||||
return orderedIds;
|
|
||||||
}
|
|
||||||
|
|
||||||
const centerTolX = Math.max(primarySize.width * STACK_CENTER_TOLERANCE, STACK_CENTER_MIN);
|
|
||||||
const centerTolY = Math.max(primarySize.height * STACK_CENTER_TOLERANCE, STACK_CENTER_MIN);
|
|
||||||
|
|
||||||
const filteredIds = [];
|
|
||||||
|
|
||||||
orderedIds.forEach((docKey, index) => {
|
|
||||||
if (!docKey) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (index === 0 || docKey === targetKey) {
|
|
||||||
filteredIds.push(docKey);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidateDoc = documentLookup.get(docKey) || null;
|
|
||||||
if (!candidateDoc) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidateLayout = layoutSnapshot.get(candidateDoc.id) ?? layoutRef.current.get(docKey);
|
|
||||||
if (!candidateLayout) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidateSize = ensureDocumentSize(candidateDoc);
|
|
||||||
if (!candidateSize) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidateCenterX = Number(candidateLayout.centerX);
|
|
||||||
const candidateCenterY = Number(candidateLayout.centerY);
|
|
||||||
if (!Number.isFinite(candidateCenterX) || !Number.isFinite(candidateCenterY)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dx = Math.abs(candidateCenterX - primaryCenterX);
|
|
||||||
const dy = Math.abs(candidateCenterY - primaryCenterY);
|
|
||||||
if (dx > centerTolX || dy > centerTolY) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const candidateRotation = Number(candidateLayout.rotation) || 0;
|
|
||||||
const rotationDiffRaw = Math.abs(candidateRotation - primaryRotation) % 360;
|
|
||||||
const rotationDiff = rotationDiffRaw > 180 ? 360 - rotationDiffRaw : rotationDiffRaw;
|
|
||||||
if (rotationDiff > STACK_ROTATION_TOLERANCE) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const sizeRatio = candidateSize.width && primarySize.width
|
|
||||||
? Math.min(candidateSize.width, primarySize.width) / Math.max(candidateSize.width, primarySize.width)
|
|
||||||
: 1;
|
|
||||||
const heightRatio = candidateSize.height && primarySize.height
|
|
||||||
? Math.min(candidateSize.height, primarySize.height) / Math.max(candidateSize.height, primarySize.height)
|
|
||||||
: 1;
|
|
||||||
|
|
||||||
if (sizeRatio < 0.55 || heightRatio < 0.55) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
filteredIds.push(docKey);
|
|
||||||
});
|
|
||||||
|
|
||||||
return filteredIds;
|
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
activeTagSet,
|
activeTagSet,
|
||||||
@@ -2142,7 +2084,6 @@ const DesktopWorkspaceView = () => {
|
|||||||
layoutRef,
|
layoutRef,
|
||||||
layoutSnapshot,
|
layoutSnapshot,
|
||||||
containerRef,
|
containerRef,
|
||||||
documentLookup,
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -380,8 +380,7 @@ const useDocumentDrag = () => {
|
|||||||
const localPointerOffsetX = pointerOffsetX * cosInitial - pointerOffsetY * sinInitial;
|
const localPointerOffsetX = pointerOffsetX * cosInitial - pointerOffsetY * sinInitial;
|
||||||
const localPointerOffsetY = pointerOffsetX * sinInitial + pointerOffsetY * cosInitial;
|
const localPointerOffsetY = pointerOffsetX * sinInitial + pointerOffsetY * cosInitial;
|
||||||
|
|
||||||
const stackRandom = () => Math.random();
|
const groupItems = groupDocIds.map((id) => {
|
||||||
const groupItems = groupDocIds.map((id, index) => {
|
|
||||||
const itemDoc = documentLookup.get(id);
|
const itemDoc = documentLookup.get(id);
|
||||||
const itemSize = ensureDocumentSize(itemDoc) || sizeInfo;
|
const itemSize = ensureDocumentSize(itemDoc) || sizeInfo;
|
||||||
const itemWidth = itemSize.width || docWidth;
|
const itemWidth = itemSize.width || docWidth;
|
||||||
@@ -391,10 +390,8 @@ const useDocumentDrag = () => {
|
|||||||
typeof itemEntry?.centerX === 'number' ? itemEntry.centerX : canvasPadding + itemWidth / 2;
|
typeof itemEntry?.centerX === 'number' ? itemEntry.centerX : canvasPadding + itemWidth / 2;
|
||||||
const itemCenterY =
|
const itemCenterY =
|
||||||
typeof itemEntry?.centerY === 'number' ? itemEntry.centerY : canvasPadding + itemHeight / 2;
|
typeof itemEntry?.centerY === 'number' ? itemEntry.centerY : canvasPadding + itemHeight / 2;
|
||||||
const radius = index === 0 ? 0 : 24 + index * 8;
|
const baseOffsetX = itemCenterX - centerX;
|
||||||
const offsetAngle = (index * 1.618 + stackRandom() * 0.5) * Math.PI;
|
const baseOffsetY = itemCenterY - centerY;
|
||||||
const offsetX = Math.cos(offsetAngle) * radius;
|
|
||||||
const offsetY = Math.sin(offsetAngle) * radius;
|
|
||||||
const initialRotation = itemEntry?.rotation ?? 0;
|
const initialRotation = itemEntry?.rotation ?? 0;
|
||||||
const targetRotation = initialRotation;
|
const targetRotation = initialRotation;
|
||||||
return {
|
return {
|
||||||
@@ -403,8 +400,10 @@ const useDocumentDrag = () => {
|
|||||||
height: itemHeight,
|
height: itemHeight,
|
||||||
currentCenterX: itemCenterX,
|
currentCenterX: itemCenterX,
|
||||||
currentCenterY: itemCenterY,
|
currentCenterY: itemCenterY,
|
||||||
offsetX,
|
baseOffsetX,
|
||||||
offsetY,
|
baseOffsetY,
|
||||||
|
offsetX: baseOffsetX,
|
||||||
|
offsetY: baseOffsetY,
|
||||||
initialRotation,
|
initialRotation,
|
||||||
displayRotation: initialRotation,
|
displayRotation: initialRotation,
|
||||||
targetRotation,
|
targetRotation,
|
||||||
@@ -599,14 +598,15 @@ const useDocumentDrag = () => {
|
|||||||
if (isPrimary) {
|
if (isPrimary) {
|
||||||
item.currentCenterX = centerX;
|
item.currentCenterX = centerX;
|
||||||
item.currentCenterY = centerY;
|
item.currentCenterY = centerY;
|
||||||
item.offsetX *= 0.92;
|
item.offsetX = item.baseOffsetX ?? 0;
|
||||||
item.offsetY *= 0.92;
|
item.offsetY = item.baseOffsetY ?? 0;
|
||||||
item.displayRotation = state.rotation ?? item.displayRotation ?? 0;
|
item.displayRotation = state.rotation ?? item.displayRotation ?? 0;
|
||||||
} else {
|
} else {
|
||||||
item.offsetX *= 0.92;
|
const decay = 0.82;
|
||||||
item.offsetY *= 0.92;
|
const currentOffsetX = (item.offsetX ?? item.baseOffsetX ?? 0) * decay;
|
||||||
if (Math.abs(item.offsetX) < 1) item.offsetX = 0;
|
const currentOffsetY = (item.offsetY ?? item.baseOffsetY ?? 0) * decay;
|
||||||
if (Math.abs(item.offsetY) < 1) item.offsetY = 0;
|
item.offsetX = Math.abs(currentOffsetX) < 0.5 ? 0 : currentOffsetX;
|
||||||
|
item.offsetY = Math.abs(currentOffsetY) < 0.5 ? 0 : currentOffsetY;
|
||||||
|
|
||||||
const targetX = centerX + item.offsetX;
|
const targetX = centerX + item.offsetX;
|
||||||
const targetY = centerY + item.offsetY;
|
const targetY = centerY + item.offsetY;
|
||||||
|
|||||||
Reference in New Issue
Block a user