mirror of
https://github.com/wavetermdev/docusaurus-og.git
synced 2026-08-05 13:46:35 -07:00
Merge pull request #48 from acid-info/theme/mobile-header
Theme/mobile header
This commit is contained in:
@@ -10,5 +10,10 @@ export const Button = (
|
||||
props: TProps & HTMLProps<HTMLButtonElement>,
|
||||
): JSX.Element => {
|
||||
const { children, className, ...rest } = props
|
||||
return <button className={clsx('button', className)}>{children}</button>
|
||||
|
||||
return (
|
||||
<button className={clsx('button', className)} {...(rest as any)}>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,6 +15,8 @@ import LinkedinSvg from '@site/static/icons/linkedin.svg'
|
||||
import DiscourseSvg from '@site/static/icons/discourse.svg'
|
||||
import TelegramSvg from '@site/static/icons/telegram.svg'
|
||||
import StatusSvg from '@site/static/icons/status.svg'
|
||||
import SearchSvg from '@site/static/icons/search.svg'
|
||||
import DropdownSvg from '@site/static/icons/dropdown.svg'
|
||||
|
||||
type TIconProps = {
|
||||
size?: 's' | 'm' | 'l'
|
||||
@@ -113,3 +115,15 @@ export const IconFolderFilled = (props: TIconProps): JSX.Element => (
|
||||
<FolderSvg />
|
||||
</Icon>
|
||||
)
|
||||
|
||||
export const IconSearch = (props: TIconProps): JSX.Element => (
|
||||
<Icon {...props}>
|
||||
<SearchSvg />
|
||||
</Icon>
|
||||
)
|
||||
|
||||
export const IconDropdown = (props: TIconProps): JSX.Element => (
|
||||
<Icon {...props}>
|
||||
<DropdownSvg />
|
||||
</Icon>
|
||||
)
|
||||
|
||||
@@ -251,9 +251,6 @@ small {
|
||||
.footer__logo {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.menu_src-theme-Navbar-Content-styles-module svg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.card {
|
||||
padding: var(--ifm-alert-padding-vertical) var(--ifm-alert-padding-horizontal);
|
||||
|
||||
@@ -0,0 +1,215 @@
|
||||
import React, { type ComponentProps, 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 type { Props } from '@theme/DocSidebarItem/Category'
|
||||
import {
|
||||
IconArrowLeftCircle,
|
||||
IconArrowRightCircle,
|
||||
IconDropdown,
|
||||
} from '@site/src/components/Icon'
|
||||
|
||||
// If we navigate to a category and it becomes active, it should automatically
|
||||
// expand itself
|
||||
function useAutoExpandActiveCategory({
|
||||
isActive,
|
||||
collapsed,
|
||||
updateCollapsed,
|
||||
}: {
|
||||
isActive: boolean
|
||||
collapsed: boolean
|
||||
updateCollapsed: (b: boolean) => void
|
||||
}) {
|
||||
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: Props['item'],
|
||||
): string | undefined {
|
||||
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,
|
||||
}: {
|
||||
categoryLabel: string
|
||||
onClick: ComponentProps<'button'>['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
|
||||
}: Props): JSX.Element {
|
||||
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: boolean = !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--sublist-caret': !href && 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}
|
||||
<span className="dropdown-icon">
|
||||
<IconDropdown />
|
||||
</span>
|
||||
</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>
|
||||
)
|
||||
}
|
||||
@@ -23,6 +23,13 @@
|
||||
background-color: var(--ifm-background-surface-color);
|
||||
}
|
||||
|
||||
:root {
|
||||
&,
|
||||
body {
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
}
|
||||
|
||||
@include utils.responsive('lg', 'down') {
|
||||
.background-underlay {
|
||||
display: none;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
.root {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
width: 100%;
|
||||
padding: 0 1.875rem;
|
||||
|
||||
& > div {
|
||||
flex: 0 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.root > div:nth-child(2) {
|
||||
width: 57%;
|
||||
flex-basis: 57%;
|
||||
margin-left: 12px;
|
||||
flex-shrink: 0;
|
||||
flex-grow: 1;
|
||||
flex: 1 1 57% !important;
|
||||
|
||||
button {
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.root > div:last-child > button {
|
||||
font-size: 0.75rem;
|
||||
font-weight: bold;
|
||||
color: #909091;
|
||||
margin-left: 8px;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import NavbarLogo from '@theme/Navbar/Logo'
|
||||
import clsx from 'clsx'
|
||||
import React, { useState } from 'react'
|
||||
import { useDebounce } from 'react-use'
|
||||
import { SearchBar } from '../../../SearchBar/SearchBar'
|
||||
import styles from './MobileSearch.module.scss'
|
||||
|
||||
export const MobileSearch: React.FC<{
|
||||
render: boolean
|
||||
onCancel: () => void
|
||||
}> = ({ render, onCancel }) => {
|
||||
const [renderSearchBar, setRenderSearchBar] = useState(false)
|
||||
useDebounce(() => setRenderSearchBar(render), render ? 0 : 1000, [render])
|
||||
|
||||
return (
|
||||
<div className={clsx(styles.root)}>
|
||||
<div>
|
||||
<NavbarLogo />
|
||||
</div>
|
||||
<div>{renderSearchBar && <SearchBar />}</div>
|
||||
<div>
|
||||
<button className={clsx('clean-btn')} onClick={onCancel}>
|
||||
Cancel
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './MobileSearch'
|
||||
@@ -1,5 +1,10 @@
|
||||
import { useThemeConfig } from '@docusaurus/theme-common'
|
||||
import { useNavbarMobileSidebar } from '@docusaurus/theme-common/internal'
|
||||
import {
|
||||
useNavbarMobileSidebar,
|
||||
useSearchPage,
|
||||
} from '@docusaurus/theme-common/internal'
|
||||
import { IconSearch } from '@site/src/components/Icon'
|
||||
import { IconButton } from '@site/src/components/IconButton'
|
||||
import {
|
||||
globalStore,
|
||||
selectHiddenSidebar,
|
||||
@@ -12,7 +17,9 @@ import NavbarSearch from '@theme/Navbar/Search'
|
||||
import NavbarItem, { Props as NavbarItemConfig } from '@theme/NavbarItem'
|
||||
import SearchBar from '@theme/SearchBar'
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import { useSearch } from '../../SearchBar/hooks/useSearch'
|
||||
import { MobileSearch } from './MobileSearch/MobileSearch'
|
||||
import { ShareButton } from './ShareButton'
|
||||
import styles from './styles.module.scss'
|
||||
|
||||
@@ -59,8 +66,16 @@ export default function NavbarContent(): JSX.Element {
|
||||
const dispatch = globalStore.useDispatch()
|
||||
const hiddenDesktopSidebar = globalStore.useSelector(selectHiddenSidebar)
|
||||
|
||||
const [mobileSearch, setMobileSearch] = useState(false)
|
||||
|
||||
return (
|
||||
<div className={clsx('row', styles.root)}>
|
||||
<div
|
||||
className={clsx(
|
||||
'row',
|
||||
styles.root,
|
||||
mobileSearch && styles.activeMobileSearch,
|
||||
)}
|
||||
>
|
||||
<div className="col col--2">
|
||||
<SidebarToggleButton
|
||||
onToggle={mobileSidebar.toggle}
|
||||
@@ -92,7 +107,7 @@ export default function NavbarContent(): JSX.Element {
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<nav className={styles.menu}>
|
||||
<nav className={clsx(styles.menu, styles.links)}>
|
||||
<NavbarItems items={links} />
|
||||
</nav>
|
||||
</div>
|
||||
@@ -108,6 +123,34 @@ export default function NavbarContent(): JSX.Element {
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
'col',
|
||||
styles.headerRightMobile,
|
||||
mobileSearch && styles.shifted,
|
||||
)}
|
||||
>
|
||||
{localeDropdown && <NavbarItem {...localeDropdown} />}
|
||||
<IconButton
|
||||
className={styles.searchButton}
|
||||
onClick={() => setMobileSearch(true)}
|
||||
>
|
||||
<IconSearch />
|
||||
</IconButton>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={clsx(
|
||||
styles.mobileSearchContainer,
|
||||
mobileSearch && styles.visible,
|
||||
)}
|
||||
>
|
||||
<MobileSearch
|
||||
render={mobileSearch}
|
||||
onCancel={() => setMobileSearch(false)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -116,16 +116,15 @@
|
||||
|
||||
.root > * {
|
||||
width: auto;
|
||||
flex: 0 0 auto !important;
|
||||
flex: 0 0 auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.headerLeft {
|
||||
.root > div:first-child {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.headerMiddle {
|
||||
margin-left: 20px;
|
||||
|
||||
.leftContainer.shifted {
|
||||
transform: none;
|
||||
}
|
||||
@@ -138,6 +137,55 @@
|
||||
.headerRight {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.searchContainer {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.headerRightMobile {
|
||||
visibility: visible;
|
||||
display: flex !important;
|
||||
|
||||
flex: 1 1 auto !important;
|
||||
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
& > div {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
& > div:first-child {
|
||||
border-radius: 12px;
|
||||
padding: 4px 12px;
|
||||
height: 2.125rem;
|
||||
|
||||
a {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
svg {
|
||||
margin-left: 0.75rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
margin-right: 0.75rem;
|
||||
}
|
||||
|
||||
.searchButton {
|
||||
background-color: #eeeef1;
|
||||
padding: 7px 13px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
transition: all 0.8s, opacity 0.4s;
|
||||
transform: translateX(0%);
|
||||
|
||||
&.shifted {
|
||||
transform: translateX(-100%);
|
||||
opacity: 0%;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -159,4 +207,65 @@ Hide color mode toggle in small viewports
|
||||
.sidebarButton.desktop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.root > div:first-child,
|
||||
.headerMiddle {
|
||||
transition: 1s;
|
||||
}
|
||||
}
|
||||
|
||||
.root {
|
||||
& > div:first-child,
|
||||
.headerMiddle {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.root.activeMobileSearch {
|
||||
& > div:first-child,
|
||||
.headerMiddle {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.mobileSearchContainer {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
|
||||
background-color: var(--ifm-background-surface-color);
|
||||
transform: translateX(100%);
|
||||
opacity: 0;
|
||||
transition: 0.6s ease-in-out;
|
||||
visibility: hidden;
|
||||
|
||||
&.visible {
|
||||
opacity: 1;
|
||||
transform: translateX(0%);
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.headerRightMobile {
|
||||
display: none !important;
|
||||
visibility: hidden;
|
||||
|
||||
flex-grow: 1 !important;
|
||||
}
|
||||
|
||||
@include utils.responsive('lg', 'down') {
|
||||
.headerRightMobile {
|
||||
display: flex !important;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.links {
|
||||
a svg {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useNavbarMobileSidebar } from '@docusaurus/theme-common/internal'
|
||||
import { IconArrowLeftCircle } from '@site/src/components/Icon'
|
||||
import NavbarColorModeToggle from '@theme/Navbar/ColorModeToggle'
|
||||
import NavbarLogo from '@theme/Navbar/Logo'
|
||||
import React from 'react'
|
||||
|
||||
function CloseButton() {
|
||||
const mobileSidebar = useNavbarMobileSidebar()
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="clean-btn navbar-sidebar__close"
|
||||
onClick={() => mobileSidebar.toggle()}
|
||||
>
|
||||
<IconArrowLeftCircle />
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default function NavbarMobileSidebarHeader(): JSX.Element {
|
||||
return (
|
||||
<div className="navbar-sidebar__brand">
|
||||
<div
|
||||
style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}
|
||||
>
|
||||
<CloseButton />
|
||||
<NavbarLogo />
|
||||
</div>
|
||||
<NavbarColorModeToggle className="margin-right--md" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import React, { type ComponentProps } from 'react'
|
||||
import { useThemeConfig } from '@docusaurus/theme-common'
|
||||
import { useNavbarSecondaryMenu } from '@docusaurus/theme-common/internal'
|
||||
import Translate from '@docusaurus/Translate'
|
||||
import { ButtonWithIcon } from '@site/src/components/Button/ButtonWithIcon'
|
||||
import { IconArrowLeftCircle } from '@site/src/components/Icon'
|
||||
import styles from './styles.module.scss'
|
||||
|
||||
function SecondaryMenuBackButton(props: ComponentProps<'button'>) {
|
||||
return (
|
||||
<ButtonWithIcon
|
||||
className={styles.backButton}
|
||||
icon={<IconArrowLeftCircle></IconArrowLeftCircle>}
|
||||
{...props}
|
||||
>
|
||||
<Translate
|
||||
id="theme.navbar.mobileSidebarSecondaryMenu.backButtonLabel"
|
||||
description="The label of the back button to return to main menu, inside the mobile navbar sidebar secondary menu (notably used to display the docs sidebar)"
|
||||
>
|
||||
Main Menu
|
||||
</Translate>
|
||||
</ButtonWithIcon>
|
||||
)
|
||||
}
|
||||
|
||||
// The secondary menu slides from the right and shows contextual information
|
||||
// such as the docs sidebar
|
||||
export default function NavbarMobileSidebarSecondaryMenu(): JSX.Element | null {
|
||||
const isPrimaryMenuEmpty = useThemeConfig().navbar.items.length === 0
|
||||
const secondaryMenu = useNavbarSecondaryMenu()
|
||||
return (
|
||||
<>
|
||||
{/* edge-case: prevent returning to the primaryMenu when it's empty */}
|
||||
{!isPrimaryMenuEmpty && (
|
||||
<SecondaryMenuBackButton onClick={() => secondaryMenu.hide()} />
|
||||
)}
|
||||
{secondaryMenu.content}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
.backButton {
|
||||
& > div {
|
||||
flex-direction: row-reverse;
|
||||
|
||||
color: #373738;
|
||||
font-size: 0.875rem;
|
||||
|
||||
svg {
|
||||
* {
|
||||
stroke: #909091;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
border: 1px solid #eeeef1;
|
||||
padding: 12px 18px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
@use '@site/src/css/utils';
|
||||
|
||||
.navbar-sidebar__brand {
|
||||
--ifm-navbar-padding-horizontal: 1rem;
|
||||
}
|
||||
@@ -11,5 +13,58 @@
|
||||
background: none;
|
||||
}
|
||||
|
||||
:root {
|
||||
@include utils.responsive('lg', 'down') {
|
||||
.navbar {
|
||||
background: var(--ifm-background-surface-color);
|
||||
}
|
||||
}
|
||||
|
||||
.overflow-hidden {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
||||
.navbar-sidebar__item.menu,
|
||||
.theme-doc-sidebar-menu {
|
||||
li a,
|
||||
.theme-doc-sidebar-item-category a {
|
||||
padding: 0.75rem 1.125rem;
|
||||
border-radius: 0.75rem;
|
||||
|
||||
font-size: 0.875rem;
|
||||
color: #373738;
|
||||
|
||||
.menu__link--active {
|
||||
background-color: #eeeef0;
|
||||
}
|
||||
}
|
||||
|
||||
.menu__list-item-collapsible {
|
||||
border-radius: 0.75rem;
|
||||
|
||||
a::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dropdown-icon {
|
||||
position: absolute;
|
||||
right: 1.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
li.menu__list-item {
|
||||
.dropdown-icon {
|
||||
transform: rotate(0deg);
|
||||
transition: transform var(--ifm-transition-fast);
|
||||
}
|
||||
|
||||
&--collapsed {
|
||||
.dropdown-icon {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
li:not(:first-child) {
|
||||
margin-top: 0.125rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,27 @@
|
||||
import React from 'react'
|
||||
import { useNavbarMobileSidebar } from '@docusaurus/theme-common/internal'
|
||||
import type { WrapperProps } from '@docusaurus/types'
|
||||
import Navbar from '@theme-original/Navbar'
|
||||
import type NavbarType from '@theme/Navbar'
|
||||
import type { WrapperProps } from '@docusaurus/types'
|
||||
import React, { useEffect } from 'react'
|
||||
import './index.scss'
|
||||
|
||||
type Props = WrapperProps<typeof NavbarType>
|
||||
|
||||
export default function NavbarWrapper(props: Props): JSX.Element {
|
||||
const mobileSidebar = useNavbarMobileSidebar()
|
||||
|
||||
useEffect(() => {
|
||||
if (mobileSidebar.shown) {
|
||||
document
|
||||
.querySelector('#__docusaurus')
|
||||
?.classList?.add?.('overflow-hidden')
|
||||
} else {
|
||||
document
|
||||
.querySelector('#__docusaurus')
|
||||
?.classList?.remove?.('overflow-hidden')
|
||||
}
|
||||
}, [mobileSidebar.shown])
|
||||
|
||||
return (
|
||||
<>
|
||||
<Navbar {...props} />
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
import React from 'react'
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
|
||||
import { useAlternatePageUtils } from '@docusaurus/theme-common/internal'
|
||||
import { translate } from '@docusaurus/Translate'
|
||||
import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem'
|
||||
import IconLanguage from '@theme/Icon/Language'
|
||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext'
|
||||
import type { LinkLikeNavbarItemProps } from '@theme/NavbarItem'
|
||||
import DropdownNavbarItem from '@theme/NavbarItem/DropdownNavbarItem'
|
||||
import type { Props } from '@theme/NavbarItem/LocaleDropdownNavbarItem'
|
||||
|
||||
import styles from './styles.module.css'
|
||||
import React from 'react'
|
||||
|
||||
export default function LocaleDropdownNavbarItem({
|
||||
mobile,
|
||||
@@ -59,7 +56,7 @@ export default function LocaleDropdownNavbarItem({
|
||||
<DropdownNavbarItem
|
||||
{...props}
|
||||
mobile={mobile}
|
||||
label={<>{dropdownLabel}</>}
|
||||
label={<>{currentLocale.toUpperCase()}</>}
|
||||
items={items}
|
||||
/>
|
||||
)
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
@use '@site/src/css/utils';
|
||||
|
||||
.root {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@include utils.responsive('lg', 'down') {
|
||||
.root {
|
||||
position: unset;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// TODO: use ifm variables
|
||||
|
||||
@use '@site/src/css/utils';
|
||||
|
||||
.root {
|
||||
width: auto;
|
||||
position: relative;
|
||||
@@ -72,3 +74,27 @@
|
||||
background-color: var(--ifm-background-color);
|
||||
}
|
||||
}
|
||||
|
||||
@include utils.responsive('lg', 'down') {
|
||||
.shortcuts {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.root {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.root input {
|
||||
width: 100%;
|
||||
border-radius: 12px;
|
||||
background: #eeeef0;
|
||||
}
|
||||
|
||||
.root.expanded input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.label {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useOS } from '@site/src/lib/useOS'
|
||||
import { useWindowEventListener } from '@site/src/lib/useWindowEventListener'
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
import {} from 'react-use'
|
||||
import { useMedia } from 'react-use'
|
||||
import styles from './SearchInput.module.scss'
|
||||
|
||||
export type SearchInputProps = Omit<
|
||||
@@ -30,6 +30,7 @@ export const SearchInput: React.FC<SearchInputProps> = ({
|
||||
...props
|
||||
}) => {
|
||||
const os = useOS()
|
||||
const isMobile = useMedia('(max-width: 996px)')
|
||||
|
||||
const expanded = active || value?.length > 0
|
||||
|
||||
@@ -67,7 +68,7 @@ export const SearchInput: React.FC<SearchInputProps> = ({
|
||||
ref={inputRef}
|
||||
onFocus={onFocus}
|
||||
{...inputProps}
|
||||
placeholder={expanded ? inputProps.placeholder : ''}
|
||||
placeholder={expanded || isMobile ? inputProps.placeholder : ''}
|
||||
/>
|
||||
<div className={styles.shortcuts}>
|
||||
{active ? (
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
@use '@site/src/css/utils';
|
||||
|
||||
.root {
|
||||
background-color: var(--ifm-background-surface-color);
|
||||
border-radius: 1rem;
|
||||
@@ -19,3 +21,11 @@
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
@include utils.responsive('lg', 'down') {
|
||||
.root {
|
||||
width: 100%;
|
||||
margin-top: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user