|
| 1 | +<script lang="ts" setup> |
| 2 | +import { NButton, NDrawerContent, NFlex, NIcon } from 'naive-ui'; |
| 3 | +
|
| 4 | +import { ref } from 'vue'; |
| 5 | +
|
| 6 | +import IconChevronLeft from '~/components/icons/IconChevronLeft.vue'; |
| 7 | +import IconClose from '~/components/icons/IconClose.vue'; |
| 8 | +import { handleSwipeLeftRight, SwipeDirection } from '~/utils/touch.utils'; |
| 9 | +
|
| 10 | +const emits = defineEmits<{ |
| 11 | + (name: 'onBack', e: MouseEvent | TouchEvent): void; |
| 12 | + (name: 'onClose', e: MouseEvent | TouchEvent): void; |
| 13 | +}>(); |
| 14 | +
|
| 15 | +const contentRef = ref<HTMLDivElement>(); |
| 16 | +const touchStart = ref<TouchEvent>(); |
| 17 | +
|
| 18 | +const onTouchStart = (e: TouchEvent) => { |
| 19 | + touchStart.value = e; |
| 20 | +}; |
| 21 | +
|
| 22 | +const onTouchEnd = (e: TouchEvent) => { |
| 23 | + const _touchStart = touchStart.value?.targetTouches?.[0]; |
| 24 | + const _touchEnd = e.changedTouches?.[0]; |
| 25 | + if (!_touchStart) return; |
| 26 | + touchStart.value = undefined; |
| 27 | + const { clientWidth, clientHeight } = contentRef.value || {}; |
| 28 | + const swipe = handleSwipeLeftRight(_touchStart, _touchEnd, { |
| 29 | + vertical: clientHeight ? Math.min(clientHeight / 2, 300) : 300, |
| 30 | + left: clientWidth ? Math.min(clientWidth / 2, 400) : 400, |
| 31 | + right: clientWidth ? Math.min(clientWidth / 2, 400) : 400, |
| 32 | + }); |
| 33 | + if (swipe === SwipeDirection.Right) emits('onBack', e); |
| 34 | +}; |
| 35 | +</script> |
| 36 | + |
| 37 | +<template> |
| 38 | + <NDrawerContent |
| 39 | + :native-scrollbar="false" |
| 40 | + @touchstart="onTouchStart" |
| 41 | + @touchend="onTouchEnd" |
| 42 | + > |
| 43 | + <!-- Header --> |
| 44 | + <NFlex justify="space-between" class="panel-header"> |
| 45 | + <NButton circle quaternary @click="e => emits('onBack', e)"> |
| 46 | + <template #icon> |
| 47 | + <NIcon> |
| 48 | + <IconChevronLeft /> |
| 49 | + </NIcon> |
| 50 | + </template> |
| 51 | + </NButton> |
| 52 | + <NButton circle quaternary @click="e => emits('onClose', e)"> |
| 53 | + <template #icon> |
| 54 | + <NIcon> |
| 55 | + <IconClose /> |
| 56 | + </NIcon> |
| 57 | + </template> |
| 58 | + </NButton> |
| 59 | + </NFlex> |
| 60 | + |
| 61 | + <!-- Content --> |
| 62 | + <div ref="contentRef" class="panel-content"> |
| 63 | + <slot /> |
| 64 | + </div> |
| 65 | + </NDrawerContent> |
| 66 | +</template> |
| 67 | + |
| 68 | +<style lang="scss" scoped> |
| 69 | +.panel { |
| 70 | + &-header { |
| 71 | + position: sticky; |
| 72 | + top: 1rem; |
| 73 | + } |
| 74 | +
|
| 75 | + &-content { |
| 76 | + margin-top: -1.125rem; |
| 77 | + padding: 0 3rem 1.25rem; |
| 78 | +
|
| 79 | + :deep(.n-skeleton) { |
| 80 | + opacity: 1; |
| 81 | + transition: opacity 0.1s ease-in 0.1s; |
| 82 | +
|
| 83 | + /* Adds 0.2s delay and 1s transition to loading indicator to prevent flashing */ |
| 84 | + @starting-style { |
| 85 | + opacity: 0; |
| 86 | + } |
| 87 | + } |
| 88 | + } |
| 89 | +} |
| 90 | +</style> |
0 commit comments