Skip to content

Commit 9721c9d

Browse files
Add loading indicator to sidebar links (#4693)
* add loading indicator to sidebar links * fix clicking on same route whould show indicator * maybe final? * handle routeChangeError as well as cleaning up timeout * clearing timeout before creating one instead of routeChangeError * rm unused * add license header * Update usePendingRoute.ts Co-authored-by: dan <dan.abramov@gmail.com>
1 parent ee75472 commit 9721c9d

File tree

3 files changed

+51
-1
lines changed

3 files changed

+51
-1
lines changed

beta/src/components/Layout/Sidebar/SidebarLink.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ interface SidebarLinkProps {
2121
isExpanded?: boolean;
2222
isBreadcrumb?: boolean;
2323
hideArrow?: boolean;
24+
isPending: boolean;
2425
}
2526

2627
export function SidebarLink({
@@ -32,6 +33,7 @@ export function SidebarLink({
3233
isExpanded,
3334
isBreadcrumb,
3435
hideArrow,
36+
isPending,
3537
}: SidebarLinkProps) {
3638
const ref = React.useRef<HTMLAnchorElement>(null);
3739
const isMobile = useIsMobile();
@@ -67,8 +69,11 @@ export function SidebarLink({
6769
!selected && !heading,
6870
'text-base text-link dark:text-link-dark bg-highlight dark:bg-highlight-dark border-blue-40 hover:bg-highlight hover:text-link dark:hover:bg-highlight-dark dark:hover:text-link-dark':
6971
selected,
72+
'dark:bg-gray-60 bg-gray-3 dark:hover:bg-gray-60 hover:bg-gray-3':
73+
isPending,
7074
}
7175
)}>
76+
{/* This here needs to be refactored ofc */}
7277
{title}
7378
{isExpanded != null && !heading && !hideArrow && (
7479
<span

beta/src/components/Layout/Sidebar/SidebarRouteTree.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {useRouteMeta} from '../useRouteMeta';
1111
import {SidebarLink} from './SidebarLink';
1212
import useCollapse from 'react-collapsed';
1313
import {useLayoutEffect} from 'react';
14+
import usePendingRoute from 'hooks/usePendingRoute';
1415

1516
interface SidebarRouteTreeProps {
1617
isMobile?: boolean;
@@ -77,8 +78,9 @@ export function SidebarRouteTree({
7778
}: SidebarRouteTreeProps) {
7879
const {breadcrumbs} = useRouteMeta(routeTree);
7980
const {pathname} = useRouter();
80-
const slug = pathname;
81+
const pendingRoute = usePendingRoute();
8182

83+
const slug = pathname;
8284
const currentRoutes = routeTree.routes as RouteItem[];
8385
const expandedPath = currentRoutes.reduce(
8486
(acc: string | undefined, curr: RouteItem) => {
@@ -121,6 +123,7 @@ export function SidebarRouteTree({
121123
<SidebarLink
122124
key={`${title}-${path}-${level}-link`}
123125
href={pagePath}
126+
isPending={pendingRoute === pagePath}
124127
selected={selected}
125128
level={level}
126129
title={title}
@@ -143,6 +146,7 @@ export function SidebarRouteTree({
143146
return (
144147
<li key={`${title}-${path}-${level}-link`}>
145148
<SidebarLink
149+
isPending={pendingRoute === pagePath}
146150
href={pagePath}
147151
selected={selected}
148152
level={level}

beta/src/hooks/usePendingRoute.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*/
4+
5+
import {useRouter} from 'next/router';
6+
import React from 'react';
7+
8+
const usePendingRoute = () => {
9+
const {events} = useRouter();
10+
const [pendingRoute, setPendingRoute] = React.useState<string | null>(null);
11+
const currentRoute = React.useRef<string | null>(null);
12+
React.useEffect(() => {
13+
let routeTransitionTimer: any = null;
14+
15+
const handleRouteChangeStart = (url: string) => {
16+
clearTimeout(routeTransitionTimer);
17+
routeTransitionTimer = setTimeout(() => {
18+
if (currentRoute.current !== url) {
19+
currentRoute.current = url;
20+
setPendingRoute(url);
21+
}
22+
}, 100);
23+
};
24+
const handleRouteChangeComplete = () => {
25+
setPendingRoute(null);
26+
clearTimeout(routeTransitionTimer);
27+
};
28+
events.on('routeChangeStart', handleRouteChangeStart);
29+
events.on('routeChangeComplete', handleRouteChangeComplete);
30+
31+
return () => {
32+
events.off('routeChangeStart', handleRouteChangeStart);
33+
events.off('routeChangeComplete', handleRouteChangeComplete);
34+
clearTimeout(routeTransitionTimer);
35+
};
36+
}, []);
37+
38+
return pendingRoute;
39+
};
40+
41+
export default usePendingRoute;

0 commit comments

Comments
 (0)