fixed issues with libraries navigation, refactors (#1213) (#1250)

There's a good amount of refactoring in this, so this is going to look
like a much bigger change than it is. In reality it makes the dispatch
code simpler.

Fixes: 
1. Resolved the issue with the libraries pages not redirecting correctly
2. Resolved an issue around categories not being preserved moving from
page to page.

The issue with the redirects was there was a tug of war on arriving on
/library between the various ways of determining where the user should
end up.
I added a `/libraries/grid/` url and now `/libraries/` determines which
list page the user should end up on based on 1) url 2) cookie, 3) the
default, same for version preference. We can probably get rid of
dispatch() later. This has the added bonus of allowing reliable linking
to a specific list view (e.g. for users to bookmark one type)

Refactors:
1. Separated the navigation on the three library pages into a standalone
template.
2. Moved some constants to constants.py
3. Moved a lot of the views methods which were only used by the
dispatch() call to utils.py .
4. At that stage there were circular imports so I moved the docs
generation functions which were only used in constants.py to
constants_utils.py. utils.py is more general.
This commit is contained in:
daveoconnor
2024-09-19 12:06:36 -07:00
committed by GitHub
parent b4a58bd73c
commit 50559609e9
11 changed files with 353 additions and 422 deletions

View File

@@ -149,8 +149,9 @@ urlpatterns = (
LibraryListByCategory.as_view(),
name="libraries-by-category",
),
path("libraries/mini/", LibraryListMini.as_view(), name="libraries-mini"),
path("libraries/", LibraryList.as_view(), name="libraries"),
path("libraries/mini/", LibraryListMini.as_view(), name="libraries-mini"),
path("libraries/grid/", LibraryList.as_view(), name="libraries-grid"),
path(
"libraries/<slug:slug>/<slug:version_slug>/",
LibraryDetail.as_view(),

View File

@@ -1,4 +1,4 @@
from .utils import (
from .constants_utils import (
generate_library_docs_url,
generate_library_docs_url_v2,
generate_library_docs_url_v3,
@@ -11,18 +11,17 @@ from .utils import (
generate_library_docs_url_utility_v3,
generate_library_docs_url_circular_buffer,
generate_library_docs_url_core,
generate_library_docs_url_double_nested_library_htm,
generate_library_docs_url_double_nested_library_html,
generate_library_docs_url_double_nested_library_htm,
generate_library_docs_url_algorithm,
generate_library_docs_url_numeric,
generate_library_docs_url_numeric_2,
generate_library_docs_url_string_ref,
generate_library_docs_url_string_view,
generate_library_docs_url_utility_anchor,
generate_library_docs_url_throwexception,
generate_library_docs_url_utility_anchor,
)
# Mapping for exeptions to loading URLs for older docs.
# key: Taken from Library.slug
# value: List of dictionaries with instructions for how to format docs URLs for
@@ -309,3 +308,7 @@ SKIP_LIBRARY_VERSIONS = {
# List of versions for which we know docs are missing
VERSION_DOCS_MISSING = ["boost-1.33.0"]
DEFAULT_LIBRARIES_LANDING_VIEW = "libraries-grid"
SELECTED_BOOST_VERSION_COOKIE_NAME = "boost_version"
SELECTED_LIBRARY_VIEW_COOKIE_NAME = "library_view"

View File

@@ -0,0 +1,157 @@
def generate_library_docs_url(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
General use
"""
return f"/doc/libs/{boost_url_slug}/libs/{library_slug}/doc/html/index.html"
def generate_library_docs_url_v2(boost_url_slug, library_slug):
""" "Generate a documentation url with a specific format
For use primarily with IO, versions 1.73.0 and up
"""
new_boost_url_slug = boost_url_slug.replace("boost_", "")
return f"/doc/libs/{new_boost_url_slug}/libs/{library_slug}/doc/html/{library_slug}.html" # noqa
def generate_library_docs_url_v3(boost_url_slug, library_slug):
""" "Generate a documentation url with a specific format
For use primarily with IO, versions 1.64.0-1.72.
"""
return f"/doc/libs/{boost_url_slug}/libs/{library_slug}/doc/index.html"
def generate_library_docs_url_v4(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Any, versions 1.33.0 and older
"""
return f"/doc/libs/{boost_url_slug}/doc/html/{library_slug}.html"
def generate_library_docs_url_bind_v1(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Member Function, versions 1.60.0 and older
"""
return f"/doc/libs/{boost_url_slug}/libs/bind/doc/html/{library_slug}.html"
def generate_library_docs_url_bind_v2(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Member Function, versions 1.60.0 and older
"""
return f"/doc/libs/{boost_url_slug}/libs/bind/{library_slug}.html"
def generate_library_docs_url_math_v1(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Math Common Factor, versions 1.60.0 and older
"""
return f"/doc/libs/{boost_url_slug}/libs/math/doc/html/{library_slug}.html"
def generate_library_docs_url_utility_v1(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Call Traits, versions 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/utility/{library_slug}.htm"
def generate_library_docs_url_utility_v2(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Identity Types, version 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/utility/{library_slug}/doc/html/index.html"
def generate_library_docs_url_utility_v3(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
Same as v1, but .html and not .htm
First used for In Place Factories, version 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/utility/{library_slug}.html"
def generate_library_docs_url_circular_buffer(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used with Circular Buffer v. 1.54.0 and before"""
return f"/doc/libs/{boost_url_slug}/libs/{library_slug}/doc/{library_slug}.html"
def generate_library_docs_url_core(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Enable If, versions 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/core/doc/html/core/{library_slug}.html"
def generate_library_docs_url_double_nested_library_html(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Dynamic Bitset, versions 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/{library_slug}/{library_slug}.html"
def generate_library_docs_url_double_nested_library_htm(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
Ends in .htm, not .html
First used for Dynamic Bitset, versions 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/{library_slug}/{library_slug}.htm"
def generate_library_docs_url_algorithm(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used with Min Max, versions 1.60.0 and below"""
return f"/doc/libs/{boost_url_slug}/libs/algorithm/{library_slug}/index.html"
def generate_library_docs_url_numeric(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used with Interval, versions 1.60.0 and below"""
return (
f"/doc/libs/{boost_url_slug}/libs/numeric/{library_slug}/doc/{library_slug}.htm"
)
def generate_library_docs_url_numeric_2(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used with Interval, versions 1.60.0 and below"""
return f"/doc/libs/{boost_url_slug}/libs/numeric/{library_slug}/doc/html/index.html"
def generate_library_docs_url_string_ref(boost_url_slug, library_slug):
"""Generate a documentation URL for the string-ref library-versions"""
return f"/doc/libs/{boost_url_slug}/libs/utility/doc/html/{library_slug}.html"
def generate_library_docs_url_string_view(boost_url_slug, library_slug):
"""Generate a documentation URL for the string-view library-versions"""
return f"/doc/libs/{boost_url_slug}/libs/utility/doc/html/utility/utilities/{library_slug}.html" # noqa
def generate_library_docs_url_throwexception(boost_url_slug, library_slug):
"""Generate a documentation URL for the string-view library-versions"""
return f"/doc/libs/{boost_url_slug}/libs/exception/doc/{library_slug}.html"
def generate_library_docs_url_utility_anchor(boost_url_slug, library_slug):
"""Generate a documentation URL for a URL that uses an anchor"""
return f"/doc/libs/{boost_url_slug}/libs/utility/utility.htm#{library_slug}"

View File

@@ -6,6 +6,12 @@ from dateutil.relativedelta import relativedelta
from libraries.utils import (
decode_content,
generate_fake_email,
get_first_last_day_last_month,
parse_date,
version_within_range,
write_content_to_tempfile,
)
from libraries.constants_utils import (
generate_library_docs_url,
generate_library_docs_url_v2,
generate_library_docs_url_v3,
@@ -18,15 +24,11 @@ from libraries.utils import (
generate_library_docs_url_utility_v3,
generate_library_docs_url_circular_buffer,
generate_library_docs_url_core,
generate_library_docs_url_double_nested_library_htm,
generate_library_docs_url_double_nested_library_html,
generate_library_docs_url_double_nested_library_htm,
generate_library_docs_url_numeric,
generate_library_docs_url_string_ref,
generate_library_docs_url_string_view,
get_first_last_day_last_month,
parse_date,
version_within_range,
write_content_to_tempfile,
)

View File

@@ -29,6 +29,9 @@ def test_library_list(library_version, tp, url_name="libraries"):
)
res = tp.get(url_name)
if url_name == "libraries":
tp.response_302(res)
return
tp.response_200(res)
assert "library_list" in res.context
assert library_version.library in res.context["library_list"]
@@ -36,11 +39,18 @@ def test_library_list(library_version, tp, url_name="libraries"):
assert v_no_libraries not in res.context["versions"]
def test_library_root_redirect_to_grid(tp):
"""GET /"""
res = tp.get("libraries")
tp.response_302(res)
assert res.url == "/libraries/grid/"
def test_library_list_no_data(tp):
"""GET /libraries/"""
Library.objects.all().delete()
Version.objects.all().delete()
res = tp.get("libraries")
res = tp.get("libraries-grid")
tp.response_200(res)
@@ -67,7 +77,7 @@ def test_library_list_no_pagination(library_version, tp):
).library
for i in range(30)
] + [library_version.library]
res = tp.get("libraries")
res = tp.get("libraries-grid")
tp.response_200(res)
library_list = res.context.get("library_list")
@@ -86,7 +96,7 @@ def test_library_list_select_category(library_version, category, tp):
new_lib_version = baker.make(
"libraries.LibraryVersion", version=library_version.version, library=new_lib
)
res = tp.get(f"/libraries/?category={category.slug}")
res = tp.get(f"/libraries/grid/?category={category.slug}")
tp.response_200(res)
assert library_version.library in res.context["library_list"]
assert new_lib_version.library not in res.context["library_list"]

View File

@@ -11,6 +11,12 @@ from django.utils.text import slugify
from django.urls import reverse
from django.shortcuts import redirect
from libraries.constants import (
DEFAULT_LIBRARIES_LANDING_VIEW,
SELECTED_BOOST_VERSION_COOKIE_NAME,
SELECTED_LIBRARY_VIEW_COOKIE_NAME,
)
logger = structlog.get_logger()
@@ -32,165 +38,6 @@ def generate_fake_email(val: str) -> str:
return f"{local_email}@example.com"
def generate_library_docs_url(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
General use
"""
return f"/doc/libs/{boost_url_slug}/libs/{library_slug}/doc/html/index.html"
def generate_library_docs_url_v2(boost_url_slug, library_slug):
""" "Generate a documentation url with a specific format
For use primarily with IO, versions 1.73.0 and up
"""
new_boost_url_slug = boost_url_slug.replace("boost_", "")
return f"/doc/libs/{new_boost_url_slug}/libs/{library_slug}/doc/html/{library_slug}.html" # noqa
def generate_library_docs_url_v3(boost_url_slug, library_slug):
""" "Generate a documentation url with a specific format
For use primarily with IO, versions 1.64.0-1.72.
"""
return f"/doc/libs/{boost_url_slug}/libs/{library_slug}/doc/index.html"
def generate_library_docs_url_v4(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Any, versions 1.33.0 and older
"""
return f"/doc/libs/{boost_url_slug}/doc/html/{library_slug}.html"
def generate_library_docs_url_bind_v1(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Member Function, versions 1.60.0 and older
"""
return f"/doc/libs/{boost_url_slug}/libs/bind/doc/html/{library_slug}.html"
def generate_library_docs_url_bind_v2(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Member Function, versions 1.60.0 and older
"""
return f"/doc/libs/{boost_url_slug}/libs/bind/{library_slug}.html"
def generate_library_docs_url_math_v1(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Math Common Factor, versions 1.60.0 and older
"""
return f"/doc/libs/{boost_url_slug}/libs/math/doc/html/{library_slug}.html"
def generate_library_docs_url_utility_v1(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Call Traits, versions 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/utility/{library_slug}.htm"
def generate_library_docs_url_utility_v2(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Identity Types, version 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/utility/{library_slug}/doc/html/index.html"
def generate_library_docs_url_utility_v3(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
Same as v1, but .html and not .htm
First used for In Place Factories, version 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/utility/{library_slug}.html"
def generate_library_docs_url_circular_buffer(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used with Circular Buffer v. 1.54.0 and before"""
return f"/doc/libs/{boost_url_slug}/libs/{library_slug}/doc/{library_slug}.html"
def generate_library_docs_url_core(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Enable If, versions 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/core/doc/html/core/{library_slug}.html"
def generate_library_docs_url_double_nested_library_html(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used for Dynamic Bitset, versions 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/{library_slug}/{library_slug}.html"
def generate_library_docs_url_double_nested_library_htm(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
Ends in .htm, not .html
First used for Dynamic Bitset, versions 1.60.0 and below.
"""
return f"/doc/libs/{boost_url_slug}/libs/{library_slug}/{library_slug}.htm"
def generate_library_docs_url_algorithm(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used with Min Max, versions 1.60.0 and below"""
return f"/doc/libs/{boost_url_slug}/libs/algorithm/{library_slug}/index.html"
def generate_library_docs_url_numeric(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used with Interval, versions 1.60.0 and below"""
return (
f"/doc/libs/{boost_url_slug}/libs/numeric/{library_slug}/doc/{library_slug}.htm"
)
def generate_library_docs_url_numeric_2(boost_url_slug, library_slug):
"""Generate a documentation url with a specific format
First used with Interval, versions 1.60.0 and below"""
return f"/doc/libs/{boost_url_slug}/libs/numeric/{library_slug}/doc/html/index.html"
def generate_library_docs_url_string_ref(boost_url_slug, library_slug):
"""Generate a documentation URL for the string-ref library-versions"""
return f"/doc/libs/{boost_url_slug}/libs/utility/doc/html/{library_slug}.html"
def generate_library_docs_url_string_view(boost_url_slug, library_slug):
"""Generate a documentation URL for the string-view library-versions"""
return f"/doc/libs/{boost_url_slug}/libs/utility/doc/html/utility/utilities/{library_slug}.html" # noqa
def generate_library_docs_url_throwexception(boost_url_slug, library_slug):
"""Generate a documentation URL for the string-view library-versions"""
return f"/doc/libs/{boost_url_slug}/libs/exception/doc/{library_slug}.html"
def generate_library_docs_url_utility_anchor(boost_url_slug, library_slug):
"""Generate a documentation URL for a URL that uses an anchor"""
return f"/doc/libs/{boost_url_slug}/libs/utility/utility.htm#{library_slug}"
def generate_random_string(length=4):
characters = string.ascii_letters
random_string = "".join(random.choice(characters) for _ in range(length))
@@ -244,5 +91,76 @@ def redirect_to_view_with_params(view_name, params, query_params):
"""Redirect to a view with parameters and query parameters."""
base_url = reverse(view_name, kwargs=params)
query_string = urlencode(query_params)
url = "{}?{}".format(base_url, query_string)
url = base_url
if query_string:
url = "{}?{}".format(base_url, query_string)
return redirect(url)
def get_version_from_url(request):
return request.GET.get("version")
def get_version_from_cookie(request):
return request.COOKIES.get(SELECTED_BOOST_VERSION_COOKIE_NAME)
def get_view_from_url(request):
return request.GET.get("view")
def get_view_from_cookie(request):
return request.COOKIES.get(SELECTED_LIBRARY_VIEW_COOKIE_NAME)
def set_view_in_cookie(response, view):
response.set_cookie(SELECTED_LIBRARY_VIEW_COOKIE_NAME, view)
def get_prioritized_version(request):
"""
Version Priorities:
1. URL parameter
2. Cookie
3. Default to latest version
"""
url_version = get_version_from_url(request)
cookie_version = get_version_from_cookie(request)
default_version = None
return url_version or cookie_version or default_version
def get_prioritized_library_view(request):
"""
View Priorities:
1. URL parameter
2. Cookie
3. Default to grid view
"""
url_view = get_view_from_url(request)
cookie_view = get_view_from_cookie(request)
return url_view or cookie_view or DEFAULT_LIBRARIES_LANDING_VIEW
def build_view_query_params_from_request(request):
query_params = {}
version = get_prioritized_version(request)
category = get_category(request)
if version and version != "latest":
query_params["version"] = version
if category:
query_params["category"] = category
return query_params
def get_category(request):
return request.GET.get("category", "")
def build_route_name_for_view(view):
return f"libraries-{view}"
def determine_view_from_library_request(request):
split_path_info = request.path_info.split("/")
return None if split_path_info[-2] == "libraries" else split_path_info[-2]

View File

@@ -17,10 +17,16 @@ from versions.models import Version
from .forms import VersionSelectionForm
from .mixins import VersionAlertMixin
from .models import Category, CommitData, Library, LibraryVersion
from .utils import redirect_to_view_with_params
SELECTED_BOOST_VERSION_COOKIE_NAME = "boost_version"
SELECTED_LIBRARY_VIEW_COOKIE_NAME = "library_view"
from .utils import (
redirect_to_view_with_params,
get_view_from_cookie,
set_view_in_cookie,
get_prioritized_library_view,
build_view_query_params_from_request,
build_route_name_for_view,
determine_view_from_library_request,
)
from .constants import SELECTED_BOOST_VERSION_COOKIE_NAME
logger = structlog.get_logger()
@@ -46,7 +52,6 @@ class LibraryList(VersionAlertMixin, ListView):
version_slug = self.request.COOKIES.get(
SELECTED_BOOST_VERSION_COOKIE_NAME, None
)
if version_slug is None:
version_slug = self.request.GET.get("version", None)
@@ -59,26 +64,13 @@ class LibraryList(VersionAlertMixin, ListView):
def set_selected_boost_version(self, response, version: str) -> None:
"""Set the selected version in the cookies."""
valid_versions = Version.objects.version_dropdown_strict()
if version in [v.slug for v in valid_versions] or version == "latest":
if version in [v.slug for v in valid_versions]:
response.set_cookie(SELECTED_BOOST_VERSION_COOKIE_NAME, version)
elif version == "latest":
response.delete_cookie(SELECTED_BOOST_VERSION_COOKIE_NAME)
else:
logger.warning(f"Attempted to set invalid version slug: {version}")
def get_selected_library_view(self) -> str:
"""Get the user's preferred view for the libraries page."""
return self.request.COOKIES.get(SELECTED_LIBRARY_VIEW_COOKIE_NAME)
def set_selected_library_view(
self, response, view: str, *, clear: bool = False
) -> str:
"""Set the user's preferred view for the libraries page."""
if clear:
response.delete_cookie(SELECTED_LIBRARY_VIEW_COOKIE_NAME)
else:
response.set_cookie(SELECTED_LIBRARY_VIEW_COOKIE_NAME, view)
return view
def get_queryset(self):
queryset = super().get_queryset()
params = self.request.GET.copy()
@@ -141,6 +133,7 @@ class LibraryList(VersionAlertMixin, ListView):
context["categories"] = self.get_categories(context["version"])
context["versions"] = self.get_versions()
context["library_list"] = self.get_queryset()
context["url_params"] = build_view_query_params_from_request(self.request)
return context
def get_categories(self, version=None):
@@ -174,72 +167,24 @@ class LibraryList(VersionAlertMixin, ListView):
versions = versions.exclude(name__in=["develop", "master", "head"])
return versions
def get_version_request_from_url(self):
return self.request.GET.get("version")
def view_route_from_url(self):
path_info = self.request.path_info
# Determine the route name based on the URL.
if "/by-category/" in path_info:
route_name = "libraries-by-category"
elif "/mini/" in path_info:
route_name = "libraries-mini"
else:
route_name = "libraries"
return route_name
def dispatch(self, request, *args, **kwargs):
"""Set the selected version in the cookies."""
response = super().dispatch(request, *args, **kwargs)
query_params = build_view_query_params_from_request(request)
# check if one was set, if not then default to cookie value for latest
# (practically speaking, that means no cookie)
self.set_selected_boost_version(response, query_params.get("version", "latest"))
# If the user has requested a reset, clear the session value.
if "reset_view" in request.GET:
self.set_selected_library_view(response, "", clear=True)
view = determine_view_from_library_request(request)
# The following conditional practically only applies on "/libraries/", at
# which point the redirection will be determined by prioritised view
if not view:
view = get_prioritized_library_view(request)
set_view_in_cookie(response, build_route_name_for_view(view))
return redirect_to_view_with_params(view, kwargs, query_params)
# Determine the route name based on the URL.
route_name = self.view_route_from_url()
if route_name in ["libraries-mini", "libraries-by-category"]:
self.set_selected_library_view(response, route_name)
redirect_to_version = self.get_selected_boost_version()
redirect_to_route = self.get_selected_library_view()
route_in_url = self.view_route_from_url()
version_in_url = self.get_version_request_from_url()
# If the user has a preferred view, use that.
if redirect_to_route and route_in_url != redirect_to_route:
if "reset_view" not in request.GET:
self.set_selected_library_view(response, route_name)
return redirect(redirect_to_route)
route_name = redirect_to_route
# Set the cookie value to the version in the URL.
if version_in_url:
self.set_selected_boost_version(response, version_in_url)
else:
# If no version is present in the URL, default to the cookie value.
if redirect_to_version:
# Construct the URL with the version from the cookie.
current_category = request.GET.get("category", "")
reset_view = "reset_view" in request.GET
query_params = {
"category": current_category,
"version": redirect_to_version,
}
if reset_view:
query_params.update(reset_view="1")
if not current_category:
del query_params["category"]
# Redirect to the correct view with the correct version.
redirect_to_route = self.get_selected_library_view()
route_name = redirect_to_route if redirect_to_route else route_name
return redirect_to_view_with_params(route_name, kwargs, query_params)
if view != get_view_from_cookie(request):
set_view_in_cookie(response, build_route_name_for_view(view))
return response

View File

@@ -33,62 +33,7 @@
{% endcomment %}
</div>
<div class="pt-2 px-0 mb-2 text-right md:mb-2 mx-3 md:mx-0">
<form action="." method="get">
<div class="flex relative space-x-3 justify-start">
{# Search #}
{% comment %} {% include "libraries/includes/search_input.html" %} {% endcomment %}
{# Select a category #}
{% comment %}
<div>
{# todo: if someone selects a category and hits back, it retains their choice here. #}
<select onchange="this.form.submit()"
name="category"
class="block py-2 pr-11 pl-5 mb-3 w-full text-sm bg-white rounded-md border border-gray-300 cursor-pointer sm:inline-block md:mb-0 md:w-auto dark:bg-black dark:border-slate"
id="id_category"
disabled
>
<option value="">Filter by category</option>
{% for c in categories %}
<option value="{{ c.slug }}" {% if category == c %}selected="selected"{% endif %}>{{ c.name }}</option>
{% endfor %}
</select>
</div>
{% endcomment %}
{# Display options #}
<div class=" flex space-x-3">
<div class="relative group">
<a title="Name View" href="{% url 'libraries-mini' %}"><i class="link rounded border border-gray-300 cursor-pointer fas fa-list p-[10px] hover:bg-gray-100 dark:hover:bg-slate"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">List&nbsp;View</span>
</div>
<div class="relative group">
<a title="Grid View" href="{% url 'libraries' %}?reset_view=1"><i class="link rounded border border-gray-300 cursor-pointer fas fa-th-large p-[10px] hover:bg-gray-100 dark:hover:bg-slate"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">Grid&nbsp;View</span>
</div>
<div class="relative group">
<a title="Category View" href="{% url 'libraries-by-category' %}"><i class="link rounded border border-gray-300 cursor-pointer fas fa-cat p-[10px] bg-gray-100 dark:bg-slate"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">Category&nbsp;View</span>
</div>
</div>
{# Select a version #}
<div class="grow">
<select onchange="this.form.submit()"
name="version"
class="dropdown py-2 md:block h-[38px] ml-3 md:ml-0 "
id="id_version">
{% for v in versions %}
<option value="{{ v.slug }}" {% if version == v %}selected="selected"{% endif %}>{{ v.display_name }}</option>
{% endfor %}
</select>
</div>
</div>
</form>
</div>
{% include "libraries/includes/library_preferences.html" %}
{# alert for non-current Boost versions #}
{% include "libraries/includes/version_alert.html" %}

View File

@@ -33,61 +33,7 @@
{% endcomment %}
</div>
<div class="mx-3 md:mx-0 pt-2 px-0 mb-2 text-right md:mb-2">
<form action="." method="get">
<div class="flex relative space-x-3 justify-start">
{# Search #}
{% comment %} {% include "libraries/includes/search_input.html" %} {% endcomment %}
{# Display options #}
<div class="flex space-x-3">
<div class="relative group">
<a title="Name View" href="{% url 'libraries-mini' %}"><i class="link rounded border border-gray-300 cursor-pointer fas fa-list p-[10px] bg-gray-100 dark:bg-slate"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">List&nbsp;View</span>
</div>
<div class="relative group">
<a title="Grid View" href="{% url 'libraries' %}?reset_view=1"><i class="link rounded border border-gray-300 cursor-pointer fas fa-th-large p-[10px] hover:bg-gray-100 dark:hover:bg-slate"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">Grid&nbsp;View</span>
</div>
<div class="relative group">
<a title="Category View" href="{% url 'libraries-by-category' %}"><i class="link rounded border border-gray-300 cursor-pointer fas fa-cat p-[10px] hover:bg-gray-100 dark:hover:bg-slate"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">Category&nbsp;View</span>
</div>
</div>
<div></div>
{# Select a category #}
<div>
{# todo: if someone selects a category and hits back, it retains their choice here. #}
<select onchange="this.form.submit()"
name="category"
class="block py-2 pr-11 pl-5 mb-3 w-full text-sm bg-white rounded-md border border-gray-300 cursor-pointer sm:inline-block md:mb-0 ml-3 md:ml-0 md:w-auto dark:bg-black dark:border-slate"
id="id_category"
>
<option value="">Filter by category</option>
{% for c in categories %}
<option value="{{ c.slug }}" {% if category == c %}selected="selected"{% endif %}>{{ c.name }}</option>
{% endfor %}
</select>
</div>
{# Select a version #}
<div class="grow">
<select onchange="this.form.submit()"
name="version"
class="dropdown py-2 ml-auto md:block h-[38px]"
id="id_version">
{% for v in versions %}
<option value="{{ v.slug }}" {% if version == v %}selected="selected"{% endif %}>{{ v.display_name }}</option>
{% endfor %}
</select>
</div>
</div>
</form>
</div>
{% include "libraries/includes/library_preferences.html" %}
{# alert for non-current Boost versions #}
{% include "libraries/includes/version_alert.html" %}

View File

@@ -0,0 +1,59 @@
{% with request.resolver_match.view_name as view_name %}
<div class="pt-2 px-0 mb-2 text-right md:mb-2 mx-3 md:mx-0">
<form action="{{request.path}}" method="get">
<div class="flex relative space-x-3 justify-start">
{# Search #}
{% comment %} {% include "libraries/includes/search_input.html" %} {% endcomment %}
{# Display options #}
<div class="flex space-x-3">
<div class="relative group">
<a title="Name View" href="{% url 'libraries-mini' %}{% if url_params %}?{{ request.GET.urlencode }}{% endif %}"><i class="link rounded border border-gray-300 cursor-pointer fas fa-list p-[10px] {% if view_name == 'libraries-mini' %}bg-gray-100 dark:bg-slate{% else %}hover:bg-gray-100 dark:hover:bg-slate{% endif %}"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">List&nbsp;View</span>
</div>
<div class="relative group">
<a title="Grid View" href="{% url 'libraries-grid' %}{% if url_params %}?{{ request.GET.urlencode }}{% endif %}"><i class="link rounded border border-gray-300 cursor-pointer fas fa-th-large p-[10px] {% if view_name == 'libraries-grid' %}bg-gray-100 dark:bg-slate{% else %}hover:bg-gray-100 dark:hover:bg-slate{% endif %}"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">Grid&nbsp;View</span>
</div>
<div class="relative group">
<a title="Category View" href="{% url 'libraries-by-category' %}{% if url_params %}?{{ request.GET.urlencode }}{% endif %}"><i class="link rounded border border-gray-300 cursor-pointer fas fa-cat p-[10px] {% if view_name == 'libraries-by-category' %}bg-gray-100 dark:bg-slate{% else %}hover:bg-gray-100 dark:hover:bg-slate{% endif %}"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">Category&nbsp;View</span>
</div>
</div>
<div></div>
{# Select a category #}
{% if view_name != 'libraries-by-category' %}
<div>
{# todo: if someone selects a category and hits back, it retains their choice here. #}
<select onchange="this.form.submit()"
name="category"
class="block py-2 pr-11 pl-5 mb-3 w-full text-sm bg-white rounded-md border border-gray-300 cursor-pointer sm:inline-block md:mb-0 ml-3 md:ml-0 md:w-auto dark:bg-black dark:border-slate"
id="id_category"
>
<option value="">Filter by category</option>
{% for c in categories %}
<option value="{{ c.slug }}" {% if category == c %}selected="selected"{% endif %}>{{ c.name }}</option>
{% endfor %}
</select>
</div>
{% endif %}
{# Select a version #}
<div class="flex grow justify-end">
<select onchange="this.form.submit()"
name="version"
class="dropdown py-2 md:block h-[38px] ml-3 md:ml-0"
id="id_version">
<option value="latest" {% if version == "latest" %}selected="selected"{% endif %}>Latest</option>
{% for v in versions %}
<option value="{{ v.slug }}" {% if version == v %}selected="selected"{% endif %}>{{ v.display_name }}</option>
{% endfor %}
</select>
</div>
</div>
</form>
</div>
{% endwith %}

View File

@@ -33,62 +33,7 @@
{% endcomment %}
</div>
<div class="mx-3 md:mx-0 pt-2 px-0 mb-2 text-right md:mb-2">
<form action="." method="get">
<div class="flex relative space-x-3 justify-start">
{# Search #}
{% comment %} {% include "libraries/includes/search_input.html" %} {% endcomment %}
{# Display options #}
<div class="flex space-x-3">
<div class="relative group">
<a title="Name View" href="{% url 'libraries-mini' %}"><i class="link rounded border border-gray-300 cursor-pointer fas fa-list p-[10px] hover:bg-gray-100 dark:hover:bg-slate"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">List&nbsp;View</span>
</div>
<div class="relative group">
<a title="Grid View" href="{% url 'libraries' %}?reset_view=1"><i class="link rounded border border-gray-300 cursor-pointer fas fa-th-large p-[10px] bg-gray-100 dark:bg-slate"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">Grid&nbsp;View</span>
</div>
<div class="relative group">
<a title="Category View" href="{% url 'libraries-by-category' %}"><i class="link rounded border border-gray-300 cursor-pointer fas fa-cat p-[10px] hover:bg-gray-100 dark:hover:bg-slate"></i></a>
<span class="group-hover:opacity-100 transition-opacity bg-slate px-1 text-xs text-gray-100 rounded-sm absolute top-5 left-1/2 -translate-x-1/2 translate-y-full opacity-0 m-0 mx-auto w-auto">Category&nbsp;View</span>
</div>
</div>
<div></div>
{# Select a category #}
<div>
{# todo: if someone selects a category and hits back, it retains their choice here. #}
<select onchange="this.form.submit()"
name="category"
class="block py-2 pr-11 pl-5 mb-3 w-full text-sm bg-white rounded-md border border-gray-300 cursor-pointer sm:inline-block md:mb-0 md:w-auto ml-3 md:ml-0 dark:bg-black dark:border-slate"
id="id_category"
>
<option value="">Filter by category</option>
{% for c in categories %}
<option value="{{ c.slug }}" {% if category == c %}selected="selected"{% endif %}>{{ c.name }}</option>
{% endfor %}
</select>
</div>
{# Select a version #}
<div class="grow">
<select onchange="this.form.submit()"
name="version"
class="dropdown py-2 ml-auto md:block h-[38px]"
id="id_version">
<option value="latest" {% if version == "latest" %}selected="selected"{% endif %}>Latest</option>
{% for v in versions %}
<option value="{{ v.slug }}" {% if version == v %}selected="selected"{% endif %}>{{ v.display_name }}</option>
{% endfor %}
</select>
</div>
</div>
</form>
</div>
{% include "libraries/includes/library_preferences.html" %}
{# alert for non-current Boost versions #}
{% include "libraries/includes/version_alert.html" %}