mirror of
https://github.com/wavetermdev/docusaurus-og.git
synced 2026-08-05 13:46:35 -07:00
@@ -59,7 +59,7 @@ const config = {
|
||||
({
|
||||
docs: { sidebar: { hideable: true } },
|
||||
navbar: {
|
||||
title: 'My Site',
|
||||
title: '',
|
||||
logo: {
|
||||
alt: 'My Site Logo',
|
||||
src: 'img/logo.svg',
|
||||
@@ -69,12 +69,10 @@ const config = {
|
||||
type: 'doc',
|
||||
docId: 'intro',
|
||||
position: 'left',
|
||||
label: 'Tutorial',
|
||||
label: 'Docs',
|
||||
},
|
||||
{ to: '/blog', label: 'Blog', position: 'left' },
|
||||
{
|
||||
href: 'https://github.com/facebook/docusaurus',
|
||||
label: 'GitHub',
|
||||
type: 'localeDropdown',
|
||||
position: 'right',
|
||||
},
|
||||
],
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
"@docusaurus/preset-classic": "2.1.0",
|
||||
"@mdx-js/react": "^1.6.22",
|
||||
"clsx": "^1.2.1",
|
||||
"copy-to-clipboard": "^3.3.2",
|
||||
"prism-react-renderer": "^1.3.5",
|
||||
"react": "^17.0.2",
|
||||
"react-dom": "^17.0.2"
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
.root {
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 50%;
|
||||
transition: background var(--ifm-transition-fast);
|
||||
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
}
|
||||
|
||||
.root:hover {
|
||||
background: var(--ifm-color-emphasis-200);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
import styles from './IconButton.module.scss'
|
||||
|
||||
export const IconButton: React.FC<
|
||||
React.DetailedHTMLProps<
|
||||
React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||
HTMLButtonElement
|
||||
> & {}
|
||||
> = ({ children, className, ...props }) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={clsx('clean-btn', styles.root, className)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export * from './IconButton'
|
||||
@@ -6,6 +6,7 @@ export namespace GlobalStore {
|
||||
}
|
||||
|
||||
export type Actions = {
|
||||
toggleSidebar: () => void
|
||||
setHiddenSidebar: (payload: any) => void
|
||||
}
|
||||
|
||||
@@ -14,6 +15,9 @@ export namespace GlobalStore {
|
||||
|
||||
export const globalStore = new GlobalStore.Store(() => {
|
||||
return {
|
||||
toggleSidebar: (payload, state, setState, dispatch) => {
|
||||
setState((state) => void (state.hiddenSidebar = !state.hiddenSidebar))
|
||||
},
|
||||
setHiddenSidebar: (payload, state, setState, dispatch) => {
|
||||
setState((state) => {
|
||||
state.hiddenSidebar =
|
||||
|
||||
@@ -351,7 +351,11 @@
|
||||
--ifm-hero-text-color: var(--ifm-color-black);
|
||||
}
|
||||
|
||||
/* sidebar */
|
||||
:root {
|
||||
--doc-sidebar-width: 16.66vw !important;
|
||||
--doc-sidebar-max-width: 320px;
|
||||
|
||||
--ifm-navbar-item-padding-vertical: 8px;
|
||||
--ifm-navbar-item-padding-horizontal: 12px;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { translate } from '@docusaurus/Translate'
|
||||
import useIsBrowser from '@docusaurus/useIsBrowser'
|
||||
import { IconButton } from '@site/src/components/IconButton'
|
||||
import type { Props } from '@theme/ColorModeToggle'
|
||||
import IconDarkMode from '@theme/Icon/DarkMode'
|
||||
import IconLightMode from '@theme/Icon/LightMode'
|
||||
import clsx from 'clsx'
|
||||
import React from 'react'
|
||||
import styles from './styles.module.css'
|
||||
|
||||
function ColorModeToggle({ className, value, onChange }: Props): JSX.Element {
|
||||
const isBrowser = useIsBrowser()
|
||||
|
||||
const title = translate(
|
||||
{
|
||||
message: 'Switch between dark and light mode (currently {mode})',
|
||||
id: 'theme.colorToggle.ariaLabel',
|
||||
description: 'The ARIA label for the navbar color mode toggle',
|
||||
},
|
||||
{
|
||||
mode:
|
||||
value === 'dark'
|
||||
? translate({
|
||||
message: 'dark mode',
|
||||
id: 'theme.colorToggle.ariaLabel.mode.dark',
|
||||
description: 'The name for the dark color mode',
|
||||
})
|
||||
: translate({
|
||||
message: 'light mode',
|
||||
id: 'theme.colorToggle.ariaLabel.mode.light',
|
||||
description: 'The name for the light color mode',
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={clsx(styles.toggle, className)}>
|
||||
<IconButton
|
||||
title={title}
|
||||
aria-label={title}
|
||||
disabled={!isBrowser}
|
||||
className={clsx(!isBrowser && styles.toggleButtonDisabled)}
|
||||
onClick={() => onChange(value === 'dark' ? 'light' : 'dark')}
|
||||
>
|
||||
<IconLightMode
|
||||
className={clsx(styles.toggleIcon, styles.lightToggleIcon)}
|
||||
/>
|
||||
<IconDarkMode
|
||||
className={clsx(styles.toggleIcon, styles.darkToggleIcon)}
|
||||
/>
|
||||
</IconButton>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(ColorModeToggle)
|
||||
@@ -0,0 +1,8 @@
|
||||
[data-theme='light'] .darkToggleIcon,
|
||||
[data-theme='dark'] .lightToggleIcon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.toggleButtonDisabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import React from 'react'
|
||||
import type { Props } from '@theme/Icon/LightMode'
|
||||
import Icon from '@site/static/icons/day.svg'
|
||||
|
||||
export default function IconLightMode(props: Props): JSX.Element {
|
||||
return <Icon {...props} />
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { IconButton } from '@site/src/components/IconButton'
|
||||
import React from 'react'
|
||||
import Icon from '@site/static/icons/share.svg'
|
||||
import styles from './styles.module.scss'
|
||||
import copy2clipboard from 'copy-to-clipboard'
|
||||
|
||||
export const ShareButton: React.FC<{}> = ({}) => {
|
||||
return (
|
||||
<IconButton
|
||||
title="share this page"
|
||||
aria-label="share this page"
|
||||
onClick={() => {
|
||||
if (typeof navigator?.share === 'function') navigator.share()
|
||||
else copy2clipboard(window.location.href)
|
||||
}}
|
||||
className={styles.shareButton}
|
||||
>
|
||||
<Icon />
|
||||
</IconButton>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import { useThemeConfig } from '@docusaurus/theme-common'
|
||||
import { useNavbarMobileSidebar } from '@docusaurus/theme-common/internal'
|
||||
import {
|
||||
globalStore,
|
||||
selectHiddenSidebar,
|
||||
} from '@site/src/containers/GlobalStore'
|
||||
import ChevronIcon from '@site/static/icons/chevron-left.svg'
|
||||
import HamburgerIcon from '@site/static/icons/hamburger-menu.svg'
|
||||
import NavbarColorModeToggle from '@theme/Navbar/ColorModeToggle'
|
||||
import NavbarLogo from '@theme/Navbar/Logo'
|
||||
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 { ShareButton } from './ShareButton'
|
||||
import styles from './styles.module.scss'
|
||||
|
||||
function useNavbarItems() {
|
||||
// TODO temporary casting until ThemeConfig type is improved
|
||||
return useThemeConfig().navbar.items as NavbarItemConfig[]
|
||||
}
|
||||
|
||||
function NavbarItems({ items }: { items: NavbarItemConfig[] }): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
{items.map((item, i) => (
|
||||
<NavbarItem {...item} key={i} />
|
||||
))}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default function NavbarContent(): JSX.Element {
|
||||
const mobileSidebar = useNavbarMobileSidebar()
|
||||
|
||||
const items = useNavbarItems()
|
||||
|
||||
const dispatch = globalStore.useDispatch()
|
||||
const hiddenDesktopSidebar = globalStore.useSelector(selectHiddenSidebar)
|
||||
|
||||
const localeDropdown = items.find((item) => item.type === 'localeDropdown')
|
||||
const links = items.filter(
|
||||
(item) => item.type === 'doc' || !!(item as any).to,
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={clsx('row', styles.root)}>
|
||||
<div className="col col--2">
|
||||
<button
|
||||
onClick={dispatch.toggleSidebar}
|
||||
className={clsx(
|
||||
styles.sidebarButton,
|
||||
hiddenDesktopSidebar && styles.expand,
|
||||
)}
|
||||
>
|
||||
{!hiddenDesktopSidebar ? <ChevronIcon /> : <HamburgerIcon />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className={clsx('col', styles.headerMiddle)}>
|
||||
<div
|
||||
className={clsx(
|
||||
styles.menu,
|
||||
styles.leftContainer,
|
||||
hiddenDesktopSidebar && styles.shifted,
|
||||
)}
|
||||
>
|
||||
<div className={styles.navbarLogoWrapper}>
|
||||
<NavbarLogo />
|
||||
</div>
|
||||
<div>
|
||||
<NavbarSearch>
|
||||
<SearchBar />
|
||||
</NavbarSearch>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<nav className={styles.menu}>
|
||||
<NavbarItems items={links} />
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={clsx('col col--5', styles.headerRight)}>
|
||||
<ShareButton />
|
||||
<NavbarColorModeToggle />
|
||||
{localeDropdown && (
|
||||
<>
|
||||
<div className={styles.divider} />
|
||||
<NavbarItem {...localeDropdown} />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
@use '@site/src/css/utils';
|
||||
|
||||
.root {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
|
||||
& > div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
& > div:first-child {
|
||||
max-width: var(--doc-sidebar-max-width);
|
||||
}
|
||||
|
||||
& > .headerMiddle {
|
||||
justify-content: space-between;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.leftContainer {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
transition: 0.2s;
|
||||
transform: translateX(0);
|
||||
|
||||
&.shifted {
|
||||
transform: translateX(
|
||||
calc(-1 * min(var(--doc-sidebar-max-width), 16.66vw) + 64px)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
.headerRight {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.navbarLogoWrapper {
|
||||
& > * {
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.menu {
|
||||
@extend %menu-common;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.sidebarButton {
|
||||
border: none;
|
||||
background: none;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
@extend %menu-common;
|
||||
|
||||
width: 52px;
|
||||
height: 46px;
|
||||
|
||||
&.expand {
|
||||
svg,
|
||||
svg * {
|
||||
fill: currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.expand) {
|
||||
svg,
|
||||
svg * {
|
||||
stroke: currentColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
%menu-common {
|
||||
padding: 6px;
|
||||
background-color: var(--ifm-background-surface-color);
|
||||
border-radius: 1rem;
|
||||
}
|
||||
|
||||
.headerRight {
|
||||
& > *:not(:last-child) {
|
||||
margin-right: 34px;
|
||||
}
|
||||
}
|
||||
|
||||
.divider {
|
||||
height: 11px;
|
||||
width: 1px;
|
||||
|
||||
background-color: #d9d9d9; // TODO: use infima variables
|
||||
}
|
||||
|
||||
.shareButton {
|
||||
svg,
|
||||
svg * {
|
||||
fill: currentColor;
|
||||
stroke: currentColor;
|
||||
}
|
||||
}
|
||||
|
||||
@include utils.responsive('lg', 'down') {
|
||||
.root {
|
||||
padding: 0 var(--ifm-spacing-horizontal);
|
||||
}
|
||||
|
||||
.root > * {
|
||||
width: auto;
|
||||
flex: 0 0 auto !important;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.headerLeft {
|
||||
}
|
||||
|
||||
.headerMiddle {
|
||||
margin-left: 20px;
|
||||
|
||||
.leftContainer.shifted {
|
||||
transform: none;
|
||||
}
|
||||
|
||||
nav {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.headerRight {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Hide color mode toggle in small viewports
|
||||
*/
|
||||
@media (max-width: 996px) {
|
||||
.colorModeToggle {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
.navbar {
|
||||
--ifm-navbar-height: 3.875rem;
|
||||
--ifm-navbar-background-color: none;
|
||||
--ifm-navbar-shadow: none;
|
||||
--ifm-navbar-padding-horizontal: 0px;
|
||||
--ifm-navbar-padding-vertical: 8px;
|
||||
}
|
||||
|
||||
:root {
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import React from 'react'
|
||||
import Navbar from '@theme-original/Navbar'
|
||||
import type NavbarType from '@theme/Navbar'
|
||||
import type { WrapperProps } from '@docusaurus/types'
|
||||
import './index.scss'
|
||||
|
||||
type Props = WrapperProps<typeof NavbarType>
|
||||
|
||||
export default function NavbarWrapper(props: Props): JSX.Element {
|
||||
return (
|
||||
<>
|
||||
<Navbar {...props} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
.navbar__item.dropdown {
|
||||
background-color: #eeeef1;
|
||||
border-radius: 0.5rem;
|
||||
|
||||
& > .navbar__link {
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& + svg {
|
||||
margin-left: 0.75rem;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-top: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
.navbar__item.dropdown {
|
||||
background-color: var(--ifm-background-color);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
import React, { useState, useRef, useEffect } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import {
|
||||
isRegexpStringMatch,
|
||||
useCollapsible,
|
||||
Collapsible,
|
||||
} from '@docusaurus/theme-common'
|
||||
import { isSamePath, useLocalPathname } from '@docusaurus/theme-common/internal'
|
||||
import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink'
|
||||
import NavbarItem, { LinkLikeNavbarItemProps } from '@theme/NavbarItem'
|
||||
import type {
|
||||
DesktopOrMobileNavBarItemProps,
|
||||
Props,
|
||||
} from '@theme/NavbarItem/DropdownNavbarItem'
|
||||
import './DropdownNavbarItem.scss'
|
||||
import DropdownIcon from '@site/static/icons/dropdown.svg'
|
||||
|
||||
function isItemActive(
|
||||
item: LinkLikeNavbarItemProps,
|
||||
localPathname: string,
|
||||
): boolean {
|
||||
if (isSamePath(item.to, localPathname)) {
|
||||
return true
|
||||
}
|
||||
if (isRegexpStringMatch(item.activeBaseRegex, localPathname)) {
|
||||
return true
|
||||
}
|
||||
if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function containsActiveItems(
|
||||
items: readonly LinkLikeNavbarItemProps[],
|
||||
localPathname: string,
|
||||
): boolean {
|
||||
return items.some((item) => isItemActive(item, localPathname))
|
||||
}
|
||||
|
||||
function DropdownNavbarItemDesktop({
|
||||
items,
|
||||
position,
|
||||
className,
|
||||
onClick,
|
||||
...props
|
||||
}: DesktopOrMobileNavBarItemProps) {
|
||||
const dropdownRef = useRef<HTMLDivElement>(null)
|
||||
const [showDropdown, setShowDropdown] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
const handleClickOutside = (event: MouseEvent | TouchEvent) => {
|
||||
if (
|
||||
!dropdownRef.current ||
|
||||
dropdownRef.current.contains(event.target as Node)
|
||||
) {
|
||||
return
|
||||
}
|
||||
setShowDropdown(false)
|
||||
}
|
||||
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
document.addEventListener('touchstart', handleClickOutside)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
document.removeEventListener('touchstart', handleClickOutside)
|
||||
}
|
||||
}, [dropdownRef])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={dropdownRef}
|
||||
className={clsx('navbar__item', 'dropdown', 'dropdown--hoverable', {
|
||||
'dropdown--right': position === 'right',
|
||||
'dropdown--show': showDropdown,
|
||||
})}
|
||||
>
|
||||
<NavbarNavLink
|
||||
aria-haspopup="true"
|
||||
aria-expanded={showDropdown}
|
||||
role="button"
|
||||
href={props.to ? undefined : '#'}
|
||||
className={clsx('navbar__link', className)}
|
||||
{...props}
|
||||
onClick={props.to ? undefined : (e) => e.preventDefault()}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
e.preventDefault()
|
||||
setShowDropdown(!showDropdown)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{props.children ?? props.label}
|
||||
</NavbarNavLink>
|
||||
<DropdownIcon />
|
||||
<ul className="dropdown__menu">
|
||||
{items.map((childItemProps, i) => (
|
||||
<NavbarItem
|
||||
isDropdownItem
|
||||
onKeyDown={(e) => {
|
||||
if (i === items.length - 1 && e.key === 'Tab') {
|
||||
e.preventDefault()
|
||||
setShowDropdown(false)
|
||||
const nextNavbarItem = dropdownRef.current!.nextElementSibling
|
||||
if (nextNavbarItem) {
|
||||
const targetItem =
|
||||
nextNavbarItem instanceof HTMLAnchorElement
|
||||
? nextNavbarItem
|
||||
: // Next item is another dropdown; focus on the inner
|
||||
// anchor element instead so there's outline
|
||||
nextNavbarItem.querySelector('a')!
|
||||
targetItem.focus()
|
||||
}
|
||||
}
|
||||
}}
|
||||
activeClassName="dropdown__link--active"
|
||||
{...childItemProps}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function DropdownNavbarItemMobile({
|
||||
items,
|
||||
className,
|
||||
position, // Need to destructure position from props so that it doesn't get passed on.
|
||||
onClick,
|
||||
...props
|
||||
}: DesktopOrMobileNavBarItemProps) {
|
||||
const localPathname = useLocalPathname()
|
||||
const containsActive = containsActiveItems(items, localPathname)
|
||||
|
||||
const { collapsed, toggleCollapsed, setCollapsed } = useCollapsible({
|
||||
initialState: () => !containsActive,
|
||||
})
|
||||
|
||||
// Expand/collapse if any item active after a navigation
|
||||
useEffect(() => {
|
||||
if (containsActive) {
|
||||
setCollapsed(!containsActive)
|
||||
}
|
||||
}, [localPathname, containsActive, setCollapsed])
|
||||
|
||||
return (
|
||||
<li
|
||||
className={clsx('menu__list-item', {
|
||||
'menu__list-item--collapsed': collapsed,
|
||||
})}
|
||||
>
|
||||
<NavbarNavLink
|
||||
role="button"
|
||||
className={clsx(
|
||||
'menu__link menu__link--sublist menu__link--sublist-caret',
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
onClick={(e) => {
|
||||
e.preventDefault()
|
||||
toggleCollapsed()
|
||||
}}
|
||||
>
|
||||
{props.children ?? props.label}
|
||||
</NavbarNavLink>
|
||||
<Collapsible lazy as="ul" className="menu__list" collapsed={collapsed}>
|
||||
{items.map((childItemProps, i) => (
|
||||
<NavbarItem
|
||||
mobile
|
||||
isDropdownItem
|
||||
onClick={onClick}
|
||||
activeClassName="menu__link--active"
|
||||
{...childItemProps}
|
||||
key={i}
|
||||
/>
|
||||
))}
|
||||
</Collapsible>
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
export default function DropdownNavbarItem({
|
||||
mobile = false,
|
||||
...props
|
||||
}: Props): JSX.Element {
|
||||
const Comp = mobile ? DropdownNavbarItemMobile : DropdownNavbarItemDesktop
|
||||
return <Comp {...props} />
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
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 type { LinkLikeNavbarItemProps } from '@theme/NavbarItem'
|
||||
import type { Props } from '@theme/NavbarItem/LocaleDropdownNavbarItem'
|
||||
|
||||
import styles from './styles.module.css'
|
||||
|
||||
export default function LocaleDropdownNavbarItem({
|
||||
mobile,
|
||||
dropdownItemsBefore,
|
||||
dropdownItemsAfter,
|
||||
...props
|
||||
}: Props): JSX.Element {
|
||||
const {
|
||||
i18n: { currentLocale, locales, localeConfigs },
|
||||
} = useDocusaurusContext()
|
||||
const alternatePageUtils = useAlternatePageUtils()
|
||||
|
||||
const localeItems = locales.map((locale): LinkLikeNavbarItemProps => {
|
||||
const to = `pathname://${alternatePageUtils.createUrl({
|
||||
locale,
|
||||
fullyQualified: false,
|
||||
})}`
|
||||
return {
|
||||
label: localeConfigs[locale]!.label,
|
||||
lang: localeConfigs[locale]!.htmlLang,
|
||||
to,
|
||||
target: '_self',
|
||||
autoAddBaseUrl: false,
|
||||
className:
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
locale === currentLocale
|
||||
? // Similar idea as DefaultNavbarItem: select the right Infima active
|
||||
// class name. This cannot be substituted with isActive, because the
|
||||
// target URLs contain `pathname://` and therefore are not NavLinks!
|
||||
mobile
|
||||
? 'menu__link--active'
|
||||
: 'dropdown__link--active'
|
||||
: '',
|
||||
}
|
||||
})
|
||||
|
||||
const items = [...dropdownItemsBefore, ...localeItems, ...dropdownItemsAfter]
|
||||
|
||||
// Mobile is handled a bit differently
|
||||
const dropdownLabel = mobile
|
||||
? translate({
|
||||
message: 'Languages',
|
||||
id: 'theme.navbar.mobileLanguageDropdown.label',
|
||||
description: 'The label for the mobile language switcher dropdown',
|
||||
})
|
||||
: localeConfigs[currentLocale]!.label
|
||||
|
||||
return (
|
||||
<DropdownNavbarItem
|
||||
{...props}
|
||||
mobile={mobile}
|
||||
label={<>{dropdownLabel}</>}
|
||||
items={items}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
.iconLanguage {
|
||||
vertical-align: text-bottom;
|
||||
margin-right: 5px;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M11.7647 5.29407L7.05884 9.99995L11.7647 14.7058" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 243 B |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user