Media

Embed medias like videos or tweets into your document.

Loading...
Files
components/demo.tsx
'use client';

import React from 'react';

import { Plate } from '@udecode/plate-common/react';

import { editorPlugins } from '@/components/editor/plugins/editor-plugins';
import { useCreateEditor } from '@/components/editor/use-create-editor';
import { Editor, EditorContainer } from '@/components/plate-ui/editor';

import { DEMO_VALUES } from './values/demo-values';

export default function Demo({ id }: { id: string }) {
  const editor = useCreateEditor({
    plugins: [...editorPlugins],
    value: DEMO_VALUES[id],
  });

  return (
    <Plate editor={editor}>
      <EditorContainer variant="demo">
        <Editor />
      </EditorContainer>
    </Plate>
  );
}

Features

Media Features

  • 可编辑的标题
  • 可调整大小的元素

Media Support

  • 文件类型:
    • 图片
    • 视频
    • 音频
    • 其他 (PDF, Word, etc.)
  • 视频提供者:
    • 本地视频文件
    • YouTube, Vimeo, Dailymotion, Youku, Coub
  • 嵌入提供者:
    • Tweets

Upload

  • 多重上传方法:
    • 工具栏按钮与文件选择器
    • 从文件系统拖放
    • 从剪贴板粘贴(图片)
    • 外部媒体嵌入
  • 上传体验:
    • 实时进度跟踪
    • 上传期间预览
    • 自动将占位符转换为适当媒体元素(图片、视频、音频、文件)一旦上传或嵌入提交
    • 错误处理
    • 文件大小验证
    • 类型验证

Installation

npm install @udecode/plate-media

Usage

import {
  AudioPlugin,
  FilePlugin,
  ImagePlugin,
  MediaEmbedPlugin,
  PlaceholderPlugin,
  VideoPlugin,
} from '@udecode/plate-media/react';
import { SelectOnBackspacePlugin } from '@udecode/plate-select';
const plugins = [
  // ...otherPlugins,
  ImagePlugin,
  VideoPlugin,
  AudioPlugin,
  FilePlugin,
  MediaEmbedPlugin,
  SelectOnBackspacePlugin.configure({
    options: {
      query: {
        allow: [ImagePlugin.key, VideoPlugin.key, AudioPlugin.key, FilePlugin.key, MediaEmbedPlugin.key],
      },
    },
  }),
  PlaceholderPlugin.configure({
    options: { disableEmptyPlaceholder: true },
    render: { afterEditable: MediaUploadToast },
  }),
];
const components = {
  // ...otherComponents,
  [ImagePlugin.key]: ImageElement,
  [VideoPlugin.key]: VideoElement,
  [AudioPlugin.key]: AudioElement,
  [FilePlugin.key]: FileElement,
  [MediaEmbedPlugin.key]: MediaEmbedElement,
  [PlaceholderPlugin.key]: MediaPlaceholderElement,
};

标题

要启用媒体标题,请使用 Caption Plugin

上传

有两种方法在您的编辑器中实现文件上传:

  1. 使用我们的 UploadThing 实现
  2. 使用您首选的解决方案创建自定义实现

UploadThing

  1. 添加 MediaPlaceholderElement 组件

  2. 添加 UploadThing API 路由:

npx shadcx@latest add plate/api-uploadthing
  1. UploadThing 获取您的秘密密钥
  2. 将您的 UploadThing 秘密密钥添加到 .env
.env
UPLOADTHING_TOKEN=xxx

自定义实现

对于自定义实现,您需要创建一个与我们的接口匹配的上传钩子。这可以与任何上传后端(AWS S3、UploadThing、Cloudinary、Firebase Storage 等)一起使用。

上传钩子应实现此接口:

interface UseUploadFileProps {
  onUploadComplete?: (file: UploadedFile) => void;
  onUploadError?: (error: unknown) => void;
  headers?: Record<string, string>;
  onUploadBegin?: (fileName: string) => void;
  onUploadProgress?: (progress: { progress: number }) => void;
  skipPolling?: boolean;
}
 
interface UploadedFile {
  key: string;    // Unique identifier
  url: string;    // Public URL of the uploaded file
  name: string;   // Original filename
  size: number;   // File size in bytes
  type: string;   // MIME type
}

示例实现使用 S3 预签名 URL:

