Grapesjs: Text component problem

Created on 15 Jan 2018  路  25Comments  路  Source: artf/grapesjs

Hi. I wish a text component that do not create new components every time I press "Enter" key to create new paragraphs (this is a bad usability). In addition, the actual text component from the core of grapes has a problem (described in the screencast below). Thanks in advance for your attention!
gif-screen-grapes

bug help wanted

Most helpful comment

Not sure if I found this code in an Issue on here or on Stack Overflow, but this code here replaces the Divs with a
when you press the enter key. I placed this code in the same file as my grapes-config

grapesExample

var iframeBody = editor.Canvas.getBody();
    $(iframeBody).on("keydown", "[contenteditable]", e => {
        // trap the return key being pressed
        if (e.keyCode === 13) {
                e.preventDefault();
                // insert 2 br tags (if only one br tag is inserted the cursor won't go to the next line)
                e.target.ownerDocument.execCommand("insertHTML", false, "<br><br>");
                // prevent the default behaviour of return key pressed
                return false;
        }
    });

All 25 comments

I looked into this myself a few days ago...apparently there's an insertBrOnReturn option for execCommand, but the browser support is terrible. A solution like this is probably better.

@artf How would you want this to change? Should hitting enter insert <br> tags as shown in the top answer on the that SO discussion? Would you want to expose this as a configuration option?

I've found a solution to my needs using CKEditor and overriding the native "text" component creating my own. My text component now is a div with a css class that identifies the component:

