1<template>
2 <div class="rounded-4 p-3 shadow-sm w-full">
3 <Editor
4 ref="inner"
5 v-model="content"
6 :extensions="extensions"
7 :upload-function="uploadFunction"
8 :autofocus="autofocus"
9 >
10 <template #default="{ editor, isEmpty }">
11 <span class="text-base">
12 <span class="flex items-center justify-between">
13 <UserAvatar
14 :name="authStore.userName"
15 :image="authStore.userImage"
16 expand
17 strong
18 />
19 <slot name="top-right" />
20 </span>
21 <slot name="top-bottom" />
22 </span>
23
24 <EditorBubbleMenu :items="commentToolbar" />
25 <!-- Scroll here so selected nodes aren't clipped. -->
26 <div class="-mx-3 px-3 max-h-64 overflow-auto my-4">
27 <EditorContent
28 :class="['prose-f max-w-none min-h-[5rem]', getFontFamily(content)]"
29 />
30 </div>
31
32 <div class="flex flex-col gap-2">
33 <slot name="bottom-top" />
34 <div
35 class="flex flex-col space-y-1.5 overflow-auto sm:flex-row sm:justify-between"
36 >
37 <div class="flex items-center">
38 <slot name="bottom-left" />
39 <EditorFixedMenu :items="ticketToolbar" />
40 </div>
41 <div class="flex items-center gap-2">
42 <Button
43 :label="__('Discard')"
44 theme="gray"
45 variant="subtle"
46 v-if="!isEmpty"
47 @click="
48 () => {
49 editor.commands.clearContent(true);
50 $emit('clear');
51 }
52 "
53 />
54 <slot name="bottom-right" />
55 </div>
56 </div>
57 </div>
58 </template>
59 </Editor>
60 </div>
61</template>
62<script setup lang="ts">
63import { UserAvatar } from "@/components";
64import { useAuthStore } from "@/stores/auth";
65import { __ } from "@/translation";
66import { getFontFamily } from "@/utils";
67import { Button } from "frappe-ui";
68import {
69 Editor,
70 EditorBubbleMenu,
71 EditorContent,
72 EditorFixedMenu,
73} from "frappe-ui/editor";
74import { computed, ref } from "vue";
75import {
76 buildEditorExtensions,
77 commentToolbar,
78 ticketToolbar,
79} from "./editor/config";
80
81interface P {
82 modelValue: string;
83 autofocus?: boolean;
84 uploadFunction?: (file: any, options?: any) => Promise<any>;
85}
86
87interface E {
88 (event: "clear"): void;
89 (event: "update:modelValue", value: string): void;
90}
91
92const props = withDefaults(defineProps<P>(), {
93 autofocus: false,
94});
95
96const emit = defineEmits<E>();
97
98const authStore = useAuthStore();
99const inner = ref(null);
100
101const content = computed({
102 get: () => props.modelValue,
103 set: (value) => emit("update:modelValue", value),
104});
105
106const extensions = buildEditorExtensions();
107
108const editor = computed(() => inner.value?.editor);
109
110defineExpose({
111 editor,
112});
113</script>
114