from dataclasses import dataclass from typing import Optional, Tuple, Union, Dict, List class Unit(): unit = "px" def __init__(self, amount:float): self.amount = amount def __repr__(self) -> str: return f'{self.amount}{self.unit}' class PX(Unit): pass class EM(Unit): unit = "em" class REM(Unit): unit = "rem" class Cluster(): map:Dict[str, str] = {} def __init__(self): pass def __repr__(self)->str: print("repr called on cluster") return self.render() def render(self)->str: output:List[str] = [] for key in self.map: value = self.__getattribute__(self.map[key]) if value: output.append(f'{key}:{value};') return "".join(output) @dataclass class Font(Cluster): family: Optional[str] = None kerning: Optional[str] = None size: Optional[Unit] = None map = { "font-family":"family", "font-kerning":"kerning", "font-size":"size" } @dataclass class Space(Cluster): top: Optional[Unit] = None right: Optional[Unit] = None bottom: Optional[Unit] = None left: Optional[Unit] = None @dataclass class XForm(Space): width: Optional[Unit] = None height: Optional[Unit] = None angle: Optional[float] = None class Chainer(): def __init__(self) -> None: self.size = 0 self.dump_id:int = -1 self.log:List[str] = [] pass def __getitem__(self, args:Union[Cluster, Tuple[Cluster, ...]]): print("bracket access", args) styledata = "".join(c.render() for c in args) if isinstance(args, tuple) else args.render() if self.size: self.log.append(f'\n@media(min-width:{self.size}px){{ {styledata} }}') else: self.log.append(styledata) return self def __call__(self, key:int): print("call access", key) self.size = key return self def dump(self, log:List[str], fullName:str, dump_id:int): if self.dump_id != dump_id: log.append(f'.{fullName} {{\n {"".join(self.log)} \n}}') self.dump_id = dump_id self.log = [] self.size = 0 class Kickoff(): def __getitem__(self, args:Union[Cluster, Tuple[Cluster, ...]]): return Chainer().__getitem__(args) def __call__(self, key:int): return Chainer().__call__(key) CSS = Kickoff() ########################## class Writer: dump_id = 0 log:List[str] = [] @classmethod def start(cls): cls.dump_id += 1 cls.log.clear() print("Writer reset, dump_id:", cls.dump_id) @classmethod def capture(cls, fullName:str, chainer:Chainer): chainer.dump(cls.log, fullName, Writer.dump_id) @classmethod def dump(cls) -> str: output = "\n".join(cls.log) cls.start() # Reset after dumping return output class _SHEET(type): def __getattribute__(cls, name:str): if name != "__name__" and name != "__module__": print("class name access", name) value = super().__getattribute__(name) if isinstance(value, Chainer): fullName = f'{cls.__module__}_{cls.__name__}_{name}' Writer.capture(fullName, value) return fullName return super().__getattribute__(name) class SHEET(metaclass=_SHEET): pass