From bd3a0adfed8f7caf1b5f662d9ac3bb519cd6d285 Mon Sep 17 00:00:00 2001 From: Miguel Camba Date: Fri, 25 Oct 2024 18:19:13 +0200 Subject: [PATCH] Improve flow detection when elements have one element is shorter than its siblings and vertically centered. --- assets/svelte/utils/drag-helpers.ts | 63 ++++++++++++++++------------- 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/assets/svelte/utils/drag-helpers.ts b/assets/svelte/utils/drag-helpers.ts index f65df57f..829335f8 100644 --- a/assets/svelte/utils/drag-helpers.ts +++ b/assets/svelte/utils/drag-helpers.ts @@ -24,44 +24,49 @@ export function mouseDiff(mouseMovement: CoordsDiff): Coords { } } -function centerInAxis(rect: DOMRect, axis: "x" | "y"): number { - return axis === "x" ? rect.x + rect.width / 2 : rect.y + rect.height / 2 -} // Detects if elements flow generally in an horizontal direction, a vertical one or // both (e.g. they form a grid or overflow to the the next line) function detectFlow(rects: DOMRect[]) { - let horizontal = false - let vertical = false + // Sort elements by their left position (to analyze horizontal arrangement) + const sortedByLeft = [...rects].sort((a, b) => a.left - b.left); + // Sort elements by their top position (to analyze vertical arrangement) + const sortedByTop = [...rects].sort((a, b) => a.top - b.top); + + // Calculate the average horizontal and vertical spacing between elements + const avgHorizontalDiff = getAverageDifference(sortedByLeft, 'left'); + const avgVerticalDiff = getAverageDifference(sortedByTop, 'top'); + + // Determine the dominant flow + if (avgHorizontalDiff > avgVerticalDiff) { + // Check if all elements are roughly centered vertically + const isCenteredHorizontally = checkVerticalCenterAlignment(sortedByLeft); + return isCenteredHorizontally ? 'horizontal' : 'both'; + } else if (avgVerticalDiff > avgHorizontalDiff) { + return 'vertical'; + } + return 'both'; +} - const threshold = 5 +// Helper to calculate average difference for a given property (top or left) +function getAverageDifference(rects, property) { + let totalDiff = 0; for (let i = 1; i < rects.length; i++) { - let prevRect = rects[i - 1] - let currentRect = rects[i] - - // Use the centers in each axis to determine flow, not the top/left corners because - // when elements have different heights/widths but are vertically/horizontally aligned - // their corners might not be aligned but the general direction is still clear - let xChange = Math.abs(centerInAxis(currentRect, "x") - centerInAxis(prevRect, "x")) - let yChange = Math.abs(centerInAxis(currentRect, "y") - centerInAxis(prevRect, "y")) - - // Check for horizontal flow: significant x change with minimal y change - if (xChange > threshold && yChange < threshold) { - horizontal = true - } - if (yChange > threshold) { - vertical = true - } + totalDiff += Math.abs(rects[i][property] - rects[i - 1][property]); } + return totalDiff / (rects.length - 1); +} - if (horizontal && vertical) { - return "both" - } else if (horizontal) { - return "horizontal" - } else { - return "vertical" - } +// Helper to check if elements are centered vertically relative to each other +function checkVerticalCenterAlignment(rects) { + const centers = rects.map(rect => (rect.top + rect.bottom) / 2); + const minCenter = Math.min(...centers); + const maxCenter = Math.max(...centers); + return (maxCenter - minCenter) < 17; // tolerance for alignment, adjust as needed } + + + // Determines if the drag of the current element should vertical or horizontal based on the // flow of its siblings // If prefers a practical approach, checking if the last element is further down (or right)