add renderless MenuBubble
This commit is contained in:
@@ -95,6 +95,11 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity 0.2s, visibility 0.2s;
|
transition: opacity 0.2s, visibility 0.2s;
|
||||||
|
|
||||||
|
&.is-active {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
&__button {
|
&__button {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
background: transparent;
|
background: transparent;
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="editor">
|
<div class="editor">
|
||||||
<menu-bubble class="menububble" :editor="editor">
|
<menu-bubble :editor="editor">
|
||||||
<template slot-scope="{ commands, isActive }">
|
<div
|
||||||
|
slot-scope="{ commands, isActive, menu }"
|
||||||
|
class="menububble"
|
||||||
|
:class="{ 'is-active': menu.isActive }"
|
||||||
|
:style="`left: ${menu.left}px; bottom: ${menu.bottom}px;`"
|
||||||
|
>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="menububble__button"
|
class="menububble__button"
|
||||||
@@ -27,7 +32,7 @@
|
|||||||
<icon name="code" />
|
<icon name="code" />
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</template>
|
</div>
|
||||||
</menu-bubble>
|
</menu-bubble>
|
||||||
|
|
||||||
<editor-content class="editor__content" :editor="editor" />
|
<editor-content class="editor__content" :editor="editor" />
|
||||||
|
|||||||
@@ -7,29 +7,44 @@ export default {
|
|||||||
type: Object,
|
type: Object,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
menu: {
|
||||||
|
isActive: false,
|
||||||
|
left: 0,
|
||||||
|
bottom: 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
editor: {
|
editor: {
|
||||||
immediate: true,
|
immediate: true,
|
||||||
handler(editor) {
|
handler(editor) {
|
||||||
if (editor) {
|
if (editor) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
editor.registerPlugin(MenuBubble(this.$el))
|
editor.registerPlugin(MenuBubble({
|
||||||
|
element: this.$el,
|
||||||
|
onUpdate: menu => {
|
||||||
|
this.menu = menu
|
||||||
|
},
|
||||||
|
}))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
render(createElement) {
|
render() {
|
||||||
if (!this.editor) {
|
if (!this.editor) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return createElement('div', this.$scopedSlots.default({
|
return this.$scopedSlots.default({
|
||||||
focused: this.editor.view.focused,
|
focused: this.editor.view.focused,
|
||||||
focus: this.editor.focus,
|
focus: this.editor.focus,
|
||||||
commands: this.editor.commands,
|
commands: this.editor.commands,
|
||||||
isActive: this.editor.isActive.bind(this.editor),
|
isActive: this.editor.isActive.bind(this.editor),
|
||||||
markAttrs: this.editor.markAttrs.bind(this.editor),
|
markAttrs: this.editor.markAttrs.bind(this.editor),
|
||||||
}))
|
menu: this.menu,
|
||||||
|
})
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,19 @@
|
|||||||
import { Plugin } from 'prosemirror-state'
|
import { Plugin } from 'prosemirror-state'
|
||||||
|
|
||||||
class Toolbar {
|
class Menu {
|
||||||
|
|
||||||
constructor({ element, editorView }) {
|
constructor({ options, editorView }) {
|
||||||
|
this.options = {
|
||||||
|
...{
|
||||||
|
element: null,
|
||||||
|
onUpdate: () => false,
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
}
|
||||||
this.editorView = editorView
|
this.editorView = editorView
|
||||||
this.element = element
|
this.isActive = false
|
||||||
this.element.style.visibility = 'hidden'
|
this.left = 0
|
||||||
this.element.style.opacity = 0
|
this.bottom = 0
|
||||||
|
|
||||||
this.editorView.dom.addEventListener('blur', this.hide.bind(this))
|
this.editorView.dom.addEventListener('blur', this.hide.bind(this))
|
||||||
}
|
}
|
||||||
@@ -26,7 +33,6 @@ class Toolbar {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Otherwise, reposition it and update its content
|
// Otherwise, reposition it and update its content
|
||||||
this.show()
|
|
||||||
const { from, to } = state.selection
|
const { from, to } = state.selection
|
||||||
|
|
||||||
// These are in screen coordinates
|
// These are in screen coordinates
|
||||||
@@ -34,18 +40,25 @@ class Toolbar {
|
|||||||
const end = view.coordsAtPos(to)
|
const end = view.coordsAtPos(to)
|
||||||
|
|
||||||
// The box in which the tooltip is positioned, to use as base
|
// The box in which the tooltip is positioned, to use as base
|
||||||
const box = this.element.offsetParent.getBoundingClientRect()
|
const box = this.options.element.offsetParent.getBoundingClientRect()
|
||||||
|
|
||||||
// Find a center-ish x position from the selection endpoints (when
|
// Find a center-ish x position from the selection endpoints (when
|
||||||
// crossing lines, end may be more to the left)
|
// crossing lines, end may be more to the left)
|
||||||
const left = Math.max((start.left + end.left) / 2, start.left + 3)
|
const left = Math.max((start.left + end.left) / 2, start.left + 3)
|
||||||
this.element.style.left = `${left - box.left}px`
|
|
||||||
this.element.style.bottom = `${box.bottom - start.top}px`
|
this.isActive = true
|
||||||
|
this.left = parseInt(left - box.left, 10)
|
||||||
|
this.bottom = parseInt(box.bottom - start.top, 10)
|
||||||
|
|
||||||
|
this.sendUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
sendUpdate() {
|
||||||
this.element.style.visibility = 'visible'
|
this.options.onUpdate({
|
||||||
this.element.style.opacity = 1
|
isActive: this.isActive,
|
||||||
|
left: this.left,
|
||||||
|
bottom: this.bottom,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
hide(event) {
|
hide(event) {
|
||||||
@@ -53,8 +66,8 @@ class Toolbar {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
this.element.style.visibility = 'hidden'
|
this.isActive = false
|
||||||
this.element.style.opacity = 0
|
this.sendUpdate()
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy() {
|
destroy() {
|
||||||
@@ -63,10 +76,10 @@ class Toolbar {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function (element) {
|
export default function (options) {
|
||||||
return new Plugin({
|
return new Plugin({
|
||||||
view(editorView) {
|
view(editorView) {
|
||||||
return new Toolbar({ editorView, element })
|
return new Menu({ editorView, options })
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user