diff --git a/app/javascript/controllers/dashboard_sortable_controller.js b/app/javascript/controllers/dashboard_sortable_controller.js index 6ce64b470..67d3cfa4e 100644 --- a/app/javascript/controllers/dashboard_sortable_controller.js +++ b/app/javascript/controllers/dashboard_sortable_controller.js @@ -12,6 +12,7 @@ export default class extends Controller { this.draggedElement = null; this.placeholder = null; this.touchStartY = 0; + this.currentTouchX = 0; this.currentTouchY = 0; this.isTouching = false; this.keyboardGrabbedElement = null; @@ -37,7 +38,7 @@ export default class extends Controller { event.preventDefault(); event.dataTransfer.dropEffect = "move"; - const afterElement = this.getDragAfterElement(event.clientY); + const afterElement = this.getDragAfterElement(event.clientX, event.clientY); const container = this.element; this.clearPlaceholders(); @@ -53,7 +54,7 @@ export default class extends Controller { event.preventDefault(); event.stopPropagation(); - const afterElement = this.getDragAfterElement(event.clientY); + const afterElement = this.getDragAfterElement(event.clientX, event.clientY); const container = this.element; if (afterElement == null) { @@ -81,7 +82,9 @@ export default class extends Controller { if (section.getAttribute("draggable") === "false") return; this.pendingSection = section; + this.touchStartX = event.touches[0].clientX; this.touchStartY = event.touches[0].clientY; + this.currentTouchX = this.touchStartX; this.currentTouchY = this.touchStartY; this.holdActivated = false; @@ -110,9 +113,10 @@ export default class extends Controller { if (!this.holdActivated || !this.isTouching || !this.draggedElement) return; event.preventDefault(); + this.currentTouchX = event.touches[0].clientX; this.currentTouchY = event.touches[0].clientY; - const afterElement = this.getDragAfterElement(this.currentTouchY); + const afterElement = this.getDragAfterElement(this.currentTouchX, this.currentTouchY); this.clearPlaceholders(); if (afterElement == null) { @@ -130,7 +134,7 @@ export default class extends Controller { return; } - const afterElement = this.getDragAfterElement(this.currentTouchY); + const afterElement = this.getDragAfterElement(this.currentTouchX, this.currentTouchY); const container = this.element; if (afterElement == null) { @@ -240,23 +244,32 @@ export default class extends Controller { } } - getDragAfterElement(y) { - const draggableElements = [ - ...this.sectionTargets.filter((section) => section !== this.draggedElement), - ]; + getDragAfterElement(pointerX, pointerY) { + const draggableElements = this.sectionTargets.filter( + (section) => section !== this.draggedElement, + ); - return draggableElements.reduce( - (closest, child) => { - const box = child.getBoundingClientRect(); - const offset = y - box.top - box.height / 2; + if (draggableElements.length === 0) return null; - if (offset < 0 && offset > closest.offset) { - return { offset: offset, element: child }; - } - return closest; - }, - { offset: Number.NEGATIVE_INFINITY }, - ).element; + let closest = null; + let minDistance = Number.POSITIVE_INFINITY; + + draggableElements.forEach((child) => { + const rect = child.getBoundingClientRect(); + const centerX = rect.left + rect.width / 2; + const centerY = rect.top + rect.height / 2; + + const dx = pointerX - centerX; + const dy = pointerY - centerY; + const distance = Math.sqrt(dx * dx + dy * dy); + + if (distance < minDistance) { + minDistance = distance; + closest = child; + } + }); + + return closest; } showPlaceholder(element, position) { diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 32b76a0ae..a523dbeaa 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -126,7 +126,7 @@ end %> <% end %> <%# SHARED - Main content %> - <%= tag.main class: class_names("grow overflow-y-auto px-3 lg:px-10 w-full mx-auto max-w-5xl"), data: { app_layout_target: "content", viewport_target: "content" } do %> + <%= tag.main class: class_names("grow overflow-y-auto px-3 lg:px-10 w-full mx-auto"), data: { app_layout_target: "content", viewport_target: "content" } do %> <% unless intro_mode %>