Improve TextEditor

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2022-10-11 17:47:53 +02:00
parent ff1fbdfdba
commit e22cb8c3bd
No known key found for this signature in database
GPG key ID: A061B9DDE0CA0773

View file

@ -13,7 +13,7 @@
>
<button
class="menubar__button"
:class="{ 'is-active': editor.isActive('bold') }"
:class="{ 'is-active': editor?.isActive('bold') }"
@click="editor?.chain().focus().toggleBold().run()"
type="button"
:title="t('Bold')"
@ -23,7 +23,7 @@
<button
class="menubar__button"
:class="{ 'is-active': editor.isActive('italic') }"
:class="{ 'is-active': editor?.isActive('italic') }"
@click="editor?.chain().focus().toggleItalic().run()"
type="button"
:title="t('Italic')"
@ -33,7 +33,7 @@
<button
class="menubar__button"
:class="{ 'is-active': editor.isActive('underline') }"
:class="{ 'is-active': editor?.isActive('underline') }"
@click="editor?.chain().focus().toggleUnderline().run()"
type="button"
:title="t('Underline')"
@ -44,7 +44,7 @@
<button
v-if="!isBasicMode"
class="menubar__button"
:class="{ 'is-active': editor.isActive('heading', { level: 1 }) }"
:class="{ 'is-active': editor?.isActive('heading', { level: 1 }) }"
@click="editor?.chain().focus().toggleHeading({ level: 1 }).run()"
type="button"
:title="t('Heading Level 1')"
@ -55,7 +55,7 @@
<button
v-if="!isBasicMode"
class="menubar__button"
:class="{ 'is-active': editor.isActive('heading', { level: 2 }) }"
:class="{ 'is-active': editor?.isActive('heading', { level: 2 }) }"
@click="editor?.chain().focus().toggleHeading({ level: 2 }).run()"
type="button"
:title="t('Heading Level 2')"
@ -66,7 +66,7 @@
<button
v-if="!isBasicMode"
class="menubar__button"
:class="{ 'is-active': editor.isActive('heading', { level: 3 }) }"
:class="{ 'is-active': editor?.isActive('heading', { level: 3 }) }"
@click="editor?.chain().focus().toggleHeading({ level: 3 }).run()"
type="button"
:title="t('Heading Level 3')"
@ -77,7 +77,7 @@
<button
class="menubar__button"
@click="showLinkMenu()"
:class="{ 'is-active': editor.isActive('link') }"
:class="{ 'is-active': editor?.isActive('link') }"
type="button"
:title="t('Add link')"
>
@ -85,7 +85,7 @@
</button>
<button
v-if="editor.isActive('link')"
v-if="editor?.isActive('link')"
class="menubar__button"
@click="editor?.chain().focus().unsetLink().run()"
type="button"
@ -107,7 +107,7 @@
<button
class="menubar__button"
v-if="!isBasicMode"
:class="{ 'is-active': editor.isActive('bulletList') }"
:class="{ 'is-active': editor?.isActive('bulletList') }"
@click="editor?.chain().focus().toggleBulletList().run()"
type="button"
:title="t('Bullet list')"
@ -118,7 +118,7 @@
<button
v-if="!isBasicMode"
class="menubar__button"
:class="{ 'is-active': editor.isActive('orderedList') }"
:class="{ 'is-active': editor?.isActive('orderedList') }"
@click="editor?.chain().focus().toggleOrderedList().run()"
type="button"
:title="t('Ordered list')"
@ -129,7 +129,7 @@
<button
v-if="!isBasicMode"
class="menubar__button"
:class="{ 'is-active': editor.isActive('blockquote') }"
:class="{ 'is-active': editor?.isActive('blockquote') }"
@click="editor?.chain().focus().toggleBlockquote().run()"
type="button"
:title="t('Quote')"
@ -193,7 +193,7 @@
</template>
<script lang="ts" setup>
import { Editor, EditorContent, BubbleMenu } from "@tiptap/vue-3";
import { useEditor, EditorContent, BubbleMenu } from "@tiptap/vue-3";
import Blockquote from "@tiptap/extension-blockquote";
import BulletList from "@tiptap/extension-bullet-list";
import Heading from "@tiptap/extension-heading";
@ -254,8 +254,6 @@ const props = withDefaults(
const emit = defineEmits(["update:modelValue"]);
const editor = ref<Editor | null>(null);
const isDescriptionMode = computed((): boolean => {
return props.mode === "description" || isBasicMode.value;
});
@ -278,13 +276,28 @@ const isBasicMode = computed((): boolean => {
// const observer = ref<MutationObserver | null>(null);
onMounted(() => {
editor.value = new Editor({
const transformPastedHTML = (html: string): string => {
// When using comment mode, limit to acceptable tags
if (isCommentMode.value) {
// return sanitizeHtml(html, {
// allowedTags: ["b", "i", "em", "strong", "a"],
// allowedAttributes: {
// a: ["href", "rel", "target"],
// },
// });
return html;
}
return html;
};
const editor = useEditor({
editorProps: {
attributes: {
"aria-multiline": isShortMode.value.toString(),
"aria-label": props.ariaLabel ?? "",
role: "textbox",
class:
"prose dark:prose-invert prose-sm sm:prose lg:prose-lg xl:prose-xl m-5 focus:outline-none !max-w-full",
},
transformPastedHTML: transformPastedHTML,
},
@ -316,23 +329,8 @@ onMounted(() => {
onUpdate: () => {
emit("update:modelValue", editor.value?.getHTML());
},
});
});
const transformPastedHTML = (html: string): string => {
// When using comment mode, limit to acceptable tags
if (isCommentMode.value) {
// return sanitizeHtml(html, {
// allowedTags: ["b", "i", "em", "strong", "a"],
// allowedAttributes: {
// a: ["href", "rel", "target"],
// },
// });
return html;
}
return html;
};
const value = computed(() => props.modelValue);
watch(value, (val: string) => {
@ -351,6 +349,7 @@ const { t } = useI18n({ useScope: "global" });
const showLinkMenu = (): void => {
dialog?.prompt({
message: t("Enter the link URL"),
hasInput: true,
inputAttrs: {
type: "url",
},