Skip to content

Commit

Permalink
Merge pull request #59 from wrabit/component_cleanup
Browse files Browse the repository at this point in the history
improved some coherence in component and slot
  • Loading branch information
wrabit authored Jul 15, 2024
2 parents 9241473 + d53432b commit 6abd7a8
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 45 deletions.
58 changes: 33 additions & 25 deletions django_cotton/templatetags/_component.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,54 +53,62 @@ def __init__(self, nodelist, template_path, component_key, kwargs):
self.kwargs = kwargs

def render(self, context):
local_ctx = context.flatten()
attrs = {}

for key, value in self.kwargs.items():
# strip single or double quotes only if both sides have them
if value and value[0] == value[-1] and value[0] in ('"', "'"):
value = value[1:-1]

if key.startswith(":"):
key = key[1:]
attrs[key] = self.process_dynamic_attribute(value, context)
elif value == "":
attrs[key] = True
else:
attrs[key] = value
attrs = self._build_attrs(context)

# Add the remainder as the default slot
local_ctx = context.flatten()
local_ctx["slot"] = self.nodelist.render(context)

# Merge slots and attributes into the local context
all_slots_ctx = context.get("cotton_slots", {})
component_slots_ctx = all_slots_ctx.get(self.component_key, {})
local_ctx.update(component_slots_ctx)
all_named_slots_ctx = context.get("cotton_named_slots", {})
local_named_slots_ctx = all_named_slots_ctx.get(self.component_key, {})
local_ctx.update(local_named_slots_ctx)

# We need to check if any dynamic attributes are present in the component slots and move them over to attrs
if "ctn_template_expression_attrs" in component_slots_ctx:
for expression_attr in component_slots_ctx["ctn_template_expression_attrs"]:
attrs[expression_attr] = component_slots_ctx[expression_attr]
if "ctn_template_expression_attrs" in local_named_slots_ctx:
for expression_attr in local_named_slots_ctx[
"ctn_template_expression_attrs"
]:
attrs[expression_attr] = local_named_slots_ctx[expression_attr]

# Build attrs string before formatting any '-' to '_' in attr names
attrs_string = " ".join(
f"{key}={ensure_quoted(value)}" for key, value in attrs.items()
)
local_ctx["attrs"] = mark_safe(attrs_string)

# Make the attrs available in the context for the vars frame, also before formatting the attr names
local_ctx["attrs_dict"] = attrs

# Store attr names in a callable format, i.e. 'x-init' will be accessible by {{ x_init }}
attrs = {key.replace("-", "_"): value for key, value in attrs.items()}
local_ctx.update(attrs)

# Reset the component's slots in context to prevent data leaking between components
all_slots_ctx[self.component_key] = {}
all_named_slots_ctx[self.component_key] = {}

return render_template(self.template_path, local_ctx)

def process_dynamic_attribute(self, value, context):
def _build_attrs(self, context):
"""
Build the attributes dictionary for the component
"""
attrs = {}

for key, value in self.kwargs.items():
# strip single or double quotes only if both sides have them
if value and value[0] == value[-1] and value[0] in ('"', "'"):
value = value[1:-1]

if key.startswith(":"):
key = key[1:]
attrs[key] = self._process_dynamic_attribute(value, context)
elif value == "":
attrs[key] = True
else:
attrs[key] = value

return attrs

def _process_dynamic_attribute(self, value, context):
"""
Process a dynamic attribute (prefixed with ":")
"""
Expand Down
39 changes: 19 additions & 20 deletions django_cotton/templatetags/_slot.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
def cotton_slot(parser, token):
"""
Template tag to render a cotton slot.
is_expression_attr: bool whether the attribute is dynamic or not.
dynamic attributes are from the use of native tags {{ {% in the component attribute. We put them through as a named
slot so they can be rendered and provided as a template variable
is_expression_attr: bool whether we are using the named_slot as an "expression attribute" or not.
expression attributes are from the use of native tags {{ {% and \n newlines within the attribute. We put them
through as a named slot so they can be rendered and provided as a template variable
"""
try:
tag_name, slot_name, component_key, *optional = token.split_contents()
Expand All @@ -29,29 +29,28 @@ def __init__(self, slot_name, nodelist, component_key, is_expression_attr):

def render(self, context):
# Add the rendered content to the context.
if "cotton_slots" not in context:
context.update({"cotton_slots": {}})
if "cotton_named_slots" not in context:
context.update({"cotton_named_slots": {}})

output = self.nodelist.render(context)

# Store the slot data in a component-namespaced dictionary
if self.component_key not in context["cotton_slots"]:
context["cotton_slots"][self.component_key] = {}
if self.component_key not in context["cotton_named_slots"]:
context["cotton_named_slots"][self.component_key] = {}

context["cotton_slots"][self.component_key][self.slot_name] = mark_safe(output)
context["cotton_named_slots"][self.component_key][self.slot_name] = mark_safe(
output
)

# If the slot is a dynamic attribute, we record it so it can be transferred to attrs in the component
# If the slot is being used as an expression attribute, we record it so it can be transferred to attrs in the component
if self.is_expression_attr:
if (
"ctn_template_expression_attrs"
not in context["cotton_slots"][self.component_key]
):
context["cotton_slots"][self.component_key][
"ctn_template_expression_attrs"
] = []

context["cotton_slots"][self.component_key][
"ctn_template_expression_attrs"
].append(self.slot_name)
key = "ctn_template_expression_attrs"

if key not in context["cotton_named_slots"][self.component_key]:
context["cotton_named_slots"][self.component_key][key] = []

context["cotton_named_slots"][self.component_key][key].append(
self.slot_name
)

return ""

0 comments on commit 6abd7a8

Please sign in to comment.