({
text,
textAtom,
showTocAtom,
style,
className,
contentClassName,
resolveOpts,
fontSizeOverride,
fixedFontSizeOverride,
scrollable = true,
rehype = true,
onClickExecute,
}: MarkdownProps)
| 307 | }; |
| 308 | |
| 309 | const Markdown = ({ |
| 310 | text, |
| 311 | textAtom, |
| 312 | showTocAtom, |
| 313 | style, |
| 314 | className, |
| 315 | contentClassName, |
| 316 | resolveOpts, |
| 317 | fontSizeOverride, |
| 318 | fixedFontSizeOverride, |
| 319 | scrollable = true, |
| 320 | rehype = true, |
| 321 | onClickExecute, |
| 322 | }: MarkdownProps) => { |
| 323 | const textAtomValue = useAtomValueSafe<string>(textAtom); |
| 324 | const tocRef = useRef<TocItem[]>([]); |
| 325 | const showToc = useAtomValueSafe(showTocAtom) ?? false; |
| 326 | const contentsOsRef = useRef<OverlayScrollbarsComponentRef>(null); |
| 327 | const [focusedHeading, setFocusedHeading] = useState<string>(null); |
| 328 | |
| 329 | // Ensure uniqueness of ids between MD preview instances. |
| 330 | const [idPrefix] = useState<string>(crypto.randomUUID()); |
| 331 | |
| 332 | text = textAtomValue ?? text ?? ""; |
| 333 | const transformedOutput = transformBlocks(text); |
| 334 | const transformedText = transformedOutput.content; |
| 335 | const contentBlocksMap = transformedOutput.blocks; |
| 336 | |
| 337 | useEffect(() => { |
| 338 | if (focusedHeading && contentsOsRef.current && contentsOsRef.current.osInstance()) { |
| 339 | const { viewport } = contentsOsRef.current.osInstance().elements(); |
| 340 | const heading = document.getElementById(idPrefix + focusedHeading.slice(1)); |
| 341 | if (heading) { |
| 342 | const headingBoundingRect = heading.getBoundingClientRect(); |
| 343 | const viewportBoundingRect = viewport.getBoundingClientRect(); |
| 344 | const headingTop = headingBoundingRect.top - viewportBoundingRect.top; |
| 345 | viewport.scrollBy({ top: headingTop }); |
| 346 | } |
| 347 | } |
| 348 | }, [focusedHeading]); |
| 349 | |
| 350 | const markdownComponents: Partial<Components> = { |
| 351 | a: (props: React.HTMLAttributes<HTMLAnchorElement>) => ( |
| 352 | <Link props={props} setFocusedHeading={setFocusedHeading} /> |
| 353 | ), |
| 354 | p: (props: React.HTMLAttributes<HTMLParagraphElement>) => <div className="paragraph" {...props} />, |
| 355 | h1: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading props={props} hnum={1} />, |
| 356 | h2: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading props={props} hnum={2} />, |
| 357 | h3: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading props={props} hnum={3} />, |
| 358 | h4: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading props={props} hnum={4} />, |
| 359 | h5: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading props={props} hnum={5} />, |
| 360 | h6: (props: React.HTMLAttributes<HTMLHeadingElement>) => <Heading props={props} hnum={6} />, |
| 361 | img: (props: React.HTMLAttributes<HTMLImageElement>) => <MarkdownImg props={props} resolveOpts={resolveOpts} />, |
| 362 | source: (props: React.HTMLAttributes<HTMLSourceElement>) => ( |
| 363 | <MarkdownSource props={props} resolveOpts={resolveOpts} /> |
| 364 | ), |
| 365 | code: Code, |
| 366 | pre: (props: React.HTMLAttributes<HTMLPreElement>) => ( |
nothing calls this directly
no test coverage detected