neos / neos-ui

Neos CMS UI written in ReactJS with Immutable data structures.
GNU General Public License v3.0
265 stars 135 forks source link

WIP: Html comments for marking content elements and inline editables | Iframe post-message communication | Inline toolbar in host via position observing #3231

Open mhsdesign opened 2 years ago

mhsdesign commented 2 years ago

This is @grebaldi idea - im just trying to document it ^^

the current state of inline communication could be improved.

currently

Currently, a div is wrapped around the CE to tell the UI which node it came from, and also attaches a script tag that is used for details of the node for the UI.

proposed

3 major improvements which are:

the content element wrapping will consist of gt l comment start and en markers (and doesnt need to wrap a div element if there are multiple roots) The content element outline will be done in the host frame with javascript positioning (where we can calculate the total height of an component)

pro

copied from the docs at https://www.storyblok.com/docs/guide/essentials/visual-editor#enabling-click-events-on-your-html-elements:

Enabling click events on your HTML Elements

To enable the click events that will send messages to the Visual Editor you will have to output the content of the _editable property before each of your own HTML Elements. The content of the _editable property contains a HTML comment for each Story and Blok. The Storyblok Bridge will then automatically register click events of the HTML Element that follow that HTML comment.

Example Explanation

When you load one story using the URL parameter version=draft from the Content Delivery API you will receive a response like this:

{
  "story": {
    "id": 172223,
    "content": {
      "_editable": "<!--#storyblok#{\"name\": \"page\", \"space\": \"14141\", \"uid\": \"b370f32d-cd89-4dd3-9437-16b5e9746b31\", \"id\": \"172223\"}-->",
      "component": "page",
      "body": [
        {
          "_editable": "<!--#storyblok#{\"name\": \"hero\", \"space\": \"14141\", \"uid\": \"98b685a1-da10-4df2-837e-3429d5ea6f88\", \"id\": \"172223\"}-->",
          "component":"hero",
          "headline": "This is my headline!"
        }
      ]
    }
  }
}

We can then push that data through any template engine or framework of your choice to render HTML.

{{story.content._editable}}
<div class=\"content-type\">
    {% for blok in story.content.body %}
        {{blok._editable}}
        <div class=\"blok\">
            {{blok.custom_field}}
        </div>
    {% endfor %}
</div>

We end up with HTML that looks like this:

<!--#storyblok#{\"name\": \"page\", \"space\": \"14141\", \"uid\": \"b370f32d-cd89-4dd3-9437-16b5e9746b31\", \"id\": \"172223\"}-->
<div class="content-type">
    <!--#storyblok#{\"name\": \"hero\", \"space\": \"14141\", \"uid\": \"98b685a1-da10-4df2-837e-3429d5ea6f88\", \"id\": \"172223\"}-->
    <div class="blok">
        This is my headline!
    </div>
</div>

The result of combining the template and the loaded draft content is the above HTML, which will be loaded by the browser. At this point, the Storyblok Bridge will be loaded and initialized. Your part of the integration is already done by adding the HTML comments and from here on out the Storyblok JS Bridge will take over.

During this initialization, the Storyblok Bridge looks for every HTML comment in your HTML. Each HTML comment starting with <!--#storyblok# will be parsed. The parsed JSON will then be used to add two HTML attributes data-blok-c and data-blok-uid and the class storyblok--outline to the HTML element right after the HTML comment.

<!--#storyblok#{\"name\": \"page\", \"space\": \"14141\", \"uid\": \"b370f32d-cd89-4dd3-9437-16b5e9746b31\", \"id\": \"172223\"}-->
<div class="content-type storyblok--outline" data-blok-c="{escapedJSONData}" data-blok-uid="b370f32d-cd89-4dd3-9437-16b5e9746b31">
    <!--#storyblok#{\"name\": \"hero\", \"space\": \"14141\", \"uid\": \"98b685a1-da10-4df2-837e-3429d5ea6f88\", \"id\": \"172223\"}-->
    <div class="blok storyblok--outline" data-blok-c="{escapedJSONData}" data-blok-uid="98b685a1-da10-4df2-837e-3429d5ea6f88">
        This is my headline!
    </div>
</div>

The class storyblok--outline will add a dotted border to your HTML element while hovering on your site in the Visual Editor Preview. Once you click on one HTML element the name of the blok will be added with a colored border to show which block is currently selected.

In this process, the Storyblok Bridge will also register the click eventListeners on your HTML elements that will open up the story or block in the Visual Editor Content area.

... wip

contra

Sebobo commented 2 years ago

I think I removed already all content scripts tags in a PR as they were not needed anymore since I refactored the content tree. Have to check again.

grebaldi commented 1 year ago

Thanks for writing this up @mhsdesign :rocket: . I always have all this stuff floating around in my head and I never get around to writing it down :sweat_smile: .

Some additions:

RE @mhsdesign:

html comments for marking content elements and inline editables

This is what I call "minimally invasive content element wrapping". I'm planning on creating an experimental PR for this soonish. It can be done independently from the other ones and would imho be a huge improvement.

RE @mhsdesign:

why should the ckeditor live inline in the iframe and the structured https://github.com/Flowpack/Flowpack.StructuredEditing inline editor be in the host -> where is the line

@mficzel and @dfeyer both have played with the idea of providing our editors (as in @neos-project/neos-ui-editors) as web components for general use. This is primarily meant for integration with backend modules, but I think it could be designed in a way that covers inline editing as well.

Strictly speaking, all inline editors would live in the guest frame, but all would be built from one code base and isolated from other guest frame contents.

RE @Sebobo:

I think I removed already all content scripts tags in a PR as they were not needed anymore since I refactored the content tree. Have to check again.

I think so too.

However, the guest frame still contains a global window['@Neos.Neos.Ui:DocumentInformation'] object that is being evaluated as plain JavaScript atm. A slight performance improvement could be achieved by reading this as plain JSON instead, because as it turns out JSON.parse can be up to 2x faster than parsing the same chunk of text as JavaScript (see: https://v8.dev/blog/cost-of-javascript-2019#json).

mhsdesign commented 1 year ago

Iframe post-message communication

this initial goal of mine doesnt seem to be easily reachable as we initialize the ckeditor from the host. This is important as we can easily register plugins. So for a headless approach we require the use of a proxy to avoid cross origin domain problems.


We would feel more confortable with a html comment based api between host and iframe which should also become documented api for the usecase of headless cms.

grebaldi commented 1 year ago

Regarding "Minimally Invasive Content Element Wrapping": I've started working on this a while ago (though I'm stuck). In the end there are supposed to be 2 PRs. One's for neos-development-collection: https://github.com/neos/neos-development-collection/compare/8.3...grebaldi:neos-de[…]on:proof-of-concept/minimally-invasive-content-element-wrapping

This one changes the way in which Fusion handles Content Collections. It also creates a basis for a slightly better integration of Content Element Wrapping in general, so that this is no longer done via an Aspect in neos-ui.

The second PR's for neos-ui: https://github.com/neos/neos-ui/compare/8.3...grebaldi:PackageFactory.Guevara:proof-of-concept/minimally-invasive-content-element-wrapping

This one is supposed to handle the parsing of HTML comments and initialization of content element DOM nodes. I've been working in an explorative manner around this, and still haven't settled the overall approach and architecture, but the basic principle is probably visible in the code.

Sebobo commented 6 months ago

I'm currently working on dropping the script tags for content nodes completely in https://github.com/neos/neos-ui/pull/3770