export function useUploadFile({ 
  onUploadComplete, 
  onUploadError, 
  onUploadProgress 
}: UseUploadFileProps = {}) {
  const [uploadedFile, setUploadedFile] = useState<UploadedFile>();
  const [uploadingFile, setUploadingFile] = useState<File>();
  const [progress, setProgress] = useState(0);
  const [isUploading, setIsUploading] = useState(false);
 
  async function uploadFile(file: File) {
    setIsUploading(true);
    setUploadingFile(file);
 
    try {
      // Get presigned URL and final URL from your backend
      const { presignedUrl, fileUrl, fileKey } = await fetch('/api/upload', {
        method: 'POST',
        body: JSON.stringify({
          filename: file.name,
          contentType: file.type,
        }),
      }).then(r => r.json());
 
      // Upload to S3 using presigned URL
      await axios.put(presignedUrl, file, {
        headers: { 'Content-Type': file.type },
        onUploadProgress: (progressEvent) => {
          const progress = (progressEvent.loaded / progressEvent.total) * 100;
          setProgress(progress);
          onUploadProgress?.({ progress });
        },
      });
 
      const uploadedFile = {
        key: fileKey,
        url: fileUrl,
        name: file.name,
        size: file.size,
        type: file.type,
      };
 
      setUploadedFile(uploadedFile);
      onUploadComplete?.(uploadedFile);
      
      return uploadedFile;
    } catch (error) {
      onUploadError?.(error);
      throw error;
    } finally {
      setProgress(0);
      setIsUploading(false);
      setUploadingFile(undefined);
    }
  }
 
  return {
    isUploading,
    progress,
    uploadFile,
    uploadedFile,
    uploadingFile,
  };
}

Examples

Plate UI

参考上面的预览。

Plate Plus

Plugins

PlaceholderPlugin

用于空媒体占位符元素的插件。处理文件上传、拖放和剪贴板粘贴事件。

Options

Collapse all

    不同文件类型的配置。默认配置:

    {
      audio: {
        maxFileCount: 1,
        maxFileSize: '8MB',
        mediaType: AudioPlugin.key,
        minFileCount: 1,
      },
      blob: {
        maxFileCount: 1,
        maxFileSize: '8MB',
        mediaType: FilePlugin.key,
        minFileCount: 1,
      },
      image: {
        maxFileCount: 3,
        maxFileSize: '4MB',
        mediaType: ImagePlugin.key,
        minFileCount: 1,
      },
      pdf: {
        maxFileCount: 1,
        maxFileSize: '4MB',
        mediaType: FilePlugin.key,
        minFileCount: 1,
      },
      text: {
        maxFileCount: 1,
        maxFileSize: '64KB',
        mediaType: FilePlugin.key,
        minFileCount: 1,
      },
      video: {
        maxFileCount: 1,
        maxFileSize: '16MB',
        mediaType: VideoPlugin.key,
        minFileCount: 1,
      },
    }

    Supported file types: 'image' | 'video' | 'audio' | 'pdf' | 'text' | 'blob'

    禁用空占位符,当没有文件上传时。

    • 默认: false

    禁用拖放文件上传功能。

    • 默认: false

    如果未通过 uploadConfig 指定,则一次可以上传的最大文件数量。

    • 默认: 5

    允许一次上传多个相同类型的文件。

    • 默认: true

MediaPluginOptions

媒体插件使用的选项。

Attributes

Collapse all

    用于检查文本字符串是否为 URL 的函数。

    用于转换 URL 的函数。

ImagePlugin

用于处理空图像元素的插件。选项继承自 MediaPluginOptions

Options

Collapse all

    继承自 MediaPluginOptions

    一个可选的方法,用于将图片上传到服务器。该方法接收以下参数之一:

    • 来自 FileReader.readAsDataURL 的数据 URL(字符串)
    • 来自剪贴板数据的 ArrayBuffer

    应返回以下内容之一:

    • 已上传图片的 URL 字符串
    • 如果不需要上传,则返回原始数据 URL/ArrayBuffer

    如果未提供此方法,将使用原始数据 URL/ArrayBuffer 作为图片源。

    如果设置为 true,则在数据插入时禁用文件上传。

    如果设置为 true,则在数据插入时禁用 URL 嵌入。

VideoPlugin

用于处理空视频元素的插件。

AudioPlugin

用于处理空音频元素的插件。

FilePlugin

用于处理空文件元素的插件。

MediaEmbedPlugin

用于处理空媒体嵌入元素的插件。选项继承自 MediaPluginOptions

API 占位符

editor.tf.insert.media()

将媒体文件插入编辑器,并带有上传占位符。

Parameters

Collapse all

    要上传的文件。根据配置的文件类型和限制进行验证。

转换过程:

  • 根据配置的限制(大小、数量、类型)验证文件
  • 为每个文件创建占位符元素
  • 按顺序处理多个文件上传
  • 维护上传历史以支持撤销/重做操作
  • 如果验证失败则触发错误处理

Error codes:

enum UploadErrorCode {
  INVALID_FILE_TYPE = 400,
  TOO_MANY_FILES = 402,
  INVALID_FILE_SIZE = 403,
  TOO_LESS_FILES = 405,
  TOO_LARGE = 413,
}

editor.tf.insert.audioPlaceholder

插入一个占位符。完成后转换为音频元素。

Parameters

Collapse all

    插入节点转换的选项。

editor.tf.insert.filePlaceholder

插入一个占位符。完成后转换为文件元素。

Parameters

