replace Quill.js with tiptap (based on ProseMirror)
This commit is contained in:
@@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<div class="ui-rte" :disabled="disabled">
|
||||
<div ref="editor" :id="id" class="pell" v-show="!raw"></div>
|
||||
<div class="pell-raw" v-show="raw">
|
||||
<div class="pell-actionbar">
|
||||
<button class="pell-button" title="Accept" type="button" @click="acceptHtml"><i class="fth-check"></i></button>
|
||||
<button class="pell-button" title="Cancel" type="button" @click="raw = false"><i class="fth-x"></i></button>
|
||||
</div>
|
||||
<pre class="pell-raw-content"></pre>
|
||||
<div ref="editor" :id="id">
|
||||
<editor-menu-bubble :editor="editor" :keep-in-bounds="true" v-slot="{ commands, isActive, menu }">
|
||||
<div class="ui-rte-controls theme-dark" :class="{ 'is-active': menu.isActive }" :style="`left: ${menu.left}px; bottom: ${menu.bottom}px;`">
|
||||
<button type="button" class="ui-rte-control" :class="{ 'is-active': isActive.bold() }" @click="commands.bold">B</button>
|
||||
<button type="button" class="ui-rte-control" :class="{ 'is-active': isActive.italic() }" @click="commands.italic">I</button>
|
||||
<button type="button" class="ui-rte-control" :class="{ 'is-active': isActive.code() }" @click="commands.code">C</button>
|
||||
</div>
|
||||
</editor-menu-bubble>
|
||||
<editor-content class="ui-rte-input" :editor="editor" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -14,11 +16,20 @@
|
||||
|
||||
<script>
|
||||
import Strings from 'zero/helpers/strings.js';
|
||||
import Pell from './rte.pell.dependency.js';
|
||||
import { debounce as _debounce } from 'underscore';
|
||||
import { Editor, EditorContent, EditorMenuBubble } from 'tiptap';
|
||||
import
|
||||
{
|
||||
Blockquote, BulletList, CodeBlock, HardBreak, Heading, ListItem, OrderedList, TodoItem, TodoList,
|
||||
Bold, Code, Italic, Link, Strike, Underline, History
|
||||
}
|
||||
from 'tiptap-extensions';
|
||||
|
||||
export default {
|
||||
name: 'uiRte',
|
||||
|
||||
components: { EditorContent, EditorMenuBubble },
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
@@ -31,218 +42,132 @@
|
||||
},
|
||||
|
||||
data: () => ({
|
||||
raw: false,
|
||||
editor: null,
|
||||
id: 'rte-' + Strings.guid(),
|
||||
id: 'quill-' + Strings.guid(),
|
||||
blocked: false,
|
||||
actions: [
|
||||
'bold',
|
||||
'underline',
|
||||
'italic',
|
||||
'strikethrough',
|
||||
'olist',
|
||||
'ulist',
|
||||
'line'
|
||||
]
|
||||
onDebouncedChange: null,
|
||||
editor: null
|
||||
}),
|
||||
|
||||
watch: {
|
||||
value()
|
||||
{
|
||||
this.setValue();
|
||||
this.init();
|
||||
}
|
||||
},
|
||||
|
||||
created()
|
||||
{
|
||||
this.actions.push({
|
||||
name: 'link',
|
||||
result: this.onLinkCreate
|
||||
});
|
||||
|
||||
this.actions.push({
|
||||
name: 'raw',
|
||||
icon: '<i class="fth-edit-2"></i>',
|
||||
title: 'Edit HTML',
|
||||
result: () =>
|
||||
{
|
||||
this.$el.querySelector('.pell-raw-content').innerText = this.value;
|
||||
this.raw = true;
|
||||
}
|
||||
});
|
||||
this.onDebouncedChange = _debounce(this.onChange, 350);
|
||||
},
|
||||
|
||||
mounted()
|
||||
{
|
||||
this.initialize();
|
||||
this.editor = new Editor({
|
||||
extensions: [
|
||||
new CodeBlock(),
|
||||
new HardBreak(),
|
||||
new Link(),
|
||||
new Bold(),
|
||||
new Code(),
|
||||
new Italic(),
|
||||
new Strike(),
|
||||
new Underline(),
|
||||
new History(),
|
||||
],
|
||||
onUpdate: ({ getHTML }) =>
|
||||
{
|
||||
this.onDebouncedChange(getHTML());
|
||||
},
|
||||
});
|
||||
|
||||
this.init();
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
initialize()
|
||||
init()
|
||||
{
|
||||
this.$el.querySelector('.pell-raw-content').contentEditable = true;
|
||||
|
||||
this.editor = Pell.init({
|
||||
element: this.$refs.editor,
|
||||
defaultParagraphSeparator: 'p',
|
||||
onChange: html =>
|
||||
{
|
||||
this.blocked = true;
|
||||
this.$emit('input', html);
|
||||
this.blocked = false;
|
||||
//setValue(html);
|
||||
},
|
||||
actions: this.actions
|
||||
});
|
||||
|
||||
this.editor.content.innerHTML = this.value;
|
||||
//this.setValue();
|
||||
this.editor.setContent(this.value);
|
||||
},
|
||||
|
||||
|
||||
setValue()
|
||||
onChange(content)
|
||||
{
|
||||
if (this.editor.content.innerHTML !== this.value)
|
||||
{
|
||||
this.editor.content.innerHTML = this.value;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
onLinkCreate()
|
||||
{
|
||||
|
||||
},
|
||||
|
||||
|
||||
acceptHtml()
|
||||
{
|
||||
var html = this.$el.querySelector('.pell-raw-content').innerText;
|
||||
this.editor.content.innerHTML = html;
|
||||
this.raw = false;
|
||||
// TODO this will automatically set the cursor to the end, not desired ;-)
|
||||
this.$emit('input', content);
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy()
|
||||
{
|
||||
this.editor.destroy();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.ui-rte
|
||||
|
||||
.ui-rte-input
|
||||
{
|
||||
font-size: var(--font-size);
|
||||
height: auto;
|
||||
min-height: 42px;
|
||||
padding-top: 9px;
|
||||
|
||||
.pell,
|
||||
.pell-raw
|
||||
{
|
||||
background: var(--color-input);
|
||||
max-width: 800px;
|
||||
min-height: 42px;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
font-size: var(--font-size);
|
||||
display: inline-block;
|
||||
line-height: 1.5;
|
||||
color: var(--color-text);
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid transparent;
|
||||
|
||||
&:focus-within
|
||||
{
|
||||
background: var(--color-input-focus-bg) !important;
|
||||
border: var(--color-input-focus-border);
|
||||
box-shadow: var(--color-input-focus-shadow);
|
||||
outline: none;
|
||||
|
||||
.pell-button:hover,
|
||||
.pell-button-selected
|
||||
{
|
||||
background: var(--color-box-nested);
|
||||
color: var(--color-text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pell, .pell-content, .pell-raw
|
||||
{
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pell-content,
|
||||
.pell-raw-content
|
||||
{
|
||||
min-height: 60px;
|
||||
outline: none;
|
||||
overflow-y: auto;
|
||||
padding: 6px 12px 12px;
|
||||
max-height: 400px;
|
||||
}
|
||||
|
||||
pre.pell-raw-content
|
||||
p
|
||||
{
|
||||
margin: 0;
|
||||
background: none;
|
||||
border: none;
|
||||
word-wrap: normal;
|
||||
white-space: normal;
|
||||
font-family: Monaco,Menlo,Consolas,Courier New,monospace;
|
||||
font-size: var(--font-size);
|
||||
}
|
||||
|
||||
u-rte.-oneline .pell-content
|
||||
p + p
|
||||
{
|
||||
min-height: 46px;
|
||||
}
|
||||
|
||||
.pell-content
|
||||
{
|
||||
p
|
||||
{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
h3, h2
|
||||
{
|
||||
margin-bottom: .5em;
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.pell-content a, .pell-content a:hover, .pell-content a:visited, .pell-content a:focus
|
||||
{
|
||||
text-decoration: underline;
|
||||
color: var(--color-text);
|
||||
}
|
||||
|
||||
.pell-actionbar
|
||||
{
|
||||
border-radius: var(--radius) var(--radius) 0 0;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.pell-button
|
||||
{
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
font-size: 14px;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
.pell-button + .pell-button
|
||||
{
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.pell-button:hover,
|
||||
.pell-button-selected
|
||||
{
|
||||
background: var(--color-box);
|
||||
color: var(--color-text);
|
||||
margin-top: 1em;
|
||||
}
|
||||
}
|
||||
|
||||
.ui-rte-controls
|
||||
{
|
||||
position: absolute;
|
||||
display: flex;
|
||||
z-index: 20;
|
||||
background: var(--color-bg);
|
||||
border-radius: var(--radius);
|
||||
padding: 5px;
|
||||
margin-bottom: 8px;
|
||||
transform: translateX(-50%);
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s, visibility 0.2s;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ui-rte-controls.is-active
|
||||
{
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.ui-rte-control
|
||||
{
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: var(--color-text);
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
margin-right: 4px;
|
||||
border-radius: var(--radius);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ui-rte-control:hover
|
||||
{
|
||||
background-color: var(--color-bg-shade-1);
|
||||
}
|
||||
|
||||
.ui-rte-control.is-active
|
||||
{
|
||||
background-color: var(--color-bg-shade-1);
|
||||
color: var(--color-text-dim);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user