import toH from 'hast-to-hyperscript' import type { ReactElement, ReactNode, FC } from 'react' import React, { useMemo, createElement, isValidElement } from 'react' import { Container } from '@toptal/picasso-container' import { Typography } from '@toptal/picasso-typography' import { List, ListItem } from '@toptal/picasso-list' import { Link } from '@toptal/picasso-link' import { twMerge } from '@toptal/picasso-tailwind-merge' import type { ASTType } from '../../types' import { Emoji, Image, Code, CodeBlock } from '../../components' import { isCustomEmoji } from '../../../utils' type Props = { children?: React.ReactNode className?: string } // List internaly passes another props to ListItem const Li = ({ children, ...props }: Props) => ( {children} ) /* eslint-disable id-length */ const P = ({ children, ...props }: Props) => ( {children} ) const Strong = ({ children, ...props }: Props) => ( {children} ) const Em = ({ children, ...props }: Props) => ( {children} ) const H3 = ({ children, ...props }: Props) => ( {children} ) const Ul = ({ children, ...props }: Props) => ( {children} ) const Ol = ({ children, ...props }: Props) => ( {children} ) const A = ({ children, className, ...props }: Props) => ( strong]:text-purple-500 [&:visited>em]:text-purple-500', className )} > {children} ) const Img = ({ ...props }: Props) => isCustomEmoji(props) ? : const componentMap: Record = { p: P, strong: Strong, em: Em, h3: H3, li: Li, ol: Ol, ul: Ul, a: A, img: Img, code: Code, pre: CodeBlock, } as const const picassoMapper = (child: ReactNode): ReactNode => { if (!isValidElement(child)) { return child } const type = componentMap[child.type as keyof typeof componentMap] || child.type const mappedChildren = child.props.children?.map(picassoMapper) ?? null return createElement(type, { ...child.props, key: child.key }, mappedChildren) } const useRichText = (value: ASTType): ReactNode[] | ReactNode => { const mappedTextNodes = useMemo(() => { const { children: astChildren } = value if (!astChildren?.length) { return null } const isSingleChild = astChildren.length === 1 const reactElement = toH(createElement, value) as ReactElement if (isSingleChild) { return picassoMapper(reactElement) } // first node of tree is always "root", // which is transformed to wrapping div when children.length > 1 // when single children, there is no wrapping div and it returns textNode right away const textNodes = reactElement.props.children return textNodes.map(picassoMapper) }, [value]) return mappedTextNodes } export default useRichText