diff --git a/nicegui/classes.py b/nicegui/classes.py index 74f90c1e1..062684b16 100644 --- a/nicegui/classes.py +++ b/nicegui/classes.py @@ -15,8 +15,9 @@ def __init__(self, *args, element: T, **kwargs) -> None: def __call__(self, add: Optional[str] = None, *, remove: Optional[str] = None, + toggle: Optional[str] = None, replace: Optional[str] = None) -> T: - """Apply, remove, or replace HTML classes. + """Apply, remove, toggle, or replace HTML classes. This allows modifying the look of the element or its layout using `Tailwind `_ or `Quasar `_ classes. @@ -24,9 +25,10 @@ def __call__(self, :param add: whitespace-delimited string of classes :param remove: whitespace-delimited string of classes to remove from the element + :param toggle: whitespace-delimited string of classes to toggle :param replace: whitespace-delimited string of classes to use instead of existing ones """ - new_classes = self.update_list(self, add, remove, replace) + new_classes = self.update_list(self, add, remove, toggle, replace) if self != new_classes: self[:] = new_classes self.element.update() @@ -36,10 +38,18 @@ def __call__(self, def update_list(classes: List[str], add: Optional[str] = None, remove: Optional[str] = None, + toggle: Optional[str] = None, replace: Optional[str] = None) -> List[str]: """Update a list of classes.""" class_list = classes if replace is None else [] class_list = [c for c in class_list if c not in (remove or '').split()] class_list += (add or '').split() class_list += (replace or '').split() - return list(dict.fromkeys(class_list)) # NOTE: remove duplicates while preserving order + class_list = list(dict.fromkeys(class_list)) # NOTE: remove duplicates while preserving order + if toggle is not None: + for class_ in toggle.split(): + if class_ in class_list: + class_list.remove(class_) + else: + class_list.append(class_) + return class_list diff --git a/nicegui/element.py b/nicegui/element.py index 29af87090..ffa8e13b4 100644 --- a/nicegui/element.py +++ b/nicegui/element.py @@ -222,8 +222,9 @@ def classes(self) -> Classes[Self]: def default_classes(cls, add: Optional[str] = None, *, remove: Optional[str] = None, + toggle: Optional[str] = None, replace: Optional[str] = None) -> type[Self]: - """Apply, remove, or replace default HTML classes. + """Apply, remove, toggle, or replace default HTML classes. This allows modifying the look of the element or its layout using `Tailwind `_ or `Quasar `_ classes. @@ -233,9 +234,10 @@ def default_classes(cls, :param add: whitespace-delimited string of classes :param remove: whitespace-delimited string of classes to remove from the element + :param toggle: whitespace-delimited string of classes to toggle :param replace: whitespace-delimited string of classes to use instead of existing ones """ - cls._default_classes = Classes.update_list(cls._default_classes, add, remove, replace) + cls._default_classes = Classes.update_list(cls._default_classes, add, remove, toggle, replace) return cls @property diff --git a/nicegui/elements/query.py b/nicegui/elements/query.py index 67d4a78b8..7c603afd5 100644 --- a/nicegui/elements/query.py +++ b/nicegui/elements/query.py @@ -37,9 +37,13 @@ def __init__(self, selector: str) -> None: else: self.element = QueryElement(selector) - def classes(self, add: Optional[str] = None, *, remove: Optional[str] = None, replace: Optional[str] = None) \ - -> Self: - """Apply, remove, or replace HTML classes. + def classes(self, + add: Optional[str] = None, *, + remove: Optional[str] = None, + toggle: Optional[str] = None, + replace: Optional[str] = None, + ) -> Self: + """Apply, remove, toggle, or replace HTML classes. This allows modifying the look of the element or its layout using `Tailwind `_ or `Quasar `_ classes. @@ -47,9 +51,10 @@ def classes(self, add: Optional[str] = None, *, remove: Optional[str] = None, re :param add: whitespace-delimited string of classes :param remove: whitespace-delimited string of classes to remove from the element + :param toggle: whitespace-delimited string of classes to toggle :param replace: whitespace-delimited string of classes to use instead of existing ones """ - classes = Classes.update_list(self.element.props['classes'], add, remove, replace) + classes = Classes.update_list(self.element.props['classes'], add, remove, toggle, replace) new_classes = [c for c in classes if c not in self.element.props['classes']] old_classes = [c for c in self.element.props['classes'] if c not in classes] if new_classes: diff --git a/tests/test_element.py b/tests/test_element.py index 66cce207e..e7592e0af 100644 --- a/tests/test_element.py +++ b/tests/test_element.py @@ -35,6 +35,12 @@ def assert_classes(classes: str) -> None: label.classes(replace='four') assert_classes('four') + label.classes(toggle='bg-red-500') + assert_classes('four bg-red-500') + + label.classes(toggle='bg-red-500') + assert_classes('four') + @pytest.mark.parametrize('value,expected', [ (None, {}),