From 069d119d032d7ab762833d562803b4194fd4392b Mon Sep 17 00:00:00 2001 From: bruno Date: Mon, 27 Jul 2026 22:42:45 -0400 Subject: [PATCH] =?UTF-8?q?fix:=20drag=20&=20drop=20corrig=C3=A9=20?= =?UTF-8?q?=E2=80=94=203=20bugs=20critiques=20r=C3=A9solus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. types.includes → Array.from(types).includes (DOMStringList n'a pas .includes) 2. dragleave: compteur _dragCounter empêche clear sur child enter/leave 3. drag-to-split: zone recalculée dans drop handler (plus de closure corrompue) 4. drop: stopPropagation + skip same-paneId dans container handler 5. dragenter: appel preventDefault pour accepter le drop dès l'entrée --- frontend/js/pane-manager.js | 64 +++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 27 deletions(-) diff --git a/frontend/js/pane-manager.js b/frontend/js/pane-manager.js index 8276744..3a70153 100644 --- a/frontend/js/pane-manager.js +++ b/frontend/js/pane-manager.js @@ -734,11 +734,18 @@ const PaneManager = { /** Add dragover/drop on a pane container for cross-pane transfers + drag-to-split */ _setupPaneDropZone(container, paneId) { - let _edgeZone = null; // 'right' | 'bottom' | null + let _dragCounter = 0; + + container.addEventListener('dragenter', (e) => { + const types = Array.from(e.dataTransfer.types || []); + if (!types.includes('application/obsigate-tab')) return; + _dragCounter++; + e.preventDefault(); + }); container.addEventListener('dragover', (e) => { - const raw = e.dataTransfer.types.includes('application/obsigate-tab'); - if (!raw) return; + const types = Array.from(e.dataTransfer.types || []); + if (!types.includes('application/obsigate-tab')) return; e.preventDefault(); e.dataTransfer.dropEffect = 'move'; @@ -750,37 +757,35 @@ const PaneManager = { // Clean up previous indicators container.querySelectorAll('.pane-drop-zone-indicator').forEach(d => d.remove()); - _edgeZone = null; + const tabBar = container.querySelector('.tab-bar'); + if (tabBar) tabBar.classList.remove('drop-target'); - if (isSingle) { - if (relX > rect.width * 0.75) { - _edgeZone = 'right'; - } else if (relY > rect.height * 0.75) { - _edgeZone = 'bottom'; - } - } - - if (_edgeZone) { - // Show edge zone indicator + if (isSingle && relX > rect.width * 0.72) { const indicator = document.createElement('div'); - indicator.className = 'pane-drop-zone-indicator pane-drop-zone-' + _edgeZone; + indicator.className = 'pane-drop-zone-indicator pane-drop-zone-right'; + container.appendChild(indicator); + } else if (isSingle && relY > rect.height * 0.72) { + const indicator = document.createElement('div'); + indicator.className = 'pane-drop-zone-indicator pane-drop-zone-bottom'; container.appendChild(indicator); } else { // Show tab-bar highlight for cross-pane transfer - const tabBar = container.querySelector('.tab-bar'); if (tabBar) tabBar.classList.add('drop-target'); } }); container.addEventListener('dragleave', (e) => { + _dragCounter--; + if (_dragCounter > 0) return; // Still inside container (entered a child) container.querySelectorAll('.pane-drop-zone-indicator').forEach(d => d.remove()); const tabBar = container.querySelector('.tab-bar'); if (tabBar) tabBar.classList.remove('drop-target'); - _edgeZone = null; }); container.addEventListener('drop', (e) => { e.preventDefault(); + e.stopPropagation(); + _dragCounter = 0; container.querySelectorAll('.pane-drop-zone-indicator').forEach(d => d.remove()); const tabBar = container.querySelector('.tab-bar'); if (tabBar) tabBar.classList.remove('drop-target'); @@ -789,28 +794,33 @@ const PaneManager = { if (!raw) return; try { const dragData = JSON.parse(raw); + if (dragData.paneId === paneId) return; // Same pane, let tab handler deal with it - if (_edgeZone === 'right') { - // Drag to right edge → split right with this tab + // Recalculate edge zone from drop coordinates + const rect = container.getBoundingClientRect(); + const relX = e.clientX - rect.left; + const relY = e.clientY - rect.top; + const isSingle = !this.isSplit(); + + if (isSingle && relX > rect.width * 0.72) { + // Dropped on right edge → split right with this tab this.splitRight(); - // Transfer the dragged tab to the new pane setTimeout(() => { const newPaneId = this.panes.length - 1; this.transferTab(dragData.paneId, dragData.tabId, newPaneId); - }, 100); - } else if (_edgeZone === 'bottom') { - // Drag to bottom edge → split down with this tab + }, 150); + } else if (isSingle && relY > rect.height * 0.72) { + // Dropped on bottom edge → split down with this tab this.splitDown(); setTimeout(() => { const newPaneId = this.panes.length - 1; this.transferTab(dragData.paneId, dragData.tabId, newPaneId); - }, 100); - } else if (dragData.paneId !== paneId) { - // Cross-pane transfer (dropped on tab bar or content area of another pane) + }, 150); + } else { + // Cross-pane transfer (dropped on tab bar or empty area of another pane) this.transferTab(dragData.paneId, dragData.tabId, paneId); } } catch (e2) { /* ignore */ } - _edgeZone = null; }); },