summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-09-07 18:00:04 +0900
committerAdam Malczewski <[email protected]>2026-09-07 18:00:04 +0900
commit48b23a8132537209c338aa04311f8057b1f5f5b5 (patch)
treecf1d2ffb57f5e054ee8ee33481f0951469776b17
parent8c595b2ea88793dfa751efcc927ad1e755bf2d3a (diff)
downloaddispatch-web-48b23a8132537209c338aa04311f8057b1f5f5b5.tar.gz
dispatch-web-48b23a8132537209c338aa04311f8057b1f5f5b5.zip
fix(workspaces): mobile (tall) grid can't scroll — root height collapse + touch capture
Two blockers for scrolling the tall grid on a phone: 1. HomeTileGrid's root was `flex-1` inside a BLOCK parent (the workspace region), so flex-1 did nothing and the root sized to its content; the scroller's `h-full` collapsed to auto and overflow-y-auto never engaged — extra tiles were clipped by the shell's `overflow-hidden` with no way to scroll. The root is now `h-full`, so the scroller gets a definite height and tall mode scrolls (wide mode was unaffected: horizontal overflow scrolls regardless of container height). 2. `setPointerCapture` was called for TOUCH pointers at pointerdown, which can suppress native panning in WebKit. Touch now skips explicit capture: the browser's implicit capture already retargets the press, and after the long-press lift the moved events bubble up to the grid container where the handlers live — scroll gestures keep working and touch drags are intact. Verification: svelte-check 0 errors/0 warnings; vitest 1694/1694; build ok. Needs a human on a real phone: swipe the tall grid when tiles overflow — it must scroll; long-press-and-drag a tile must still reorder.
-rw-r--r--backend-handoff.md7
-rw-r--r--src/features/workspaces/ui/HomeTileGrid.svelte9
2 files changed, 14 insertions, 2 deletions
diff --git a/backend-handoff.md b/backend-handoff.md
index 735d2cd..c15db17 100644
--- a/backend-handoff.md
+++ b/backend-handoff.md
@@ -286,6 +286,13 @@ changes, NO contract changes — the wire types and every endpoint consumed are
elements to the GRID CONTAINER, which `setPointerCapture`s the pointer and never gets
re-parented/`pointer-events: none`/unmounted — so move/up stream to it over empty cells and off
the canvas, and capture can never be lost to a DOM move again.
+- **Mobile (tall) scroll fix**: the grid's root was `flex-1` inside a BLOCK parent (the workspace
+ region), so it sized to its content and the scroller's `h-full` collapsed → `overflow-y-auto`
+ never engaged and the shell's `overflow-hidden` clipped the extra tiles. The root is now `h-full`,
+ giving the scroller a definite height so tall mode scrolls. Also: touch pointers are no longer
+ `setPointerCapture`d at pointerdown (an early touch capture can suppress native panning in
+ WebKit; touch already gets implicit capture and post-lift moves bubble to the container), keeping
+ touch scrolling reliable while drags still work.
- **Drag append bound** (`dragAppendLimit` in `logic/tile-layout.ts`; `cellAt` in
`HomeTileGrid.svelte`): the drop target is clamped to at most 3 columns (wide) / rows (tall)
past the committed content edge (span-aware, measured on the COMMITTED layout so it can't
diff --git a/src/features/workspaces/ui/HomeTileGrid.svelte b/src/features/workspaces/ui/HomeTileGrid.svelte
index d259aea..489c783 100644
--- a/src/features/workspaces/ui/HomeTileGrid.svelte
+++ b/src/features/workspaces/ui/HomeTileGrid.svelte
@@ -160,7 +160,12 @@
if (!tileEl || insideInteractive(e.target)) return;
const id = tileEl.getAttribute("data-tile-id");
if (id === null) return;
- gridEl?.setPointerCapture?.(e.pointerId);
+ // Capture mouse/pen so fast drags keep streaming to this container. TOUCH
+ // is purposely NOT captured here: an early touch capture can suppress
+ // native scrolling (WebKit), and touch already gets the browser's implicit
+ // capture — after the long-press lift the moved events bubble up to this
+ // container anyway.
+ if (e.pointerType !== "touch") gridEl?.setPointerCapture?.(e.pointerId);
// Where within the tile the pointer grabbed — the follow transform keeps
// THIS point under the pointer (not the tile's top-left origin).
const rect = tileEl.getBoundingClientRect();
@@ -453,7 +458,7 @@
<svelte:window onkeydowncapture={windowKeydown} />
-<div class="relative min-h-0 flex-1">
+<div class="relative h-full">
<div
bind:this={scrollerEl}
class="h-full overscroll-contain"