BUg in RichTextEditor
Question
GrapesJS version
- I confirm to use the latest version of GrapesJS
What browser are you using?
chrome
Reproducible demo link
i have given the code in issues
### Describe the bug
[Screencast from 12-06-24 03:09:58 PM IST.webm](https://github.com/GrapesJS/grapesjs/assets/130237200/d5bbb21e-2e44-4564-a407-4768776c1cd8)
problem is right after applying any bold or italic it is getting disappeard once i click somewhere.
onMounted(async () => {
componentsTypeScript.value = {};
componentsDefaults.value = {};
appId.value = useRoute().query.appId;
designerStore.bAppLoaded = false;
await designerStore.getPlugins();
// try {
// const idbPlugins: Plugin[] = await loadPlugins()
// if (idbPlugins.length === 0) {
// const allPlugins: Plugin[] = await designerStore.getPlugins()
// storePlugins(allPlugins)
// } else {
// console.log('idbPlugins', idbPlugins)
// designerStore.setStoredPlugins(idbPlugins)
// designerStore.getPlugins().then(function (pluginResponse: Plugin[]) {
// storePlugins(pluginResponse)
// })
// }
// } catch (error) {
// const allPlugins: Plugin[] = await designerStore.getPlugins()
// storePlugins(allPlugins)
// }
designerStore.bAppLoaded = true;
// let latestVersion= await designerStore.getAppLatestVersion(appId.value);
designerStore.currentBAApplication = await designerStore.getApp(appId.value, undefined)//,latestVersion.version);
designerStore.currentBAApplication={...designerStore.currentBAApplication,id:designerStore.currentBAApplication.clonedBAId}
designerStore.modifiedAppName = appNameTuner(designerStore.currentBAApplication.baAppName);
// Get Primary Dependencies to initialize the editor with
const baAppDependencies = await designerStore.getDependencies();
const libs = baAppDependencies;
const jsLibs: any[] = [];
const cssLibs: any[] = [];
libs.forEach((link) => {
if (link.endsWith(".js")) {
jsLibs.push(link);
} else if (link.endsWith(".css")) {
cssLibs.push(link);
}
});
// const uniqCssLibs = uniq(cssLibs)
// libs.forEach((dep: any) => {
// if (dep.type == 'css') {
// cssLibs.push(dep.src)
// } else if (dep.type == 'js') {
// jsLibs.push(dep.src)
// }
// })
let uniqCssLibs = uniq(cssLibs);
let uniqJsLibs = uniq(jsLibs);
uniqJsLibs = [
"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/primeui/4.1.15/primeui.min.js",
...uniqJsLibs,
];
editorNonReactive= grapesjs.init({
height: "100%",
container: "#canvasBlock",
fromElement: true,
canvasCss: `
.gjs-selected {
outline: 2px solid #c200fdb8 !important;
}
`,
layerManager: {
custom: true
},
richTextEditor:{
stylePrefix: 'rte-',
adjustToolbar: true,
actions: ['bold', 'italic', 'underline', 'strikethrough', 'link', 'wrap'],
custom: true,
},
selectorManager: {
appendTo: "#selectors",
// This is make grapesjs to use class styles as the priority and change styles of class instead of id provided by grapesjs
componentFirst: true,
},
styleManager: {
appendTo: "#styles",
},
colorPicker: {
appendTo: "parent",
showButtons: false,
showSelectionPalette: false,
show: function (this: any) {
const sideBarElement: any = document.getElementById("widgetResizable");
const handlerXPos: number = this.getBoundingClientRect().x;
const sidebarXPos: number = sideBarElement.getBoundingClientRect().x;
if (handlerXPos - sidebarXPos < 160) {
this.nextElementSibling.style.left = "0px";
}
},
offset: { top: 30, left: -180 },
},
traitManager: {
appendTo: "#traits",
},
deviceManager: {
default: "Desktop",
devices: [
{
id: "desktop",
name: "Desktop",
width: "",
widthMedia: "",
},
{
id: "tablet",
name: "Tablet",
width: "768px",
widthMedia: "768px",
height: "1024px",
},
{
id: "mobile",
name: "Mobile",
width: "360px",
widthMedia: "360px",
},
],
},
plugins: [
Basics,
styleBackground,
// tailwindBlocks,
"grapesjs-preset-webpage",
"grapesjs-tabs",
styleFilter,
pluginRulers,
customType,
plugin,
domComponents,
loopComponent,
loginform,
forgetPasswordForm,
signupForm,
formComponents,
nativeformComponents,
primeUiPlugin,
panelsManager,
traitManager,
dynamicPlugins,
dynamicWidgets,
animationPlugin,
dynamicComponent,
customBlockComponent,
scrollAnimationComponent,
googleIcons,
listPlugin,
blockManager,
assetManager,
componentManager,
parserPostCSS,
],
pluginsOpts: {
"grapesjs-preset-webpage": {
blocks: ["tab-contents"],
},
"grapesjs-tabs": {},
[plugin]: {
/* options */
},
},
canvas: {
styles: [
"https://fonts.googleapis.com/css2?family=Lato:wght@400;500;700;900&display=swap",
"https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;700&display=swap",
"https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700;900&display=swap",
"https://fonts.googleapis.com/css2?family=Roboto:wght@400;500;700;900&display=swap",
"https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;700;900&display=swap",
"https://fonts.googleapis.com/css?family=Montserrat:400,700&display=swap",
"https://fonts.googleapis.com/css?family=Plus+Jakarta+Sans:wght@400,700&display=swap",
"https://fonts.googleapis.com/css?family=Montserrat:400,700&display=swap",
"https://fonts.googleapis.com/css?family=Plus+Jakarta+Sans:wght@400,700&display=swap",
...uniqCssLibs,
],
scripts: [...uniqJsLibs],
},
jsInHtml: true,
storageManager: {
autoload: true,
onLoad: async (data: any, opts: any) => {
try {
console.log("onLoad");
store.state.appLoading=true;
let resp: any;
// let latestVersion= await designerStore.getAppLatestVersion(appId.value);
if (designerStore.triedLoadFromFile) {
resp = designerStore.app;
} else {
resp = designerStore.currentBAApplication ? designerStore.currentBAApplication : await designerStore.getApp(appId.value, undefined)
}
appId.value=resp.clonedBAId
setSchemasAttrs(resp.sources);
const pageData = resp.config;
BaApplication.value = {...resp,id:resp.clonedBAId};
store.state.appLoading=false;
if (designerStore.triedLoadFromFile) {
setInterval(() => {
// do nothing
}, 1000);
designerStore.setLoadFromFile(false);
}
//we are returning {} if pageData is undefined in order to avoid caching of the previous UI in to newly created BaApp.
return pageData!==undefined? pageData : {};
} catch (error) {
$q.notify({
type: "negative",
message: error.response.data.errorMessage,
});
if (error.response.data.errorCode == 4040) {
// const cleanedStr = editor.value.getHtml().replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
async function generateThumbnail(
thumbnail: string,
defaultThumb: any
) {
if (thumbnail && isUuid(thumbnail)) {
apiUrlGain.contentService
.getImageByThumbnailID(thumbnail)
.then((imgData) => {
page.value.thumbnail =
"data:image/png;base64, " + imgData.data.base64;
})
.catch((e) => {
console.log("Get Thumbnail ", e);
});
} else if (thumbnail) {
page.value.thumbnail = thumbnail;
} else {
page.value.thumbnail = defaultThumb;
}
}
const postBody: any = {
id: designerStore.currentBAApplication.id,
baAppName: designerStore.currentBAApplication.baAppName,
title: designerStore.currentBAApplication.title,
bbandEntryPageUrl: "string",
bcastEntryPackageUrl: "string",
bcastEntryPageUrl: "string",
thumbnail: "string",
config: {},
css: "",
deleted: false,
entryPageURL: "string",
files: designerStore.app.files,
html: {
pre: getHeadContent(),
body: getHTMLBODYV2(),
},
metaData: [],
script: "string",
sources: [],
types: {},
defaults: {},
version: designerStore.currentBAApplication.version,
wrapper: [],
events: [],
aqIds: [],
groupIds: [],
};
const resp = await designerStore.postApp(postBody);
$q.notify({
type: "positive",
message: "App created",
});
console.log("broad", resp.sources);
setSchemasAttrs(resp.sources);
const pageData = resp.config;
BaApplication.value = resp;
return pageData;
}
}
},
autosave: false,
onStore: async (data: any, opts: any) => {
console.log("onStore", data, opts, editor.value, designerStore.triedSaveToFile);
editor.value = await editorStore.getEditor();
const baAppNameSaved =
designerStore.currentBAApplication.baAppName.replace(/\s/g, "") +
"_" +
Date.now();
// const strippedWrapper = editor.value.getComponents().map((element:any) => {
// const { attributes, components } = element;
// return { attributes, components };
// });
componentsTypeScript.value = {};
componentsDefaults.value = {};
generateComponentTypeScript(editor.value.getWrapper());
const cssapp: any = editor.value.getCss({ avoidProtected: true });
const rules = editor.value.CssComposer.getAll();
const allCss = rules.map((rule: any) => rule.toCSS()).join("\n");
// const newCss = cssapp.replace(
// /#(?![\da-fA-F]{6}|[\da-fA-F]{3})(.+?)[\s|\{]/g,
// (match: any, id: any) => {
// // const newCss = baApp.css.replace(/#(.+?)[\s|\{]/g, (match, id) => {
// return `[id^=${id}]{`
// }
// )
let fileSaveConfig: any = {
bbandEntryPageUrl: "string",
bcastEntryPackageUrl: "string",
bcastEntryPageUrl: "string",
config: data,
css: allCss,
files: designerStore.app.files,
html: {
pre: getHeadContent(),
body: getHTMLBODYV2(),
},
script: designerStore.app.script || editor.value.getJs(),
sources: designerStore.app.sources || [],
types: componentsTypeScript.value,
defaults: componentsDefaults.value,
wrapper: editor.value.getComponents(),
events: designerStore.app.events || [],
variables: designerStore.app.variables || [],
aqIds: [],
groupIds: [],
}
if (designerStore.triedSaveToFile) {
const fileName = designerStore.modifiedAppName || baAppNameSaved;
const status = await downloadBAAppConfigAsJson(fileSaveConfig, fileName+'.json');
if (status) {
$q.notify({
type: "positive",
message: "Applet config download successful",
});
} else {
$q.notify({
type: "negative",
message: "Something went wrong while downloading the Applet config",
});
}
designerStore.setSaveToFile(false);
return;
}
const tenantId = useAuthStore().tenantId;
const postBody: any = {
...fileSaveConfig,
...{
id: designerStore.currentBAApplication.id,
ownerId: tenantId,
baAppName: baAppNameSaved,
title: designerStore.currentBAApplication.title,
deleted: false,
entryPageURL: `${baAppNameSaved}.html`,
metaData: [],
// version: designerStore.app.version,
}
};
try {
console.log("postBody", postBody);
store.commit('setSave',false)
await designerStore.putApp(postBody);
console.log("false")
// if(!store.state.saveToggle){}
Notify.create({
message: "App saved successfully!",
timeout: 2000,
position: "bottom",
color: "green",
textColor: "white",
badgeStyle: "display: none",
});
// store.commit('setSave',true);
designerStore
.captureScreenshot(designerStore.currentBAApplication.id)
.then((screenshotUrl) => {
const payload = {
id: designerStore.currentBAApplication.id,
thumbnail: screenshotUrl,
};
return designerStore.patchApp(payload);
});
} catch (error: any) {
$q.notify({
type: "negative",
message: error.response.data.errorMessage,
});
}
},
},
});
editor.value =editorNonReactive;
editor.value.on('rte:enable',()=>{
console.log("hello world 222222222222")
})
// Assuming you have access to the Rich Text Editor instance
const rte = editor.value.RichTextEditor;
// Add the 'bold' functionality
rte.add('bold', {
icon: '<b>B</b>',
attributes: { title: 'Bold' },
result: (rte: { exec: (arg0: string) => any; }) => rte.exec('bold')
});
rte.add('italic', {
icon: '<i>I</i>',
attributes: { title: 'Italic' },
result: (rte: { exec: (arg0: string) => any; }) => rte.exec('italic')
});
editor.value.onReady(async (e: any) => {
console.log("Ready", e);
editorStore.setEditor(editorNonReactive);
editorStore.setLayers(editorNonReactive.Layers);
// editor.value.runCommand("open-layers");
let blocks = editor.value?.BlockManager?.blocks?.models || [];
editorStore.addBlockManagerImages(blocks);
await nextTick();
try{
editorNonReactive.on('layer:custom', handleCustom);
editorNonReactive.on('layer:root', handleRootChange);
const lm = editorNonReactive.LayerManager;
lm.__trgCustom({ container: layerManagerContainer.value });
}
catch(error){
console.log(error);
}
// blockManager(editor.value)
styleManager(editor.value);
loadCustomFonts(editor.value);
loadZoomCommand(editor.value);
designerInitialized.value = true;
editor.value.on("component:add", (model: any) => {
if (model.attributes.type === "gjs-row") {
model.attributes.resizable = true;
model.setDragMode("absolute");
console.log("MOdel",model);
}
// this is to enable resize for all the elements other than row inside the column
else if(model.attributes.type !== "gjs-row" && model.attributes.type !== ""){
model.attributes.resizable = true;
model.setDragMode("absolute");
console.log("MOdel",model);
}
});
editor.value.on("modal", (props: any) => {
if (props.open) {
document
.querySelector("#canvasBlock > div.gjs-mdl-container")
?.setAttribute("title", "");
}
});
editor.value.on("component:select", (model: any) => {
console.log("select");
activeDrawerContent.value = "styles";
activeStylesTab.value = "Style Manager";
const selectedElement: HTMLElement = model.view.el;
const selectedChild: any =
selectedElement.childNodes.length > 0
? selectedElement.childNodes[0]
: null;
if (selectedChild !== null) {
const selectedCanvas: any =
selectedChild.childNodes.length > 0
? selectedChild.childNodes[0]
: null;
if (selectedCanvas !== null && selectedCanvas.tagName === "CANVAS") {
isChartCanvasSelected.value = true;
const classList = selectedElement.className;
if (classList.includes("gjs-selected")) {
const filteredClasses = classList
.replace("gjs-selected", "")
.trim();
if(filteredClasses.split(" ").length !== 0) {
var filteredArr = filteredClasses.split(" ");
filteredArr.forEach((ele_class: string) => {
selectedCanvas.classList.add(ele_class)
});
}
else {
selectedCanvas.classList.add(filteredClasses);
}
}
return;
}
}
isChartCanvasSelected.value = false;
setTimeout(() => {
setElementSelectedType(model.attributes);
}, 1);
});
editor.value.on("styleable:change", (model: any, property: any) => {
const value = model.getStyle()[property];
if (
property === "height" &&
isChartCanvasSelected.value &&
!value.includes("!important")
) {
model.addStyle({ [property]: value + ` !important` });
}
});
});
designerStore.changeFunctionality();
editor.value.runCommand("zoom-in-out-canvas", { value: 100 });
editor.value.on("run:ruler-visibility", (): void => {
console.log(toggleRuler.value);
toggleRuler.value = !toggleRuler.value;
});
// search plagiun hide and show
// designerStore.addSerchFilterToPlugins();
});
here is my code
### Code of Conduct
- [X] I agree to follow this project's Code of ConductAnswers (1)
From the official demo it looks to work as expected, so I guess it's related to your custom logic. Please provide a minimal reproducible demo of the issue.
Related Questions and Answers
Continue research with similar issue discussions.
Issue #5484
BUG: clone tabs and clone tab is not right now
GrapesJS version [X] I confirm to use the latest version of GrapesJS What browser are you using? latest version of Chrome Reproducible demo...
Issue #5549
BUG: mj-preview is not editable
GrapesJS version[X] I confirm to use the latest version of GrapesJSWhat browser are you using? Safari 17Reproducible demo link https://grap...
Issue #5006
aphostrope problem in grapejs - 's not showing while displaying from html string
GrapesJS version [X] I confirm to use the latest version of GrapesJS What browser are you using? Chrome v91 Reproducible demo link https://...
Issue #5941
BUG: GrapesJS CDN not working
GrapesJS version [X] I confirm to use the latest version of GrapesJS What browser are you using? Chrome 125.0.6422.113 Reproducible demo li...
Paid Plugins That Match This Issue
Curated by issue keywords and label relevance to help you ship faster.
Loading paid plugin recommendations...
Browse Plugin Categories
Jump directly to plugin category pages on the marketplace.