mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-28 17:57:48 +02:00
fix(v2): enable scrolling for sidebar menu only (#2645)
* fix(v2): enable scrolling for sidebar menu only * Add support for announcement bar * fix: remove redundant styles
This commit is contained in:
parent
34e664ac27
commit
d391a2bcdb
9 changed files with 186 additions and 65 deletions
|
@ -5,44 +5,23 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import React, {useState, useEffect} from 'react';
|
||||
import React from 'react';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import useAnnouncementBarContext from '@theme/hooks/useAnnouncementBarContext';
|
||||
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const STORAGE_DISMISS_KEY = 'docusaurus.announcement.dismiss';
|
||||
const STORAGE_ID_KEY = 'docusaurus.announcement.id';
|
||||
|
||||
function AnnouncementBar() {
|
||||
const {
|
||||
siteConfig: {themeConfig: {announcementBar = {}}} = {},
|
||||
} = useDocusaurusContext();
|
||||
const {id, content, backgroundColor, textColor} = announcementBar;
|
||||
const [isClosed, setClosed] = useState(true);
|
||||
const handleClose = () => {
|
||||
localStorage.setItem(STORAGE_DISMISS_KEY, true);
|
||||
setClosed(true);
|
||||
};
|
||||
const {content, backgroundColor, textColor} = announcementBar;
|
||||
const {
|
||||
isAnnouncementBarClosed,
|
||||
closeAnnouncementBar,
|
||||
} = useAnnouncementBarContext();
|
||||
|
||||
useEffect(() => {
|
||||
const viewedId = localStorage.getItem(STORAGE_ID_KEY);
|
||||
const isNewAnnouncement = id !== viewedId;
|
||||
|
||||
localStorage.setItem(STORAGE_ID_KEY, id);
|
||||
|
||||
if (isNewAnnouncement) {
|
||||
localStorage.setItem(STORAGE_DISMISS_KEY, false);
|
||||
}
|
||||
|
||||
if (
|
||||
isNewAnnouncement ||
|
||||
localStorage.getItem(STORAGE_DISMISS_KEY) === 'false'
|
||||
) {
|
||||
setClosed(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (!content || isClosed) {
|
||||
if (!content || isAnnouncementBarClosed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -59,7 +38,7 @@ function AnnouncementBar() {
|
|||
<button
|
||||
type="button"
|
||||
className={styles.announcementBarClose}
|
||||
onClick={handleClose}
|
||||
onClick={closeAnnouncementBar}
|
||||
aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
|
|
|
@ -5,13 +5,24 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--docusaurus-announcement-bar-height: auto;
|
||||
}
|
||||
|
||||
.announcementBar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: var(--docusaurus-announcement-bar-height);
|
||||
background-color: var(--ifm-color-primary);
|
||||
color: var(--ifm-color-black);
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1024px) {
|
||||
:root {
|
||||
--docusaurus-announcement-bar-height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
.announcementBarClose {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* 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 {createContext} from 'react';
|
||||
|
||||
const AnnouncementBarContext = createContext({
|
||||
isAnnouncementBarClosed: false,
|
||||
closeAnnouncementBar: () => {},
|
||||
});
|
||||
|
||||
export default AnnouncementBarContext;
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
* 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 from 'react';
|
||||
|
||||
import AnnouncementBarContext from '@theme/AnnouncementBarContext';
|
||||
import useAnnouncementBar from '@theme/hooks/useAnnouncementBar';
|
||||
|
||||
function AnnouncementBarProvider(props) {
|
||||
const {isAnnouncementBarClosed, closeAnnouncementBar} = useAnnouncementBar();
|
||||
|
||||
return (
|
||||
<AnnouncementBarContext.Provider
|
||||
value={{isAnnouncementBarClosed, closeAnnouncementBar}}>
|
||||
{props.children}
|
||||
</AnnouncementBarContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export default AnnouncementBarProvider;
|
|
@ -8,8 +8,10 @@
|
|||
import React, {useState, useCallback} from 'react';
|
||||
import classnames from 'classnames';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
import useAnnouncementBarContext from '@theme/hooks/useAnnouncementBarContext';
|
||||
import useLockBodyScroll from '@theme/hooks/useLockBodyScroll';
|
||||
import useLogo from '@theme/hooks/useLogo';
|
||||
import useScrollPosition from '@theme/hooks/useScrollPosition';
|
||||
import Link from '@docusaurus/Link';
|
||||
import isInternalUrl from '@docusaurus/isInternalUrl';
|
||||
|
||||
|
@ -134,6 +136,8 @@ function DocSidebar(props) {
|
|||
isClient,
|
||||
} = useDocusaurusContext();
|
||||
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
||||
const {isAnnouncementBarClosed} = useAnnouncementBarContext();
|
||||
const {scrollY} = useScrollPosition();
|
||||
|
||||
const {
|
||||
docsSidebars,
|
||||
|
@ -163,7 +167,10 @@ function DocSidebar(props) {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className={styles.sidebar}>
|
||||
<div
|
||||
className={classnames(styles.sidebar, {
|
||||
[styles.sidebarWithHideableNavbar]: hideOnScroll,
|
||||
})}>
|
||||
{hideOnScroll && (
|
||||
<Link
|
||||
tabIndex="-1"
|
||||
|
@ -179,6 +186,8 @@ function DocSidebar(props) {
|
|||
<div
|
||||
className={classnames('menu', 'menu--responsive', styles.menu, {
|
||||
'menu--show': showResponsiveSidebar,
|
||||
[styles.menuWithAnnouncementBar]:
|
||||
!isAnnouncementBarClosed && scrollY === 0,
|
||||
})}>
|
||||
<button
|
||||
aria-label={showResponsiveSidebar ? 'Close Menu' : 'Open Menu'}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
@media (min-width: 997px) {
|
||||
.sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
position: sticky;
|
||||
|
@ -14,6 +16,10 @@
|
|||
padding-top: var(--ifm-navbar-height);
|
||||
}
|
||||
|
||||
.sidebarWithHideableNavbar {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.sidebar::-webkit-scrollbar {
|
||||
width: 7px;
|
||||
}
|
||||
|
@ -35,10 +41,9 @@
|
|||
.sidebarLogo {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
margin: 0 var(--ifm-navbar-padding-horizontal);
|
||||
height: var(--ifm-navbar-height);
|
||||
min-height: var(--ifm-navbar-height);
|
||||
max-height: var(--ifm-navbar-height);
|
||||
color: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
@ -49,8 +54,13 @@
|
|||
}
|
||||
|
||||
.menu {
|
||||
flex-grow: 1;
|
||||
padding: 0.5rem;
|
||||
}
|
||||
|
||||
.menuWithAnnouncementBar {
|
||||
margin-bottom: var(--docusaurus-announcement-bar-height);
|
||||
}
|
||||
}
|
||||
|
||||
.sidebarLogo {
|
||||
|
|
|
@ -13,6 +13,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
|
|||
|
||||
import ThemeProvider from '@theme/ThemeProvider';
|
||||
import TabGroupChoiceProvider from '@theme/TabGroupChoiceProvider';
|
||||
import AnnouncementBarProvider from '@theme/AnnouncementBarProvider';
|
||||
import AnnouncementBar from '@theme/AnnouncementBar';
|
||||
import Navbar from '@theme/Navbar';
|
||||
import Footer from '@theme/Footer';
|
||||
|
@ -50,38 +51,43 @@ function Layout(props) {
|
|||
return (
|
||||
<ThemeProvider>
|
||||
<TabGroupChoiceProvider>
|
||||
<Head>
|
||||
{/* TODO: Do not assume that it is in english language */}
|
||||
<html lang="en" />
|
||||
<AnnouncementBarProvider>
|
||||
<Head>
|
||||
{/* TODO: Do not assume that it is in english language */}
|
||||
<html lang="en" />
|
||||
|
||||
{metaTitle && <title>{metaTitle}</title>}
|
||||
{metaTitle && <meta property="og:title" content={metaTitle} />}
|
||||
{favicon && <link rel="shortcut icon" href={faviconUrl} />}
|
||||
{description && <meta name="description" content={description} />}
|
||||
{description && (
|
||||
<meta property="og:description" content={description} />
|
||||
)}
|
||||
{version && <meta name="docsearch:version" content={version} />}
|
||||
{keywords && keywords.length && (
|
||||
<meta name="keywords" content={keywords.join(',')} />
|
||||
)}
|
||||
{metaImage && <meta property="og:image" content={metaImageUrl} />}
|
||||
{metaImage && (
|
||||
<meta property="twitter:image" content={metaImageUrl} />
|
||||
)}
|
||||
{metaImage && (
|
||||
<meta name="twitter:image:alt" content={`Image for ${metaTitle}`} />
|
||||
)}
|
||||
{permalink && (
|
||||
<meta property="og:url" content={siteUrl + permalink} />
|
||||
)}
|
||||
{permalink && <link rel="canonical" href={siteUrl + permalink} />}
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
</Head>
|
||||
<AnnouncementBar />
|
||||
<Navbar />
|
||||
<div className="main-wrapper">{children}</div>
|
||||
{!noFooter && <Footer />}
|
||||
{metaTitle && <title>{metaTitle}</title>}
|
||||
{metaTitle && <meta property="og:title" content={metaTitle} />}
|
||||
{favicon && <link rel="shortcut icon" href={faviconUrl} />}
|
||||
{description && <meta name="description" content={description} />}
|
||||
{description && (
|
||||
<meta property="og:description" content={description} />
|
||||
)}
|
||||
{version && <meta name="docsearch:version" content={version} />}
|
||||
{keywords && keywords.length && (
|
||||
<meta name="keywords" content={keywords.join(',')} />
|
||||
)}
|
||||
{metaImage && <meta property="og:image" content={metaImageUrl} />}
|
||||
{metaImage && (
|
||||
<meta property="twitter:image" content={metaImageUrl} />
|
||||
)}
|
||||
{metaImage && (
|
||||
<meta
|
||||
name="twitter:image:alt"
|
||||
content={`Image for ${metaTitle}`}
|
||||
/>
|
||||
)}
|
||||
{permalink && (
|
||||
<meta property="og:url" content={siteUrl + permalink} />
|
||||
)}
|
||||
{permalink && <link rel="canonical" href={siteUrl + permalink} />}
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
</Head>
|
||||
<AnnouncementBar />
|
||||
<Navbar />
|
||||
<div className="main-wrapper">{children}</div>
|
||||
{!noFooter && <Footer />}
|
||||
</AnnouncementBarProvider>
|
||||
</TabGroupChoiceProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
* 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 {useState, useEffect} from 'react';
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
|
||||
const STORAGE_DISMISS_KEY = 'docusaurus.announcement.dismiss';
|
||||
const STORAGE_ID_KEY = 'docusaurus.announcement.id';
|
||||
|
||||
const useAnnouncementBar = () => {
|
||||
const {
|
||||
siteConfig: {
|
||||
themeConfig: {
|
||||
announcementBar: {id},
|
||||
},
|
||||
} = {},
|
||||
} = useDocusaurusContext();
|
||||
const [isClosed, setClosed] = useState(true);
|
||||
const handleClose = () => {
|
||||
localStorage.setItem(STORAGE_DISMISS_KEY, true);
|
||||
setClosed(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const viewedId = localStorage.getItem(STORAGE_ID_KEY);
|
||||
const isNewAnnouncement = id !== viewedId;
|
||||
|
||||
localStorage.setItem(STORAGE_ID_KEY, id);
|
||||
|
||||
if (isNewAnnouncement) {
|
||||
localStorage.setItem(STORAGE_DISMISS_KEY, false);
|
||||
}
|
||||
|
||||
if (
|
||||
isNewAnnouncement ||
|
||||
localStorage.getItem(STORAGE_DISMISS_KEY) === 'false'
|
||||
) {
|
||||
setClosed(false);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return {
|
||||
isAnnouncementBarClosed: isClosed,
|
||||
closeAnnouncementBar: handleClose,
|
||||
};
|
||||
};
|
||||
|
||||
export default useAnnouncementBar;
|
|
@ -0,0 +1,15 @@
|
|||
/**
|
||||
* 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 {useContext} from 'react';
|
||||
import AnnouncementBarContext from '@theme/AnnouncementBarContext';
|
||||
|
||||
function useAnnouncementBarContext() {
|
||||
return useContext(AnnouncementBarContext);
|
||||
}
|
||||
|
||||
export default useAnnouncementBarContext;
|
Loading…
Add table
Reference in a new issue