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

Bootstrap 5 Renderer #84

Closed
wants to merge 5 commits into from
Closed
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
14 changes: 12 additions & 2 deletions flask_nav3/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ def get_renderer(app, iid): # noqa: WPS210
:param app: :class:`~flask.Flask` application to look ``iid`` up on
:param iid: Internal renderer id-string to look up
"""
if iid is None and app.extensions["nav"].bootstrap:
iid = "bootstrap"
renderer = app.extensions.get("nav_renderers", {})[iid]

if isinstance(renderer, tuple):
Expand Down Expand Up @@ -99,33 +101,41 @@ class Nav(object):
"""The Flask-Nav extension.

:param app: An optional :class:`~flask.Flask` app to initialize.
:param bootstrap: Use bootstrap 5 rendering. Default: False.
"""

def __init__(self, app=None):
def __init__(self, app=None, bootstrap=False):
"""Construct a Nav instance.

Parameters
----------
app : Flask, optional
The Flask app, by default None
bootstrap: boolean
Use bootstrap 5 rendering. Default: False.
"""
self.bootstrap = bootstrap
self.elems = ElementRegistry()

# per default, register the simple renderer
simple = "{0}.renderers".format(__name__), "SimpleRenderer"
bootstrap = "{0}.renderers".format(__name__), "BootStrapRenderer"
self._renderers = [
("simple", simple),
("bootstrap", bootstrap),
(None, simple, False),
]

if app:
self.init_app(app)

def init_app(self, app):
def init_app(self, app, bootstrap=False):
"""Initialize an application.

:param app: A :class:`~flask.Flask` app.
:param bootstrap: Use bootstrap 5 rendering. Default: False.
"""
self.bootstrap = bootstrap
if not hasattr(app, "extensions"):
app.extensions = {}

Expand Down
75 changes: 75 additions & 0 deletions flask_nav3/renderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,78 @@ def visit_Separator(self, node):
def visit_Text(self, node):
"""Returns nav-label spans."""
return tags.span(node.text, _class="nav-label")


class BootStrapRenderer(Renderer):
"""A very basic Bootstrap 5 renderer.

Renders a navigational structure using ``<nav>`` and ``<ul>`` tags that
can be styled using modern CSS.

:param kwargs: Additional attributes to pass on to the root ``<nav>``-tag.
"""

def __init__(self, **kwargs):
"""Constructor for ``SimpleRenderer``."""
self.kwargs = kwargs

def visit_Link(self, node):
"""Returns arefs matching url."""
return tags.a(node.text, href=node.get_url(), _class="nav-link")

def visit_Navbar(self, node):
"""Returns navbar classes."""
kwargs = self.kwargs.copy()

addclass = []
if "class" in self.kwargs:
addclass = kwargs["class"].split(" ")

kwargs["class"] = " ".join(addclass + ["navbar", "navbar-expand-lg"])

cont = tags.nav(**kwargs)
ul = cont.add(tags.ul(_class=" ".join(addclass + ["nav"])))

for item in node.items:
ul.add(tags.li(self.visit(item), _class="nav-item"))

return cont

def visit_View(self, node):
"""Returns arefs."""
kwargs = {"class": "nav-link"}
if node.active:
kwargs["_class"] = "nav-link active"
return tags.a(
node.text,
href=node.get_url(),
title=node.text,
**kwargs,
) # noqa: WPS221

def visit_Subgroup(self, node):
"""Returns subgroup divs."""
group = tags.ul(_class="dropdown-menu")
kwargs = {"data-bs-toggle": "dropdown"}
title = tags.a(
node.title,
href="#",
_class="nav-link dropdown-toggle",
**kwargs,
)

if node.active:
title.attributes["class"] = "nav-link dropdown-toggle active"

for item in node.items:
group.add(tags.li(self.visit(item), _class="dropdown-item"))

return tags.div(title, group, _class="dropdown")

def visit_Separator(self, node):
"""Returns separator hrs."""
return tags.hr(_class="dropdown-divider")

def visit_Text(self, node):
"""Returns nav-label spans."""
return tags.a(node.text, _class="nav-link disabled")
Loading