summaryrefslogtreecommitdiff
path: root/web
diff options
context:
space:
mode:
Diffstat (limited to 'web')
-rw-r--r--web/static/js/app.js16
-rw-r--r--web/templates/partials/shopping-tab.html13
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>