Refactor CKEditor integration across application

Replaced `ClassicEditor` with a consistent `classicEditor` and added centralized editor configuration using `editorConfig`. Simplified webpack configuration by removing CKEditor-specific setups and dependencies, improving maintainability.
This commit is contained in:
Julien Fastré 2025-01-30 11:09:45 +01:00 committed by Julie Lenaerts
parent 9df127a82c
commit 183a220e7b
11 changed files with 71 additions and 116 deletions

View File

@ -6,12 +6,6 @@
"@apidevtools/swagger-cli": "^4.0.4", "@apidevtools/swagger-cli": "^4.0.4",
"@babel/core": "^7.20.5", "@babel/core": "^7.20.5",
"@babel/preset-env": "^7.20.2", "@babel/preset-env": "^7.20.2",
"@ckeditor/ckeditor5-build-classic": "^44.1.0",
"@ckeditor/ckeditor5-dev-translations": "^43.0.1",
"@ckeditor/ckeditor5-dev-utils": "^43.0.1",
"@ckeditor/ckeditor5-dev-webpack-plugin": "^31.1.13",
"@ckeditor/ckeditor5-markdown-gfm": "^44.1.0",
"@ckeditor/ckeditor5-theme-lark": "^44.1.0",
"@ckeditor/ckeditor5-vue": "^7.3.0", "@ckeditor/ckeditor5-vue": "^7.3.0",
"@eslint/js": "^9.14.0", "@eslint/js": "^9.14.0",
"@luminateone/eslint-baseline": "^1.0.9", "@luminateone/eslint-baseline": "^1.0.9",
@ -23,6 +17,7 @@
"bindings": "^1.5.0", "bindings": "^1.5.0",
"bootstrap": "5.2.3", "bootstrap": "5.2.3",
"chokidar": "^3.5.1", "chokidar": "^3.5.1",
"ckeditor5": "^44.1.0",
"dompurify": "^3.1.0", "dompurify": "^3.1.0",
"eslint": "^9.14.0", "eslint": "^9.14.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",

View File

