Xzya / django-web-components

A simple way to create reusable template components in Django.
MIT License
159 stars 4 forks source link

modify the slot render method, e.g. using markdownify #12

Closed nerdoc closed 8 months ago

nerdoc commented 9 months ago

Hi Xzya, thanks for this wonderful piece of software. I have an idea, and would like to hear if you'd find this useful, I did not find a way of achieving it.

Would it be possible to modify the rendering for each block in dwc? In short, I'd like to have the inner_block of a component render Markdown, e.g. using the markdownify filter. I think this must somehow be done in the render_slot method.

Would it be possible to add a customization there somehow?

I think this would be a helpful addon. It would enable component slots to be written in Markdown, uppercase etc.

Or am I overlooking something completely and it is easy to do?

Xzya commented 8 months ago

Hello,

If I understand correctly, you want to have a component who's content (inner_block) should be converted from markdown to HTML (or the other way around), e.g.

{% some_component %}
    **This is bold text**

    *This is italic text*
{% endsome_component %}

<!-- Will be turned into this -->
<strong>This is bold text</strong>
<em>This is italic text</em>

My initial thought is that this can be easily achieved without the need of components, by creating a custom Django template tag, e.g.

# templatetags/app_tags.py
from django import template
from django.utils.safestring import mark_safe
import markdown
import textwrap

register = template.Library()

class MarkdownNode(template.Node):
    def __init__(self, nodelist):
        self.nodelist = nodelist

    def render(self, context):
        content = self.nodelist.render(context)
        content = textwrap.dedent(content)
        return mark_safe(markdown.markdown(content))

@register.tag(name="markdown")
def do_markdown(parser, token):
    nodelist = parser.parse(("endmarkdown",))
    parser.delete_first_token()
    return MarkdownNode(nodelist)
{% load app_tags %}

{% markdown %}
    **This is bold text**

    *This is italic text*
{% endmarkdown %}

<!-- Will be turned into this -->
<strong>This is bold text</strong>
<em>This is italic text</em>

This can then be used inside any Django template, including with components, e.g.

{% some_component %}
    {% markdown %}
        **This is bold text**

        *This is italic text*
    {% endmarkdown %}
{% endsome_component %}

Does this make sense? Or am I misunderstanding your use case?

nerdoc commented 8 months ago

Yes, this would be possible, but adds visual clutter. I'd like to have a component where the inner_block, or the header or foo slot is rendered in a special way. Markdown would be one (but very common) one.

I think this would be good to implement in a more generic way than just inner_block -> markdown...

With your example, I would have to write the templatetags again and again in each component's render tags, so this is not DRY.

Xzya commented 8 months ago

Hello,

You could also wrap the render_slot tag with the markdown block inside the component:

@component.register
def some_component(context):
    return CachedTemplate(
        """
        {% load app_tags %}

        <div>
            {% markdown %}
                {% render_slot slots.inner_block %}
            {% endmarkdown %}
        </div>
        """,
        name="some_component",
    ).render(context)

Now you can use the component without explicitly wrapping it in the markdown block:

{% some_component %}
    **This is bold text**

    *This is italic text*  
{% endsome_component%}

This should work with any slots, not just the inner_block.

nerdoc commented 8 months ago

Now that's a good idea. Thanks, for helping me out from being a blockhead. No need to do more. There is no markdown/endmarkdown templatetag yet, but that would be easy to achieve. I opened an issue at markdownify.