diff --git a/packages/docusaurus/lib/client/App.js b/packages/docusaurus/lib/client/App.js index f9ac309c68..9ada08c456 100644 --- a/packages/docusaurus/lib/client/App.js +++ b/packages/docusaurus/lib/client/App.js @@ -7,13 +7,13 @@ import React, {useState} from 'react'; import {renderRoutes} from 'react-router-config'; -import ReactListenerProvider from 'react-listener-provider'; import Head from '@docusaurus/Head'; // eslint-disable-line import routes from '@generated/routes'; // eslint-disable-line import metadata from '@generated/metadata'; // eslint-disable-line import siteConfig from '@generated/docusaurus.config'; //eslint-disable-line import DocusaurusContext from '@docusaurus/context'; // eslint-disable-line +import PendingNavigation from './PendingNavigation'; function App() { const [context, setContext] = useState({}); @@ -36,7 +36,9 @@ function App() { type="text/css" /> - {renderRoutes(routes)} + + {renderRoutes(routes)} + ); } diff --git a/packages/docusaurus/lib/client/PendingNavigation.js b/packages/docusaurus/lib/client/PendingNavigation.js new file mode 100644 index 0000000000..f792558d96 --- /dev/null +++ b/packages/docusaurus/lib/client/PendingNavigation.js @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React from 'react'; +import {Route, withRouter} from 'react-router-dom'; +import nprogress from 'nprogress'; +import 'nprogress/nprogress.css'; +import preload from './preload'; + +nprogress.configure({showSpinner: false}); + +class PendingNavigation extends React.Component { + constructor(props) { + super(props); + + this.progressBarTimeout = null; + this.state = { + previousLocation: null, + }; + } + + componentWillReceiveProps(nextProps) { + const navigated = nextProps.location !== this.props.location; + const {routes, delay = 1000} = this.props; + + if (navigated) { + window.scrollTo(0, 0); + + this.startProgressBar(delay); + // save the location so we can render the old screen + this.setState({ + previousLocation: this.props.location, + }); + + // load data while the old screen remains + preload(routes, nextProps.location.pathname) + .then(() => { + this.setState( + { + previousLocation: null, + }, + this.stopProgressBar, + ); + }) + .catch(e => console.warn(e)); + } + } + + clearProgressBarTimeout() { + if (this.progressBarTimeout) { + clearTimeout(this.progressBarTimeout); + this.progressBarTimeout = null; + } + } + + startProgressBar(delay) { + this.clearProgressBarTimeout(); + this.progressBarTimeout = setTimeout(() => { + nprogress.start(); + }, delay); + } + + stopProgressBar() { + this.clearProgressBarTimeout(); + nprogress.done(); + } + + render() { + const {children, location} = this.props; + const {previousLocation} = this.state; + + // use a controlled to trick all descendants into + // rendering the old location + return ( + children} /> + ); + } +} + +export default withRouter(PendingNavigation); diff --git a/packages/docusaurus/lib/client/exports/Link.js b/packages/docusaurus/lib/client/exports/Link.js index b7c7b03726..58ad2aeec9 100644 --- a/packages/docusaurus/lib/client/exports/Link.js +++ b/packages/docusaurus/lib/client/exports/Link.js @@ -6,15 +6,15 @@ */ import React, {useEffect} from 'react'; -import Perimeter from 'react-perimeter'; import {NavLink} from 'react-router-dom'; const internalRegex = /^\/(?!\/)/; function Link(props) { - const {to, href, preloadProximity = 20} = props; + const {to, href} = props; const targetLink = to || href; const isInternal = internalRegex.test(targetLink); + let preloaded = false; const IOSupported = typeof window !== 'undefined' && 'IntersectionObserver' in window; @@ -47,6 +47,13 @@ function Link(props) { } }; + const onMouseEnter = () => { + if (!preloaded) { + window.docusaurus.preload(targetLink); + preloaded = true; + } + }; + useEffect(() => { // If IO is not supported. We prefetch by default (only once) if (!IOSupported && isInternal) { @@ -64,12 +71,12 @@ function Link(props) { // eslint-disable-next-line jsx-a11y/anchor-has-content ) : ( - window.docusaurus.preload(targetLink)} - once> - - + ); } diff --git a/packages/docusaurus/package.json b/packages/docusaurus/package.json index 8f4a5c4669..61364dc75e 100644 --- a/packages/docusaurus/package.json +++ b/packages/docusaurus/package.json @@ -55,13 +55,12 @@ "is-wsl": "^1.1.0", "lodash": "^4.17.11", "mini-css-extract-plugin": "^0.4.1", + "nprogress": "^0.2.0", "portfinder": "^1.0.13", "react-dev-utils": "^8.0.0", "react-helmet": "^6.0.0-beta", - "react-listener-provider": "^0.2.0", "react-loadable": "^5.5.0", "react-loadable-ssr-addon": "^0.1.6", - "react-perimeter": "^0.4.0", "react-router-config": "^5.0.0", "react-router-dom": "^5.0.0", "semver": "^6.0.0", diff --git a/yarn.lock b/yarn.lock index c5be8a6519..357ad2eb8d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9509,6 +9509,11 @@ npm-which@^3.0.1: gauge "~2.7.3" set-blocking "~2.0.0" +nprogress@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/nprogress/-/nprogress-0.2.0.tgz#cb8f34c53213d895723fcbab907e9422adbcafb1" + integrity sha1-y480xTIT2JVyP8urkH6UIq28r7E= + nth-check@^1.0.2, nth-check@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" @@ -10986,11 +10991,6 @@ react-is@^16.8.1, react-is@^16.8.4: resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.5.tgz#c54ac229dd66b5afe0de5acbe47647c3da692ff8" integrity sha512-sudt2uq5P/2TznPV4Wtdi+Lnq3yaYW8LfvPKLM9BKD8jJNBkxMVyB0C9/GmVhLw7Jbdmndk/73n7XQGeN9A3QQ== -react-listener-provider@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/react-listener-provider/-/react-listener-provider-0.2.0.tgz#fb6ce123f9e20ad948f75906ddf647db1df31f18" - integrity sha1-+2zhI/niCtlI91kG3fZH2x3zHxg= - react-loadable-ssr-addon@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/react-loadable-ssr-addon/-/react-loadable-ssr-addon-0.1.6.tgz#de3e45f2b3876dc7b9afae19138370f0588b7300" @@ -11003,13 +11003,6 @@ react-loadable@^5.5.0: dependencies: prop-types "^15.5.0" -react-perimeter@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/react-perimeter/-/react-perimeter-0.4.0.tgz#be86b5560bb96650272aaf7bd37639460b82645b" - integrity sha512-hSjth5oh4L/ZVR6PfR9eu7B1lil0AteGXLn7ki7YNJI7bkKlZf28pwFyFKNIXkoRJT3FiD01kGYkAmIz1k8b9A== - optionalDependencies: - react-listener-provider "^0.2.0" - react-router-config@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/react-router-config/-/react-router-config-5.0.0.tgz#3d7e298dc64479bf9e1cc77080b8778e9a8d966c"