Change regular text form to be tip tap, revert old component
This commit is contained in:
parent
031e4d58de
commit
233a2655f7
|
|
@ -52,7 +52,7 @@
|
||||||
const ImageForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/ImageForm');
|
const ImageForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/ImageForm');
|
||||||
const DocumentForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/DocumentForm');
|
const DocumentForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/DocumentForm');
|
||||||
const AssignmentForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/AssignmentForm');
|
const AssignmentForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/AssignmentForm');
|
||||||
const TextForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/TextForm');
|
const TextForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/TipTap.vue');
|
||||||
const SubtitleForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/SubtitleForm');
|
const SubtitleForm = () => import(/* webpackChunkName: "content-forms" */'@/components/content-forms/SubtitleForm');
|
||||||
|
|
||||||
const CHOOSER = 'content-block-element-chooser-widget';
|
const CHOOSER = 'content-block-element-chooser-widget';
|
||||||
|
|
|
||||||
|
|
@ -1,161 +1,37 @@
|
||||||
<template>
|
<template>
|
||||||
<!-- eslint-disable vue/no-v-html -->
|
|
||||||
<div class="text-form">
|
<div class="text-form">
|
||||||
<toggle
|
<textarea
|
||||||
:checked="isList"
|
:value="text"
|
||||||
label="Liste"
|
class="text-form__input skillbox-textarea"
|
||||||
@input="toggleList"
|
data-cy="text-form-input"
|
||||||
/>
|
placeholder="Text erfassen..."
|
||||||
|
@input="$emit('change-text', $event.target.value, index)"
|
||||||
<tip-tap
|
|
||||||
:value="value.text"
|
|
||||||
@input="$emit('change-text', $event)"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script>
|
||||||
import Vue, {PropType} from "vue";
|
export default {
|
||||||
import Toggle from "@/components/ui/Toggle.vue";
|
|
||||||
import TipTap from "@/components/content-forms/TipTap.vue";
|
|
||||||
|
|
||||||
interface Value {
|
|
||||||
text: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Data {
|
|
||||||
localOnlyText: string,
|
|
||||||
textToStoreWhileFocused: string,
|
|
||||||
textBuffer: string, // text that gets saved, but not updated, when the user is typing
|
|
||||||
htmlBuffer: string, // same as above, but this is the html that's generated
|
|
||||||
isList: boolean,
|
|
||||||
// listHTML: string
|
|
||||||
}
|
|
||||||
|
|
||||||
const ul = /<\/?ul>/;
|
|
||||||
const li = /<\/?li>/;
|
|
||||||
const br = /<br(\/)?>/;
|
|
||||||
const div = /(<\/?)div(>)/g;
|
|
||||||
const anythingElse = /<(?!\/?(?:ul|li|p))\/?([^>]*)>/;
|
|
||||||
|
|
||||||
// the non-breaking space is used so the cursor is positioned inside the element by default. We remove it later
|
|
||||||
const nonBreakingSpace = ' ';
|
|
||||||
const emptyParagraph = `<p>${nonBreakingSpace}</p>`;
|
|
||||||
const emptyList = `<ul><li>${nonBreakingSpace}</li></ul>`;
|
|
||||||
|
|
||||||
export default Vue.extend({
|
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: Object as PropType<Value>,
|
type: Object,
|
||||||
validator(value: Value) {
|
default: null,
|
||||||
|
validator(value) {
|
||||||
return Object.prototype.hasOwnProperty.call(value, 'text');
|
return Object.prototype.hasOwnProperty.call(value, 'text');
|
||||||
},
|
}
|
||||||
},
|
},
|
||||||
},
|
index: {
|
||||||
components: {TipTap, Toggle},
|
type: Number,
|
||||||
|
default: -1
|
||||||
data(): Data {
|
}
|
||||||
return {
|
},
|
||||||
// this is the text that's tracked by the input field. It will not update every time the prop updates, otherwise we keep losing focus
|
|
||||||
localOnlyText: '',
|
|
||||||
// this is the variable that stores our text while the user is typing and also updates the outer value
|
|
||||||
textToStoreWhileFocused: '', // we need to have a text that always updates for user input
|
|
||||||
textBuffer: '',
|
|
||||||
htmlBuffer: '',
|
|
||||||
isList: false,
|
|
||||||
// listHTML: `<ul>
|
|
||||||
// <li>Hello</li>
|
|
||||||
// <li>World</li>
|
|
||||||
// </ul>`
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
text(): string {
|
text() {
|
||||||
// if has ul / li, return as is
|
return this.value.text ? this.value.text.replace(/<br(\/)?>/, '\n').replace(/(<([^>]+)>)/ig, '') : '';
|
||||||
if (this.isList) {
|
}
|
||||||
return this.value.text;
|
}
|
||||||
} else {
|
};
|
||||||
// need p tags
|
|
||||||
return this.value.text ? this.value.text.replace(/<br(\/)?>/, '\n').replace(/(<([^>]+)>)/ig, '') : emptyParagraph;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
html(): string {
|
|
||||||
return this.value.text;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
console.log('i was mounted now');
|
|
||||||
// sync this only once, when the component gets mounted
|
|
||||||
this.isList = ul.test(this.value.text);
|
|
||||||
console.log(this.text);
|
|
||||||
this.localOnlyText = this.text;
|
|
||||||
this.textBuffer = this.text;
|
|
||||||
this.htmlBuffer = this.text;
|
|
||||||
// when mounted, we also check if the text already contains some li or ul elements
|
|
||||||
// after loading, we use the local data property to determine if we want a list or now
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
updateSomeText() {
|
|
||||||
console.log('htmlBuffer', this.htmlBuffer);
|
|
||||||
let newText = this.htmlBuffer.replace(div, '$1p$2')
|
|
||||||
.replace(nonBreakingSpace, '')
|
|
||||||
// .replace(br, '\n')
|
|
||||||
.replace(anythingElse, '');
|
|
||||||
if (newText === '') {
|
|
||||||
// if the new text would be empty, we replace it according to our needs
|
|
||||||
if (this.isList) {
|
|
||||||
newText = emptyList;
|
|
||||||
} else {
|
|
||||||
newText = emptyParagraph;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.localOnlyText = newText;
|
|
||||||
console.log(newText);
|
|
||||||
|
|
||||||
},
|
|
||||||
toggleList(checked: boolean) {
|
|
||||||
console.log(checked);
|
|
||||||
if (checked) {
|
|
||||||
this.makeList();
|
|
||||||
} else {
|
|
||||||
this.localOnlyText = this.textBuffer;
|
|
||||||
this.isList = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
makeList() {
|
|
||||||
// this.localOnlyText = this.localOnlyText
|
|
||||||
let sep = /\n+/;
|
|
||||||
let lines = this.textBuffer.split(sep);
|
|
||||||
let listItems = lines.reduce((previous, current) => {
|
|
||||||
return `${previous}<li>${current}</li>`;
|
|
||||||
}, '');
|
|
||||||
let list = `
|
|
||||||
<ul>
|
|
||||||
${listItems}
|
|
||||||
</ul>`;
|
|
||||||
console.log(list);
|
|
||||||
this.localOnlyText = list;
|
|
||||||
this.isList = true;
|
|
||||||
},
|
|
||||||
changeSomeText(event: Event) {
|
|
||||||
const target = (<HTMLInputElement>event.target);
|
|
||||||
let newHtml = target.innerHTML;
|
|
||||||
let newText = target.innerText;
|
|
||||||
this.htmlBuffer = newHtml;
|
|
||||||
this.textBuffer = newText;
|
|
||||||
this.$emit('change-text', newHtml);
|
|
||||||
},
|
|
||||||
change(event: Event) {
|
|
||||||
const target = (<HTMLInputElement>event.target);
|
|
||||||
const newText = target.innerText;
|
|
||||||
|
|
||||||
this.$emit('change-text', newText);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
@ -165,26 +41,5 @@
|
||||||
&__input {
|
&__input {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__contenteditable {
|
|
||||||
width: 100%;
|
|
||||||
height: 300px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/deep/ ul {
|
|
||||||
list-style: initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
/deep/ li {
|
|
||||||
@include inputfont;
|
|
||||||
}
|
|
||||||
|
|
||||||
/deep/ div {
|
|
||||||
@include inputfont;
|
|
||||||
}
|
|
||||||
|
|
||||||
/deep/ p {
|
|
||||||
@include inputfont;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,53 +1,70 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="tip-tap">
|
<div class="tip-tap">
|
||||||
<button @click="makeList">
|
<toggle
|
||||||
List
|
:bordered="false"
|
||||||
</button>
|
:checked="isList"
|
||||||
|
label="Als Liste formatieren"
|
||||||
|
@input="toggleList"
|
||||||
|
/>
|
||||||
|
|
||||||
<editor-content :editor="editor" />
|
<editor-content :editor="editor" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue, {PropType} from 'vue';
|
||||||
import {Editor, EditorContent} from "@tiptap/vue-2";
|
import {Editor, EditorContent} from "@tiptap/vue-2";
|
||||||
import Document from '@tiptap/extension-document';
|
import Document from '@tiptap/extension-document';
|
||||||
import Paragraph from '@tiptap/extension-paragraph';
|
import Paragraph from '@tiptap/extension-paragraph';
|
||||||
import Text from '@tiptap/extension-text';
|
import Text from '@tiptap/extension-text';
|
||||||
import BulletList from '@tiptap/extension-bullet-list';
|
import BulletList from '@tiptap/extension-bullet-list';
|
||||||
import ListItem from '@tiptap/extension-list-item';
|
import ListItem from '@tiptap/extension-list-item';
|
||||||
|
import Toggle from "@/components/ui/Toggle.vue";
|
||||||
|
|
||||||
interface Data {
|
interface Data {
|
||||||
editor: Editor | null;
|
editor: Editor | undefined;
|
||||||
|
}
|
||||||
|
interface Value {
|
||||||
|
text: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: String,
|
type: Object as PropType<Value>,
|
||||||
default: ''
|
validator(value: Value) {
|
||||||
}
|
return Object.prototype.hasOwnProperty.call(value, 'text');
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
Toggle,
|
||||||
EditorContent
|
EditorContent
|
||||||
},
|
},
|
||||||
|
|
||||||
data(): Data {
|
data(): Data {
|
||||||
return {
|
return {
|
||||||
editor: null,
|
editor: undefined,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
isList(): boolean {
|
||||||
|
return this.editor?.isActive('bulletList') || false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
watch: {
|
watch: {
|
||||||
value(newVal) {
|
value({text}: Value) {
|
||||||
const editor = this.editor as Editor; // editor is always initialized on mount, cast it
|
const editor = this.editor as Editor; // editor is always initialized on mount, cast it
|
||||||
const isSame = editor.getHTML() === newVal;
|
const isSame = editor.getHTML() === text;
|
||||||
|
|
||||||
if (isSame) {
|
if (isSame) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
editor.commands.setContent(newVal, false);
|
editor.commands.setContent(text, false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -68,7 +85,10 @@
|
||||||
ListItem
|
ListItem
|
||||||
],
|
],
|
||||||
onUpdate: () => {
|
onUpdate: () => {
|
||||||
this.$emit('input', (<Editor>this.editor).getHTML());
|
const newValue = {
|
||||||
|
text:(<Editor>this.editor).getHTML()
|
||||||
|
};
|
||||||
|
this.$emit('input', newValue);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
@ -78,10 +98,10 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
makeList() {
|
toggleList() {
|
||||||
const editor = this.editor as Editor;
|
const editor = this.editor as Editor;
|
||||||
editor.chain().selectAll().toggleBulletList().run();
|
editor.chain().selectAll().toggleBulletList().run();
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -95,5 +115,21 @@
|
||||||
@include inputstyle;
|
@include inputstyle;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/deep/ ul {
|
||||||
|
list-style: initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ li {
|
||||||
|
@include inputfont;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ div {
|
||||||
|
@include inputfont;
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ p {
|
||||||
|
@include inputfont;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue