Coverage for phml\utils\misc\classes.py: 21%
48 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-30 09:38 -0600
« prev ^ index » next coverage.py v6.5.0, created at 2022-11-30 09:38 -0600
1"""utils.misc
3A collection of utilities that don't fit in with finding, selecting, testing,
4transforming, traveling, or validating nodes.
5"""
7from re import search, split, sub
8from typing import Optional
10from phml.nodes import Element
12__all__ = ["classnames", "ClassList"]
15def classnames(
16 node: Optional[Element] = None, *conditionals: str | int | list | dict[str, bool]
17) -> str:
18 """Concat a bunch of class names. Can take a str as a class,
19 int which is cast to a str to be a class, a dict of conditional classes,
20 and a list of all the previous conditions including itself.
22 Examples:
23 * `classnames(node, 'flex')` yields `'flex'`
24 * `classnames(node, 13)` yields `'13'`
25 * `classnames(node, {'shadow': True, 'border': 0})` yields `'shadow'`
26 * `classnames(node, 'a', 13, {'b': True}, ['c', {'d': False}])` yields `'a b c'`
28 Args:
29 node (Element | None): Node to apply the classes too. If no node is given
30 then the function returns a string.
32 Returns:
33 str: The concat string of classes after processing.
34 """
36 classes = []
37 for condition in conditionals:
38 if isinstance(condition, str):
39 classes.extend(split(r" ", sub(r" +", "", condition.strip())))
40 elif isinstance(condition, int):
41 classes.append(str(condition))
42 elif isinstance(condition, dict):
43 for key, value in condition.items():
44 if value:
45 classes.extend(split(r" ", sub(r" +", "", key.strip())))
46 elif isinstance(condition, list):
47 classes.extend(classnames(*condition).split(" "))
48 else:
49 raise TypeError(f"Unkown conditional statement: {condition}")
51 if node is None:
52 return " ".join(classes)
53 else:
54 node.properties["class"] = node.properties["class"] or "" + f" {' '.join(classes)}"
57class ClassList:
58 """Utility class to manipulate the class list on a node.
60 Based on the hast-util-class-list:
61 https://github.com/brechtcs/hast-util-class-list
62 """
64 def __init__(self, node: Element):
65 self.node = node
67 def contains(self, klass: str):
68 """Check if `class` contains a certain class."""
69 from phml.utils import has_property
71 if has_property(self.node, "class"):
72 return search(klass, self.node.properties["class"]) is not None
73 return False
75 def toggle(self, *klasses: str):
76 """Toggle a class in `class`."""
78 for klass in klasses:
79 if search(f"\b{klass}\b", self.node.properties["class"]) is not None:
80 sub(f"\b{klass}\b", "", self.node.properties["class"])
81 sub(r" +", " ", self.node.properties["class"])
82 else:
83 self.node.properties["class"] = self.node.properties["class"].strip() + f" {klass}"
85 def add(self, *klasses: str):
86 """Add one or more classes to `class`."""
88 for klass in klasses:
89 if search(f"\b{klass}\b", self.node.properties["class"]) is None:
90 self.node.properties["class"] = self.node.properties["class"].strip() + f" {klass}"
92 def replace(self, old_klass: str, new_klass: str):
93 """Replace a certain class in `class` with
94 another class.
95 """
97 if search(f"\b{old_klass}\b", self.node.properties["class"]) is not None:
98 sub(f"\b{old_klass}\b", f"\b{new_klass}\b", self.node.properties["class"])
99 sub(r" +", " ", self.node.properties["class"])
101 def remove(self, *klasses: str):
102 """Remove one or more classes from `class`."""
104 for klass in klasses:
105 if search(f"\b{klass}\b", self.node.properties["class"]) is not None:
106 sub(f"\b{klass}\b", "", self.node.properties["class"])
107 sub(r" +", " ", self.node.properties["class"])