@intility/bifrost-react
Version:
React library for Intility's design system, Bifrost.
42 lines (41 loc) • 1.8 kB
JavaScript
"use client";
import { useEffect, useState } from "react";
import closestElement, { isEverScrollable } from "../utils/closestElement.js";
/**
* Listens to scroll event and reports which direction (`'up'` or `'down'`) the user is scrolling,
* or `'none'` if not scrolled yet.
* @param options Optional options object, with `threshold` and `elementRef`
* @returns `'none'` if not scrolled yet, otherwise `'up'` or `'down'`
*/ export default function useScrollDirection({ threshold = 50, elementRef } = {}) {
// inspired by https://stackoverflow.com/a/62497293/833146
const [scrollDir, setScrollDir] = useState("none");
useEffect(()=>{
// find closest element that can become scrollable. defaults to document.documentElement
const scrollableElement = closestElement(isEverScrollable, elementRef?.current);
let lastScrollY = scrollableElement.scrollTop;
let ticking = false;
const updateScrollDir = ()=>{
const scrollY = scrollableElement.scrollTop;
if (Math.abs(scrollY - lastScrollY) < threshold) {
ticking = false;
return;
}
setScrollDir(scrollY > lastScrollY ? "down" : "up");
lastScrollY = scrollY > 0 ? scrollY : 0;
ticking = false;
};
const onScroll = ()=>{
if (!ticking) {
requestAnimationFrame(updateScrollDir);
ticking = true;
}
};
let eventTarget = scrollableElement;
if (scrollableElement === document.documentElement) {
eventTarget = document;
}
eventTarget.addEventListener("scroll", onScroll);
return ()=>eventTarget.removeEventListener("scroll", onScroll);
}, []);
return scrollDir;
}