@ -1,30 +1,24 @@
import ClassicEditorBase from "@ckeditor/ckeditor5-editor-classic/src/classiceditor"; import {
import EssentialsPlugin from "@ckeditor/ckeditor5-essentials/src/essentials"; Essentials,
import MarkdownPlugin from "@ckeditor/ckeditor5-markdown-gfm/src/markdown"; Bold,
import BoldPlugin from "@ckeditor/ckeditor5-basic-styles/src/bold"; Italic,
import ItalicPlugin from "@ckeditor/ckeditor5-basic-styles/src/italic"; Paragraph,
import BlockQuotePlugin from "@ckeditor/ckeditor5-block-quote/src/blockquote"; Mention,
import HeadingPlugin from "@ckeditor/ckeditor5-heading/src/heading"; Markdown,
import LinkPlugin from "@ckeditor/ckeditor5-link/src/link"; BlockQuote,
import ListPlugin from "@ckeditor/ckeditor5-list/src/list"; Heading,
import ParagraphPlugin from "@ckeditor/ckeditor5-paragraph/src/paragraph"; Link,
List,
} from 'ckeditor5';
import coreTranslations from 'ckeditor5/translations/fr.js';
import 'ckeditor5/ckeditor5.css';
import "./index.scss"; import "./index.scss";
export default class ClassicEditor extends ClassicEditorBase {}
ClassicEditor.builtinPlugins = [ export default {
EssentialsPlugin, plugins: [Essentials, Markdown, Bold, Italic, BlockQuote, Heading, Link, List, Paragraph],
MarkdownPlugin,
BoldPlugin,
ItalicPlugin,
BlockQuotePlugin,
HeadingPlugin,
LinkPlugin,
ListPlugin,
ParagraphPlugin,
];
ClassicEditor.defaultConfig = {
toolbar: { toolbar: {
items: [ items: [
"heading", "heading",
@ -39,6 +33,8 @@ ClassicEditor.defaultConfig = {
"redo", "redo",
], ],
}, },
language: "fr", translations: [
coreTranslations
],
licenseKey: "GPL", licenseKey: "GPL",
}; } ;

View File

@ -1,14 +1,13 @@
import ClassicEditor from "./editor_config"; import config from "./editor_config";
import {ClassicEditor} from "ckeditor5";
const ckeditorFields: NodeListOf<HTMLTextAreaElement> = const ckeditorFields: NodeListOf<HTMLTextAreaElement> =
document.querySelectorAll("textarea[ckeditor]"); document.querySelectorAll("textarea[ckeditor]");
ckeditorFields.forEach((field: HTMLTextAreaElement): void => { ckeditorFields.forEach((field: HTMLTextAreaElement): void => {
ClassicEditor.create(field) ClassicEditor.create(field, config)
.then((editor) => {
//console.log( 'CkEditor was initialized', editor );
})
.catch((error) => { .catch((error) => {
console.error(error.stack); console.error(error.stack);
throw error;
}); });
}); });
//Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea')); //Fields.push.apply(Fields, document.querySelectorAll('.cf-fields textarea'));

View File

@ -1,45 +1,3 @@
const { styles } = require("@ckeditor/ckeditor5-dev-utils");
const {
CKEditorTranslationsPlugin,
} = require("@ckeditor/ckeditor5-dev-translations");
buildCKEditor = function (encore) {
encore
.addPlugin(
new CKEditorTranslationsPlugin({
language: "fr",
addMainLanguageTranslationsToAllAssets: true,
verbose: !encore.isProduction(),
strict: true,
}),
)
// Use raw-loader for CKEditor 5 SVG files.
.addRule({
test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,
loader: "raw-loader",
})
// Configure other image loaders to exclude CKEditor 5 SVG files.
.configureLoaderRule("images", (loader) => {
loader.exclude =
/ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/;
})
// Configure PostCSS loader.
.addLoader({
test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,
loader: "postcss-loader",
options: {
postcssOptions: styles.getPostCssConfig({
themeImporter: {
themePath: require.resolve("@ckeditor/ckeditor5-theme-lark"),
},
minify: true,
}),
},
});
};
// Compile and loads all assets from the Chill Main Bundle // Compile and loads all assets from the Chill Main Bundle
module.exports = function (encore, entries) { module.exports = function (encore, entries) {
@ -79,8 +37,6 @@ module.exports = function (encore, entries) {
__dirname + "/Resources/public/page/export/download-export.js", __dirname + "/Resources/public/page/export/download-export.js",
); );
buildCKEditor(encore);
// Modules entrypoints // Modules entrypoints
encore.addEntry( encore.addEntry(
"mod_collection", "mod_collection",

View File

@ -16,7 +16,8 @@
<ckeditor <ckeditor
name="content" name="content"
:placeholder="$t('comment.content')" :placeholder="$t('comment.content')"
:editor="editor" :editor="classicEditor"
:config="editorConfig"
v-model="content" v-model="content"
tag-name="textarea" tag-name="textarea"
/> />
@ -60,8 +61,9 @@
</template> </template>
<script> <script>
import {ClassicEditor} from "ckeditor5";
import {Ckeditor} from "@ckeditor/ckeditor5-vue"; import {Ckeditor} from "@ckeditor/ckeditor5-vue";
import ClassicEditor from "../../../../../../ChillMainBundle/Resources/public/module/ckeditor5/editor_config"; import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
import { mapState } from "vuex"; import { mapState } from "vuex";
export default { export default {
@ -71,7 +73,6 @@ export default {
}, },
data() { data() {
return { return {
editor: ClassicEditor,
loading: false, loading: false,
lastRecordedContent: null, lastRecordedContent: null,
}; };
@ -80,6 +81,8 @@ export default {
...mapState({ ...mapState({
pinnedComment: (state) => state.accompanyingCourse.pinnedComment, pinnedComment: (state) => state.accompanyingCourse.pinnedComment,
}), }),
classicEditor: () => ClassicEditor,
editorConfig: () => classicEditorConfig,
content: { content: {
set(value) { set(value) {
console.log("new comment value", value); console.log("new comment value", value);

View File

@ -22,6 +22,7 @@
name="content" name="content"
:placeholder="$t('comment_placeholder')" :placeholder="$t('comment_placeholder')"
:editor="editor" :editor="editor"
:config="editorConfig"
v-model="content" v-model="content"
tag-name="textarea" tag-name="textarea"
/> />
@ -39,7 +40,8 @@
import Modal from "ChillMainAssets/vuejs/_components/Modal.vue"; import Modal from "ChillMainAssets/vuejs/_components/Modal.vue";
import { makeFetch } from "ChillMainAssets/lib/api/apiMethods"; import { makeFetch } from "ChillMainAssets/lib/api/apiMethods";
import { Ckeditor }from "@ckeditor/ckeditor5-vue"; import { Ckeditor }from "@ckeditor/ckeditor5-vue";
import ClassicEditor from "ChillMainAssets/module/ckeditor5/editor_config"; import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
import {ClassicEditor} from "ckeditor5";
export default { export default {
name: "WriteComment", name: "WriteComment",
@ -55,7 +57,6 @@ export default {
showModal: false, showModal: false,
modalDialogClass: "modal-dialog-scrollable modal-xl", modalDialogClass: "modal-dialog-scrollable modal-xl",
}, },
editor: ClassicEditor,
formdata: { formdata: {
content: this.resource.comment, content: this.resource.comment,
}, },
@ -71,6 +72,8 @@ export default {
}, },
}, },
computed: { computed: {
editor: () => ClassicEditor,
editorConfig: () => classicEditorConfig,
content: { content: {
set(value) { set(value) {
this.formdata.content = value; this.formdata.content = value;

View File

@ -36,14 +36,15 @@
<label class="col-form-label">{{ $t("private_comment") }}</label> <label class="col-form-label">{{ $t("private_comment") }}</label>
<ckeditor <ckeditor
v-model="privateComment" v-model="privateComment"
:editor="editor" :editor="classicEditor"
:config="editorConfig"
tag-name="textarea" tag-name="textarea"
></ckeditor> ></ckeditor>
</div> </div>
<div id="comment" class="action-row"> <div id="comment" class="action-row">
<label class="col-form-label">{{ $t("comments") }}</label> <label class="col-form-label">{{ $t("comments") }}</label>
<ckeditor v-model="note" :editor="editor" tag-name="textarea"></ckeditor> <ckeditor v-model="note" :editor="classicEditor" :config="editorConfig" tag-name="textarea"></ckeditor>
</div> </div>
<div id="objectives" class="action-row"> <div id="objectives" class="action-row">
@ -438,7 +439,8 @@ import {
ISOToDatetime, ISOToDatetime,
} from "ChillMainAssets/chill/js/date"; } from "ChillMainAssets/chill/js/date";
import { Ckeditor } from "@ckeditor/ckeditor5-vue"; import { Ckeditor } from "@ckeditor/ckeditor5-vue";
import ClassicEditor from "ChillMainAssets/module/ckeditor5/editor_config"; import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
import {ClassicEditor} from "ckeditor5";
import AddResult from "./components/AddResult.vue"; import AddResult from "./components/AddResult.vue";
import AddEvaluation from "./components/AddEvaluation.vue"; import AddEvaluation from "./components/AddEvaluation.vue";
import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue"; import AddPersons from "ChillPersonAssets/vuejs/_components/AddPersons.vue";
@ -521,7 +523,6 @@ export default {
return { return {
docAnchorId: null, docAnchorId: null,
isExpanded: false, isExpanded: false,
editor: ClassicEditor,
showAddObjective: false, showAddObjective: false,
showAddEvaluation: false, showAddEvaluation: false,
handlingThirdPartyPicker: { handlingThirdPartyPicker: {
@ -588,6 +589,8 @@ export default {
"hasThirdParties", "hasThirdParties",
"hasReferrers", "hasReferrers",
]), ]),
classicEditor: () => ClassicEditor,
editorConfig: () => classicEditorConfig,
startDate: { startDate: {
get() { get() {
return this.$store.state.startDate; return this.$store.state.startDate;

View File

@ -89,7 +89,8 @@
}}</label> }}</label>
<div class="col-sm-12"> <div class="col-sm-12">
<ckeditor <ckeditor
:editor="editor" :editor="classicEditor"
:config="editorConfig"
:placeholder="$t('evaluation_comment_placeholder')" :placeholder="$t('evaluation_comment_placeholder')"
v-model="comment" v-model="comment"
tag-name="textarea" tag-name="textarea"
@ -276,8 +277,9 @@ import {
ISOToDate, ISOToDate,
ISOToDatetime, ISOToDatetime,
} from "ChillMainAssets/chill/js/date"; } from "ChillMainAssets/chill/js/date";
import {Ckeditor } from "@ckeditor/ckeditor5-vue"; import {Ckeditor} from "@ckeditor/ckeditor5-vue";
import ClassicEditor from "ChillMainAssets/module/ckeditor5/editor_config"; import {ClassicEditor} from "ckeditor5";
import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
import { mapGetters, mapState } from "vuex"; import { mapGetters, mapState } from "vuex";
import PickTemplate from "ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue"; import PickTemplate from "ChillDocGeneratorAssets/vuejs/_components/PickTemplate.vue";
import { buildLink } from "ChillDocGeneratorAssets/lib/document-generator"; import { buildLink } from "ChillDocGeneratorAssets/lib/document-generator";
@ -333,7 +335,6 @@ export default {
i18n, i18n,
data() { data() {
return { return {
editor: ClassicEditor,
template: null, template: null,
asyncUploadOptions: { asyncUploadOptions: {
maxFiles: 1, maxFiles: 1,
@ -374,6 +375,8 @@ export default {
}, },
computed: { computed: {
...mapState(["isPosting", "work", "me"]), ...mapState(["isPosting", "work", "me"]),
classicEditor: () => ClassicEditor,
editorConfig: () => classicEditorConfig,
AmIRefferer() { AmIRefferer() {
return !( return !(
this.$store.state.work.accompanyingPeriod.user && this.$store.state.work.accompanyingPeriod.user &&

View File

@ -29,7 +29,7 @@
</div> </div>
<div class="item-row comment"> <div class="item-row comment">
<ckeditor :editor="editor" v-model="comment" tag-name="textarea" /> <ckeditor :editor="classicEditor" :config="editorConfig" v-model="comment" tag-name="textarea" />
</div> </div>
<div class="item-row participation-details"> <div class="item-row participation-details">
@ -98,8 +98,9 @@ div.participation-details {
<script> <script>
import { mapGetters } from "vuex"; import { mapGetters } from "vuex";
import PersonRenderBox from "ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue"; import PersonRenderBox from "ChillPersonAssets/vuejs/_components/Entity/PersonRenderBox.vue";
import {Ckeditor }from "@ckeditor/ckeditor5-vue"; import {Ckeditor} from "@ckeditor/ckeditor5-vue";
import ClassicEditor from "ChillMainAssets/module/ckeditor5/editor_config"; import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
import {ClassicEditor} from "ckeditor5";
export default { export default {
name: "MemberDetails", name: "MemberDetails",
@ -108,13 +109,10 @@ export default {
ckeditor: Ckeditor, ckeditor: Ckeditor,
}, },
props: ["conc"], props: ["conc"],
data() {
return {
editor: ClassicEditor,
};
},
computed: { computed: {
...mapGetters(["concByPersonId"]), ...mapGetters(["concByPersonId"]),
classicEditor: () => ClassicEditor,
editorConfig: () => classicEditorConfig,
isHolder() { isHolder() {
return this.conc.holder; return this.conc.holder;
}, },

View File

@ -1,18 +1,20 @@
<template> <template>
<ckeditor <ckeditor
name="content" name="content"
:placeholder=" :placeholder="
$t('household_members_editor.positioning.comment_placeholder') $t('household_members_editor.positioning.comment_placeholder')
" "
:editor="editor" :editor="editor"
v-model="content" :config="editorConfig"
tag-name="textarea" v-model="content"
/> tag-name="textarea"
/>
</template> </template>
<script> <script>
import { Ckeditor } from "@ckeditor/ckeditor5-vue"; import { Ckeditor } from "@ckeditor/ckeditor5-vue";
import ClassicEditor from "ChillMainAssets/module/ckeditor5/editor_config"; import classicEditorConfig from "ChillMainAssets/module/ckeditor5/editor_config";
import {ClassicEditor} from "ckeditor5";
export default { export default {
name: "PersonComment.vue", name: "PersonComment.vue",
@ -20,12 +22,9 @@ export default {
ckeditor: Ckeditor ckeditor: Ckeditor
}, },
props: ["conc"], props: ["conc"],
data() {
return {
editor: ClassicEditor,
};
},
computed: { computed: {
editor: () => ClassicEditor,
editorConfig: () => classicEditorConfig,
content: { content: {
get() { get() {
return this.$props.conc.comment || ""; return this.$props.conc.comment || "";

View File

@ -43,7 +43,7 @@
</div> </div>
</div> </div>
<div class="item-row"> <div class="item-row">
<div> <div class="col-12">
<h6>{{ $t("household_members_editor.positioning.comment") }}</h6> <h6>{{ $t("household_members_editor.positioning.comment") }}</h6>
<person-comment :conc="conc" /> <person-comment :conc="conc" />
</div> </div>