add NodeViewWrapper and NodeViewContent component
This commit is contained in:
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
25
packages/vue-3/src/NodeViewContent.ts
Normal file
25
packages/vue-3/src/NodeViewContent.ts
Normal 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,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
26
packages/vue-3/src/NodeViewWrapper.ts
Normal file
26
packages/vue-3/src/NodeViewWrapper.ts
Normal 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?.(),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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'
|
||||||
|
|||||||
Reference in New Issue
Block a user