<template>
<v-dialogv-model="dialog"transition="dialog-bottom-transition"fullscreen>
<v-card>
<DrowEditor :close="close" :recevData="recevData" :imageUrl="imageUrl" />
</v-card>
</v-dialog>
<divclass="control-section mg_auto">
<divclass="sample-container">
<divclass="default-section">
<!-- <v-btn @click="onClickDrow">Drow</v-btn> -->
<form
:id="'myForm' + uniqueId"
class="form-vertical richtexteditor_box"
>
<ejs-richtexteditor
:toolbarClick="toolbarClick"
:id="'book_mention_integration_' + uniqueId"
ref="rteObj"
name="defaultRTE"
:quickToolbarSettings="quickToolbarSettings"
height="1124"
:insertImageSettings="insertImageSettings"
:toolbarSettings="toolbarSettings"
v-model="objModel.html"
:change="createdTextEditor"
:allowEditing="false"
:readonly="readonly"
:enabled="!readonly"
:enableTabKey="true"
@blur="onBlur"
@created="focusEditor"
:created="onCreate"
:imageUploadSuccess="imageUploadSuccess"
:afterImageDelete="afterImageDelete"
enterKey="DIV"
saveInterval="9000"
:pasteCleanupSettings="pasteCleanupSettings"
>
</ejs-richtexteditor>
<divclass="error-element"></div>
</form>
</div>
</div>
</div>
</template>
<script>
import {
RichTextEditorComponent,
Toolbar,
Link,
Image,
HtmlEditor,
QuickToolbar,
EmojiPicker,
PasteCleanup,
Table,
Video,
Audio,
NodeSelection,
} from"@syncfusion/ej2-vue-richtexteditor";
import { getImageURL } from"@/api/todo";
import { FormValidator } from"@syncfusion/ej2-inputs";
import { DropDownButton } from"@syncfusion/ej2-vue-splitbuttons";
importDrowEditorfrom"./DrowEditor.vue";
import { PAGE_UPLOAD_ATTACHMENT } from"@/store/actions/confluence";
consthostUrl = import.meta.env.VITE_USER_ROOT_API;
exportdefault {
name:"BookRichTextEditor",
components: {
"ejs-richtexteditor":RichTextEditorComponent,
DrowEditor,
},
props: {
propName: {
type:String,
required:true,
},
objModel: {
type:Object,
required:true,
},
isRequired: {
type:Boolean,
required:false,
default:true,
},
users: {
type:Array,
required:false,
},
isMention: {
type:Boolean,
required:false,
Default:false,
},
readonly: {
type:Boolean,
required:false,
default:false,
},
},
// emits: ["update:modelValue"],
provide: {
richtexteditor: [
Toolbar,
Link,
Image,
QuickToolbar,
HtmlEditor,
PasteCleanup,
Table,
Video,
Audio,
EmojiPicker,
],
},
// provide: {
// richtexteditor: [
// Toolbar,
// Link,
// Image,
// Count,
// HtmlEditor,
// QuickToolbar,
// EmojiPicker,
// ],
// },
data() {
return {
pasteCleanupSettings: {
plainText:true,
},
imageUrl:"",
dialog:false,
proxy:this,
customBtn:null,
insertImageSettings: {
saveFormat:"blob",
saveUrl:
hostUrl +
"/api/v1/task/editor-attachment/" +
this.$store.getters.getSelectedCompanyId,
removeUrl:hostUrl + "/api/v1/task/delete-editor-attachment",
path:hostUrl + "/api/v1/download/",
},
selection:newNodeSelection(),
ranges:null,
uniqueId:Math.random().toString(36).substr(2, 9),
value:"",
getImageURL:getImageURL,
dropButton:DropDownButton,
selectedUserIndex: -1,
quickToolbarSettings: {
image: [
"Replace",
"Align",
"Caption",
"Remove",
"InsertLink",
"OpenImageLink",
"-",
"EditImageLink",
"RemoveImageLink",
"Display",
"AltText",
"Dimension",
{
tooltipText:"Edit Image",
template:
'<button class="e-tbar-btn e-btn" id="imageEditor"><span class="e-btn-icon e-icons e-rte-image-editor"></span></button>',
},
],
link: ["Open", "Edit", "UnLink"],
text: [
"Bold",
"Italic",
"Underline",
"FontColor",
"BackgroundColor",
"Alignments",
"-",
"FontSize",
"FontName",
"Formats",
"OrderedList",
"UnorderedList",
"FormatPainter",
],
},
items1: [
{
text:"Information",
},
{
text:"Warning",
},
{
text:"Success",
},
{
text:"Danger",
},
],
toolbarSettings: {
enableFloating:false,
items: [
"Bold",
"Italic",
"Underline",
"StrikeThrough",
"FontName",
"FontSize",
"FontColor",
"BackgroundColor",
"LowerCase",
"UpperCase",
"|",
"Formats",
"Alignments",
"Blockquote",
"OrderedList",
"UnorderedList",
"Outdent",
"Indent",
"|",
"CreateLink",
"Image",
"CreateTable",
"|",
"EmojiPicker",
"|",
"ClearFormat",
"Print",
"SourceCode",
"|",
"Undo",
"Redo",
{
tooltipText:"Insert Symbol",
template:
'<button class="e-tbar-btn e-btn" tabindex="-1" id="drop" style="width:100%">Callouts</button>',
},
{
tooltipText:"Insert Diagram",
template:
'<button class="e-tbar-btn e-btn" tabindex="-1" id="custom_tbar" style="width:100%"><div class="e-tbar-btn-text" style="font-weight: 400;">Add Diagram</div></button>',
},
],
},
};
},
computed: {},
watch: {},
created() {},
mounted() {
// this.focusEditor();
},
beforeUnmount() {
//
},
methods: {
onCreate() {
this.proxy = this;
// initialize DropDownList component
this.dropButton = newDropDownButton({
items:this.items1,
content:"CallOuts",
select:this.select,
});
// render initialized DropDownList
this.dropButton.appendTo("#drop");
this.customBtn = document.getElementById("custom_tbar");
constpage = this;
this.customBtn.onclick = function (event) {
event.preventDefault();
page.onClickDrow();
};
},
select:function (args) {
varrte = this.$refs.rteObj.ej2Instances;
consttext = rte.getSelection() ? rte.getSelection() : "Enter text";
if (args.item.text == "Information") {
rte.executeCommand("insertHTML", `<p class='callout info'>${text}</p>`);
} elseif (args.item.text == "Warning") {
rte.executeCommand(
"insertHTML",
`<p class='callout warning'>${text}</p>`
);
} elseif (args.item.text == "Success") {
rte.executeCommand(
"insertHTML",
`<p class='callout success'>${text}</p>`
);
} else {
rte.executeCommand(
"insertHTML",
`<p class='callout danger'>${text}</p>`
);
}
},
// focusEditor() {
// this.$nextTick(() => {
// const rteInstance = this.$refs.rteObj.ej2Instances;
// rteInstance.focusIn();
// const editorElement = rteInstance.contentModule.getEditPanel();
// const editorText = editorElement.textContent || editorElement.innerText;
// if (editorText.trim().length > 0) {
// // If there's text, move cursor to the end
// const range = document.createRange();
// const sel = window.getSelection();
// range.selectNodeContents(editorElement);
// range.collapse(false);
// sel.removeAllRanges();
// sel.addRange(range);
// } else {
// // If no text, move cursor to the start
// const range = document.createRange();
// const sel = window.getSelection();
// range.selectNodeContents(editorElement);
// range.collapse(true);
// sel.removeAllRanges();
// sel.addRange(range);
// }
// });
// },
onBlur() {
// this.$emit("blurFunction");
},
createdTextEditor() {
if (this.isRequired) {
varoption = {
rules: {
defaultRTE: {
required:true,
minLength: [3, "The field is required"],
},
},
customPlacement: (element, errorElement) => {
document.querySelector(".error-element").appendChild(errorElement);
},
};
newFormValidator("#myForm" + this.uniqueId, option);
}
},
onChange(e) {
// console.log("on Chawngeweeew11v -", e);
// console.log("onChan ge1-", this.$refs.rteObj.ej2Instances);
},
imageUploadSuccess(args) {
args.file.name = args.e.srcElement.response;
},
afterImageDelete(args) {
constbaseUrl = hostUrl + "/api/v1/download/";
constfilePath = args.src.replace(baseUrl, "").trim();
this.objModel.imagePath.push(filePath);
// this.$store
// .dispatch(TASK_DELETE_ATTACHMENT, {
// isText: false,
// url: encodeURIComponent(filePath),
// })
// .then(() => {
// // page.loading = false;
// // if (page.$store.getters.getTaskStatus !== "success") return;
// // page.refresh();
// });
},
onClickDrow() {
this.dialog = true;
},
close() {
this.dialog = false;
},
asyncrecevData(imgurl, isEdit = false) {
// console.log("Sssssssssssssssssss", imgurl);
constfile = awaitthis.base64ToFile(imgurl, "drow.png");
consturl = awaitthis.handleFilesUpload(file);
constserverImg = hostUrl + "/api/v1/download/" + (awaiturl);
if (isEdit) {
this.$refs.rteObj.ej2Instances.executeCommand("insertImage", {
url:serverImg,
cssClass:"e-rte-image e-imgleft drawio",
});
setTimeout(() => {
constinsertedImage = document.querySelector(
".e-rte-image.e-imgleft.drawio"
); // Select the newly inserted image based on class
if (insertedImage) {
insertedImage.id = "drawio " + newDate().getTime(); // Set the desired ID
// console.log("Image updated with ID:", insertedImage);
} else {
console.error("Image not found");
}
}, 0);
} else {
this.$refs.rteObj.ej2Instances.executeCommand("insertImage", {
url:serverImg,
cssClass:"e-rte-image e-imgleft drawio",
selection:this.saveSelection,
});
}
this.$refs.rteObj.ej2Instances.formatter.saveData();
this.$refs.rteObj.ej2Instances.formatter.enableUndo(
this.$refs.rteObj.ej2Instances
);
this.$refs.rteObj.ej2Instances.focusIn();
// rteInstance.focusIn();
},
asynchandleFilesUpload(e) {
letpage = this;
letattachmentFiles = e;
returnawaitpage.$store
.dispatch(PAGE_UPLOAD_ATTACHMENT, {
id:this.$store.getters.getSelectedCompanyId,
attachmentFiles:attachmentFiles,
})
.then(async (response) => {
returnawaitresponse;
// page.loading = false;
// page.refresh();
// if (page.$store.getters.getTaskStatus !== "success") return;
// if (e.target.files) {
// page.$refs.files.value = "";
// }
});
},
// Function to decode Base64 string and convert it to a File object
asyncbase64ToFile(base64String, fileName) {
consttimestamp = newDate().toISOString().replace(/[:.-]/g, "");
constnewFileName = `${fileName.replace(
/\.[^/.]+$/,
""
)}_${timestamp}.${fileName.split(".").pop()}`;
// Separate the metadata from the Base64 data
const [metadata, data] = base64String.split(",");
// Extract MIME type from metadata
constmimeType = metadata.match(/:(.*?);/)[1];
// Decode Base64 data into a binary string
constbinaryData = awaitatob(data);
// Convert binary string to a Uint8Array (array of bytes)
constbyteNumbers = Uint8Array.from(binaryData, (char) =>
char.charCodeAt(0)
);
// Create and return a File object
returnnewFile([byteNumbers], newFileName, { type:mimeType });
},
toolbarClick(args) {
if (args.item.tooltipText === "Edit Image") {
this.range = this.selection.getRange(document);
this.saveSelection = this.selection.save(this.range, document);
// this.$refs.dialogObj.show();
// this.$refs.defaultRTE.ej2Instances.quickToolbarModule.imageQTBar.hidePopup();
varselectNodes =
this.$refs.rteObj.ej2Instances.formatter.editorManager.nodeSelection.getNodeCollection(
this.range
);
if (!selectNodes[0].classList.value.includes("drawio")) {
alert("you can't edit this image ");
return;
}
// console.log(selectNodes[0].src);
// this.imageUrl = selectNodes[0].src;
this.imageUrl = selectNodes[0].src;
this.$refs.rteObj.ej2Instances.quickToolbarModule.imageQTBar.hidePopup();
this.onClickDrow();
// document.getElementById("image").value = selectNodes[0].src;
// this.imageELement = selectNodes[0];
}
},
},
};
</script>
<style>
@import"../../../node_modules/@syncfusion/ej2-base/styles/material.css";
@import"../../../node_modules/@syncfusion/ej2-inputs/styles/material.css";
@import"../../../node_modules/@syncfusion/ej2-lists/styles/material.css";
@import"../../../node_modules/@syncfusion/ej2-popups/styles/material.css";
@import"../../../node_modules/@syncfusion/ej2-buttons/styles/material.css";
@import"../../../node_modules/@syncfusion/ej2-navigations/styles/material.css";
@import"../../../node_modules/@syncfusion/ej2-splitbuttons/styles/material.css";
@import"../../../node_modules/@syncfusion/ej2-vue-richtexteditor/styles/material.css";
.e-dropdownbase.e-list-item.e-active,
.e-dropdownbase.e-list-item.e-active.e-hover {
background-color: #0d6efd;
border-color: #fff;
color: #fff;
}
.e-dropdownbase.e-content {
overflow: auto;
position: relative;
}
.e-dropdownbase {
border-color: #dee2e6;
}
.e-list-item.e-hover {
background-color: #dee2e6;
}
.e-mention.e-popup {
background: #fff;
border: 0;
box-shadow: 02px3px1pxrgba(0, 0, 0, 0.21);
margin-top: 2px;
position: absolute;
z-index: 9999!important;
}
.error-element {
margin-top: 5px;
}
.v-theme--dark.sample-container.form-vertical.e-richtexteditor,
.v-theme--dark
.sample-container
.form-vertical
.e-richtexteditor
.e-toolbar-wrapper,
.v-theme--dark.e-toolbar.e-toolbar-items,
.v-theme--dark.e-toolbar.e-toolbar-item.e-tbar-btn,
.v-theme--dark.e-richtexteditor.e-rte-toolbar.e-toolbar-item.e-icons,
.v-theme--dark.e-toolbar,
.v-theme--dark.e-toolbar.e-hor-nav,
.v-theme--dark.e-richtexteditor.e-rte-content,
.v-theme--dark.e-richtexteditor.e-source-content,
.v-theme--dark.e-toolbar.e-extended-toolbar.e-toolbar-pop,
.v-theme--dark
.e-toolbar.e-extended-toolbar
.e-toolbar-extended
.e-toolbar-item.e-overlay,
.v-theme--dark
.e-toolbar.e-extended-toolbar
.e-toolbar-extended
.e-toolbar-item
.e-tbar-btn {
background: rgb(var(--v-theme-surface));
color: #ffff!important;
}
.v-theme--dark.e-toolbar.e-popup-down-icon.e-icons,
.v-theme--dark.e-toolbar.e-popup-up-icon.e-icons {
color: #ffff!important;
}
.v-theme--dark.e-richtexteditor.e-rte-tb-expand {
border: 1pxsolidwhite!important;
}
.v-theme--dark.e-richtexteditor.e-rte-tb-expand.e-rte-content,
.v-theme--dark.e-richtexteditor.e-rte-tb-expand.e-source-content {
border-bottom: 1pxsolid#ffff;
border-top: 1pxsolid#ffff;
}
/* test */
.e-rte-contentul,
.e-rte-contentol {
margin-left: 20px!important;
}
.e-rte-quick-popup.e-rte-quick-toolbar.e-rte-image-editor::before {
content: "\e81e";
}
.e-richtexteditor {
padding-bottom: 30px!important;
}
.e-rte-container {
border: 0px!important;
}
.richtexteditor_box.richtexteditor_box {
border: 1px#dddsolid!important;
background-color: #fff!important;
}
/* :root {
--rte-visual-lines: 5;
--rte-line-height: 1.2em;
}
.richtexteditor_box .e-richtexteditor .e-rte-content::after,
.richtexteditor_box .e-richtexteditor .e-content::after,
.richtexteditor_box .e-richtexteditor .e-editable::after,
.richtexteditor_box .e-richtexteditor .e-source-content::after,
.e-richtexteditor .e-rte-content::after {
content: "";
display: block;
height: calc(var(--rte-line-height) * var(--rte-visual-lines));
pointer-events: none;
user-select: none;
} */
.e-richtexteditor.e-content::after {
/* content: ""; */
display: block;
height: calc(1.2em * 5);
pointer-events: none;
user-select: none;
}
/* keep list indent behaviour */
</style>