/
d.vader
/
e2e
Обзор
Документация
Войти
/
d.vader
/
e2e
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
src/components/styleExample/Adaptive2.tsx
116 строк
5 KB
Boris Meshcheryakov
initial commit
01 окт 2025, 09:22
01 окт 2025, 09:22
3edac10
Код
Авторство
О чём код?
// Navbar.tsx import { useState, useEffect, useRef } from 'react'; export const Navbar: React.FC = () => { // Состояние для мобильного меню const [isMenuOpen, setIsMenuOpen] = useState(false); // Реф для контейнера меню (для focus trap и клика снаружи) const menuRef = useRef<HTMLDivElement>(null); // Хук для обработки нажатия клавиши Escape и клика вне меню useEffect(() => { const handleClickOutside = (event: MouseEvent) => { if (menuRef.current && !menuRef.current.contains(event.target as Node)) { setIsMenuOpen(false); } }; const handleEscapePress = (event: KeyboardEvent) => { if (event.key === 'Escape') { setIsMenuOpen(false); } }; document.addEventListener('mousedown', handleClickOutside); document.addEventListener('keydown', handleEscapePress); // Очистка эффекта return () => { document.removeEventListener('mousedown', handleClickOutside); document.removeEventListener('keydown', handleEscapePress); }; }, []); // Функция для закрытия меню (например, после перехода по ссылке) const closeMenu = () => setIsMenuOpen(false); return ( <nav className="bg-gray-800 text-white p-4" aria-label="Основная навигация"> <div className="container mx-auto flex justify-between items-center"> {/* Логотип / Название */} <a href="/" className="text-xl font-bold"> MySite </a> {/* Кнопка бургер-меню для мобильных */} <button className="md:hidden p-2 rounded focus:outline-none focus:ring-2 focus:ring-white" onClick={() => setIsMenuOpen(!isMenuOpen)} aria-expanded={isMenuOpen} // Сообщает скринридеру, открыто ли меню aria-controls="mobile-menu" // Связывает кнопку с меню, которым она управляет aria-label={isMenuOpen ? 'Закрыть меню' : 'Открыть меню'} > {/* Иконка гамбургера (условный рендеринг) */} <span className="block w-6 h-0.5 bg-white mb-1"></span> <span className="block w-6 h-0.5 bg-white mb-1"></span> <span className="block w-6 h-0.5 bg-white"></span> </button> {/* Десктопное меню - всегда видно на md и выше */} <div className="hidden md:flex space-x-6"> <NavLinks onLinkClick={closeMenu} /> </div> </div> {/* Мобильное меню - появляется поверх контента */} {/* Условный рендеринг и анимация через opacity и translate */} <div id="mobile-menu" ref={menuRef} className={`md:hidden fixed inset-0 bg-gray-800 z-50 transform transition-transform duration-300 ease-in-out ${ isMenuOpen ? 'translate-x-0' : 'translate-x-full' }`} // role="dialog" и aria-modal можно добавить для строгой семантики модального окна > <div className="container mx-auto p-4 mt-16"> <NavLinks onLinkClick={closeMenu} isMobile /> </div> </div> </nav> ); }; // Выносим ссылки в отдельный компонент для переиспользования interface NavLinksProps { onLinkClick: () => void; isMobile?: boolean; } const NavLinks: React.FC<NavLinksProps> = ({ onLinkClick, isMobile = false }) => { // Массив ссылок для удобства const links = [ { href: '/', text: 'Главная' }, { href: '/about', text: 'О нас' }, { href: '/services', text: 'Услуги' }, { href: '/contact', text: 'Контакты' }, ]; return ( <ul className={`space-y-4 ${isMobile ? 'text-2xl' : 'flex space-x-6'}`}> {links.map((link) => ( <li key={link.href}> <a href={link.href} className="hover:text-blue-300 transition-colors block py-2 focus:outline-none focus:ring-2 focus:ring-blue-300 focus:ring-inset rounded" onClick={onLinkClick} // Закрываем меню на мobile после клика // Табируемость: в мобильном меню ссылки должны быть в taborder только когда оно открыто tabIndex={isMobile ? 0 : undefined} > {link.text} </a> </li> ))} </ul> ); };