RFC: Proposal for adding multiple surfaces in substance Editor
Trying to solve the issue of multiple paragraphs in Notes and Isolating the Chapter Title feature from the main surface, i thinking of finding a more generic solution which solves both issues. So i would like to mention the way substance works to render the content and how we can refactor that to accept multiple containers.
How Substance works
Substance requests the content as a string of html tags, when we import it in createSession() with importDocument method. We can provide the whole html structure or just paragraphs.
Exactly before parsing the content Substance initializes the Article Class (ProseArticle) in which initializes a node with type "Container" with the id "Body", which is essential in order to add to that container the content.
_initialize() {
this.create({
type: 'container',
id: 'body',
nodes: []
})
}
Substance will parse the content and will create the Surface with id "body", which is hardcoded in the ConvertDocument() of HtmlImporter.
convertDocument (bodyEls) {
...
...
this.convertContainer(bodyElement, 'body')
}
How substance parses the content
Substance takes the input we gave him and creates the whole structure :
input :
<p>11111</p><p>2222</p>
substance:
- creates the html strucure with the body we gave him
<html>
<head></head>
<body>
<p>11111</p>
<p>2222</p>
</body>
</html>
- Selects the body Tags and gives is to the convertContainer mentioned above:
const content = content.el.querySelector('Body')
this.convertContainer(content, 'body') ' Hardcoded \"body\" String
- In render in the Default layouts we display the actual content by generating a ContainerEditor Component by providing the containerId we want to use as surface. In our case we using only the containerId "body" which has been created by default.
In this way when we want to show the actual content of the editor we render the ContainerEditor Component using the surface (container) with containerId : "body" which again is hardcoded in our code.
Proposal
My thinking is that we can use the process of Substance and create multiple containers which gives us many advantages.
-
Having multiple surfaces bind in the same ToolGroup or New ToolGRoup (needs investigation but i think is feasible),
-
Isolating one surface from the other. We came to that problem when we were trying to put the Chapter Title in the editor without that be editable . So we had to put an non editable element in a contentitable area which is doable but there are many cases where this element can be deleted.
-
We solve the issue with multiple paragraphs in the Notes Editor.
In order to implement that we are going to change the parsing method of Substance by rewriting the convertDocument. The idea is to give to substance a content in that format:
<div id="header">
<p>1234</p>
<p>5678</p>
</div>
<div id="body">
<p>91011</p>
<p>1213</p>
</div>
<div id="footer">
<p>1415</p>
<p>1617</p>
</div>
Substance will parse content like mentioned above.
- Creates a valid html document
<body>
<div id="header">
...
</div>
<div id="body">
...
</div>
<div id="footer">
...
</div>
</body>
</hmtl>
-
Then selects the body tag :
content = querySelector('body') and then returns the the children of body in DOmBroswerElement Object Format: > <div id="header"> , <div id="body"> , <div id="footer">
The parsed content passed to the convertDocument which creates convertContainer. So i rewrite that method and create instead of one, 3 containers mapping with the id of each div#id.
bodyELs = [<div id="header"> , <div id="body"> , <div id="footer">]
bodyEls.forEach((elm, containerId) => {
const bodyElement = elm.childNodes // <p>1111</p><p>2222</p>
this.convertContainer(bodyElement, (elm.id ? elm.id : 'body'))
})
So now we can create a ContainerEditor with id depending the id of divs. containerId = header, containerId = body, containerId=footer.
Probably we can go further by introducing a custom tag to mark the available containers in the html and not the div.