add QuillEditor Component

This commit is contained in:
Julian Krauser 2025-07-17 12:56:33 +02:00
parent 075c6cc7e4
commit a2101db747
3 changed files with 187 additions and 29 deletions

View file

@ -0,0 +1,110 @@
<template>
<div ref="quill"></div>
</template>
<script setup lang="ts">
import { defineComponent, type PropType } from "vue";
import Quill, { Delta } from "quill";
import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css";
type RangeStatic = { index: number; length: number };
</script>
<script lang="ts">
export default defineComponent({
props: {
toolbar: {
type: [String, Array, Object],
default: "",
},
modules: {
type: Object as PropType<Record<string, unknown>>,
default: {},
},
content: {
type: [String, Object] as PropType<string | Delta>,
default: "",
},
contentType: {
type: String as PropType<"delta" | "html" | "text">,
default: "text",
},
readonly: { type: Boolean, default: false },
placeholder: String,
},
emits: {
"update:content": (content: Delta | string) => true,
textChange: (tc: { delta: Delta; oldDelta: Delta; source: string }) => true,
selectionChange: (sc: { range: RangeStatic; oldRange: RangeStatic; source: string }) => true,
focus: () => true,
blur: () => true,
ready: (quill: Quill) => true,
},
watch: {
conent(val, oldVal) {
this.setContent(val);
},
},
data() {
return {
instance: undefined as undefined | Quill,
};
},
mounted() {
this.instance = new Quill(this.$refs.quill as HTMLElement, {
theme: "snow",
modules: {
toolbar: this.toolbar,
},
placeholder: this.placeholder,
readOnly: this.readonly,
});
this.instance.on("selection-change", (range, oldRange, source) => {
if (!range) {
this.$emit("blur");
} else {
this.$emit("focus");
}
this.$emit("selectionChange", { range, oldRange, source });
});
this.instance.on("text-change", (delta, oldDelta, source) => {
this.$emit("update:content", this.getContent());
this.$emit("textChange", { delta, oldDelta, source });
});
this.$emit("ready", this.instance as Quill);
this.setContent(this.content);
},
beforeUnmount() {
this.instance = undefined;
},
methods: {
getContent(): string | Delta {
if (this.contentType === "delta") {
return (this.instance?.getContents() ?? {}) as Delta;
} else if (this.contentType === "html") {
return this.instance?.getSemanticHTML() ?? "";
} else {
return this.instance?.getText() ?? "";
}
},
setContent(content: Delta | string) {
if (this.contentType === "delta") {
if (typeof content !== "string") {
this.instance?.setContents(content);
}
} else if (this.contentType === "html") {
if (typeof content === "string" && this.instance) {
this.instance.root.innerHTML = content;
}
} else {
if (typeof content === "string") {
this.instance?.setText(content);
}
}
},
},
});
</script>