转换 Markdown

富文本转 Markdown

如果你能访问 Payload Config 和 lexical 编辑器配置,你可以使用以下方法将 lexical 编辑器状态转换为 Markdown:

import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'

import {
  convertLexicalToMarkdown,
  editorConfigFactory,
} from '@payloadcms/richtext-lexical'

// 你的富文本数据在这里
const data: SerializedEditorState = {}

const markdown = convertLexicalToMarkdown({
  data,
  editorConfig: await editorConfigFactory.default({
    config, // <= 确保你能访问你的 Payload Config
  }),
})

示例 - 从 Collection 输出 Markdown

import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
import type { CollectionConfig, RichTextField } from 'payload'

import {
  convertLexicalToMarkdown,
  editorConfigFactory,
  lexicalEditor,
} from '@payloadcms/richtext-lexical'

const Pages: CollectionConfig = {
  slug: 'pages',
  fields: [
    {
      name: 'nameOfYourRichTextField',
      type: 'richText',
      editor: lexicalEditor(),
    },
    {
      name: 'markdown',
      type: 'textarea',
      admin: {
        hidden: true,
      },
      hooks: {
        afterRead: [
          ({ siblingData, siblingFields }) => {
            const data: SerializedEditorState =
              siblingData['nameOfYourRichTextField']

            if (!data) {
              return ''
            }

            const markdown = convertLexicalToMarkdown({
              data,
              editorConfig: editorConfigFactory.fromField({
                field: siblingFields.find(
                  (field) =>
                    'name' in field && field.name === 'nameOfYourRichTextField',
                ) as RichTextField,
              }),
            })

            return markdown
          },
        ],
        beforeChange: [
          ({ siblingData }) => {
            // 确保 markdown 字段不会被保存到数据库
            delete siblingData['markdown']
            return null
          },
        ],
      },
    },
  ],
}

Markdown 转 Richtext

如果你可以访问 Payload Config 和 lexical editor config,你可以使用以下方式将 Markdown 转换为 lexical 编辑器状态:

import {
  convertMarkdownToLexical,
  editorConfigFactory,
} from '@payloadcms/richtext-lexical'

const lexicalJSON = convertMarkdownToLexical({
  editorConfig: await editorConfigFactory.default({
    config, // <= 确保你可以访问你的 Payload Config
  }),
  markdown: '# Hello world\n\nThis is a **test**.',
})

转换 MDX

Payload 支持序列化和反序列化 MDX 内容。Markdown 转换器存储在 features 上,而 MDX 转换器则存储在你传递给 BlocksFeature 的 blocks 上。

定义自定义区块

以下是一个 Banner 区块的示例。

该区块:

  • 在管理界面中渲染为带有特定字段(如类型、内容)的标准 Lexical 区块
  • 转换为 MDX 的 Banner 组件
  • 可以将该 MDX Banner 解析回 Lexical 状态
展示 Lexical 编辑器中的 Banner 字段和 MDX 输出
Lexical 编辑器中的 Banner 字段和 MDX 输出
import type { SerializedEditorState } from '@payloadcms/richtext-lexical/lexical'
import type { Block, CollectionConfig, RichTextField } from 'payload'

import {
  BlocksFeature,
  convertLexicalToMarkdown,
  editorConfigFactory,
  lexicalEditor,
} from '@payloadcms/richtext-lexical'

const BannerBlock: Block = {
  slug: 'Banner',
  fields: [
    {
      name: 'type',
      type: 'select',
      defaultValue: 'info',
      options: [
        { label: 'Info', value: 'info' },
        { label: 'Warning', value: 'warning' },
        { label: 'Error', value: 'error' },
      ],
    },
    {
      name: 'content',
      type: 'richText',
      editor: lexicalEditor(),
    },
  ],
  jsx: {
    /**
     * 从 Lexical 转换为 MDX:
     * <Banner type="..." >子内容</Banner>
     */
    export: ({ fields, lexicalToMarkdown }) => {
      const props: any = {}
      if (fields.type) {
        props.type = fields.type
      }

      return {
        children: lexicalToMarkdown({ editorState: fields.content }),
        props,
      }
    },
    /**
     * 从 MDX 转换为 Lexical:
     */
    import: ({ children, markdownToLexical, props }) => {
      return {
        type: props?.type,
        content: markdownToLexical({ markdown: children }),
      }
    },
  },
}

const Pages: CollectionConfig = {
  slug: 'pages',
  fields: [
    {
      name: 'nameOfYourRichTextField',
      type: 'richText',
      editor: lexicalEditor({
        features: ({ defaultFeatures }) => [
          ...defaultFeatures,
          BlocksFeature({
            blocks: [BannerBlock],
          }),
        ],
      }),
    },
    {
      name: 'markdown',
      type: 'textarea',
      hooks: {
        afterRead: [
          ({ siblingData, siblingFields }) => {
            const data: SerializedEditorState =
              siblingData['nameOfYourRichTextField']

            if (!data) {
              return ''
            }

            const markdown = convertLexicalToMarkdown({
              data,
              editorConfig: editorConfigFactory.fromField({
                field: siblingFields.find(
                  (field) =>
                    'name' in field && field.name === 'nameOfYourRichTextField',
                ) as RichTextField,
              }),
            })

            return markdown
          },
        ],
        beforeChange: [
          ({ siblingData }) => {
            // 确保 markdown 字段不会保存到数据库
            delete siblingData['markdown']
            return null
          },
        ],
      },
    },
  ],
}

转换是通过区块的 jsx 属性完成的。export 函数在从 Lexical 转换为 MDX 时调用,import 函数在从 MDX 转换为 Lexical 时调用。

导出

export 函数接收块字段数据和 lexicalToMarkdown 函数作为参数。它返回以下对象:

属性类型描述
childrenstring这将位于块的开始标签和结束标签之间。
propsobject这将位于块的开始标签中。

导入

import 函数提供从 MDX 提取的数据。它接收以下参数:

参数类型描述
childrenstring这是块开始标签和结束标签之间的文本。
propsobject这些是传递给块的属性,从开始标签解析为对象。

返回的对象等同于块字段数据。