verbb / vizy

A flexible visual editor for Craft CMS
Other
44 stars 8 forks source link

How to declare twig markup for a custom node or mark? #291

Open tomkiss opened 6 months ago

tomkiss commented 6 months ago

Question

I've read through the user guides for custom nodes and marks, but I am left wondering - is it possible to declare custom twig markup for a mark, without getting caught up in repetition in twig? I have some very twig specific shared code that I'd like to use in my custom mark output (in a different manner to the editor).

For example, here I can render my custom mark with whatever twig code I like:

  {% for node in vizyBlocks %}
      {% if node.type == 'paragraph' %}
        <p>
          {% for nodeContent in node.content %}
            {% if nodeContent.type == "customMark" %}
              {# Some very twig specific markup here #}
            {% else %}
              {{ nodeContent.renderHtml() }}
            {% endif %}
          {% endfor %}
        </p>
      {% else %}
        {{ node.renderHtml() }}
      {% endif %}    
  {% endfor %}

Some markup like that would work fine, until I use the mark in a listItem, at which point I would need to add another place to parse it:

  {% for node in vizyBlocks %}
      {% if node.type == 'paragraph' %}
        <p>
          {% for nodeContent in node.content %}
            {% if nodeContent.type == "customMark" %}
              {# My very twig-specific markup here #}
            {% else %}
              {{ nodeContent.renderHtml() }}
            {% endif %}
          {% endfor %}
        </p>
      {% elseif node.type == 'orderedList' or node.type == 'bulletList' %}
        {{ node.type == 'orderedList' ? "<ol>" : "<ul>" }}
          {% for nodeContent in node.content %}
            {% if nodeContent.type == "listItem" %}
              <li>
              {% for nestedNodeContent in nodeContent.content %}
                {% if nestedNodeContent.type == "customMark" %}
                  {# My very-twig specific markup here #}
                {% else %}
                  {{ nestedNodeContent.renderHtml() }}
                {% endif %}
              {% endfor %}
              </li>
            {% else %}
              {{ nodeContent.renderHtml() }}
            {% endif %}
          {% endfor %}
          {{ node.type == 'orderedList' ? "</ol>" : "</ul>" }}
      {% else %}
        {{ node.renderHtml() }}
      {% endif %}    
  {% endfor %}

And then suddenly we have repetition, not to mention some horrible conditional markup. Plus, there will be other nested occurances of the mark where even further checking is required (table cell or similar elements).

Would there be a cleaner way to achieve this?

Additional context

No response

engram-design commented 6 months ago

The short answer is - not really. That's because of how the ProseMirror schema and structure works for nodes and marks. They aren't self-contained, as they need to factor in multiple marks.

Conceptually, you have a Node (paragraph) with text, and multiple marks (bold and italic). You need to wrap the text with both bold and italic tags, which are then wrapped with a paragraph. For example

So while it would be neat to be able to provide a Twig template for just the modular mark or node, that's not currently possible until I can come up with a nice way to handle things.

Now technically, a mark doesn't have content, it's the parent node that has the content, so you can handle things somewhat, it depends on how custom you want to go with your content in your comment below. Maybe it's a better case for a Node over a Mark?

This will allow you to ditch the:

{% if nodeContent.type == "customMark" %}
  {# My very twig-specific markup here #}
{% else %}
  {{ nodeContent.renderHtml() }}
{% endif %}

And just use:

{{ nodeContent.renderHtml() }}

This can be achieved by overriding the getTag() function for a node/mark

You could also use the renderOpeningTag() and renderClosingTag() for more flexibility (just in case you want to add multiple tags, static content, any HTML you require).

It's on my list to improve the modularity of templates for nodes and marks, and make things a little more Craft-friendly, so you don't have to worry about the intricacies of dealing with ProseMirror's schema for this sort of thing.