拖动手柄 React 扩展

版本下载量

你是否曾想过在基于 React 的编辑器中拖动节点?我们也有同样的需求——所以这里有一个扩展实现它。

DragHandleReact 组件允许你轻松处理编辑器中节点的拖动。你可以定义自定义渲染函数、位置等。 它本质上将 DragHandle 扩展封装到一个 React 组件中,该组件会自动向编辑器注册和注销扩展。

安装

npm install @tiptap/extension-drag-handle-react @tiptap/extension-drag-handle @tiptap/extension-node-range @tiptap/extension-collaboration @tiptap/y-tiptap yjs y-protocols

属性

所有属性遵循与 DragHandle 扩展相同的结构。

children

应显示在拖动手柄内部的内容。

<DragHandle>
  <div>拖动我!</div>
</DragHandle>

computePositionConfig

拖动手柄的位置计算配置,使用 floating-ui/dom 包。你可以传递在 floating-ui 文档 中可用的任何选项。

默认值:{ placement: 'left-start', strategy: 'absolute' }

<DragHandle
  computePositionConfig={{
    placement: 'left',
    strategy: 'fixed',
  }}
>
  <div>拖动我!</div>
</DragHandle>

onNodeChange

当节点被悬停时返回一个节点或 null。这可以用于高亮当前悬停的节点。

默认值:undefined

function Component() {
  const [selectedNode, setSelectedNode] = useState(null)

  return (
    <DragHandle
      onNodeChange={({ node, editor, pos }) => {
        setSelectedNode(node)
        // 对节点进行某些操作
      }}
    >
      <div>拖动我!</div>
    </DragHandle>
  )
}

getReferencedVirtualElement

一个返回拖动手柄虚拟元素的函数。当菜单需要相对于特定的 DOM 元素定位时,这很有用。

默认值:undefined

<DragHandle
  getReferencedVirtualElement={() => {
    // 返回一个用于自定义定位的虚拟元素
    return null
  }}
>
  <div>拖动我!</div>
</DragHandle>

locked

锁定拖动手柄的位置和可见性。如果拖动手柄原本可见,则会保持可见直到解除锁定;如果原本隐藏,则保持隐藏直到解除锁定。

默认值:false

<DragHandle locked={true}>
  <div>拖动我!</div>
</DragHandle>

pluginKey

在编辑器中存储插件时应使用的键。如果在同一个编辑器中有多个拖动手柄,这非常有用。

默认值:undefined

<DragHandle pluginKey="myCustomDragHandle">
  <div>拖动我!</div>
</DragHandle>

onElementDragStart

当元素开始被拖动时调用的函数。可以用于添加拖动开始时的自定义逻辑。

<DragHandle
  onElementDragStart={(e: DragEvent) => {
    // 拖动开始时执行某些操作
  }}
>
  <div>拖动我!</div>
</DragHandle>

onElementDragEnd

当元素停止被拖动时调用的函数。可以用于添加拖动结束时的自定义逻辑。

<DragHandle
  onElementDragEnd={(e: DragEvent) => {
    // 拖动结束时执行某些操作
  }}
>
  <div>拖动我!</div>
</DragHandle>

nested

启用针对嵌套内容(如列表项、引用块及其他嵌套结构)的拖动手柄。

启用后,拖动手柄不仅会出现在顶级块,还会出现在嵌套块。基于规则的评分系统会根据光标位置和配置规则确定目标节点。

默认值:false

// 使用默认值启用
<DragHandle nested={true}>
  <div>拖动我!</div>
</DragHandle>

// 使用自定义边缘检测启用
<DragHandle nested={{ edgeDetection: { threshold: -16 } }}>
  <div>拖动我!</div>
</DragHandle>

带嵌套支持的完整示例

import DragHandle from '@tiptap/extension-drag-handle-react'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import { useState } from 'react'

const NESTED_CONFIG = { edgeDetection: { threshold: -16 } }

export default function Editor() {
  const [nested, setNested] = useState(true)

  const editor = useEditor({
    extensions: [StarterKit],
    content: '<ul><li>Item 1</li><li>Item 2</li></ul>',
  })

  return (
    <>
      <button onClick={() => setNested(!nested)}>
        切换嵌套
      </button>
      <DragHandle editor={editor} nested={nested ? NESTED_CONFIG : false}>
        <div className="drag-handle" />
      </DragHandle>
      <EditorContent editor={editor} />
    </>
  )
}

性能提示

当使用 React 状态切换 nested 属性时,请将配置对象定义为组件外部的常量,避免不必要的重新渲染。

请参见 DragHandle 嵌套文档 获取包括边缘检测、自定义规则及允许容器等详细配置选项。

命令

请参见 DragHandle 扩展以获取可用的编辑器命令。