From daa8e724772218c0689584f5aaf2b2499a6ce7dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Ku=CC=88hn?= Date: Tue, 30 Mar 2021 14:07:18 +0200 Subject: [PATCH] add doc page for bubble menu --- .../src/demos/Extensions/BubbleMenu/index.vue | 53 +++++-------------- .../docPages/api/extensions/bubble-menu.md | 39 ++++++++++++++ docs/src/links.yaml | 2 + packages/core/src/Editor.ts | 17 ++++-- .../src/bubble-menu-plugin.ts | 6 +-- .../extension-bubble-menu/src/bubble-menu.ts | 4 +- packages/extension-bubble-menu/src/index.ts | 1 + packages/vue-2/package.json | 1 + packages/vue-2/src/BubbleMenu.ts | 45 ++++++++++++++++ packages/vue-2/src/index.ts | 1 + 10 files changed, 122 insertions(+), 47 deletions(-) create mode 100644 docs/src/docPages/api/extensions/bubble-menu.md create mode 100644 packages/vue-2/src/BubbleMenu.ts diff --git a/docs/src/demos/Extensions/BubbleMenu/index.vue b/docs/src/demos/Extensions/BubbleMenu/index.vue index 1c1d249c..b5805588 100644 --- a/docs/src/demos/Extensions/BubbleMenu/index.vue +++ b/docs/src/demos/Extensions/BubbleMenu/index.vue @@ -1,24 +1,28 @@ diff --git a/docs/src/docPages/api/extensions/bubble-menu.md b/docs/src/docPages/api/extensions/bubble-menu.md new file mode 100644 index 00000000..b66305c4 --- /dev/null +++ b/docs/src/docPages/api/extensions/bubble-menu.md @@ -0,0 +1,39 @@ +# Bubble Menu +[![Version](https://img.shields.io/npm/v/@tiptap/extension-bubble-menu.svg?label=version)](https://www.npmjs.com/package/@tiptap/extension-bubble-menu) +[![Downloads](https://img.shields.io/npm/dm/@tiptap/extension-bubble-menu.svg)](https://npmcharts.com/compare/@tiptap/extension-bubble-menu?minimal=true) + +This extension will make a contextual menu appear near a selection of text. + +## Installation +```bash +# with npm +npm install @tiptap/extension-bubble-menu +# with Yarn +yarn add @tiptap/extension-bubble-menu +``` + +## Settings +| Option | Type | Default | Description | +| ------------ | ------------- | --------- | ----------- | +| element | `HTMLElement` | `null` | | +| keepInBounds | `Boolean` | `true` | | + +## Source code +[packages/extension-bubble-menu/](https://github.com/ueberdosis/tiptap-next/blob/main/packages/extension-bubble-menu/) + +## Vanilla JavaScript +```js +import { Editor } from '@tiptap/core' +import BubbleMenu from '@tiptap/extension-bubble-menu' + +new Editor({ + extensions: [ + BubbleMenu.configure({ + element: document.querySelector('.menu'), + }), + ], +}) +``` + +## Vue + diff --git a/docs/src/links.yaml b/docs/src/links.yaml index 0afecb59..9c7b1c0d 100644 --- a/docs/src/links.yaml +++ b/docs/src/links.yaml @@ -195,6 +195,8 @@ # - title: Annotation # link: /api/extensions/annotation # type: draft + - title: BubbleMenu + link: /api/extensions/bubble-menu - title: CharacterCount link: /api/extensions/character-count - title: Collaboration diff --git a/packages/core/src/Editor.ts b/packages/core/src/Editor.ts index dbc818a5..ee08562b 100644 --- a/packages/core/src/Editor.ts +++ b/packages/core/src/Editor.ts @@ -1,4 +1,6 @@ -import { EditorState, Plugin, Transaction } from 'prosemirror-state' +import { + EditorState, Plugin, PluginKey, Transaction, +} from 'prosemirror-state' import { EditorView } from 'prosemirror-view' import { Schema, DOMParser, Node } from 'prosemirror-model' import elementFromString from './utilities/elementFromString' @@ -172,10 +174,19 @@ export class Editor extends EventEmitter { * * @param name The plugins name */ - public unregisterPlugin(name: string): void { + public unregisterPlugin(nameOrPluginKey: string | PluginKey): void { + if (this.isDestroyed) { + return + } + + const name = typeof nameOrPluginKey === 'string' + ? `${nameOrPluginKey}$` + // @ts-ignore + : nameOrPluginKey.key + const state = this.state.reconfigure({ // @ts-ignore - plugins: this.state.plugins.filter(plugin => !plugin.key.startsWith(`${name}$`)), + plugins: this.state.plugins.filter(plugin => !plugin.key.startsWith(name)), }) this.view.updateState(state) diff --git a/packages/extension-bubble-menu/src/bubble-menu-plugin.ts b/packages/extension-bubble-menu/src/bubble-menu-plugin.ts index 0768b521..24d78c7d 100644 --- a/packages/extension-bubble-menu/src/bubble-menu-plugin.ts +++ b/packages/extension-bubble-menu/src/bubble-menu-plugin.ts @@ -3,13 +3,13 @@ import { EditorState, Plugin, PluginKey } from 'prosemirror-state' import { EditorView } from 'prosemirror-view' import { coordsAtPos } from './helpers' -export interface BubbleMenuPluginOptions { +export interface BubbleMenuPluginProps { editor: Editor, element: HTMLElement, keepInBounds: boolean, } -export type BubbleMenuViewOptions = BubbleMenuPluginOptions & { +export type BubbleMenuViewOptions = BubbleMenuPluginProps & { view: EditorView, } @@ -137,7 +137,7 @@ export class BubbleMenuView { export const BubbleMenuPluginKey = new PluginKey('menuBubble') -export const BubbleMenuPlugin = (options: BubbleMenuPluginOptions) => { +export const BubbleMenuPlugin = (options: BubbleMenuPluginProps) => { return new Plugin({ key: BubbleMenuPluginKey, view: view => new BubbleMenuView({ view, ...options }), diff --git a/packages/extension-bubble-menu/src/bubble-menu.ts b/packages/extension-bubble-menu/src/bubble-menu.ts index b0948c31..49faba65 100644 --- a/packages/extension-bubble-menu/src/bubble-menu.ts +++ b/packages/extension-bubble-menu/src/bubble-menu.ts @@ -1,7 +1,7 @@ import { Extension } from '@tiptap/core' -import { BubbleMenuPlugin, BubbleMenuPluginOptions } from './bubble-menu-plugin' +import { BubbleMenuPlugin, BubbleMenuPluginProps } from './bubble-menu-plugin' -export type BubbleMenuOptions = Omit +export type BubbleMenuOptions = Omit export const BubbleMenu = Extension.create({ name: 'bubbleMenu', diff --git a/packages/extension-bubble-menu/src/index.ts b/packages/extension-bubble-menu/src/index.ts index 71fe7f0d..df47407f 100644 --- a/packages/extension-bubble-menu/src/index.ts +++ b/packages/extension-bubble-menu/src/index.ts @@ -1,5 +1,6 @@ import { BubbleMenu } from './bubble-menu' export * from './bubble-menu' +export * from './bubble-menu-plugin' export default BubbleMenu diff --git a/packages/vue-2/package.json b/packages/vue-2/package.json index 3a3740a8..a067ef15 100644 --- a/packages/vue-2/package.json +++ b/packages/vue-2/package.json @@ -26,6 +26,7 @@ "vue": "^2.6.12" }, "dependencies": { + "@tiptap/extension-bubble-menu": "^2.0.0-beta.1", "prosemirror-view": "^1.18.2" } } diff --git a/packages/vue-2/src/BubbleMenu.ts b/packages/vue-2/src/BubbleMenu.ts new file mode 100644 index 00000000..f442b627 --- /dev/null +++ b/packages/vue-2/src/BubbleMenu.ts @@ -0,0 +1,45 @@ +import Vue, { PropType } from 'vue' +import { BubbleMenuPlugin, BubbleMenuPluginKey, BubbleMenuPluginProps } from '@tiptap/extension-bubble-menu' + +export const BubbleMenu = Vue.extend({ + name: 'BubbleMenu', + + props: { + editor: { + type: Object as PropType, + required: true, + }, + + keepInBounds: { + type: Boolean as PropType, + default: true, + }, + }, + + watch: { + editor: { + immediate: true, + handler(editor: BubbleMenuPluginProps['editor']) { + if (!editor) { + return + } + + this.$nextTick(() => { + editor.registerPlugin(BubbleMenuPlugin({ + editor, + element: this.$el as HTMLElement, + keepInBounds: this.keepInBounds, + })) + }) + }, + }, + }, + + render(createElement) { + return createElement('div', {}, this.$slots.default) + }, + + beforeDestroy() { + this.editor.unregisterPlugin(BubbleMenuPluginKey) + }, +}) diff --git a/packages/vue-2/src/index.ts b/packages/vue-2/src/index.ts index 9dbf56fb..4233fd98 100644 --- a/packages/vue-2/src/index.ts +++ b/packages/vue-2/src/index.ts @@ -1,4 +1,5 @@ export * from '@tiptap/core' +export * from './BubbleMenu' export { Editor } from './Editor' export * from './EditorContent' export * from './VueRenderer'