mirror of
https://github.com/facebook/docusaurus.git
synced 2025-06-07 05:12:31 +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 React, {useRef, useState} from 'react';
|
||||||
import clsx from 'clsx';
|
import clsx from 'clsx';
|
||||||
import {useLocation} from '@docusaurus/router';
|
|
||||||
import {translate} from '@docusaurus/Translate';
|
import {translate} from '@docusaurus/Translate';
|
||||||
|
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
import {ThemeClassNames, useScrollPosition} from '@docusaurus/theme-common';
|
import {
|
||||||
|
ThemeClassNames,
|
||||||
|
useScrollPosition,
|
||||||
|
useLocationChange,
|
||||||
|
} from '@docusaurus/theme-common';
|
||||||
|
|
||||||
const threshold = 300;
|
const threshold = 300;
|
||||||
|
|
||||||
|
@ -68,18 +71,23 @@ function useSmoothScrollToTop(): UseSmoothScrollTopReturn {
|
||||||
}
|
}
|
||||||
|
|
||||||
function BackToTopButton(): JSX.Element {
|
function BackToTopButton(): JSX.Element {
|
||||||
const location = useLocation();
|
|
||||||
const {smoothScrollTop, cancelScrollToTop} = useSmoothScrollToTop();
|
|
||||||
const [show, setShow] = useState(false);
|
const [show, setShow] = useState(false);
|
||||||
|
const isFocusedAnchor = useRef(false);
|
||||||
|
const {smoothScrollTop, cancelScrollToTop} = useSmoothScrollToTop();
|
||||||
|
|
||||||
useScrollPosition(
|
useScrollPosition(({scrollY: scrollTop}, lastPosition) => {
|
||||||
({scrollY: scrollTop}, lastPosition) => {
|
const lastScrollTop = lastPosition?.scrollY;
|
||||||
// No lastPosition means component is just being mounted.
|
|
||||||
|
// No lastScrollTop means component is just being mounted.
|
||||||
// Not really a scroll event from the user, so we ignore it
|
// Not really a scroll event from the user, so we ignore it
|
||||||
if (!lastPosition) {
|
if (!lastScrollTop) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFocusedAnchor.current) {
|
||||||
|
isFocusedAnchor.current = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const lastScrollTop = lastPosition.scrollY;
|
|
||||||
|
|
||||||
const isScrollingUp = scrollTop < lastScrollTop;
|
const isScrollingUp = scrollTop < lastScrollTop;
|
||||||
|
|
||||||
|
@ -101,9 +109,14 @@ function BackToTopButton(): JSX.Element {
|
||||||
} else {
|
} else {
|
||||||
setShow(false);
|
setShow(false);
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
[location],
|
|
||||||
);
|
useLocationChange((locationChangeEvent) => {
|
||||||
|
if (locationChangeEvent.location.hash) {
|
||||||
|
isFocusedAnchor.current = true;
|
||||||
|
setShow(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -5,47 +5,34 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {useState, useCallback, useEffect, useRef} from 'react';
|
import {useState, useCallback, useRef} from 'react';
|
||||||
import {useLocation} from '@docusaurus/router';
|
|
||||||
import {useLocationChange, useScrollPosition} from '@docusaurus/theme-common';
|
import {useLocationChange, useScrollPosition} from '@docusaurus/theme-common';
|
||||||
import type {useHideableNavbarReturns} from '@theme/hooks/useHideableNavbar';
|
import type {useHideableNavbarReturns} from '@theme/hooks/useHideableNavbar';
|
||||||
|
|
||||||
const useHideableNavbar = (hideOnScroll: boolean): useHideableNavbarReturns => {
|
const useHideableNavbar = (hideOnScroll: boolean): useHideableNavbarReturns => {
|
||||||
const location = useLocation();
|
|
||||||
const [isNavbarVisible, setIsNavbarVisible] = useState(hideOnScroll);
|
const [isNavbarVisible, setIsNavbarVisible] = useState(hideOnScroll);
|
||||||
const isFocusedAnchor = useRef(false);
|
const isFocusedAnchor = useRef(false);
|
||||||
const [navbarHeight, setNavbarHeight] = useState(0);
|
const navbarHeight = useRef(0);
|
||||||
const navbarRef = useCallback((node: HTMLElement | null) => {
|
const navbarRef = useCallback((node: HTMLElement | null) => {
|
||||||
if (node !== null) {
|
if (node !== null) {
|
||||||
setNavbarHeight(node.getBoundingClientRect().height);
|
navbarHeight.current = node.getBoundingClientRect().height;
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useScrollPosition(
|
useScrollPosition((currentPosition, lastPosition) => {
|
||||||
(currentPosition, lastPosition) => {
|
|
||||||
const scrollTop = currentPosition.scrollY;
|
|
||||||
const lastScrollTop = lastPosition?.scrollY;
|
|
||||||
if (!hideOnScroll) {
|
if (!hideOnScroll) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scrollTop < navbarHeight) {
|
|
||||||
setIsNavbarVisible(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFocusedAnchor.current) {
|
if (isFocusedAnchor.current) {
|
||||||
isFocusedAnchor.current = false;
|
isFocusedAnchor.current = false;
|
||||||
setIsNavbarVisible(false);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lastScrollTop && scrollTop === 0) {
|
const scrollTop = currentPosition.scrollY;
|
||||||
setIsNavbarVisible(true);
|
const lastScrollTop = lastPosition?.scrollY;
|
||||||
}
|
|
||||||
|
|
||||||
const documentHeight =
|
const documentHeight =
|
||||||
document.documentElement.scrollHeight - navbarHeight;
|
document.documentElement.scrollHeight - navbarHeight.current;
|
||||||
const windowHeight = window.innerHeight;
|
const windowHeight = window.innerHeight;
|
||||||
|
|
||||||
if (lastScrollTop && scrollTop >= lastScrollTop) {
|
if (lastScrollTop && scrollTop >= lastScrollTop) {
|
||||||
|
@ -53,30 +40,22 @@ const useHideableNavbar = (hideOnScroll: boolean): useHideableNavbarReturns => {
|
||||||
} else if (scrollTop + windowHeight < documentHeight) {
|
} else if (scrollTop + windowHeight < documentHeight) {
|
||||||
setIsNavbarVisible(true);
|
setIsNavbarVisible(true);
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
[navbarHeight, isFocusedAnchor],
|
|
||||||
);
|
|
||||||
|
|
||||||
useLocationChange((locationChangeEvent) => {
|
useLocationChange((locationChangeEvent) => {
|
||||||
if (!hideOnScroll || locationChangeEvent.location.hash) {
|
if (!hideOnScroll) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (locationChangeEvent.location.hash) {
|
||||||
|
isFocusedAnchor.current = true;
|
||||||
|
setIsNavbarVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsNavbarVisible(true);
|
setIsNavbarVisible(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!hideOnScroll) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!location.hash) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isFocusedAnchor.current = true;
|
|
||||||
}, [location.hash]);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
navbarRef,
|
navbarRef,
|
||||||
isNavbarVisible,
|
isNavbarVisible,
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* 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 {useLocation} from '@docusaurus/router';
|
||||||
import {Location} from '@docusaurus/history';
|
import {Location} from '@docusaurus/history';
|
||||||
import {usePrevious} from './usePrevious';
|
import {usePrevious} from './usePrevious';
|
||||||
|
@ -20,15 +20,8 @@ type OnLocationChange = (locationChangeEvent: LocationChangeEvent) => void;
|
||||||
export function useLocationChange(onLocationChange: OnLocationChange): void {
|
export function useLocationChange(onLocationChange: OnLocationChange): void {
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const previousLocation = usePrevious(location);
|
const previousLocation = usePrevious(location);
|
||||||
const isFirst = useRef<boolean>(true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Prevent first effect to trigger the listener on mount
|
|
||||||
if (isFirst.current) {
|
|
||||||
isFirst.current = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onLocationChange({
|
onLocationChange({
|
||||||
location,
|
location,
|
||||||
previousLocation,
|
previousLocation,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue