refactor: cleanup scroll handlers (#5709)

This commit is contained in:
Alexey Pyltsyn 2021-10-15 14:07:03 +03:00 committed by GitHub
parent c12fe963c9
commit e22170408a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 77 additions and 92 deletions

View file

@ -7,11 +7,14 @@
import React, {useRef, useState} from 'react';
import clsx from 'clsx';
import {useLocation} from '@docusaurus/router';
import {translate} from '@docusaurus/Translate';
import styles from './styles.module.css';
import {ThemeClassNames, useScrollPosition} from '@docusaurus/theme-common';
import {
ThemeClassNames,
useScrollPosition,
useLocationChange,
} from '@docusaurus/theme-common';
const threshold = 300;
@ -68,42 +71,52 @@ function useSmoothScrollToTop(): UseSmoothScrollTopReturn {
}
function BackToTopButton(): JSX.Element {
const location = useLocation();
const {smoothScrollTop, cancelScrollToTop} = useSmoothScrollToTop();
const [show, setShow] = useState(false);
const isFocusedAnchor = useRef(false);
const {smoothScrollTop, cancelScrollToTop} = useSmoothScrollToTop();
useScrollPosition(
({scrollY: scrollTop}, lastPosition) => {
// No lastPosition means component is just being mounted.
// Not really a scroll event from the user, so we ignore it
if (!lastPosition) {
return;
useScrollPosition(({scrollY: scrollTop}, lastPosition) => {
const lastScrollTop = lastPosition?.scrollY;
// No lastScrollTop means component is just being mounted.
// Not really a scroll event from the user, so we ignore it
if (!lastScrollTop) {
return;
}
if (isFocusedAnchor.current) {
isFocusedAnchor.current = false;
return;
}
const isScrollingUp = scrollTop < lastScrollTop;
if (!isScrollingUp) {
cancelScrollToTop();
}
if (scrollTop < threshold) {
setShow(false);
return;
}
if (isScrollingUp) {
const documentHeight = document.documentElement.scrollHeight;
const windowHeight = window.innerHeight;
if (scrollTop + windowHeight < documentHeight) {
setShow(true);
}
const lastScrollTop = lastPosition.scrollY;
} else {
setShow(false);
}
});
const isScrollingUp = scrollTop < lastScrollTop;
if (!isScrollingUp) {
cancelScrollToTop();
}
if (scrollTop < threshold) {
setShow(false);
return;
}
if (isScrollingUp) {
const documentHeight = document.documentElement.scrollHeight;
const windowHeight = window.innerHeight;
if (scrollTop + windowHeight < documentHeight) {
setShow(true);
}
} else {
setShow(false);
}
},
[location],
);
useLocationChange((locationChangeEvent) => {
if (locationChangeEvent.location.hash) {
isFocusedAnchor.current = true;
setShow(false);
}
});
return (
<button

View file

@ -5,78 +5,57 @@
* LICENSE file in the root directory of this source tree.
*/
import {useState, useCallback, useEffect, useRef} from 'react';
import {useLocation} from '@docusaurus/router';
import {useState, useCallback, useRef} from 'react';
import {useLocationChange, useScrollPosition} from '@docusaurus/theme-common';
import type {useHideableNavbarReturns} from '@theme/hooks/useHideableNavbar';
const useHideableNavbar = (hideOnScroll: boolean): useHideableNavbarReturns => {
const location = useLocation();
const [isNavbarVisible, setIsNavbarVisible] = useState(hideOnScroll);
const isFocusedAnchor = useRef(false);
const [navbarHeight, setNavbarHeight] = useState(0);
const navbarHeight = useRef(0);
const navbarRef = useCallback((node: HTMLElement | null) => {
if (node !== null) {
setNavbarHeight(node.getBoundingClientRect().height);
navbarHeight.current = node.getBoundingClientRect().height;
}
}, []);
useScrollPosition(
(currentPosition, lastPosition) => {
const scrollTop = currentPosition.scrollY;
const lastScrollTop = lastPosition?.scrollY;
if (!hideOnScroll) {
return;
}
useScrollPosition((currentPosition, lastPosition) => {
if (!hideOnScroll) {
return;
}
if (scrollTop < navbarHeight) {
setIsNavbarVisible(true);
return;
}
if (isFocusedAnchor.current) {
isFocusedAnchor.current = false;
return;
}
if (isFocusedAnchor.current) {
isFocusedAnchor.current = false;
setIsNavbarVisible(false);
return;
}
const scrollTop = currentPosition.scrollY;
const lastScrollTop = lastPosition?.scrollY;
const documentHeight =
document.documentElement.scrollHeight - navbarHeight.current;
const windowHeight = window.innerHeight;
if (lastScrollTop && scrollTop === 0) {
setIsNavbarVisible(true);
}
const documentHeight =
document.documentElement.scrollHeight - navbarHeight;
const windowHeight = window.innerHeight;
if (lastScrollTop && scrollTop >= lastScrollTop) {
setIsNavbarVisible(false);
} else if (scrollTop + windowHeight < documentHeight) {
setIsNavbarVisible(true);
}
},
[navbarHeight, isFocusedAnchor],
);
if (lastScrollTop && scrollTop >= lastScrollTop) {
setIsNavbarVisible(false);
} else if (scrollTop + windowHeight < documentHeight) {
setIsNavbarVisible(true);
}
});
useLocationChange((locationChangeEvent) => {
if (!hideOnScroll || locationChangeEvent.location.hash) {
if (!hideOnScroll) {
return;
}
if (locationChangeEvent.location.hash) {
isFocusedAnchor.current = true;
setIsNavbarVisible(false);
return;
}
setIsNavbarVisible(true);
});
useEffect(() => {
if (!hideOnScroll) {
return;
}
if (!location.hash) {
return;
}
isFocusedAnchor.current = true;
}, [location.hash]);
return {
navbarRef,
isNavbarVisible,

View file

@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/
import {useEffect, useRef} from 'react';
import {useEffect} from 'react';
import {useLocation} from '@docusaurus/router';
import {Location} from '@docusaurus/history';
import {usePrevious} from './usePrevious';
@ -20,15 +20,8 @@ type OnLocationChange = (locationChangeEvent: LocationChangeEvent) => void;
export function useLocationChange(onLocationChange: OnLocationChange): void {
const location = useLocation();
const previousLocation = usePrevious(location);
const isFirst = useRef<boolean>(true);
useEffect(() => {
// Prevent first effect to trigger the listener on mount
if (isFirst.current) {
isFirst.current = false;
return;
}
onLocationChange({
location,
previousLocation,