Merge branch 'docs/node-views' into main

# Conflicts:
#	yarn.lock
This commit is contained in:
Hans Pagel
2021-03-10 20:58:10 +01:00
8 changed files with 454 additions and 61 deletions

View File

@@ -0,0 +1,69 @@
<template>
<node-view-wrapper class="vue-component">
<span class="label">Vue Component</span>
<div class="content">
<button @click="increase">
This button has been clicked {{ node.attrs.count }} times.
</button>
</div>
</node-view-wrapper>
</template>
<script>
import { NodeViewWrapper } from '@tiptap/vue-2'
export default {
components: {
NodeViewWrapper,
},
props: {
updateAttributes: {
type: Function,
required: true,
},
node: {
type: Object,
required: true,
},
},
methods: {
increase() {
this.updateAttributes({
count: this.node.attrs.count + 1,
})
},
},
}
</script>
<style lang="scss">
.vue-component {
border: 1px solid #adb5bd;
border-radius: 0.5rem;
margin: 1rem 0;
position: relative;
}
.label {
margin-left: 1rem;
background-color: #adb5bd;
font-size: 0.6rem;
letter-spacing: 1px;
font-weight: bold;
text-transform: uppercase;
color: #fff;
position: absolute;
top: 0;
padding: 0.25rem 0.75rem;
border-radius: 0 0 0.5rem 0.5rem;
}
.content {
margin-top: 1.5rem;
padding: 1rem;
}
</style>

View File

@@ -0,0 +1,35 @@
import { Node, mergeAttributes } from '@tiptap/core'
import { VueNodeViewRenderer } from '@tiptap/vue-2'
import Component from './Component.vue'
export default Node.create({
name: 'vueComponent',
group: 'block',
atom: true,
addAttributes() {
return {
count: {
default: 0,
},
}
},
parseHTML() {
return [
{
tag: 'vue-component',
},
]
},
renderHTML({ HTMLAttributes }) {
return ['vue-component', mergeAttributes(HTMLAttributes)]
},
addNodeView() {
return VueNodeViewRenderer(Component)
},
})

View File

@@ -0,0 +1,63 @@
<template>
<editor-content :editor="editor" />
</template>
<script>
import { Editor, EditorContent } from '@tiptap/vue-2'
import { defaultExtensions } from '@tiptap/starter-kit'
import VueComponent from './index.js'
export default {
components: {
EditorContent,
},
data() {
return {
editor: null,
}
},
mounted() {
this.editor = new Editor({
extensions: [
...defaultExtensions(),
VueComponent,
],
content: `
<p>
This is still the text editor youre used to, but enriched with node views.
</p>
<vue-component count="0"></vue-component>
<p>
Did you see that? Thats a Vue component. We are really living in the future.
</p>
`,
})
},
beforeDestroy() {
this.editor.destroy()
},
}
</script>
<style lang="scss">
/* Basic editor styles */
.ProseMirror {
> * + * {
margin-top: 0.75em;
}
}
.output {
background-color: #0D0D0D;
color: #fff;
padding: 1rem;
font-family: monospace;
font-size: 1.1rem;
border-radius: 0.5rem;
margin: 1rem 0;
position: relative;
}
</style>

View File

@@ -0,0 +1,47 @@
<template>
<node-view-wrapper class="vue-component">
<span class="label">Vue Component</span>
<node-view-content class="content" />
</node-view-wrapper>
</template>
<script>
import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2'
export default {
components: {
NodeViewWrapper,
NodeViewContent,
},
}
</script>
<style lang="scss">
.vue-component {
border: 1px solid #adb5bd;
border-radius: 0.5rem;
margin: 1rem 0;
position: relative;
}
.label {
margin-left: 1rem;
background-color: #adb5bd;
font-size: 0.6rem;
letter-spacing: 1px;
font-weight: bold;
text-transform: uppercase;
color: #fff;
position: absolute;
top: 0;
padding: 0.25rem 0.75rem;
border-radius: 0 0 0.5rem 0.5rem;
}
.content {
margin: 2.5rem 1rem 1rem;
padding: 1rem;
border: 1px dashed #adb5bd;
}
</style>

