puppygirl-py/puppygirl/elements/host.py

104 lines
2.4 KiB
Python

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