Skip to content

Commit

Permalink
🐛 fix: Auto scroll to top when change route
Browse files Browse the repository at this point in the history
  • Loading branch information
canisminor1990 committed Sep 6, 2023
1 parent 5159619 commit c0e9354
Show file tree
Hide file tree
Showing 9 changed files with 239 additions and 212 deletions.
109 changes: 109 additions & 0 deletions src/layouts/DocLayout/DocumentLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import { Layout } from '@lobehub/ui';
import { useResponsive, useTheme } from 'antd-style';
import { Helmet, useIntl, useLocation } from 'dumi';
import isEqual from 'fast-deep-equal';
import { memo, useCallback, useEffect } from 'react';
import { shallow } from 'zustand/shallow';

import Changelog from '@/pages/Changelog';
import Docs from '@/pages/Docs';
import Home from '@/pages/Home';
import Footer from '@/slots/Footer';
import Header from '@/slots/Header';
import Sidebar from '@/slots/Sidebar';
import Toc from '@/slots/Toc';
import { isHeroPageSel, siteTitleSel, tocAnchorItemSel, useSiteStore } from '@/store';

const DocumentLayout = memo(() => {
const intl = useIntl();
const { hash } = useLocation();
const theme = useTheme();
const { mobile, laptop } = useResponsive();

const { loading, page, siteTitle, noToc } = useSiteStore((s) => {
const isChanlogPage = s.location.pathname === '/changelog';
const isHomePage = isHeroPageSel(s);
let page;

if (isHomePage) {
page = 'home';
} else if (isChanlogPage) {
page = 'changelog';
} else {
page = 'docs';
}

return {
loading: s.siteData.loading,
noToc: tocAnchorItemSel(s).length === 0,
page: page,
siteTitle: siteTitleSel(s),
};
}, shallow);

const fm = useSiteStore((s) => s.routeMeta.frontmatter, isEqual);

const hideSidebar = page !== 'docs' || mobile || fm.sidebar === false;
const shouldHideToc = fm.toc === false || noToc;
const hideToc = mobile ? shouldHideToc : !laptop || shouldHideToc;

const HelmetBlock = useCallback(
() => (
<Helmet>
<html lang={intl.locale.replace(/-.+$/, '')} />
{fm.title && <meta content={fm.title} property="og:title" />}
{fm.description && <meta content={fm.description} name="description" />}
{fm.description && <meta content={fm.description} property="og:description" />}
{fm.keywords && <meta content={fm.keywords.join(',')} name="keywords" />}
{fm.keywords && <meta content={fm.keywords.join(',')} property="og:keywords" />}
{!fm.title || page === 'home' ? (
<title>{siteTitle}</title>
) : (
<title>
{fm.title} - {siteTitle}
</title>
)}
</Helmet>
),
[intl, fm, siteTitle, page],
);

// handle hash change or visit page hash after async chunk loaded
useEffect(() => {
const id = hash.replace('#', '');

if (!id) return;
setTimeout(() => {
const elm = document.querySelector(`#${decodeURIComponent(id)}`);
if (elm) {
elm.scrollIntoView();
window.scrollBy({ top: -80 });
}
}, 1);
}, [loading, hash]);

useEffect(() => {
document.body.scrollTo(0, 0);
}, [siteTitle]);

return (
<>
<HelmetBlock />
<Layout
asideWidth={theme.sidebarWidth}
footer={<Footer />}
header={<Header />}
headerHeight={mobile && page !== 'home' ? theme.headerHeight + 36 : theme.headerHeight}
sidebar={hideSidebar ? undefined : <Sidebar />}
toc={hideToc ? undefined : <Toc />}
tocWidth={hideToc ? 0 : theme.tocWidth}
>
{page === 'home' && <Home />}
{page === 'changelog' && <Changelog />}
{page === 'docs' && <Docs />}
</Layout>
</>
);
});

