add NodeViewWrapper and NodeViewContent component

This commit is contained in:
Philipp Kühn
2021-03-05 00:02:28 +01:00
parent a997b647f4
commit 3bf900e7f6
5 changed files with 89 additions and 75 deletions

View File

@@ -64,7 +64,7 @@ export class Editor extends EventEmitter {
constructor(options: Partial<EditorOptions> = {}) { constructor(options: Partial<EditorOptions> = {}) {
super() super()
this.options = { ...this.options, ...options } this.setOptions(options)
this.createExtensionManager() this.createExtensionManager()
this.createCommandManager() this.createCommandManager()
this.createSchema() this.createSchema()
@@ -121,6 +121,10 @@ export class Editor extends EventEmitter {
*/ */
public setOptions(options: Partial<EditorOptions> = {}): void { public setOptions(options: Partial<EditorOptions> = {}): void {
this.options = { ...this.options, ...options } this.options = { ...this.options, ...options }
}
public setEditable(editable: boolean): void {
this.setOptions({ editable })
if (this.view && this.state && !this.isDestroyed) { if (this.view && this.state && !this.isDestroyed) {
this.view.updateState(this.state) this.view.updateState(this.state)
@@ -212,7 +216,14 @@ export class Editor extends EventEmitter {
// `editor.view` is not yet available at this time. // `editor.view` is not yet available at this time.
// Therefore we will add all plugins and node views directly afterwards. // Therefore we will add all plugins and node views directly afterwards.
const newState = this.state.reconfigure({ const newState = this.state.reconfigure({
plugins: this.extensionManager.plugins, plugins: [
new Plugin({
view: () => ({
update: () => this.emit('viewUpdate'),
}),
}),
...this.extensionManager.plugins,
],
}) })
this.view.updateState(newState) this.view.updateState(newState)
@@ -410,7 +421,7 @@ export class Editor extends EventEmitter {
/** /**
* Check if the editor is already destroyed. * Check if the editor is already destroyed.
*/ */
private get isDestroyed(): boolean { public get isDestroyed(): boolean {
// @ts-ignore // @ts-ignore
return !this.view?.docView return !this.view?.docView
} }

View File

@@ -0,0 +1,25 @@
import { h, defineComponent } from 'vue'
export const NodeViewContent = defineComponent({
props: {
as: {
type: String,
default: 'div',
},
},
inject: ['editable'],
render() {
return h(
this.as, {
style: {
whiteSpace: 'pre-wrap',
},
'data-node-view-content': '',
// @ts-ignore (https://github.com/vuejs/vue-next/issues/3031)
contenteditable: this.editable.value,
},
)
},
})

View File

@@ -0,0 +1,26 @@
import { h, defineComponent } from 'vue'
export const NodeViewWrapper = defineComponent({
props: {
as: {
type: String,
default: 'div',
},
},
inject: ['onDragStart'],
render() {
return h(
this.as, {
style: {
whiteSpace: 'normal',
},
'data-node-view-wrapper': '',
// @ts-ignore
onDragStart: this.onDragStart,
},
this.$slots.default?.(),
)
},
})

View File

@@ -4,8 +4,8 @@ import {
NodeViewRendererProps, NodeViewRendererProps,
} from '@tiptap/core' } from '@tiptap/core'
import { import {
h, ref,
markRaw, provide,
Component, Component,
defineComponent, defineComponent,
} from 'vue' } from 'vue'
@@ -32,8 +32,6 @@ class VueNodeView implements NodeView {
decorations!: Decoration[] decorations!: Decoration[]
id!: string
getPos!: any getPos!: any
isDragging = false isDragging = false
@@ -49,40 +47,10 @@ class VueNodeView implements NodeView {
this.extension = props.extension this.extension = props.extension
this.node = props.node this.node = props.node
this.getPos = props.getPos this.getPos = props.getPos
this.createUniqueId()
this.mount(component) this.mount(component)
} }
createUniqueId() { onDragStart(event: DragEvent) {
this.id = `id_${Math.floor(Math.random() * 0xFFFFFFFF)}`
}
createNodeViewWrapper() {
const { handleDragStart } = this
const dragstart = handleDragStart.bind(this)
return markRaw(defineComponent({
props: {
as: {
type: String,
default: 'div',
},
},
render() {
return h(
this.as, {
style: {
whiteSpace: 'normal',
},
onDragStart: dragstart,
},
this.$slots.default?.(),
)
},
}))
}
handleDragStart(event: DragEvent) {
const { view } = this.editor const { view } = this.editor
const target = (event.target as HTMLElement) const target = (event.target as HTMLElement)
@@ -99,39 +67,8 @@ class VueNodeView implements NodeView {
view.dispatch(transaction) view.dispatch(transaction)
} }
createNodeViewContent() {
const { id } = this
const { isEditable } = this.editor
return markRaw(defineComponent({
inheritAttrs: false,
props: {
as: {
type: String,
default: 'div',
},
},
render() {
return h(
this.as, {
style: {
whiteSpace: 'pre-wrap',
},
id,
contenteditable: isEditable,
},
)
},
}))
}
mount(component: Component) { mount(component: Component) {
const NodeViewWrapper = this.createNodeViewWrapper()
const NodeViewContent = this.createNodeViewContent()
const props = { const props = {
NodeViewWrapper,
NodeViewContent,
editor: this.editor, editor: this.editor,
node: this.node, node: this.node,
decorations: this.decorations, decorations: this.decorations,
@@ -141,12 +78,21 @@ class VueNodeView implements NodeView {
updateAttributes: (attributes = {}) => this.updateAttributes(attributes), updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
} }
const onDragStart = this.onDragStart.bind(this)
const isEditable = ref(this.editor.isEditable)
this.editor.on('viewUpdate', () => {
isEditable.value = this.editor.isEditable
})
const extendedComponent = defineComponent({ const extendedComponent = defineComponent({
extends: { ...component }, extends: { ...component },
props: Object.keys(props), props: Object.keys(props),
components: { setup() {
NodeViewWrapper, provide('onDragStart', onDragStart)
NodeViewContent, provide('editable', isEditable)
return (component as any).setup?.()
}, },
}) })
@@ -161,11 +107,15 @@ class VueNodeView implements NodeView {
} }
get contentDOM() { get contentDOM() {
if (this.dom.id === this.id) { const hasContent = !this.node.type.isAtom
return this.dom
if (!hasContent) {
return null
} }
return this.dom.querySelector(`#${this.id}`) const contentElement = this.dom.querySelector('[data-node-view-content]')
return contentElement || this.dom
} }
stopEvent(event: Event) { stopEvent(event: Event) {

View File

@@ -4,3 +4,5 @@ export * from './EditorContent'
export * from './useEditor' export * from './useEditor'
export * from './VueRenderer' export * from './VueRenderer'
export * from './VueNodeViewRenderer' export * from './VueNodeViewRenderer'
export * from './NodeViewWrapper'
export * from './NodeViewContent'