diff --git a/src/components/CarbonAd.js b/src/components/CarbonAd.js
index f99f1d29..896d01e1 100644
--- a/src/components/CarbonAd.js
+++ b/src/components/CarbonAd.js
@@ -2,10 +2,7 @@ import React, { useEffect } from 'react';
const CarbonAds = () => {
return (
- <>
-
- >
);
};
diff --git a/src/pages/index.js b/src/pages/index.js
index 72791e92..af1fdf9a 100644
--- a/src/pages/index.js
+++ b/src/pages/index.js
@@ -1,9 +1,6 @@
import React from 'react';
import Layout from '@theme/Layout';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
-import Head from '@docusaurus/Head';
-import styles from './index.module.scss';
-
import HomePageHeader from '../components/HomePageHeader';
import HomePageFeatures from '../components/HomepageFeatures';
@@ -16,7 +13,7 @@ export default function Home() {
description="Dashy, the self-hosted dashboard app for your homelab">
-
+
);
diff --git a/src/styles/custom.scss b/src/styles/custom.scss
index f0bda5b6..5acc899e 100644
--- a/src/styles/custom.scss
+++ b/src/styles/custom.scss
@@ -244,3 +244,29 @@ html[data-theme='light'] {
--carbon-text-color: hsl(0, 0%, 90%);
}
}
+
+.sidebar-ad {
+ --carbon-bg-primary: var(--background, #18191a);
+ --carbon-bg-secondary: #282a36;
+ --carbon-text-color: #e6e6e6;
+
+ #carbonads {
+ margin: 0.5rem;
+ }
+ #carbonads .carbon-img img, .avatar__photo-link {
+ border-radius: 5px;
+ }
+ #carbonads .carbon-wrap {
+ justify-content: center;
+ }
+ #carbonads .carbon-text {
+ font-size: 1rem;
+ }
+ #carbonads > span {
+ box-shadow: none;
+ }
+ #carbonads .carbon-poweredby {
+ border-radius: 5px;
+ font-size: 0.7rem;
+ }
+}
diff --git a/src/theme/DocSidebar/index.js b/src/theme/DocSidebar/index.js
new file mode 100644
index 00000000..76dd38f2
--- /dev/null
+++ b/src/theme/DocSidebar/index.js
@@ -0,0 +1,353 @@
+/**
+ * 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';
+import CarbonAds from '../../components/CarbonAd';
+const MOBILE_TOGGLE_SIZE = 24;
+
+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 (
+
+
+ {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 00000000..6dcbbfb1
--- /dev/null
+++ b/src/theme/DocSidebar/styles.module.css
@@ -0,0 +1,150 @@
+/**
+ * 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);
+ }
+
+ .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;
+}
+
+.sidebar-ad {
+ --carbon-bg-primary: var(--background, #18191a);
+ --carbon-bg-secondary: #282a36;
+ --carbon-text-color: #e6e6e6;
+}
+
+.sidebar-ad #carbonads {
+ margin: 0.5rem;
+}
+.sidebar-ad #carbonads .carbon-img img, .sidebar-ad .avatar__photo-link {
+ border-radius: 5px;
+}
+.sidebar-ad #carbonads .carbon-wrap {
+ justify-content: center;
+}
+.sidebar-ad #carbonads .carbon-text {
+ font-size: 1rem;
+}
+.sidebar-ad #carbonads > span {
+ box-shadow: none;
+}
+.sidebar-ad #carbonads .carbon-poweredby {
+ border-radius: 5px;
+ font-size: 0.7rem;
+}