export default DocumentLayout;
119 changes: 8 additions & 111 deletions src/layouts/DocLayout/index.tsx
Original file line number Diff line number Diff line change
@@ -1,125 +1,20 @@
import { Layout, ThemeProvider } from '@lobehub/ui';
import { extractStaticStyle, useResponsive, useTheme } from 'antd-style';
import { Helmet, useIntl, useLocation } from 'dumi';
import isEqual from 'fast-deep-equal';
import { memo, useCallback, useEffect } from 'react';
import { ThemeProvider } from '@lobehub/ui';
import { extractStaticStyle } from 'antd-style';
import { memo } from 'react';
import { shallow } from 'zustand/shallow';

import Favicons from '@/components/Favicons';
import { StoreUpdater } from '@/components/StoreUpdater';
import GlobalStyle from '@/layouts/DocLayout/GlobalStyle';
import Changelog from '@/pages/Changelog';
import Docs from '@/pages/Docs';
import Home from '@/pages/Home';
import Footer from '@/slots/Footer';
import Header from '@/slots/Header';
import Sidebar from '@/slots/Sidebar';
import Toc from '@/slots/Toc';
import {
isHeroPageSel,
siteTitleSel,
tocAnchorItemSel,
useSiteStore,
useThemeStore,
} from '@/store';
import { useThemeStore } from '@/store';
import customToken from '@/styles/customToken';

const DocumentLayout = memo(() => {
const intl = useIntl();
const { hash } = useLocation();
const theme = useTheme();
const { mobile, laptop } = useResponsive();

const { loading, page, siteTitle, noToc } = useSiteStore((s) => {
const isChanlogPage = s.location.pathname === '/changelog';
const isHomePage = isHeroPageSel(s);
let page;

if (isHomePage) {
page = 'home';
} else if (isChanlogPage) {
page = 'changelog';
} else {
page = 'docs';
}

return {
loading: s.siteData.loading,
noToc: tocAnchorItemSel(s).length === 0,
page: page,
siteTitle: siteTitleSel(s),
};
}, shallow);

const fm = useSiteStore((s) => s.routeMeta.frontmatter, isEqual);

const hideSidebar = page !== 'docs' || mobile || fm.sidebar === false;
const shouldHideToc = fm.toc === false || noToc;
const hideToc = mobile ? shouldHideToc : !laptop || shouldHideToc;

const HelmetBlock = useCallback(
() => (
<Helmet>
<html lang={intl.locale.replace(/-.+$/, '')} />
{fm.title && <meta content={fm.title} property="og:title" />}
{fm.description && <meta content={fm.description} name="description" />}
{fm.description && <meta content={fm.description} property="og:description" />}
{fm.keywords && <meta content={fm.keywords.join(',')} name="keywords" />}
{fm.keywords && <meta content={fm.keywords.join(',')} property="og:keywords" />}
{!fm.title || page === 'home' ? (
<title>{siteTitle}</title>
) : (
<title>
{fm.title} - {siteTitle}
</title>
)}
</Helmet>
),
[intl, fm, siteTitle, page],
);

// handle hash change or visit page hash after async chunk loaded
useEffect(() => {
const id = hash.replace('#', '');

if (!id) return;
setTimeout(() => {
const elm = document.querySelector(`#${decodeURIComponent(id)}`);
if (elm) {
elm.scrollIntoView();
window.scrollBy({ top: -80 });
}
}, 1);
}, [loading, hash]);

useEffect(() => {
document.body.scrollTo(0, 0);
}, [siteTitle]);

return (
<>
<HelmetBlock />
<Layout
asideWidth={theme.sidebarWidth}
footer={<Footer />}
header={<Header />}
headerHeight={mobile && page !== 'home' ? theme.headerHeight + 36 : theme.headerHeight}
sidebar={hideSidebar ? undefined : <Sidebar />}
toc={hideToc ? undefined : <Toc />}
tocWidth={hideToc ? 0 : theme.tocWidth}
>
{page === 'home' && <Home />}
{page === 'changelog' && <Changelog />}
{page === 'docs' && <Docs />}
</Layout>
</>
);
});
import DocumentLayout from './DocumentLayout';

