pyx/pyx.py
2025-08-22 09:26:18 -04:00

99 lines
3.1 KiB
Python

from typing import List, Tuple, Union, Optional
from css import Chainer, Writer
from dataclasses import dataclass
Attributes = dict[str, Union[str, bool, None]]
Stringy = Union[str, Tuple[str, ...]]
Renderable = Union[str, 'Branch', 'Leaf']
TAGGED = Union[Tuple[Chainer], Chainer]
def MakeAttrs(css:TAGGED, id:Optional[str], attributes: Attributes) -> str:
if not attributes:
attributes = {}
if css:
attributes['class'] = " ".join(css) if isinstance(css, tuple) else css
if id:
attributes['id'] = id
attrList: List[str] = []
for key, value in attributes.items():
if value:
attrList.append(f'{key}="{value}"')
return " " + " ".join(attrList)
class Leaf():
name:str = ""
def __init__(self, name:Optional[str] = None):
if name:
self.name = name
self.attrs = None
def props(self, css:Optional[TAGGED], id:Optional[str], attributes: Attributes):
clone = self.__class__(self.name)
clone.attrs = MakeAttrs(css, id, attributes)
return clone
def __call__(self, css:Optional[TAGGED] = None, id:Optional[str] = None):
return self.props(css, id, {})
@classmethod
def template(cls, name:str, attrs:Optional[str] = None):
return f'<{name}{attrs or ""}/>'
def __repr__(self) -> str:
return __class__.template(self.name, self.attrs)
class Branch(Leaf):
def __init__(self, name:str):
self.name = name
self.attrs = None
@classmethod
def template(cls, name:str, attrs:Optional[str] = None, children:Optional[str] = None):
return f'<{name}{attrs or ""}>\n{children}\n</{name}>'
def __repr__(self) -> str:
return self.__class__.template(self.name, self.attrs)
def __getitem__(self, key:Union[Renderable, Tuple[Renderable, ...]]) -> Renderable:
children = f'\n'.join(str(k) for k in key) if isinstance(key, tuple) else str(key)
return self.__class__.template(self.name, self.attrs, children)
class IMGTag(Leaf):
def __call__(self, css:Optional[TAGGED] = None, id:Optional[str] = None, src:Optional[str] = None):
return self.props(css, id, {"src":src})
class ATag(Branch):
def __call__(self, css:Optional[TAGGED] = None, id:Optional[str] = None, href:Optional[str] = None, target:Optional[str] = None):
return self.props(css, id, {"href":href, "target":target})
class HTMLTag(Branch):
@classmethod
def template(cls, name:str, attrs:Optional[str] = None, children:Optional[str] = None):
return f'<!DOCTYPE html><{name}{attrs or ""}>\n{children}\n</{name}>'
IMG = IMGTag("img")
A = ATag("a")
BR = Leaf("br")
HR = Leaf("hr")
DIV = Branch("div")
P = Branch("p")
SPAN = Branch("span")
EM = Branch("EM")
H1 = Branch("h1")
H2 = Branch("h2")
H3 = Branch("h3")
H4 = Branch("h4")
SECTION = Branch("section")
MAIN = Branch("main")
HTML = HTMLTag("html")
HEAD = Branch("head")
BODY = Branch("body")
STYLE = Branch("style")
@dataclass
class Capture:
def __init__(self, html:Renderable):
self.html = html
self.css = Writer.dump()