mirror of
https://github.com/IT4Change/Ocelot-Social.git
synced 2025-12-13 07:45:56 +00:00
Added basic editor under /post/edit/p1
This commit is contained in:
parent
99fbd52f6e
commit
4745577290
@ -1,5 +1,33 @@
|
||||
<template>
|
||||
<div class="editor">
|
||||
<editor-menu-bubble :editor="editor">
|
||||
<div
|
||||
slot-scope="{ commands, isActive, menu }"
|
||||
class="menububble tooltip"
|
||||
:class="{ 'is-active': menu.isActive }"
|
||||
:style="`left: ${menu.left}px; bottom: ${menu.bottom}px;`"
|
||||
>
|
||||
<ds-button
|
||||
class="menububble__button"
|
||||
size="small"
|
||||
:hover="isActive.bold()"
|
||||
ghost
|
||||
@click.prevent="commands.bold"
|
||||
>
|
||||
<ds-icon name="bold" />
|
||||
</ds-button>
|
||||
|
||||
<ds-button
|
||||
class="menububble__button"
|
||||
size="small"
|
||||
:hover="isActive.italic()"
|
||||
ghost
|
||||
@click.prevent="commands.italic"
|
||||
>
|
||||
<ds-icon name="italic" />
|
||||
</ds-button>
|
||||
</div>
|
||||
</editor-menu-bubble>
|
||||
<editor-floating-menu :editor="editor">
|
||||
<div
|
||||
slot-scope="{ commands, isActive, menu }"
|
||||
@ -76,7 +104,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Editor, EditorContent, EditorFloatingMenu } from 'tiptap'
|
||||
import stringHash from 'string-hash'
|
||||
import {
|
||||
Editor,
|
||||
EditorContent,
|
||||
EditorFloatingMenu,
|
||||
EditorMenuBubble
|
||||
} from 'tiptap'
|
||||
import EventHandler from './plugins/eventHandler.js'
|
||||
import {
|
||||
Heading,
|
||||
@ -95,18 +129,23 @@ import {
|
||||
History
|
||||
} from 'tiptap-extensions'
|
||||
|
||||
let throttleInputEvent
|
||||
|
||||
export default {
|
||||
components: {
|
||||
EditorContent,
|
||||
EditorFloatingMenu
|
||||
EditorFloatingMenu,
|
||||
EditorMenuBubble
|
||||
},
|
||||
props: {
|
||||
html: { type: String, default: '' },
|
||||
value: { type: String, default: '' },
|
||||
doc: { type: Object, default: () => {} }
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
lastValueHash: null,
|
||||
editor: new Editor({
|
||||
content: this.value || '',
|
||||
doc: this.doc,
|
||||
extensions: [
|
||||
new EventHandler(),
|
||||
@ -128,19 +167,28 @@ export default {
|
||||
emptyNodeText: 'Schreib etwas inspirerendes…'
|
||||
}),
|
||||
new History()
|
||||
]
|
||||
],
|
||||
onUpdate: e => {
|
||||
clearTimeout(throttleInputEvent)
|
||||
throttleInputEvent = setTimeout(() => this.onUpdate(e), 300)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
html: {
|
||||
value: {
|
||||
immediate: true,
|
||||
handler: function(html) {
|
||||
html = html
|
||||
.replace(/(<br\s*\/*>\s*){2,}/gim, '<br/>')
|
||||
.replace(/<\/p>\s*(<br\s*\/*>\s*)+\s*<p>/gim, '</p><p>')
|
||||
.replace(/<p>\s*(<br\s*\/*>\s*)+\s*<\/p>/gim, '')
|
||||
this.editor.setContent(html)
|
||||
handler: function(content, old) {
|
||||
const contentHash = stringHash(content)
|
||||
if (!content || contentHash === this.lastValueHash) {
|
||||
return
|
||||
}
|
||||
this.lastValueHash = contentHash
|
||||
// content = content
|
||||
// .replace(/(<br\s*\/*>\s*){2,}/gim, '<br/>')
|
||||
// .replace(/<\/p>\s*(<br\s*\/*>\s*)+\s*<p>/gim, '</p><p>')
|
||||
// .replace(/<p>\s*(<br\s*\/*>\s*)+\s*<\/p>/gim, '')
|
||||
this.editor.setContent(content)
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -148,9 +196,13 @@ export default {
|
||||
this.editor.destroy()
|
||||
},
|
||||
methods: {
|
||||
onDialog({ mark, key, name, focus }) {
|
||||
focus() // focus the editor if not already done to get the needed context
|
||||
this.$emit('dialog', { mark, key, name, focus })
|
||||
onUpdate(e) {
|
||||
const content = e.getHTML()
|
||||
const contentHash = stringHash(content)
|
||||
if (contentHash !== this.lastValueHash) {
|
||||
this.lastValueHash = contentHash
|
||||
this.$emit('input', content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -200,6 +252,35 @@ li > p {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
.menububble {
|
||||
position: absolute;
|
||||
// margin-top: -0.5rem;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: all 0.2s;
|
||||
transition-delay: 150ms;
|
||||
transform: translate(-50%, -25%);
|
||||
|
||||
background-color: $background-color-inverse-soft;
|
||||
// color: $text-color-inverse;
|
||||
border-radius: $border-radius-base;
|
||||
padding: $space-xx-small;
|
||||
box-shadow: $box-shadow-large;
|
||||
|
||||
.ds-button {
|
||||
color: $text-color-inverse;
|
||||
|
||||
&.ds-button-hover,
|
||||
&:hover {
|
||||
color: $text-color-base;
|
||||
}
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
// p:not(.is-empty) {
|
||||
// br:first-of-type,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Extension, Plugin } from 'tiptap'
|
||||
import { Slice } from 'prosemirror-model'
|
||||
// import { Slice, Fragment } from 'prosemirror-model'
|
||||
|
||||
export default class EventHandler extends Extension {
|
||||
get name() {
|
||||
@ -9,23 +9,58 @@ export default class EventHandler extends Extension {
|
||||
return [
|
||||
new Plugin({
|
||||
props: {
|
||||
handleDOMEvents: {
|
||||
drop(view, event) {
|
||||
event.stopImmediatePropagation()
|
||||
},
|
||||
paste(view, event, slice) {
|
||||
console.log('#### PASTE', slice)
|
||||
return 'ABC'
|
||||
}
|
||||
},
|
||||
transformPasted(slice) {
|
||||
console.log('#### transformPasted', slice)
|
||||
return new Slice(slice.content, slice.openStart, slice.openEnd)
|
||||
transformPastedText(text) {
|
||||
// console.log('#### transformPastedText', text)
|
||||
return text.trim()
|
||||
},
|
||||
transformPastedHTML(html) {
|
||||
console.log('#### transformPastedHTML', html)
|
||||
return 'HELLO WORLD'
|
||||
html = html
|
||||
.replace(/(<br\s*\/*>\s*){2,}/gim, '<br/>')
|
||||
.replace(
|
||||
/<\/(p|div|th|tr)>\s*(<br\s*\/*>\s*)+\s*<(p|div|th|tr)>/gim,
|
||||
'</p><p>'
|
||||
)
|
||||
.replace(
|
||||
/<(p|div|th|tr)>\s*(<br\s*\/*>\s*)+\s*<\/(p|div|th|tr)>/gim,
|
||||
''
|
||||
)
|
||||
.replace(/<(p|div|th|tr)>\s*/gim, '<p>')
|
||||
.replace(/\s*<\/(p|div|th|tr)>/gim, '</p>')
|
||||
|
||||
// console.log('#### transformPastedHTML', html)
|
||||
return html
|
||||
}
|
||||
// transformPasted(slice) {
|
||||
// // console.log('#### transformPasted', slice.content)
|
||||
// let content = []
|
||||
// let size = 0
|
||||
// slice.content.forEach((node, offset, index) => {
|
||||
// // console.log(node)
|
||||
// // console.log('isBlock', node.type.isBlock)
|
||||
// // console.log('childCount', node.content.childCount)
|
||||
// // console.log('index', index)
|
||||
// if (node.content.childCount) {
|
||||
// content.push(node.content)
|
||||
// size += node.content.size
|
||||
// }
|
||||
// })
|
||||
// console.log(content)
|
||||
// console.log(slice.content)
|
||||
// let fragment = Fragment.fromArray(content)
|
||||
// fragment.size = size
|
||||
// console.log('#fragment', fragment, slice.content)
|
||||
// console.log('----')
|
||||
// console.log('#1', slice)
|
||||
// // const newSlice = new Slice(fragment, slice.openStart, slice.openEnd)
|
||||
// slice.fragment = fragment
|
||||
// // slice.content.content = fragment.content
|
||||
// // slice.content.size = fragment.size
|
||||
// console.log('#2', slice)
|
||||
// // console.log(newSlice)
|
||||
// console.log('----')
|
||||
// return slice
|
||||
// // return newSlice
|
||||
// }
|
||||
}
|
||||
})
|
||||
]
|
||||
|
||||
@ -53,6 +53,7 @@
|
||||
"nuxt-env": "^0.0.4",
|
||||
"nuxt-sass-resources-loader": "^2.0.5",
|
||||
"portal-vue": "~1.5.1",
|
||||
"string-hash": "^1.1.3",
|
||||
"tiptap": "~1.8.0",
|
||||
"tiptap-extensions": "~1.8.0",
|
||||
"v-tooltip": "^2.0.0-rc.33",
|
||||
|
||||
@ -6,8 +6,20 @@
|
||||
<ds-flex-item :width="{ base: '100%', sm: 3, md: 5, lg: 3 }">
|
||||
<ds-card>
|
||||
<no-ssr>
|
||||
<hc-editor :html="htmlContent" />
|
||||
<hc-editor v-model="content" />
|
||||
</no-ssr>
|
||||
<div
|
||||
slot="footer"
|
||||
style="text-align: right"
|
||||
>
|
||||
<ds-button
|
||||
icon="check"
|
||||
primary
|
||||
@click="save"
|
||||
>
|
||||
Speichern
|
||||
</ds-button>
|
||||
</div>
|
||||
</ds-card>
|
||||
</ds-flex-item>
|
||||
<ds-flex-item :width="{ base: '100%', sm: 2, md: 2, lg: 1 }">
|
||||
@ -24,11 +36,32 @@ export default {
|
||||
components: {
|
||||
HcEditor
|
||||
},
|
||||
computed: {
|
||||
htmlContent() {
|
||||
return this.Post ? this.Post[0].content : ''
|
||||
data() {
|
||||
return {
|
||||
content: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
Post: {
|
||||
immediate: true,
|
||||
handler: function(post) {
|
||||
console.log('try to set content', this.content, post)
|
||||
if (!post || !post[0].content) {
|
||||
return
|
||||
}
|
||||
console.log(post[0].content)
|
||||
this.content = post[0].content
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
save() {
|
||||
console.log(this.content)
|
||||
}
|
||||
//onUpdate(data) {
|
||||
// console.log('onUpdate', data)
|
||||
//}
|
||||
},
|
||||
apollo: {
|
||||
Post: {
|
||||
query() {
|
||||
|
||||
@ -10852,6 +10852,11 @@ string-argv@0.0.2:
|
||||
resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.0.2.tgz#dac30408690c21f3c3630a3ff3a05877bdcbd736"
|
||||
integrity sha1-2sMECGkMIfPDYwo/86BYd73L1zY=
|
||||
|
||||
string-hash@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b"
|
||||
integrity sha1-6Kr8CsGFW0Zmkp7X3RJ1311sgRs=
|
||||
|
||||
string-length@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-2.0.0.tgz#d40dbb686a3ace960c1cffca562bf2c45f8363ed"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user