Corp/backend/static/js/bottom-navbar.js
Игорь Надеин 0375337543 feat: implement Telegram-style badges system
- Created comprehensive badges-telegram-style.css (240 lines) based on Telegram Web K
- Updated all badge markup in templates (navbar, sidebar, chat_list, chat_detail, chat_title)
- Replaced Bootstrap badge classes with .badge-telegram classes
- Implemented Telegram specifications: 20px height, 10px radius, 11px font, 600 weight
- Added smooth animations: pulse (on update), animate-in (on mount), animate-out (on unmount)
- Updated JavaScript: replaced .d-none with style.display for badge visibility
- Updated 5 JS files: bottom-navbar, notification-manager, chatBadgeHandler, chatBadgeManager, chatListRealtime
- Added pulse animation on badge value increase
- Improved shadows and transitions with cubic-bezier easing
- Added badges-telegram-style.css to base.html for global availability
- Created documentation: TELEGRAM_BADGES_GUIDE.md, TELEGRAM_BADGES_UPDATE.md

Technical details:
- Research: Analyzed 50+ files from Telegram Web K GitHub repository
- Perfect circles: border-radius: 10px (not 50%)
- Spring animation: cubic-bezier(0.175, 0.885, 0.32, 1.275)
- Auto-hide: :empty and [data-count='0'] selectors
- Responsive design + dark theme support
2026-01-12 17:37:41 +03:00

84 lines
3.2 KiB
JavaScript

/**
* 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 isVisible = notificationBadge.style.display !== 'none';
bottomNotificationBadge.textContent = count;
if (isVisible && count !== '0') {
bottomNotificationBadge.style.display = '';
bottomNotificationBadge.classList.add('pulse');
setTimeout(() => bottomNotificationBadge.classList.remove('pulse'), 600);
} else {
bottomNotificationBadge.style.display = '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';
}
});
});