diff options
| -rw-r--r-- | web/static/js/app.js | 16 | ||||
| -rw-r--r-- | web/templates/partials/shopping-tab.html | 13 |
2 files changed, 25 insertions, 4 deletions
diff --git a/web/static/js/app.js b/web/static/js/app.js index 8058dc2..6071ce3 100644 --- a/web/static/js/app.js +++ b/web/static/js/app.js @@ -316,7 +316,9 @@ function toggleTask(taskId) { // Mobile Swipe Navigation (function() { let touchStartX = 0; + let touchStartY = 0; let touchEndX = 0; + let touchEndY = 0; const SWIPE_THRESHOLD = 50; // Minimum px for a swipe // Get ordered list of tab names (main tabs only for swipe) @@ -324,11 +326,23 @@ function toggleTask(taskId) { function handleSwipe() { const swipeDistance = touchEndX - touchStartX; + const verticalDistance = touchEndY - touchStartY; if (Math.abs(swipeDistance) < SWIPE_THRESHOLD) { return; // Not a significant swipe } + // A normal vertical scroll through a long tab (e.g. the shopping list) + // easily drifts 50+ horizontal px over its course from natural thumb + // motion -- without comparing against how far the touch moved + // vertically, that drift alone was enough to fire a tab change mid-scroll + // (reported as "shopping randomly jumps back to timeline", which sits + // immediately before it in TAB_ORDER). Require the horizontal component + // to clearly dominate before treating it as an intentional swipe. + if (Math.abs(swipeDistance) < Math.abs(verticalDistance) * 2) { + return; + } + const currentIndex = TAB_ORDER.indexOf(currentTab); if (currentIndex === -1) return; @@ -362,10 +376,12 @@ function toggleTask(taskId) { tabContent.addEventListener('touchstart', function(e) { touchStartX = e.changedTouches[0].screenX; + touchStartY = e.changedTouches[0].screenY; }, { passive: true }); tabContent.addEventListener('touchend', function(e) { touchEndX = e.changedTouches[0].screenX; + touchEndY = e.changedTouches[0].screenY; handleSwipe(); }, { passive: true }); }); diff --git a/web/templates/partials/shopping-tab.html b/web/templates/partials/shopping-tab.html index e3742d9..06d2b56 100644 --- a/web/templates/partials/shopping-tab.html +++ b/web/templates/partials/shopping-tab.html @@ -32,16 +32,21 @@ hx-indicator="#shopping-add-indicator" class="bg-panel backdrop-blur-sm rounded-xl p-4 sm:p-5"> <input type="hidden" name="grouped" value="{{.Grouped}}"> - <div class="flex gap-2"> + <div class="flex flex-wrap gap-2"> <input type="text" name="name" placeholder="Add item..." - class="flex-1 bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-white/30 text-sm" + class="flex-1 min-w-0 bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white placeholder-white/40 focus:outline-none focus:ring-2 focus:ring-white/30 text-sm" required autocomplete="off"> - <select name="store" class="bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:ring-2 focus:ring-white/30" required> + <!-- shrink-0 + a capped width: a long store name (e.g. "Hardware Store") was + otherwise letting this <select> claim its full content width, which on a + narrow phone pushed the submit button past the right edge of the screen -- + unreachable, not just visually cramped, since the row had no wrap or shrink + to fall back on. --> + <select name="store" class="shrink-0 w-28 bg-white/10 border border-white/20 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:ring-2 focus:ring-white/30" required> {{range .Stores}} <option value="{{.Name}}">{{.Name}}</option> {{end}} </select> - <button type="submit" class="bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"> + <button type="submit" class="shrink-0 bg-white/20 hover:bg-white/30 text-white px-4 py-2 rounded-lg text-sm font-medium transition-colors"> <span id="shopping-add-indicator" class="htmx-indicator">...</span> <span class="htmx-hide-indicator">Add</span> </button> |
