tiptap/demos/src/Experiments/All/Vue/index.vue

635 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="editor" class="container">
<div class="control-group">
<div class="button-group">
<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>
</div>
<editor-content :editor="editor" />
</div>
</template>
<script>
import Blockquote from '@tiptap/extension-blockquote'
import Bold from '@tiptap/extension-bold'
import BulletList from '@tiptap/extension-bullet-list'
import Code from '@tiptap/extension-code'
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
import Color from '@tiptap/extension-color'
import Document from '@tiptap/extension-document'
import Dropcursor from '@tiptap/extension-dropcursor'
import Gapcursor from '@tiptap/extension-gapcursor'
import HardBreak from '@tiptap/extension-hard-break'
import Heading from '@tiptap/extension-heading'
import Highlight from '@tiptap/extension-highlight'
import History from '@tiptap/extension-history'
import HorizontalRule from '@tiptap/extension-horizontal-rule'
import Image from '@tiptap/extension-image'
import Italic from '@tiptap/extension-italic'
import Link from '@tiptap/extension-link'
import ListItem from '@tiptap/extension-list-item'
import Mention from '@tiptap/extension-mention'
import OrderedList from '@tiptap/extension-ordered-list'
import Paragraph from '@tiptap/extension-paragraph'
import Placeholder from '@tiptap/extension-placeholder'
import Strike from '@tiptap/extension-strike'
import Subscript from '@tiptap/extension-subscript'
import Superscript from '@tiptap/extension-superscript'
import Table from '@tiptap/extension-table'
import TableCell from '@tiptap/extension-table-cell'
import TableHeader from '@tiptap/extension-table-header'
import TableRow from '@tiptap/extension-table-row'
import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import Text from '@tiptap/extension-text'
import TextAlign from '@tiptap/extension-text-align'
import TextStyle from '@tiptap/extension-text-style'
import Underline from '@tiptap/extension-underline'
import { Editor, EditorContent } from '@tiptap/vue-3'
import { all, createLowlight } from 'lowlight'
const lowlight = createLowlight(all)
export default {
components: {
EditorContent,
},
data() {
return {
editor: null,
}
},
mounted() {
this.editor = new Editor({
extensions: [
Blockquote,
Bold,
BulletList,
Code,
CodeBlockLowlight.configure({
lowlight,
}),
Document,
Dropcursor,
Gapcursor,
HardBreak,
Heading,
History,
HorizontalRule,
Italic,
ListItem,
OrderedList,
Paragraph,
Strike,
Text,
Underline,
Superscript,
Subscript,
Link,
Mention.configure({
HTMLAttributes: {
class: 'mention',
},
}),
Table.configure({
resizable: true,
}),
TableRow,
TableHeader,
TableCell,
Image,
TaskList,
TaskItem,
Placeholder.configure({
placeholder: 'This is a placeholder …',
includeChildren: true,
}),
TextAlign.configure({
types: ['heading', 'paragraph'],
}),
TextStyle,
Color,
Highlight.configure({ multicolor: true }),
],
content: `
<h2>
Hi there,
</h2>
<p>
this is a <em>basic</em> example of <strong>Tiptap</strong>. Sure, there are all kind of basic text styles youd probably expect from a text editor. But wait until you see the lists:
</p>
<ul>
<li>
That's a bullet list with one …
</li>
<li>
… or two list items.
</li>
</ul>
<p>
Isn't that great? And all of that is editable. But wait, there's more. Let's try a code block:
</p>
<pre><code class="language-javascript">for (var i=1; i <= 20; i++)
{
if (i % 15 == 0)
console.log("FizzBuzz");
else if (i % 3 == 0)
console.log("Fizz");
else if (i % 5 == 0)
console.log("Buzz");
else
console.log(i);
}</code></pre>
<p>
I know, I know, this is impressive. It's only the tip of the iceberg though. Give it a try and click a little bit around. Dont forget to check the other examples too.
</p>
<blockquote>
Wow, that's amazing. Good work, boy! 👏
<br />
— Mom
</blockquote>
<h2>Text align</h2>
<p style="text-align: center">first paragraph</p>
<p style="text-align: right">second paragraph</p>
<h2>Color</h2>
<p><span style="color: #958DF1">Oh, for some reason that's purple.</span></p>
<h2>Highlight</h2>
<p>This isn't highlighted.</s></p>
<p><mark>But that one is.</mark></p>
<p><mark style="background-color: red;">And this is highlighted too, but in a different color.</mark></p>
<p><mark data-color="#ffa8a8">And this one has a data attribute.</mark></p>
<h2>Task list</h2>
<ul data-type="taskList">
<li data-type="taskItem" data-checked="true">A list item</li>
<li data-type="taskItem" data-checked="false">And another one</li>
</ul>
<p>Some text with a mention <span data-type="mention" data-id="Mention 1"></span> and another one <span data-type="mention" data-id="Mention 2"></span>. Great.</p>
<table>
<tbody>
<tr>
<th>Name</th>
<th colspan="3">Description</th>
</tr>
<tr>
<td>Cyndi Lauper</td>
<td>Singer</td>
<td>Songwriter</td>
<td>Actress</td>
</tr>
<tr>
<td>Marie Curie</td>
<td>Scientist</td>
<td>Chemist</td>
<td>Physicist</td>
</tr>
<tr>
<td>Indira Gandhi</td>
<td>Prime minister</td>
<td colspan="2">Politician</td>
</tr>
</tbody>
</table>
<p>This is a basic example of implementing images. Drag to re-order.</p>
<img src="https://placehold.co/800x400" />
<img src="https://placehold.co/800x400/6A00F5/white" />
`,
})
},
beforeUnmount() {
this.editor.destroy()
},
}
</script>
<style lang="scss">
/* Basic editor styles */
.tiptap {
:first-child {
margin-top: 0;
}
/* Placeholder (on every new line) */
.is-empty::before {
color: var(--gray-4);
content: attr(data-placeholder);
float: left;
height: 0;
pointer-events: none;
}
a {
color: var(--purple);
cursor: pointer;
&:hover {
color: var(--purple-contrast);
}
}
/* List styles */
ul,
ol {
padding: 0 1rem;
margin: 1.25rem 1rem 1.25rem 0.4rem;
li p {
margin-top: 0.25em;
margin-bottom: 0.25em;
}
}
/* Task list specific styles */
ul[data-type="taskList"] {
list-style: none;
margin-left: 0;
padding: 0;
li {
align-items: flex-start;
display: flex;
> label {
flex: 0 0 auto;
margin-right: 0.5rem;
user-select: none;
}
> div {
flex: 1 1 auto;
}
}
input[type="checkbox"] {
cursor: pointer;
}
ul[data-type="taskList"] {
margin: 0;
}
}
/* Heading styles */
h1,
h2,
h3,
h4,
h5,
h6 {
line-height: 1.1;
margin-top: 2.5rem;
text-wrap: pretty;
}
h1,
h2 {
margin-top: 3.5rem;
margin-bottom: 1.5rem;
}
h1 {
font-size: 1.4rem;
}
h2 {
font-size: 1.2rem;
}
h3 {
font-size: 1.1rem;
}
h4,
h5,
h6 {
font-size: 1rem;
}
/* Display empty p */
p:empty::before {
content: '\00a0';
}
/* Code and preformatted text styles */
code {
background-color: var(--purple-light);
border-radius: 0.4rem;
color: var(--black);
font-size: 0.85rem;
padding: 0.25em 0.3em;
}
pre {
background: var(--black);
border-radius: 0.5rem;
color: var(--white);
font-family: 'JetBrainsMono', monospace;
margin: 1.5rem 0;
padding: 0.75rem 1rem;
code {
background: none;
color: inherit;
font-size: 0.8rem;
padding: 0;
}
/* Code styling */
.hljs-comment,
.hljs-quote {
color: #616161;
}
.hljs-variable,
.hljs-template-variable,
.hljs-attribute,
.hljs-tag,
.hljs-name,
.hljs-regexp,
.hljs-link,
.hljs-name,
.hljs-selector-id,
.hljs-selector-class {
color: #f98181;
}
.hljs-number,
.hljs-meta,
.hljs-built_in,
.hljs-builtin-name,
.hljs-literal,
.hljs-type,
.hljs-params {
color: #fbbc88;
}
.hljs-string,
.hljs-symbol,
.hljs-bullet {
color: #b9f18d;
}
.hljs-title,
.hljs-section {
color: #faf594;
}
.hljs-keyword,
.hljs-selector-tag {
color: #70cff8;
}
.hljs-emphasis {
font-style: italic;
}
.hljs-strong {
font-weight: 700;
}
}
.code-block {
position: relative;
select {
position: absolute;
background-color: var(--white);
background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="Black" d="M7 10l5 5 5-5z"/></svg>');
right: 0.5rem;
top: 0.5rem;
}
}
mark {
background-color: #FAF594;
border-radius: 0.4rem;
box-decoration-break: clone;
padding: 0.1rem 0.3rem;
}
img {
display: block;
height: auto;
margin: 1.5rem 0;
max-width: 100%;
&.ProseMirror-selectednode {
outline: 3px solid var(--purple);
transition: outline 0.15s;
}
}
blockquote {
border-left: 3px solid var(--gray-3);
margin: 1.5rem 0;
padding-left: 1rem;
}
hr {
border: none;
border-top: 1px solid var(--gray-2);
margin: 2rem 0;
}
.mention {
background-color: var(--purple-light);
border-radius: 0.4rem;
box-decoration-break: clone;
color: var(--purple);
padding: 0.1rem 0.3rem;
}
/* Color swatches */
.color {
white-space: nowrap;
&::before {
background-color: var(--color);
border: 1px solid rgba(128, 128, 128, 0.3);
border-radius: 2px;
content: " ";
display: inline-block;
height: 1em;
margin-bottom: 0.15em;
margin-right: 0.1em;
vertical-align: middle;
width: 1em;
}
}
/* Table-specific styling */
table {
border-collapse: collapse;
margin: 0;
overflow: hidden;
table-layout: fixed;
width: 100%;
td,
th {
border: 1px solid var(--gray-3);
box-sizing: border-box;
min-width: 1em;
padding: 6px 8px;
position: relative;
vertical-align: top;
> * {
margin-bottom: 0;
}
}
th {
background-color: var(--gray-1);
font-weight: bold;
text-align: left;
}
.selectedCell:after {
background: var(--gray-2);
content: "";
left: 0; right: 0; top: 0; bottom: 0;
pointer-events: none;
position: absolute;
z-index: 2;
}
.column-resize-handle {
background-color: var(--purple);
bottom: -2px;
pointer-events: none;
position: absolute;
right: -2px;
top: 0;
width: 4px;
}
}
.tableWrapper {
margin: 1.5rem 0;
overflow-x: auto;
}
&.resize-cursor {
cursor: ew-resize;
cursor: col-resize;
}
}
/* Floating/Bubble Menus */
.bubble-menu {
background-color: var(--white);
border: 1px solid var(--gray-1);
border-radius: 0.7rem;
box-shadow: var(--shadow);
display: flex;
padding: 0.2rem;
button {
background-color: unset;
&:hover {
background-color: var(--gray-3);
}
&.is-active {
background-color: var(--purple);
&:hover {
background-color: var(--purple-contrast);
}
}
}
}
.floating-menu {
display: flex;
background-color: var(--gray-3);
padding: 0.1rem;
border-radius: 0.5rem;
button {
background-color: unset;
padding: 0.275rem 0.425rem;
border-radius: 0.3rem;
&:hover {
background-color: var(--gray-3);
}
&.is-active {
background-color: var(--white);
color: var(--purple);
&:hover {
color: var(--purple-contrast);
}
}
}
}
</style>