99 lines
3.1 KiB
Python
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() |