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