Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add turbo_power support #35

Merged
merged 8 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/turbo_helper/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from .constants import TURBO_STREAM_MIME_TYPE as TURBO_STREAM_CONTENT_TYPE
from .constants import ResponseFormat
from .response import HttpResponseSeeOther, TurboStreamResponse
from .shortcuts import redirect_303, response_format
from .stream import register_turbo_stream_action, turbo_stream
from .templatetags.turbo_helper import dom_id

# extend turbo_stream actions, inspired by https://github.com/marcoroth/turbo_power
from .turbo_power import * # noqa

__all__ = [
"turbo_stream",
"register_turbo_stream_action",
Expand All @@ -14,5 +16,4 @@
"dom_id",
"response_format",
"ResponseFormat",
"TURBO_STREAM_CONTENT_TYPE",
]
7 changes: 6 additions & 1 deletion src/turbo_helper/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ def render_turbo_stream(

element_attributes_array = []
for key, value in element_attributes.items():
if value is None:
continue
# TODO: bool type django/forms/widgets/attrs.html
element_attributes_array.append(f'{key}="{escape(value)}"')

attribute_string = mark_safe(" ".join(element_attributes_array))

django_engine = engines["django"]
template_string = """<turbo-stream action="{{ action }}"{% if target %} target="{{ target }}"{% else %} targets="{{ targets }}"{% endif %}{% if attribute_string %} {{ attribute_string }}{% endif %}><template>{{ content|default:'' }}</template></turbo-stream>"""
template_string = """<turbo-stream action="{{ action }}"{% if target %} target="{{ target }}"{% elif targets %} targets="{{ targets }}"{% endif %}{% if attribute_string %} {{ attribute_string }}{% endif %}><template>{{ content|default:'' }}</template></turbo-stream>"""
context = {
"content": content,
"action": action,
Expand All @@ -43,13 +45,16 @@ def render_turbo_frame(frame_id: str, content: str, attributes: Dict[str, Any])
# convert data_xxx to data-xxx
element_attributes = {}
for key, value in attributes.items():
# convert data_xxx to data-xxx
if key.startswith("data"):
element_attributes[key.replace("_", "-")] = value
else:
element_attributes[key] = value

element_attributes_array = []
for key, value in element_attributes.items():
if value is None:
continue
# TODO: bool type django/forms/widgets/attrs.html
element_attributes_array.append(f'{key}="{escape(value)}"')

Expand Down
24 changes: 18 additions & 6 deletions src/turbo_helper/templatetags/turbo_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,24 @@ def render(self, context):
)
elif targets:
action = self.action.resolve(context)
func = getattr(turbo_stream, f"{action}_all")
return func(
targets=targets,
content=children,
**attributes,
)
func = getattr(turbo_stream, f"{action}_all", None)

if func:
return func(
targets=targets,
content=children,
**attributes,
)
else:
# fallback to pass targets to the single target handler
# we do this because of turbo_power
action = self.action.resolve(context)
func = getattr(turbo_stream, f"{action}")
return func(
targets=targets,
content=children,
**attributes,
)


class TurboStreamFromTagNode(Node):
Expand Down
153 changes: 153 additions & 0 deletions src/turbo_helper/turbo_power.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
"""
https://github.com/marcoroth/turbo_power-rails

Bring turbo_power to Django
"""
import json

from django.utils.safestring import mark_safe

from turbo_helper import register_turbo_stream_action, turbo_stream


def transform_attributes(attributes):
transformed_attributes = {}
for key, value in attributes.items():
transformed_key = transform_key(key)
transformed_value = transform_value(value)
transformed_attributes[transformed_key] = transformed_value
return transformed_attributes


def transform_key(key):
return str(key).replace("_", "-")


def transform_value(value):
if isinstance(value, str):
return value
elif isinstance(value, (int, float, bool)):
return str(value).lower()
elif value is None:
return None
else:
return json.dumps(value)


################################################################################


def custom_action(action, target=None, content=None, **kwargs):
return turbo_stream.action(
action, target=target, content=content, **transform_attributes(kwargs)
)


def custom_action_all(action, targets=None, content=None, **kwargs):
return turbo_stream.action_all(
action, targets=targets, content=content, **transform_attributes(kwargs)
)


# DOM Actions


@register_turbo_stream_action("graft")
def graft(targets=None, parent=None, **attributes):
return custom_action_all(
"graft",
targets=targets,
parent=parent,
**attributes,
)


@register_turbo_stream_action("morph")
def morph(targets=None, html=None, **attributes):
html = html or attributes.pop("content", None)
return custom_action_all(
"morph",
targets=targets,
content=mark_safe(html) if html else None,
**attributes,
)


# Attribute Actions


@register_turbo_stream_action("add_css_class")
def add_css_class(targets=None, classes="", **attributes):
classes = attributes.get("classes", classes)
if isinstance(classes, list):
classes = " ".join(classes)

return custom_action_all(
"add_css_class",
targets=targets,
classes=classes,
**attributes,
)


# Event Actions


@register_turbo_stream_action("dispatch_event")
def dispatch_event(targets=None, name=None, detail=None, **attributes):
detail = detail or {}
return custom_action_all(
"dispatch_event",
targets=targets,
name=name,
content=mark_safe(json.dumps(detail, separators=(",", ":"))),
**attributes,
)


# Notification Actions


@register_turbo_stream_action("notification")
def notification(title=None, **attributes):
return custom_action(
"notification",
title=title,
**attributes,
)


# Turbo Actions


@register_turbo_stream_action("redirect_to")
def redirect_to(url=None, turbo_action="advance", turbo_frame=None, **attributes):
return custom_action(
"redirect_to",
url=url,
turbo_action=turbo_action,
turbo_frame=turbo_frame,
**attributes,
)


# Turbo Frame Actions


@register_turbo_stream_action("turbo_frame_reload")
def turbo_frame_reload(target=None, **attributes):
return custom_action(
"turbo_frame_reload",
target=target,
**attributes,
)


@register_turbo_stream_action("turbo_frame_set_src")
def turbo_frame_set_src(target=None, src=None, **attributes):
return custom_action(
"turbo_frame_set_src",
target=target,
src=src,
**attributes,
)
Loading
Loading