rewirte in composition api to prevent this ref errors

This commit is contained in:
Julian Krauser 2025-07-18 15:18:09 +02:00
parent 08c3698dd8
commit e6dfde058d

View file

@ -3,126 +3,125 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { defineComponent, type PropType } from "vue"; import { markRaw, onMounted, onUnmounted, ref, useTemplateRef, watch, type PropType } from "vue";
import Quill, { Delta, type QuillOptions } from "quill"; import Quill, { Delta, type QuillOptions } from "quill";
import "quill/dist/quill.core.css"; import "quill/dist/quill.core.css";
import "quill/dist/quill.snow.css"; import "quill/dist/quill.snow.css";
import isEqual from "lodash.isequal";
import cloneDeep from "lodash.clonedeep";
type RangeStatic = { index: number; length: number }; type RangeStatic = { index: number; length: number };
</script>
<script lang="ts"> const quillElement = useTemplateRef("quill");
export default defineComponent({
props: {
options: {
type: Object as PropType<QuillOptions>,
default: {},
},
toolbar: {
type: [String, Array, Object],
default: "",
},
content: {
type: [String, Object] as PropType<string | Delta | null>,
default: "",
},
contentType: {
type: String as PropType<"delta" | "html" | "text">,
default: "text",
validator: (value: string) => {
return ["delta", "html", "text"].includes(value);
},
},
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,
};
},
computed: {
value: {
get(): string | Delta {
return this.content ?? "";
},
set(val: string | Delta) {
this.$emit("update:content", val);
},
},
},
mounted() {
this.instance = new Quill(this.$refs.quill as HTMLElement, {
theme: "snow",
modules: {
toolbar: this.toolbar,
},
placeholder: this.placeholder,
readOnly: this.readonly,
...this.options,
});
this.instance.on("selection-change", (range, oldRange, source) => { const instance = ref<undefined | Quill>();
if (!range) { const model = ref<string | Delta>("");
this.$emit("blur");
} else {
this.$emit("focus");
}
this.$emit("selectionChange", { range, oldRange, source });
});
this.instance.on("text-change", (delta, oldDelta, source) => {
this.value = this.getContent();
this.$emit("textChange", { delta, oldDelta, source });
});
this.$emit("ready", this.instance as Quill); const props = defineProps({
this.setContent(this.value); options: {
type: Object as PropType<QuillOptions>,
default: {},
}, },
beforeUnmount() { toolbar: {
this.instance = undefined; type: [String, Array, Object],
default: "",
}, },
methods: { content: {
getContent(): string | Delta { type: [String, Object] as PropType<string | Delta | null>,
if (this.contentType === "delta") { default: "",
return (this.instance?.getContents() ?? {}) as Delta; },
} else if (this.contentType === "html") { contentType: {
return this.instance?.getSemanticHTML() ?? ""; type: String as PropType<"delta" | "html" | "text">,
} else { default: "text",
return this.instance?.getText() ?? ""; validator: (value: string) => {
} return ["delta", "html", "text"].includes(value);
},
setContent(content: Delta | string) {
if (content == "") return;
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;
// this.instance.clipboard.dangerouslyPasteHTML(content);
// let delta = this.instance.clipboard.convert({ html: content });
// this.instance?.setContents(delta);
}
} else {
if (typeof content === "string") {
this.instance?.setText(content);
}
}
}, },
}, },
readonly: { type: Boolean, default: false },
placeholder: String,
}); });
const emit = defineEmits({
"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(
() => props.content,
(val, oldVal) => {
if (!instance.value || !val || isEqual(val, model.value)) return;
console.log("hi");
setContent(cloneDeep(val));
},
{ deep: true }
);
watch(model, (val, oldVal) => {
emit("update:content", cloneDeep(val));
});
onMounted(() => {
if (!quillElement.value) return;
const quill = new Quill(quillElement.value, {
theme: "snow",
modules: {
toolbar: props.toolbar,
},
placeholder: props.placeholder,
readOnly: props.readonly,
...props.options,
});
quill.on(Quill.events.SELECTION_CHANGE, (range, oldRange, source) => {
if (!range) {
emit("blur");
} else {
emit("focus");
}
console.log(range);
emit("selectionChange", { range, oldRange, source });
});
quill.on(Quill.events.TEXT_CHANGE, (delta, oldDelta, source) => {
model.value = getContent();
emit("textChange", { delta, oldDelta, source });
});
emit("ready", quill);
instance.value = markRaw(quill);
setContent(cloneDeep(props.content ?? ""));
});
onUnmounted(() => {
instance.value = undefined;
});
function getContent(): string | Delta {
if (props.contentType === "delta") {
return (instance.value?.getContents() ?? {}) as Delta;
} else if (props.contentType === "html") {
return instance.value?.getSemanticHTML() ?? "";
} else {
return instance.value?.getText() ?? "";
}
}
function setContent(content: Delta | string) {
if (content == "") return;
if (props.contentType === "delta") {
if (typeof content !== "string") {
instance.value?.setContents(content);
}
} else if (props.contentType === "html") {
if (typeof content === "string" && instance.value) {
instance.value.clipboard.dangerouslyPasteHTML(content);
let delta = instance.value.clipboard.convert({ html: content });
instance.value?.setContents(delta);
}
} else {
if (typeof content === "string") {
instance.value?.setText(content);
}
}
}
</script> </script>