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, {}),