add NodeViewWrapper and NodeViewContent to vue-2

This commit is contained in:
Philipp Kühn
2021-03-05 10:18:44 +01:00
parent 27b55a2c53
commit 983b56a5f2
10 changed files with 127 additions and 80 deletions

View File

@@ -26,6 +26,7 @@
</template> </template>
<script> <script>
import { NodeViewWrapper } from '@tiptap/vue-2'
import { v4 as uuid } from 'uuid' import { v4 as uuid } from 'uuid'
import * as d3 from 'd3' import * as d3 from 'd3'
import simplify from 'simplify-js' import simplify from 'simplify-js'
@@ -37,6 +38,10 @@ const getRandomElement = list => {
export default { export default {
name: 'Paper', name: 'Paper',
components: {
NodeViewWrapper,
},
props: { props: {
updateAttributes: { updateAttributes: {
type: Function, type: Function,

View File

@@ -10,6 +10,17 @@
</node-view-wrapper> </node-view-wrapper>
</template> </template>
<script>
import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2'
export default {
components: {
NodeViewWrapper,
NodeViewContent,
},
}
</script>
<style lang="scss" scoped> <style lang="scss" scoped>
.draggable-item { .draggable-item {
display: flex; display: flex;

View File

@@ -14,7 +14,13 @@
</template> </template>
<script> <script>
import { NodeViewWrapper } from '@tiptap/vue-2'
export default { export default {
components: {
NodeViewWrapper,
},
props: { props: {
editor: { editor: {
type: Object, type: Object,

View File

@@ -97,13 +97,17 @@ export default Node.create({
```html ```html
<template> <template>
<node-view-wrapper> <node-view-wrapper />
<node-view-content />
</node-view-wrapper>
</template> </template>
<script> <script>
import { NodeViewWrapper } from '@tiptap/vue-2'
export default { export default {
components: {
NodeViewWrapper,
},
props: { props: {
editor: { editor: {
type: Object, type: Object,
@@ -145,6 +149,16 @@ export default {
<node-view-content class="content-dom" /> <node-view-content class="content-dom" />
</node-view-wrapper> </node-view-wrapper>
</template> </template>
<script>
import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2'
export default {
components: {
NodeViewWrapper,
NodeViewContent,
},
}
``` ```
## Reference ## Reference

View File

@@ -0,0 +1,27 @@
import Vue from 'vue'
export const NodeViewContent = Vue.extend({
props: {
as: {
type: String,
default: 'div',
},
},
inject: ['isEditable'],
render(createElement) {
return createElement(
this.as, {
style: {
whiteSpace: 'pre-wrap',
},
attrs: {
'data-node-view-content': '',
// @ts-ignore
contenteditable: this.isEditable.value,
},
},
)
},
})

View File

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

View File

@@ -34,8 +34,6 @@ class VueNodeView implements NodeView {
decorations!: Decoration[] decorations!: Decoration[]
id!: string
getPos!: any getPos!: any
isDragging = false isDragging = false
@@ -51,42 +49,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 Vue.extend({
props: {
as: {
type: String,
default: 'div',
},
},
render(createElement) {
return createElement(
this.as, {
style: {
whiteSpace: 'normal',
},
on: {
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)
@@ -103,41 +69,8 @@ class VueNodeView implements NodeView {
view.dispatch(transaction) view.dispatch(transaction)
} }
createNodeViewContent() {
const { id } = this
const { isEditable } = this.editor
return Vue.extend({
inheritAttrs: false,
props: {
as: {
type: String,
default: 'div',
},
},
render(createElement) {
return createElement(
this.as, {
style: {
whiteSpace: 'pre-wrap',
},
domProps: {
id,
contenteditable: isEditable,
},
},
)
},
})
}
mount(component: Vue | VueConstructor) { mount(component: Vue | VueConstructor) {
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,
@@ -147,13 +80,24 @@ class VueNodeView implements NodeView {
updateAttributes: (attributes = {}) => this.updateAttributes(attributes), updateAttributes: (attributes = {}) => this.updateAttributes(attributes),
} }
const onDragStart = this.onDragStart.bind(this)
const isEditable = Vue.observable({
value: this.editor.isEditable,
})
this.editor.on('viewUpdate', () => {
isEditable.value = this.editor.isEditable
})
const Component = Vue const Component = Vue
.extend(component) .extend(component)
.extend({ .extend({
props: Object.keys(props), props: Object.keys(props),
components: { provide() {
NodeViewWrapper, return {
NodeViewContent, onDragStart,
isEditable,
}
}, },
}) })
@@ -168,15 +112,23 @@ class VueNodeView implements NodeView {
} }
get dom() { get dom() {
if (!this.renderer.element.hasAttribute('data-node-view-wrapper')) {
throw Error('Please use the NodeViewWrapper component for your node view.')
}
return this.renderer.element return this.renderer.element
} }
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

@@ -2,3 +2,5 @@ export * from '@tiptap/core'
export * from './VueRenderer' export * from './VueRenderer'
export * from './VueNodeViewRenderer' export * from './VueNodeViewRenderer'
export * from './EditorContent' export * from './EditorContent'
export * from './NodeViewWrapper'
export * from './NodeViewContent'

View File

@@ -8,7 +8,7 @@ export const NodeViewContent = defineComponent({
}, },
}, },
inject: ['editable'], inject: ['isEditable'],
render() { render() {
return h( return h(
@@ -18,7 +18,7 @@ export const NodeViewContent = defineComponent({
}, },
'data-node-view-content': '', 'data-node-view-content': '',
// @ts-ignore (https://github.com/vuejs/vue-next/issues/3031) // @ts-ignore (https://github.com/vuejs/vue-next/issues/3031)
contenteditable: this.editable.value, contenteditable: this.isEditable.value,
}, },
) )
}, },

View File

@@ -86,7 +86,7 @@ class VueNodeView implements NodeView {
props: Object.keys(props), props: Object.keys(props),
setup() { setup() {
provide('onDragStart', onDragStart) provide('onDragStart', onDragStart)
provide('editable', isEditable) provide('isEditable', isEditable)
return (component as any).setup?.() return (component as any).setup?.()
}, },