import { CodeButtonGroup, type CodeButtonGroupProps, IconArrowDown, SvgWrapper, useCodeButtonGroup, } from '@rspress/core/theme'; import clsx from 'clsx'; import { useCallback, useEffect, useRef, useState } from 'react'; import './index.scss'; const DEFAULT_FOLD_HEIGHT = 300; export type CodeBlockProps = { title?: string; lang?: string; /** * @default false */ wrapCode?: boolean; /** * @default false */ lineNumbers?: boolean; /** * @default false */ fold?: boolean; height?: number; containerElementClassName?: string; codeButtonGroupProps?: Omit< CodeButtonGroupProps, 'preElementRef' | 'wrapCode' | 'toggleWrapCode' >; children?: React.ReactNode; }; /** * This component only provides styling and must be used with Shiki. * If you need runtime code highlighting, please use instead. * * @example * * Input: * * ```tsx * *
 *      
 *    
*
* ``` * * Expected html output: * * ```html *
*
test.js
*
*
*
 *        
 *        
 *      
*
*
* * *
*
*
*``` */ export function CodeBlock({ containerElementClassName, title, lang = 'txt', wrapCode: wrapCodeProp = false, lineNumbers: lineNumbersProp = false, fold = false, height, codeButtonGroupProps, children, }: CodeBlockProps) { if (import.meta.env.SSG_MD) { return <>{children}; } const { wrapCode: wrapCodeState, toggleWrapCode, copyElementRef, } = useCodeButtonGroup(wrapCodeProp); const [expanded, setExpanded] = useState(false); const [needFold, setNeedFold] = useState(false); const hasScroll = !fold && height !== undefined; const codeBlockRef = useRef(null); const contentRef = useRef(null); useEffect(() => { if (!fold || !contentRef.current) { setNeedFold(false); return; } const foldHeight = height ?? DEFAULT_FOLD_HEIGHT; const realHeight = contentRef.current.scrollHeight; setNeedFold(realHeight > foldHeight); }, [fold, height]); const handleFoldToggle = useCallback(() => { if (expanded) { setExpanded(false); requestAnimationFrame(() => { if (codeBlockRef.current) { const rect = codeBlockRef.current.getBoundingClientRect(); if (rect.top < 0) { window.scrollBy({ top: rect.top - 16, behavior: 'smooth', }); } } }); } else { setExpanded(true); } }, [expanded]); return (
{title &&
{title}
}
{children}
{needFold && ( )}
); }