comps.addType('text', {
      model: textModel.extend({
        defaults: Object.assign({}, textModel.prototype.defaults, {
          tagName: 'div',
          name: 'Texto',
          draggable: '*',
          droppable: false
        }),
      },
      {
        isComponent: function(el) {
          if(el.tagName == 'DIV' && el.classList.contains('txt')){
            return {type: 'text'};
          }
        },
      })
/*...*/

Now, typing ENTER does not create a new component. Nevertheless, this solution do not solve this problem, for those who don't want to use third party text editors.

@gabrigcl I agree and think that Ryan's suggestion could be a solution (apart the fact insertHTML is not supported by IE)

Yeah, I agree. Will you put this into roadmap?

@gabrigcl I leave this issue as an opened bug so no need to put it into roadmap

when use SHIFT + "Enter" to break line in text it will create <br/> tag instead of creating a new component maybe is rich text editor behavior (using default rte not using CKEditor).

when use SHIFT + "Enter" to break line in text it will create
tag instead of creating a new component maybe is rich text editor behavior (using default rte not using CKEditor).

WebKit's behavior

Hi,

There should be a solution by listening on component:add and then convert these <div> to <br/>.

I need help on how to convert these in the event callback, please could you have a look ?

editor.on('component:add', (child) => {
   // Check if new component is a 'text'
   if (child.attributes.tagName === 'div' && child.attributes.type === 'text') {
      // Check if parent of this new component is also a "text"
      const parent = child.parent()
      if (parent.attributes.tagName === 'div' && parent.attributes.type === 'text') {
         // Now here, replace '<div>content</div>' to '<br/>content'
         // FIXME this doesn't work
         const t = child.getEl().innerHTML
         child.destroy()
         parent.getEl().innerHTML += '<br/>' + t
      }
   }
})

I still struggle a lot with this issue.

Here is an idea, which is still not perfect, as it requires to rename the text component, and it removes empty lines :

var originalText = comps.getType('text')
comps.addType('text', {
   model: originalText.model.extend({
      defaults: Object.assign({}, originalText.model.prototype.defaults, {
         tagName: 'div',
         name: 'MyText',
         draggable: '*',
         droppable: false,
         attributes: { 'data-mytext': 'MyText' },
      }),
   }, {
      isComponent: function (el) {
         if (el.tagName === 'DIV' && el.getAttribute('data-mytext') === 'MyText') {
            const contentTexts = []
            Array.from(el.childNodes).forEach((node) => {
               if (node.textContent !== '') {
                  contentTexts.push(node.textContent)
               }
            })
            const content = contentTexts.join('<br/>')
            return { type: 'text', name: 'MyText', content: content, components: [] }
         }
      },
   }),
   view: originalText.view,
})

Ok, this is simpler and seems to work, but I don't even know how / why.

var originalText = comps.getType('text')
comps.addType('text', {
   model: originalText.model.extend({
      defaults: Object.assign({}, originalText.model.prototype.defaults, {
         tagName: 'div',
         name: 'MyText',
         draggable: '*',
         droppable: false,
         attributes: { 'data-mytext': 'MyText' },
      }),
   }, {
      isComponent: function (el) {
         if (el.tagName === 'DIV' && el.getAttribute('data-mytext') === 'MyText') {
            return { type: 'text', name: 'MyText', content: el.innerHTML, components: [] }
         }
      },
   }),
   view: originalText.view,
})

Hi drasil. Congratulations on your finding a solution, I'll test It. I've found a solution too, the same way, extending the text component

I'll share it soon

Any update for this?

The new release https://github.com/artf/grapesjs/releases/tag/v0.14.33 improved a bit the TextComponent. It still creates new paragraphs but the editor will hide them from the selection.
@gabrigcl from your original problem, one big issue that I guess you're doing there is, when you change the page, relying on HTML/CSS instead of the JSON and this is why are not editable after the change

one big issue that I guess you're doing there is, when you change the page, relying on HTML/CSS instead of the JSON and this is why are not editable after the change

Yes, I was doing like this. However I'm currently using a custom text component, made by me, that doesn't have this issue.

Great, for now, I close this

one big issue that I guess you're doing there is, when you change the page, relying on HTML/CSS instead of the JSON and this is why are not editable after the change

Yes, I was doing like this. However I'm currently using a custom text component, made by me, that doesn't have this issue.

Any news on this? We are still having issues when reloading.

@frasza your issue is not related https://github.com/artf/grapesjs/issues/1635#issuecomment-445242563

@artf Not exactly, but it could be. I would much rather prefer my Text block to insert <br> instead of creating new <div>.

Not sure if I found this code in an Issue on here or on Stack Overflow, but this code here replaces the Divs with a
when you press the enter key. I placed this code in the same file as my grapes-config

grapesExample

var iframeBody = editor.Canvas.getBody();
    $(iframeBody).on("keydown", "[contenteditable]", e => {
        // trap the return key being pressed
        if (e.keyCode === 13) {
                e.preventDefault();
                // insert 2 br tags (if only one br tag is inserted the cursor won't go to the next line)
                e.target.ownerDocument.execCommand("insertHTML", false, "<br><br>");
                // prevent the default behaviour of return key pressed
                return false;
        }
    });

The new release https://github.com/artf/grapesjs/releases/tag/v0.14.33 improved a bit the TextComponent. It still creates new paragraphs but the editor will hide them from the selection.
@gabrigcl from your original problem, one big issue that I guess you're doing there is, when you change the page, relying on HTML/CSS instead of the JSON and this is why are not editable after the change

I'd appreciate more information on how to use the JSON in cases when the data (HTML/CSS) will be stored in DB and used again for editing.

Also I have a custom component when dropped inside a Text component (I am using a custom text component that extends the original text component) after saving and reloading the HTML/CSS I am experiencing the same problem - the text component converts to a Box. I use a custom attribute, but no matter the attribute the type of the component converts from Text to Default.

Before saving:

<div data-gjs-type="text" data-highlightable="1" data-text-extended="Text">Insert <span data-gjs-type="custom-type" contenteditable="false" data-gjs-textable="true" >Custom comp test</span>your text here</div>

On loading already saved content:

<div data-gjs-type="default" data-highlightable="1" data-text-extended="Text">Insert <span data-gjs-type="custom-type" contenteditable="false" data-gjs-textable="true" >Custom comp test</span>your text here</div>

I'd appreciate more information on how to use the JSON in cases when the data (HTML/CSS) will be stored in DB and used again for editing.

The point @inaLar is: you shouldn't use the HTML/CSS if your purpose is to store and edit the template, you can use it only for the initial import...

The JSON is like a "file type" for the editor, so it would be the same like asking to have a Photoshop file with all the groups and layers by starting from a JPEG image... you can't, the image doesn't have that information, the same is for the HTML and CSS created by GrapesJS

@artf , I understand well what a JSON is, but I haven't seen any particular example or use case of the JSON storage for GrapesJS. It may be that I have missed some part of the documentation. Forgive me, but I am new to this and also I started with 0 experience in Backbone. If there is such an example I will appreciate any links. Speaking about examples - it will be very useful to have in the documentation information like what are the dependencies, etc. It may be very clear to you, as a creator what is the idea and the related technologies, but for a newbe with GrapesJS it takes not small effort to understand the basics without knowing these. I will risk to sound stupid, but it took me like a week to understand that it is based on Backbone :)

As for the HTML and CSS, for some reason I sore them and still managed to recognise which are the components on reload.

I understand well what a JSON is, but I haven't seen any particular example or use case of the JSON storage for GrapesJS

All you need is here: https://grapesjs.com/docs/modules/Storage.html

but it took me like a week to understand that it is based on Backbone

Well, that means that more or less I'm doing a good job 馃槀 but it would be better not to see it at all. Backbone is just an internal tiny layer for managing some basic structure stuff (maybe even too much tiny) but being a dependency it shouldn't be exposed, so doing this have in the documentation information like what are the dependencies would be totally wrong, from a framework point of view. Indeed, my goal is to cover the Backbone's API as much as possible, so if one day it'll be removed (which is might happen soon) the API will not break.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Geczy picture Geczy  路  3Comments

ionic666 picture ionic666  路  3Comments

kawika-connell picture kawika-connell  路  3Comments

adam-gpc picture adam-gpc  路  3Comments

kosirm picture kosirm  路  3Comments