From 2bccf04c0fecb364ccbbf4f8c898742dbfc2acde Mon Sep 17 00:00:00 2001 From: unix Date: Thu, 26 Mar 2020 11:55:19 +0800 Subject: [PATCH] feat: add body scroll hooks --- components/utils/use-body-scroll.ts | 56 +++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 components/utils/use-body-scroll.ts diff --git a/components/utils/use-body-scroll.ts b/components/utils/use-body-scroll.ts new file mode 100644 index 0000000..21a66df --- /dev/null +++ b/components/utils/use-body-scroll.ts @@ -0,0 +1,56 @@ +import { Dispatch, MutableRefObject, SetStateAction, useEffect, useRef, useState } from 'react' + +export type ElementStackItem = { + last: string +} + +const elementStack = new Map() + +const isIos = () => { + if (typeof window === 'undefined' || !window.navigator) return false + return /iP(ad|hone|od)/.test(window.navigator.platform) +} + +const touchHandler = (event: TouchEvent): boolean => { + if (event.touches.length > 1) return true + event.preventDefault() + return false +} + +const useBodyScroll = ( + elementRef?: MutableRefObject +): [boolean, Dispatch>] => { + if (typeof document === 'undefined') return [false, (t: boolean) => t] + const elRef = elementRef || useRef(document.body) + const [hidden, setHidden] = useState(false) + + useEffect(() => { + const lastOverflow = elRef.current.style.overflow + if (hidden) { + if (elementStack.has(elRef.current)) return + if (!isIos()) { + elRef.current.style.overflow = 'hidden' + } else { + document.addEventListener('touchmove', touchHandler, { passive: false }) + } + elementStack.set(elRef.current, { + last: lastOverflow, + }) + return + } + + // reset element overflow + if (!elementStack.has(elRef.current)) return + if (!isIos()) { + const store = elementStack.get(elRef.current) || { last: 'auto' } + elRef.current.style.overflow = store.last + } else { + document.removeEventListener('touchmove', touchHandler) + } + elementStack.delete(elRef.current) + }, [hidden, elRef.current]) + + return [hidden, setHidden] +} + +export default useBodyScroll