fix(dragdrop): unify tree+tab drop zone logic, fix cross-pane transfer
CI / lint (push) Failing after 9s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Successful in 18s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
CI / lint (push) Failing after 9s
CI / test (push) Has been skipped
CI / build (push) Has been skipped
CI / e2e (push) Has been skipped
CI / security (push) Successful in 18s
Desktop Build / build-windows (push) Has been cancelled
Desktop Build / build-linux (push) Has been cancelled
Three fixes for drag-and-drop in split view (#75): 1. Tree drag-to-split: edge zone detection now runs BEFORE the tree/tab dispatch, so dragging a sidebar file to the right/bottom edge of a single pane correctly creates a split and opens the file in the new pane (was: tree check returned early before edge zone logic). 2. Cross-pane transfer consistency: transferTab() now tracks pane count before the transfer — if closePane() fires inside transferTabTo() (source pane becomes empty), the DOM is rebuilt and the original targetTm reference is stale. After close, re-lookup the tab manager that actually holds the transferred tab before _renderTabs()+activate. 3. dragdrop.js flicker: onDragLeave() now checks relatedTarget instead of unconditionally clearing highlights on every child boundary.
This commit is contained in:
Vendored
+3
-2
@@ -196,10 +196,11 @@ function onDragOver(e) {
|
||||
}
|
||||
|
||||
function onDragLeave(e) {
|
||||
// Only clear when actually leaving the container, not entering a child
|
||||
// Only clear when actually leaving the container, not entering a child.
|
||||
// Use relatedTarget: if it's still inside #vault-tree, keep highlights.
|
||||
const container = document.getElementById('vault-tree');
|
||||
if (!container) return;
|
||||
// Use a small delay to avoid flickering when moving between children
|
||||
if (e.relatedTarget && container.contains(e.relatedTarget)) return;
|
||||
clearAllHighlights();
|
||||
}
|
||||
|
||||
|
||||
+71
-29
@@ -659,12 +659,29 @@ const PaneManager = {
|
||||
const fromTm = this.tabManagers[fromPaneId];
|
||||
const toTm = this.tabManagers[toPaneId];
|
||||
if (!fromTm || !toTm) return false;
|
||||
|
||||
// Track state BEFORE transfer (closePane may rebuild DOM)
|
||||
const paneCountBefore = this.panes.length;
|
||||
|
||||
const ok = fromTm.transferTabTo(tabId, toTm);
|
||||
if (ok) {
|
||||
toTm._renderTabs();
|
||||
toTm.activate(tabId);
|
||||
this._persist();
|
||||
if (!ok) return false;
|
||||
|
||||
// If source pane was auto-closed, DOM was rebuilt — re-lookup target
|
||||
let actualTm = toTm;
|
||||
if (this.panes.length < paneCountBefore) {
|
||||
// Find which pane manager now holds the tab
|
||||
for (const [pid, tm] of Object.entries(this.tabManagers)) {
|
||||
if (tm._tabs.find(t => t.id === tabId)) {
|
||||
actualTm = tm;
|
||||
console.log('[PaneManager] source closed, tab now in pane', pid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
actualTm._renderTabs();
|
||||
actualTm.activate(tabId);
|
||||
this._persist();
|
||||
return ok;
|
||||
},
|
||||
|
||||
@@ -1060,15 +1077,53 @@ const PaneManager = {
|
||||
|
||||
if (!raw) return;
|
||||
|
||||
// Handle tree drag (file from sidebar)
|
||||
// --- Determine drop zone ---
|
||||
const rect = container.getBoundingClientRect();
|
||||
const relX = e.clientX - rect.left;
|
||||
const relY = e.clientY - rect.top;
|
||||
const isSingle = !this.isSplit();
|
||||
const onRightEdge = isSingle && relX > rect.width * 0.72;
|
||||
const onBottomEdge = isSingle && relY > rect.height * 0.72;
|
||||
|
||||
console.log('[PaneManager] drop at', relX.toFixed(0), relY.toFixed(0),
|
||||
'rect', rect.width.toFixed(0), 'x', rect.height.toFixed(0),
|
||||
'isSingle', isSingle, 'paneId', paneId,
|
||||
'onRightEdge', onRightEdge, 'onBottomEdge', onBottomEdge);
|
||||
|
||||
// --- Tree drag (file from sidebar) ---
|
||||
if (raw.startsWith('tree:')) {
|
||||
try {
|
||||
const treeData = JSON.parse(raw.substring(5));
|
||||
if (treeData.vault && treeData.path) {
|
||||
console.log('[PaneManager] tree drop → opening in pane', paneId);
|
||||
const tm = window.PaneManager.getTabManager(paneId);
|
||||
if (!treeData.vault || !treeData.path) return;
|
||||
console.log('[PaneManager] tree drop vault=', treeData.vault, 'path=', treeData.path);
|
||||
|
||||
if (onRightEdge) {
|
||||
console.log('[PaneManager] → tree splitRight');
|
||||
this.splitRight();
|
||||
setTimeout(() => {
|
||||
const newPaneId = this.panes.length - 1;
|
||||
const tm = this.getTabManager(newPaneId);
|
||||
if (tm) {
|
||||
this.setActivePane(newPaneId);
|
||||
tm.openPreview(treeData.vault, treeData.path);
|
||||
}
|
||||
}, 200);
|
||||
} else if (onBottomEdge) {
|
||||
console.log('[PaneManager] → tree splitDown');
|
||||
this.splitDown();
|
||||
setTimeout(() => {
|
||||
const newPaneId = this.panes.length - 1;
|
||||
const tm = this.getTabManager(newPaneId);
|
||||
if (tm) {
|
||||
this.setActivePane(newPaneId);
|
||||
tm.openPreview(treeData.vault, treeData.path);
|
||||
}
|
||||
}, 200);
|
||||
} else {
|
||||
console.log('[PaneManager] → tree open in pane', paneId);
|
||||
const tm = this.getTabManager(paneId);
|
||||
if (tm) {
|
||||
window.PaneManager.setActivePane(paneId);
|
||||
this.setActivePane(paneId);
|
||||
tm.openPreview(treeData.vault, treeData.path);
|
||||
}
|
||||
}
|
||||
@@ -1076,39 +1131,26 @@ const PaneManager = {
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle tab drag
|
||||
// --- Tab drag ---
|
||||
let dragData;
|
||||
try { dragData = JSON.parse(raw); } catch (e2) { console.log('[PaneManager] JSON parse error:', e2); return; }
|
||||
if (dragData.paneId == null || dragData.tabId == null) { console.log('[PaneManager] missing paneId/tabId'); return; }
|
||||
|
||||
// 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();
|
||||
|
||||
console.log('[PaneManager] drop at', relX.toFixed(0), relY.toFixed(0),
|
||||
'rect', rect.width.toFixed(0), 'x', rect.height.toFixed(0),
|
||||
'isSingle', isSingle, 'paneId', paneId, 'srcPane', dragData.paneId);
|
||||
|
||||
if (isSingle && relX > rect.width * 0.72) {
|
||||
if (onRightEdge) {
|
||||
console.log('[PaneManager] → splitRight');
|
||||
this.splitRight();
|
||||
// Transfer after DOM settles
|
||||
const transfer = () => {
|
||||
setTimeout(() => {
|
||||
const newPaneId = this.panes.length - 1;
|
||||
console.log('[PaneManager] transferring tab', dragData.tabId, 'to pane', newPaneId);
|
||||
this.transferTab(dragData.paneId, dragData.tabId, newPaneId);
|
||||
};
|
||||
setTimeout(transfer, 200);
|
||||
} else if (isSingle && relY > rect.height * 0.72) {
|
||||
}, 200);
|
||||
} else if (onBottomEdge) {
|
||||
console.log('[PaneManager] → splitDown');
|
||||
this.splitDown();
|
||||
const transfer = () => {
|
||||
setTimeout(() => {
|
||||
const newPaneId = this.panes.length - 1;
|
||||
this.transferTab(dragData.paneId, dragData.tabId, newPaneId);
|
||||
};
|
||||
setTimeout(transfer, 200);
|
||||
}, 200);
|
||||
} else if (dragData.paneId !== paneId) {
|
||||
console.log('[PaneManager] → cross-pane transfer', dragData.paneId, '→', paneId);
|
||||
this.transferTab(dragData.paneId, dragData.tabId, paneId);
|
||||
|
||||
Reference in New Issue
Block a user