elk/components/nav/NavBottomMoreMenu.vue

195 lines
6.0 KiB
Vue

<script lang="ts" setup>
import { invoke } from '@vueuse/core'
const modelValue = defineModel<boolean>({ required: true })
const colorMode = useColorMode()
const userSettings = useUserSettings()
const drawerEl = ref<HTMLDivElement>()
function toggleVisible() {
modelValue.value = !modelValue.value
}
const buttonEl = ref<HTMLDivElement>()
/**
* Close the drop-down menu if the mouse click is not on the drop-down menu button when the drop-down menu is opened
* @param mouse
*/
function clickEvent(mouse: MouseEvent) {
if (mouse.target && !buttonEl.value?.children[0].contains(mouse.target as any)) {
if (modelValue.value) {
document.removeEventListener('click', clickEvent)
modelValue.value = false
}
}
}
function toggleDark() {
colorMode.preference = colorMode.value === 'dark' ? 'light' : 'dark'
}
watch(modelValue, (val) => {
if (val && typeof document !== 'undefined')
document.addEventListener('click', clickEvent)
})
onBeforeUnmount(() => {
document.removeEventListener('click', clickEvent)
})
// Pull down to close
const { dragging, dragDistance } = invoke(() => {
const triggerDistance = 120
let scrollTop = 0
let beforeTouchPointY = 0
const dragDistance = ref(0)
const dragging = ref(false)
useEventListener(drawerEl, 'scroll', (e: Event) => {
scrollTop = (e.target as HTMLDivElement).scrollTop
// Prevent the page from scrolling when the drawer is being dragged.
if (dragDistance.value > 0)
(e.target as HTMLDivElement).scrollTop = 0
}, { passive: true })
useEventListener(drawerEl, 'touchstart', (e: TouchEvent) => {
if (!modelValue.value)
return
beforeTouchPointY = e.touches[0].pageY
dragDistance.value = 0
}, { passive: true })
useEventListener(drawerEl, 'touchmove', (e: TouchEvent) => {
if (!modelValue.value)
return
// Do not move the entire drawer when its contents are not scrolled to the top.
if (scrollTop > 0 && dragDistance.value <= 0) {
dragging.value = false
beforeTouchPointY = e.touches[0].pageY
return
}
const { pageY } = e.touches[0]
// Calculate the drag distance.
dragDistance.value += pageY - beforeTouchPointY
if (dragDistance.value < 0)
dragDistance.value = 0
beforeTouchPointY = pageY
// Marked as dragging.
if (dragDistance.value > 1)
dragging.value = true
// Prevent the page from scrolling when the drawer is being dragged.
if (dragDistance.value > 0) {
if (e?.cancelable && e?.preventDefault)
e.preventDefault()
e?.stopPropagation()
}
}, { passive: true })
useEventListener(drawerEl, 'touchend', () => {
if (!modelValue.value)
return
if (dragDistance.value >= triggerDistance)
modelValue.value = false
dragging.value = false
// code
}, { passive: true })
return {
dragDistance,
dragging,
}
})
</script>
<template>
<div ref="buttonEl" flex items-center static>
<slot :toggle-visible="toggleVisible" :show="modelValue" />
<!-- Drawer -->
<Transition
enter-active-class="transition duration-250 ease-out"
enter-from-class="opacity-0 children:(translate-y-full)"
enter-to-class="opacity-100 children:(translate-y-0)"
leave-active-class="transition duration-250 ease-in"
leave-from-class="opacity-100 children:(translate-y-0)"
leave-to-class="opacity-0 children:(translate-y-full)"
>
<div
v-show="modelValue"
absolute inset-x-0 top-auto bottom-full z-20 h-100vh
flex items-end of-y-scroll of-x-hidden scrollbar-hide overscroll-none
bg="black/50"
>
<!-- The style `scrollbar-hide overscroll-none overflow-y-scroll mb="-1px"` and `h="[calc(100%+0.5px)]"` is used to implement scroll locking, -->
<!-- corresponding to issue: #106, so please don't remove it. -->
<div absolute inset-0 opacity-0 h="[calc(100vh+0.5px)]" />
<div
ref="drawerEl"
:style="{
transform: dragging ? `translateY(${dragDistance}px)` : '',
}"
:class="{
'duration-0': dragging,
'duration-250': !dragging,
'backdrop-blur-md': !getPreferences(userSettings, 'optimizeForLowPerformanceDevice'),
}"
transition="transform ease-in"
flex-1 min-w-48 py-6 mb="-1px"
of-y-auto scrollbar-hide overscroll-none max-h="[calc(100vh-200px)]"
rounded-t-lg bg="white/85 dark:neutral-900/85" backdrop-filter
border-t-1 border-base
>
<!-- Nav -->
<NavSide />
<!-- Divider line -->
<div border="neutral-300 dark:neutral-700 t-1" m="x-3 y-2" />
<!-- Function menu -->
<div flex="~ col gap2">
<!-- Toggle Theme -->
<button
flex flex-row items-center
block px-5 py-2 focus-blue w-full
text-sm text-base capitalize text-left whitespace-nowrap
transition-colors duration-200 transform
hover="bg-gray-100 dark:(bg-gray-700 text-white)"
@click="toggleDark()"
>
<span class="i-ri:sun-line dark:i-ri:moon-line flex-shrink-0 text-xl me-4 !align-middle" />
{{ colorMode.value === 'light' ? $t('menu.toggle_theme.dark') : $t('menu.toggle_theme.light') }}
</button>
<!-- Zen Mode -->
<button
flex flex-row items-center
block px-5 py-2 focus-blue w-full
text-sm text-base capitalize text-left whitespace-nowrap
transition-colors duration-200 transform
hover="bg-gray-100 dark:(bg-gray-700 text-white)"
:aria-label="$t('nav.zen_mode')"
@click="togglePreferences('zenMode')"
>
<span :class="getPreferences(userSettings, 'zenMode') ? 'i-ri:layout-right-2-line' : 'i-ri:layout-right-line'" class="flex-shrink-0 text-xl me-4 !align-middle" />
{{ $t('nav.zen_mode') }}
</button>
</div>
</div>
</div>
</Transition>
</div>
</template>