mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-01 18:32:52 +02:00
refactor: cleanup scroll handlers (#5709)
This commit is contained in:
parent
c12fe963c9
commit
e22170408a
3 changed files with 77 additions and 92 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue