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:
Alexey Pyltsyn 2020-05-25 20:47:40 +03:00 committed by GitHub
parent 34e664ac27
commit d391a2bcdb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 186 additions and 65 deletions

View file

@ -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">&times;</span>
</button>

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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'}

View file

@ -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 {

View file

@ -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>
);

View file

@ -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;

View file

@ -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;