Adds Carbon to sidebar

This commit is contained in:
Alicia Sykes 2024-01-08 11:41:55 +00:00
parent fb1b1529fa
commit fc1f364f3f
5 changed files with 530 additions and 7 deletions

View File

@ -2,10 +2,7 @@ import React, { useEffect } from 'react';
const CarbonAds = () => {
return (
<>
<div id="carbonads"></div>
<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CWYIC53L&placement=dashyto" id="_carbonads_js"></script>
</>
);
};

View File

@ -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">
<HomePageHeader />
<HomePageFeatures />
<main>
<main>
</main>
</Layout>
);

View File

@ -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;
}
}

View File

@ -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) => (
<DocSidebarItem
key={index} // sidebar is static, the index does not change
item={item}
{...props}
/>
));
});
function DocSidebarItem(props) {
switch (props.item.type) {
case 'category':
return <DocSidebarItemCategory {...props} />;
case 'link':
default:
return <DocSidebarItemLink {...props} />;
}
}
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 (
<li
className={clsx('menu__list-item', {
'menu__list-item--collapsed': collapsed,
})}>
<a
className={clsx('menu__link', {
'menu__link--sublist': collapsible,
'menu__link--active': collapsible && isActive,
[styles.menuLinkText]: !collapsible,
})}
onClick={collapsible ? handleItemClick : undefined}
href={collapsible ? '#!' : undefined}
{...props}>
{label}
</a>
<ul
className="menu__list"
ref={menuListRef}
style={{
height: menuListHeight,
}}
onTransitionEnd={() => {
if (!collapsed) {
handleMenuListHeight(false);
}
}}>
<DocSidebarItems
items={items}
tabIndex={collapsed ? '-1' : '0'}
onItemClick={onItemClick}
collapsible={collapsible}
activePath={activePath}
/>
</ul>
</li>
);
}
function DocSidebarItemLink({
item,
onItemClick,
activePath,
collapsible: _collapsible,
...props
}) {
const {href, label} = item;
const isActive = isActiveSidebarItem(item, activePath);
return (
<li className="menu__list-item" key={label}>
<Link
className={clsx('menu__link', {
'menu__link--active': isActive,
})}
to={href}
{...(isInternalUrl(href) && {
isNavLink: true,
exact: true,
onClick: onItemClick,
})}
{...props}>
{isInternalUrl(href) ? (
label
) : (
<span>
{label}
<IconExternalLink />
</span>
)}
</Link>
</li>
);
}
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 (
<button
type="button"
title={translate({
id: 'theme.docs.sidebar.collapseButtonTitle',
message: 'Collapse sidebar',
description: 'The title attribute for collapse button of doc sidebar',
})}
aria-label={translate({
id: 'theme.docs.sidebar.collapseButtonAriaLabel',
message: 'Collapse sidebar',
description: 'The title attribute for collapse button of doc sidebar',
})}
className={clsx(
'button button--secondary button--outline',
styles.collapseSidebarButton,
)}
onClick={onClick}>
<IconArrow className={styles.collapseSidebarButtonIcon} />
</button>
);
}
function ResponsiveSidebarButton({responsiveSidebarOpened, onClick}) {
return (
<button
aria-label={
responsiveSidebarOpened
? translate({
id: 'theme.docs.sidebar.responsiveCloseButtonLabel',
message: 'Close menu',
description:
'The ARIA label for close button of mobile doc sidebar',
})
: translate({
id: 'theme.docs.sidebar.responsiveOpenButtonLabel',
message: 'Open menu',
description:
'The ARIA label for open button of mobile doc sidebar',
})
}
aria-haspopup="true"
className="button button--secondary button--sm menu__button"
type="button"
onClick={onClick}>
{responsiveSidebarOpened ? (
<span
className={clsx(styles.sidebarMenuIcon, styles.sidebarMenuCloseIcon)}>
&times;
</span>
) : (
<IconMenu
className={styles.sidebarMenuIcon}
height={MOBILE_TOGGLE_SIZE}
width={MOBILE_TOGGLE_SIZE}
/>
)}
</button>
);
}
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 (
<div
className={clsx(styles.sidebar, {
[styles.sidebarWithHideableNavbar]: hideOnScroll,
[styles.sidebarHidden]: isHidden,
})}>
{hideOnScroll && <Logo tabIndex={-1} className={styles.sidebarLogo} />}
<nav
className={clsx(
'menu',
'menu--responsive',
'thin-scrollbar',
styles.menu,
{
'menu--show': showResponsiveSidebar,
[styles.menuWithAnnouncementBar]:
!isAnnouncementBarClosed && showAnnouncementBar,
},
)}
aria-label={translate({
id: 'theme.docs.sidebar.navAriaLabel',
message: 'Sidebar navigation',
description: 'The ARIA label for documentation menu',
})}>
<ResponsiveSidebarButton
responsiveSidebarOpened={showResponsiveSidebar}
onClick={toggleResponsiveSidebar}
/>
<ul className="menu__list">
<DocSidebarItems
items={sidebar}
onItemClick={closeResponsiveSidebar}
collapsible={sidebarCollapsible}
activePath={path}
/>
</ul>
</nav>
<div class="sidebar-ad"><CarbonAds /></div>
{hideableSidebar && <HideableSidebarButton onClick={onCollapse} />}
</div>
);
}
export default DocSidebar;

View File

@ -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;
}