import type { ComponentType, ReactNode } from 'react'; import { getWebProps } from 'react-native-unistyles/web'; import type { MarkdownStyleOverrides, LinkHandler, ImageHandler, CodeBlockOptions, } from '../../Markdown/types'; import type { MarkdownDynamicProps } from '../../Markdown/Markdown.styles'; interface CreateWebRenderersOptions { styles: any; dynamicProps: MarkdownDynamicProps; styleOverrides?: MarkdownStyleOverrides; linkHandler?: LinkHandler; imageHandler?: ImageHandler; codeOptions?: CodeBlockOptions; } type RendererProps = { children?: ReactNode; node?: any; [key: string]: any; }; /** * Creates custom component renderers for react-markdown * that use theme-integrated styles. */ export function createWebRenderers({ styles, dynamicProps, styleOverrides, linkHandler, imageHandler, codeOptions, }: CreateWebRenderersOptions): Record> { // Helper to get styled props const getStyledProps = (elementName: string) => { const styleArray = [ (styles[elementName] as any)?.(dynamicProps), (styleOverrides as any)?.[elementName], ].filter(Boolean); return getWebProps(styleArray); }; return { // Headings h1: ({ children }: RendererProps) => (

{children}

), h2: ({ children }: RendererProps) => (

{children}

), h3: ({ children }: RendererProps) => (

{children}

), h4: ({ children }: RendererProps) => (

{children}

), h5: ({ children }: RendererProps) => (
{children}
), h6: ({ children }: RendererProps) => (
{children}
), // Paragraph p: ({ children }: RendererProps) => (

{children}

), // Emphasis strong: ({ children }: RendererProps) => ( {children} ), em: ({ children }: RendererProps) => ( {children} ), del: ({ children }: RendererProps) => ( {children} ), // Links a: ({ children, href, title }: RendererProps) => { const handleClick = (e: React.MouseEvent) => { if (linkHandler?.onLinkPress) { const preventDefault = linkHandler.onLinkPress(href || '', title); if (preventDefault) { e.preventDefault(); } } }; const isExternal = href?.startsWith('http://') || href?.startsWith('https://'); const shouldOpenExternal = isExternal && (linkHandler?.openExternalLinks ?? true); return ( {children} ); }, // Blockquote blockquote: ({ children }: RendererProps) => (
{children}
), // Code code: ({ children, className, inline }: RendererProps) => { // Check if it's inline code if (inline) { return {children}; } // Block code - extract language from className const match = /language-(\w+)/.exec(className || ''); const language = match ? match[1] : ''; return ( {children} ); }, pre: ({ children }: RendererProps) => (
{children}
), // Lists ul: ({ children }: RendererProps) => ( ), ol: ({ children }: RendererProps) => (
    {children}
), li: ({ children, checked }: RendererProps) => { // Task list item if (typeof checked === 'boolean') { return (
  • {children}
  • ); } return
  • {children}
  • ; }, // Table table: ({ children }: RendererProps) => ( {children}
    ), thead: ({ children }: RendererProps) => ( {children} ), tbody: ({ children }: RendererProps) => {children}, tr: ({ children }: RendererProps) => ( {children} ), th: ({ children, style: alignStyle }: RendererProps) => ( {children} ), td: ({ children, style: alignStyle }: RendererProps) => ( {children} ), // Image img: ({ src, alt, title }: RendererProps) => { const resolvedSrc = imageHandler?.resolveImageUrl ? imageHandler.resolveImageUrl(src || '') : src; const handleClick = () => { if (imageHandler?.onImagePress) { imageHandler.onImagePress(src || '', alt); } }; return ( {alt} ); }, // Horizontal rule hr: () =>
    , // Break br: () =>
    , }; }