import * as React from 'react'; import styled from 'styled-components'; import type { JSX } from 'react'; import type { MdHeading } from '@redocly/theme/core/types'; import { useActiveHeading, useThemeHooks, useThemeConfig, useFullHeight, } from '@redocly/theme/core/hooks'; import { breakpoints, getDisplayedHeadings, getLeastDepth } from '@redocly/theme/core/utils'; export type TableOfContentProps = { headings?: Array | null | undefined; contentWrapper: HTMLDivElement | null; className?: string; }; export function TableOfContent(props: TableOfContentProps): JSX.Element | null { const { headings, contentWrapper, className } = props; const { useTranslate, useTelemetry } = useThemeHooks(); const { translate } = useTranslate(); const telemetry = useTelemetry(); const sidebar = React.useRef(null); useFullHeight(sidebar); const { markdown: { toc = {} } = {} } = useThemeConfig(); const displayedHeadings = getDisplayedHeadings(headings, toc.depth || 3); const leastDepth = getLeastDepth(displayedHeadings); const { heading: activeHeading, handleHeadingClick } = useActiveHeading( contentWrapper, displayedHeadings, ); if (toc?.hide) { return null; } return ( {displayedHeadings.length ? ( {translate('toc.header', toc.header || 'On this page')} ) : null} {displayedHeadings.map((heading: MdHeading | null, idx: number) => { if (!heading) { return null; } const href = '#' + heading.id; return ( { e.preventDefault(); telemetry.sendTocItemClickedMessage(); handleHeadingClick(heading.id); }} /> ); })} ); } const TableOfContentMenu = styled.aside` background-color: var(--toc-bg-color); flex-shrink: 0; display: none !important; @media screen and (min-width: ${breakpoints.medium}) { width: var(--toc-width); display: block !important; } `; const TableOfContentHeader = styled.div` font-family: var(--toc-heading-font-family); font-size: var(--toc-heading-font-size); font-weight: var(--toc-heading-font-weight); padding: var(--toc-item-padding-vertical) var(--toc-item-padding-horizontal); color: var(--toc-heading-text-color); text-transform: var(--toc-heading-text-transform); line-height: var(--toc-heading-line-height); `; const TableOfContentItems = styled.div` position: sticky; top: calc(var(--navbar-height) + var(--toc-offset-top)); max-height: calc(100vh - var(--navbar-height) - var(--toc-offset-top)); overflow-y: auto; width: var(--toc-width); border-left: solid 1px var(--toc-border-color); `; const TableOfContentMenuItem = styled.a<{ depth: number }>` display: block; color: var(--toc-item-text-color); font-weight: var(--toc-item-font-weight); background-color: var(--toc-item-bg-color); cursor: pointer; font-size: var(--toc-item-font-size); padding: var(--toc-item-padding-vertical) var(--toc-item-padding-horizontal); padding-left: calc(var(--toc-item-nested-offset) * ${({ depth }) => depth}); transition: background-color 0.3s, color 0.3s; text-decoration: none; word-break: break-word; font-family: var(--heading-font-family, var(--toc-item-font-family)); line-height: var(--toc-item-line-height); border-left: solid 2px transparent; &:hover { color: var(--toc-item-text-color-active); } &.active { color: var(--toc-item-text-color-active); border-left: solid 2px var(--toc-item-border-color-active); font-weight: var(--toc-item-font-weight-active); } &:empty { padding: 0; } `;