mirror of
https://github.com/django-components/django-components.git
synced 2025-09-22 13:42:27 +00:00
refactor: simplify slot intermediate slot types
This commit is contained in:
parent
094e05054d
commit
8d3a2ba8db
2 changed files with 32 additions and 33 deletions
|
@ -2,7 +2,7 @@ import inspect
|
|||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, ClassVar, Dict, Iterable, List, Mapping, MutableMapping, Optional, Tuple, Type, Union
|
||||
from typing import Any, ClassVar, Dict, List, Mapping, MutableMapping, Optional, Sequence, Tuple, Type, Union
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.forms.widgets import Media, MediaDefiningClass
|
||||
|
@ -23,12 +23,12 @@ from django_components.component_registry import AlreadyRegistered, ComponentReg
|
|||
from django_components.logger import logger
|
||||
from django_components.middleware import is_dependency_middleware_active
|
||||
from django_components.slots import (
|
||||
DefaultFillContent,
|
||||
ImplicitFillNode,
|
||||
NamedFillContent,
|
||||
NamedFillNode,
|
||||
FillContent,
|
||||
SlotName,
|
||||
render_component_template_with_slots,
|
||||
DEFAULT_SLOT_KEY,
|
||||
)
|
||||
from django_components.utils import search
|
||||
|
||||
|
@ -186,7 +186,7 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
|||
self,
|
||||
registered_name: Optional[str] = None,
|
||||
outer_context: Optional[Context] = None,
|
||||
fill_content: Union[DefaultFillContent, Iterable[NamedFillContent]] = (), # type: ignore
|
||||
fill_content: dict[str, FillContent] = {}, # type: ignore
|
||||
):
|
||||
self.registered_name: Optional[str] = registered_name
|
||||
self.outer_context: Context = outer_context or Context()
|
||||
|
@ -280,14 +280,13 @@ class Component(View, metaclass=SimplifiedInterfaceMediaDefiningClass):
|
|||
escape_content: bool = True,
|
||||
) -> None:
|
||||
"""Fill component slots outside of template rendering."""
|
||||
self.fill_content = [
|
||||
(
|
||||
slot_name,
|
||||
self.fill_content = {
|
||||
slot_name: FillContent(
|
||||
TextNode(escape(content) if escape_content else content),
|
||||
None,
|
||||
)
|
||||
for (slot_name, content) in slots_data.items()
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
class ComponentNode(Node):
|
||||
|
@ -299,20 +298,14 @@ class ComponentNode(Node):
|
|||
context_args: List[FilterExpression],
|
||||
context_kwargs: Mapping[str, FilterExpression],
|
||||
isolated_context: bool = False,
|
||||
fill_nodes: Union[ImplicitFillNode, Iterable[NamedFillNode]] = (),
|
||||
fill_nodes: Sequence[Union[ImplicitFillNode, NamedFillNode]] = (),
|
||||
) -> None:
|
||||
self.name_fexp = name_fexp
|
||||
self.context_args = context_args or []
|
||||
self.context_kwargs = context_kwargs or {}
|
||||
self.isolated_context = isolated_context
|
||||
self.fill_nodes = fill_nodes
|
||||
self.nodelist = self._create_nodelist(fill_nodes)
|
||||
|
||||
def _create_nodelist(self, fill_nodes: Union[Iterable[Node], ImplicitFillNode]) -> NodeList:
|
||||
if isinstance(fill_nodes, ImplicitFillNode):
|
||||
return NodeList([fill_nodes])
|
||||
else:
|
||||
return NodeList(fill_nodes)
|
||||
self.nodelist = NodeList(fill_nodes)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "<ComponentNode: {}. Contents: {!r}>".format(
|
||||
|
@ -330,16 +323,18 @@ class ComponentNode(Node):
|
|||
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()}
|
||||
|
||||
if isinstance(self.fill_nodes, ImplicitFillNode):
|
||||
fill_content = self.fill_nodes.nodelist
|
||||
if len(self.fill_nodes) == 1 and isinstance(self.fill_nodes[0], ImplicitFillNode):
|
||||
fill_content: Dict[str, FillContent] = {
|
||||
DEFAULT_SLOT_KEY: FillContent(self.fill_nodes[0].nodelist, None)
|
||||
}
|
||||
else:
|
||||
fill_content = []
|
||||
fill_content = {}
|
||||
for fill_node in self.fill_nodes:
|
||||
# Note that outer component context is used to resolve variables in
|
||||
# fill tag.
|
||||
resolved_name = fill_node.name_fexp.resolve(context)
|
||||
resolved_fill_alias = fill_node.resolve_alias(context, resolved_component_name)
|
||||
fill_content.append((resolved_name, fill_node.nodelist, resolved_fill_alias))
|
||||
fill_content[resolved_name] = FillContent(fill_node.nodelist, resolved_fill_alias)
|
||||
|
||||
component: Component = component_cls(
|
||||
registered_name=resolved_component_name,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import difflib
|
||||
import sys
|
||||
from typing import Dict, Iterable, List, Optional, Set, Tuple, Type, Union
|
||||
from typing import Dict, List, NamedTuple, Optional, Sequence, Set, Tuple, Type, Union
|
||||
|
||||
if sys.version_info[:2] < (3, 9):
|
||||
from typing import ChainMap
|
||||
|
@ -19,16 +19,20 @@ from django.template.exceptions import TemplateSyntaxError
|
|||
from django.utils.safestring import SafeString, mark_safe
|
||||
|
||||
FILLED_SLOTS_CONTENT_CONTEXT_KEY = "_DJANGO_COMPONENTS_FILLED_SLOTS"
|
||||
DEFAULT_SLOT_KEY = "_DJANGO_COMPONENTS_DEFAULT_SLOT"
|
||||
|
||||
# Type aliases
|
||||
|
||||
SlotName = str
|
||||
AliasName = str
|
||||
|
||||
DefaultFillContent: TypeAlias = NodeList
|
||||
NamedFillContent = Tuple[SlotName, NodeList, Optional[AliasName]]
|
||||
|
||||
FillContent = Tuple[NodeList, Optional[AliasName]]
|
||||
class FillContent(NamedTuple):
|
||||
"""Data passed from component to slot to render that slot"""
|
||||
nodes: NodeList
|
||||
alias: Optional[AliasName]
|
||||
|
||||
|
||||
FilledSlotsContext = ChainMap[Tuple[SlotName, Template], FillContent]
|
||||
|
||||
|
||||
|
@ -103,7 +107,7 @@ class SlotNode(Node, TemplateAwareNodeMixin):
|
|||
|
||||
extra_context = {}
|
||||
try:
|
||||
slot_fill_content: FillContent = filled_slots_map[(self.name, self.template)]
|
||||
slot_fill_content = filled_slots_map[(self.name, self.template)]
|
||||
except KeyError:
|
||||
if self.is_required:
|
||||
raise TemplateSyntaxError(
|
||||
|
@ -244,7 +248,7 @@ class IfSlotFilledNode(Node):
|
|||
def parse_slot_fill_nodes_from_component_nodelist(
|
||||
component_nodelist: NodeList,
|
||||
ComponentNodeCls: Type[Node],
|
||||
) -> Union[Iterable[NamedFillNode], ImplicitFillNode]:
|
||||
) -> Sequence[Union[NamedFillNode, ImplicitFillNode]]:
|
||||
"""
|
||||
Given a component body (`django.template.NodeList`), find all slot fills,
|
||||
whether defined explicitly with `{% fill %}` or implicitly.
|
||||
|
@ -263,7 +267,7 @@ def parse_slot_fill_nodes_from_component_nodelist(
|
|||
Then this function returns the nodes (`django.template.Node`) for `fill "first_fill"`
|
||||
and `fill "second_fill"`.
|
||||
"""
|
||||
fill_nodes: Union[Iterable[NamedFillNode], ImplicitFillNode] = []
|
||||
fill_nodes: Sequence[Union[NamedFillNode, ImplicitFillNode]] = []
|
||||
if _block_has_content(component_nodelist):
|
||||
for parse_fn in (
|
||||
_try_parse_as_default_fill,
|
||||
|
@ -340,7 +344,7 @@ def _block_has_content(nodelist: NodeList) -> bool:
|
|||
def render_component_template_with_slots(
|
||||
template: Template,
|
||||
context: Context,
|
||||
fill_content: Union[DefaultFillContent, Iterable[NamedFillContent]],
|
||||
fill_content: Dict[str, FillContent],
|
||||
registered_name: Optional[str],
|
||||
) -> str:
|
||||
"""
|
||||
|
@ -363,16 +367,16 @@ def render_component_template_with_slots(
|
|||
|
||||
def _prepare_component_template_filled_slot_context(
|
||||
template: Template,
|
||||
fill_content: Union[DefaultFillContent, Iterable[NamedFillContent]],
|
||||
fill_content: Dict[str, FillContent],
|
||||
slots_context: Optional[FilledSlotsContext],
|
||||
registered_name: Optional[str],
|
||||
) -> FilledSlotsContext:
|
||||
if isinstance(fill_content, NodeList):
|
||||
default_fill_content = (fill_content, None)
|
||||
named_fills_content = {}
|
||||
if DEFAULT_SLOT_KEY in fill_content:
|
||||
named_fills_content = fill_content.copy()
|
||||
default_fill_content = named_fills_content.pop(DEFAULT_SLOT_KEY)
|
||||
else:
|
||||
named_fills_content = fill_content
|
||||
default_fill_content = None
|
||||
named_fills_content = {name: (nodelist, alias) for name, nodelist, alias in list(fill_content)}
|
||||
|
||||
# If value is `None`, then slot is unfilled.
|
||||
slot_name2fill_content: Dict[SlotName, Optional[FillContent]] = {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue