anvilsign in

collin/anvil · 273fd302

fix: make board drag reliable on touch (document listeners, not capture)

Collin Richards · 2026-06-10 18:37 UTC · 273fd302197f146bb5d74bf62cc8c92d09e66e95 · parent 7e03cd50 · browse files

modifiedcrates/anvil-web/src/todomd.rs+31 −19
⋯ 694 unchanged lines
695695 var url = board.dataset.moveUrl, tip = board.dataset.tip, csrf = board.dataset.csrf;
696696 var card = null, fromSection = null, grip = null, pid = null, moved = false;
697697
698+ // The column whose box contains (x, y) — by geometry, so it works for both
699+ // the horizontal desktop layout and the stacked mobile layout, and doesn't
700+ // depend on elementFromPoint (which the dragged card can shadow).
701+ function colAt(x, y){
702+ var cols = board.querySelectorAll('.col');
703+ for (var i = 0; i < cols.length; i++){
704+ var b = cols[i].getBoundingClientRect();
705+ if (x >= b.left && x <= b.right && y >= b.top && y <= b.bottom) return cols[i];
706+ }
707+ return null;
708+ }
698709 function afterCard(col, y){
699710 var cards = [].slice.call(col.querySelectorAll('.card:not(.dragging)'));
700711 var best = null, bestOffset = -Infinity;
⋯ 5 unchanged lines
706717 return best;
707718 }
708719 function onMove(e){
709- if (!card) return;
710- e.preventDefault();
720+ if (!card || e.pointerId !== pid) return;
721+ e.preventDefault(); // stop the page scrolling mid-drag
711722 moved = true;
712- var under = document.elementFromPoint(e.clientX, e.clientY);
713- var col = (under && under.closest && under.closest('.col')) || card.closest('.col');
723+ var col = colAt(e.clientX, e.clientY) || card.closest('.col');
714724 var after = afterCard(col, e.clientY);
715725 if (after == null) col.appendChild(card);
716726 else col.insertBefore(card, after);
717727 }
718- function onUp(){
719- if (!card) return;
720- grip.removeEventListener('pointermove', onMove);
721- grip.removeEventListener('pointerup', onUp);
722- grip.removeEventListener('pointercancel', onUp);
728+ function onUp(e){
729+ if (!card || (e && e.pointerId !== pid)) return;
730+ document.removeEventListener('pointermove', onMove);
731+ document.removeEventListener('pointerup', onUp);
732+ document.removeEventListener('pointercancel', onUp);
723733 var c = card;
724734 card = null;
725735 c.classList.remove('dragging');
736+ try { grip.releasePointerCapture(pid); } catch (_) {}
726737 if (!moved) return; // a tap or cancelled gesture — nothing to commit
727738 var col = c.closest('.col');
728739 var cards = [].slice.call(col.querySelectorAll('.card'));
⋯ 16 unchanged lines
745756 board.addEventListener('pointerdown', function(e){
746757 var g = e.target.closest('.card-grip');
747758 if (!g) return;
748- card = g.closest('.card');
749- if (!card) return;
759+ var c = g.closest('.card');
760+ if (!c) return;
750761 e.preventDefault();
751- grip = g;
752- pid = e.pointerId;
753- fromSection = card.closest('.col').dataset.section;
754- card.classList.add('dragging');
755- grip.setPointerCapture(pid);
756- grip.addEventListener('pointermove', onMove);
757- grip.addEventListener('pointerup', onUp);
758- grip.addEventListener('pointercancel', onUp);
762+ card = c; grip = g; pid = e.pointerId; moved = false;
763+ fromSection = c.closest('.col').dataset.section;
764+ c.classList.add('dragging');
765+ // Capture is best-effort (it can throw on some browsers); the move/up
766+ // listeners live on `document` so the drag works whether or not it sticks.
767+ try { g.setPointerCapture(e.pointerId); } catch (_) {}
768+ document.addEventListener('pointermove', onMove, { passive: false });
769+ document.addEventListener('pointerup', onUp);
770+ document.addEventListener('pointercancel', onUp);
759771 });
760772 })();
761773 "#;
⋯ 287 unchanged lines