diff --git a/.ottotime b/.ottotime
index 6a3f18c..5f6de5d 100644
--- a/.ottotime
+++ b/.ottotime
@@ -76,4 +76,5 @@
1767345728- 93:01
1767373107- 6:35
1767376587- 12:56
-1767377746- 5:52
+1767377746- 15:56
+1767379098- 24:00
diff --git a/components/AppMenu.vue b/components/AppMenu.vue
index 189d6b6..a75f988 100644
--- a/components/AppMenu.vue
+++ b/components/AppMenu.vue
@@ -10,32 +10,44 @@
-
-
-
-
{{ auth.user.nev }}
-
{{ auth.user.email }}
+
+
+
+
+
{{ props.title }}
@@ -65,59 +77,178 @@ const auth = useAuthStore()
const config = useMyConfigStore()
const token = useCookie('_auth')
-const menuShow = ref(false)
const router = useRouter()
-const isHome = computed(()=>{
- return (route.fullPath === '/')
+const isHome = computed(() => {
+ return (route.fullPath === '/')
})
-// Swipe gesture handling
-const touchStartX = ref(0)
-const touchEndX = ref(0)
-const minSwipeDistance = 50
+// Drawer constants
+const DRAWER_WIDTH = 288
+const SWIPE_THRESHOLD = 80
+const EDGE_ZONE = 40
+
+// Simple state
+const drawerRef = ref
(null)
+const isOpen = ref(false)
+const isDragging = ref(false)
+const dragOffset = ref(0) // 0 = fully open position, DRAWER_WIDTH = fully closed
+const menuHistoryPushed = ref(false)
+
+// Touch tracking
+let touchStartX = 0
+let touchStartY = 0
+let startedFromEdge = false
+let isHorizontalSwipe: boolean | null = null
+
+// Computed translate value
+const translateX = computed(() => {
+ if (isDragging.value) {
+ return Math.max(0, Math.min(DRAWER_WIDTH, dragOffset.value))
+ }
+ return isOpen.value ? 0 : DRAWER_WIDTH
+})
+
+// Backdrop opacity based on position
+const backdropOpacity = computed(() => {
+ const openness = 1 - (translateX.value / DRAWER_WIDTH)
+ return openness * 0.5
+})
+
+// Dynamic classes
+const drawerClass = computed(() => ({
+ 'transition-transform duration-300 ease-out': !isDragging.value,
+ 'pointer-events-none': !isOpen.value && !isDragging.value
+}))
+
+const backdropClass = computed(() => ({
+ 'transition-opacity duration-300': !isDragging.value,
+ 'pointer-events-none': backdropOpacity.value <= 0
+}))
+
+function openMenu() {
+ isOpen.value = true
+ if (!menuHistoryPushed.value) {
+ window.history.pushState({ menuOpen: true }, '')
+ menuHistoryPushed.value = true
+ }
+}
+
+function closeMenu() {
+ isOpen.value = false
+ if (menuHistoryPushed.value) {
+ menuHistoryPushed.value = false
+ window.history.back()
+ }
+}
+
+function closeMenuWithoutHistory() {
+ isOpen.value = false
+ menuHistoryPushed.value = false
+}
+
+function handlePopState() {
+ if (isOpen.value) {
+ closeMenuWithoutHistory()
+ }
+}
function handleTouchStart(e: TouchEvent) {
- touchStartX.value = e.touches[0].clientX
-}
+ const touch = e.touches[0]
+ touchStartX = touch.clientX
+ touchStartY = touch.clientY
+ isHorizontalSwipe = null
-function handleTouchEnd(e: TouchEvent) {
- touchEndX.value = e.changedTouches[0].clientX
- handleSwipe()
-}
-
-function handleSwipe() {
- const swipeDistance = touchEndX.value - touchStartX.value
const screenWidth = window.innerWidth
+ startedFromEdge = touchStartX > screenWidth - EDGE_ZONE
- // Swipe left (from right edge) -> open menu
- if (swipeDistance < -minSwipeDistance && touchStartX.value > screenWidth - 50) {
- if (props.menu) {
- menuShow.value = true
+ if (isOpen.value) {
+ dragOffset.value = 0
+ } else if (startedFromEdge) {
+ dragOffset.value = DRAWER_WIDTH
+ }
+}
+
+function handleTouchMove(e: TouchEvent) {
+ if (!isOpen.value && !startedFromEdge) return
+
+ const touch = e.touches[0]
+ const deltaX = touch.clientX - touchStartX
+ const deltaY = touch.clientY - touchStartY
+
+ // Determine swipe direction on first significant movement
+ if (isHorizontalSwipe === null && (Math.abs(deltaX) > 10 || Math.abs(deltaY) > 10)) {
+ isHorizontalSwipe = Math.abs(deltaX) > Math.abs(deltaY)
+ }
+
+ if (!isHorizontalSwipe) return
+
+ // Start dragging
+ if (!isDragging.value) {
+ if (isOpen.value && deltaX > 0) {
+ isDragging.value = true
+ } else if (startedFromEdge && deltaX < 0) {
+ isDragging.value = true
}
}
- // Swipe right -> close menu (if open)
- if (swipeDistance > minSwipeDistance && menuShow.value) {
- menuShow.value = false
+ if (isDragging.value) {
+ if (isOpen.value) {
+ // Dragging to close: deltaX > 0 means moving right
+ dragOffset.value = Math.max(0, deltaX)
+ } else {
+ // Dragging to open: deltaX < 0 means moving left
+ dragOffset.value = DRAWER_WIDTH + deltaX
+ }
}
}
+function handleTouchEnd() {
+ if (!isDragging.value) {
+ startedFromEdge = false
+ isHorizontalSwipe = null
+ return
+ }
+
+ const currentOffset = dragOffset.value
+
+ if (isOpen.value) {
+ // Was open - check if should close
+ if (currentOffset > SWIPE_THRESHOLD) {
+ closeMenu()
+ }
+ } else {
+ // Was closed - check if should open
+ if (currentOffset < DRAWER_WIDTH - SWIPE_THRESHOLD) {
+ openMenu()
+ }
+ }
+
+ // Reset
+ isDragging.value = false
+ startedFromEdge = false
+ isHorizontalSwipe = null
+}
+
onMounted(() => {
document.addEventListener('touchstart', handleTouchStart, { passive: true })
+ document.addEventListener('touchmove', handleTouchMove, { passive: true })
document.addEventListener('touchend', handleTouchEnd, { passive: true })
+ window.addEventListener('popstate', handlePopState)
})
onUnmounted(() => {
document.removeEventListener('touchstart', handleTouchStart)
+ document.removeEventListener('touchmove', handleTouchMove)
document.removeEventListener('touchend', handleTouchEnd)
+ window.removeEventListener('popstate', handlePopState)
})
-function logOut(){
- token.value = null
- auth.user = null
- router.push({path:'/login'})
+function logOut() {
+ token.value = null
+ auth.user = null
+ router.push({ path: '/login' })
}
-
\ No newline at end of file
+
diff --git a/components/RentStep.vue b/components/RentStep.vue
index 6af0620..9c3b73e 100644
--- a/components/RentStep.vue
+++ b/components/RentStep.vue
@@ -1,5 +1,5 @@
-