View File

@@ -0,0 +1,35 @@
import { Node, mergeAttributes } from '@tiptap/core'
import { VueNodeViewRenderer } from '@tiptap/vue-2'
import Component from './Component.vue'
export default Node.create({
name: 'vueComponent',
group: 'block',
content: 'inline*',
addAttributes() {
return {
count: {
default: 0,
},
}
},
parseHTML() {
return [
{
tag: 'vue-component',
},
]
},
renderHTML({ HTMLAttributes }) {
return ['vue-component', mergeAttributes(HTMLAttributes)]
},
addNodeView() {
return VueNodeViewRenderer(Component)
},
})

View File

@@ -0,0 +1,65 @@
<template>
<editor-content :editor="editor" />
</template>
<script>
import { Editor, EditorContent } from '@tiptap/vue-2'
import { defaultExtensions } from '@tiptap/starter-kit'
import VueComponent from './index.js'
export default {
components: {
EditorContent,
},
data() {
return {
editor: null,
}
},
mounted() {
this.editor = new Editor({
extensions: [
...defaultExtensions(),
VueComponent,
],
content: `
<p>
This is still the text editor youre used to, but enriched with node views.
</p>
<vue-component>
<p>This is editable.</p>
</vue-component>
<p>
Did you see that? Thats a Vue component. We are really living in the future.
</p>
`,
})
},
beforeDestroy() {
this.editor.destroy()
},
}
</script>
<style lang="scss">
/* Basic editor styles */
.ProseMirror {
> * + * {
margin-top: 0.75em;
}
}
.output {
background-color: #0D0D0D;
color: #fff;
padding: 1rem;
font-family: monospace;
font-size: 1.1rem;
border-radius: 0.5rem;
margin: 1rem 0;
position: relative;
}
</style>

View File