// @ts-ignore
global.__ANTD_CACHE__ = extractStaticStyle.cache;

export default memo(() => {
const App = memo(() => {
const themeMode = useThemeStore((st) => st.themeMode, shallow);

return (
Expand All @@ -137,3 +32,5 @@ export default memo(() => {
</>
);
});

export default App;
7 changes: 6 additions & 1 deletion src/pages/Changelog/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useResponsive } from 'antd-style';
import { useOutlet } from 'dumi';
import isEqual from 'fast-deep-equal';
import { memo } from 'react';
import { memo, useEffect } from 'react';
import { Center } from 'react-layout-kit';
import { shallow } from 'zustand/shallow';

Expand Down Expand Up @@ -29,6 +29,11 @@ const Changelog = memo(() => {

const { styles } = useStyles();

useEffect(() => {
window.scrollTo(0, 0);
document.body.scrollTo(0, 0);
}, []);

return (
<>
<div className={styles.background} />
Expand Down
7 changes: 6 additions & 1 deletion src/pages/Docs/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Giscus } from '@lobehub/ui';
import { useResponsive } from 'antd-style';
import { useOutlet } from 'dumi';
import { memo, useCallback } from 'react';
import { memo, useCallback, useEffect } from 'react';
import { Center } from 'react-layout-kit';
import { shallow } from 'zustand/shallow';

Expand All @@ -20,6 +20,11 @@ const Documents = memo(() => {
);
const { styles } = useStyles();

useEffect(() => {
window.scrollTo(0, 0);
document.body.scrollTo(0, 0);
}, [location.pathname]);

const Comment = useCallback(
() =>
giscus && (
Expand Down
7 changes: 6 additions & 1 deletion src/pages/Home/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useOutlet } from 'dumi';
import { memo } from 'react';
import { memo, useEffect } from 'react';
import { Flexbox } from 'react-layout-kit';

import Features from '@/slots/Features';
Expand All @@ -8,6 +8,11 @@ import Hero from '@/slots/Hero';
const Home = memo(() => {
const outlet = useOutlet();

useEffect(() => {
window.scrollTo(0, 0);
document.body.scrollTo(0, 0);
}, []);

return (
<Flexbox align={'center'} gap={64} style={{ minHeight: '64vh', padding: '64px 24px' }}>
<Hero />
Expand Down
12 changes: 8 additions & 4 deletions src/slots/Content/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Skeleton, Typography } from 'antd';
import { Skeleton } from 'antd';
import { useResponsive } from 'antd-style';
import { memo } from 'react';
import { memo, useEffect } from 'react';
import { Flexbox } from 'react-layout-kit';

import ContentFooter from '@/slots/ContentFooter';
Expand All @@ -15,11 +15,15 @@ const Content = memo<DivProps>(({ children, ...props }) => {
const { styles, cx } = useStyles();
const { mobile } = useResponsive();

useEffect(() => {
document.body.scrollTo(0, 0);
}, [loading]);

return (
<Flexbox gap={mobile ? 0 : 24} width={'100%'} {...props}>
<Typography />
<div style={{ height: 0 }} />
<div className={cx('dumi-antd-style-content', styles.content)}>
<Skeleton active loading={loading} paragraph />
<Skeleton active loading={loading} paragraph style={{ minHeight: '50vh' }} />
<div
style={{
display: loading ? 'none' : undefined,
Expand Down
5 changes: 4 additions & 1 deletion src/slots/Header/ThemeSwitch.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { ThemeSwitch as ThemeSwitchButton } from '@lobehub/ui';
import { memo } from 'react';
import { usePrefersColor } from 'dumi';
import { memo, useEffect } from 'react';

import { useThemeStore } from '@/store/useThemeStore';

const ThemeSwitch = memo(() => {
const themeMode = useThemeStore((s) => s.themeMode);
const setColorMode = usePrefersColor()[2];
useEffect(() => setColorMode(themeMode), [themeMode]);

return (
<ThemeSwitchButton
Expand Down
Loading

0 comments on commit c0e9354

Please sign in to comment.