mirror of
https://github.com/facebook/docusaurus.git
synced 2025-07-18 09:07:57 +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
|
||||
|
||||
## Unreleased
|
||||
- Docs plugin is rewritten in TypeScript
|
||||
- Docs sidebar can now be more than one level deep, theoretically up to infinity.
|
||||
- more documentation ...
|
||||
|
||||
- Docs, pages plugin is rewritten in TypeScript
|
||||
- Docs sidebar can now be more than one level deep, theoretically up to infinity
|
||||
- Collapsible docs sidebar!
|
||||
- More documentation ...
|
||||
|
||||
## 2.0.0-alpha.25
|
||||
|
||||
|
|
|
@ -24,7 +24,11 @@ function DocLegacyPage(props) {
|
|||
<div className="container container--fluid">
|
||||
<div className="row">
|
||||
<div className="col col--3">
|
||||
<DocLegacySidebar docsSidebars={docsSidebars} sidebar={sidebar} />
|
||||
<DocLegacySidebar
|
||||
docsSidebars={docsSidebars}
|
||||
location={location}
|
||||
sidebar={sidebar}
|
||||
/>
|
||||
</div>
|
||||
<main className="col">
|
||||
<MDXProvider components={MDXComponents}>
|
||||
|
|
|
@ -14,49 +14,102 @@ import styles from './styles.module.css';
|
|||
|
||||
const MOBILE_TOGGLE_SIZE = 24;
|
||||
|
||||
function DocSidebarItem({item, onItemClick}) {
|
||||
const {items, href, label, type} = item;
|
||||
const [collapsed, setCollapsed] = useState(item.collapsed);
|
||||
const [prevCollapsedProp, setPreviousCollapsedProp] = useState(null);
|
||||
|
||||
// If the collapsing state from props changed, probably a navigation event
|
||||
// occurred. Overwrite the component's collapsed state with the props'
|
||||
// collapsed value.
|
||||
if (item.collapsed !== prevCollapsedProp) {
|
||||
setPreviousCollapsedProp(item.collapsed);
|
||||
setCollapsed(item.collapsed);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'category':
|
||||
return (
|
||||
<li
|
||||
className={classnames('menu__list-item', {
|
||||
'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>
|
||||
<ul className="menu__list">
|
||||
{items.map(childItem => (
|
||||
<DocSidebarItem
|
||||
key={childItem.label}
|
||||
item={childItem}
|
||||
onItemClick={onItemClick}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</li>
|
||||
);
|
||||
|
||||
case 'link':
|
||||
default:
|
||||
return (
|
||||
<li className="menu__list-item" key={label}>
|
||||
<Link
|
||||
activeClassName="menu__link--active"
|
||||
className="menu__link"
|
||||
to={href}
|
||||
onClick={onItemClick}>
|
||||
{label}
|
||||
</Link>
|
||||
</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, sidebar} = props;
|
||||
|
||||
if (!sidebar) {
|
||||
const {docsSidebars, location, sidebar: currentSidebar} = props;
|
||||
|
||||
if (!currentSidebar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const thisSidebar = docsSidebars[sidebar];
|
||||
const sidebarData = docsSidebars[currentSidebar];
|
||||
|
||||
if (!thisSidebar) {
|
||||
throw new Error(`Can not find ${sidebar} config`);
|
||||
if (!sidebarData) {
|
||||
throw new Error(`Can not find ${currentSidebar} config`);
|
||||
}
|
||||
|
||||
const renderItem = item => {
|
||||
switch (item.type) {
|
||||
case 'category':
|
||||
return (
|
||||
<li className="menu__list-item" key={item.label}>
|
||||
<a className="menu__link" href="#!">
|
||||
{item.label}
|
||||
</a>
|
||||
<ul className="menu__list">{item.items.map(renderItem)}</ul>
|
||||
</li>
|
||||
);
|
||||
|
||||
case 'link':
|
||||
default:
|
||||
return (
|
||||
<li className="menu__list-item" key={item.label}>
|
||||
<Link
|
||||
activeClassName="menu__link--active"
|
||||
className="menu__link"
|
||||
to={item.href}
|
||||
onClick={() => {
|
||||
setShowResponsiveSidebar(false);
|
||||
}}>
|
||||
{item.label}
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
};
|
||||
sidebarData.forEach(sidebarItem =>
|
||||
mutateSidebarCollapsingState(sidebarItem, location),
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={styles.sidebar}>
|
||||
|
@ -100,7 +153,15 @@ function DocLegacySidebar(props) {
|
|||
)}
|
||||
</button>
|
||||
<ul className="menu__list">
|
||||
{thisSidebar.map(item => renderItem(item, {root: true}))}
|
||||
{sidebarData.map(item => (
|
||||
<DocSidebarItem
|
||||
key={item.label}
|
||||
item={item}
|
||||
onItemClick={() => {
|
||||
setShowResponsiveSidebar(false);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -31,9 +31,3 @@ html[data-theme='dark'] {
|
|||
--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