add doc page for bubble menu
This commit is contained in:
@@ -1,24 +1,28 @@
|
|||||||
<template>
|
<template>
|
||||||
<div style="position: relative">
|
<div style="position: relative">
|
||||||
<div ref="menu">
|
<bubble-menu :editor="editor" v-if="editor">
|
||||||
<div v-if="editor">
|
<button @click="editor.chain().focus().toggleBold().run()" :class="{ 'is-active': editor.isActive('bold') }">
|
||||||
<button @click="editor.chain().focus().toggleBold().run()" :class="{ 'is-active': editor.isActive('bold') }">
|
bold
|
||||||
bold
|
</button>
|
||||||
</button>
|
<button @click="editor.chain().focus().toggleItalic().run()" :class="{ 'is-active': editor.isActive('italic') }">
|
||||||
</div>
|
italic
|
||||||
</div>
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleCode().run()" :class="{ 'is-active': editor.isActive('code') }">
|
||||||
|
code
|
||||||
|
</button>
|
||||||
|
</bubble-menu>
|
||||||
<editor-content :editor="editor" />
|
<editor-content :editor="editor" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { Editor, EditorContent } from '@tiptap/vue-2'
|
import { Editor, EditorContent, BubbleMenu } from '@tiptap/vue-2'
|
||||||
import { defaultExtensions } from '@tiptap/starter-kit'
|
import { defaultExtensions } from '@tiptap/starter-kit'
|
||||||
import BubbleMenu from '@tiptap/extension-bubble-menu'
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
EditorContent,
|
EditorContent,
|
||||||
|
BubbleMenu,
|
||||||
},
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
@@ -31,19 +35,10 @@ export default {
|
|||||||
this.editor = new Editor({
|
this.editor = new Editor({
|
||||||
extensions: [
|
extensions: [
|
||||||
...defaultExtensions(),
|
...defaultExtensions(),
|
||||||
BubbleMenu.configure({
|
|
||||||
element: this.$refs.menu,
|
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
content: `
|
content: `
|
||||||
<p>
|
<p>
|
||||||
paragraph
|
Hey, try to select some text here. There will popup a menu for selecting some inline styles. Remember: you have full control about content and styling of this menu.
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
paragraph
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
paragraph
|
|
||||||
</p>
|
</p>
|
||||||
`,
|
`,
|
||||||
})
|
})
|
||||||
@@ -56,30 +51,10 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.has-focus {
|
|
||||||
border-radius: 3px;
|
|
||||||
box-shadow: 0 0 0 3px #68CEF8;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Basic editor styles */
|
/* Basic editor styles */
|
||||||
.ProseMirror {
|
.ProseMirror {
|
||||||
> * + * {
|
> * + * {
|
||||||
margin-top: 0.75em;
|
margin-top: 0.75em;
|
||||||
}
|
}
|
||||||
|
|
||||||
ul,
|
|
||||||
ol {
|
|
||||||
padding: 0 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote {
|
|
||||||
padding-left: 1rem;
|
|
||||||
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
background-color: rgba(#616161, 0.1);
|
|
||||||
color: #616161;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
39
docs/src/docPages/api/extensions/bubble-menu.md
Normal file
39
docs/src/docPages/api/extensions/bubble-menu.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Bubble Menu
|
||||||
|
[](https://www.npmjs.com/package/@tiptap/extension-bubble-menu)
|
||||||
|
[](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
|
||||||
|
<demo name="Extensions/BubbleMenu" />
|
||||||
@@ -195,6 +195,8 @@
|
|||||||
# - title: Annotation
|
# - title: Annotation
|
||||||
# link: /api/extensions/annotation
|
# link: /api/extensions/annotation
|
||||||
# type: draft
|
# type: draft
|
||||||
|
- title: BubbleMenu
|
||||||
|
link: /api/extensions/bubble-menu
|
||||||
- title: CharacterCount
|
- title: CharacterCount
|
||||||
link: /api/extensions/character-count
|
link: /api/extensions/character-count
|
||||||
- title: Collaboration
|
- title: Collaboration
|
||||||
|
|||||||
@@ -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 { EditorView } from 'prosemirror-view'
|
||||||
import { Schema, DOMParser, Node } from 'prosemirror-model'
|
import { Schema, DOMParser, Node } from 'prosemirror-model'
|
||||||
import elementFromString from './utilities/elementFromString'
|
import elementFromString from './utilities/elementFromString'
|
||||||
@@ -172,10 +174,19 @@ export class Editor extends EventEmitter {
|
|||||||
*
|
*
|
||||||
* @param name The plugins name
|
* @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({
|
const state = this.state.reconfigure({
|
||||||
// @ts-ignore
|
// @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)
|
this.view.updateState(state)
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ import { EditorState, Plugin, PluginKey } from 'prosemirror-state'
|
|||||||
import { EditorView } from 'prosemirror-view'
|
import { EditorView } from 'prosemirror-view'
|
||||||
import { coordsAtPos } from './helpers'
|
import { coordsAtPos } from './helpers'
|
||||||
|
|
||||||
export interface BubbleMenuPluginOptions {
|
export interface BubbleMenuPluginProps {
|
||||||
editor: Editor,
|
editor: Editor,
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
keepInBounds: boolean,
|
keepInBounds: boolean,
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BubbleMenuViewOptions = BubbleMenuPluginOptions & {
|
export type BubbleMenuViewOptions = BubbleMenuPluginProps & {
|
||||||
view: EditorView,
|
view: EditorView,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,7 +137,7 @@ export class BubbleMenuView {
|
|||||||
|
|
||||||
export const BubbleMenuPluginKey = new PluginKey('menuBubble')
|
export const BubbleMenuPluginKey = new PluginKey('menuBubble')
|
||||||
|
|
||||||
export const BubbleMenuPlugin = (options: BubbleMenuPluginOptions) => {
|
export const BubbleMenuPlugin = (options: BubbleMenuPluginProps) => {
|
||||||
return new Plugin({
|
return new Plugin({
|
||||||
key: BubbleMenuPluginKey,
|
key: BubbleMenuPluginKey,
|
||||||
view: view => new BubbleMenuView({ view, ...options }),
|
view: view => new BubbleMenuView({ view, ...options }),
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Extension } from '@tiptap/core'
|
import { Extension } from '@tiptap/core'
|
||||||
import { BubbleMenuPlugin, BubbleMenuPluginOptions } from './bubble-menu-plugin'
|
import { BubbleMenuPlugin, BubbleMenuPluginProps } from './bubble-menu-plugin'
|
||||||
|
|
||||||
export type BubbleMenuOptions = Omit<BubbleMenuPluginOptions, 'editor'>
|
export type BubbleMenuOptions = Omit<BubbleMenuPluginProps, 'editor'>
|
||||||
|
|
||||||
export const BubbleMenu = Extension.create<BubbleMenuOptions>({
|
export const BubbleMenu = Extension.create<BubbleMenuOptions>({
|
||||||
name: 'bubbleMenu',
|
name: 'bubbleMenu',
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { BubbleMenu } from './bubble-menu'
|
import { BubbleMenu } from './bubble-menu'
|
||||||
|
|
||||||
export * from './bubble-menu'
|
export * from './bubble-menu'
|
||||||
|
export * from './bubble-menu-plugin'
|
||||||
|
|
||||||
export default BubbleMenu
|
export default BubbleMenu
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
"vue": "^2.6.12"
|
"vue": "^2.6.12"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@tiptap/extension-bubble-menu": "^2.0.0-beta.1",
|
||||||
"prosemirror-view": "^1.18.2"
|
"prosemirror-view": "^1.18.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
45
packages/vue-2/src/BubbleMenu.ts
Normal file
45
packages/vue-2/src/BubbleMenu.ts
Normal file
@@ -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<BubbleMenuPluginProps['editor']>,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
keepInBounds: {
|
||||||
|
type: Boolean as PropType<BubbleMenuPluginProps['keepInBounds']>,
|
||||||
|
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)
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
export * from '@tiptap/core'
|
export * from '@tiptap/core'
|
||||||
|
export * from './BubbleMenu'
|
||||||
export { Editor } from './Editor'
|
export { Editor } from './Editor'
|
||||||
export * from './EditorContent'
|
export * from './EditorContent'
|
||||||
export * from './VueRenderer'
|
export * from './VueRenderer'
|
||||||
|
|||||||
Reference in New Issue
Block a user