64 lines
1.7 KiB
TypeScript
64 lines
1.7 KiB
TypeScript
import { ref, watch, onUnmounted, type Ref } from 'vue';
|
|
|
|
export function useAutoScroll(elementRef: Ref<HTMLElement | null>) {
|
|
const userIsScrollingUp = ref(false);
|
|
const THRESHOLD = 50;
|
|
|
|
const isAtBottom = () => {
|
|
const el = elementRef.value;
|
|
if (!el) return false;
|
|
const distanceToBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
|
|
return distanceToBottom <= THRESHOLD;
|
|
};
|
|
|
|
const scrollToBottom = (behavior: ScrollBehavior = 'auto') => {
|
|
const el = elementRef.value;
|
|
if (!el) return;
|
|
el.scrollTo({
|
|
top: el.scrollHeight,
|
|
behavior,
|
|
});
|
|
};
|
|
|
|
const handleScroll = () => {
|
|
const el = elementRef.value;
|
|
if (!el) return;
|
|
|
|
userIsScrollingUp.value = !isAtBottom();
|
|
};
|
|
|
|
let observer: MutationObserver | null = null;
|
|
|
|
watch(elementRef, (newEl, oldEl) => {
|
|
if (oldEl) {
|
|
oldEl.removeEventListener('scroll', handleScroll);
|
|
observer?.disconnect();
|
|
}
|
|
|
|
if (newEl) {
|
|
newEl.addEventListener('scroll', handleScroll, { passive: true });
|
|
|
|
observer = new MutationObserver(() => {
|
|
if (!userIsScrollingUp.value) {
|
|
scrollToBottom();
|
|
}
|
|
});
|
|
|
|
observer.observe(newEl, {
|
|
childList: true,
|
|
subtree: true,
|
|
characterData: true
|
|
});
|
|
}
|
|
});
|
|
|
|
onUnmounted(() => {
|
|
elementRef.value?.removeEventListener('scroll', handleScroll);
|
|
observer?.disconnect();
|
|
});
|
|
|
|
return {
|
|
userIsScrollingUp,
|
|
scrollToBottom,
|
|
};
|
|
} |