51 lines
1.4 KiB
Vue
51 lines
1.4 KiB
Vue
<script setup lang="ts">
|
|
import { ChevronDownIcon } from '@lucide/vue'
|
|
import type { HTMLAttributes } from 'vue'
|
|
import { onBeforeUnmount, ref, watchEffect } from 'vue'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
const props = defineProps<{ viewport: HTMLElement | null, class?: HTMLAttributes['class'] }>()
|
|
|
|
const visible = ref(false)
|
|
let scrollInterval: ReturnType<typeof setInterval> | undefined
|
|
|
|
function updateVisibility() {
|
|
const viewport = props.viewport
|
|
visible.value = !!viewport && viewport.scrollTop + viewport.clientHeight < viewport.scrollHeight
|
|
}
|
|
|
|
watchEffect((onCleanup) => {
|
|
const viewport = props.viewport
|
|
if (!viewport) return
|
|
updateVisibility()
|
|
viewport.addEventListener('scroll', updateVisibility)
|
|
onCleanup(() => viewport.removeEventListener('scroll', updateVisibility))
|
|
})
|
|
|
|
function startScroll() {
|
|
stopScroll()
|
|
scrollInterval = setInterval(() => {
|
|
if (props.viewport) props.viewport.scrollTop += 8
|
|
}, 16)
|
|
}
|
|
function stopScroll() {
|
|
if (scrollInterval) clearInterval(scrollInterval)
|
|
scrollInterval = undefined
|
|
}
|
|
onBeforeUnmount(stopScroll)
|
|
</script>
|
|
|
|
<template>
|
|
<div
|
|
v-if="visible"
|
|
data-slot="select-scroll-down-button"
|
|
:class="cn('bg-popover z-10 flex cursor-default items-center justify-center py-1 [&_svg:not([class*=size-])]:size-4', props.class)"
|
|
@pointerdown="startScroll"
|
|
@pointerup="stopScroll"
|
|
@pointerleave="stopScroll"
|
|
>
|
|
<slot>
|
|
<ChevronDownIcon />
|
|
</slot>
|
|
</div>
|
|
</template>
|