Source code for svgen.attribute.style
"""
svgen - A module for the 'style' attribute.
"""
# built-in
from typing import NamedTuple, Union
# internal
from svgen.attribute import Attribute
from svgen.color import Colorlike
from svgen.color.resolve import get_color
[docs]
class CssProperty(NamedTuple):
"""A class for interacting with css properties."""
key: str
value: str
def __eq__(self, other: object) -> bool:
"""Determine if two css properties are the same."""
if not isinstance(other, CssProperty):
return NotImplemented
return self.key == other.key and self.value == other.value
[docs]
@staticmethod
def color(color: Colorlike, prop: str = "fill", **kwargs) -> "CssProperty":
"""Get a CSS property for a color."""
return CssProperty(prop, str(get_color(color, **kwargs)))
[docs]
@staticmethod
def decode(value: str) -> list["CssProperty"]:
"""Create css properties from a string."""
result: list[CssProperty] = []
value = value.strip()
props = [x.strip() for x in value.split(";")]
for prop in props:
if prop:
tokens = prop.split(":")
assert len(tokens) == 2
key = tokens[0].strip()
val = tokens[1].strip()
result.append(CssProperty(key, val))
return result
@property
def encoded(self) -> str:
"""Convert this css property to a string."""
return f"{self.key}: {self.value}"
[docs]
@staticmethod
def encode(props: list["CssProperty"]) -> str:
"""Encode a list of css properties to a string."""
return "; ".join(x.encoded for x in props)
[docs]
@staticmethod
def from_dict(
data: dict[str, Union[str, int, float]],
) -> list["CssProperty"]:
"""Get a list of attributes from dictionary data."""
return [CssProperty(key, str(value)) for key, value in data.items()]
PossibleProperties = Union[
dict[str, Union[str, int, float]], list[CssProperty], CssProperty
]
[docs]
def properties(data: PossibleProperties = None) -> list[CssProperty]:
"""
Get properties from either an existing list of properties, or dictionary
data.
"""
if data is None:
data = []
if isinstance(data, dict):
data = CssProperty.from_dict(data)
elif not isinstance(data, list):
data = [data]
return data
[docs]
class Style(Attribute):
"""An interface for style attributes."""
def __init__(self, props: PossibleProperties = None) -> None:
"""Construct a new style attribute."""
self.properties: list[CssProperty] = properties(props)
[docs]
def add(self, props: PossibleProperties) -> None:
"""Add CSS properties to this style instance."""
self.properties.extend(properties(props))
def __eq__(self, other: object) -> bool:
"""Determine if two style attributes are the same."""
if not isinstance(other, Style):
return NotImplemented
result = self.key == other.key
result = result and len(self.properties) == len(other.properties)
if result:
props = sorted(self.properties)
other_props = sorted(other.properties)
for idx, prop in enumerate(props):
if not result:
break
result = result and prop == other_props[idx]
return result
[docs]
def add_color(
self,
color: Colorlike,
prop: str = "fill",
**kwargs,
) -> "Style":
"""Set a style property to a color."""
self.properties.append(CssProperty.color(color, prop=prop, **kwargs))
return self
@property
def value(self) -> str:
"""Get the string value for this attribute."""
return CssProperty.encode(self.properties)
[docs]
@classmethod
def decode(cls: type["Style"], key: str, value: str) -> "Style":
"""Create this attribute from a string."""
assert key == "style"
return cls(CssProperty.decode(value))