Source code for wildewidgets.widgets.buttons

from __future__ import annotations

from copy import copy
from typing import Any

from .base import Block


[docs]class Button(Block): """ Render a ``<button>`` with Bootstrap styling. Example: .. code-block:: python from wildewidgets import Button button = Button(text="My Button") When rendered in the template with the ``wildewdigets`` template tag, this will produce: .. code-block:: html <button type="button" class="button btn btn-secondary">My Button</button> Keyword Args: text: The text to use for the button color: The Boostrap color class to use for the button close: The Boostrap close icon will be used for the button size: The Bootstrap button size - None, 'sm', 'lg' disabled: If ``True``, the button will be disabled """ block: str = "button" tag: str = "button" #: The Boostrap color class to use for the button color: str = "secondary" #: The text to use for the button text: str = "Button" #: If ``True``, ignore :py:attr:`text` and make this into a Bootstrap #: "close" button with a close icon. close: bool = False #: If ``True``, ignore :py:attr:`text` and make this into a Bootstrap #: "close" button with a close icon. size: str | None = None disabled: bool = False def __init__( self, text: str | None = None, color: str | None = None, close: bool | None = None, size: str | None = None, disabled: bool | None = None, **kwargs: Any, ) -> None: self.color = color if color is not None else self.color self.text = text if text is not None else self.text self.close = close if close is not None else self.close self.size = size if size is not None else self.size self.disabled = disabled if disabled is not None else self.disabled if self.disabled: if "attributes" in kwargs: kwargs["attributes"]["disabled"] = True else: kwargs["attributes"] = {"disabled": True} if self.close: self.text = "" super().__init__(self.text, **kwargs) self._attributes["type"] = "button" if self.close: self.add_class("btn-close") self._aria_attributes["label"] = "Close" else: self.add_class("btn") self.add_class(f"btn-{self.color}") if self.size: self.add_class(f"btn-{self.size}")
[docs]class ModalButton(Button): """ Render a ``<button>`` with Bootstrap styling which toggles a Bootstrap modal. Example: .. code-block:: python from wildewidgets import ModalButton button = ModalButton(text="My Button", target='#mymodal') When rendered in the template with the ``wildewdigets`` template tag, this will produce: .. code-block:: html <button type="button" class="button button--modal btn btn-secondary" data-toggle="modal" data-target="#mymodal">My Button</button> Keyword Args: target: The CSS target for the Bootstrap modal """ block: str = "button button--modal" #: The CSS target for the Bootstrap modal target: str | None = None def __init__(self, target: str | None = None, **kwargs: Any) -> None: self.target = target or self.target assert self.target is not None, "ModalButton requires a target" # noqa: S101 super().__init__(**kwargs) self._data_attributes["toggle"] = "modal" self._data_attributes["target"] = self.target
[docs]class CollapseButton(Button): """ Render a ``<button>`` with Bootstrap styling which toggles a ``collapse``. Example: ... code-block:: python from wildewidgets import CollapseButton button = CollapseButton(text="My Button", target='#mymodal') When rendered in the template with the ``wildewdigets`` template tag, this will produce: ... code-block:: html <button type="button" class="button button--collapse btn btn-secondary" data-toggle="collapse" data-target="#mymodal" aria-expanded="false" aria-controls="mymodal">My Button</button> Keyword Args: target: The CSS target for the Bootstrap collapse """ block: str = "button button--collapse" #: The CSS target for the Bootstrap collapse target: str | None = None def __init__(self, target: str | None = None, **kwargs: Any) -> None: self.target = target or self.target assert self.target is not None, "CollapseButton requires a target" # noqa: S101 super().__init__(**kwargs) self._data_attributes["toggle"] = "collapse" self._data_attributes["target"] = self.target self._aria_attributes["expanded"] = "false" self._aria_attributes["controls"] = self.target.lstrip("#")
[docs]class LinkButton(Button): """ Render an ``<a>`` with Bootstrap button styling which toggles a Bootstrap modal. Example: ... code-block:: python from wildewidgets import LinkButton button = LinkButton(text="My Button", url='https://myexample.com') When rendered in the template with the ``wildewdigets`` template tag, this will produce: ... code-block:: html <a href="https://myexample.com" class="button button--link btn btn-secondary"> My Button </a> Keyword Args: url: The URL for the ``href`` attribute of the <a> record, defaults to no URL. """ block: str = "button button--link" tag: str = "a" url: str | None = None def __init__(self, url: str | None = None, **kwargs: Any) -> None: self.url = url or self.url assert self.url is not None, "LinkButton requires a url" # noqa: S101 super().__init__(**kwargs) del self._attributes["type"] if url: self._attributes["href"] = self.url
[docs]class InputButton(Button): """ Render an ``<input type="submit">`` with Bootstrap button styling. Example:: .. code-block:: python from wildewidgets import InputButton button = InputButton(text="Save") When rendered in the template with the ``wildewdigets`` template tag, this will produce: .. code-block:: html <input type="submit" class="button button--submit btn btn-secondary" value="Save"> """ template_name: str = "wildewidgets/block--simple.html" block: str = "button--submit" tag: str = "input" confirm_text: str | None = None def __init__(self, confirm_text: str | None = None, **kwargs: Any) -> None: self.confirm_text = confirm_text or self.confirm_text super().__init__(**kwargs) self._attributes["type"] = "submit" self._attributes["value"] = self.text if self.confirm_text: self._attributes["onclick"] = f"return confirm('{self.confirm_text}');" self.text = ""
[docs]class FormButton(Block): """ Render a ``<form>`` with optional hidden inputs and a submit button. Example:: .. code-block:: python from wildewidgets import FormButton button = FormButton( text="Save", action='/my/form/action', data={'field1': 'value1'} ) When rendered in the template with the ``wildewdigets`` template tag, this will produce: .. code-block:: html <form action="/my/form/action" method="post" class="button-form"> <input type="hidden" name="csrfmiddlewaretoken" value="__THE_CSRF_TOKEN__"> <input type="hidden" name="field1" value="value1"> <input type="submit" class="button button--submit btn btn-secondary" value="Save"> </form> All the constructor parameters can be set in a subclass of this class as class attributes. Parameters to the constructor override any defined class attributes. Keyword Args: text: The text to use for the button, defaults to 'Button' color: The Boostrap color class to use for the button, defaults to 'secondary' button_css_class: a string of classes to apply to the button, defaults to no classes. button_attributes: Set any additional attributes for the button as key, value pairs, defaults to no additional attributes. button_data_attributes: Set ``data-`` attributes for the button, defaults to no data attributes """ template_name: str = "wildewidgets/button--form.html" tag: str = "form" block: str = "button-form" action: str | None = None method: str = "post" text: str | None = None color: str = "secondary" button_css_class: str | None = None button_attributes: dict[str, str] = {} # noqa: RUF012 button_data_attributes: dict[str, str] = {} # noqa: RUF012 confirm_text: str | None = None data: dict[str, Any] = {} # noqa: RUF012 def __init__(self, **kwargs: Any) -> None: action = kwargs.pop("action", self.action) method = kwargs.pop("method", self.method) self.close = kwargs.pop("close", False) self.data = kwargs.pop("data", copy(self.data)) self.button = self.__build_button(kwargs) super().__init__(**kwargs) self._attributes["method"] = method self._attributes["action"] = action def __build_button(self, kwargs: dict[str, Any]) -> InputButton: """ Build the submit button for the form. Creates an InputButton with appropriate styling and attributes based on the configuration provided to the FormButton. Args: kwargs: Dictionary of keyword arguments to process Returns: InputButton: The configured submit button for the form """ button_kwargs = { "text": kwargs.pop("text", self.text), "color": kwargs.pop("color", self.color), "css_class": kwargs.pop("button_css_class", self.button_css_class), "attributes": kwargs.pop("button_attributes", copy(self.button_attributes)), "data_attributes": kwargs.pop( "button_data_attributes", copy(self.button_data_attributes) ), } if self.close: button_kwargs["close"] = True button_kwargs["aria_attributes"] = {"label": "Close"} button_kwargs["confirm_text"] = kwargs.pop("confirm_text", self.confirm_text) return InputButton(**button_kwargs)
[docs] def get_context_data(self, *args, **kwargs) -> dict[str, Any]: context = super().get_context_data(*args, **kwargs) context["button"] = self.button context["data"] = self.data return context
[docs]class ButtonRow(Block): """ Renders a row of buttons with right alignment. This class creates a horizontal row of buttons with right-aligned (end) justification using Bootstrap's flexbox utilities. It's useful for placing action buttons at the bottom of forms or dialogs. Note: This class is planned for deprecation in favor of :py:class:`HorizontalLayoutBlock`. Example: .. code-block:: python from wildewidgets import Button, ButtonRow save_button = Button(text="Save", color="primary") cancel_button = Button(text="Cancel") button_row = ButtonRow(save_button, cancel_button) When rendered, this produces: .. code-block:: html <div class="d-flex justify-content-end"> <button type="button" class="button btn btn-primary">Save</button> <button type="button" class="button btn btn-secondary">Cancel</button> </div> Args: *blocks: Button instances or other blocks to include in the row Keyword Args: **kwargs: Additional keyword arguments passed to the parent Block class """ def __init__(self, *blocks: Any, **kwargs: Any) -> None: """ Initialize a ButtonRow with buttons and styling. Adds flexbox utility classes to create a right-aligned row of buttons. Args: *blocks: Button instances or other blocks to include in the row **kwargs: Additional keyword arguments including css_class """ css_class = kwargs.get("css_class", "") css_class += " d-flex justify-content-end" kwargs["css_class"] = css_class super().__init__(*blocks, **kwargs)