Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): rework client modules lifecycles, officially make API public #6732

Merged
merged 21 commits into from
Apr 29, 2022
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default (function analyticsModule() {
}

return {
onRouteUpdate({location}: {location: Location}) {
onRouteDidUpdate({location}: {location: Location}) {
// Set page so that subsequent hits on this page are attributed
// to this page. This is recommended for Single-page Applications.
window.ga('set', 'page', location.pathname);
Expand Down
2 changes: 1 addition & 1 deletion packages/docusaurus-plugin-google-gtag/src/gtag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default (function gtagModule() {
.default as PluginOptions;

return {
onRouteUpdate({location}: {location: Location}) {
onRouteDidUpdate({location}: {location: Location}) {
// Always refer to the variable on window in case it gets overridden
// elsewhere.
window.gtag('config', trackingID, {
Expand Down
2 changes: 2 additions & 0 deletions packages/docusaurus-theme-classic/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
"copy-text-to-clipboard": "^3.0.1",
"infima": "0.2.0-alpha.38",
"lodash": "^4.17.21",
"nprogress": "^0.2.0",
"postcss": "^8.4.12",
"prism-react-renderer": "^1.3.1",
"prismjs": "^1.28.0",
Expand All @@ -48,6 +49,7 @@
"@docusaurus/module-type-aliases": "2.0.0-beta.18",
"@docusaurus/types": "2.0.0-beta.18",
"@types/mdx-js__react": "^1.5.5",
"@types/nprogress": "^0.2.0",
"@types/prismjs": "^1.26.0",
"@types/rtlcss": "^3.1.4",
"cross-env": "^7.0.3",
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus-theme-classic/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ export default function docusaurusThemeClassic(
require.resolve(getInfimaCSSFile(direction)),
'./prism-include-languages',
'./admonitions.css',
'./nprogress',
];

if (customCss) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,16 @@
* https://github.com/rstacruz/nprogress/blob/master/nprogress.css
*/

:root {
--docusaurus-progress-bar-color: var(--ifm-color-primary);
}

#nprogress {
pointer-events: none;
}

#nprogress .bar {
background: #29d;
background: var(--docusaurus-progress-bar-color);
position: fixed;
z-index: 1031;
top: 0;
Expand All @@ -30,7 +34,8 @@
right: 0;
width: 100px;
height: 100%;
box-shadow: 0 0 10px #29d, 0 0 5px #29d;
box-shadow: 0 0 10px var(--docusaurus-progress-bar-color),
0 0 5px var(--docusaurus-progress-bar-color);
opacity: 1;
transform: rotate(3deg) translate(0, -4px);
}
34 changes: 34 additions & 0 deletions packages/docusaurus-theme-classic/src/nprogress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import nprogress from 'nprogress';
import './nprogress.css';
import type {Location} from 'history';

nprogress.configure({showSpinner: false});

const delay = 200;

export function onRouteUpdate({
location,
previousLocation,
}: {
location: Location;
previousLocation: Location | null;
}): (() => void) | undefined {
if (location.pathname !== previousLocation?.pathname) {
const progressBarTimeout = window.setTimeout(() => {
nprogress.start();
}, delay);
return () => window.clearTimeout(progressBarTimeout);
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
}
return undefined;
}

export function onRouteDidUpdate(): void {
nprogress.done();
}
3 changes: 1 addition & 2 deletions packages/docusaurus-theme-classic/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@
"lib": ["DOM", "ES2019"],
"module": "esnext",
"noEmit": true,
"jsx": "react-native",
"baseUrl": "src"
"jsx": "react-native"
},
"include": ["src/"]
}
7 changes: 5 additions & 2 deletions packages/docusaurus-types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -601,11 +601,14 @@ export type TOCItem = {
};

export type ClientModule = {
onRouteDidUpdate?: (args: {
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
previousLocation: Location | null;
location: Location;
}) => (() => void) | void;
onRouteUpdate?: (args: {
previousLocation: Location | null;
location: Location;
}) => void;
onRouteUpdateDelayed?: (args: {location: Location}) => void;
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
}) => (() => void) | void;
};

/** What the user configures. */
Expand Down
2 changes: 0 additions & 2 deletions packages/docusaurus/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@
"leven": "^3.1.0",
"lodash": "^4.17.21",
"mini-css-extract-plugin": "^2.6.0",
"nprogress": "^0.2.0",
"postcss": "^8.4.12",
"postcss-loader": "^6.2.1",
"prompts": "^2.4.2",
Expand Down Expand Up @@ -108,7 +107,6 @@
"@docusaurus/module-type-aliases": "2.0.0-beta.18",
"@docusaurus/types": "2.0.0-beta.18",
"@types/detect-port": "^1.3.2",
"@types/nprogress": "^0.2.0",
"@types/react-dom": "^18.0.2",
"@types/react-router-config": "^5.0.6",
"@types/rtl-detect": "^1.0.0",
Expand Down
7 changes: 2 additions & 5 deletions packages/docusaurus/src/client/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
*/

import React from 'react';
import '@generated/client-modules';

import routes from '@generated/routes';
import {useLocation} from '@docusaurus/router';
Expand All @@ -19,8 +20,6 @@ import SiteMetadataDefaults from './SiteMetadataDefaults';
import Root from '@theme/Root';
import SiteMetadata from '@theme/SiteMetadata';

import './clientLifecyclesDispatcher';

// TODO, quick fix for CSS insertion order
import ErrorBoundary from '@docusaurus/ErrorBoundary';
import Error from '@theme/Error';
Expand All @@ -36,9 +35,7 @@ export default function App(): JSX.Element {
<SiteMetadataDefaults />
<SiteMetadata />
<BaseUrlIssueBanner />
<PendingNavigation
location={normalizeLocation(location)}
delay={200}>
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
<PendingNavigation location={normalizeLocation(location)}>
{routeElement}
</PendingNavigation>
</Root>
Expand Down
65 changes: 65 additions & 0 deletions packages/docusaurus/src/client/ClientLifecyclesDispatcher.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, {
useImperativeHandle,
useLayoutEffect,
type ReactElement,
} from 'react';
import clientModules from '@generated/client-modules';
import type {ClientModule} from '@docusaurus/types';
import type {Location} from 'history';

function dispatchLifecycleAction<K extends keyof ClientModule>(
lifecycleAction: K,
...args: Parameters<NonNullable<ClientModule[K]>>
): () => void {
const callbacks = clientModules.map((clientModule) => {
const lifecycleFunction = (clientModule?.default?.[lifecycleAction] ??
clientModule[lifecycleAction]) as
| ((
...a: Parameters<NonNullable<ClientModule[K]>>
) => (() => void) | void)
| undefined;

return lifecycleFunction?.(...args);
});
return () => callbacks.forEach((cb) => cb?.());
}

function ClientLifecyclesDispatcher(
{
children,
location,
previousLocation,
}: {
children: ReactElement;
location: Location;
previousLocation: Location | null;
},
ref: React.ForwardedRef<ClientModule>,
): JSX.Element {
useLayoutEffect(() => {
if (previousLocation !== location) {
const {hash} = location;
if (!hash) {
window.scrollTo(0, 0);
} else {
const id = decodeURIComponent(hash.substring(1));
const element = document.getElementById(id);
element?.scrollIntoView();
}
dispatchLifecycleAction('onRouteDidUpdate', {previousLocation, location});
}
}, [previousLocation, location]);
useImperativeHandle(ref, () => ({
onRouteUpdate: (args) => dispatchLifecycleAction('onRouteUpdate', args),
}));
return children;
}

export default React.forwardRef(ClientLifecyclesDispatcher);
68 changes: 21 additions & 47 deletions packages/docusaurus/src/client/PendingNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,12 @@

import React from 'react';
import {Route} from 'react-router-dom';
import nprogress from 'nprogress';

import clientLifecyclesDispatcher from './clientLifecyclesDispatcher';
import ClientLifecyclesDispatcher from './ClientLifecyclesDispatcher';
import preload from './preload';
import type {Location} from 'history';

import './nprogress.css';

nprogress.configure({showSpinner: false});
import type {ClientModule} from '@docusaurus/types';

type Props = {
readonly delay: number;
readonly location: Location;
readonly children: JSX.Element;
};
Expand All @@ -28,14 +22,16 @@ type State = {

class PendingNavigation extends React.Component<Props, State> {
private previousLocation: Location | null;
private progressBarTimeout: number | null;
private routeUpdateCb: (() => void) | undefined;
private clientLifecyclesDispatcher: React.RefObject<Required<ClientModule>>;

constructor(props: Props) {
super(props);

// previousLocation doesn't affect rendering, hence not stored in state.
this.previousLocation = null;
this.progressBarTimeout = null;
this.routeUpdateCb = undefined;
this.clientLifecyclesDispatcher = React.createRef();
this.state = {
nextRouteHasLoaded: true,
};
Expand All @@ -56,56 +52,34 @@ class PendingNavigation extends React.Component<Props, State> {
// Save the location first.
this.previousLocation = this.props.location;
this.setState({nextRouteHasLoaded: false});
this.startProgressBar();
this.routeUpdateCb =
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
this.clientLifecyclesDispatcher.current?.onRouteUpdate({
previousLocation: this.previousLocation,
location: nextLocation,
}) || undefined;

// Load data while the old screen remains.
preload(nextLocation.pathname)
.then(() => {
clientLifecyclesDispatcher.onRouteUpdate({
previousLocation: this.previousLocation,
location: nextLocation,
});
this.setState({nextRouteHasLoaded: true}, this.stopProgressBar);
const {hash} = nextLocation;
if (!hash) {
window.scrollTo(0, 0);
} else {
const id = decodeURIComponent(hash.substring(1));
const element = document.getElementById(id);
element?.scrollIntoView();
}
this.setState({nextRouteHasLoaded: true});
this.routeUpdateCb?.();
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
})
.catch((e) => console.warn(e));
return false;
}

private clearProgressBarTimeout() {
if (this.progressBarTimeout) {
window.clearTimeout(this.progressBarTimeout);
this.progressBarTimeout = null;
}
}

private startProgressBar() {
this.clearProgressBarTimeout();
this.progressBarTimeout = window.setTimeout(() => {
clientLifecyclesDispatcher.onRouteUpdateDelayed({
location: this.props.location,
});
nprogress.start();
}, this.props.delay);
}

private stopProgressBar() {
this.clearProgressBarTimeout();
nprogress.done();
}

override render(): JSX.Element {
const {children, location} = this.props;
// Use a controlled <Route> to trick all descendants into rendering the old
// location.
return <Route location={location} render={() => children} />;
return (
<ClientLifecyclesDispatcher
ref={this.clientLifecyclesDispatcher}
previousLocation={this.previousLocation}
location={location}>
<Route location={location} render={() => children} />
</ClientLifecyclesDispatcher>
);
}
}

Expand Down
34 changes: 0 additions & 34 deletions packages/docusaurus/src/client/clientLifecyclesDispatcher.ts

This file was deleted.

1 change: 1 addition & 0 deletions project-words.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ preconfigured
preconnect
prefetch
prefetching
preloads
prepended
preprocessors
prerendered
Expand Down
Loading