@@ -1,34 +1,14 @@
# Complex node views # Interactive node views
## toc ## toc
## Introduction ## Introduction
Node views are the best thing since sliced bread, at least if youre a fan of customization (and bread). Node views enable you to add literally anything to a node. If you can write it in JavaScript, you can use it in your editor. Node views are the best thing since sliced bread, at least if you are a fan of customization (and bread). With node views you can add interactive nodes to your editor content. That can literally be everything. If you can write it in JavaScript, you can use it in your editor.
<!-- ```js
import { Node } from '@tiptap/core'
import { VueNodeViewRenderer } from '@tiptap/vue-2'
import Component from './Component.vue'
export default Node.create({
addNodeView() {
return ({ editor, node, getPos, HTMLAttributes, decorations, extension }) => {
const dom = document.createElement('div')
dom.innerHTML = 'Im a node view'
return {
dom,
}
})
},
})
``` -->
## Different types of node views ## Different types of node views
TODO
### Simple ### Editable content
```html ```html
<div class="Prosemirror" contenteditable="true"> <div class="Prosemirror" contenteditable="true">
<p>text</p> <p>text</p>
@@ -37,12 +17,7 @@ export default Node.create({
</div> </div>
``` ```
#### Example: Task item ### Non-editable content
https://github.com/ueberdosis/tiptap-next/blob/main/packages/extension-task-item/src/task-item.ts#L74
### Without content
```html ```html
<div class="Prosemirror" contenteditable="true"> <div class="Prosemirror" contenteditable="true">
<p>text</p> <p>text</p>
@@ -51,14 +26,7 @@ https://github.com/ueberdosis/tiptap-next/blob/main/packages/extension-task-item
</div> </div>
``` ```
#### Example: Table of contents ### Mixed content
<demo name="Guide/NodeViews/TableOfContents" />
#### Example: Drawing in the editor
<demo name="Examples/Drawing" />
### Advanced node views with content
```html ```html
<div class="Prosemirror" contenteditable="true"> <div class="Prosemirror" contenteditable="true">
<p>text</p> <p>text</p>
@@ -74,12 +42,8 @@ https://github.com/ueberdosis/tiptap-next/blob/main/packages/extension-task-item
</div> </div>
``` ```
#### Example: Drag handles ## Node views with JavaScript
<demo name="Guide/NodeViews/DragHandle" /> TODO
## Render Vue components
### Node
```js ```js
import { Node } from '@tiptap/core' import { Node } from '@tiptap/core'
@@ -87,13 +51,119 @@ import { VueNodeViewRenderer } from '@tiptap/vue-2'
import Component from './Component.vue' import Component from './Component.vue'
export default Node.create({ export default Node.create({
addNodeView() {
return ({ editor, node, getPos, HTMLAttributes, decorations, extension }) => {
const dom = document.createElement('div')
dom.innerHTML = 'Hello, Im a node view!'
return {
dom,
}
})
},
})
```
## Node views with Vue
Using Vanilla JavaScript can feel complex, if you are used to work in Vue. Good news: You can even use regular Vue components in your node views. There is just a little bit you need to know, but lets go through this one by one.
### Render a Vue component
Here is what you need to do to render Vue components inside your text editor:
1. [Create a node extension](/guide/build-extensions)
2. Create a Vue component
3. Pass that component to the provided `VueNodeViewRenderer`
4. Register it with `addNodeView()`
5. [Configure tiptap to use your new node](/guide/configuration)
This is how your node could look like:
```js
import { Node } from '@tiptap/core'
import { VueNodeViewRenderer } from '@tiptap/vue-2'
import Component from './Component.vue'
export default Node.create({
// configuration …
addNodeView() { addNodeView() {
return VueNodeViewRenderer(Component) return VueNodeViewRenderer(Component)
}, },
}) })
``` ```
### Component There is a little bit of magic required to make this work. But dont worry, we provide a Vue component you can use to get started easily. Dont forget to add it to your custom Vue component, like shown below:
```html
<template>
<node-view-wrapper>
Vue Component
</node-view-wrapper>
</template>
```
Got it? Lets see that in action. Feel free to copy the example to get started.
<demo name="Guide/NodeViews/VueComponent" />
### Access node attributes
```js
props: {
node: {
type: Object,
required: true,
},
},
```
```js
this.node.attrs.count
```
### Update node attributes
```js
props: {
updateAttributes: {
type: Function,
required: true,
},
},
```
```js
this.updateAttributes({
count: this.node.attrs.count + 1,
})
```
### Adding a content editable
```html
<template>
<node-view-wrapper class="dom">
<node-view-content class="content-dom" />
</node-view-wrapper>
</template>
<script>
import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2'
export default {
components: {
NodeViewWrapper,
NodeViewContent,
},
}
```
<demo name="Guide/NodeViews/VueComponentContent" />
`content: 'block+'`
### All available props
```html ```html
<template> <template>
@@ -141,26 +211,35 @@ export default {
</script> </script>
``` ```
### Component with Content ## Node views with React
TODO
```html ## Rendered content
<template>
<node-view-wrapper class="dom">
<node-view-content class="content-dom" />
</node-view-wrapper>
</template>
<script> ```js
import { NodeViewWrapper, NodeViewContent } from '@tiptap/vue-2' parseHTML() {
return [{
export default { tag: 'vue-component',
components: { }]
NodeViewWrapper, },
NodeViewContent,
renderHTML({ HTMLAttributes }) {
return ['vue-component', mergeAttributes(HTMLAttributes)]
}, },
}
``` ```
## Examples
TODO
<!-- ### Drag handles
<demo name="Guide/NodeViews/DragHandle" />
### Table of contents
<demo name="Guide/NodeViews/TableOfContents" />
### Drawing in the editor
<demo name="Examples/Drawing" /> -->
## Reference ## Reference
### dom: ?dom.Node ### dom: ?dom.Node

View File

@@ -92,7 +92,7 @@
link: /guide/extend-extensions link: /guide/extend-extensions
- title: Build extensions - title: Build extensions
link: /guide/build-extensions link: /guide/build-extensions
- title: Complex node views - title: Interactive node views
link: /guide/node-views link: /guide/node-views
type: draft type: draft
- title: Working with TypeScript - title: Working with TypeScript