fix(front): add a required attribute to the text editor and show error message if text empty on blur
Also improve text editor borders Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
parent
ef20585f8c
commit
ba66874cc3
|
@ -32,6 +32,7 @@
|
||||||
v-if="currentActor"
|
v-if="currentActor"
|
||||||
:currentActor="currentActor"
|
:currentActor="currentActor"
|
||||||
:placeholder="t('Write a new message')"
|
:placeholder="t('Write a new message')"
|
||||||
|
:required="true"
|
||||||
/>
|
/>
|
||||||
<o-notification
|
<o-notification
|
||||||
class="my-2"
|
class="my-2"
|
||||||
|
|
|
@ -217,7 +217,15 @@
|
||||||
</button>
|
</button>
|
||||||
</bubble-menu>
|
</bubble-menu>
|
||||||
|
|
||||||
<editor-content class="editor__content" :editor="editor" v-if="editor" />
|
<editor-content
|
||||||
|
class="editor__content"
|
||||||
|
:class="{ editorErrorStatus }"
|
||||||
|
:editor="editor"
|
||||||
|
v-if="editor"
|
||||||
|
/>
|
||||||
|
<p v-if="editorErrorMessage" class="text-sm text-mbz-danger">
|
||||||
|
{{ editorErrorMessage }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -249,7 +257,7 @@ import Underline from "@tiptap/extension-underline";
|
||||||
import Link from "@tiptap/extension-link";
|
import Link from "@tiptap/extension-link";
|
||||||
import { AutoDir } from "./Editor/Autodir";
|
import { AutoDir } from "./Editor/Autodir";
|
||||||
// import sanitizeHtml from "sanitize-html";
|
// import sanitizeHtml from "sanitize-html";
|
||||||
import { computed, inject, onBeforeUnmount, watch } from "vue";
|
import { computed, inject, onBeforeUnmount, ref, watch } from "vue";
|
||||||
import { Dialog } from "@/plugins/dialog";
|
import { Dialog } from "@/plugins/dialog";
|
||||||
import { useI18n } from "vue-i18n";
|
import { useI18n } from "vue-i18n";
|
||||||
import { useMutation } from "@vue/apollo-composable";
|
import { useMutation } from "@vue/apollo-composable";
|
||||||
|
@ -279,11 +287,13 @@ const props = withDefaults(
|
||||||
currentActor: IPerson;
|
currentActor: IPerson;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
headingLevel?: Level[];
|
headingLevel?: Level[];
|
||||||
|
required?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
mode: "description",
|
mode: "description",
|
||||||
maxSize: 100_000_000,
|
maxSize: 100_000_000,
|
||||||
headingLevel: () => [3, 4, 5],
|
headingLevel: () => [3, 4, 5],
|
||||||
|
required: false,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -333,7 +343,7 @@ const editor = useEditor({
|
||||||
"aria-label": ariaLabel.value ?? "",
|
"aria-label": ariaLabel.value ?? "",
|
||||||
role: "textbox",
|
role: "textbox",
|
||||||
class:
|
class:
|
||||||
"prose dark:prose-invert prose-sm lg:prose-lg xl:prose-xl bg-zinc-50 dark:bg-zinc-700 focus:outline-none !max-w-full",
|
"prose dark:prose-invert prose-sm lg:prose-lg xl:prose-xl bg-white dark:bg-zinc-700 focus:outline-none !max-w-full",
|
||||||
},
|
},
|
||||||
transformPastedHTML: transformPastedHTML,
|
transformPastedHTML: transformPastedHTML,
|
||||||
},
|
},
|
||||||
|
@ -373,6 +383,13 @@ const editor = useEditor({
|
||||||
onUpdate: () => {
|
onUpdate: () => {
|
||||||
emit("update:modelValue", editor.value?.getHTML());
|
emit("update:modelValue", editor.value?.getHTML());
|
||||||
},
|
},
|
||||||
|
onBlur: () => {
|
||||||
|
checkEditorEmpty();
|
||||||
|
},
|
||||||
|
onFocus: () => {
|
||||||
|
editorErrorStatus.value = false;
|
||||||
|
editorErrorMessage.value = "";
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(value, (val: string) => {
|
watch(value, (val: string) => {
|
||||||
|
@ -470,6 +487,18 @@ defineExpose({ replyToComment, focus });
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
editor.value?.destroy();
|
editor.value?.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const editorErrorStatus = ref(false);
|
||||||
|
const editorErrorMessage = ref("");
|
||||||
|
|
||||||
|
const isEmpty = computed(
|
||||||
|
() => props.required === true && editor.value?.isEmpty === true
|
||||||
|
);
|
||||||
|
|
||||||
|
const checkEditorEmpty = () => {
|
||||||
|
editorErrorStatus.value = isEmpty.value;
|
||||||
|
editorErrorMessage.value = isEmpty.value ? t("You need to enter a text") : "";
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@use "@/styles/_mixins" as *;
|
@use "@/styles/_mixins" as *;
|
||||||
|
@ -525,7 +554,6 @@ onBeforeUnmount(() => {
|
||||||
min-height: 2.5rem;
|
min-height: 2.5rem;
|
||||||
box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1);
|
box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1);
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
border: 1px solid #dbdbdb;
|
|
||||||
padding: 12px 6px;
|
padding: 12px 6px;
|
||||||
|
|
||||||
&:focus {
|
&:focus {
|
||||||
|
@ -655,4 +683,15 @@ onBeforeUnmount(() => {
|
||||||
.mention[data-id] {
|
.mention[data-id] {
|
||||||
@apply inline-block border border-zinc-600 dark:border-zinc-300 rounded py-0.5 px-1;
|
@apply inline-block border border-zinc-600 dark:border-zinc-300 rounded py-0.5 px-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.editor__content {
|
||||||
|
@apply border focus:border-[#2563eb] rounded border-[#6b7280];
|
||||||
|
}
|
||||||
|
|
||||||
|
.editorErrorStatus {
|
||||||
|
@apply border-red-500;
|
||||||
|
}
|
||||||
|
.editor__content p.is-editor-empty:first-child::before {
|
||||||
|
@apply text-slate-300;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -1642,5 +1642,6 @@
|
||||||
"Visit {instance_domain}": "Visit {instance_domain}",
|
"Visit {instance_domain}": "Visit {instance_domain}",
|
||||||
"Software details: {software_details}": "Software details: {software_details}",
|
"Software details: {software_details}": "Software details: {software_details}",
|
||||||
"Only instances with an application actor can be followed": "Only instances with an application actor can be followed",
|
"Only instances with an application actor can be followed": "Only instances with an application actor can be followed",
|
||||||
"Domain or instance name": "Domain or instance name"
|
"Domain or instance name": "Domain or instance name",
|
||||||
|
"You need to enter a text": "You need to enter a text"
|
||||||
}
|
}
|
|
@ -1636,5 +1636,6 @@
|
||||||
"Visit {instance_domain}": "Visiter {instance_domain}",
|
"Visit {instance_domain}": "Visiter {instance_domain}",
|
||||||
"Software details: {software_details}": "Détails du logiciel : {software_details}",
|
"Software details: {software_details}": "Détails du logiciel : {software_details}",
|
||||||
"Only instances with an application actor can be followed": "Seules les instances avec un acteur application peuvent être suivies",
|
"Only instances with an application actor can be followed": "Seules les instances avec un acteur application peuvent être suivies",
|
||||||
"Domain or instance name": "Domaine ou nom de l'instance"
|
"Domain or instance name": "Domaine ou nom de l'instance",
|
||||||
|
"You need to enter a text": "Vous devez entrer un texte"
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue