mirror of
https://github.com/Lissy93/dashy.git
synced 2025-04-08 17:06:18 +02:00
Adds Carbon to sidebar
This commit is contained in:
parent
fb1b1529fa
commit
fc1f364f3f
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
|
@ -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>
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
353
src/theme/DocSidebar/index.js
Normal file
353
src/theme/DocSidebar/index.js
Normal 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)}>
|
||||
×
|
||||
</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;
|
150
src/theme/DocSidebar/styles.module.css
Normal file
150
src/theme/DocSidebar/styles.module.css
Normal 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;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user