mirror of
https://gitlab.com/Chill-Projet/chill-bundles.git
synced 2025-06-07 18:44:08 +00:00
Merge branch '321-text-editor-fallback' into 'master'
Text editor: Add a toggle button to switch to simple text editor and add emoji and fullscreen to ckeditor toolbar Closes #321 See merge request Chill-Projet/chill-bundles!827
This commit is contained in:
commit
992e6d29d0
6
.changes/unreleased/Feature-20250523-133341.yaml
Normal file
6
.changes/unreleased/Feature-20250523-133341.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
kind: Feature
|
||||
body: Add Emoji and Fullscreen feature to ckeditor configuration
|
||||
time: 2025-05-23T13:33:41.645095128+02:00
|
||||
custom:
|
||||
Issue: ""
|
||||
SchemaChange: No schema change
|
6
.changes/unreleased/Feature-20250523-133434.yaml
Normal file
6
.changes/unreleased/Feature-20250523-133434.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
kind: Feature
|
||||
body: Create editor which allow us to toggle between rich and simple text editor
|
||||
time: 2025-05-23T13:34:34.56795603+02:00
|
||||
custom:
|
||||
Issue: "321"
|
||||
SchemaChange: No schema change
|
@ -20,7 +20,7 @@
|
||||
"bindings": "^1.5.0",
|
||||
"bootstrap": "5.2.3",
|
||||
"chokidar": "^3.5.1",
|
||||
"ckeditor5": "^44.1.0",
|
||||
"ckeditor5": "^45.1.0",
|
||||
"dompurify": "^3.1.0",
|
||||
"eslint": "^9.14.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
|
@ -41,9 +41,7 @@ const i18nGendoc = _createI18n({});
|
||||
document.querySelectorAll("div[data-docgen-template-picker]").forEach((el) => {
|
||||
fetchTemplates(el.dataset.entityClass).then((templates) => {
|
||||
const picker = {
|
||||
template:
|
||||
'<pick-template :templates="this.templates" :preventDefaultMoveToGenerate="true" ' +
|
||||
':entityClass="faked" @go-to-generate-document="generateDoc"></pick-template>',
|
||||
template: `<pick-template :templates="this.templates" :preventDefaultMoveToGenerate="true" @go-to-generate-document="generateDoc"></pick-template>`,
|
||||
components: {
|
||||
PickTemplate,
|
||||
},
|
||||
@ -54,7 +52,7 @@ document.querySelectorAll("div[data-docgen-template-picker]").forEach((el) => {
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
generateDoc({ event, link, template }) {
|
||||
generateDoc({ link, template }) {
|
||||
console.log("generateDoc");
|
||||
console.log("link", link);
|
||||
console.log("template", template);
|
||||
|
@ -8,6 +8,9 @@ import {
|
||||
Heading,
|
||||
Link,
|
||||
List,
|
||||
Emoji,
|
||||
Mention,
|
||||
Fullscreen,
|
||||
} from "ckeditor5";
|
||||
import coreTranslations from "ckeditor5/translations/fr.js";
|
||||
|
||||
@ -26,6 +29,11 @@ export default {
|
||||
Link,
|
||||
List,
|
||||
Paragraph,
|
||||
// both Emoji and Mention are required for Emoji feature
|
||||
Emoji,
|
||||
Mention,
|
||||
// to enable fullscreen
|
||||
Fullscreen,
|
||||
],
|
||||
toolbar: {
|
||||
items: [
|
||||
@ -37,8 +45,13 @@ export default {
|
||||
"bulletedList",
|
||||
"numberedList",
|
||||
"blockQuote",
|
||||
"|",
|
||||
"emoji",
|
||||
"|",
|
||||
"undo",
|
||||
"redo",
|
||||
"|",
|
||||
"fullscreen",
|
||||
],
|
||||
},
|
||||
translations: [coreTranslations],
|
||||
|
@ -1,12 +1,32 @@
|
||||
import config from "./editor_config";
|
||||
import { ClassicEditor } from "ckeditor5";
|
||||
import { createApp } from "vue";
|
||||
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
|
||||
|
||||
const ckeditorFields: NodeListOf<HTMLTextAreaElement> =
|
||||
document.querySelectorAll("textarea[ckeditor]");
|
||||
ckeditorFields.forEach((field: HTMLTextAreaElement): void => {
|
||||
ClassicEditor.create(field, config).catch((error) => {
|
||||
console.error(error.stack);
|
||||
throw error;
|
||||
});
|
||||
const content = field.value;
|
||||
const div = document.createElement("div");
|
||||
|
||||
if (field.parentNode !== null) {
|
||||
field.parentNode.insertBefore(div, field);
|
||||
} else {
|
||||
throw "parent is null";
|
||||
}
|
||||
|
||||
createApp({
|
||||
components: { CommentEditor },
|
||||
template: `<comment-editor v-model="content" @input="handleInput"></comment-editor>`,
|
||||
data() {
|
||||
return {
|
||||
content,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleInput() {
|
||||
field.value = this.content;
|
||||
},
|
||||
},
|
||||
}).mount(div);
|
||||
|
||||
field.style.display = "none";
|
||||
});
|
||||
//Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea'));
|
||||
|
@ -0,0 +1,121 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, onUnmounted, ref } from "vue";
|
||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
|
||||
import { ClassicEditor } from "ckeditor5";
|
||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
|
||||
import {
|
||||
trans,
|
||||
EDITOR_SWITCH_TO_SIMPLE,
|
||||
EDITOR_SWITCH_TO_COMPLEX,
|
||||
} from "translator";
|
||||
|
||||
const EDITOR_MODE_KEY = "editorMode";
|
||||
const kind = ref<"simple" | "rich">("simple");
|
||||
const value = defineModel({ required: true });
|
||||
|
||||
const isSimple = computed(() => kind.value === "simple");
|
||||
|
||||
const toggleButtonClass = computed(() => {
|
||||
return {
|
||||
["toggle-button"]: true,
|
||||
onEditor: !isSimple.value,
|
||||
onSimple: isSimple.value,
|
||||
};
|
||||
});
|
||||
|
||||
const toggleEditor = () => {
|
||||
console.log("toggleEditor");
|
||||
let newValue;
|
||||
|
||||
newValue = kind.value === "simple" ? "rich" : "simple";
|
||||
kind.value = "rich";
|
||||
window.localStorage.setItem(EDITOR_MODE_KEY, newValue);
|
||||
|
||||
window.dispatchEvent(new Event("toggleEditorKind"));
|
||||
|
||||
console.log("new storage", window.localStorage.getItem(EDITOR_MODE_KEY));
|
||||
};
|
||||
|
||||
const onKindChange = function (/* event: StorageEvent | Event */) {
|
||||
const newValue = window.localStorage.getItem(EDITOR_MODE_KEY);
|
||||
|
||||
if (null === newValue || !(newValue === "rich" || newValue === "simple")) {
|
||||
throw "invalid new value: " + newValue;
|
||||
}
|
||||
|
||||
if (kind.value !== newValue) {
|
||||
kind.value = newValue;
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(function () {
|
||||
const storage = window.localStorage;
|
||||
const savedKind = storage.getItem(EDITOR_MODE_KEY);
|
||||
|
||||
if (
|
||||
null !== kind.value &&
|
||||
(savedKind === "simple" || savedKind === "rich")
|
||||
) {
|
||||
kind.value = savedKind;
|
||||
}
|
||||
|
||||
window.addEventListener("storage", onKindChange);
|
||||
window.addEventListener("toggleEditorKind", onKindChange);
|
||||
});
|
||||
|
||||
onUnmounted(function () {
|
||||
window.removeEventListener("storage", onKindChange);
|
||||
window.removeEventListener("toggleEditorKind", onKindChange);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="'rich' === kind">
|
||||
<Ckeditor
|
||||
:editor="ClassicEditor"
|
||||
:config="classicEditorConfig"
|
||||
v-model="value"
|
||||
/>
|
||||
</div>
|
||||
<div v-else>
|
||||
<textarea
|
||||
v-model="value"
|
||||
class="form-control simple-editor"
|
||||
></textarea>
|
||||
</div>
|
||||
|
||||
<button :class="toggleButtonClass" type="button" @click="toggleEditor">
|
||||
{{
|
||||
isSimple
|
||||
? trans(EDITOR_SWITCH_TO_COMPLEX)
|
||||
: trans(EDITOR_SWITCH_TO_SIMPLE)
|
||||
}}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.toggle-button {
|
||||
background: white;
|
||||
border: none;
|
||||
padding: 0.15rem;
|
||||
color: #069;
|
||||
cursor: pointer;
|
||||
|
||||
&.onEditor {
|
||||
position: relative;
|
||||
left: 1rem;
|
||||
top: -1.5rem;
|
||||
}
|
||||
&.onSimple {
|
||||
position: relative;
|
||||
top: -0.75rem;
|
||||
left: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
.simple-editor {
|
||||
min-height: 190px;
|
||||
}
|
||||
</style>
|
@ -915,3 +915,7 @@ multiselect:
|
||||
select_group_label: Appuyer sur "Entrée" pour sélectionner ce groupe
|
||||
deselect_group_label: Appuyer sur "Entrée" pour désélectionner ce groupe
|
||||
selected_label: Sélectionné'
|
||||
|
||||
editor:
|
||||
switch_to_simple: Éditeur simple
|
||||
switch_to_complex: Éditeur riche
|
||||
|
@ -13,14 +13,7 @@
|
||||
$t("comment.label")
|
||||
}}</label>
|
||||
|
||||
<ckeditor
|
||||
name="content"
|
||||
:placeholder="$t('comment.content')"
|
||||
:editor="classicEditor"
|
||||
:config="editorConfig"
|
||||
v-model="content"
|
||||
tag-name="textarea"
|
||||
/>
|
||||
<comment-editor v-model="content" />
|
||||
|
||||
<div class="sub-comment">
|
||||
<div
|
||||
@ -61,15 +54,13 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { ClassicEditor } from "ckeditor5";
|
||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
|
||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
|
||||
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
|
||||
import { mapState } from "vuex";
|
||||
|
||||
export default {
|
||||
name: "Comment",
|
||||
components: {
|
||||
ckeditor: Ckeditor,
|
||||
CommentEditor,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
@ -18,14 +18,7 @@
|
||||
</h3>
|
||||
</template>
|
||||
<template #body>
|
||||
<ckeditor
|
||||
name="content"
|
||||
:placeholder="$t('comment_placeholder')"
|
||||
:editor="editor"
|
||||
:config="editorConfig"
|
||||
v-model="content"
|
||||
tag-name="textarea"
|
||||
/>
|
||||
<comment-editor v-model="content" />
|
||||
</template>
|
||||
<template #footer>
|
||||
<a class="btn btn-save" @click="saveAction">
|
||||
@ -39,15 +32,13 @@
|
||||
<script>
|
||||
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
|
||||
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
|
||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
|
||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
|
||||
import { ClassicEditor } from "ckeditor5";
|
||||
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
|
||||
|
||||
export default {
|
||||
name: "WriteComment",
|
||||
components: {
|
||||
Modal,
|
||||
ckeditor: Ckeditor,
|
||||
CommentEditor,
|
||||
},
|
||||
props: ["resource"],
|
||||
emits: ["updateComment"],
|
||||
|
@ -34,22 +34,12 @@
|
||||
|
||||
<div id="privateComment" class="action-row">
|
||||
<label class="col-form-label">{{ $t("private_comment") }}</label>
|
||||
<ckeditor
|
||||
v-model="privateComment"
|
||||
:editor="classicEditor"
|
||||
:config="editorConfig"
|
||||
tag-name="textarea"
|
||||
></ckeditor>
|
||||
<comment-editor v-model="privateComment"></comment-editor>
|
||||
</div>
|
||||
|
||||
<div id="comment" class="action-row">
|
||||
<label class="col-form-label">{{ $t("comments") }}</label>
|
||||
<ckeditor
|
||||
v-model="note"
|
||||
:editor="classicEditor"
|
||||
:config="editorConfig"
|
||||
tag-name="textarea"
|
||||
></ckeditor>
|
||||
<comment-editor v-model="note"></comment-editor>
|
||||
</div>
|
||||
|
||||
<div id="objectives" class="action-row">
|
||||
@ -461,9 +451,7 @@
|
||||
|
||||
<script>
|
||||
import { mapState, mapGetters } from "vuex";
|
||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
|
||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
|
||||
import { ClassicEditor } from "ckeditor5";
|
||||
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
|
||||
import AddResult from "./components/AddResult.vue";
|
||||
import AddEvaluation from "./components/AddEvaluation.vue";
|
||||
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
|
||||
@ -529,7 +517,7 @@ const i18n = {
|
||||
export default {
|
||||
name: "App",
|
||||
components: {
|
||||
ckeditor: Ckeditor,
|
||||
CommentEditor,
|
||||
AddResult,
|
||||
AddEvaluation,
|
||||
AddPersons,
|
||||
|
@ -88,13 +88,7 @@
|
||||
$t("evaluation_public_comment")
|
||||
}}</label>
|
||||
<div class="col-sm-12">
|
||||
<ckeditor
|
||||
:editor="classicEditor"
|
||||
:config="editorConfig"
|
||||
:placeholder="$t('evaluation_comment_placeholder')"
|
||||
v-model="comment"
|
||||
tag-name="textarea"
|
||||
></ckeditor>
|
||||
<comment-editor v-model:value="comment"></comment-editor>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -273,9 +267,6 @@
|
||||
|
||||
<script>
|
||||
import { ISOToDatetime } from "ChillMainAssets/chill/js/date";
|
||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
|
||||
import { ClassicEditor } from "ckeditor5";
|
||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
|
||||
import { mapState } from "vuex";
|
||||
import PickTemplate from "ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue";
|
||||
import { buildLink } from "ChillDocGeneratorAssets/lib/document-generator";
|
||||
@ -284,6 +275,7 @@ import { buildLinkCreate } from "ChillMainAssets/lib/entity-workflow/api";
|
||||
import { buildLinkCreate as buildLinkCreateNotification } from "ChillMainAssets/lib/entity-notification/api";
|
||||
import DocumentActionButtonsGroup from "ChillDocStoreAssets/vuejs/DocumentActionButtonsGroup.vue";
|
||||
import DropFileModal from "ChillDocStoreAssets/vuejs/DropFileWidget/DropFileModal.vue";
|
||||
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
|
||||
|
||||
const i18n = {
|
||||
messages: {
|
||||
@ -322,8 +314,8 @@ export default {
|
||||
name: "FormEvaluation",
|
||||
props: ["evaluation", "docAnchorId"],
|
||||
components: {
|
||||
CommentEditor,
|
||||
DropFileModal,
|
||||
ckeditor: Ckeditor,
|
||||
PickTemplate,
|
||||
ListWorkflowModal,
|
||||
DocumentActionButtonsGroup,
|
||||
@ -371,8 +363,6 @@ export default {
|
||||
},
|
||||
computed: {
|
||||
...mapState(["isPosting", "work", "me"]),
|
||||
classicEditor: () => ClassicEditor,
|
||||
editorConfig: () => classicEditorConfig,
|
||||
AmIRefferer() {
|
||||
return !(
|
||||
this.$store.state.work.accompanyingPeriod.user &&
|
||||
|
@ -29,12 +29,7 @@
|
||||
</div>
|
||||
|
||||
<div class="item-row comment">
|
||||
<ckeditor
|
||||
:editor="classicEditor"
|
||||
:config="editorConfig"
|
||||
v-model="comment"
|
||||
tag-name="textarea"
|
||||
/>
|
||||
<comment-editor v-model="comment" />
|
||||
</div>
|
||||
|
||||
<div class="item-row participation-details">
|
||||
@ -103,15 +98,13 @@ div.participation-details {
|
||||
<script>
|
||||
import { mapGetters } from "vuex";
|
||||
import PersonRenderBox from "ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue";
|
||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
|
||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
|
||||
import { ClassicEditor } from "ckeditor5";
|
||||
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
|
||||
|
||||
export default {
|
||||
name: "MemberDetails",
|
||||
components: {
|
||||
PersonRenderBox,
|
||||
ckeditor: Ckeditor,
|
||||
CommentEditor,
|
||||
},
|
||||
props: ["conc"],
|
||||
computed: {
|
||||
|
@ -1,30 +1,17 @@
|
||||
<template>
|
||||
<ckeditor
|
||||
name="content"
|
||||
:placeholder="
|
||||
$t('household_members_editor.positioning.comment_placeholder')
|
||||
"
|
||||
:editor="editor"
|
||||
:config="editorConfig"
|
||||
v-model="content"
|
||||
tag-name="textarea"
|
||||
/>
|
||||
<comment-editor v-model="content" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { Ckeditor } from "@ckeditor/ckeditor5-vue";
|
||||
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
|
||||
import { ClassicEditor } from "ckeditor5";
|
||||
import CommentEditor from "ChillMainAssets/vuejs/_components/CommentEditor/CommentEditor.vue";
|
||||
|
||||
export default {
|
||||
name: "PersonComment.vue",
|
||||
components: {
|
||||
ckeditor: Ckeditor,
|
||||
CommentEditor,
|
||||
},
|
||||
props: ["conc"],
|
||||
computed: {
|
||||
editor: () => ClassicEditor,
|
||||
editorConfig: () => classicEditorConfig,
|
||||
content: {
|
||||
get() {
|
||||
return this.$props.conc.comment || "";
|
||||
|
@ -8,7 +8,7 @@ L'usager {{ oldPersonLocation|chill_entity_render_string }} a déménagé.
|
||||
Son adresse était utilisée pour localiser le parcours n°{{ period.id }}, dont vous êtes
|
||||
le référent.
|
||||
|
||||
En conséquence de ce déménage, le parcours est toujours localisé à cette adresse, mais à l'aide d'une
|
||||
En conséquence de ce déménagement, le parcours est toujours localisé à cette adresse, mais à l'aide d'une
|
||||
adresse temporaire.
|
||||
|
||||
Si vous continuez à suivre le parcours, vous pouvez le localiser à nouveau auprès de l'adresse de
|
||||
|
Loading…
x
Reference in New Issue
Block a user