Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(theme-classic): auto-collapse sibling categories in doc sidebar #3811

Merged
merged 15 commits into from
Jan 20, 2022
3 changes: 3 additions & 0 deletions packages/docusaurus-theme-classic/src/theme/DocPage/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ function DocPageContent({
sidebarCollapsible={
siteConfig.themeConfig?.sidebarCollapsible ?? true
}
autoCollapseSidebar={
siteConfig.themeConfig?.autoCollapseSidebar ?? false
}
onCollapse={toggleSidebar}
isHidden={hiddenSidebar}
/>
Expand Down
68 changes: 53 additions & 15 deletions packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* LICENSE file in the root directory of this source tree.
*/

import React, {useState, useCallback, useEffect, useRef} from 'react';
import React, {useState, useEffect, useRef} from 'react';
import clsx from 'clsx';
import {useThemeConfig, isSamePath} from '@docusaurus/theme-common';
import useUserPreferencesContext from '@theme/hooks/useUserPreferencesContext';
Expand All @@ -23,6 +23,10 @@ import styles from './styles.module.css';

const MOBILE_TOGGLE_SIZE = 24;

function sleep(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}

function usePrevious(value) {
const ref = useRef(value);
useEffect(() => {
Expand All @@ -48,6 +52,8 @@ function DocSidebarItemCategory({
onItemClick,
collapsible,
activePath,
autoCollapseCategory,
autoCollapse,
...props
}) {
const {items, label} = item;
Expand All @@ -65,6 +71,7 @@ function DocSidebarItemCategory({
});

const menuListRef = useRef<HTMLUListElement>(null);
const [currentCategory, setCurrentCategory] = useState(item);
const [menuListHeight, setMenuListHeight] = useState<string | undefined>(
undefined,
);
Expand All @@ -82,18 +89,14 @@ function DocSidebarItemCategory({
}
}, [isActive, wasActive, collapsed]);

const handleItemClick = useCallback(
(e) => {
e.preventDefault();
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved

if (!menuListHeight) {
handleMenuListHeight();
}
const handleItemClick = async (category) => {
if (!menuListHeight) {
handleMenuListHeight();
}

setTimeout(() => setCollapsed((state) => !state), 100);
},
[menuListHeight],
);
const updatedCategory = await autoCollapseCategory(category);
setCurrentCategory(updatedCategory);
};

if (items.length === 0) {
return null;
Expand All @@ -102,7 +105,7 @@ function DocSidebarItemCategory({
return (
<li
className={clsx('menu__list-item', {
'menu__list-item--collapsed': collapsed,
'menu__list-item--collapsed': currentCategory.collapsed,
})}
key={label}>
<a
Expand All @@ -111,7 +114,9 @@ function DocSidebarItemCategory({
'menu__link--active': collapsible && isActive,
[styles.menuLinkText]: !collapsible,
})}
onClick={collapsible ? handleItemClick : undefined}
onClick={
collapsible ? () => handleItemClick(currentCategory) : undefined
}
href={collapsible ? '#!' : undefined}
{...props}>
{label}
Expand All @@ -132,6 +137,8 @@ function DocSidebarItemCategory({
tabIndex={collapsed ? '-1' : '0'}
key={childItem.label}
item={childItem}
autoCollapse={autoCollapse}
parent={items}
onItemClick={onItemClick}
collapsible={collapsible}
activePath={activePath}
Expand Down Expand Up @@ -176,9 +183,37 @@ function DocSidebarItemLink({
}

function DocSidebarItem(props) {
const {autoCollapse, parent} = props;

const autoCollapseCategory = async (item) => {
const isCollapsed = item.collapsed;

if (!isCollapsed) {
item.collapsed = !item.collapsed;
} else {
if (autoCollapse) {
for (const s in parent) {
if (parent[s].type !== 'link') {
parent[s].collapsed = true;
} else {
parent[s].collapsed = false;
}
}
}
item.collapsed = !item.collapsed;
}
await sleep(100);
return item;
};

Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
switch (props.item.type) {
case 'category':
return <DocSidebarItemCategory {...props} />;
return (
<DocSidebarItemCategory
{...props}
autoCollapseCategory={autoCollapseCategory}
/>
);
case 'link':
default:
return <DocSidebarItemLink {...props} />;
Expand All @@ -189,6 +224,7 @@ function DocSidebar({
path,
sidebar,
sidebarCollapsible = true,
autoCollapseSidebar,
onCollapse,
isHidden,
}: Props): JSX.Element | null {
Expand Down Expand Up @@ -262,6 +298,8 @@ function DocSidebar({
setShowResponsiveSidebar(false);
}}
collapsible={sidebarCollapsible}
parent={sidebar}
autoCollapse={autoCollapseSidebar}
activePath={path}
/>
))}
Expand Down
1 change: 1 addition & 0 deletions packages/docusaurus-theme-classic/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ declare module '@theme/DocSidebar' {
readonly path: string;
readonly sidebar: readonly PropSidebarItem[];
readonly sidebarCollapsible?: boolean;
readonly autoCollapseSidebar?: boolean;
readonly onCollapse: () => void;
readonly isHidden: boolean;
};
Expand Down
2 changes: 1 addition & 1 deletion packages/docusaurus/src/webpack/__tests__/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ describe('extending generated webpack config', () => {

describe('getFileLoaderUtils()', () => {
test('plugin svgo/removeViewBox should be disabled', () => {
const use = getFileLoaderUtils().rules.svg().use;
const {use} = getFileLoaderUtils().rules.svg();
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved
expect(use).toContainEqual(
expect.objectContaining({
loader: '@svgr/webpack',
Expand Down
6 changes: 2 additions & 4 deletions website/docs/deployment.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,9 @@ First, modify your `docusaurus.config.js` and add the required params:
| `url` | URL for your GitHub Page's user/organization page. This is commonly https://_username_.github.io. |
| `baseUrl` | Base URL for your project. For projects hosted on GitHub pages, it follows the format "/_projectName_/". For https://github.com/facebook/docusaurus, `baseUrl` is `/docusaurus/`. |

:::info
In case you want to use your custom domain for GitHub Pages, create a `CNAME` file in the `static` directory. Anything within the `static` directory will be copied to the root of the `build` directory for deployment.
:::info In case you want to use your custom domain for GitHub Pages, create a `CNAME` file in the `static` directory. Anything within the `static` directory will be copied to the root of the `build` directory for deployment.
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved

You may refer to GitHub Pages' documentation [User, Organization, and Project Pages](https://help.github.com/en/articles/user-organization-and-project-pages) for more details.
:::
You may refer to GitHub Pages' documentation [User, Organization, and Project Pages](https://help.github.com/en/articles/user-organization-and-project-pages) for more details. :::

Example:

Expand Down
18 changes: 15 additions & 3 deletions website/docs/sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,7 @@ module.exports = {
};
```

:::note
Shorthand notation relies on the iteration order of JavaScript object keys for the category name. When using this notation, keep in mind that EcmaScript does not guarantee `Object.keys({a,b}) === ['a','b']`, yet this is generally true.
:::
:::note Shorthand notation relies on the iteration order of JavaScript object keys for the category name. When using this notation, keep in mind that EcmaScript does not guarantee `Object.keys({a,b}) === ['a','b']`, yet this is generally true. :::
Josh-Cena marked this conversation as resolved.
Show resolved Hide resolved

## Using multiple sidebars

Expand Down Expand Up @@ -270,6 +268,20 @@ module.exports = {
};
```

### Auto Collapsable Sidebar

Using the enabled `themeConfig.autoCollapseSidebar` option, you can make the sidebar only have one parent category open at a time, helping users not get cluttered and only focus on the content they selected. If you want them to be enable this feature, set `themeConfig.autoCollapseSidebar` to `true`:

```js {4} title="docusaurus.config.js"
module.exports = {
// ...
themeConfig: {
autoCollapseSidebar: true,
// ...
},
};
```

#### Expanded categories by default

For docs that have collapsible categories, you may want more fine-grain control over certain categories. If you want specific categories to be always expanded, you can set `collapsed` to `false`:
Expand Down
1 change: 1 addition & 0 deletions website/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ module.exports = {
],
themeConfig: {
hideableSidebar: true,
autoCollapseSidebar: true,
colorMode: {
defaultMode: 'light',
disableSwitch: false,
Expand Down