UNPKG

@mantine/hooks

Version:

A collection of 50+ hooks for state and UI management

1 lines 7.41 kB
{"version":3,"file":"use-headroom.cjs","names":["useScrollDirection","useWindowScroll"],"sources":["../../src/use-headroom/use-headroom.ts"],"sourcesContent":["import { useEffectEvent, useRef } from 'react';\nimport { useIsomorphicEffect } from '../use-isomorphic-effect/use-isomorphic-effect';\nimport { useScrollDirection } from '../use-scroll-direction/use-scroll-direction';\nimport { useWindowScroll } from '../use-window-scroll/use-window-scroll';\n\nexport { useScrollDirection } from '../use-scroll-direction/use-scroll-direction';\n\nexport const isFixed = (current: number, fixedAt: number) => current <= fixedAt;\nexport const isPinned = (current: number, previous: number) => current <= previous;\nexport const isReleased = (current: number, previous: number, fixedAt: number) =>\n !isPinned(current, previous) && !isFixed(current, fixedAt);\n\nexport const isPinnedOrReleased = (\n current: number,\n fixedAt: number,\n isCurrentlyPinnedRef: React.RefObject<boolean>,\n isScrollingUp: boolean,\n onPin?: () => void,\n onRelease?: () => void\n) => {\n const isInFixedPosition = isFixed(current, fixedAt);\n if (isInFixedPosition && !isCurrentlyPinnedRef.current) {\n isCurrentlyPinnedRef.current = true;\n onPin?.();\n } else if (!isInFixedPosition && isScrollingUp && !isCurrentlyPinnedRef.current) {\n isCurrentlyPinnedRef.current = true;\n onPin?.();\n } else if (!isInFixedPosition && !isScrollingUp && isCurrentlyPinnedRef.current) {\n isCurrentlyPinnedRef.current = false;\n onRelease?.();\n }\n};\n\nexport interface UseHeadroomInput {\n /** Number in px at which element should be fixed */\n fixedAt?: number;\n\n /** Number of px to scroll to fully reveal or hide the element, 100 by default */\n scrollDistance?: number;\n\n /** Called when element is pinned */\n onPin?: () => void;\n\n /** Called when element is at fixed position */\n onFix?: () => void;\n\n /** Called when element is unpinned */\n onRelease?: () => void;\n}\n\nexport interface UseHeadroomReturnValue {\n /** True when the element is at least partially visible */\n pinned: boolean;\n\n /** Reveal progress: 0 = fully hidden, 1 = fully visible */\n scrollProgress: number;\n}\n\nexport function useHeadroom({\n fixedAt = 0,\n scrollDistance = 100,\n onPin,\n onFix,\n onRelease,\n}: UseHeadroomInput = {}): UseHeadroomReturnValue {\n const isCurrentlyPinnedRef = useRef(false);\n const scrollDirection = useScrollDirection();\n const isScrollingUp = scrollDirection === 'up';\n const [{ y: scrollPosition }] = useWindowScroll();\n\n const onPinEvent = useEffectEvent(() => onPin?.());\n const onReleaseEvent = useEffectEvent(() => onRelease?.());\n const onFixEvent = useEffectEvent(() => onFix?.());\n\n useIsomorphicEffect(() => {\n isPinnedOrReleased(\n scrollPosition,\n fixedAt,\n isCurrentlyPinnedRef,\n isScrollingUp,\n onPinEvent,\n onReleaseEvent\n );\n }, [scrollPosition, fixedAt, isScrollingUp]);\n\n useIsomorphicEffect(() => {\n if (isFixed(scrollPosition, fixedAt)) {\n onFixEvent();\n }\n }, [scrollPosition, fixedAt]);\n\n // Refs for scroll-progress tracking. Mutated during render (safe for refs).\n const currentlyFixed = isFixed(scrollPosition, fixedAt);\n const prevIsFixedRef = useRef(currentlyFixed);\n const directionChangeScrollYRef = useRef(scrollPosition);\n const progressAtDirectionChangeRef = useRef(currentlyFixed ? 1 : 0);\n const prevIsScrollingUpRef = useRef(isScrollingUp);\n\n // Detect fixed-zone transitions first. When leaving the fixed zone the baseline\n // is anchored at fixedAt (not the current scroll position) so the delta is measured\n // from where the element was last fully visible, regardless of how scroll position\n // was initialised on the first render.\n if (prevIsFixedRef.current !== currentlyFixed) {\n prevIsFixedRef.current = currentlyFixed;\n\n if (!currentlyFixed) {\n directionChangeScrollYRef.current = fixedAt;\n progressAtDirectionChangeRef.current = 1;\n } else {\n directionChangeScrollYRef.current = scrollPosition;\n progressAtDirectionChangeRef.current = 1;\n }\n\n prevIsScrollingUpRef.current = isScrollingUp;\n }\n\n // When scroll direction changes outside the fixed zone, save the current progress\n // so the next direction accumulates from that point (handles partial reveals).\n if (!currentlyFixed && prevIsScrollingUpRef.current !== isScrollingUp) {\n const transitionDelta = Math.abs(scrollPosition - directionChangeScrollYRef.current);\n const transitionProgress = prevIsScrollingUpRef.current\n ? Math.min(progressAtDirectionChangeRef.current + transitionDelta / scrollDistance, 1)\n : Math.max(progressAtDirectionChangeRef.current - transitionDelta / scrollDistance, 0);\n\n prevIsScrollingUpRef.current = isScrollingUp;\n directionChangeScrollYRef.current = scrollPosition;\n progressAtDirectionChangeRef.current = transitionProgress;\n }\n\n let scrollProgress: number;\n\n if (currentlyFixed) {\n scrollProgress = 1;\n } else {\n const scrollDelta = Math.abs(scrollPosition - directionChangeScrollYRef.current);\n\n if (isScrollingUp) {\n scrollProgress = Math.min(\n progressAtDirectionChangeRef.current + scrollDelta / scrollDistance,\n 1\n );\n } else {\n scrollProgress = Math.max(\n progressAtDirectionChangeRef.current - scrollDelta / scrollDistance,\n 0\n );\n }\n }\n\n return { pinned: scrollProgress > 0, scrollProgress };\n}\n\nexport namespace useHeadroom {\n export type Input = UseHeadroomInput;\n export type ReturnValue = UseHeadroomReturnValue;\n}\n"],"mappings":";;;;;;AAOA,MAAa,WAAW,SAAiB,YAAoB,WAAW;AAKxE,MAAa,sBACX,SACA,SACA,sBACA,eACA,OACA,cACG;CACH,MAAM,oBAAoB,QAAQ,SAAS,QAAQ;AACnD,KAAI,qBAAqB,CAAC,qBAAqB,SAAS;AACtD,uBAAqB,UAAU;AAC/B,WAAS;YACA,CAAC,qBAAqB,iBAAiB,CAAC,qBAAqB,SAAS;AAC/E,uBAAqB,UAAU;AAC/B,WAAS;YACA,CAAC,qBAAqB,CAAC,iBAAiB,qBAAqB,SAAS;AAC/E,uBAAqB,UAAU;AAC/B,eAAa;;;AA6BjB,SAAgB,YAAY,EAC1B,UAAU,GACV,iBAAiB,KACjB,OACA,OACA,cACoB,EAAE,EAA0B;CAChD,MAAM,wBAAA,GAAA,MAAA,QAA8B,MAAM;CAE1C,MAAM,gBADkBA,6BAAAA,oBAAoB,KACF;CAC1C,MAAM,CAAC,EAAE,GAAG,oBAAoBC,0BAAAA,iBAAiB;CAEjD,MAAM,cAAA,GAAA,MAAA,sBAAkC,SAAS,CAAC;CAClD,MAAM,kBAAA,GAAA,MAAA,sBAAsC,aAAa,CAAC;CAC1D,MAAM,cAAA,GAAA,MAAA,sBAAkC,SAAS,CAAC;AAElD,+BAAA,0BAA0B;AACxB,qBACE,gBACA,SACA,sBACA,eACA,YACA,eACD;IACA;EAAC;EAAgB;EAAS;EAAc,CAAC;AAE5C,+BAAA,0BAA0B;AACxB,MAAI,QAAQ,gBAAgB,QAAQ,CAClC,aAAY;IAEb,CAAC,gBAAgB,QAAQ,CAAC;CAG7B,MAAM,iBAAiB,QAAQ,gBAAgB,QAAQ;CACvD,MAAM,kBAAA,GAAA,MAAA,QAAwB,eAAe;CAC7C,MAAM,6BAAA,GAAA,MAAA,QAAmC,eAAe;CACxD,MAAM,gCAAA,GAAA,MAAA,QAAsC,iBAAiB,IAAI,EAAE;CACnE,MAAM,wBAAA,GAAA,MAAA,QAA8B,cAAc;AAMlD,KAAI,eAAe,YAAY,gBAAgB;AAC7C,iBAAe,UAAU;AAEzB,MAAI,CAAC,gBAAgB;AACnB,6BAA0B,UAAU;AACpC,gCAA6B,UAAU;SAClC;AACL,6BAA0B,UAAU;AACpC,gCAA6B,UAAU;;AAGzC,uBAAqB,UAAU;;AAKjC,KAAI,CAAC,kBAAkB,qBAAqB,YAAY,eAAe;EACrE,MAAM,kBAAkB,KAAK,IAAI,iBAAiB,0BAA0B,QAAQ;EACpF,MAAM,qBAAqB,qBAAqB,UAC5C,KAAK,IAAI,6BAA6B,UAAU,kBAAkB,gBAAgB,EAAE,GACpF,KAAK,IAAI,6BAA6B,UAAU,kBAAkB,gBAAgB,EAAE;AAExF,uBAAqB,UAAU;AAC/B,4BAA0B,UAAU;AACpC,+BAA6B,UAAU;;CAGzC,IAAI;AAEJ,KAAI,eACF,kBAAiB;MACZ;EACL,MAAM,cAAc,KAAK,IAAI,iBAAiB,0BAA0B,QAAQ;AAEhF,MAAI,cACF,kBAAiB,KAAK,IACpB,6BAA6B,UAAU,cAAc,gBACrD,EACD;MAED,kBAAiB,KAAK,IACpB,6BAA6B,UAAU,cAAc,gBACrD,EACD;;AAIL,QAAO;EAAE,QAAQ,iBAAiB;EAAG;EAAgB"}