mirror of
https://github.com/django-components/django-components.git
synced 2025-09-23 06:02:27 +00:00
refactor: merge Base, Implicit and Named FillNodes into FillNode
This commit is contained in:
parent
c0c9e145a9
commit
9d9462162a
3 changed files with 40 additions and 46 deletions
|
@ -23,11 +23,8 @@ from django_components.component_registry import AlreadyRegistered, ComponentReg
|
||||||
from django_components.logger import logger
|
from django_components.logger import logger
|
||||||
from django_components.middleware import is_dependency_middleware_active
|
from django_components.middleware import is_dependency_middleware_active
|
||||||
from django_components.slots import (
|
from django_components.slots import (
|
||||||
DEFAULT_SLOT_KEY,
|
FillNode,
|
||||||
OUTER_CONTEXT_CONTEXT_KEY,
|
|
||||||
FillContent,
|
FillContent,
|
||||||
ImplicitFillNode,
|
|
||||||
NamedFillNode,
|
|
||||||
SlotName,
|
SlotName,
|
||||||
render_component_template_with_slots,
|
render_component_template_with_slots,
|
||||||
)
|
)
|
||||||
|
@ -299,7 +296,7 @@ class ComponentNode(Node):
|
||||||
context_args: List[FilterExpression],
|
context_args: List[FilterExpression],
|
||||||
context_kwargs: Mapping[str, FilterExpression],
|
context_kwargs: Mapping[str, FilterExpression],
|
||||||
isolated_context: bool = False,
|
isolated_context: bool = False,
|
||||||
fill_nodes: Sequence[Union[ImplicitFillNode, NamedFillNode]] = (),
|
fill_nodes: Sequence[FillNode] = (),
|
||||||
) -> None:
|
) -> None:
|
||||||
self.name_fexp = name_fexp
|
self.name_fexp = name_fexp
|
||||||
self.context_args = context_args or []
|
self.context_args = context_args or []
|
||||||
|
@ -330,7 +327,7 @@ class ComponentNode(Node):
|
||||||
resolved_context_args = [safe_resolve(arg, context) for arg in self.context_args]
|
resolved_context_args = [safe_resolve(arg, context) for arg in self.context_args]
|
||||||
resolved_context_kwargs = {key: safe_resolve(kwarg, context) for key, kwarg in self.context_kwargs.items()}
|
resolved_context_kwargs = {key: safe_resolve(kwarg, context) for key, kwarg in self.context_kwargs.items()}
|
||||||
|
|
||||||
if len(self.fill_nodes) == 1 and isinstance(self.fill_nodes[0], ImplicitFillNode):
|
if len(self.fill_nodes) == 1 and self.fill_nodes[0].is_implicit:
|
||||||
fill_content: Dict[str, FillContent] = {DEFAULT_SLOT_KEY: FillContent(self.fill_nodes[0].nodelist, None)}
|
fill_content: Dict[str, FillContent] = {DEFAULT_SLOT_KEY: FillContent(self.fill_nodes[0].nodelist, None)}
|
||||||
else:
|
else:
|
||||||
fill_content = {}
|
fill_content = {}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import difflib
|
import difflib
|
||||||
|
import json
|
||||||
import sys
|
import sys
|
||||||
from typing import Dict, List, NamedTuple, Optional, Sequence, Set, Tuple, Type, Union
|
from typing import Dict, List, NamedTuple, Optional, Sequence, Set, Tuple, Type, Union
|
||||||
|
|
||||||
|
@ -13,7 +14,7 @@ else:
|
||||||
from typing import TypeAlias
|
from typing import TypeAlias
|
||||||
|
|
||||||
from django.template import Context, Template
|
from django.template import Context, Template
|
||||||
from django.template.base import FilterExpression, Node, NodeList, TextNode
|
from django.template.base import FilterExpression, Node, NodeList, TextNode, Parser
|
||||||
from django.template.defaulttags import CommentNode
|
from django.template.defaulttags import CommentNode
|
||||||
from django.template.exceptions import TemplateSyntaxError
|
from django.template.exceptions import TemplateSyntaxError
|
||||||
from django.utils.safestring import SafeString, mark_safe
|
from django.utils.safestring import SafeString, mark_safe
|
||||||
|
@ -149,12 +150,25 @@ class SlotNode(Node, TemplateAwareNodeMixin):
|
||||||
raise ValueError(f"Unknown value for SLOT_CONTEXT_BEHAVIOR: '{app_settings.SLOT_CONTEXT_BEHAVIOR}'")
|
raise ValueError(f"Unknown value for SLOT_CONTEXT_BEHAVIOR: '{app_settings.SLOT_CONTEXT_BEHAVIOR}'")
|
||||||
|
|
||||||
|
|
||||||
class BaseFillNode(Node):
|
class FillNode(Node):
|
||||||
def __init__(self, nodelist: NodeList):
|
is_implicit: bool
|
||||||
self.nodelist: NodeList = nodelist
|
"""
|
||||||
|
Set when a `component` tag pair is passed template content that
|
||||||
|
excludes `fill` tags. Nodes of this type contribute their nodelists to slots marked
|
||||||
|
as 'default'.
|
||||||
|
"""
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __init__(
|
||||||
raise NotImplementedError
|
self,
|
||||||
|
nodelist: NodeList,
|
||||||
|
name_fexp: FilterExpression,
|
||||||
|
alias_fexp: Optional[FilterExpression] = None,
|
||||||
|
is_implicit: bool = False,
|
||||||
|
):
|
||||||
|
self.nodelist: NodeList = nodelist
|
||||||
|
self.name_fexp = name_fexp
|
||||||
|
self.alias_fexp = alias_fexp
|
||||||
|
self.is_implicit = is_implicit
|
||||||
|
|
||||||
def render(self, context: Context) -> str:
|
def render(self, context: Context) -> str:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(
|
||||||
|
@ -162,19 +176,7 @@ class BaseFillNode(Node):
|
||||||
"You are probably seeing this because you have used one outside "
|
"You are probably seeing this because you have used one outside "
|
||||||
"a {% component %} context."
|
"a {% component %} context."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class NamedFillNode(BaseFillNode):
|
|
||||||
def __init__(
|
|
||||||
self,
|
|
||||||
nodelist: NodeList,
|
|
||||||
name_fexp: FilterExpression,
|
|
||||||
alias_fexp: Optional[FilterExpression] = None,
|
|
||||||
):
|
|
||||||
super().__init__(nodelist)
|
|
||||||
self.name_fexp = name_fexp
|
|
||||||
self.alias_fexp = alias_fexp
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f"<{type(self)} Name: {self.name_fexp}. Contents: {repr(self.nodelist)}.>"
|
return f"<{type(self)} Name: {self.name_fexp}. Contents: {repr(self.nodelist)}.>"
|
||||||
|
|
||||||
|
@ -192,17 +194,6 @@ class NamedFillNode(BaseFillNode):
|
||||||
return resolved_alias
|
return resolved_alias
|
||||||
|
|
||||||
|
|
||||||
class ImplicitFillNode(BaseFillNode):
|
|
||||||
"""
|
|
||||||
Instantiated when a `component` tag pair is passed template content that
|
|
||||||
excludes `fill` tags. Nodes of this type contribute their nodelists to slots marked
|
|
||||||
as 'default'.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"<{type(self)} Contents: {repr(self.nodelist)}.>"
|
|
||||||
|
|
||||||
|
|
||||||
class _IfSlotFilledBranchNode(Node):
|
class _IfSlotFilledBranchNode(Node):
|
||||||
def __init__(self, nodelist: NodeList) -> None:
|
def __init__(self, nodelist: NodeList) -> None:
|
||||||
self.nodelist = nodelist
|
self.nodelist = nodelist
|
||||||
|
@ -272,7 +263,7 @@ class IfSlotFilledNode(Node):
|
||||||
def parse_slot_fill_nodes_from_component_nodelist(
|
def parse_slot_fill_nodes_from_component_nodelist(
|
||||||
component_nodelist: NodeList,
|
component_nodelist: NodeList,
|
||||||
ComponentNodeCls: Type[Node],
|
ComponentNodeCls: Type[Node],
|
||||||
) -> Sequence[Union[NamedFillNode, ImplicitFillNode]]:
|
) -> Sequence[FillNode]:
|
||||||
"""
|
"""
|
||||||
Given a component body (`django.template.NodeList`), find all slot fills,
|
Given a component body (`django.template.NodeList`), find all slot fills,
|
||||||
whether defined explicitly with `{% fill %}` or implicitly.
|
whether defined explicitly with `{% fill %}` or implicitly.
|
||||||
|
@ -291,7 +282,7 @@ def parse_slot_fill_nodes_from_component_nodelist(
|
||||||
Then this function returns the nodes (`django.template.Node`) for `fill "first_fill"`
|
Then this function returns the nodes (`django.template.Node`) for `fill "first_fill"`
|
||||||
and `fill "second_fill"`.
|
and `fill "second_fill"`.
|
||||||
"""
|
"""
|
||||||
fill_nodes: Sequence[Union[NamedFillNode, ImplicitFillNode]] = []
|
fill_nodes: Sequence[FillNode] = []
|
||||||
if _block_has_content(component_nodelist):
|
if _block_has_content(component_nodelist):
|
||||||
for parse_fn in (
|
for parse_fn in (
|
||||||
_try_parse_as_default_fill,
|
_try_parse_as_default_fill,
|
||||||
|
@ -314,11 +305,11 @@ def parse_slot_fill_nodes_from_component_nodelist(
|
||||||
def _try_parse_as_named_fill_tag_set(
|
def _try_parse_as_named_fill_tag_set(
|
||||||
nodelist: NodeList,
|
nodelist: NodeList,
|
||||||
ComponentNodeCls: Type[Node],
|
ComponentNodeCls: Type[Node],
|
||||||
) -> Sequence[NamedFillNode]:
|
) -> Sequence[FillNode]:
|
||||||
result = []
|
result = []
|
||||||
seen_name_fexps: Set[FilterExpression] = set()
|
seen_name_fexps: Set[FilterExpression] = set()
|
||||||
for node in nodelist:
|
for node in nodelist:
|
||||||
if isinstance(node, NamedFillNode):
|
if isinstance(node, FillNode):
|
||||||
if node.name_fexp in seen_name_fexps:
|
if node.name_fexp in seen_name_fexps:
|
||||||
raise TemplateSyntaxError(
|
raise TemplateSyntaxError(
|
||||||
f"Multiple fill tags cannot target the same slot name: "
|
f"Multiple fill tags cannot target the same slot name: "
|
||||||
|
@ -338,11 +329,11 @@ def _try_parse_as_named_fill_tag_set(
|
||||||
def _try_parse_as_default_fill(
|
def _try_parse_as_default_fill(
|
||||||
nodelist: NodeList,
|
nodelist: NodeList,
|
||||||
ComponentNodeCls: Type[Node],
|
ComponentNodeCls: Type[Node],
|
||||||
) -> Sequence[ImplicitFillNode]:
|
) -> Sequence[FillNode]:
|
||||||
nodes_stack: List[Node] = list(nodelist)
|
nodes_stack: List[Node] = list(nodelist)
|
||||||
while nodes_stack:
|
while nodes_stack:
|
||||||
node = nodes_stack.pop()
|
node = nodes_stack.pop()
|
||||||
if isinstance(node, NamedFillNode):
|
if isinstance(node, FillNode):
|
||||||
return []
|
return []
|
||||||
elif isinstance(node, ComponentNodeCls):
|
elif isinstance(node, ComponentNodeCls):
|
||||||
# Stop searching here, as fill tags are permitted inside component blocks
|
# Stop searching here, as fill tags are permitted inside component blocks
|
||||||
|
@ -351,7 +342,13 @@ def _try_parse_as_default_fill(
|
||||||
for nodelist_attr_name in node.child_nodelists:
|
for nodelist_attr_name in node.child_nodelists:
|
||||||
nodes_stack.extend(getattr(node, nodelist_attr_name, []))
|
nodes_stack.extend(getattr(node, nodelist_attr_name, []))
|
||||||
else:
|
else:
|
||||||
return [ImplicitFillNode(nodelist=nodelist)]
|
return [
|
||||||
|
FillNode(
|
||||||
|
nodelist=nodelist,
|
||||||
|
name_fexp=FilterExpression(json.dumps(DEFAULT_SLOT_KEY), Parser('')),
|
||||||
|
is_implicit=True,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def _block_has_content(nodelist: NodeList) -> bool:
|
def _block_has_content(nodelist: NodeList) -> bool:
|
||||||
|
|
|
@ -19,7 +19,7 @@ from django_components.slots import (
|
||||||
IfSlotFilledConditionBranchNode,
|
IfSlotFilledConditionBranchNode,
|
||||||
IfSlotFilledElseBranchNode,
|
IfSlotFilledElseBranchNode,
|
||||||
IfSlotFilledNode,
|
IfSlotFilledNode,
|
||||||
NamedFillNode,
|
FillNode,
|
||||||
SlotNode,
|
SlotNode,
|
||||||
_IfSlotFilledBranchNode,
|
_IfSlotFilledBranchNode,
|
||||||
parse_slot_fill_nodes_from_component_nodelist,
|
parse_slot_fill_nodes_from_component_nodelist,
|
||||||
|
@ -156,7 +156,7 @@ def do_slot(parser: Parser, token: Token) -> SlotNode:
|
||||||
|
|
||||||
|
|
||||||
@register.tag("fill")
|
@register.tag("fill")
|
||||||
def do_fill(parser: Parser, token: Token) -> NamedFillNode:
|
def do_fill(parser: Parser, token: Token) -> FillNode:
|
||||||
"""Block tag whose contents 'fill' (are inserted into) an identically named
|
"""Block tag whose contents 'fill' (are inserted into) an identically named
|
||||||
'slot'-block in the component template referred to by a parent component.
|
'slot'-block in the component template referred to by a parent component.
|
||||||
It exists to make component nesting easier.
|
It exists to make component nesting easier.
|
||||||
|
@ -182,7 +182,7 @@ def do_fill(parser: Parser, token: Token) -> NamedFillNode:
|
||||||
nodelist = parser.parse(parse_until=["endfill"])
|
nodelist = parser.parse(parse_until=["endfill"])
|
||||||
parser.delete_first_token()
|
parser.delete_first_token()
|
||||||
|
|
||||||
return NamedFillNode(
|
return FillNode(
|
||||||
nodelist,
|
nodelist,
|
||||||
name_fexp=FilterExpression(tgt_slot_name, tag),
|
name_fexp=FilterExpression(tgt_slot_name, tag),
|
||||||
alias_fexp=alias_fexp,
|
alias_fexp=alias_fexp,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue