diff --git a/djangoproject/scss/_dark-mode.scss b/djangoproject/scss/_dark-mode.scss index 717ba1aee..6da713f51 100644 --- a/djangoproject/scss/_dark-mode.scss +++ b/djangoproject/scss/_dark-mode.scss @@ -284,13 +284,17 @@ html[data-theme="dark"] .img-release { cursor: pointer; border: none; background: transparent; - vertical-align: middle; + margin: 0 2px; padding: 0; color: var(--menu); + width: 45px; + height: 45px; + display: flex; + align-items: center; + justify-content: center; } .theme-toggle svg { - vertical-align: middle; max-width: 16px; max-height: 16px; display: none; @@ -347,31 +351,6 @@ html[data-theme="light"] .theme-toggle .theme-label-when-light { background-color: var(--body-bg); } -// Section of the header that is only visible on mobile. -.header-mobile-only { - line-height: 45px; - text-align: center; - display: flex; - align-items: center; - - @include respond-min(1080px) { - display: none; - } - - .light-dark-mode-toggle { - height: 45px; - width: 45px; - } -} - -// The last 2 items of the nav list are the searchbar and the light/dark mode toggle. -// These are not visible on mobile as they are within the .header-mobile-only section. -header nav li:nth-last-child(-n+2) { - @include respond-max(1079px) { - display: none; - } -} - html[data-theme="light"] .admonition { .admonition-title::before { opacity: 0.5; diff --git a/djangoproject/scss/_style.scss b/djangoproject/scss/_style.scss index e1c14e820..c8db3b45b 100644 --- a/djangoproject/scss/_style.scss +++ b/djangoproject/scss/_style.scss @@ -465,8 +465,6 @@ header { align-items: center; } - .header-mobile-only {margin-left: auto} - .meta { @include font-size(13); color: var(--primary); @@ -475,7 +473,7 @@ header { margin: 8px 0 0 10px; display: none; - @include respond-min(1080px) { + @include respond-min(1500px) { width: 200px; display: block; } @@ -509,6 +507,22 @@ header { } } + .header-tools { + display: flex; + align-items: center; + order: 1; + margin-left: auto; + + .search { + flex: 0 1 240px; + } + + @include respond-min(1280px) { + order: 3; + margin-left: 0; + } + } + .menu-button { @include font-size(20); background: $green-dark; @@ -520,8 +534,9 @@ header { text-align: center; text-decoration: none; width: 45px; + order: 2; - @include respond-min(1080px) { + @include respond-min(1280px) { display: none; } @@ -535,19 +550,24 @@ header { } nav { + order: 4; width: 100%; background: $green-dark; transition: max-height 0.3s ease-out; max-height: 0; // hide mobile menu by default overflow: hidden; padding: 0 2px; + flex-basis: 100%; &.active { max-height: 580px; } - @include respond-min(1080px) { + @include respond-min(1280px) { width: auto; + order: 2; + margin-left: auto; + flex-basis: auto; max-height: none; // always show menu on a desktop width } @@ -555,10 +575,10 @@ header { margin: 10px 0 0; padding: 0; - @include respond-min(1080px) { + @include respond-min(1280px) { display: flex; align-items: center; - margin: 0; + margin: 2px 0; } } @@ -576,7 +596,7 @@ header { padding: 20px 0px; } - @include respond-min(1080px) { + @include respond-min(1280px) { margin: 0; border: 0; float: left; @@ -616,7 +636,7 @@ header { color: var(--secondary-accent); } - @include respond-min(1080px) { + @include respond-min(1280px) { padding: 20px 10px; } } diff --git a/djangoproject/templates/includes/header.html b/djangoproject/templates/includes/header.html index aa62a1bc1..d8fe681d0 100644 --- a/djangoproject/templates/includes/header.html +++ b/djangoproject/templates/includes/header.html @@ -6,12 +6,6 @@

The web framework for perfectionists with deadlines.

-
- {% search_form %} -
- {% include "includes/toggle_theme.html" %} -
-
diff --git a/djangoproject/utils.py b/djangoproject/utils.py deleted file mode 100644 index d06f1863d..000000000 --- a/djangoproject/utils.py +++ /dev/null @@ -1,36 +0,0 @@ -from django import template - - -class CachedLibrary(template.Library): - def cached_context_inclusion_tag(self, template_name, *, name=None): - """ - Wraps @register.inclusion_tag(template_name, takes_context=True) to - automatically cache the returned context dictionary inside the - template's render_context for the duration of a single render pass. - - This is useful when a tag may be rendered multiple times within the - same template and computing its context is expensive (e.g. due to - database queries). - """ - - def decorator(func): - tag_name = name or func.__name__ - - @self.inclusion_tag(template_name, takes_context=True, name=tag_name) - def wrapper(context, *args, **kwargs): - render_context = getattr(context, "render_context", None) - cache_key = f"{tag_name}_cached_context" - - if render_context is not None and cache_key in render_context: - return render_context[cache_key] - - result = func(context, *args, **kwargs) - - if render_context is not None: - render_context[cache_key] = result - - return result - - return wrapper - - return decorator diff --git a/docs/templatetags/docs.py b/docs/templatetags/docs.py index 0f2157f37..065e33441 100644 --- a/docs/templatetags/docs.py +++ b/docs/templatetags/docs.py @@ -10,17 +10,15 @@ from pygments.formatters.html import HtmlFormatter from pygments.lexers import get_lexer_by_name -from djangoproject.utils import CachedLibrary - from ..forms import DocSearchForm from ..models import DocumentRelease from ..search import START_SEL, STOP_SEL from ..utils import get_doc_path, get_doc_root, get_module_path -register = CachedLibrary() +register = template.Library() -@register.cached_context_inclusion_tag("search_form.html") +@register.inclusion_tag("search_form.html", takes_context=True) def search_form(context): if "request" not in context: # Django's built-in error views (like django.views.defaults.server_error) diff --git a/docs/tests/test_templates.py b/docs/tests/test_templates.py index f1f3af4aa..22abd0572 100644 --- a/docs/tests/test_templates.py +++ b/docs/tests/test_templates.py @@ -2,7 +2,6 @@ import shutil import tempfile from pathlib import Path -from unittest.mock import Mock from django.conf import settings from django.template import Context, Template @@ -251,19 +250,3 @@ def test_search_form_renders_without_request_in_template(self): kwargs={"lang": settings.DEFAULT_LANGUAGE_CODE, "version": "dev"}, ) self.assertIn(f'
', rendered) - - def test_search_form_queries_multiple_renders(self): - r2 = Release.objects.create(version="2.0") - DocumentRelease.objects.create( - lang=settings.DEFAULT_LANGUAGE_CODE, release=r2, is_default=True - ) - template = Template("{% load docs %}{% search_form %}{% search_form %}") - with self.assertNumQueries(1): - rendered = template.render(Context({"request": Mock()})) - - docs_search_url = reverse_with_host( - "document-search", - host="docs", - kwargs={"lang": settings.DEFAULT_LANGUAGE_CODE, "version": "2.0"}, - ) - self.assertEqual(rendered.count(f''), 2)