+
+
toggleTagsSelector(!showTagsSelector)}>
+
Select tags or components
+
+
+
+
+
+
+ {Object.keys(components.tags).map(i => (
+
+ setTags(i)}/>
+
+
+ ))}
+
+
+
+
+ {ESMs.map(esm =>
+ setESM(esm)}
+ type="radio"
+ name="esm-types"
+ value={esm}
+ checked={esm === components.esm}
+ />
+
+
)}
+
+
+
+
+ {downloadTypes.map(type =>
+ setDownloadType(type)}
+ type="radio"
+ name="download-types"
+ value={type}
+ checked={type === components.downloadType}
+ />
+
+
)}
+
+
+
+
+ );
+};
+
+export default ComponentSelector;
diff --git a/src/theme/DocSidebar/index.js b/src/theme/DocSidebar/index.js
new file mode 100644
index 0000000000..8cddf05d4b
--- /dev/null
+++ b/src/theme/DocSidebar/index.js
@@ -0,0 +1,354 @@
+/**
+ * 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, {useState, useCallback, useEffect, useRef, memo} from 'react';
+import clsx from 'clsx';
+import {
+ useThemeConfig,
+ isSamePath,
+ usePrevious,
+ useAnnouncementBar,
+} from '@docusaurus/theme-common';
+import useLockBodyScroll from '@theme/hooks/useLockBodyScroll';
+import useWindowSize, {windowSizes} from '@theme/hooks/useWindowSize';
+import useScrollPosition from '@theme/hooks/useScrollPosition';
+import Link from '@docusaurus/Link';
+import isInternalUrl from '@docusaurus/isInternalUrl';
+import Logo from '@theme/Logo';
+import IconArrow from '@theme/IconArrow';
+import IconMenu from '@theme/IconMenu';
+import IconExternalLink from '@theme/IconExternalLink';
+import {translate} from '@docusaurus/Translate';
+import styles from './styles.module.css';
+const MOBILE_TOGGLE_SIZE = 24;
+import ComponentSelector from './ComponentSelector';
+
+const isActiveSidebarItem = (item, activePath) => {
+ if (item.type === 'link') {
+ return isSamePath(item.href, activePath);
+ }
+
+ if (item.type === 'category') {
+ return item.items.some((subItem) =>
+ isActiveSidebarItem(subItem, activePath),
+ );
+ }
+
+ return false;
+}; // Optimize sidebar at each "level"
+// TODO this item should probably not receive the "activePath" props
+// TODO this triggers whole sidebar re-renders on navigation
+
+const DocSidebarItems = memo(function DocSidebarItems({items, ...props}) {
+ return items.map((item, index) => (
+
+ ));
+});
+
+function DocSidebarItem(props) {
+ switch (props.item.type) {
+ case 'category':
+ return
;
+
+ case 'link':
+ default:
+ return
;
+ }
+}
+
+function DocSidebarItemCategory({
+ item,
+ onItemClick,
+ collapsible,
+ activePath,
+ ...props
+}) {
+ const {items, label} = item;
+ const isActive = isActiveSidebarItem(item, activePath);
+ const wasActive = usePrevious(isActive); // active categories are always initialized as expanded
+ // the default (item.collapsed) is only used for non-active categories
+
+ const [collapsed, setCollapsed] = useState(() => {
+ if (!collapsible) {
+ return false;
+ }
+
+ return isActive ? false : item.collapsed;
+ });
+ const menuListRef = useRef(null);
+ const [menuListHeight, setMenuListHeight] = useState(undefined);
+
+ const handleMenuListHeight = (calc = true) => {
+ setMenuListHeight(
+ calc ? `${menuListRef.current?.scrollHeight}px` : undefined,
+ );
+ }; // If we navigate to a category, it should automatically expand itself
+
+ useEffect(() => {
+ const justBecameActive = isActive && !wasActive;
+
+ if (justBecameActive && collapsed) {
+ setCollapsed(false);
+ }
+ }, [isActive, wasActive, collapsed]);
+ const handleItemClick = useCallback(
+ (e) => {
+ e.preventDefault();
+
+ if (!menuListHeight) {
+ handleMenuListHeight();
+ }
+
+ setTimeout(() => setCollapsed((state) => !state), 100);
+ },
+ [menuListHeight],
+ );
+
+ if (items.length === 0) {
+ return null;
+ }
+
+ return (
+
+ {/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
+
+ {label}
+
+ {
+ if (!collapsed) {
+ handleMenuListHeight(false);
+ }
+ }}>
+
+
+
+ );
+}
+
+function DocSidebarItemLink({
+ item,
+ onItemClick,
+ activePath,
+ collapsible: _collapsible,
+ ...props
+}) {
+ const {href, label} = item;
+ const isActive = isActiveSidebarItem(item, activePath);
+ return (
+
+
+ {isInternalUrl(href) ? (
+ label
+ ) : (
+
+ {label}
+
+
+ )}
+
+
+ );
+}
+
+function useShowAnnouncementBar() {
+ const {isClosed} = useAnnouncementBar();
+ const [showAnnouncementBar, setShowAnnouncementBar] = useState(!isClosed);
+ useScrollPosition(({scrollY}) => {
+ if (!isClosed) {
+ setShowAnnouncementBar(scrollY === 0);
+ }
+ });
+ return showAnnouncementBar;
+}
+
+function useResponsiveSidebar() {
+ const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false);
+ useLockBodyScroll(showResponsiveSidebar);
+ const windowSize = useWindowSize();
+ useEffect(() => {
+ if (windowSize === windowSizes.desktop) {
+ setShowResponsiveSidebar(false);
+ }
+ }, [windowSize]);
+ const closeResponsiveSidebar = useCallback(
+ (e) => {
+ e.target.blur();
+ setShowResponsiveSidebar(false);
+ },
+ [setShowResponsiveSidebar],
+ );
+ const toggleResponsiveSidebar = useCallback(() => {
+ setShowResponsiveSidebar((value) => !value);
+ }, [setShowResponsiveSidebar]);
+ return {
+ showResponsiveSidebar,
+ closeResponsiveSidebar,
+ toggleResponsiveSidebar,
+ };
+}
+
+function HideableSidebarButton({onClick}) {
+ return (
+
+ );
+}
+
+function ResponsiveSidebarButton({responsiveSidebarOpened, onClick}) {
+ return (
+
+ );
+}
+
+function DocSidebar({
+ path,
+ sidebar,
+ sidebarCollapsible = true,
+ onCollapse,
+ isHidden,
+}) {
+ const showAnnouncementBar = useShowAnnouncementBar();
+ const {
+ navbar: {hideOnScroll},
+ hideableSidebar,
+ } = useThemeConfig();
+ const {isClosed: isAnnouncementBarClosed} = useAnnouncementBar();
+ const {
+ showResponsiveSidebar,
+ closeResponsiveSidebar,
+ toggleResponsiveSidebar,
+ } = useResponsiveSidebar();
+ return (
+
+ {hideOnScroll &&
}
+
+ {hideableSidebar &&
}
+
+ );
+}
+
+export default DocSidebar;
diff --git a/src/theme/DocSidebar/styles.module.css b/src/theme/DocSidebar/styles.module.css
new file mode 100644
index 0000000000..2b51d3377f
--- /dev/null
+++ b/src/theme/DocSidebar/styles.module.css
@@ -0,0 +1,186 @@
+/**
+ * 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.
+ */
+
+:root {
+ --collapse-button-bg-color-dark: #2e333a;
+}
+
+@media (min-width: 997px) {
+ .sidebar {
+ display: flex;
+ flex-direction: column;
+ max-height: 100vh;
+ height: 100%;
+ position: sticky;
+ top: 0;
+ padding-top: var(--ifm-navbar-height);
+ width: var(--doc-sidebar-width);
+ transition: opacity 50ms ease;
+ }
+
+ .sidebarWithHideableNavbar {
+ padding-top: 0;
+ }
+
+ .sidebarHidden {
+ opacity: 0;
+ height: 0;
+ overflow: hidden;
+ visibility: hidden;
+ }
+
+ .sidebarLogo {
+ display: flex !important;
+ align-items: center;
+ margin: 0 var(--ifm-navbar-padding-horizontal);
+ min-height: var(--ifm-navbar-height);
+ max-height: var(--ifm-navbar-height);
+ color: inherit !important;
+ text-decoration: none !important;
+ }
+
+ .sidebarLogo img {
+ margin-right: 0.5rem;
+ height: 2rem;
+ }
+
+ .menu {
+ flex-grow: 1;
+ padding: 0.5rem;
+ }
+
+ .menuLinkText {
+ cursor: initial;
+ }
+
+ .menuLinkText:hover {
+ background: none;
+ }
+
+ .menuWithAnnouncementBar {
+ margin-bottom: var(--docusaurus-announcement-bar-height);
+ }
+
+ .tagsSelectorContainer {
+ padding: 24px 0px 0px;
+ }
+
+ .tagsSelectorLabel {
+ margin: 0;
+ color: black;
+ }
+
+ [data-theme="dark"] .tagsSelectorLabel {
+ color: inherit;
+ }
+
+ .collapsibleTagsSelector {
+ cursor: pointer;
+ padding: 4px 16px;
+ border-radius: 4px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ }
+
+ .collapsibleTagsSelector:hover {
+ background: var(--ifm-menu-color-background-active);
+ }
+
+ .tagsSeparator {
+ border-top: solid 1px var(--ifm-toc-border-color);
+ margin: 0 16px 8px 16px;
+ }
+
+ .tagsSection {
+ padding-bottom: 12px;
+ }
+
+ .tagSectionLabel {
+ color: var(--ifm-font-color-base)
+ }
+
+ .tagOption {
+ display: flex;
+ align-items: center;
+ height: 24px;
+ }
+
+ .tagRadioInput {
+ margin: 0 7px 0 4px;
+ }
+
+ .arrowIcon {
+ transition: transform 0.2s linear;
+ max-width: 20px;
+ max-height: 20px;
+ }
+
+ .tagsSelectorContent {
+ color: var(--ifm-menu-color);
+ padding: 8px 16px 0 32px;
+ overflow: hidden;
+ transition: height 0.2s linear;
+ }
+
+ .collapseSidebarButton {
+ display: block !important;
+ background-color: var(--ifm-button-background-color);
+ height: 40px;
+ position: sticky;
+ bottom: 0;
+ border-radius: 0;
+ border: 1px solid var(--ifm-toc-border-color);
+ }
+
+ .collapseSidebarButtonIcon {
+ transform: rotate(180deg);
+ margin-top: 4px;
+ }
+ html[dir='rtl'] .collapseSidebarButtonIcon {
+ transform: rotate(0);
+ }
+
+ html[data-theme='dark'] .collapseSidebarButton {
+ background-color: var(--collapse-button-bg-color-dark);
+ }
+
+ html[data-theme='dark'] .collapseSidebarButton:hover,
+ html[data-theme='dark'] .collapseSidebarButton:focus {
+ background-color: var(--ifm-color-emphasis-200);
+ }
+}
+
+.sidebarLogo,
+.collapseSidebarButton {
+ display: none;
+}
+
+.sidebarMenuIcon {
+ vertical-align: middle;
+}
+
+.sidebarMenuCloseIcon {
+ display: inline-flex;
+ justify-content: center;
+ align-items: center;
+ height: 24px;
+ font-size: 1.5rem;
+ font-weight: var(--ifm-font-weight-bold);
+ line-height: 0.9;
+ width: 24px;
+}
+
+:global(.menu__list) :global(.menu__list) {
+ overflow-y: hidden;
+ will-change: height;
+ transition: height var(--ifm-transition-fast) linear;
+}
+
+:global(.menu__list-item--collapsed) :global(.menu__list) {
+ height: 0 !important;
+}