Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow toggling classes #4019

Merged
merged 1 commit into from
Nov 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions nicegui/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,20 @@ 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 <https://tailwindcss.com/>`_ or `Quasar <https://quasar.dev/>`_ classes.

Removing or replacing classes can be helpful if predefined classes are not desired.

: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()
Expand All @@ -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
6 changes: 4 additions & 2 deletions nicegui/element.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://tailwindcss.com/>`_ or `Quasar <https://quasar.dev/>`_ classes.

Expand All @@ -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
Expand Down
13 changes: 9 additions & 4 deletions nicegui/elements/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,24 @@ 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 <https://tailwindcss.com/>`_ or `Quasar <https://quasar.dev/>`_ classes.

Removing or replacing classes can be helpful if predefined classes are not desired.

: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:
Expand Down
6 changes: 6 additions & 0 deletions tests/test_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, {}),
Expand Down