55from hashlib import md5
66from pathlib import Path
77from threading import Lock
8+ from typing import TYPE_CHECKING
89
910from cachetools import LRUCache
1011from django .apps import apps
1112from django .conf import settings
1213from django .template .backends .django import Template as DjangoTemplate
14+ from django .template .base import Node
15+ from django .template .base import NodeList
16+ from django .template .base import TextNode
1317from django .template .context import Context
1418from django .template .engine import Engine
1519from django .template .exceptions import TemplateDoesNotExist
1923from django_bird .params import Params
2024from django_bird .params import Value
2125from django_bird .staticfiles import Asset
26+ from django_bird .templatetags .tags .slot import DEFAULT_SLOT
27+ from django_bird .templatetags .tags .slot import SlotNode
2228
2329from .conf import app_settings
2430from .staticfiles import AssetType
2531from .templates import get_template_names
2632
33+ if TYPE_CHECKING :
34+ from django_bird .templatetags .tags .bird import BirdNode
35+
2736
2837@dataclass (frozen = True , slots = True )
2938class Component :
@@ -37,9 +46,9 @@ def get_asset(self, asset_filename: str) -> Asset | None:
3746 return asset
3847 return None
3948
40- def get_bound_component (self , attrs : list [ Param ] ):
41- params = Params .with_attrs (attrs )
42- return BoundComponent (component = self , params = params )
49+ def get_bound_component (self , node : BirdNode ):
50+ params = Params .with_attrs (node . attrs )
51+ return BoundComponent (component = self , params = params , nodelist = node . nodelist )
4352
4453 @property
4554 def data_attribute_name (self ):
@@ -108,6 +117,7 @@ def next(self, component: Component) -> int:
108117class BoundComponent :
109118 component : Component
110119 params : Params
120+ nodelist : NodeList | None
111121 _sequence : SequenceGenerator = field (default_factory = SequenceGenerator )
112122
113123 def render (self , context : Context ):
@@ -123,15 +133,46 @@ def render(self, context: Context):
123133
124134 props = self .params .render_props (self .component .nodelist , context )
125135 attrs = self .params .render_attrs (context )
136+ slots = self .fill_slots (context )
126137
127138 with context .push (
128139 ** {
129140 "attrs" : attrs ,
130141 "props" : props ,
142+ "slot" : slots .get (DEFAULT_SLOT ),
143+ "slots" : slots ,
131144 }
132145 ):
133146 return self .component .template .template .render (context )
134147
148+ def fill_slots (self , context : Context ):
149+ if self .nodelist is None :
150+ return {
151+ DEFAULT_SLOT : None ,
152+ }
153+
154+ slot_nodes = {
155+ node .name : node for node in self .nodelist if isinstance (node , SlotNode )
156+ }
157+ default_nodes = NodeList (
158+ [node for node in self .nodelist if not isinstance (node , SlotNode )]
159+ )
160+
161+ slots : dict [str , Node | NodeList ] = {
162+ DEFAULT_SLOT : default_nodes ,
163+ ** slot_nodes ,
164+ }
165+
166+ if context .get ("slots" ):
167+ for name , content in context ["slots" ].items ():
168+ if name not in slots or not slots .get (name ):
169+ slots [name ] = TextNode (str (content ))
170+
171+ if not slots [DEFAULT_SLOT ] and "slot" in context :
172+ slots [DEFAULT_SLOT ] = TextNode (context ["slot" ])
173+
174+ return {name : node .render (context ) for name , node in slots .items () if node }
175+
135176 @property
136177 def id (self ):
137178 return str (self ._sequence .next (self .component ))
0 commit comments