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 %} +