import React, { createElement, isValidElement } from 'react'; import styled from 'styled-components'; import { useLocation } from 'react-router-dom'; import type { JSX, PropsWithChildren, ReactElement, ReactNode } from 'react'; import { Image as MarkdocImage } from '@redocly/theme/markdoc/components/Image/Image'; import { concatClassNames } from '@redocly/theme/core/utils'; import { LinkIcon } from '@redocly/theme/icons/LinkIcon/LinkIcon'; import { PageActions } from '@redocly/theme/components/PageActions/PageActions'; import { useThemeHooks } from '@redocly/theme/core/hooks'; import { ANCHOR_CLASS, HEADING_ANCHOR_CLASS, HEADING_CLASS_PREFIX, MARKDOWN_CLASS_NAME, } from '@redocly/theme/core/constants'; function renderWithSpanWrapper(children: ReactNode): ReactNode { const childrenArray = React.Children.toArray(children); if (childrenArray.length === 0) return null; const first = childrenArray[0]; const last = childrenArray[childrenArray.length - 1]; // Check if an element is an or the Markdoc Image component const isImage = (node: ReactNode): node is ReactElement => { if (!isValidElement(node)) return false; if (typeof node.type === 'string') return node.type === 'img'; return node.type === MarkdocImage; }; const middleChildren = childrenArray.slice( isImage(first) ? 1 : 0, isImage(last) ? childrenArray.length - 1 : childrenArray.length, ); return ( <> {isImage(first) && first} {middleChildren.length > 0 && {middleChildren}} {isImage(last) && last} ); } export function Heading({ level, id, children, 'data-source': dataSource, 'data-hash': dataHash, className, __idx, }: PropsWithChildren<{ level: number; id: string; 'data-source'?: string; 'data-hash'?: string; className?: string; __idx?: number; }>): JSX.Element { const { pathname } = useLocation(); const { usePageProps } = useThemeHooks(); const pageProps = usePageProps(); const isMarkdownPage = pageProps?.metadata?.type === 'markdown' || pageProps?.metadata?.type === 'asciidoc'; const headingTextId = `${id}-heading-text`; const linkEl = ( ); const headingText = createElement( `h${level}`, { id: headingTextId }, renderWithSpanWrapper(children), ); return (
{linkEl} {headingText} {isMarkdownPage && __idx === 0 ? : null}
); } const HeadingContentWrapper = styled.div` display: flex; gap: var(--spacing-xs); & > .${ANCHOR_CLASS} { display: flex; align-items: center; height: 1lh; } &:has([data-component-name='PageActions/PageActions']:hover) { && .${ANCHOR_CLASS} svg { visibility: hidden; } } `; const StyledHeadingText = styled.span` margin: auto 0; && > img:only-child { display: inline-block; } `;