Collapse all

    插入节点转换的选项。

editor.tf.insert.imagePlaceholder

插入一个占位符。完成后转换为图片元素。

Parameters

Collapse all

    插入节点转换的选项。

editor.tf.insert.videoPlaceholder

插入一个占位符。完成后转换为视频元素。

Parameters

Collapse all

    插入节点转换的选项。

editor.api.placeholder.addUploadingFile()

跟踪当前正在上传的文件。

Parameters

Collapse all

    占位符元素的唯一标识符。

    正在上传的文件。

editor.api.placeholder.getUploadingFile()

获取当前正在上传的文件。

Parameters

Collapse all

    占位符元素的唯一标识符。

Returns

Collapse all

    如果找到则返回正在上传的文件,否则返回 undefined。

editor.api.placeholder.removeUploadingFile()

在上传完成或失败后从上传跟踪状态中移除文件。

Parameters

Collapse all

    要移除的占位符元素的唯一标识符。

API Media

parseMediaUrl

根据媒体插件的配置规则解析媒体 URL 并返回相关数据。

Parameters

Collapse all

    编辑器实例。

submitFloatingMedia

通过设置 URL 并执行必要的转换来提交浮动媒体元素。

Parameters

Collapse all

    编辑器实例。

EmbedUrlData

定义从解析嵌入 URL 返回的数据类型。

Attributes

Collapse all

    嵌入内容的 URL。

    嵌入内容的提供者。

    嵌入内容的唯一 ID。

    用于渲染嵌入内容的组件。

API Image

insertImage

在编辑器中插入图片元素。

Parameters

Collapse all

    编辑器实例。

    图片的 URL 或 ArrayBuffer。

    插入图片元素的其他选项。

isImageUrl

检查给定的 URL 是否是有效的图片 URL。

Parameters

Collapse all

    要检查的 URL。

withImageUpload

增强编辑器以支持从剪贴板粘贴图片。

Parameters

Collapse all

    编辑器实例。

    plate 插件。

withImageEmbed

增强编辑器以在粘贴 URL 时自动插入图片。

Parameters

Collapse all

    编辑器实例。

    plate 插件。

API Media Embed

insertMediaEmbed

在当前选择位置插入媒体嵌入元素。

Parameters

Collapse all

    编辑器实例。

    插入节点的选项。

parseIframeUrl

解析 iframe 嵌入的 URL。

Parameters

Collapse all

    iframe 的 URL 或嵌入代码。

parseTwitterUrl

解析 Twitter URL 并提取推文 ID。

Parameters

Collapse all

    Twitter URL。

Returns

Collapse all

    如果解析成功,返回包含推文 ID 和提供者的对象。 如果 URL 无效或不匹配任何支持的视频提供者,则返回 undefined。

parseVideoUrl

解析视频 URL 并提取视频 ID 和提供者特定的嵌入 URL。

Parameters

Collapse all

    视频 URL。

Returns

Collapse all

    如果解析成功,返回包含视频 ID 和提供者的对象。 如果 URL 无效或不匹配任何支持的视频提供者,则返回 undefined。

API Components

useResizable

用于可调整大小元素的行为钩子。

State

Collapse all

    可调整大小元素内容的对齐方式。

    可调整大小元素的最小宽度。

    可调整大小元素的最大宽度。

    用于在调整大小时设置节点宽度的函数。

    直接设置可调整大小元素宽度的函数。

    可调整大小元素的当前宽度。可以是字符串(如百分比或 'auto')或数字(表示像素)。

Returns

Collapse all

    指向包装可调整大小元素的最外层 div 的 React 引用。

    要传递给包装可调整大小元素的最外层 div 的属性。

    要传递给可调整大小元素的属性。

useFloatingMediaEditButton

用于浮动媒体编辑按钮的行为钩子。

Returns

Collapse all

useFloatingMediaUrlInput

用于浮动媒体 URL 输入的行为钩子。

Props

Collapse all

    URL 输入字段的默认值。

Returns

Collapse all

useImage

用于图片元素的行为钩子。

Returns

Collapse all

useMediaState

用于媒体元素的状态钩子。

Parameters

Collapse all

Returns

Collapse all

    媒体元素的对齐方式。

    媒体元素当前是否获得焦点。

    媒体元素当前是否被选中。

    编辑器是否处于只读模式。

    媒体元素的解析嵌入数据。

    媒体元素是否为推文。

    媒体元素是否为视频。

    媒体元素是否为 YouTube 视频。

useMediaToolbarButton

用于媒体工具栏按钮的行为钩子。

Parameters

Collapse all

Returns

Collapse all

类型

TMediaElement

export interface TMediaElement extends TElement {
  url: string;
  id?: string;
  align?: 'center' | 'left' | 'right';
  isUpload?: boolean;
  name?: string;
  placeholderId?: string;
}

TPlaceholderElement

export interface TPlaceholderElement extends TElement {
  mediaType: string;
}