GrapesJS / grapesjs

Free and Open source Web Builder Framework. Next generation tool for building templates without coding
https://grapesjs.com
BSD 3-Clause "New" or "Revised" License
22.33k stars 4.05k forks source link

BUg in RichTextEditor #5942

Closed adarshsingh197 closed 3 months ago

adarshsingh197 commented 3 months ago

GrapesJS version

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

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

artf commented 3 months ago

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.