implement sidebar

This commit is contained in:
jinhojang6
2023-05-26 03:31:20 +09:00
parent 113d66f1cf
commit ccaee13ea5
14 changed files with 537 additions and 2 deletions
@@ -0,0 +1,6 @@
---
title: 1
sidebar_position: 1
---
# 111
@@ -0,0 +1,6 @@
---
title: 2
sidebar_position: 1
---
# 222
@@ -0,0 +1,6 @@
---
title: 333
sidebar_position: 1
---
# 333
@@ -12,7 +12,6 @@
--ifm-navbar-link-color: rgb(var(--lsd-text-primary));
--ifm-dropdown-link-color: rgb(var(--lsd-text-secondary));
--ifm-dropdown-hover-background-color: rgb(var(--lsd-surface-secondary));
--ifm-menu-color-active: rgb(var(--lsd-text-secondary));
/* Colors. */
/*--ifm-dark-value: 10%;*/
@@ -159,7 +158,8 @@
--ifm-menu-link-padding-horizontal: var(--ifm-spacing-horizontal);
--ifm-menu-link-padding-vertical: 0.375rem;
--ifm-menu-color-background-active: var(--ifm-color-gray-300);
--ifm-menu-color: rgb(var(--lsd-text-primary), 0.6);
--ifm-menu-color-background-active: transparent;
--ifm-menu-link-sublist-icon: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="16px" height="16px" viewBox="0 0 24 24"><path fill="rgba(0,0,0,0.5)" d="M7.41 15.41L12 10.83l4.59 4.58L18 14l-6-6-6 6z"></path></svg>');
--ifm-menu-link-sublist-icon-filter: none;
@@ -317,6 +317,12 @@ small {
border: none !important;
}
.menu__link {
display: flex;
justify-content: space-between;
align-items: center;
}
@media (max-width: 576px) {
:root {
--ifm-global-spacing: 0.5rem;
@@ -0,0 +1,74 @@
import React, { useState, useCallback } from 'react'
import clsx from 'clsx'
import { prefersReducedMotion, ThemeClassNames } from '@docusaurus/theme-common'
import { useDocsSidebar } from '@docusaurus/theme-common/internal'
import { useLocation } from '@docusaurus/router'
import DocSidebar from '@theme/DocSidebar'
import ExpandButton from '@theme/DocPage/Layout/Sidebar/ExpandButton'
import styles from './styles.module.css'
// Reset sidebar state when sidebar changes
// Use React key to unmount/remount the children
// See https://github.com/facebook/docusaurus/issues/3414
function ResetOnSidebarChange({ children }) {
const sidebar = useDocsSidebar()
return (
<React.Fragment key={sidebar?.name ?? 'noSidebar'}>
{children}
</React.Fragment>
)
}
export default function DocPageLayoutSidebar({
sidebar,
hiddenSidebarContainer,
setHiddenSidebarContainer,
}) {
const { pathname } = useLocation()
const [hiddenSidebar, setHiddenSidebar] = useState(false)
const toggleSidebar = useCallback(() => {
if (hiddenSidebar) {
setHiddenSidebar(false)
}
// onTransitionEnd won't fire when sidebar animation is disabled
// fixes https://github.com/facebook/docusaurus/issues/8918
if (!hiddenSidebar && prefersReducedMotion()) {
setHiddenSidebar(true)
}
setHiddenSidebarContainer((value) => !value)
}, [setHiddenSidebarContainer, hiddenSidebar])
return (
<aside
className={clsx(
ThemeClassNames.docs.docSidebarContainer,
styles.docSidebarContainer,
hiddenSidebarContainer && styles.docSidebarContainerHidden,
)}
onTransitionEnd={(e) => {
if (
!e.currentTarget.classList.contains(styles.docSidebarContainer ?? '')
) {
return
}
if (hiddenSidebarContainer) {
setHiddenSidebar(true)
}
}}
>
<ResetOnSidebarChange>
<div
className={clsx(
styles.sidebarViewport,
hiddenSidebar && styles.sidebarViewportHidden,
)}
>
<DocSidebar
sidebar={sidebar}
path={pathname}
onCollapse={toggleSidebar}
isHidden={hiddenSidebar}
/>
{hiddenSidebar && <ExpandButton toggleSidebar={toggleSidebar} />}
</div>
</ResetOnSidebarChange>
</aside>
)
}
@@ -0,0 +1,31 @@
:root {
--doc-sidebar-width: 300px;
--doc-sidebar-hidden-width: 30px;
}
.docSidebarContainer {
display: none;
}
@media (min-width: 997px) {
.docSidebarContainer {
display: block;
width: var(--doc-sidebar-width);
margin-top: calc(-1 * var(--ifm-navbar-height));
will-change: width;
transition: width var(--ifm-transition-fast) ease;
clip-path: inset(0);
}
.docSidebarContainerHidden {
width: var(--doc-sidebar-hidden-width);
cursor: pointer;
}
.sidebarViewport {
top: 0;
position: sticky;
height: 100%;
max-height: 100vh;
}
}
@@ -0,0 +1,30 @@
import React from 'react'
import clsx from 'clsx'
import { translate } from '@docusaurus/Translate'
import IconArrow from '@theme/Icon/Arrow'
import styles from './styles.module.css'
export default function CollapseButton({ 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>
)
}
@@ -0,0 +1,40 @@
:root {
--docusaurus-collapse-button-bg: transparent;
--docusaurus-collapse-button-bg-hover: rgb(0 0 0 / 10%);
}
[data-theme='dark']:root {
--docusaurus-collapse-button-bg: rgb(255 255 255 / 5%);
--docusaurus-collapse-button-bg-hover: rgb(255 255 255 / 10%);
}
@media (min-width: 997px) {
.collapseSidebarButton {
display: block !important;
background-color: var(--docusaurus-collapse-button-bg);
height: 40px;
position: sticky;
bottom: 0;
border-radius: 0;
border: 1px solid var(--ifm-toc-border-color);
}
.collapseSidebarButtonIcon {
transform: rotate(180deg);
margin-top: 4px;
}
[dir='rtl'] .collapseSidebarButtonIcon {
transform: rotate(0);
}
.collapseSidebarButton:hover,
.collapseSidebarButton:focus {
background-color: var(--docusaurus-collapse-button-bg-hover);
}
}
.collapseSidebarButton {
display: none;
margin: 0;
}
@@ -0,0 +1,45 @@
import React, { useState } from 'react'
import clsx from 'clsx'
import { ThemeClassNames } from '@docusaurus/theme-common'
import {
useAnnouncementBar,
useScrollPosition,
} from '@docusaurus/theme-common/internal'
import { translate } from '@docusaurus/Translate'
import DocSidebarItems from '@theme/DocSidebarItems'
import styles from './styles.module.css'
function useShowAnnouncementBar() {
const { isActive } = useAnnouncementBar()
const [showAnnouncementBar, setShowAnnouncementBar] = useState(isActive)
useScrollPosition(
({ scrollY }) => {
if (isActive) {
setShowAnnouncementBar(scrollY === 0)
}
},
[isActive],
)
return isActive && showAnnouncementBar
}
export default function DocSidebarDesktopContent({ path, sidebar, className }) {
const showAnnouncementBar = useShowAnnouncementBar()
return (
<nav
aria-label={translate({
id: 'theme.docs.sidebar.navAriaLabel',
message: 'Docs sidebar',
description: 'The ARIA label for the sidebar navigation',
})}
className={clsx(
'menu thin-scrollbar',
styles.menu,
showAnnouncementBar && styles.menuWithAnnouncementBar,
className,
)}
>
<ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list')}>
<DocSidebarItems items={sidebar} activePath={path} level={1} />
</ul>
</nav>
)
}
@@ -0,0 +1,16 @@
@media (min-width: 997px) {
.menu {
flex-grow: 1;
padding: 0.5rem;
}
@supports (scrollbar-gutter: stable) {
.menu {
padding: 0.5rem 0 0.5rem 0.5rem;
scrollbar-gutter: stable;
}
}
.menuWithAnnouncementBar {
margin-bottom: var(--docusaurus-announcement-bar-height);
}
}
@@ -0,0 +1,27 @@
import React from 'react'
import clsx from 'clsx'
import { useThemeConfig } from '@docusaurus/theme-common'
import Logo from '@theme/Logo'
import Content from '@theme/DocSidebar/Desktop/Content'
import styles from './styles.module.css'
function DocSidebarDesktop({ path, sidebar, onCollapse, isHidden }) {
const {
navbar: { hideOnScroll },
docs: {
sidebar: { hideable },
},
} = useThemeConfig()
return (
<div
className={clsx(
styles.sidebar,
hideOnScroll && styles.sidebarWithHideableNavbar,
isHidden && styles.sidebarHidden,
)}
>
{hideOnScroll && <Logo tabIndex={-1} className={styles.sidebarLogo} />}
<Content path={path} sidebar={sidebar} />
</div>
)
}
export default React.memo(DocSidebarDesktop)
@@ -0,0 +1,37 @@
@media (min-width: 997px) {
.sidebar {
display: flex;
flex-direction: column;
height: 100%;
padding-top: var(--ifm-navbar-height);
width: var(--doc-sidebar-width);
}
.sidebarWithHideableNavbar {
padding-top: 0;
}
.sidebarHidden {
opacity: 0;
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;
}
}
.sidebarLogo {
display: none;
}
@@ -0,0 +1,18 @@
import React from 'react'
import { useWindowSize } from '@docusaurus/theme-common'
import DocSidebarDesktop from '@theme/DocSidebar/Desktop'
import DocSidebarMobile from '@theme/DocSidebar/Mobile'
export default function DocSidebar(props) {
const windowSize = useWindowSize()
// Desktop sidebar visible on hydration: need SSR rendering
const shouldRenderSidebarDesktop =
windowSize === 'desktop' || windowSize === 'ssr'
// Mobile sidebar not visible on hydration: can avoid SSR rendering
const shouldRenderSidebarMobile = windowSize === 'mobile'
return (
<>
{shouldRenderSidebarDesktop && <DocSidebarDesktop {...props} />}
{shouldRenderSidebarMobile && <DocSidebarMobile {...props} />}
</>
)
}
@@ -0,0 +1,193 @@
import React, { useEffect, useMemo } from 'react'
import clsx from 'clsx'
import {
ThemeClassNames,
useThemeConfig,
usePrevious,
Collapsible,
useCollapsible,
} from '@docusaurus/theme-common'
import {
isActiveSidebarItem,
findFirstCategoryLink,
useDocSidebarItemsExpandedState,
isSamePath,
} from '@docusaurus/theme-common/internal'
import Link from '@docusaurus/Link'
import { translate } from '@docusaurus/Translate'
import useIsBrowser from '@docusaurus/useIsBrowser'
import DocSidebarItems from '@theme/DocSidebarItems'
import { ArrowDownIcon, ArrowUpIcon } from '@acid-info/lsd-react'
// If we navigate to a category and it becomes active, it should automatically
// expand itself
function useAutoExpandActiveCategory({ isActive, collapsed, updateCollapsed }) {
const wasActive = usePrevious(isActive)
useEffect(() => {
const justBecameActive = isActive && !wasActive
if (justBecameActive && collapsed) {
updateCollapsed(false)
}
}, [isActive, wasActive, collapsed, updateCollapsed])
}
/**
* When a collapsible category has no link, we still link it to its first child
* during SSR as a temporary fallback. This allows to be able to navigate inside
* the category even when JS fails to load, is delayed or simply disabled
* React hydration becomes an optional progressive enhancement
* see https://github.com/facebookincubator/infima/issues/36#issuecomment-772543188
* see https://github.com/facebook/docusaurus/issues/3030
*/
function useCategoryHrefWithSSRFallback(item) {
const isBrowser = useIsBrowser()
return useMemo(() => {
if (item.href) {
return item.href
}
// In these cases, it's not necessary to render a fallback
// We skip the "findFirstCategoryLink" computation
if (isBrowser || !item.collapsible) {
return undefined
}
return findFirstCategoryLink(item)
}, [item, isBrowser])
}
function CollapseButton({ categoryLabel, onClick }) {
return (
<button
aria-label={translate(
{
id: 'theme.DocSidebarItem.toggleCollapsedCategoryAriaLabel',
message: "Toggle the collapsible sidebar category '{label}'",
description:
'The ARIA label to toggle the collapsible sidebar category',
},
{ label: categoryLabel },
)}
type="button"
className="clean-btn menu__caret"
onClick={onClick}
/>
)
}
export default function DocSidebarItemCategory({
item,
onItemClick,
activePath,
level,
index,
...props
}) {
const { items, label, collapsible, className, href } = item
const {
docs: {
sidebar: { autoCollapseCategories },
},
} = useThemeConfig()
const hrefWithSSRFallback = useCategoryHrefWithSSRFallback(item)
const isActive = isActiveSidebarItem(item, activePath)
const isCurrentPage = isSamePath(href, activePath)
const { collapsed, setCollapsed } = useCollapsible({
// Active categories are always initialized as expanded. The default
// (`item.collapsed`) is only used for non-active categories.
initialState: () => {
if (!collapsible) {
return false
}
return isActive ? false : item.collapsed
},
})
const { expandedItem, setExpandedItem } = useDocSidebarItemsExpandedState()
// Use this instead of `setCollapsed`, because it is also reactive
const updateCollapsed = (toCollapsed = !collapsed) => {
setExpandedItem(toCollapsed ? null : index)
setCollapsed(toCollapsed)
}
useAutoExpandActiveCategory({ isActive, collapsed, updateCollapsed })
useEffect(() => {
if (
collapsible &&
expandedItem != null &&
expandedItem !== index &&
autoCollapseCategories
) {
setCollapsed(true)
}
}, [collapsible, expandedItem, index, setCollapsed, autoCollapseCategories])
return (
<li
className={clsx(
ThemeClassNames.docs.docSidebarItemCategory,
ThemeClassNames.docs.docSidebarItemCategoryLevel(level),
'menu__list-item',
{
'menu__list-item--collapsed': collapsed,
},
className,
)}
>
<div
className={clsx('menu__list-item-collapsible', {
'menu__list-item-collapsible--active': isCurrentPage,
})}
>
<Link
className={clsx('menu__link', {
'menu__link--sublist': collapsible,
'menu__link--active': isActive,
})}
onClick={
collapsible
? (e) => {
onItemClick?.(item)
if (href) {
updateCollapsed(false)
} else {
e.preventDefault()
updateCollapsed()
}
}
: () => {
onItemClick?.(item)
}
}
aria-current={isCurrentPage ? 'page' : undefined}
aria-expanded={collapsible ? !collapsed : undefined}
href={collapsible ? hrefWithSSRFallback ?? '#' : hrefWithSSRFallback}
{...props}
>
{label}
{collapsed ? <ArrowUpIcon /> : <ArrowDownIcon />}
</Link>
{href && collapsible && (
<CollapseButton
categoryLabel={label}
onClick={(e) => {
e.preventDefault()
updateCollapsed()
}}
/>
)}
</div>
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
<DocSidebarItems
items={items}
tabIndex={collapsed ? -1 : 0}
onItemClick={onItemClick}
activePath={activePath}
level={level + 1}
/>
</Collapsible>
</li>
)
}