from dataclasses import dataclass import re from typing import TYPE_CHECKING from .constants import TemplateAttr from .shadow_root import ShadowRootMode, SingleNode from ..renderer import Query, RenderState, Renderable from .shadow_root import Node if TYPE_CHECKING: from .. import Puppygirl from ..puppytype import RenderableElement @dataclass class PuppygirlHost(Renderable): puppygirl: "Puppygirl" @property def name(self): return "pg-host" def query(self) -> Query: return Query(self.name, attrs={ "template": True }, recursive=True) def render(self, node: "Node", state: "RenderState") -> "RenderableElement": node = node.clone() template = state.templates.get(node.get_attr(TemplateAttr)).clone() for tag in self.query().query(template): result = self.render(SingleNode(tag), state) Node.from_tag(tag).replace(result) shadow_root = node.attach_shadow(ShadowRootMode.Open) shadow_root.append_child(template.clone()) return node def get_exported_name(part: str, mapping: str) -> str | None: mapping = mapping.strip() map_len = len(mapping) if map_len == 0: return None index = mapping.index(part) end = index + len(part) if end >= map_len or mapping[end] == ":": return part else: try: map_end = mapping.index(",", end) return mapping[end + 1:map_end] except: return mapping[end + 1:] @dataclass class PuppygirlPart(Renderable): puppygirl: "Puppygirl" @property def name(self): return "pg-part" def query(self) -> Query: return Query(self.name, attrs={ "for": True }) def replace_part(self, node: "Node", part_name: str): template = node.parent.find("template", recursive=False) if template is None: return node parts = template(part=part_name, recusive=False) exports = template(exportparts=re.compile(part_name), recursive=False) for export in exports: mapping = get_exported_name(part_name, export["exportparts"]) if mapping is None: continue self.replace_part(SingleNode(export), part_name) attrs = { attr: value for attr, value in node.value.attrs.items() if attr != "for" } for attr, value in attrs.items(): for part in parts: part[attr] = value def render(self, node: "Node", state: "RenderState") -> "RenderableElement": part_name = node["for"] self.replace_part(node, part_name) return None