diff --git a/ui/hooks/index.ts b/ui/hooks/index.ts index f03f9b4ba0..75ff6da61d 100644 --- a/ui/hooks/index.ts +++ b/ui/hooks/index.ts @@ -3,6 +3,7 @@ export * from "./use-credentials-form"; export * from "./use-form-server-errors"; export * from "./use-local-storage"; export * from "./use-related-filters"; +export * from "./use-scroll-hint"; export * from "./use-sidebar"; export * from "./use-store"; export * from "./use-url-filters"; diff --git a/ui/hooks/use-scroll-hint.ts b/ui/hooks/use-scroll-hint.ts new file mode 100644 index 0000000000..ba50f629b8 --- /dev/null +++ b/ui/hooks/use-scroll-hint.ts @@ -0,0 +1,63 @@ +"use client"; + +import { UIEvent, useEffect, useRef, useState } from "react"; + +interface UseScrollHintOptions { + enabled?: boolean; + refreshToken?: string | number; +} + +const SCROLL_THRESHOLD_PX = 2; + +function shouldShowScrollHint(element: HTMLDivElement) { + const hasOverflow = + element.scrollHeight - element.clientHeight > SCROLL_THRESHOLD_PX; + const isAtBottom = + element.scrollTop + element.clientHeight >= + element.scrollHeight - SCROLL_THRESHOLD_PX; + + return hasOverflow && !isAtBottom; +} + +export function useScrollHint({ + enabled = true, + refreshToken, +}: UseScrollHintOptions = {}) { + const containerRef = useRef(null); + const [showScrollHint, setShowScrollHint] = useState(false); + + useEffect(() => { + if (!enabled) { + setShowScrollHint(false); + return; + } + + const frame = requestAnimationFrame(() => { + const element = containerRef.current; + if (!element) return; + setShowScrollHint(shouldShowScrollHint(element)); + }); + + const handleResize = () => { + const element = containerRef.current; + if (!element) return; + setShowScrollHint(shouldShowScrollHint(element)); + }; + + window.addEventListener("resize", handleResize); + return () => { + cancelAnimationFrame(frame); + window.removeEventListener("resize", handleResize); + }; + }, [enabled, refreshToken]); + + const handleScroll = (event: UIEvent) => { + setShowScrollHint(shouldShowScrollHint(event.currentTarget)); + }; + + return { + containerRef, + showScrollHint, + handleScroll, + }; +}