@mantine/hooks
Version:
A collection of 50+ hooks for state and UI management
76 lines (75 loc) • 3.47 kB
JavaScript
"use client";
import { useIsomorphicEffect } from "../use-isomorphic-effect/use-isomorphic-effect.mjs";
import { useWindowScroll } from "../use-window-scroll/use-window-scroll.mjs";
import { useScrollDirection } from "../use-scroll-direction/use-scroll-direction.mjs";
import { useEffectEvent, useRef } from "react";
//#region packages/@mantine/hooks/src/use-headroom/use-headroom.ts
const isFixed = (current, fixedAt) => current <= fixedAt;
const isPinnedOrReleased = (current, fixedAt, isCurrentlyPinnedRef, isScrollingUp, onPin, onRelease) => {
const isInFixedPosition = isFixed(current, fixedAt);
if (isInFixedPosition && !isCurrentlyPinnedRef.current) {
isCurrentlyPinnedRef.current = true;
onPin?.();
} else if (!isInFixedPosition && isScrollingUp && !isCurrentlyPinnedRef.current) {
isCurrentlyPinnedRef.current = true;
onPin?.();
} else if (!isInFixedPosition && !isScrollingUp && isCurrentlyPinnedRef.current) {
isCurrentlyPinnedRef.current = false;
onRelease?.();
}
};
function useHeadroom({ fixedAt = 0, scrollDistance = 100, onPin, onFix, onRelease } = {}) {
const isCurrentlyPinnedRef = useRef(false);
const isScrollingUp = useScrollDirection() === "up";
const [{ y: scrollPosition }] = useWindowScroll();
const onPinEvent = useEffectEvent(() => onPin?.());
const onReleaseEvent = useEffectEvent(() => onRelease?.());
const onFixEvent = useEffectEvent(() => onFix?.());
useIsomorphicEffect(() => {
isPinnedOrReleased(scrollPosition, fixedAt, isCurrentlyPinnedRef, isScrollingUp, onPinEvent, onReleaseEvent);
}, [
scrollPosition,
fixedAt,
isScrollingUp
]);
useIsomorphicEffect(() => {
if (isFixed(scrollPosition, fixedAt)) onFixEvent();
}, [scrollPosition, fixedAt]);
const currentlyFixed = isFixed(scrollPosition, fixedAt);
const prevIsFixedRef = useRef(currentlyFixed);
const directionChangeScrollYRef = useRef(scrollPosition);
const progressAtDirectionChangeRef = useRef(currentlyFixed ? 1 : 0);
const prevIsScrollingUpRef = useRef(isScrollingUp);
if (prevIsFixedRef.current !== currentlyFixed) {
prevIsFixedRef.current = currentlyFixed;
if (!currentlyFixed) {
directionChangeScrollYRef.current = fixedAt;
progressAtDirectionChangeRef.current = 1;
} else {
directionChangeScrollYRef.current = scrollPosition;
progressAtDirectionChangeRef.current = 1;
}
prevIsScrollingUpRef.current = isScrollingUp;
}
if (!currentlyFixed && prevIsScrollingUpRef.current !== isScrollingUp) {
const transitionDelta = Math.abs(scrollPosition - directionChangeScrollYRef.current);
const transitionProgress = prevIsScrollingUpRef.current ? Math.min(progressAtDirectionChangeRef.current + transitionDelta / scrollDistance, 1) : Math.max(progressAtDirectionChangeRef.current - transitionDelta / scrollDistance, 0);
prevIsScrollingUpRef.current = isScrollingUp;
directionChangeScrollYRef.current = scrollPosition;
progressAtDirectionChangeRef.current = transitionProgress;
}
let scrollProgress;
if (currentlyFixed) scrollProgress = 1;
else {
const scrollDelta = Math.abs(scrollPosition - directionChangeScrollYRef.current);
if (isScrollingUp) scrollProgress = Math.min(progressAtDirectionChangeRef.current + scrollDelta / scrollDistance, 1);
else scrollProgress = Math.max(progressAtDirectionChangeRef.current - scrollDelta / scrollDistance, 0);
}
return {
pinned: scrollProgress > 0,
scrollProgress
};
}
//#endregion
export { useHeadroom };
//# sourceMappingURL=use-headroom.mjs.map