Fix scrolling on mobile #150
Labels
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Grandiras/Ponente#150
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
If there are enough cards on a column to fill the screen, scrolling is not possible, because the drag state will be invoked at every single realistic touch point. We need to fix that.
🤖 On it — follow along in the action run; the reply will link the full session transcript.
📜 Live transcript — updating every ~5 minutes while the task runs.
Fixed on branch
claude/issue-150.What was wrong
SortableJS starts a drag on the first move after a finger lands, in any direction. That is survivable while a column is short — the gaps between cards and the strip under the last one still scroll it — but a column holding more cards than fit the screen has no such strip, and on a phone
.column-cardsis the only thing that scrolls vertically (.columnis capped to the viewport height). So the fuller a column got, the less of it you could reach, and a full one could not be scrolled at all.I reproduced it first: an upward swipe starting on a card in an overflowing column left
scrollTopat 0 and put the card in the air instead.The fix
Three options on the column Sortables in
src/Ponente.Kanban/wwwroot/js/sortable-interop.js:delay: 200— the finger has to rest before the card is picked up. Nothing callspreventDefaultwhile the timer runs, so the browser scrolls the column with its own gesture handling; the app's job here is to get out of the way, not to reimplement scrolling a second time.touchStartThreshold: 6— a press that travels more than 6px before the timer fires abandons the pick-up, so a flick scrolls immediately rather than after a stall. Under the browser's own ~8px of touch slop, over the jitter of a finger deliberately holding still.delayOnTouchOnly: true— a mouse stays instant. A pointer has no ambiguity to resolve, so making the desktop wait would be paying for a problem it does not have. (The admin reorder lists answer the same question the other way, with ahandle:— right for a row with a grip, wrong for a card you should be able to grab anywhere.).card-chosenalso gainsscale: 1.03(as:not(.card-ghost)): the class now lands at the end of a press, before the card has moved a pixel, so on a finger it is the only thing distinguishing "holding this" from "about to scroll past it". The:not()is because SortableJS stripschosenClassfrom the clone in flight but not from the element left behind — the hole must not be wider than the card that will fill it.Verification
Ran the app against a local Postgres and drove real touch gestures over CDP (Playwright's own input cannot drive a multi-step touch gesture, and the fallback drag path only exists under a phone user agent).
tests/e2e/mobile.spec.js— the existing touch-drag fixture now covers all three gestures: a tap under the delay still opens the card dialog, a flick scrolls the column and picks nothing up, and a press picks the card up (.card-chosen) and then autoscrolls at the column edge. 7/7 passed.tests/e2e/board.spec.js— reads the new options back off every column, beside the existingscroll/emptyInsertThresholdassertions, so a re-render rebinding cannot lose them. 23/23 passed.filter.spec.js, which does a real mouse drag, also passes — the desktop path is unchanged.dotnet build+dotnet test: 389/389 passed,DocumentationTestsincluded.pds.spec.js,requests.spec.js,feedback.spec.js's stub forge), and I re-ran the other 5 (access×2,boards,calendar,permissions) against a stashed clean tree, where they fail identically. That is the prerender racedocs/testing.mdalready documents as the reason the e2e suite is not wired into CI.Also updated:
docs/frontend.md(the drag section, with the reasoning above and why each third of the fix is load-bearing) andChangelog.Unreleasedin both locales.⏱ 27m 20s · 220 turns · tokens: 183.1k in / 68.6k out (+31422.8k cache-read)
📜 Full session transcript
→ Opened PR: #151