Skip to content

Commit 74256ee

Browse files
committed
feat(web): adds touch swipe support for header
1 parent 2be1cc3 commit 74256ee

File tree

3 files changed

+130
-2
lines changed

3 files changed

+130
-2
lines changed

src/components/common/navbar/NavbarComponent.vue

+54-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
<script lang="ts" setup>
22
import { NTab, NTabs } from 'naive-ui';
3-
import { computed, toRefs, useSlots } from 'vue';
3+
import { computed, ref, toRefs, useSlots } from 'vue';
44
import { useRoute, useRouter } from 'vue-router';
55
66
import NavbarSettingsDropdown from '~/components/common/navbar/NavbarSettingsDopdown.vue';
77
88
import { Route } from '~/models/router.model';
9+
import { Logger } from '~/services/logger.service';
910
import { NavbarService } from '~/services/navbar.service';
1011
import { useExtensionSettingsStoreRefs } from '~/stores/settings/extension.store';
12+
import { Header } from '~/styles/layout.style';
1113
import { useI18n } from '~/utils/i18n.utils';
14+
import { handleSwipe, SwipeDirection } from '~/utils/touch.utils';
1215
1316
const props = defineProps({
1417
disabled: {
@@ -43,6 +46,21 @@ const activeRoute = computed(() => {
4346
);
4447
});
4548
49+
const nextRoute = computed(() => {
50+
if (!activeRoute.value) return;
51+
const index = activableRoutes.value.indexOf(activeRoute.value);
52+
return activableRoutes.value[index + 1] ?? activableRoutes.value[0];
53+
});
54+
55+
const prevRoute = computed(() => {
56+
if (!activeRoute.value) return;
57+
const index = activableRoutes.value.indexOf(activeRoute.value);
58+
return (
59+
activableRoutes.value[index - 1] ??
60+
activableRoutes.value[activableRoutes.value.length - 1]
61+
);
62+
});
63+
4664
const { isHover, isFocus, ref: navElement } = NavbarService;
4765
4866
const hasDrawer = computed(() => {
@@ -56,16 +74,50 @@ const showDrawer = computed(() => {
5674
NavbarService.open.value = show;
5775
return show;
5876
});
77+
78+
const touchStart = ref<TouchEvent>();
79+
80+
const onTouchStart = (e: TouchEvent) => {
81+
touchStart.value = e;
82+
};
83+
84+
const onTouchEnd = (e: TouchEvent) => {
85+
const _touchStart = touchStart.value?.targetTouches?.[0];
86+
const _touchEnd = e.changedTouches?.[0];
87+
if (!_touchStart) return;
88+
touchStart.value = undefined;
89+
const swipe = handleSwipe(_touchStart, _touchEnd, {
90+
vertical: Header.totalHeight,
91+
up: Header.navbarHeight,
92+
left: 50,
93+
right: 50,
94+
});
95+
switch (swipe) {
96+
case SwipeDirection.Down:
97+
isHover.value = true;
98+
return swipe;
99+
case SwipeDirection.Up:
100+
isHover.value = false;
101+
return swipe;
102+
case SwipeDirection.Right:
103+
return nextRoute.value ? navigate(nextRoute.value) : undefined;
104+
case SwipeDirection.Left:
105+
return prevRoute.value ? navigate(prevRoute.value) : undefined;
106+
default:
107+
Logger.warn('Unknown swipe direction:', swipe);
108+
}
109+
};
59110
</script>
60111

61112
<template>
62113
<nav
63114
ref="navElement"
64115
@mouseenter="isHover = true"
65116
@mouseleave="isHover = false"
66-
@touchstart="isHover = true"
67117
@focusin="isFocus = true"
68118
@focusout="isFocus = false"
119+
@touchstart="onTouchStart"
120+
@touchend="onTouchEnd"
69121
>
70122
<NTabs
71123
:key="enabledRoutes.join('-')"

src/styles/layout.style.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
export const Header = {
22
navbarHeight: 44,
33
drawerHeight: 56,
4+
totalHeight: 100,
45
} as const;

src/utils/touch.utils.ts

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
export const SwipeDirection = {
2+
Up: 'up',
3+
Down: 'down',
4+
Left: 'left',
5+
Right: 'right',
6+
} as const;
7+
8+
export type SwipeDirections = (typeof SwipeDirection)[keyof typeof SwipeDirection];
9+
10+
export type SwipeTolerances = {
11+
horizontal?: number;
12+
up?: number;
13+
down?: number;
14+
vertical?: number;
15+
left?: number;
16+
right?: number;
17+
};
18+
19+
/**
20+
* Detects swipe up or down from touch events
21+
* @param start - The TouchStart event
22+
* @param end - The TouchEnd event
23+
* @param tolerance - The vertical tolerance for swipe detection
24+
* @param tolerance.up - The vertical tolerance for swipe up detection
25+
* @param tolerance.down - The vertical tolerance for swipe down detection
26+
* @param tolerance.horizontal - The vertical horizontal for swipe detection
27+
*/
28+
export const handleSwipeUpDown = (
29+
start?: Touch,
30+
end?: Touch,
31+
{ horizontal = 0, up = 0, down = 0 }: Pick<SwipeTolerances, 'horizontal' | 'up' | 'down'> = {},
32+
): SwipeDirections | undefined => {
33+
if (!start || !end) return;
34+
// If it is a swipe left/right above the tolerance exit
35+
if (horizontal && Math.abs(start.clientX - end.clientX) >= horizontal) return;
36+
// If it is a tap or swipe down, make hover true
37+
if (start.clientY - end.clientY < down) return SwipeDirection.Down;
38+
39+
// If it is a swipe up with some left/right tolerance, make hover false
40+
if (start.clientY - end.clientY > up) return SwipeDirection.Up;
41+
};
42+
43+
/**
44+
* Detects swipe left or right from touch events
45+
* @param start - The TouchStart event
46+
* @param end - The TouchEnd event
47+
* @param tolerance - The horizontal tolerance for swipe detection
48+
* @param tolerance.left - The horizontal tolerance for swipe left detection
49+
* @param tolerance.right - The horizontal tolerance for swipe right detection
50+
* @param tolerance.vertical - The vertical tolerance for swipe detection
51+
*/
52+
export const handleSwipeLeftRight = (
53+
start?: Touch,
54+
end?: Touch,
55+
{ vertical = 0, left = 50, right = 50 }: Pick<SwipeTolerances, 'vertical' | 'left' | 'right'> = {},
56+
): SwipeDirections | undefined => {
57+
if (!start || !end) return;
58+
// If it is a tap or swipe up/down above the tolerance exit
59+
if (vertical && Math.abs(start.clientY - end.clientY) >= vertical) return;
60+
61+
// if swipe right
62+
if (end.clientX - start.clientX > right) return SwipeDirection.Right;
63+
// if swipe left
64+
if (start.clientX - end.clientX > left) return SwipeDirection.Left;
65+
};
66+
67+
/**
68+
* Detects swipe direction from touch events
69+
* @param start - The TouchStart event
70+
* @param end - The TouchEnd event
71+
* @param tolerances - The tolerances for swipe detection
72+
*/
73+
export const handleSwipe = (start?: Touch, end?: Touch, tolerances: SwipeTolerances = {}): SwipeDirections | undefined => {
74+
return handleSwipeLeftRight(start, end, tolerances) ?? handleSwipeUpDown(start, end, tolerances);
75+
};

0 commit comments

Comments
 (0)