diff --git a/.eslintrc.js b/.eslintrc.js index 6c548184c3..8cc46d2814 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -173,6 +173,16 @@ module.exports = { ], }, overrides: [ + { + files: [ + 'packages/docusaurus-theme-*/src/theme/**/*.js', + 'packages/docusaurus-theme-*/src/theme/**/*.ts', + 'packages/docusaurus-theme-*/src/theme/**/*.tsx', + ], + rules: { + 'import/no-named-export': ERROR, + }, + }, { files: [ 'packages/create-docusaurus/templates/**/*.js', diff --git a/packages/docusaurus-theme-classic/src/theme-classic.d.ts b/packages/docusaurus-theme-classic/src/theme-classic.d.ts index c7ea3e9de4..933533dfdc 100644 --- a/packages/docusaurus-theme-classic/src/theme-classic.d.ts +++ b/packages/docusaurus-theme-classic/src/theme-classic.d.ts @@ -149,23 +149,33 @@ declare module '@theme/DocSidebar' { declare module '@theme/DocSidebarItem' { import type {PropSidebarItem} from '@docusaurus/plugin-content-docs'; - type DocSidebarPropsBase = { + export type DocSidebarPropsBase = { readonly activePath: string; readonly onItemClick?: (item: PropSidebarItem) => void; readonly level: number; readonly tabIndex?: number; }; - export interface Props extends DocSidebarPropsBase { + export interface Props { + readonly activePath: string; + readonly onItemClick?: (item: PropSidebarItem) => void; + readonly level: number; + readonly tabIndex?: number; readonly item: PropSidebarItem; } - const DocSidebarItem: (props: Props) => JSX.Element; - export default DocSidebarItem; - export type DocSidebarItemsProps = DocSidebarPropsBase & { + export default function DocSidebarItem(props: Props): JSX.Element; +} + +declare module '@theme/DocSidebarItems' { + import type {Props as DocSidebarItemProps} from '@theme/DocSidebarItem'; + import type {PropSidebarItem} from '@docusaurus/plugin-content-docs'; + + export type Props = Omit & { readonly items: readonly PropSidebarItem[]; }; - export const DocSidebarItems: (props: DocSidebarItemsProps) => JSX.Element; + + export default function DocSidebarItems(props: Props): JSX.Element; } declare module '@theme/DocVersionSuggestions' { @@ -196,12 +206,13 @@ declare module '@theme/Footer' { declare module '@theme/Heading' { import type {ComponentProps} from 'react'; - export type HeadingType = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; - export interface Props extends ComponentProps {} + type HeadingType = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; - const Heading: (Tag: HeadingType) => (props: Props) => JSX.Element; - export default Heading; - export const MainHeading: (props: Props) => JSX.Element; + export interface Props extends ComponentProps { + as: HeadingType; + } + + export default function Heading(props: Props): JSX.Element; } declare module '@theme/hooks/useHideableNavbar' { @@ -372,18 +383,9 @@ declare module '@theme/Navbar' { } declare module '@theme/NavbarItem/DefaultNavbarItem' { - import type {ReactNode} from 'react'; - import type {LinkProps} from '@docusaurus/Link'; + import type {Props as NavbarNavLinkProps} from '@theme/NavbarItem/NavbarNavLink'; - export type NavLinkProps = LinkProps & { - readonly activeBasePath?: string; - readonly activeBaseRegex?: string; - readonly exact?: boolean; - readonly label?: ReactNode; - readonly prependBaseUrlToHref?: string; - }; - - export type DesktopOrMobileNavBarItemProps = NavLinkProps & { + export type DesktopOrMobileNavBarItemProps = NavbarNavLinkProps & { readonly isDropdownItem?: boolean; readonly className?: string; readonly position?: 'left' | 'right'; @@ -393,17 +395,30 @@ declare module '@theme/NavbarItem/DefaultNavbarItem' { readonly mobile?: boolean; } - export const NavLink: (props: NavLinkProps) => JSX.Element; + export default function DefaultNavbarItem(props: Props): JSX.Element; +} - const DefaultNavbarItem: (props: Props) => JSX.Element; - export default DefaultNavbarItem; +declare module '@theme/NavbarItem/NavbarNavLink' { + import type {ReactNode} from 'react'; + import type {LinkProps} from '@docusaurus/Link'; + + export type Props = LinkProps & { + readonly activeBasePath?: string; + readonly activeBaseRegex?: string; + readonly exact?: boolean; + readonly label?: ReactNode; + readonly prependBaseUrlToHref?: string; + }; + + export default function NavbarNavLink(props: Props): JSX.Element; } declare module '@theme/NavbarItem/DropdownNavbarItem' { - import type {NavLinkProps} from '@theme/NavbarItem/DefaultNavbarItem'; + import type {Props as NavbarNavLinkProps} from '@theme/NavbarItem/NavbarNavLink'; + import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem'; - export type DesktopOrMobileNavBarItemProps = NavLinkProps & { + export type DesktopOrMobileNavBarItemProps = NavbarNavLinkProps & { readonly position?: 'left' | 'right'; readonly items: readonly LinkLikeNavbarItemProps[]; readonly className?: string; @@ -526,6 +541,10 @@ declare module '@theme/NavbarItem' { export default NavbarItem; } +declare module '@theme/NavbarItem/utils' { + export function getInfimaActiveClassName(mobile?: boolean): string; +} + declare module '@theme/PaginatorNavLink' { import type {ReactNode} from 'react'; import type {PropNavigationLink} from '@docusaurus/plugin-content-docs'; diff --git a/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx index 757713df0e..1214a86aab 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocCategoryGeneratedIndexPage/index.tsx @@ -13,7 +13,7 @@ import DocPaginator from '@theme/DocPaginator'; import Seo from '@theme/Seo'; import DocVersionBanner from '@theme/DocVersionBanner'; import DocVersionBadge from '@theme/DocVersionBadge'; -import {MainHeading} from '@theme/Heading'; +import Heading from '@theme/Heading'; import useBaseUrl from '@docusaurus/useBaseUrl'; import styles from './styles.module.css'; @@ -35,9 +35,9 @@ export default function DocCategoryGeneratedIndexPage({
- + {categoryGeneratedIndex.title} - + {categoryGeneratedIndex.description && (

{categoryGeneratedIndex.description}

)} diff --git a/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx index 1d30c2d3f6..f5b1f9ff23 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocItem/index.tsx @@ -16,7 +16,7 @@ import type {Props} from '@theme/DocItem'; import DocItemFooter from '@theme/DocItemFooter'; import TOC from '@theme/TOC'; import TOCCollapsible from '@theme/TOCCollapsible'; -import {MainHeading} from '@theme/Heading'; +import Heading from '@theme/Heading'; import styles from './styles.module.css'; import {ThemeClassNames} from '@docusaurus/theme-common'; @@ -80,7 +80,11 @@ export default function DocItem(props: Props): JSX.Element { To make both cases consistent, the added title is added under the same div.markdown block See https://github.com/facebook/docusaurus/pull/4882#issuecomment-853021120 */} - {shouldAddTitle && {title}} + {shouldAddTitle && ( +
+ {title} +
+ )} diff --git a/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx index 65fa6a2609..e5282350c1 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocSidebar/index.tsx @@ -19,7 +19,7 @@ import useWindowSize from '@theme/hooks/useWindowSize'; import Logo from '@theme/Logo'; import IconArrow from '@theme/IconArrow'; import {translate} from '@docusaurus/Translate'; -import {DocSidebarItems} from '@theme/DocSidebarItem'; +import DocSidebarItems from '@theme/DocSidebarItems'; import type {Props} from '@theme/DocSidebar'; import styles from './styles.module.css'; diff --git a/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/index.tsx index 2718bfebf5..f94d8077e8 100644 --- a/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/DocSidebarItem/index.tsx @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import React, {useEffect, memo, useMemo} from 'react'; +import React, {useEffect, useMemo} from 'react'; import clsx from 'clsx'; import { isActiveSidebarItem, @@ -20,7 +20,8 @@ import isInternalUrl from '@docusaurus/isInternalUrl'; import {translate} from '@docusaurus/Translate'; import IconExternalLink from '@theme/IconExternalLink'; -import type {Props, DocSidebarItemsProps} from '@theme/DocSidebarItem'; +import DocSidebarItems from '@theme/DocSidebarItems'; +import type {Props} from '@theme/DocSidebarItem'; import type { PropSidebarItemCategory, PropSidebarItemLink, @@ -29,23 +30,6 @@ import type { import styles from './styles.module.css'; import useIsBrowser from '@docusaurus/useIsBrowser'; -// Optimize sidebar at each "level" -// TODO this item should probably not receive the "activePath" props -// TODO this triggers whole sidebar re-renders on navigation -export const DocSidebarItems = memo( - ({items, ...props}: DocSidebarItemsProps): JSX.Element => ( - <> - {items.map((item, index) => ( - - ))} - - ), -); - export default function DocSidebarItem({ item, ...props diff --git a/packages/docusaurus-theme-classic/src/theme/DocSidebarItems/index.tsx b/packages/docusaurus-theme-classic/src/theme/DocSidebarItems/index.tsx new file mode 100644 index 0000000000..baec593128 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/DocSidebarItems/index.tsx @@ -0,0 +1,31 @@ +/** + * 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, {memo} from 'react'; + +import DocSidebarItem from '@theme/DocSidebarItem'; + +import type {Props} from '@theme/DocSidebarItems'; + +// TODO this item should probably not receive the "activePath" props +// TODO this triggers whole sidebar re-renders on navigation +function DocSidebarItems({items, ...props}: Props): JSX.Element { + return ( + <> + {items.map((item, index) => ( + + ))} + + ); +} + +// Optimize sidebar at each "level" +export default memo(DocSidebarItems); diff --git a/packages/docusaurus-theme-classic/src/theme/DocSidebarItems/styles.module.css b/packages/docusaurus-theme-classic/src/theme/DocSidebarItems/styles.module.css new file mode 100644 index 0000000000..2fcd42e59f --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/DocSidebarItems/styles.module.css @@ -0,0 +1,19 @@ +/** + * 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. + */ + +@media (min-width: 997px) { + .menuLinkText { + cursor: initial; + } + .menuLinkText:hover { + background: none; + } + + .menuLinkText.hasHref { + cursor: pointer; + } +} diff --git a/packages/docusaurus-theme-classic/src/theme/Heading/index.tsx b/packages/docusaurus-theme-classic/src/theme/Heading/index.tsx index c86b7c4243..7f7a11095d 100644 --- a/packages/docusaurus-theme-classic/src/theme/Heading/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/Heading/index.tsx @@ -7,62 +7,55 @@ import React from 'react'; import clsx from 'clsx'; -import type {HeadingType, Props} from '@theme/Heading'; +import type {Props} from '@theme/Heading'; import {translate} from '@docusaurus/Translate'; import {useThemeConfig} from '@docusaurus/theme-common'; import './styles.css'; import styles from './styles.module.css'; -type HeadingComponent = (props: Props) => JSX.Element; +function AnchorHeading({as: As, id, ...props}: Props) { + const { + navbar: {hideOnScroll}, + } = useThemeConfig(); -// eslint-disable-next-line react/function-component-definition -export const MainHeading: HeadingComponent = ({...props}) => ( -
-

; + } + + return ( + + className={clsx('anchor', { + [styles.anchorWithHideOnScrollNavbar]: hideOnScroll, + [styles.anchorWithStickyNavbar]: !hideOnScroll, + })} + id={id}> {props.children} -

-
-); - -const createAnchorHeading = - (Tag: HeadingType) => - ({id, ...props}: Props) => { - const { - navbar: {hideOnScroll}, - } = useThemeConfig(); - - if (!id) { - return ; - } + + ​ + + + ); +} +export default function Heading({as, ...props}: Props) { + if (as === 'h1') { return ( - + id={undefined} // h1 headings do not need an id because they don't appear in the TOC + > {props.children} - - ​ - - + ); - }; - -const Heading = (headingType: HeadingType): ((props: Props) => JSX.Element) => - headingType === 'h1' ? MainHeading : createAnchorHeading(headingType); - -export default Heading; + } + return ; +} diff --git a/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx b/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx index cc7bc915aa..17c34a9b06 100644 --- a/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/MDXComponents/index.tsx @@ -67,12 +67,12 @@ const MDXComponents: MDXComponentsObject = { ); }, - h1: Heading('h1'), - h2: Heading('h2'), - h3: Heading('h3'), - h4: Heading('h4'), - h5: Heading('h5'), - h6: Heading('h6'), + h1: (props) => , + h2: (props) => , + h3: (props) => , + h4: (props) => , + h5: (props) => , + h6: (props) => , }; export default MDXComponents; diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DefaultNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DefaultNavbarItem.tsx index 5ae65695c3..cb4207afc6 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DefaultNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DefaultNavbarItem.tsx @@ -7,71 +7,15 @@ import React from 'react'; import clsx from 'clsx'; -import Link from '@docusaurus/Link'; -import useBaseUrl from '@docusaurus/useBaseUrl'; + +import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink'; + import type { - NavLinkProps, DesktopOrMobileNavBarItemProps, Props, } from '@theme/NavbarItem/DefaultNavbarItem'; -import IconExternalLink from '@theme/IconExternalLink'; -import isInternalUrl from '@docusaurus/isInternalUrl'; -import {isRegexpStringMatch} from '@docusaurus/theme-common'; -import {getInfimaActiveClassName} from './index'; -const dropdownLinkActiveClass = 'dropdown__link--active'; - -export function NavLink({ - activeBasePath, - activeBaseRegex, - to, - href, - label, - activeClassName = '', - prependBaseUrlToHref, - ...props -}: NavLinkProps): JSX.Element { - // TODO all this seems hacky - // {to: 'version'} should probably be forbidden, in favor of {to: '/version'} - const toUrl = useBaseUrl(to); - const activeBaseUrl = useBaseUrl(activeBasePath); - const normalizedHref = useBaseUrl(href, {forcePrependBaseUrl: true}); - const isExternalLink = label && href && !isInternalUrl(href); - const isDropdownLink = activeClassName === dropdownLinkActiveClass; - - return ( - - activeBaseRegex - ? isRegexpStringMatch(activeBaseRegex, location.pathname) - : location.pathname.startsWith(activeBaseUrl), - } - : null), - })} - {...props}> - {isExternalLink ? ( - - {label} - - - ) : ( - label - )} - - ); -} +import {getInfimaActiveClassName} from '@theme/NavbarItem/utils'; function DefaultNavbarItemDesktop({ className, @@ -79,7 +23,7 @@ function DefaultNavbarItemDesktop({ ...props }: DesktopOrMobileNavBarItemProps) { const element = ( - - + ); } diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocNavbarItem.tsx index 895efb3b77..87083952a1 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocNavbarItem.tsx @@ -9,7 +9,7 @@ import React from 'react'; import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem'; import {useLatestVersion, useActiveDocContext} from '@theme/hooks/useDocs'; import clsx from 'clsx'; -import {getInfimaActiveClassName} from './index'; +import {getInfimaActiveClassName} from '@theme/NavbarItem/utils'; import type {Props} from '@theme/NavbarItem/DocNavbarItem'; import {useDocsPreferredVersion, uniq} from '@docusaurus/theme-common'; import type {GlobalDataVersion} from '@docusaurus/plugin-content-docs'; diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocSidebarNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocSidebarNavbarItem.tsx index f1b76b216e..9236cd790f 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocSidebarNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DocSidebarNavbarItem.tsx @@ -9,9 +9,10 @@ import React from 'react'; import DefaultNavbarItem from '@theme/NavbarItem/DefaultNavbarItem'; import {useLatestVersion, useActiveDocContext} from '@theme/hooks/useDocs'; import clsx from 'clsx'; -import {getInfimaActiveClassName} from './index'; -import type {Props} from '@theme/NavbarItem/DocSidebarNavbarItem'; +import {getInfimaActiveClassName} from '@theme/NavbarItem/utils'; import {useDocsPreferredVersion, uniq} from '@docusaurus/theme-common'; + +import type {Props} from '@theme/NavbarItem/DocSidebarNavbarItem'; import type { GlobalDataVersion, GlobalDataSidebar, diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx index 770f86bd37..cd04e822f9 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx @@ -20,7 +20,7 @@ import type { } from '@theme/NavbarItem/DropdownNavbarItem'; import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem'; -import {NavLink} from '@theme/NavbarItem/DefaultNavbarItem'; +import NavbarNavLink from '@theme/NavbarItem/NavbarNavLink'; import NavbarItem from '@theme/NavbarItem'; const dropdownLinkActiveClass = 'dropdown__link--active'; @@ -84,7 +84,7 @@ function DropdownNavbarItemDesktop({ 'dropdown--right': position === 'right', 'dropdown--show': showDropdown, })}> - {props.children ?? props.label} - +
    {items.map((childItemProps, i) => ( - {props.children ?? props.label} - + {items.map((childItemProps, i) => ( + activeBaseRegex + ? isRegexpStringMatch(activeBaseRegex, location.pathname) + : location.pathname.startsWith(activeBaseUrl), + } + : null), + })} + {...props}> + {isExternalLink ? ( + + {label} + + + ) : ( + label + )} + + ); +} diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/index.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/index.tsx index c74a0f39a6..3713c71577 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/index.tsx @@ -58,9 +58,6 @@ function getComponentType( return type as NavbarItemComponentType; } -export const getInfimaActiveClassName = (mobile?: boolean): string => - mobile ? 'menu__link--active' : 'navbar__link--active'; - export default function NavbarItem({type, ...props}: Props): JSX.Element { const componentType = getComponentType( type, diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/utils.ts b/packages/docusaurus-theme-classic/src/theme/NavbarItem/utils.ts new file mode 100644 index 0000000000..b3f9cce114 --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/utils.ts @@ -0,0 +1,10 @@ +/** + * 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. + */ + +// eslint-disable-next-line import/no-named-export +export const getInfimaActiveClassName = (mobile?: boolean): string => + mobile ? 'menu__link--active' : 'navbar__link--active'; diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts b/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts index a89ebfe8f3..bc5e2cbd3c 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useDocs.ts @@ -8,4 +8,5 @@ // Re-expose useDocs // Ensure it's always statically available even if user is not using the docs plugin // Problem reported for the blog-only mode: https://github.com/facebook/docusaurus/issues/3360 +// eslint-disable-next-line import/no-named-export export * from '@docusaurus/plugin-content-docs/lib/theme/hooks/useDocs';