Change implementation of content editable div

This commit is contained in:
Ramon Wenger 2022-02-23 23:45:47 +01:00
parent ad79285e20
commit 1043b647a0
1 changed files with 126 additions and 28 deletions

View File

@ -1,58 +1,150 @@
<template> <template>
<!-- eslint-disable vue/no-v-html --> <!-- eslint-disable vue/no-v-html -->
<div class="text-form"> <div class="text-form">
{{ isListLocal }}
<toggle
:checked="isListLocal"
label="Liste"
@input="isListLocal=$event"
/>
<button
class="button"
@click="makeList"
>
Test
</button>
<div <div
class="text-form__input skillbox-textarea" class="text-form__input skillbox-textarea"
contenteditable="true" contenteditable="true"
v-once
@input="change" @input="change"
v-text="text" v-text="text"
/> />
<textarea
:value="text" <div
class="text-form__input skillbox-textarea" contenteditable="true"
data-cy="text-form-input" class="text-form__input skillbox-textarea text-form__contenteditable"
placeholder="Text erfassen..." @input="changeSomeText"
@input="$emit('change-text', $event.target.value, index)" @blur="updateSomeText"
v-html="localOnlyText"
/> />
</div> </div>
</template> </template>
<script> <script lang="ts">
let selection; import Vue, {PropType} from "vue";
export default { import Toggle from "@/components/ui/Toggle.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
isListLocal: boolean,
// listHTML: string
}
const re = /<ul(\/)?>/;
export default Vue.extend({
props: { props: {
value: { value: {
type: Object, type: Object as PropType<Value>,
default: null, validator(value: Value) {
validator(value) {
return Object.prototype.hasOwnProperty.call(value, 'text'); return Object.prototype.hasOwnProperty.call(value, 'text');
}, },
}, },
index: { asList: {
type: Number, type: Boolean,
default: -1, default: false
}
}, },
components: {Toggle},
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: '',
isListLocal: false,
// listHTML: `<ul>
// <li>Hello</li>
// <li>World</li>
// </ul>`
};
}, },
computed: { computed: {
text() { text(): string {
return this.value.text ? this.value.text.replace(/<br(\/)?>/, '\n').replace(/(<([^>]+)>)/ig, '') : ''; return this.value.text ? this.value.text.replace(/<br(\/)?>/, '\n').replace(/(<([^>]+)>)/ig, '') : '';
}, },
html(): string {
return this.value.text;
},
isList(): boolean {
return this.asList || this.value.text.indexOf('<li>') > -1;
}
},
mounted() {
console.log('i was mounted now');
// sync this only once, when the component gets mounted
this.localOnlyText = this.text;
// when mounted, we also check if the text already contains some li or ul elements
this.isListLocal = this.asList || re.test(this.value.text);
}, },
methods: { methods: {
log(e) { updateSomeText() {
console.log(e); console.log('updating some text');
console.log('htmlBuffer', this.htmlBuffer);
this.localOnlyText = this.htmlBuffer;
}, },
change(event) { makeList() {
selection = window.getSelection().getRangeAt(0); // this.localOnlyText = this.localOnlyText
event.stopPropagation(); let sep = /\n+/;
event.preventDefault(); let lines = this.textBuffer.split(sep);
this.$emit('change-text', event.target.innerText, this.index); let listItems = lines.reduce((previous, current) => {
window.getSelection().addRange(selection); return `${previous}<li>${current}</li>`;
} }, '');
let list = `
<ul>
${listItems}
</ul>`;
console.log(list);
this.localOnlyText = list;
}, },
}; changeSomeText(event: Event) {
console.log('changing some text');
const target = (<HTMLInputElement>event.target);
let newHtml = target.innerHTML;
let newText = target.innerText;
this.htmlBuffer = newHtml;
this.textBuffer = newText;
console.log("target.innerText");
console.log(target.innerText);
console.log(target.innerText.indexOf('\n'));
console.log(target.innerHTML);
console.log('textBuffer', this.textBuffer);
console.log('htmlBuffer', this.htmlBuffer);
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">
@ -67,6 +159,12 @@
width: 100%; width: 100%;
height: 300px; height: 300px;
background-color: pink; background-color: pink;
display: flex;
flex-direction: column;
}
/deep/ ul {
list-style: initial;
} }
} }
</style> </style>