From 0ec6a4683112082b75664069c41b2c8dbf1a85f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=98=D0=B3=D0=BE=D1=80=D1=8C=20=D0=9D=D0=B0=D0=B4=D0=B5?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Mon, 12 Jan 2026 08:32:16 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BD=D0=B8=D0=B6=D0=BD=D1=8F=D1=8F=20=D0=BD?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D0=B3=D0=B0=D1=86=D0=B8=D0=BE=D0=BD=D0=BD?= =?UTF-8?q?=D0=B0=D1=8F=20=D0=BF=D0=B0=D0=BD=D0=B5=D0=BB=D1=8C=20=D0=B4?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BC=D0=BE=D0=B1=D0=B8=D0=BB=D1=8C=D0=BD=D1=8B?= =?UTF-8?q?=D1=85=20=D1=83=D1=81=D1=82=D1=80=D0=BE=D0=B9=D1=81=D1=82=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Добавлена bottom navbar с 5 кнопками (Главная, Уведомления, Поиск, Календарь, Ещё) - Панель отображается только на мобильных устройствах (до 768px) - Использованы системные CSS переменные и стили - Удалена кнопка гамбургера из верхней панели (дублирование функционала) - Добавлена синхронизация бейджей уведомлений - Поддержка светлой и темной темы - Backdrop-filter для полупрозрачности в iOS-стиле --- backend/static/css/components/navbar.css | 114 +++++++++++++++++++++++ backend/static/js/bottom-navbar.js | 81 ++++++++++++++++ backend/templates/includes/navbar.html | 88 +++++++++-------- 3 files changed, 245 insertions(+), 38 deletions(-) create mode 100644 backend/static/js/bottom-navbar.js diff --git a/backend/static/css/components/navbar.css b/backend/static/css/components/navbar.css index bc4f6b2b..c3c37229 100644 --- a/backend/static/css/components/navbar.css +++ b/backend/static/css/components/navbar.css @@ -83,3 +83,117 @@ [data-bs-theme="dark"] .mobile-search-overlay { background-color: rgba(0, 0, 0, 0.7); } + +/* ───────────────────────────────────────────────────────────── + BOTTOM NAVBAR (только для мобильных) +────────────────────────────────────────────────────────────── */ +.bottom-navbar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background-color: var(--bs-primary); + border-top: 0.5px solid var(--bs-border-color); + z-index: 1040; + padding: 0; + box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.08); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); +} + +/* Темная тема */ +[data-bs-theme="dark"] .bottom-navbar { + background-color: var(--bs-tertiary-bg); + border-top-color: var(--bs-border-color); + box-shadow: 0 -1px 10px rgba(0, 0, 0, 0.3); +} + +.bottom-navbar-container { + display: flex; + justify-content: space-around; + align-items: center; + height: 60px; + padding: 0 8px; + max-width: 100%; +} + +.bottom-nav-item { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + flex: 1; + height: 100%; + color: rgba(255, 255, 255, 0.7); + text-decoration: none; + border: none; + background: transparent; + padding: 4px 8px; + transition: all 0.2s ease; + position: relative; + cursor: pointer; +} + +/* Темная тема - другой цвет кнопок */ +[data-bs-theme="dark"] .bottom-nav-item { + color: var(--bs-secondary-color); +} + +.bottom-nav-item:hover, +.bottom-nav-item:focus, +.bottom-nav-item:active { + color: #fff; + background: rgba(255, 255, 255, 0.1); + outline: none; +} + +[data-bs-theme="dark"] .bottom-nav-item:hover, +[data-bs-theme="dark"] .bottom-nav-item:focus, +[data-bs-theme="dark"] .bottom-nav-item:active { + color: var(--bs-emphasis-color); + background: var(--bs-secondary-bg); +} + +.bottom-nav-item i { + font-size: 1.4rem; + margin-bottom: 2px; + line-height: 1; +} + +.bottom-nav-label { + font-size: 0.65rem; + font-weight: 500; + text-align: center; + line-height: 1; + margin-top: 2px; +} + +/* Иконка с бейджем для уведомлений */ +.bottom-nav-icon-wrapper { + position: relative; + display: inline-block; +} + +.bottom-nav-badge { + position: absolute; + top: -4px; + right: -8px; + background: var(--bs-danger); + color: white; + border-radius: var(--badge-radius); + padding: 2px 5px; + font-size: 0.65rem; + font-weight: 600; + min-width: 18px; + text-align: center; + line-height: 1; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); +} + +/* Padding для body, чтобы контент не перекрывался нижней панелью */ +@media (max-width: 767.98px) { + body { + padding-bottom: 60px; + } +} + diff --git a/backend/static/js/bottom-navbar.js b/backend/static/js/bottom-navbar.js new file mode 100644 index 00000000..b7fb08f6 --- /dev/null +++ b/backend/static/js/bottom-navbar.js @@ -0,0 +1,81 @@ +/** + * Bottom Navigation Bar + * Управление нижней панелью навигации для мобильных устройств + */ + +document.addEventListener('DOMContentLoaded', function() { + // Кнопка уведомлений в нижней панели + const bottomNotificationToggle = document.getElementById('bottomNotificationToggle'); + const notificationDropdown = document.getElementById('notificationDropdown'); + const notificationBadge = document.getElementById('notificationBadge'); + const bottomNotificationBadge = document.getElementById('bottomNotificationBadge'); + + // Синхронизация бейджей между верхней и нижней панелью + function syncNotificationBadges() { + if (notificationBadge && bottomNotificationBadge) { + const count = notificationBadge.textContent; + const hasNotifications = !notificationBadge.classList.contains('d-none'); + + bottomNotificationBadge.textContent = count; + if (hasNotifications) { + bottomNotificationBadge.classList.remove('d-none'); + } else { + bottomNotificationBadge.classList.add('d-none'); + } + } + } + + // Наблюдатель за изменениями бейджа уведомлений + if (notificationBadge) { + const observer = new MutationObserver(syncNotificationBadges); + observer.observe(notificationBadge, { + attributes: true, + childList: true, + characterData: true, + subtree: true + }); + // Первоначальная синхронизация + syncNotificationBadges(); + } + + // Открытие уведомлений из нижней панели + if (bottomNotificationToggle && notificationDropdown) { + bottomNotificationToggle.addEventListener('click', function(e) { + e.preventDefault(); + e.stopPropagation(); + + const isVisible = notificationDropdown.style.display !== 'none'; + + if (isVisible) { + notificationDropdown.style.display = 'none'; + } else { + notificationDropdown.style.display = 'block'; + // Позиционируем над нижней панелью + notificationDropdown.style.bottom = '70px'; + notificationDropdown.style.right = '10px'; + notificationDropdown.style.top = 'auto'; + notificationDropdown.style.left = 'auto'; + } + }); + } + + // Кнопка поиска в нижней панели + const bottomSearchToggle = document.getElementById('bottomSearchToggle'); + + if (bottomSearchToggle) { + bottomSearchToggle.addEventListener('click', function(e) { + e.preventDefault(); + // Перенаправляем на страницу поиска + window.location.href = '/search/'; + }); + } + + // Закрытие выпадающих элементов при клике вне их + document.addEventListener('click', function(e) { + if (notificationDropdown && + !notificationDropdown.contains(e.target) && + !bottomNotificationToggle?.contains(e.target)) { + notificationDropdown.style.display = 'none'; + } + }); +}); diff --git a/backend/templates/includes/navbar.html b/backend/templates/includes/navbar.html index f60dd356..56bdfd73 100644 --- a/backend/templates/includes/navbar.html +++ b/backend/templates/includes/navbar.html @@ -1,19 +1,11 @@ {% load static %} +