import React, { useContext, useRef, useEffect, useState } from 'react'; import styled, { css } from 'styled-components'; import type { JSX } from 'react'; import { CodeWalkthroughStepsContext } from '@redocly/theme/core/contexts'; import { CodeBlockContainer } from '@redocly/theme/components/CodeBlock/CodeBlockContainer'; export type CodeContainerProps = { highlightedCode: string; toolbar: JSX.Element; }; export function CodeContainer({ highlightedCode, toolbar, }: CodeContainerProps): JSX.Element | null { const { activeStep } = useContext(CodeWalkthroughStepsContext); const [isHovered, setIsHovered] = useState(false); const compRef = useRef(null); useEffect(() => { // useEffect executed before DOM is updated due to re-render called before "painting" phase. setTimeout(() => { if (compRef.current) { const highlightedLines = Array.from( compRef.current.querySelectorAll('span.line.highlighted'), ); const { bottom: panelBottom, top: panelTop } = compRef.current.getBoundingClientRect(); const linesBelow: Element[] = []; const linesAbove: Element[] = []; for (const highlightedLine of highlightedLines) { const { bottom: lineBottom, top: lineTop } = highlightedLine.getBoundingClientRect(); const below = lineBottom > panelBottom; const above = lineTop < panelTop; if (below) { linesBelow.push(highlightedLine); } else if (above) { linesAbove.push(highlightedLine); } } if ((linesBelow.length && linesAbove.length) || linesAbove.length) { linesAbove[0].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start' }); } else if (linesBelow.length) { linesBelow[linesBelow.length - 1].scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'start', }); } } }, 200); }, [activeStep]); return ( setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > {toolbar} ); } const CodeContainerWrapper = styled.div<{ hideCodeColors?: boolean }>` position: relative; display: flex; flex-direction: column; flex-grow: 1; min-height: 0; pre { display: grid; grid-auto-rows: min-content; min-height: 0; overflow: scroll; margin: 0 !important; height: 100%; padding-left: 0 !important; span.line::before { color: var(--code-panel-line-numbering-color); } ${({ hideCodeColors }) => hideCodeColors && css` .line.greyed-out { color: var(--text-color-helper) !important; * { color: var(--text-color-helper) !important; } } `} } `;