Closed gabrigcl closed 6 years ago
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
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.
@drasill Your second last solution worked OK re converting a html div e.g. <div data-mytext="MyText">HI THERE <div>sub-div</div> </div>
with nested divs into a single div with </br>
for carriage returns instead, however your later solution solution which simply returns content: el.innerHTML
doesn't work for me since it just returns the html unchanged. If you had returned el.innerText
then that at least that would have removed the nested divs and replaced them with spaces. Going back to your second last solution, it seems it can be simplified to:
const compType = 'text3'
const text3Plugin = editor => {
editor.DomComponents.addType(compType, {
extend: "text",
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: compType, name: 'MyText', content: content, components: [] }
}
},
model: {
defaults: {
tagName: 'div',
},
},
})
editor.BlockManager.add(compType, {
label: compType,
category: 'Extra',
content: '<div data-mytext="MyText">HI THERE <div>sub-div</div> </div>',
});
}
Even so, when you actually edit the component and hit ENTER divs will be inserted again. The solution by @Moikapy is the best so far, here is a non jquery version:
// Prevent DIVs whilst editing.
var iframeBody = editor.Canvas.getBody();
iframeBody.addEventListener("keydown", (e) => {
if (e.keyCode === 13 && e.target.getAttribute('contenteditable') && e.target.getAttribute('data-gjs-type') == compType) {
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;
}
});
however often it inserts too many BR's and puts the cursor in the wrong place - perhaps I re-implemented it incorrectly? https://jsfiddle.net/tcab/Lcor654e/
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!