Compare commits

...

13 Commits

Author SHA1 Message Date
Brian Tiemann
67c4da607d Use reverse_lazy instead of reverse 2025-07-14 10:03:36 -04:00
Brian Tiemann
2905c124c3 Use reverse_lazy instead of reverse 2025-07-14 09:57:34 -04:00
Brian Tiemann
0c95ac6b1a Make url a property on MenuItem/PluginMenuItem etc, overridable via a setter
Some checks failed
CI / build (20.x, 3.10) (push) Has been cancelled
CI / build (20.x, 3.11) (push) Has been cancelled
CI / build (20.x, 3.12) (push) Has been cancelled
2025-07-13 20:19:46 -04:00
Brian Tiemann
7338898ccb Back out support for callables but keep alternate prerendered url param 2025-07-08 15:38:12 -04:00
Brian Tiemann
aa4533e331 Merge branch 'main' into nav-menu-callables 2025-07-08 15:32:38 -04:00
Brian Tiemann
e400a7cb29 Merge remote-tracking branch 'origin/nav-menu-callables' into nav-menu-callables 2025-07-07 19:30:03 -04:00
btiemann
600f85ca83 Clarify docstring to differentiate link and url 2025-06-30 14:26:43 -04:00
Brian Tiemann
9d6abcf57b Merge branch 'main' into nav-menu-callables 2025-06-12 16:42:50 -04:00
Brian Tiemann
fbf639fad1 Merge branch 'main' into nav-menu-callables 2025-06-10 08:50:44 -04:00
Brian Tiemann
9a46c8e30d Merge branch 'main' into nav-menu-callables 2025-05-21 20:25:46 -04:00
Brian Tiemann
4ca48843af Fix quote on add button 2025-05-09 18:17:56 -04:00
Brian Tiemann
874d020d57 Merge branch 'main' into nav-menu-callables 2025-05-01 14:52:32 -04:00
Brian Tiemann
d0129811e2 Support menu items that are callables 2025-04-29 19:11:55 -04:00
3 changed files with 58 additions and 4 deletions

View File

@@ -1,6 +1,8 @@
from dataclasses import dataclass
from typing import Sequence, Optional
from django.urls import reverse_lazy
__all__ = (
'get_model_item',
@@ -22,20 +24,46 @@ class MenuItemButton:
link: str
title: str
icon_class: str
_url: Optional[str] = None
permissions: Optional[Sequence[str]] = ()
color: Optional[str] = None
def __post_init__(self):
if self.link:
self._url = reverse_lazy(self.link)
@property
def url(self):
return self._url
@url.setter
def url(self, value):
self._url = value
@dataclass
class MenuItem:
link: str
link_text: str
_url: Optional[str] = None
permissions: Optional[Sequence[str]] = ()
auth_required: Optional[bool] = False
staff_only: Optional[bool] = False
buttons: Optional[Sequence[MenuItemButton]] = ()
def __post_init__(self):
if self.link:
self._url = reverse_lazy(self.link)
@property
def url(self):
return self._url
@url.setter
def url(self, value):
self._url = value
@dataclass
class MenuGroup:

View File

@@ -1,3 +1,4 @@
from django.urls import reverse_lazy
from django.utils.text import slugify
from django.utils.translation import gettext as _
@@ -32,17 +33,23 @@ class PluginMenuItem:
This class represents a navigation menu item. This constitutes primary link and its text, but also allows for
specifying additional link buttons that appear to the right of the item in the van menu.
Links are specified as Django reverse URL strings.
Links are specified as Django reverse URL strings suitable for rendering via {% url item.link %}.
Alternatively, a pre-generated url can be set on the object which will be rendered literally.
Buttons are each specified as a list of PluginMenuButton instances.
"""
permissions = []
buttons = []
_url = None
def __init__(self, link, link_text, auth_required=False, staff_only=False, permissions=None, buttons=None):
def __init__(
self, link, link_text, auth_required=False, staff_only=False, permissions=None, buttons=None
):
self.link = link
self.link_text = link_text
self.auth_required = auth_required
self.staff_only = staff_only
if link:
self._url = reverse_lazy(link)
if permissions is not None:
if type(permissions) not in (list, tuple):
raise TypeError(_("Permissions must be passed as a tuple or list."))
@@ -52,6 +59,14 @@ class PluginMenuItem:
raise TypeError(_("Buttons must be passed as a tuple or list."))
self.buttons = buttons
@property
def url(self):
return self._url
@url.setter
def url(self, value):
self._url = value
class PluginMenuButton:
"""
@@ -60,11 +75,14 @@ class PluginMenuButton:
"""
color = ButtonColorChoices.DEFAULT
permissions = []
_url = None
def __init__(self, link, title, icon_class, color=None, permissions=None):
self.link = link
self.title = title
self.icon_class = icon_class
if link:
self._url = reverse_lazy(link)
if permissions is not None:
if type(permissions) not in (list, tuple):
raise TypeError(_("Permissions must be passed as a tuple or list."))
@@ -73,3 +91,11 @@ class PluginMenuButton:
if color not in ButtonColorChoices.values():
raise ValueError(_("Button color must be a choice within ButtonColorChoices."))
self.color = color
@property
def url(self):
return self._url
@url.setter
def url(self, value):
self._url = value

View File

@@ -41,11 +41,11 @@
</div>
{% for item, buttons in items %}
<div class="dropdown-item d-flex justify-content-between ps-3 py-0">
<a href="{% url item.link %}" class="d-inline-flex flex-fill py-1">{{ item.link_text }}</a>
<a href="{{ item.url }}" class="d-inline-flex flex-fill py-1">{{ item.link_text }}</a>
{% if buttons %}
<div class="btn-group ms-1">
{% for button in buttons %}
<a href="{% url button.link %}" class="btn btn-sm btn-{{ button.color|default:"outline" }} lh-2 px-2" title="{{ button.title }}">
<a href="{{ button.url }}" class="btn btn-sm btn-{{ button.color|default:"outline" }} lh-2 px-2" title="{{ button.title }}">
<i class="{{ button.icon_class }}"></i>
</a>
{% endfor %}