366 lines
10 KiB
Vue
366 lines
10 KiB
Vue
<template>
|
|
<div class="ck-editor-component">
|
|
<div ref="wordCounter" />
|
|
<div ref="ckeditor" />
|
|
<input
|
|
:id="internalId"
|
|
class="form-control"
|
|
type="hidden"
|
|
:name="intName"
|
|
:value="internalText"
|
|
:required="internalRequired"
|
|
>
|
|
</div>
|
|
</template>
|
|
|
|
<script lang="ts">
|
|
import { defineComponent } from "vue";
|
|
import "./../../../js/vendor/ckeditor";
|
|
import trans from "./../../modules/trans";
|
|
// import { CKUploadAdaptater } from "../../bo/CkUploadAdaptater";
|
|
|
|
export default defineComponent({
|
|
name: "CkEditorComponent",
|
|
emits: ["input"],
|
|
mixins: [trans],
|
|
props: {
|
|
id: {
|
|
type: String,
|
|
default: String(Math.pow(10, 16) / Math.random()),
|
|
},
|
|
required: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
name: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
dbtext: {
|
|
type: String,
|
|
default: "",
|
|
},
|
|
},
|
|
mounted() {
|
|
const json = String(this.$attrs.json ?? "{}"),
|
|
data = JSON.parse(json);
|
|
this.intName = data.name;
|
|
this.internalText = data.text ? String(data.text) : "";
|
|
this.internalId = data.id ? data.id : "";
|
|
this.internalRequired = Boolean(data.required);
|
|
this.makeSuperBuild();
|
|
},
|
|
data(): {
|
|
intName: string;
|
|
internalText: string;
|
|
internalId: string;
|
|
internalRequired: boolean;
|
|
// @ts-ignore
|
|
editor: ClassicEditor | null;
|
|
words: number;
|
|
chars: number;
|
|
} {
|
|
return {
|
|
intName: "",
|
|
internalText: "",
|
|
internalId: "",
|
|
internalRequired: false,
|
|
editor: null,
|
|
words: 0,
|
|
chars: 0,
|
|
};
|
|
},
|
|
watch: {
|
|
id() {
|
|
this.internalId = this.id;
|
|
},
|
|
name() {
|
|
this.intName = this.name;
|
|
},
|
|
required() {
|
|
this.internalRequired = this.required;
|
|
},
|
|
dbtext() {
|
|
if (this.internalText != this.dbtext) {
|
|
this.internalText = this.dbtext ? this.dbtext : "";
|
|
if (this.editor) {
|
|
const text = this.internalText;
|
|
Object.freeze(text);
|
|
this.editor.setData(text);
|
|
}
|
|
}
|
|
},
|
|
},
|
|
methods: {
|
|
makeSuperBuild() {
|
|
const self = this,
|
|
ckeditor = this.$refs.ckeditor as HTMLElement,
|
|
wordCounter = this.$refs.wordCounter as HTMLElement,
|
|
// * Top Menu
|
|
verticalTopMenuOffset = document.querySelector(
|
|
"body > #top-navbar"
|
|
) as HTMLElement | null;
|
|
|
|
if (!verticalTopMenuOffset) {
|
|
console.warn("Could not find bo #top-navbar");
|
|
}
|
|
|
|
const ui = verticalTopMenuOffset ? {
|
|
viewportOffset: {
|
|
top: verticalTopMenuOffset.getBoundingClientRect().height,
|
|
right: 0,
|
|
bottom: 0,
|
|
left: 0,
|
|
},
|
|
} : undefined;
|
|
|
|
// @ts-ignore
|
|
ClassicEditor.create(ckeditor, {
|
|
// https://github.com/ckeditor/ckeditor5-react/issues/267
|
|
extraPlugins: [
|
|
// // @ts-ignore
|
|
// (editor) => {
|
|
// editor.plugins.get("FileRepository").createUploadAdapter = (
|
|
// // @ts-ignore
|
|
// loader
|
|
// ) => {
|
|
// // Configure the URL to the upload script in your back-end here!
|
|
// return new CKUploadAdaptater(loader);
|
|
// };
|
|
// },
|
|
],
|
|
wordCount: {
|
|
container: wordCounter,
|
|
// @ts-ignore
|
|
onUpdate: (stats) => {
|
|
this.chars = stats.characters;
|
|
this.words = stats.words;
|
|
},
|
|
},
|
|
ui: ui,
|
|
link: {
|
|
// Automatically add target="_blank" and rel="noopener noreferrer" to all external links.
|
|
addTargetToExternalLinks: true,
|
|
|
|
// Let the users control the "download" attribute of each link.
|
|
decorators: {
|
|
openInNewTab: {
|
|
mode: "manual",
|
|
label: "Ouvrir dans un nouvel onglet",
|
|
attributes: {
|
|
target: "_blank",
|
|
}
|
|
}
|
|
}
|
|
},
|
|
typing: {
|
|
transformations: {
|
|
extra: [
|
|
{ from: ":)", to: "😊" },
|
|
{ from: ":-P", to: "😛" },
|
|
{ from: "8-)", to: "😎" },
|
|
{ from: ";-)", to: "😉" },
|
|
{ from: ":-D", to: "😄" },
|
|
{ from: ":twisted:", to: "😈" },
|
|
{ from: ":mrgreen:", to: "😁" },
|
|
{ from: ":lol:", to: "😆" },
|
|
{ from: ":roll:", to: "🙄" },
|
|
{ from: ":-?", to: "😕" },
|
|
{ from: ":cry:", to: "😢" },
|
|
{ from: ":-o", to: "😮" },
|
|
{ from: ":evil:", to: "😡" },
|
|
{ from: ":-|", to: "😐" },
|
|
{ from: ":oops:", to: "😳" },
|
|
{ from: ":-x", to: "😠" },
|
|
{ from: "8-O", to: "😲" },
|
|
{ from: ":-(", to: "😧" },
|
|
{ from: ":arrow:", to: "➡" },
|
|
{ from: ":idea:", to: "💡" },
|
|
],
|
|
},
|
|
},
|
|
mediaEmbed: {
|
|
previewsInData: false,
|
|
removeProviders: [
|
|
"dailymotion",
|
|
"spotify",
|
|
"youtube",
|
|
"vimeo",
|
|
"instagram",
|
|
"twitter",
|
|
"googleMaps",
|
|
"flickr",
|
|
"facebook",
|
|
],
|
|
// // see https://github.com/ckeditor/ckeditor5/blob/master/packages/ckeditor5-media-embed/src/mediaembedediting.js
|
|
// providers: [
|
|
// {
|
|
// // display local videos in oembed tags
|
|
// name: "localVideo",
|
|
// url: /^(\/(?:content|storage|uploads)\/.*)/,
|
|
// html: (match: string[]) => {
|
|
// const path = match[1];
|
|
|
|
// return (
|
|
// "<video controls allowfullscreen style=\"max-width: 90%; margin: 0 auto; display: block;\">" +
|
|
// `<source src="${path}" type="video/mp4">` +
|
|
// `<p>Votre navigateur ne prend pas en charge les vidéos HTML5. Voici <a href="${path}">un lien pour télécharger la vidéo</a>.</p>` +
|
|
// "</video>"
|
|
// );
|
|
// },
|
|
// },
|
|
// ],
|
|
},
|
|
toolbar: {
|
|
items: [
|
|
"heading",
|
|
"|",
|
|
"findAndReplace",
|
|
"|",
|
|
"bold",
|
|
"italic",
|
|
"underline",
|
|
"strikethrough",
|
|
"link",
|
|
"bulletedList",
|
|
"numberedList",
|
|
// "code",
|
|
// "codeBlock",
|
|
"|",
|
|
"outdent",
|
|
"indent",
|
|
"alignment",
|
|
"|",
|
|
"undo",
|
|
"redo",
|
|
"selectAll",
|
|
"|",
|
|
// "laravelFileManager",
|
|
"blockQuote",
|
|
"|",
|
|
// "insertTable",
|
|
// "tableColumn",
|
|
// "tableRow",
|
|
// "mergeTableCells",
|
|
"|",
|
|
// "highlight",
|
|
// "removeHighlight",
|
|
// "fontColor",
|
|
// "fontBackgroundColor",
|
|
// "fontFamily",
|
|
"fontSize",
|
|
"horizontalLine",
|
|
// "specialCharacters",
|
|
"|",
|
|
"removeFormat",
|
|
"|",
|
|
"sourceEditing",
|
|
],
|
|
},
|
|
// table: {
|
|
// contentToolbar: [
|
|
// "tableColumn",
|
|
// "tableRow",
|
|
// "mergeTableCells",
|
|
// "tableProperties",
|
|
// "tableCellProperties",
|
|
// "toggleTableCaption",
|
|
// ],
|
|
|
|
// Configuration of the TableProperties plugin.
|
|
// tableProperties: {
|
|
// ...
|
|
// },
|
|
|
|
// Configuration of the TableCellProperties plugin.
|
|
// tableCellProperties: {
|
|
// ...
|
|
// },
|
|
// },
|
|
width: "auto",
|
|
language: "fr",
|
|
image: {
|
|
toolbar: [
|
|
"toggleImageCaption",
|
|
"imageTextAlternative",
|
|
"imageStyle:inline",
|
|
"imageStyle:alignLeft",
|
|
"imageStyle:alignRight",
|
|
"imageStyle:alignCenter",
|
|
"imageStyle:side",
|
|
"imageResize",
|
|
"linkImage",
|
|
],
|
|
},
|
|
})
|
|
// @ts-ignore
|
|
.then((editor: ClassicEditor) => {
|
|
let timer = null as number|null;
|
|
const updateContent = () => {
|
|
const data = editor.getData();
|
|
self.internalText = data;
|
|
self.$emit("input", data);
|
|
timer = null;
|
|
};
|
|
// Display all toolbar elements
|
|
// console.log(Array.from(editor.ui.componentFactory.names()));
|
|
self.editor = Object.freeze(editor);
|
|
editor.setData(this.internalText);
|
|
editor.model.document.on("change:data", () => {
|
|
if(timer) {
|
|
clearTimeout(timer);
|
|
}
|
|
// * Keep track of internal editor
|
|
timer = setTimeout(updateContent, 1000) as never as number;
|
|
});
|
|
})
|
|
// @ts-ignore
|
|
.catch((error) => {
|
|
console.error("Oops, something went wrong!");
|
|
console.error(
|
|
"Please, report the following error on https://github.com/ckeditor/ckeditor5/issues with the build id and the error stack trace:"
|
|
);
|
|
console.warn("Build id: x0tjc6omqq4d-iydt761tuj51");
|
|
console.error(error);
|
|
});
|
|
},
|
|
},
|
|
});
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
@import url("./../../../js/vendor/ckeditor/build/styles.css");
|
|
.ck-editor-component{
|
|
// 2. Include functions first (so you can manipulate colors, SVGs, calc, etc)
|
|
@import "bootstrap/scss/functions";
|
|
|
|
// 3. Include remainder of required Bootstrap stylesheets
|
|
@import "bootstrap/scss/variables";
|
|
.ck-rounded-corners {
|
|
border-radius: 0 0 0.375rem 0.375rem !important;
|
|
}
|
|
.ck-word-count {
|
|
background-color: #fafafa;
|
|
border: 1px solid #c4c4c4;
|
|
border-bottom: 0px;
|
|
border-top-left-radius: 0.375rem;
|
|
border-top-right-radius: 0.375rem;
|
|
color: initial;
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-around !important;
|
|
}
|
|
.ck-content {
|
|
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
|
}
|
|
.ck-focused {
|
|
border-color: #86b7fe !important;
|
|
box-shadow: 0 0 0 0.25rem rgb(13 110 253 / 25%) !important;
|
|
outline: 0 !important;
|
|
}
|
|
.ck-editor__main{
|
|
color: $body-color;
|
|
}
|
|
}
|
|
|
|
</style>
|