mirror of
https://github.com/mealie-recipes/mealie.git
synced 2026-05-26 03:30:26 -04:00
fix: Prevent swiping AND scrolling on shopping list (#7659)
This commit is contained in:
@@ -10,24 +10,9 @@
|
|||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<v-row
|
<v-row
|
||||||
v-touch="{
|
ref="swipeRowRef"
|
||||||
move: ({ originalEvent: { touches: [{ screenX, screenY }] } }) => {
|
v-touch="{ move: onSwipeMove, start: onSwipeStart, end: onSwipeEnd }"
|
||||||
swipeInfo.touchendX = screenX;
|
style="touch-action: pan-y;"
|
||||||
swipeInfo.touchendY = screenY;
|
|
||||||
},
|
|
||||||
start: ({ originalEvent: { touches: [{ screenX, screenY }] } }) => {
|
|
||||||
swipeInfo.touchstartX = screenX;
|
|
||||||
swipeInfo.touchstartY = screenY;
|
|
||||||
},
|
|
||||||
end: () => {
|
|
||||||
if (swiping < SWIPE_THRESHOLD) {
|
|
||||||
swipeInfo = {};
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
swipeInfo = {};
|
|
||||||
toggleChecked();
|
|
||||||
},
|
|
||||||
}"
|
|
||||||
no-gutters
|
no-gutters
|
||||||
class="flex-nowrap align-center"
|
class="flex-nowrap align-center"
|
||||||
>
|
>
|
||||||
@@ -214,9 +199,23 @@ const emit = defineEmits<{
|
|||||||
}>();
|
}>();
|
||||||
|
|
||||||
const SWIPE_THRESHOLD = 50;
|
const SWIPE_THRESHOLD = 50;
|
||||||
const SCROLL_THRESHOLD = 50;
|
|
||||||
|
|
||||||
const { isRtl } = useRtl();
|
const { isRtl } = useRtl();
|
||||||
|
const swipeRowRef = ref<InstanceType<typeof import("vuetify/components").VRow> | null>(null);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
const el = swipeRowRef.value?.$el as HTMLElement | undefined;
|
||||||
|
if (!el) return;
|
||||||
|
el.addEventListener(
|
||||||
|
"touchmove",
|
||||||
|
(e: TouchEvent) => {
|
||||||
|
if (swipeInfo.value.gesture === "swipe") {
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ passive: false },
|
||||||
|
);
|
||||||
|
});
|
||||||
const i18n = useI18n();
|
const i18n = useI18n();
|
||||||
const displayRecipeRefs = ref(false);
|
const displayRecipeRefs = ref(false);
|
||||||
const itemLabelCols = computed<string>(() => (model.value?.checked ? "auto" : "6"));
|
const itemLabelCols = computed<string>(() => (model.value?.checked ? "auto" : "6"));
|
||||||
@@ -267,22 +266,66 @@ function save() {
|
|||||||
edit.value = false;
|
edit.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const swipeInfo: Ref<{ touchstartX?: number; touchendX?: number; touchstartY?: number; touchendY?: number }> = ref({});
|
type SwipeGesture = null | "scroll" | "swipe";
|
||||||
const swiping = computed(() => {
|
|
||||||
const { touchstartX, touchendX, touchstartY, touchendY } = swipeInfo.value ?? {};
|
|
||||||
if (touchstartX === undefined || touchendX === undefined) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const deltaX = isRtl.value ? touchstartX - touchendX : touchendX - touchstartX;
|
|
||||||
|
|
||||||
// If there's significant vertical movement, treat as a scroll gesture and ignore
|
const swipeInfo = ref({
|
||||||
if (touchstartY !== undefined && touchendY !== undefined) {
|
touchstartX: 0,
|
||||||
const deltaY = Math.abs(touchendY - touchstartY);
|
touchstartY: 0,
|
||||||
if (deltaY > SCROLL_THRESHOLD) {
|
touchendX: 0,
|
||||||
|
touchendY: 0,
|
||||||
|
gesture: null as SwipeGesture,
|
||||||
|
});
|
||||||
|
|
||||||
|
function getSwipePoint(e: any) {
|
||||||
|
const touch = e?.touches?.[0] ?? e?.changedTouches?.[0] ?? e;
|
||||||
|
return { x: touch?.clientX ?? 0, y: touch?.clientY ?? 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetSwipe() {
|
||||||
|
swipeInfo.value = { touchstartX: 0, touchstartY: 0, touchendX: 0, touchendY: 0, gesture: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSwipeStart(payload: any) {
|
||||||
|
const { x, y } = getSwipePoint(payload.originalEvent);
|
||||||
|
swipeInfo.value = { touchstartX: x, touchstartY: y, touchendX: x, touchendY: y, gesture: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSwipeMove(payload: any) {
|
||||||
|
const { x, y } = getSwipePoint(payload.originalEvent);
|
||||||
|
swipeInfo.value.touchendX = x;
|
||||||
|
swipeInfo.value.touchendY = y;
|
||||||
|
|
||||||
|
if (!swipeInfo.value.gesture) {
|
||||||
|
const deltaX = Math.abs(x - swipeInfo.value.touchstartX);
|
||||||
|
const deltaY = Math.abs(y - swipeInfo.value.touchstartY);
|
||||||
|
if (deltaY > 8 && deltaY > deltaX) {
|
||||||
|
swipeInfo.value.gesture = "scroll";
|
||||||
|
}
|
||||||
|
else if (deltaX > 8 && deltaX > deltaY) {
|
||||||
|
swipeInfo.value.gesture = "swipe";
|
||||||
|
}
|
||||||
|
else if (deltaX > 8 || deltaY > 8) {
|
||||||
|
// Diagonal / ambiguous — default to scroll
|
||||||
|
swipeInfo.value.gesture = "scroll";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSwipeEnd() {
|
||||||
|
if (swipeInfo.value.gesture === "swipe" && swiping.value >= SWIPE_THRESHOLD) {
|
||||||
|
toggleChecked();
|
||||||
|
}
|
||||||
|
resetSwipe();
|
||||||
|
}
|
||||||
|
|
||||||
|
const swiping = computed(() => {
|
||||||
|
if (swipeInfo.value.gesture !== "swipe") {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
const deltaX = isRtl.value
|
||||||
return Math.min(Math.max(0, deltaX), 100);
|
? swipeInfo.value.touchstartX - swipeInfo.value.touchendX
|
||||||
|
: swipeInfo.value.touchendX - swipeInfo.value.touchstartX;
|
||||||
|
return Math.max(0, Math.min(deltaX, 100));
|
||||||
});
|
});
|
||||||
|
|
||||||
const recipeList = computed<RecipeSummary[]>(() => {
|
const recipeList = computed<RecipeSummary[]>(() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user