/* eslint-disable react/no-children-prop */ import normalizePath from '@/utils/normalizePath' import clsx from 'clsx' import Link from 'next/link' import React, { CSSProperties, type FC, type ReactElement } from 'react' import Image from 'next/image' import { IS_BOLD, IS_CODE, IS_ITALIC, IS_STRIKETHROUGH, IS_SUBSCRIPT, IS_SUPERSCRIPT, IS_UNDERLINE, } from './RichTextNodeFormat' type SerializedLexicalNode = { children?: SerializedLexicalNode[] direction: string format: number indent?: string | number type: string version: number style?: string mode?: string text?: string [other: string]: any } type TextComponentProps = { children: ReactElement | string format: number } const getLinkForDocument = (doc: any, locale?: string): string => { let path = doc?.path if (!path || path.startsWith('/home') || path === '/' || path === '') path = '/' return normalizePath(`/${locale}${path}`) } function gcd(a: number, b: number): number { return b === 0 ? a : gcd(b, a % b) } function calculateAspectRatio(width: number, height: number): string { const divisor = gcd(width, height) const simplifiedWidth = width / divisor const simplifiedHeight = height / divisor return `${simplifiedWidth}x${simplifiedHeight}` } const TextComponent: FC = ({ children, format }) => { const formatFunctions: { [key: number]: (child: ReactElement | string) => ReactElement } = { [IS_BOLD]: (child) => {child}, [IS_ITALIC]: (child) => {child}, [IS_STRIKETHROUGH]: (child) => {child}, [IS_UNDERLINE]: (child) => {child}, [IS_CODE]: (child) => {child}, [IS_SUBSCRIPT]: (child) => {child}, [IS_SUPERSCRIPT]: (child) => {child}, } const formattedText = Object.entries(formatFunctions).reduce( (formattedText, [key, formatter]) => { return format & Number(key) ? formatter(formattedText) : formattedText }, children, ) return <>{formattedText} } const SerializedLink: React.FC<{ node: SerializedLexicalNode locale: string children: React.JSX.Element | null }> = ({ node, locale, children }) => { const { doc, url, newTab, linkType } = node.fields as any const document = doc?.value const href = linkType === 'custom' ? url : getLinkForDocument(document, locale) const target = newTab ? '_blank' : undefined return ( {children} ) } const getNodeClassNames = (node: SerializedLexicalNode) => { const attributes: Record = {} if (!node) return attributes let classNames = '' if (String(node?.format).toString()?.includes('left') && node.direction !== 'ltr') classNames += 'text-left ' if (String(node?.format).toString()?.includes('center')) classNames += 'text-center ' if (String(node?.format).toString()?.includes('right') && node.direction !== 'rtl') classNames += 'text-right ' if (classNames.length > 0) attributes.className = classNames.trim() const indent = parseInt(`${node?.indent || 0}`) if (!isNaN(indent) && indent !== 0) { attributes.style = { '--indent': `${indent * 10}px` } as CSSProperties attributes.className = `${attributes.className ?? ''} ml-[--indent]`.trim() } return attributes } const LexicalContent: React.FC<{ childrenNodes: SerializedLexicalNode[] locale: string className?: string lazyLoadImages: boolean }> = ({ childrenNodes, locale, lazyLoadImages = false }) => { if (!Array.isArray(childrenNodes)) return null const renderedChildren = childrenNodes.map((node, ix) => { if (!node) return null const attributes = getNodeClassNames(node || '') if (node.type === 'text') { return ( <> {Object.keys(attributes).length > 0 && {node?.text || ''}} {(Object.keys(attributes).length === 0 && node?.text) || ''} ) } const serializedChildren = node.children ? ( ) : null switch (node.type) { case 'linebreak': return
case 'link': return case 'list': const ListTag = node.listType === 'bullet' ? 'ul' : 'ol' attributes.className = clsx( attributes.className, 'mb-4 pl-8', ListTag === 'ol' ? 'list-decimal' : 'list-disc', ) return ( {serializedChildren} ) case 'listitem': return (
  • {serializedChildren}
  • ) case 'heading': const HeadingTag = node.tag as keyof React.JSX.IntrinsicElements return ( {serializedChildren} ) case 'quote': return (
    {serializedChildren}
    ) case 'upload': const upload = node?.value if (!upload) return null const imageAspectRatio = calculateAspectRatio(upload.width, upload.height) return ( {upload?.alt ) default: if ( Array.isArray(serializedChildren?.props?.childrenNodes) && serializedChildren?.props?.childrenNodes.length === 0 ) return
    return (

    {serializedChildren}

    ) } }) return <>{renderedChildren.filter((node) => node !== null)} } export default LexicalContent