tiptap 中文文档

Tiptap Floating Menu 浮动菜单

Tiptap Floating Menu 浮动菜单扩展,当新启一个空行时可以使用浮动菜单悬浮在这行上面,你可以在这个菜单上面放置需要的功能。

Install 安装

npm install @tiptap/extension-floating-menu

Settings 配置

element 放置菜单的元素,默认为null。

FloatingMenu.configure({
  element: null,
})

tippyOptions FloatingMenu依赖tippy.js,您可以直接将配置传递给它。

FloatingMenu.configure({
  tippyOptions: { },
})

pluginKey 当有多个实例时你需要用到这个参数,默认为bubbleMenu。

import { Editor } from '@tiptap/core'
import BubbleMenu from '@tiptap/extension-bubble-menu'

new Editor({
  extensions: [
    FloatingMenu.configure({
      pluginKey: 'bubbleMenuOne',
      element: document.querySelector('.menu-one'),
    }),
    FloatingMenu.configure({
      pluginKey: 'bubbleMenuTwo',
      element: document.querySelector('.menu-two'),
    }),
  ],
})

shouldShow 一个控制菜单是否显示的回调函数,你可以自定义控制菜单是否显示。

FloatingMenu.configure({
  shouldShow: ({ editor, view, state, oldState, from, to }) => {
    //如果是图片或链接才显示菜单
    return editor.isActive('image') || editor.isActive('link')
  },
})

Usage 用例

import { Editor } from '@tiptap/core'
import FloatingMenu from '@tiptap/extension-floating-menu'

new Editor({
  extensions: [
    FloatingMenu.configure({
      element: document.querySelector('.menu'),
    }),
  ],
})

源代码

floating-menu 源代码

在线例子

vue 在线例子

FloatingMenu 例子

  • Vue 例子

  • React 例子

  • React CSS

<template>
  <div>
    <floating-menu :editor="state.editor" :tippy-options="{ duration: 100 }" v-if="state.editor">
      <button @click="state.editor.chain().focus().toggleHeading({ level: 1 }).run()" :class="{ 'is-active': state.editor.isActive('heading', { level: 1 }) }">
        H1
      </button>
      <button @click="state.editor.chain().focus().toggleHeading({ level: 2 }).run()" :class="{ 'is-active': state.editor.isActive('heading', { level: 2 }) }">
        H2
      </button>
      <button @click="state.editor.chain().focus().toggleBulletList().run()" :class="{ 'is-active': state.editor.isActive('bulletList') }">
        Bullet List
      </button>
    </floating-menu>
  </div>
  <div>
    <editor-content :editor="state.editor" />
  </div>
  <h2>编辑器内容:</h2>
  <div style="min-height: 90px;">
    {{ getHtml }}
  </div>
   </template>
<script setup>
/*
 tiptap 中文文档
 https://www.itxst.com/tiptap/tutorial.html
*/
import { Editor, EditorContent, FloatingMenu } from '@tiptap/vue-3'
import StarterKit from "@tiptap/starter-kit";
import { computed, onMounted, reactive } from 'vue' 

const state = reactive({
  editor: new Editor({
    extensions: [
      EditorContent,
      FloatingMenu,
      StarterKit
    ],
    content: ` 
       <p>拖动下面的图片到文字上方试试看</p>
       <img src="https://www.itxst.com/img/logov31.png" />
      `,
  }) as any,
  result: '', 
});

onMounted(() => {
  
});

const getHtml = computed(() => {
  return state.editor.getHTML();
})
</script>
<style scoped>
.editor,
.html {
  margin: 10px 20px;
  width: 690px;
}
.is-active {
  background-color: #1512e6;
  color: #fff;
}
.editor:deep(.ProseMirror) {
  color: #333;
  border: solid 3px #333;
  padding: 0px 6px;
  height: 260px;
  overflow-y: auto;
  border-radius: 6px;
}
.editor:deep(a) {
  color: #1512e6;
  cursor: pointer;
  text-decoration: underline;
}
.editor:deep(img) {
  background-color: #000;
}
.btn {
  margin-left: 20px;
  margin-top: 10px;
}
button {
  margin-right: 10px;
}
.editor:deep(blockquote) {
  padding-left: 10px;
  border-left: 3px solid #ddd;
}
.html pre {
  white-space: pre-wrap;
}
h2 {
  padding-left: 15px;
  font-size: 15px;
}
.html,
textarea {
  margin: 0px 20px;
  color: #ddd;
  background: #282c34;
  width: 690px;
  min-height: 200px;
  white-space: pre-wrap;
  padding: 6px;
  border-radius: 3px;
}

.editor:deep(.ProseMirror) {
  code {
    font-size: 0.9rem;
    padding: 0.25em;
    border-radius: 0.25em;
    background-color: rgba(#616161, 0.1);
    color: #616161;
    box-decoration-break: clone;
  }
}
</style>
import './styles.scss'
import { EditorContent, FloatingMenu, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React, { useEffect } from 'react'

export default () => {
  const editor = useEditor({
    extensions: [
      StarterKit,
    ],
    content: `
      <p>
        This is an example of a Medium-like editor. Enter a new line and some buttons will appear.
      </p>
      <p></p>
    `,
  })

  const [isEditable, setIsEditable] = React.useState(true)

  useEffect(() => {
    if (editor) {
      editor.setEditable(isEditable)
    }
  }, [isEditable, editor])

  return (
    <>
      <div>
        <input type="checkbox" checked={isEditable} onChange={() => setIsEditable(!isEditable)} />
        Editable
      </div>
      {editor && <FloatingMenu editor={editor} tippyOptions={{ duration: 100 }}>
        <button
          onClick={() => editor.chain().focus().toggleHeading({ level: 1 }).run()}
          className={editor.isActive('heading', { level: 1 }) ? 'is-active' : ''}
        >
          h1
        </button>
        <button
          onClick={() => editor.chain().focus().toggleHeading({ level: 2 }).run()}
          className={editor.isActive('heading', { level: 2 }) ? 'is-active' : ''}
        >
          h2
        </button>
        <button
          onClick={() => editor.chain().focus().toggleBulletList().run()}
          className={editor.isActive('bulletList') ? 'is-active' : ''}
        >
          bullet list
        </button>
      </FloatingMenu>}
      <EditorContent editor={editor} />
    </>
  )
}
.ProseMirror {
  > * + * {
    margin-top: 0.75em;
  }
  ul,
  ol {
    padding: 0 1rem;
  }
}
Catalog
快速入门 Guide 向导 API 列表 tiptap 方法 tiptap 属性 titap 配置 commands 命令 nodes 节点 marks 标记 extensions 扩展 Color 颜色扩展 Bubble Menu 气泡菜单 CharacterCount 文字统计 Collaboration 实时协作 CollaborationCursor 协作光标 Dropcursor 拖动光标 Floating Menu 浮动菜单 Focus 焦点扩展 FontFamily 字体 Gapcursor 空光标 History 历史记录 Placeholder 占位符 StarterKit 核心扩展 TextAlign 对齐方式 Typography 特殊符号