feat: a dragged tab held over another tab's title opens it to be dropped into; let go with nothing done, the dragged tab comes back

This commit is contained in:
redtea committed 2026-10-16 10:21:37 +03:00
1 parent 5216aed237
commit 3b7a6271ac
1 file changed
+20
+20
View File
@@ -348,6 +348,8 @@
} }
function beginDrag() { function beginDrag() {
drag.on = true; drag.on = true;
// The tab open when the drag began: it comes back if nothing is done.
drag.origActive = active;
const src = drag.kind === 'tab' ? drag.obj.el : drag.obj.el.querySelector('.pane-head'); const src = drag.kind === 'tab' ? drag.obj.el : drag.obj.el.querySelector('.pane-head');
const g = document.createElement('div'); const g = document.createElement('div');
g.className = 'drag-ghost ' + drag.kind; g.className = 'drag-ghost ' + drag.kind;
@@ -390,6 +392,16 @@
const stripShown = document.body.classList.contains('multi') || document.body.classList.contains('multi-hint'); const stripShown = document.body.classList.contains('multi') || document.body.classList.contains('multi-hint');
const inStrip = stripShown && ev.clientY >= strip.top - 6 && ev.clientY <= strip.bottom + 10; const inStrip = stripShown && ev.clientY >= strip.top - 6 && ev.clientY <= strip.bottom + 10;
const inArea = active && (() => { const r = active.view.getBoundingClientRect(); return ev.clientX >= r.left && ev.clientX <= r.right && ev.clientY >= r.top && ev.clientY <= r.bottom; })(); const inArea = active && (() => { const r = active.view.getBoundingClientRect(); return ev.clientX >= r.left && ev.clientX <= r.right && ev.clientY >= r.top && ev.clientY <= r.bottom; })();
// A tab held over another tab's title for a moment opens that one, so
// the dragged tab can be dropped into it.
if (drag.kind === 'tab') {
const over = inStrip ? tabs.find(t => t !== drag.obj && t !== active && (() => { const r = t.el.getBoundingClientRect(); return ev.clientX >= r.left && ev.clientX <= r.right; })()) : null;
if (over !== drag.overTab) {
clearTimeout(drag.overTimer);
drag.overTab = over;
if (over) drag.overTimer = setTimeout(() => { if (drag && drag.overTab === over) activate(over); }, 450);
}
}
if (inStrip) { if (inStrip) {
const s = stripIndex(ev.clientX); const s = stripIndex(ev.clientX);
mark.hidden = false; mark.style.left = s.mx + 'px'; mark.hidden = false; mark.style.left = s.mx + 'px';
@@ -425,6 +437,14 @@
const d = drag; const d = drag;
drag = null; drag = null;
if (!d.on) return; if (!d.on) return;
clearTimeout(d.overTimer);
// Dropped with nothing done (or only moved along the strip): back to the
// tab that was dragged, if another one was opened meanwhile.
const tg0 = d.target;
if (d.kind === 'tab' && d.origActive && active !== d.origActive && !(tg0 && tg0.kind === 'merge')) {
const back = d.origActive;
setTimeout(() => activate(back), 0);
}
d.ghost.remove(); d.ghost.remove();
d.src.classList.remove('dragging'); d.src.classList.remove('dragging');
document.body.classList.remove('dnd', 'multi-hint'); document.body.classList.remove('dnd', 'multi-hint');