Conversion of User Guide to the SHH stack (#2781)
This commit is contained in:
0
guide/webapp/display/layouts/elements/__init__.py
Normal file
0
guide/webapp/display/layouts/elements/__init__.py
Normal file
75
guide/webapp/display/layouts/elements/footer.py
Normal file
75
guide/webapp/display/layouts/elements/footer.py
Normal file
@@ -0,0 +1,75 @@
|
||||
from datetime import datetime
|
||||
|
||||
from html5tagger import Builder, E # type: ignore
|
||||
from sanic import Request
|
||||
|
||||
|
||||
def do_footer(builder: Builder, request: Request) -> None:
|
||||
builder.footer(
|
||||
_pagination(request),
|
||||
_content(),
|
||||
class_="footer",
|
||||
)
|
||||
|
||||
|
||||
def _pagination(request: Request) -> Builder:
|
||||
return E.div(
|
||||
_pagination_left(request), _pagination_right(request), class_="level"
|
||||
)
|
||||
|
||||
|
||||
def _pagination_left(request: Request) -> Builder:
|
||||
item = E.div(class_="level-item")
|
||||
if not hasattr(request.ctx, "previous_page"):
|
||||
return E.div(item, class_="level-left")
|
||||
with item:
|
||||
if p := request.ctx.previous_page:
|
||||
path = p.relative_path.with_suffix(".html")
|
||||
item.a(
|
||||
f"← {p.meta.title}",
|
||||
href=f"/{path}",
|
||||
hx_get=f"/{path}",
|
||||
hx_target="#content",
|
||||
hx_swap="innerHTML",
|
||||
hx_push_url="true",
|
||||
class_="button pagination",
|
||||
)
|
||||
return E.div(item, class_="level-left")
|
||||
|
||||
|
||||
def _pagination_right(request: Request) -> Builder:
|
||||
item = E.div(class_="level-item")
|
||||
if not hasattr(request.ctx, "next_page"):
|
||||
return E.div(item, class_="level-right")
|
||||
with item:
|
||||
if p := request.ctx.next_page:
|
||||
path = p.relative_path.with_suffix(".html")
|
||||
item.a(
|
||||
f"{p.meta.title} →",
|
||||
href=f"/{path}",
|
||||
hx_get=f"/{path}",
|
||||
hx_target="#content",
|
||||
hx_swap="innerHTML",
|
||||
hx_push_url="true",
|
||||
class_="button pagination",
|
||||
)
|
||||
return E.div(item, class_="level-right")
|
||||
|
||||
|
||||
def _content() -> Builder:
|
||||
year = datetime.now().year
|
||||
inner = E.p(
|
||||
E.a(
|
||||
"MIT Licensed",
|
||||
href="https://github.com/sanic-org/sanic/blob/master/LICENSE",
|
||||
target="_blank",
|
||||
rel="nofollow noopener noreferrer",
|
||||
).br()(
|
||||
E.small(f"Copyright © 2018-{year} Sanic Community Organization")
|
||||
),
|
||||
)
|
||||
return E.div(
|
||||
inner,
|
||||
E.p("~ Made with ❤️ and ☕️ ~"),
|
||||
class_="content has-text-centered",
|
||||
)
|
||||
68
guide/webapp/display/layouts/elements/navbar.py
Normal file
68
guide/webapp/display/layouts/elements/navbar.py
Normal file
@@ -0,0 +1,68 @@
|
||||
from webapp.display.layouts.models import MenuItem
|
||||
|
||||
from html5tagger import Builder, E # type: ignore
|
||||
from sanic import Request
|
||||
|
||||
|
||||
def do_navbar(builder: Builder, request: Request) -> None:
|
||||
navbar_items = [
|
||||
_render_navbar_item(item, request)
|
||||
for item in request.app.config.NAVBAR
|
||||
]
|
||||
container = E.div(
|
||||
_search_form(request), *navbar_items, class_="navbar-end"
|
||||
)
|
||||
|
||||
builder.nav(
|
||||
E.div(container, class_="navbar-menu"),
|
||||
class_="navbar is-hidden-touch",
|
||||
)
|
||||
|
||||
|
||||
def _search_form(request: Request) -> Builder:
|
||||
return E.div(
|
||||
E.div(
|
||||
E.input(
|
||||
id_="search",
|
||||
type_="text",
|
||||
placeholder="Search",
|
||||
class_="input",
|
||||
value=request.args.get("q", ""),
|
||||
hx_target="#content",
|
||||
hx_swap="innerHTML",
|
||||
hx_push_url="true",
|
||||
hx_trigger="keyup changed delay:500ms",
|
||||
hx_get=f"/{request.ctx.language}/search",
|
||||
hx_params="*",
|
||||
),
|
||||
class_="control",
|
||||
),
|
||||
class_="navbar-item",
|
||||
)
|
||||
|
||||
|
||||
def _render_navbar_item(item: MenuItem, request: Request) -> Builder:
|
||||
if item.items:
|
||||
return E.div(
|
||||
E.a(item.label, class_="navbar-link"),
|
||||
E.div(
|
||||
*(
|
||||
_render_navbar_item(subitem, request)
|
||||
for subitem in item.items
|
||||
),
|
||||
class_="navbar-dropdown",
|
||||
),
|
||||
class_="navbar-item has-dropdown is-hoverable",
|
||||
)
|
||||
|
||||
kwargs = {
|
||||
"class_": "navbar-item",
|
||||
}
|
||||
if item.href:
|
||||
kwargs["href"] = item.href
|
||||
kwargs["target"] = "_blank"
|
||||
kwargs["rel"] = "nofollow noopener noreferrer"
|
||||
elif item.path:
|
||||
kwargs["href"] = f"/{request.ctx.language}/{item.path}"
|
||||
internal = [item.label]
|
||||
return E.a(*internal, **kwargs)
|
||||
118
guide/webapp/display/layouts/elements/sidebar.py
Normal file
118
guide/webapp/display/layouts/elements/sidebar.py
Normal file
@@ -0,0 +1,118 @@
|
||||
from webapp.display.layouts.models import MenuItem
|
||||
from webapp.display.text import slugify
|
||||
|
||||
from html5tagger import Builder, E # type: ignore
|
||||
from sanic import Request
|
||||
|
||||
|
||||
def do_sidebar(builder: Builder, request: Request) -> None:
|
||||
builder.a(class_="burger")(E.span().span().span().span())
|
||||
builder.aside(*_menu_items(request), class_="menu")
|
||||
|
||||
|
||||
def _menu_items(request: Request) -> list[Builder]:
|
||||
return [
|
||||
_sanic_logo(request),
|
||||
*_sidebar_items(request),
|
||||
E.hr(),
|
||||
E.p("Current with version ").strong(
|
||||
request.app.config.GENERAL.current_version
|
||||
),
|
||||
E.hr(),
|
||||
E.p("Want more? ").a(
|
||||
"sanicbook.com", href="https://sanicbook.com", target="_blank"
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def _sanic_logo(request: Request) -> Builder:
|
||||
return E.a(
|
||||
class_="navbar-item sanic-simple-logo",
|
||||
href=f"https://sanic.dev/{request.ctx.language}/",
|
||||
)(
|
||||
E.img(
|
||||
src="/assets/images/sanic-framework-logo-simple-400x97.png", # noqa: E501
|
||||
alt="Sanic Framework",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _sidebar_items(request: Request) -> list[Builder]:
|
||||
return [
|
||||
builder
|
||||
for item in request.app.config.SIDEBAR
|
||||
for builder in _render_sidebar_item(item, request, True)
|
||||
]
|
||||
|
||||
|
||||
def _render_sidebar_item(
|
||||
item: MenuItem, request: Request, root: bool = False
|
||||
) -> list[Builder]:
|
||||
builders: list[Builder] = []
|
||||
if root:
|
||||
builders.append(E.p(class_="menu-label")(item.label))
|
||||
else:
|
||||
builders.append(_single_sidebar_item(item, request))
|
||||
|
||||
if item.items:
|
||||
ul = E.ul(class_="menu-list")
|
||||
with ul:
|
||||
for subitem in item.items:
|
||||
sub_builders = _render_sidebar_item(subitem, request)
|
||||
ul(*sub_builders)
|
||||
builders.append(ul)
|
||||
|
||||
return builders
|
||||
|
||||
|
||||
def _single_sidebar_item(item: MenuItem, request: Request) -> Builder:
|
||||
if item.path and item.path.startswith("/"):
|
||||
path = item.path
|
||||
else:
|
||||
path = f"/{request.ctx.language}/{item.path}" if item.path else ""
|
||||
kwargs = {}
|
||||
classes: list[str] = []
|
||||
li_classes = "menu-item"
|
||||
_, page, _ = request.app.ctx.get_page(
|
||||
request.ctx.language, item.path or ""
|
||||
)
|
||||
if request.path == path:
|
||||
classes.append("is-active")
|
||||
if item.href:
|
||||
kwargs["href"] = item.href
|
||||
kwargs["target"] = "_blank"
|
||||
kwargs["rel"] = "nofollow noopener noreferrer"
|
||||
elif not path:
|
||||
li_classes += " is-group"
|
||||
if _is_open_item(item, request.ctx.language, request.path):
|
||||
classes.append("is-open")
|
||||
else:
|
||||
kwargs.update(
|
||||
{
|
||||
"href": path,
|
||||
"hx-get": path,
|
||||
"hx-target": "#content",
|
||||
"hx-swap": "innerHTML",
|
||||
"hx-push-url": "true",
|
||||
}
|
||||
)
|
||||
kwargs["class_"] = " ".join(classes)
|
||||
inner = E().a(item.label, **kwargs)
|
||||
if page and page.anchors:
|
||||
with inner.ul(class_="anchor-list"):
|
||||
for anchor in page.anchors:
|
||||
inner.li(
|
||||
E.a(anchor.strip("`"), href=f"{path}#{slugify(anchor)}"),
|
||||
class_="is-anchor",
|
||||
)
|
||||
return E.li(inner, class_=li_classes)
|
||||
|
||||
|
||||
def _is_open_item(item: MenuItem, language: str, current_path: str) -> bool:
|
||||
path = f"/{language}/{item.path}" if item.path else ""
|
||||
if current_path == path:
|
||||
return True
|
||||
for subitem in item.items:
|
||||
if _is_open_item(subitem, language, current_path):
|
||||
return True
|
||||
return False
|
||||
Reference in New Issue
Block a user