mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-19 01:28:38 +02:00
feat(v2): make sidebar collapsible (#1817)
* feat(v2): make sidebar collapsible * fix first page load sidebar category not collapsed * misc(v2): nit
This commit is contained in:
parent
e7ba8af6d9
commit
17252a079c
4 changed files with 108 additions and 47 deletions
|
@ -1,9 +1,11 @@
|
||||||
# Docusaurus 2 Changelog
|
# Docusaurus 2 Changelog
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
- Docs plugin is rewritten in TypeScript
|
|
||||||
- Docs sidebar can now be more than one level deep, theoretically up to infinity.
|
- Docs, pages plugin is rewritten in TypeScript
|
||||||
- more documentation ...
|
- Docs sidebar can now be more than one level deep, theoretically up to infinity
|
||||||
|
- Collapsible docs sidebar!
|
||||||
|
- More documentation ...
|
||||||
|
|
||||||
## 2.0.0-alpha.25
|
## 2.0.0-alpha.25
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,11 @@ function DocLegacyPage(props) {
|
||||||
<div className="container container--fluid">
|
<div className="container container--fluid">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col col--3">
|
<div className="col col--3">
|
||||||
<DocLegacySidebar docsSidebars={docsSidebars} sidebar={sidebar} />
|
<DocLegacySidebar
|
||||||
|
docsSidebars={docsSidebars}
|
||||||
|
location={location}
|
||||||
|
sidebar={sidebar}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<main className="col">
|
<main className="col">
|
||||||
<MDXProvider components={MDXComponents}>
|
<MDXProvider components={MDXComponents}>
|
||||||
|
|
|
@ -14,49 +14,102 @@ import styles from './styles.module.css';
|
||||||
|
|
||||||
const MOBILE_TOGGLE_SIZE = 24;
|
const MOBILE_TOGGLE_SIZE = 24;
|
||||||
|
|
||||||
function DocLegacySidebar(props) {
|
function DocSidebarItem({item, onItemClick}) {
|
||||||
const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false);
|
const {items, href, label, type} = item;
|
||||||
const {docsSidebars, sidebar} = props;
|
const [collapsed, setCollapsed] = useState(item.collapsed);
|
||||||
|
const [prevCollapsedProp, setPreviousCollapsedProp] = useState(null);
|
||||||
|
|
||||||
if (!sidebar) {
|
// If the collapsing state from props changed, probably a navigation event
|
||||||
return null;
|
// occurred. Overwrite the component's collapsed state with the props'
|
||||||
|
// collapsed value.
|
||||||
|
if (item.collapsed !== prevCollapsedProp) {
|
||||||
|
setPreviousCollapsedProp(item.collapsed);
|
||||||
|
setCollapsed(item.collapsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
const thisSidebar = docsSidebars[sidebar];
|
switch (type) {
|
||||||
|
|
||||||
if (!thisSidebar) {
|
|
||||||
throw new Error(`Can not find ${sidebar} config`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderItem = item => {
|
|
||||||
switch (item.type) {
|
|
||||||
case 'category':
|
case 'category':
|
||||||
return (
|
return (
|
||||||
<li className="menu__list-item" key={item.label}>
|
<li
|
||||||
<a className="menu__link" href="#!">
|
className={classnames('menu__list-item', {
|
||||||
{item.label}
|
'menu__list-item--collapsed': collapsed,
|
||||||
|
})}
|
||||||
|
key={label}>
|
||||||
|
<a
|
||||||
|
className={classnames('menu__link', 'menu__link--sublist', {
|
||||||
|
'menu__link--active': !item.collapsed,
|
||||||
|
})}
|
||||||
|
href="#!"
|
||||||
|
onClick={() => setCollapsed(!collapsed)}>
|
||||||
|
{label}
|
||||||
</a>
|
</a>
|
||||||
<ul className="menu__list">{item.items.map(renderItem)}</ul>
|
<ul className="menu__list">
|
||||||
|
{items.map(childItem => (
|
||||||
|
<DocSidebarItem
|
||||||
|
key={childItem.label}
|
||||||
|
item={childItem}
|
||||||
|
onItemClick={onItemClick}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
|
|
||||||
case 'link':
|
case 'link':
|
||||||
default:
|
default:
|
||||||
return (
|
return (
|
||||||
<li className="menu__list-item" key={item.label}>
|
<li className="menu__list-item" key={label}>
|
||||||
<Link
|
<Link
|
||||||
activeClassName="menu__link--active"
|
activeClassName="menu__link--active"
|
||||||
className="menu__link"
|
className="menu__link"
|
||||||
to={item.href}
|
to={href}
|
||||||
onClick={() => {
|
onClick={onItemClick}>
|
||||||
setShowResponsiveSidebar(false);
|
{label}
|
||||||
}}>
|
|
||||||
{item.label}
|
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
// Calculate the category collapsing state when a page navigation occurs.
|
||||||
|
// We want to automatically expand the categories which contains the current page.
|
||||||
|
function mutateSidebarCollapsingState(item, location) {
|
||||||
|
const {items, href, type} = item;
|
||||||
|
switch (type) {
|
||||||
|
case 'category': {
|
||||||
|
const anyChildItemsActive =
|
||||||
|
items
|
||||||
|
.map(childItem => mutateSidebarCollapsingState(childItem, location))
|
||||||
|
.filter(val => val).length > 0;
|
||||||
|
// eslint-disable-next-line no-param-reassign
|
||||||
|
item.collapsed = !anyChildItemsActive;
|
||||||
|
return anyChildItemsActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'link':
|
||||||
|
default:
|
||||||
|
return href === location.pathname.replace(/\/$/, '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function DocLegacySidebar(props) {
|
||||||
|
const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false);
|
||||||
|
|
||||||
|
const {docsSidebars, location, sidebar: currentSidebar} = props;
|
||||||
|
|
||||||
|
if (!currentSidebar) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sidebarData = docsSidebars[currentSidebar];
|
||||||
|
|
||||||
|
if (!sidebarData) {
|
||||||
|
throw new Error(`Can not find ${currentSidebar} config`);
|
||||||
|
}
|
||||||
|
|
||||||
|
sidebarData.forEach(sidebarItem =>
|
||||||
|
mutateSidebarCollapsingState(sidebarItem, location),
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.sidebar}>
|
<div className={styles.sidebar}>
|
||||||
|
@ -100,7 +153,15 @@ function DocLegacySidebar(props) {
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
<ul className="menu__list">
|
<ul className="menu__list">
|
||||||
{thisSidebar.map(item => renderItem(item, {root: true}))}
|
{sidebarData.map(item => (
|
||||||
|
<DocSidebarItem
|
||||||
|
key={item.label}
|
||||||
|
item={item}
|
||||||
|
onItemClick={() => {
|
||||||
|
setShowResponsiveSidebar(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -31,9 +31,3 @@ html[data-theme='dark'] {
|
||||||
--ifm-font-size-base: 17px;
|
--ifm-font-size-base: 17px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.menu__list .menu__list .menu__list .menu__link {
|
|
||||||
font-size: 0.75em;
|
|
||||||
font-weight: normal;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue