Change implementation of content editable div
This commit is contained in:
parent
ad79285e20
commit
1043b647a0
|
|
@ -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;
|
||||||
|
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
},
|
||||||
|
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);
|
||||||
},
|
},
|
||||||
change(event) {
|
|
||||||
selection = window.getSelection().getRangeAt(0);
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
this.$emit('change-text', event.target.innerText, this.index);
|
|
||||||
window.getSelection().addRange(selection);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
|
@ -64,9 +156,15 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&__contenteditable {
|
&__contenteditable {
|
||||||
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>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue