diff --git a/pss.py b/pss.py deleted file mode 100644 index 4a115c0..0000000 --- a/pss.py +++ /dev/null @@ -1,90 +0,0 @@ -from typing import TypedDict, Union, Literal, Tuple, Optional, cast - -# --- Units and Sizes --- -MediaQueryCheck = Literal["max-width", "min-width", "max-height", "min-height"] -Unit = Literal["px", "em", "rem", "%", "vh", "vw"] -Size = Tuple[float, Unit] # e.g., (16.0, "px") -MediaQuery = Tuple[MediaQueryCheck, float, Unit] - -# --- Colors --- -RGB = Tuple[float, float, float] # 0-255 or 0.0-1.0 -RGBA = Tuple[float, float, float, float] # includes alpha -Color = Union[RGB, RGBA] - -# --- Positions --- -PositionKeyword = Literal["static", "relative", "absolute", "fixed", "sticky"] - -# --- Display --- -Display = Literal["none", "block", "inline", "inline-block", "flex", "grid"] - -# --- Alignment --- -TextAlign = Literal["left", "right", "center", "justify", "start", "end"] - -# --- Overflow --- -Overflow = Literal["visible", "hidden", "scroll", "auto", "clip"] - -# --- Border Style --- -BorderStyle = Literal["none", "solid", "dashed", "dotted", "double", "groove", "ridge", "inset", "outset"] - -# --- Length / Size or keyword --- -AutoSize = Union[Size, Literal["auto"]] - - - - - -class CSS(TypedDict, total=False): - color: Color - backgroundColor: Color - fontSize: Size - margin: Union[AutoSize, Tuple[AutoSize, AutoSize], Tuple[AutoSize, AutoSize, AutoSize, AutoSize]] - padding: Union[AutoSize, Tuple[AutoSize, AutoSize], Tuple[AutoSize, AutoSize, AutoSize, AutoSize]] - width: AutoSize - height: AutoSize - display: Display - position: PositionKeyword - top: AutoSize - left: AutoSize - right: AutoSize - bottom: AutoSize - textAlign: TextAlign - overflow: Overflow - borderColor: Color - borderWidth: Size - borderStyle: BorderStyle - opacity: float # 0.0 to 1.0 - zIndex: int - lineHeight: Union[float, Size] - - -# --- Media Queries --- -ChildMQ = Tuple[Size, MediaQueryCheck] - -ScopedCSS = Tuple[MediaQuery, CSS] - - -def Styles( - color: Optional[Color] = None, - backgroundColor: Optional[Color] = None, - fontSize: Optional[Size] = None, - display: Optional[Display] = None, -) -> CSS: - return cast(CSS, {k: v for k, v in locals().items() if v is not None}) - -def ScopedStyles( - mediaQuery: MediaQuery, - styles: CSS -) -> ScopedCSS: - return (mediaQuery, styles) - -test = ScopedStyles( - ("max-width", 1024, "px"), - { - "fontSize":(16, "px"), - "color":(255, 0, 0), - "backgroundColor":(0, 255, 0), - "display":"flex", - } -) - -print(test) \ No newline at end of file diff --git a/py_jsx.py b/py_jsx.py new file mode 100644 index 0000000..a09246d --- /dev/null +++ b/py_jsx.py @@ -0,0 +1,59 @@ +from typing import List, Protocol, Tuple + +class StrArgsToStr(Protocol): + def __call__(self, *children: str) -> str: + ... + +ClassArg = str|List[str]|Tuple[str, ...]|None + +def MakeAttrs(classes:ClassArg, id:str|None, attributes: dict[str, str | None]) -> str: + + if classes: + attributes['class'] = " ".join(classes) if isinstance(classes, (list, tuple)) else classes + + if id: + attributes['id'] = id + + attrList: List[str] = [""] + for key, value in attributes.items(): + if value: + attrList.append(f'{key}="{value}"') + return " ".join(attrList) + +def ElWrapped(name: str, classes:ClassArg = None, id:str|None = None, attrs: dict[str, str | None] = {}, children:ClassArg = ()) -> StrArgsToStr: + def Wrapped(*children:str): + return f'<{name}{MakeAttrs(classes, id, attrs)}>{" ".join(children)}' + return Wrapped + +def ElInline(name: str, classes:ClassArg = None, id:str|None = None, attrs: dict[str, str | None] = {}) -> str: + return f'<{name} {MakeAttrs(classes, id, attrs)}/>' + +def SimpleWrapped(name:str): + def proxy(classes:ClassArg = None, id:str|None = None): + return ElWrapped(name, classes, id) + return proxy + +def SimpleInline(name:str): + def proxy(classes:ClassArg = None, id:str|None = None): + return ElInline(name, classes, id) + return proxy + +def A(classes:ClassArg = None, id:str|None = None, href:str|None = None, target:str|None = None) -> StrArgsToStr: + return ElWrapped("a", classes, id, {"href":href, "target":id}) + +def IMG(classes:ClassArg = None, id:str|None = None, src:str|None = None): + return ElInline("img", classes, id, {"src":src}) + +DIV = SimpleWrapped("div") +P = SimpleWrapped("P") +BR = SimpleInline("br") +HR = SimpleInline("hr") +SPAN = SimpleWrapped("span") + +print( + + DIV(classes="outer") + ( + A(classes="CTA Orange", href="www.link")("click") + ) +) \ No newline at end of file diff --git a/pyle_sheets.py b/pyle_sheets.py new file mode 100644 index 0000000..bc4f387 --- /dev/null +++ b/pyle_sheets.py @@ -0,0 +1,138 @@ +from typing import TypedDict, Union, Literal, Tuple, Optional, cast +import re + +# --- Units and Sizes --- +MediaQueryCheck = Literal["max-width", "min-width", "max-height", "min-height"] +Unit = Literal["px", "em", "rem", "%", "vh", "vw"] +Size = Tuple[float, Unit] +MediaQuery = Tuple[MediaQueryCheck, float, Unit] + +# --- Colors --- +RGB = Tuple[float, float, float] +RGBA = Tuple[float, float, float, float] +Color = Union[RGB, RGBA] + +CSSValue = Union[Size, Color, str] + +# --- Positions --- +PositionKeyword = Literal["static", "relative", "absolute", "fixed", "sticky"] + +# --- Display --- +Display = Literal["none", "block", "inline", "inline-block", "flex", "grid"] + +# --- Alignment --- +TextAlign = Literal["left", "right", "center", "justify", "start", "end"] + +# --- Overflow --- +Overflow = Literal["visible", "hidden", "scroll", "auto", "clip"] + +# --- Border Style --- +BorderStyle = Literal["none", "solid", "dashed", "dotted", "double", "groove", "ridge", "inset", "outset"] + +# --- Length / Size or keyword --- +AutoSize = Union[Size, Literal["auto"]] + +class CSS(TypedDict, total=False): + color: Color + backgroundColor: Color + fontSize: Size + margin: Union[AutoSize, Tuple[AutoSize, AutoSize], Tuple[AutoSize, AutoSize, AutoSize, AutoSize]] + padding: Union[AutoSize, Tuple[AutoSize, AutoSize], Tuple[AutoSize, AutoSize, AutoSize, AutoSize]] + width: AutoSize + height: AutoSize + display: Display + position: PositionKeyword + top: AutoSize + left: AutoSize + right: AutoSize + bottom: AutoSize + textAlign: TextAlign + overflow: Overflow + borderColor: Color + borderWidth: Size + borderStyle: BorderStyle + opacity: float # 0.0 to 1.0 + zIndex: int + lineHeight: Union[float, Size] + +def Styles( + color: Optional[Color] = None, + backgroundColor: Optional[Color] = None, + fontSize: Optional[Size] = None, + margin: Optional[Union[AutoSize, Tuple[AutoSize, AutoSize], Tuple[AutoSize, AutoSize, AutoSize, AutoSize]]] = None, + padding: Optional[Union[AutoSize, Tuple[AutoSize, AutoSize], Tuple[AutoSize, AutoSize, AutoSize, AutoSize]]] = None, + width: Optional[AutoSize] = None, + height: Optional[AutoSize] = None, + display: Optional[Display] = None, + position: Optional[PositionKeyword] = None, + top: Optional[AutoSize] = None, + left: Optional[AutoSize] = None, + right: Optional[AutoSize] = None, + bottom: Optional[AutoSize] = None, + textAlign: Optional[TextAlign] = None, + overflow: Optional[Overflow] = None, + borderColor: Optional[Color] = None, + borderWidth: Optional[Size] = None, + borderStyle: Optional[BorderStyle] = None, + opacity: Optional[float] = None, + zIndex: Optional[int] = None, + lineHeight: Optional[Union[float, Size]] = None, +) -> CSS: + """ + Function alternative to create a block of CSS styles. + """ + return cast(CSS, {k: v for k, v in locals().items() if v is not None}) + +def Sheet(mq:MediaQuery, *css:CSS): + """ + Serialize a CSS sheet with a media query and multiple CSS dictionaries. + """ + lines:list[str] = [] + lines.append(f"@media ({mq[0]}: {mq[1]}{mq[2]}) {{") + + def camel_to_kebab(name): + # Insert hyphen before capital letters and convert to lowercase + s1 = re.sub(r'(.)([A-Z][a-z]+)', r'\1-\2', name) + kebab = re.sub(r'([a-z0-9])([A-Z])', r'\1-\2', s1).lower() + return kebab + + def concat(joiner:str, tuple:Union[RGB, RGBA, Size])->str: + return joiner.join(str(item) for item in tuple) + + + merged:CSS = {} + for d in css: + merged = {**merged, **d} + + + for key, value in merged.items(): + text = value + if isinstance(value, tuple): + size = len(value) + if size < 3: + text = concat("", value) + elif size == 3: + text = "rgb(" + concat(", ", value) + ")" + elif size == 4: + text = "rgba(" + concat(", ", value) + ")" + + lines.append(f" {camel_to_kebab(key)}: {text};") + + lines.append("}") + return "\n".join(lines) + +test = Sheet( + ("max-width", 1024, "px"), + { + "fontSize":(16, "px"), + "color":(255, 0, 0), + "backgroundColor":(0, 255, 0), + "display":"block", + }, + Styles( + color=(0, 0, 255), + backgroundColor=(255, 255, 0), + ) +) + +print(test) \ No newline at end of file