awareness on quill editor

This commit is contained in:
Julian Krauser 2025-03-05 09:52:21 +01:00
parent 27e3ed525b
commit e9e38db606
5 changed files with 79 additions and 10 deletions

View file

@ -35,6 +35,14 @@ export default defineComponent({
localStorage.removeItem("refresh_token");
});
}
window.addEventListener("online", function (e) {
e.preventDefault();
});
},
beforeUnmount() {
window.removeEventListener("online", function (e) {
e.preventDefault();
});
},
});
</script>

View file

@ -2,7 +2,14 @@
<div class="flex flex-col">
<div class="flex flex-row gap-2 items-center">
<label :for="title">{{ title }}</label>
<lottie-player src="/typing_animation.json" class="w-fit h-5" loop autoplay />
<lottie-player
v-if="currentEditors.length != 0"
src="/typing_animation.json"
class="w-fit h-5"
loop
autoplay
v-tippy="currentEditors.map((c) => c.username).join(', ')"
/>
</div>
<QuillEditor
:id="title"
@ -11,6 +18,8 @@
contentType="html"
:options="{ modules: moduleOptions }"
@ready="initEditor"
@selectionChange="selectionChange"
@blur="blured"
/>
<!--
:enable="can('create', 'operation', 'mission')"
@ -28,7 +37,7 @@ import { QuillBinding } from "y-quill";
import { moduleOptions } from "@/helpers/quillConfig";
import * as Y from "yjs";
import "@lottiefiles/lottie-player";
import { Awareness } from "@/helpers/awareness";
import { Awareness, type AwarenessActions, type EditorRange, type EditorState } from "@/helpers/awareness";
</script>
<script lang="ts">
@ -39,7 +48,7 @@ export default defineComponent({
required: true,
},
awareness: {
type: Object as PropType<Partial<Awareness>>,
type: Object as PropType<Awareness>,
required: true,
},
title: {
@ -53,18 +62,64 @@ export default defineComponent({
cursors: undefined as undefined | QuillCursors,
};
},
computed: {
currentEditors() {
return this.awareness.getEditorObjsByField(this.title);
},
},
mounted() {
this.awareness.emitter.on("change", this.handleChange);
this.awareness.emitter.on("clear", this.clear);
},
beforeUnmount() {
if (this.binding) {
this.binding.destroy();
this.binding = undefined;
}
this.awareness.emitter.off("change", this.handleChange);
this.awareness.emitter.off("clear", this.clear);
},
methods: {
initEditor(quill: Quill) {
quill.history.clear();
this.binding = new QuillBinding(this.text, quill); // TODO: awareness
this.binding = new QuillBinding(this.text, quill);
this.cursors = quill.getModule("cursors") as QuillCursors;
},
selectionChange({ range, oldRange, source }: { range: EditorRange; oldRange: any; source: string }) {
if (range != null) {
this.awareness.publishMyState({
field: this.title,
range: range,
});
}
},
blured() {
this.awareness.publishMyState({
field: "blured user",
});
},
handleChange(
d: {
socketId: string;
action: AwarenessActions;
} & EditorState
) {
if (d.action == "remove") {
this.cursors?.removeCursor(d.socketId);
}
if (d.field == this.title) {
let user = this.awareness.getEditor(d.socketId);
if (!user || !d.range) return;
this.cursors?.createCursor(d.socketId, user.username, user.color);
this.cursors?.moveCursor(d.socketId, d.range);
this.cursors?.toggleFlag(d.socketId, true);
} else if (d.field == "blured user") {
this.cursors?.removeCursor(d.socketId);
}
},
clear() {
this.cursors?.clearCursors();
},
},
});
</script>

View file

@ -75,15 +75,11 @@ export default defineComponent({
focused() {
this.awareness.publishMyState({
field: this.title,
cursor: undefined,
range: undefined,
});
},
blured() {
this.awareness.publishMyState({
field: "blured user",
cursor: undefined,
range: undefined,
});
},
},

View file

@ -9,8 +9,12 @@ export interface Editor {
export interface EditorState {
field: string;
cursor: any;
range: any;
cursor?: number;
range?: EditorRange;
}
export interface EditorRange {
index: number;
length: number;
}
export type AwarenessActions = "update" | "remove";
@ -18,6 +22,7 @@ export type AwarenessActions = "update" | "remove";
export type AwarenessEvents = {
update: { data: EditorState };
change: { socketId: string; action: AwarenessActions } & EditorState;
clear: void;
};
export class Awareness {
@ -73,6 +78,7 @@ export class Awareness {
public reset() {
this.editors.clear();
this.editorStates.clear();
this.emitter.emit("clear");
}
public destroy() {

View file

@ -149,3 +149,7 @@ summary::-webkit-details-marker {
@apply !order-1;
}
}
.ql-cursor-caret-container {
z-index: 1 !important;
}