Merge branch 'main' into feature/invert-isactive
This commit is contained in:
4
docs/src/demos/Examples/Book/content.js
Normal file
4
docs/src/demos/Examples/Book/content.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export const content = `
|
||||||
|
<h1>Example</h1>
|
||||||
|
<p>TODO</p>
|
||||||
|
`
|
||||||
7
docs/src/demos/Examples/Book/index.spec.js
Normal file
7
docs/src/demos/Examples/Book/index.spec.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
context('/examples/basic', () => {
|
||||||
|
before(() => {
|
||||||
|
cy.visit('/examples/basic')
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: Write tests
|
||||||
|
})
|
||||||
145
docs/src/demos/Examples/Book/index.vue
Normal file
145
docs/src/demos/Examples/Book/index.vue
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div v-if="editor">
|
||||||
|
<button @click="editor.chain().focus().toggleBold().run()" :class="{ 'is-active': editor.isActive('bold') }">
|
||||||
|
bold
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleItalic().run()" :class="{ 'is-active': editor.isActive('italic') }">
|
||||||
|
italic
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleStrike().run()" :class="{ 'is-active': editor.isActive('strike') }">
|
||||||
|
strike
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleCode().run()" :class="{ 'is-active': editor.isActive('code') }">
|
||||||
|
code
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().unsetAllMarks().run()">
|
||||||
|
clear marks
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().clearNodes().run()">
|
||||||
|
clear nodes
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().setParagraph().run()" :class="{ 'is-active': editor.isActive('paragraph') }">
|
||||||
|
paragraph
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleHeading({ level: 1 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 1 }) }">
|
||||||
|
h1
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleHeading({ level: 2 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 2 }) }">
|
||||||
|
h2
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleHeading({ level: 3 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 3 }) }">
|
||||||
|
h3
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleHeading({ level: 4 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 4 }) }">
|
||||||
|
h4
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleHeading({ level: 5 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 5 }) }">
|
||||||
|
h5
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleHeading({ level: 6 }).run()" :class="{ 'is-active': editor.isActive('heading', { level: 6 }) }">
|
||||||
|
h6
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleBulletList().run()" :class="{ 'is-active': editor.isActive('bulletList') }">
|
||||||
|
bullet list
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleOrderedList().run()" :class="{ 'is-active': editor.isActive('orderedList') }">
|
||||||
|
ordered list
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleCodeBlock().run()" :class="{ 'is-active': editor.isActive('codeBlock') }">
|
||||||
|
code block
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleBlockquote().run()" :class="{ 'is-active': editor.isActive('blockquote') }">
|
||||||
|
blockquote
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().setHorizontalRule().run()">
|
||||||
|
horizontal rule
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().setHardBreak().run()">
|
||||||
|
hard break
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().undo().run()">
|
||||||
|
undo
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().redo().run()">
|
||||||
|
redo
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<editor-content :editor="editor" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Editor, EditorContent, defaultExtensions } from '@tiptap/vue-starter-kit'
|
||||||
|
import { content } from './content.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
EditorContent,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
editor: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.editor = new Editor({
|
||||||
|
extensions: defaultExtensions(),
|
||||||
|
content,
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.editor.destroy()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
/* Basic editor styles */
|
||||||
|
.ProseMirror {
|
||||||
|
> * + * {
|
||||||
|
margin-top: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: rgba(#616161, 0.1);
|
||||||
|
color: #616161;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: #0D0D0D;
|
||||||
|
color: #FFF;
|
||||||
|
font-family: 'JetBrainsMono', monospace;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
|
code {
|
||||||
|
color: inherit;
|
||||||
|
background: none;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
padding-left: 1rem;
|
||||||
|
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
context('/examples/links', () => {
|
|
||||||
before(() => {
|
|
||||||
cy.visit('/examples/links')
|
|
||||||
})
|
|
||||||
|
|
||||||
// TODO: Write tests
|
|
||||||
})
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div v-if="editor">
|
|
||||||
<button @click="setLink" :class="{ 'is-active': editor.isActive('link') }">
|
|
||||||
link
|
|
||||||
</button>
|
|
||||||
<button @click="editor.chain().focus().unsetLink().run()" v-if="editor.isActive('link')">
|
|
||||||
remove
|
|
||||||
</button>
|
|
||||||
<editor-content :editor="editor" />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import { Editor } from '@tiptap/core'
|
|
||||||
import { EditorContent } from '@tiptap/vue'
|
|
||||||
import Document from '@tiptap/extension-document'
|
|
||||||
import Paragraph from '@tiptap/extension-paragraph'
|
|
||||||
import Text from '@tiptap/extension-text'
|
|
||||||
import Link from '@tiptap/extension-link'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
EditorContent,
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
editor: null,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.editor = new Editor({
|
|
||||||
extensions: [
|
|
||||||
Document,
|
|
||||||
Paragraph,
|
|
||||||
Text,
|
|
||||||
Link,
|
|
||||||
],
|
|
||||||
content: `
|
|
||||||
<p>
|
|
||||||
Wow, this editor has support for links to the whole <a href="https://en.wikipedia.org/wiki/World_Wide_Web">world wide web</a>. We tested a lot of URLs and I think you can add *every URL* you want. Isn’t that cool? Let’s try <a href="https://statamic.com/">another one!</a> Yep, seems to work.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
By default every link will get a [rel="noopener noreferrer nofollow"] attribute <a href="https://web.dev/external-anchors-use-rel-noopener/">for security reasons</a>. It’s configurable though.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Ah, and links open in a new tab by default, but that’s also - yes, you’ve guessed it - configurable.
|
|
||||||
</p>
|
|
||||||
`,
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setLink() {
|
|
||||||
const url = window.prompt('URL')
|
|
||||||
|
|
||||||
this.editor.chain().focus().setLink({ href: url }).run()
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this.editor.destroy()
|
|
||||||
},
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss">
|
|
||||||
/* Basic editor styles */
|
|
||||||
.ProseMirror {
|
|
||||||
> * + * {
|
|
||||||
margin-top: 0.75em;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: underline;
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { Editor, EditorContent, defaultExtensions } from '@tiptap/vue-starter-kit'
|
import { Editor, EditorContent, defaultExtensions } from '@tiptap/vue-starter-kit'
|
||||||
import Highlight from '@tiptap/extension-highlight'
|
import Highlight from '@tiptap/extension-highlight'
|
||||||
|
import Typography from '@tiptap/extension-typography'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
@@ -32,12 +33,16 @@ export default {
|
|||||||
Those conventions are called input rules in tiptap. Some of them are enabled by default. Try <code>></code> for blockquotes, <code>*</code>, <code>-</code> or <code>+</code> for bullet lists, or <code>\`foobar\`</code> to highlight code, <code>~~tildes~~</code> to strike text, or <code>==equal signs==</code> to highlight text.
|
Those conventions are called input rules in tiptap. Some of them are enabled by default. Try <code>></code> for blockquotes, <code>*</code>, <code>-</code> or <code>+</code> for bullet lists, or <code>\`foobar\`</code> to highlight code, <code>~~tildes~~</code> to strike text, or <code>==equal signs==</code> to highlight text.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
You can overwrite existing input rules or add your own to new nodes and marks.
|
You can overwrite existing input rules or add your own to nodes, marks and extensions.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
For example, we added the <code>Typography</code> extensions here. Try typing <code>(c)</code> to see how it’s converted to a proper © character. You can also try <code>-></code>, <code>>></code>, <code>1/2</code>, <code>!=</code>, or <code>--</code>.
|
||||||
</p>
|
</p>
|
||||||
`,
|
`,
|
||||||
extensions: [
|
extensions: [
|
||||||
...defaultExtensions(),
|
...defaultExtensions(),
|
||||||
Highlight,
|
Highlight,
|
||||||
|
Typography,
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|||||||
19
docs/src/demos/Guide/StoreContent/ExportHTML/index.spec.js
Normal file
19
docs/src/demos/Guide/StoreContent/ExportHTML/index.spec.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
context('/guide/store-content', () => {
|
||||||
|
before(() => {
|
||||||
|
cy.visit('/guide/store-content')
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||||
|
editor.commands.setContent('<p>Example Text</p>')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return html', () => {
|
||||||
|
cy.get('.ProseMirror').then(([{ editor }]) => {
|
||||||
|
const html = editor.getHTML()
|
||||||
|
|
||||||
|
expect(html).to.equal('<p>Example Text</p>')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
157
docs/src/demos/Guide/StoreContent/ExportHTML/index.vue
Normal file
157
docs/src/demos/Guide/StoreContent/ExportHTML/index.vue
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div class="actions" v-if="editor">
|
||||||
|
<button class="button" @click="setContent">
|
||||||
|
Set Content
|
||||||
|
</button>
|
||||||
|
<button class="button" @click="clearContent">
|
||||||
|
Clear Content
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleBold().run()" :class="{ 'is-active': editor.isActive('bold') }">
|
||||||
|
Bold
|
||||||
|
</button>
|
||||||
|
<button @click="editor.chain().focus().toggleItalic().run()" :class="{ 'is-active': editor.isActive('italic') }">
|
||||||
|
Italic
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<editor-content :editor="editor" />
|
||||||
|
|
||||||
|
<div class="export">
|
||||||
|
<h3>HTML</h3>
|
||||||
|
<pre><code>{{ html }}</code></pre>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { Editor, EditorContent, defaultExtensions } from '@tiptap/vue-starter-kit'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
EditorContent,
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
editor: null,
|
||||||
|
html: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.editor = new Editor({
|
||||||
|
content: `
|
||||||
|
<p>
|
||||||
|
Wow, this editor instance exports its content as HTML.
|
||||||
|
</p>
|
||||||
|
`,
|
||||||
|
extensions: defaultExtensions(),
|
||||||
|
})
|
||||||
|
|
||||||
|
// Get the initial content …
|
||||||
|
this.html = this.editor.getHTML()
|
||||||
|
|
||||||
|
// … and get the content after every change.
|
||||||
|
this.editor.on('update', () => {
|
||||||
|
this.html = this.editor.getHTML()
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setContent() {
|
||||||
|
// You can pass a HTML document to the editor.
|
||||||
|
this.editor.commands.setContent(`
|
||||||
|
<p>
|
||||||
|
It’s 19871. You can’t turn on a radio, or go to a mall without hearing Olivia Newton-John’s hit song, Physical.
|
||||||
|
</p>
|
||||||
|
`, true)
|
||||||
|
|
||||||
|
// It’s likely that you’d like to focus the Editor after most commands.
|
||||||
|
this.editor.commands.focus()
|
||||||
|
},
|
||||||
|
|
||||||
|
clearContent() {
|
||||||
|
this.editor
|
||||||
|
.chain()
|
||||||
|
.clearContent(true)
|
||||||
|
.focus()
|
||||||
|
.run()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.editor.destroy()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
/* Style the export */
|
||||||
|
.export {
|
||||||
|
padding: 1rem 0 0;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 1rem 0 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
border-radius: 5px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
display: block;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background-color:#e9ecef;
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Basic editor styles */
|
||||||
|
.ProseMirror {
|
||||||
|
> * + * {
|
||||||
|
margin-top: 0.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul,
|
||||||
|
ol {
|
||||||
|
padding: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background-color: rgba(#616161, 0.1);
|
||||||
|
color: #616161;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: #0D0D0D;
|
||||||
|
color: #FFF;
|
||||||
|
font-family: 'JetBrainsMono', monospace;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
|
code {
|
||||||
|
color: inherit;
|
||||||
|
background: none;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
padding-left: 1rem;
|
||||||
|
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
context('/examples/export-html-or-json', () => {
|
context('/guide/store-content', () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
cy.visit('/examples/export-html-or-json')
|
cy.visit('/guide/store-content')
|
||||||
})
|
})
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -29,12 +29,4 @@ context('/examples/export-html-or-json', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return html', () => {
|
|
||||||
cy.get('.ProseMirror').then(([{ editor }]) => {
|
|
||||||
const html = editor.getHTML()
|
|
||||||
|
|
||||||
expect(html).to.equal('<p>Example Text</p>')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
@@ -18,8 +18,6 @@
|
|||||||
<editor-content :editor="editor" />
|
<editor-content :editor="editor" />
|
||||||
|
|
||||||
<div class="export">
|
<div class="export">
|
||||||
<h3>HTML</h3>
|
|
||||||
<pre><code>{{ html }}</code></pre>
|
|
||||||
<h3>JSON</h3>
|
<h3>JSON</h3>
|
||||||
<pre><code v-html="json" /></pre>
|
<pre><code v-html="json" /></pre>
|
||||||
</div>
|
</div>
|
||||||
@@ -38,7 +36,6 @@ export default {
|
|||||||
return {
|
return {
|
||||||
editor: null,
|
editor: null,
|
||||||
json: null,
|
json: null,
|
||||||
html: null,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -46,10 +43,7 @@ export default {
|
|||||||
this.editor = new Editor({
|
this.editor = new Editor({
|
||||||
content: `
|
content: `
|
||||||
<p>
|
<p>
|
||||||
What would be a text editor without content. At some point you want to get the content out of the editor and yes, we got you covered. There are two methods to export the current document as <code>HTML</code> or <code>JSON</code>.
|
Wow, this editor instance exports its content as JSON.
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
You can hook into the <code>update</code> event to get the content after every single change. How cool is that?
|
|
||||||
</p>
|
</p>
|
||||||
`,
|
`,
|
||||||
extensions: defaultExtensions(),
|
extensions: defaultExtensions(),
|
||||||
@@ -57,18 +51,16 @@ export default {
|
|||||||
|
|
||||||
// Get the initial content …
|
// Get the initial content …
|
||||||
this.json = this.editor.getJSON()
|
this.json = this.editor.getJSON()
|
||||||
this.html = this.editor.getHTML()
|
|
||||||
|
|
||||||
// … and get the content after every change.
|
// … and get the content after every change.
|
||||||
this.editor.on('update', () => {
|
this.editor.on('update', () => {
|
||||||
this.json = this.editor.getJSON()
|
this.json = this.editor.getJSON()
|
||||||
this.html = this.editor.getHTML()
|
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
setContent() {
|
setContent() {
|
||||||
// You can pass a JSON document …
|
// You can pass a JSON document to the editor.
|
||||||
this.editor.commands.setContent({
|
this.editor.commands.setContent({
|
||||||
type: 'document',
|
type: 'document',
|
||||||
content: [{
|
content: [{
|
||||||
@@ -82,9 +74,6 @@ export default {
|
|||||||
}],
|
}],
|
||||||
}, true)
|
}, true)
|
||||||
|
|
||||||
// … but HTML strings are also supported.
|
|
||||||
// this.editor.setContent('<p>This is some inserted text. 👋</p>')
|
|
||||||
|
|
||||||
// It’s likely that you’d like to focus the Editor after most commands.
|
// It’s likely that you’d like to focus the Editor after most commands.
|
||||||
this.editor.commands.focus()
|
this.editor.commands.focus()
|
||||||
},
|
},
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
context('/examples/read-only', () => {
|
context('/guide/store-content', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cy.visit('/examples/read-only')
|
cy.visit('/guide/store-content')
|
||||||
})
|
})
|
||||||
|
|
||||||
it.skip('should be read-only', () => {
|
it.skip('should be read-only', () => {
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
# Basic
|
# Basic text editor
|
||||||
|
|
||||||
<demo name="Examples/Basic" />
|
<demo name="Examples/Basic" />
|
||||||
|
|||||||
3
docs/src/docPages/examples/book.md
Normal file
3
docs/src/docPages/examples/book.md
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Write a book
|
||||||
|
|
||||||
|
<demo name="Examples/Book" highlight="" />
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Code Highlighting
|
|
||||||
|
|
||||||
<demo name="Examples/CodeHighlighting" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Drag Handle
|
|
||||||
|
|
||||||
<demo name="Examples/DragHandle" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Embeds
|
|
||||||
|
|
||||||
<demo name="Examples/Embeds" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Export HTML or JSON
|
|
||||||
|
|
||||||
<demo name="Examples/ExportHtmlOrJson" highlight="52-60"/>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Floating Menu
|
|
||||||
|
|
||||||
<demo name="Examples/FloatingMenu" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Hiding Menu Bar
|
|
||||||
|
|
||||||
<demo name="Examples/HidingMenuBar" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Images
|
|
||||||
|
|
||||||
<demo name="Examples/Images" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Links
|
|
||||||
|
|
||||||
<demo name="Examples/Links" highlight="3-8,19,38,55-59" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Menu Bubble
|
|
||||||
|
|
||||||
<demo name="Examples/MenuBubble" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Placeholder
|
|
||||||
|
|
||||||
<demo name="Examples/Placeholder" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Read-only
|
|
||||||
|
|
||||||
<demo name="Examples/ReadOnly" highlight="3-6,22,28,41-47" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Search and Replace
|
|
||||||
|
|
||||||
<demo name="Examples/SearchAndReplace" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Suggestions
|
|
||||||
|
|
||||||
<demo name="Examples/Suggestions" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Tables
|
|
||||||
|
|
||||||
<demo name="Examples/Tables" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Title
|
|
||||||
|
|
||||||
<demo name="Examples/Title" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Todo List
|
|
||||||
|
|
||||||
<demo name="Examples/TodoList" />
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Trailing Paragraph
|
|
||||||
|
|
||||||
<demo name="Examples/TrailingParagraph" />
|
|
||||||
@@ -6,7 +6,9 @@
|
|||||||
|
|
||||||
TODO
|
TODO
|
||||||
|
|
||||||
## Simple
|
## Different types of node views
|
||||||
|
|
||||||
|
### Simple
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div class="Prosemirror" contenteditable="true">
|
<div class="Prosemirror" contenteditable="true">
|
||||||
@@ -16,7 +18,7 @@ TODO
|
|||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Without content
|
### Without content
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div class="Prosemirror" contenteditable="true">
|
<div class="Prosemirror" contenteditable="true">
|
||||||
@@ -26,7 +28,7 @@ TODO
|
|||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
## Advanced node views with content
|
### Advanced node views with content
|
||||||
|
|
||||||
```html
|
```html
|
||||||
<div class="Prosemirror" contenteditable="true">
|
<div class="Prosemirror" contenteditable="true">
|
||||||
@@ -43,16 +45,7 @@ TODO
|
|||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
<!--
|
<demo name="Guide/NodeViews/DragHandle" />
|
||||||
## Node views with plain JavaScript
|
|
||||||
|
|
||||||
### HTML
|
## Render Vue components
|
||||||
|
|
||||||
### Content
|
|
||||||
|
|
||||||
### JavaScript
|
|
||||||
|
|
||||||
### Events
|
|
||||||
|
|
||||||
## Use Vue.js components
|
|
||||||
-->
|
|
||||||
@@ -3,11 +3,11 @@
|
|||||||
## toc
|
## toc
|
||||||
|
|
||||||
## Introduction
|
## Introduction
|
||||||
You can store your content as a JSON object or as a good old HTML string. Both work fine. And of course, you can pass both formats to the editor to restore your content.
|
You can store your content as a JSON object or as a good old HTML string. Both work fine. And of course, you can pass both formats to the editor to restore your content. Here is an interactive example, that exports the content as HTML and JSON when the document is changed:
|
||||||
|
|
||||||
You can store your content as JSON and restore the content from HTML, or the other way around. I don’t know why you would do that, but tiptap wouldn’t care.
|
## Export
|
||||||
|
|
||||||
## Option 1: JSON
|
### Option 1: JSON
|
||||||
JSON is probably easier to loop through, for example to look for a mention and it’s more like what tiptap uses under the hood. Anyway, if you want to use JSON to store the content we provide a method to retrieve the content as JSON:
|
JSON is probably easier to loop through, for example to look for a mention and it’s more like what tiptap uses under the hood. Anyway, if you want to use JSON to store the content we provide a method to retrieve the content as JSON:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -18,19 +18,10 @@ You can store that in your database (or send it to an API) and restore the docum
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
new Editor({
|
new Editor({
|
||||||
// …
|
|
||||||
content: {
|
content: {
|
||||||
"type": "document",
|
"type": "document",
|
||||||
"content": [
|
"content": [
|
||||||
{
|
// …
|
||||||
"type": "paragraph",
|
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"text": "Example Text"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -42,20 +33,16 @@ Or if you need to wait for something, you can do it later through the editor ins
|
|||||||
editor.setContent({
|
editor.setContent({
|
||||||
"type": "document",
|
"type": "document",
|
||||||
"content": [
|
"content": [
|
||||||
{
|
// …
|
||||||
"type": "paragraph",
|
|
||||||
"content": [
|
|
||||||
{
|
|
||||||
"type": "text",
|
|
||||||
"text": "Example Text"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
|
|
||||||
## Option 2: HTML
|
Here is an interactive example where you can see that in action:
|
||||||
|
|
||||||
|
<demo name="Guide/StoreContent/ExportJSON" :show-source="false"/>
|
||||||
|
|
||||||
|
### Option 2: HTML
|
||||||
HTML can be easily rendered in other places, for example in emails and it’s wildly used, so it’s probably easier to switch the editor at some point. Anyway, every editor instance provides a method to get HTML from the current document:
|
HTML can be easily rendered in other places, for example in emails and it’s wildly used, so it’s probably easier to switch the editor at some point. Anyway, every editor instance provides a method to get HTML from the current document:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
@@ -66,7 +53,6 @@ This can then be used to restore the document initially:
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
new Editor({
|
new Editor({
|
||||||
// …
|
|
||||||
content: `<p>Example Text</p>`,
|
content: `<p>Example Text</p>`,
|
||||||
})
|
})
|
||||||
```
|
```
|
||||||
@@ -76,7 +62,11 @@ Or if you want to restore the content later (e. g. after an API call has finishe
|
|||||||
editor.commands.setContent(`<p>Example Text</p>`)
|
editor.commands.setContent(`<p>Example Text</p>`)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Not an option: Markdown
|
Use this interactive example to fiddle around:
|
||||||
|
|
||||||
|
<demo name="Guide/StoreContent/ExportHTML" :show-source="false"/>
|
||||||
|
|
||||||
|
### Not an option: Markdown
|
||||||
|
|
||||||
Unfortunately, **tiptap doesn’t support Markdown as an input or output format**. We considered to add support for it, but those are the reasons why we decided to not do it:
|
Unfortunately, **tiptap doesn’t support Markdown as an input or output format**. We considered to add support for it, but those are the reasons why we decided to not do it:
|
||||||
|
|
||||||
@@ -90,10 +80,18 @@ If you still think you need Markdown, ProseMirror has an [example on how to deal
|
|||||||
|
|
||||||
That said, tiptap does support [Markdown shortcuts](/examples/markdown-shortcuts) to format your content.
|
That said, tiptap does support [Markdown shortcuts](/examples/markdown-shortcuts) to format your content.
|
||||||
|
|
||||||
## Generate HTML from ProseMirror JSON
|
## Rendering
|
||||||
If you need to render the content on the server side, for example to generate the HTML for a blog post which has been written in tiptap, you’ll probably want a way to do just that without an actual editor instance.
|
|
||||||
|
|
||||||
That’s what the `generateHTML()` is for. It’s a utility function that renders HTML without an actual editor instance. As an alternative, you can also use tiptap in a [read-only mode](/examples/read-only).
|
### Option 1: Read-only instance of tiptap
|
||||||
|
|
||||||
|
To render the saved content, set the editor to read-only. That’s how you can achieve the exact same rendering as it’s in the editor, without duplicating your CSS and other code.
|
||||||
|
|
||||||
|
<demo name="Guide/StoreContent/ReadOnly" highlight="3-6,22,28,41-47" />
|
||||||
|
|
||||||
|
### Option 2: Generate HTML from ProseMirror JSON
|
||||||
|
If you need to render the content on the server side, for example to generate the HTML for a blog post which has been written in tiptap, you’ll probably want to do just that without an actual editor instance.
|
||||||
|
|
||||||
|
That’s what the `generateHTML()` is for. It’s a helper function which renders HTML without an actual editor instance.
|
||||||
|
|
||||||
:::info Browser-only rendering
|
:::info Browser-only rendering
|
||||||
Import a lightweight implementation of `generateHTML()` from `@tiptap/core` if you’re using the function in a browser context only.
|
Import a lightweight implementation of `generateHTML()` from `@tiptap/core` if you’re using the function in a browser context only.
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ tiptap is a headless wrapper around [ProseMirror](https://ProseMirror.net) – a
|
|||||||
|
|
||||||
Create exactly the rich text editor you want out of customizable building blocks. tiptap comes with sensible defaults, a lot of extensions and a friendly API to customize every aspect. It’s backed by a welcoming community, open source, and free.
|
Create exactly the rich text editor you want out of customizable building blocks. tiptap comes with sensible defaults, a lot of extensions and a friendly API to customize every aspect. It’s backed by a welcoming community, open source, and free.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
<demo name="Examples/CollaborativeEditing" :show-source="false" />
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
**Headless.** We don’t tell you what a menu should look like or where it should be rendered in the DOM. That’s why tiptap is headless and comes without any CSS. You are in full control over markup, styling and behaviour.
|
**Headless.** We don’t tell you what a menu should look like or where it should be rendered in the DOM. That’s why tiptap is headless and comes without any CSS. You are in full control over markup, styling and behaviour.
|
||||||
|
|
||||||
|
|||||||
@@ -15,30 +15,23 @@
|
|||||||
link: /examples
|
link: /examples
|
||||||
redirect: /examples/basic
|
redirect: /examples/basic
|
||||||
items:
|
items:
|
||||||
- title: Basic
|
- title: Basic text editor
|
||||||
link: /examples/basic
|
link: /examples/basic
|
||||||
- title: Collaborative editing
|
- title: Collaborative editing
|
||||||
link: /examples/collaborative-editing
|
link: /examples/collaborative-editing
|
||||||
pro: true
|
pro: true
|
||||||
- title: Markdown shortcuts
|
- title: Markdown shortcuts
|
||||||
link: /examples/markdown-shortcuts
|
link: /examples/markdown-shortcuts
|
||||||
# - title: Formatting
|
- title: Formatting
|
||||||
# link: /examples/formatting
|
link: /examples/formatting
|
||||||
# - title: Links
|
- title: Todo app
|
||||||
# link: /examples/links
|
|
||||||
- title: Todo App
|
|
||||||
link: /examples/todo-app
|
link: /examples/todo-app
|
||||||
# - title: Read-only
|
- title: Write a book
|
||||||
# link: /examples/read-only
|
link: /examples/book
|
||||||
# - title: Minimalist
|
- title: For minimalists
|
||||||
# link: /examples/minimalist
|
link: /examples/minimalist
|
||||||
# - title: Use with v-model
|
# - title: Use with v-model
|
||||||
# link: /examples/v-model
|
# link: /examples/v-model
|
||||||
# - title: Drag handle
|
|
||||||
# link: /examples/drag-handle
|
|
||||||
# draft: true
|
|
||||||
# - title: Export HTML or JSON
|
|
||||||
# link: /examples/export-html-or-json
|
|
||||||
|
|
||||||
- title: Guide
|
- title: Guide
|
||||||
items:
|
items:
|
||||||
@@ -58,8 +51,8 @@
|
|||||||
link: /guide/store-content
|
link: /guide/store-content
|
||||||
- title: Build custom extensions
|
- title: Build custom extensions
|
||||||
link: /guide/build-custom-extensions
|
link: /guide/build-custom-extensions
|
||||||
- title: Advanced node views
|
- title: Define node views
|
||||||
link: /guide/advanced-node-views
|
link: /guide/node-views
|
||||||
draft: true
|
draft: true
|
||||||
- title: Working with TypeScript
|
- title: Working with TypeScript
|
||||||
link: /guide/working-with-typescript
|
link: /guide/working-with-typescript
|
||||||
|
|||||||
Reference in New Issue
Block a user