almost fix floating menu example

This commit is contained in:
Philipp Kühn
2018-10-23 23:44:48 +02:00
parent 804b44b146
commit 303e2f6d9c
5 changed files with 191 additions and 86 deletions

View File

@@ -1,85 +1,74 @@
<template> <template>
<div> <div class="editor">
<editor class="editor" :extensions="extensions"> <floating-menu class="editor__floating-menu" :editor="editor">
<template slot-scope="{ nodes, marks }">
<div class="editor__floating-menu" slot="floatingMenu" slot-scope="{ nodes }"> <button
<template v-if="nodes"> class="menubar__button"
:class="{ 'is-active': nodes.heading.active({ level: 1 }) }"
@click="nodes.heading.command({ level: 1 })"
>
H1
</button>
<button <button
class="menubar__button" class="menubar__button"
:class="{ 'is-active': nodes.heading.active({ level: 1 }) }" :class="{ 'is-active': nodes.heading.active({ level: 2 }) }"
@click="nodes.heading.command({ level: 1 })" @click="nodes.heading.command({ level: 2 })"
> >
H1 H2
</button> </button>
<button <button
class="menubar__button" class="menubar__button"
:class="{ 'is-active': nodes.heading.active({ level: 2 }) }" :class="{ 'is-active': nodes.heading.active({ level: 3 }) }"
@click="nodes.heading.command({ level: 2 })" @click="nodes.heading.command({ level: 3 })"
> >
H2 H3
</button> </button>
<button <button
class="menubar__button" class="menubar__button"
:class="{ 'is-active': nodes.heading.active({ level: 3 }) }" :class="{ 'is-active': nodes.bullet_list.active() }"
@click="nodes.heading.command({ level: 3 })" @click="nodes.bullet_list.command"
> >
H3 <icon name="ul" />
</button> </button>
<button <button
class="menubar__button" class="menubar__button"
:class="{ 'is-active': nodes.bullet_list.active() }" :class="{ 'is-active': nodes.ordered_list.active() }"
@click="nodes.bullet_list.command" @click="nodes.ordered_list.command"
> >
<icon name="ul" /> <icon name="ol" />
</button> </button>
<button <button
class="menubar__button" class="menubar__button"
:class="{ 'is-active': nodes.ordered_list.active() }" :class="{ 'is-active': nodes.blockquote.active() }"
@click="nodes.ordered_list.command" @click="nodes.blockquote.command"
> >
<icon name="ol" /> <icon name="quote" />
</button> </button>
<button <button
class="menubar__button" class="menubar__button"
:class="{ 'is-active': nodes.blockquote.active() }" :class="{ 'is-active': nodes.code_block.active() }"
@click="nodes.blockquote.command" @click="nodes.code_block.command"
> >
<icon name="quote" /> <icon name="code" />
</button> </button>
<button </template>
class="menubar__button" </floating-menu>
:class="{ 'is-active': nodes.code_block.active() }"
@click="nodes.code_block.command"
>
<icon name="code" />
</button>
</template> <editor-content class="editor__content" :editor="editor" />
</div>
<div class="editor__content" slot="content" slot-scope="props">
<h2>
Floating Menu
</h2>
<p>
This is an example of a medium-like editor. Enter a new line and some buttons will appear.
</p>
</div>
</editor>
</div> </div>
</template> </template>
<script> <script>
import Icon from 'Components/Icon' import Icon from 'Components/Icon'
import { Editor } from 'tiptap' import { Editor, EditorContent, FloatingMenu } from 'tiptap'
import { import {
BlockquoteNode, BlockquoteNode,
BulletListNode, BulletListNode,
@@ -99,27 +88,38 @@ import {
export default { export default {
components: { components: {
Editor, EditorContent,
FloatingMenu,
Icon, Icon,
}, },
data() { data() {
return { return {
extensions: [ editor: new Editor({
new BlockquoteNode(), extensions: [
new BulletListNode(), new BlockquoteNode(),
new CodeBlockNode(), new BulletListNode(),
new HardBreakNode(), new CodeBlockNode(),
new HeadingNode({ maxLevel: 3 }), new HardBreakNode(),
new ListItemNode(), new HeadingNode({ maxLevel: 3 }),
new OrderedListNode(), new ListItemNode(),
new TodoItemNode(), new OrderedListNode(),
new TodoListNode(), new TodoItemNode(),
new BoldMark(), new TodoListNode(),
new CodeMark(), new BoldMark(),
new ItalicMark(), new CodeMark(),
new LinkMark(), new ItalicMark(),
new HistoryExtension(), new LinkMark(),
], new HistoryExtension(),
],
content: `
<h2>
Floating Menu
</h2>
<p>
This is an example of a medium-like editor. Enter a new line and some buttons will appear.
</p>
`,
}),
} }
}, },
} }

View File

@@ -0,0 +1,32 @@
import FloatingMenu from '../Utils/FloatingMenu'
export default {
props: {
editor: {
default: null,
type: Object,
},
},
watch: {
editor: {
immediate: true,
handler(editor) {
if (editor) {
this.$nextTick(() => {
editor.registerPlugin(FloatingMenu(this.$el))
})
}
},
}
},
render(createElement) {
if (this.editor) {
return createElement('div', this.$scopedSlots.default({
nodes: this.editor.menuActions.nodes,
marks: this.editor.menuActions.marks,
focused: this.editor.view.focused,
focus: this.editor.focus,
}))
}
},
}

View File

@@ -2,6 +2,7 @@ export { default as Editor } from './Utils/Editor'
export { default as EditorContent } from './Components/EditorContent' export { default as EditorContent } from './Components/EditorContent'
export { default as MenuBar } from './Components/MenuBar' export { default as MenuBar } from './Components/MenuBar'
export { default as MenuBubble } from './Components/MenuBubble' export { default as MenuBubble } from './Components/MenuBubble'
export { default as FloatingMenu } from './Components/FloatingMenu'
export { default as Extension } from './Utils/Extension' export { default as Extension } from './Utils/Extension'
export { default as Node } from './Utils/Node' export { default as Node } from './Utils/Node'

View File

@@ -0,0 +1,72 @@
import { Plugin } from 'prosemirror-state'
class Toolbar {
constructor({ element, editorView }) {
this.editorView = editorView
this.element = element
this.element.style.visibility = 'hidden'
this.element.style.opacity = 0
this.editorView.dom.addEventListener('blur', this.hide.bind(this))
}
update(view, lastState) {
const { state } = view
// Don't do anything if the document/selection didn't change
if (lastState && lastState.doc.eq(state.doc) && lastState.selection.eq(state.selection)) {
return
}
if (!state.selection.empty) {
this.hide()
return
}
const currentDom = view.domAtPos(state.selection.$anchor.pos)
const isActive = currentDom.node.innerHTML === '<br>'
&& currentDom.node.tagName === 'P'
// && currentDom.node.parentNode === view.dom
if (!isActive) {
this.hide()
return
}
const editorBoundings = this.element.offsetParent.getBoundingClientRect()
const cursorBoundings = view.coordsAtPos(state.selection.$anchor.pos)
const top = cursorBoundings.top - editorBoundings.top
this.element.style.top = `${top}px`
this.show()
}
show() {
this.element.style.visibility = 'visible'
this.element.style.opacity = 1
}
hide(event) {
if (event && event.relatedTarget) {
return
}
this.element.style.visibility = 'hidden'
this.element.style.opacity = 0
}
destroy() {
this.editorView.dom.removeEventListener('blur', this.hide)
}
}
export default function (element) {
return new Plugin({
view(editorView) {
return new Toolbar({ editorView, element })
},
})
}

View File

@@ -2,6 +2,6 @@ export { default as buildMenuActions } from './buildMenuActions'
export { default as builtInKeymap } from './builtInKeymap' export { default as builtInKeymap } from './builtInKeymap'
export { default as ComponentView } from './ComponentView' export { default as ComponentView } from './ComponentView'
export { default as initNodeViews } from './initNodeViews' export { default as initNodeViews } from './initNodeViews'
// export { default as floatingMenu } from './floatingMenu' export { default as FloatingMenu } from './FloatingMenu'
// export { default as menuBubble } from './menuBubble' export { default as MenuBubble } from './MenuBubble'
export { default as ExtensionManager } from './ExtensionManager' export { default as ExtensionManager } from './ExtensionManager'