/
DnTyr
/
Password-Development
Обзор
Документация
Войти
/
DnTyr
/
Password-Development
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
frontend/src/App.js
152 строки
5 KB
DT
first_commit
03 июн 2026, 10:22
03 июн 2026, 10:22
357cb35
Код
Авторство
О чём код?
import Generator from "./components/Generator"; import Analyzer from "./components/Analyzer"; import { Toaster } from "react-hot-toast"; import { useState, useEffect, useRef } from "react"; import { useTranslation } from "react-i18next"; import passIcon from "./pass.webp"; import "./App.css"; import { Settings, Sun, Moon } from 'lucide-react'; import Modal from "./components/Modal"; function App() { const [resetSignal, setResetSignal] = useState(0); const { t, i18n } = useTranslation(); const [activeModal, setActiveModal] = useState(null); const [theme, setTheme] = useState("dark"); const [showThemeTip, setShowThemeTip] = useState(false); const [isMenuOpen, setIsMenuOpen] = useState(false); // 2. Создаем реф для отслеживания кликов вне меню const menuRef = useRef(null); useEffect(() => { const saved = localStorage.getItem("theme"); if (saved) { setTheme(saved); } else { const prefersDark = window.matchMedia("(prefers-color-scheme: dark)").matches; setTheme(prefersDark ? "dark" : "light"); } const savedLng = localStorage.getItem("i18nextLng"); if (savedLng) { i18n.changeLanguage(savedLng); } }, [i18n]); useEffect(() => { document.body.className = theme; localStorage.setItem("theme", theme); }, [theme]); // 3. Логика закрытия меню при клике в любое другое место useEffect(() => { const handleClickOutside = (event) => { if (isMenuOpen && menuRef.current && !menuRef.current.contains(event.target)) { setIsMenuOpen(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, [isMenuOpen]); const changeLanguage = (lng) => { i18n.changeLanguage(lng); localStorage.setItem("i18nextLng", lng); // Сохраняем выбор setIsMenuOpen(false); // Закрываем меню }; return ( <> {/* Убираем blur-content с основного контейнера */} <div className="app-container"> {/* Добавляем блюр отдельно на заголовок */} <div className={`title-wrapper ${isMenuOpen ? 'blur-content' : ''}`}> <img src={passIcon} alt="Logo" className="title-icon" /> <h1 className="title-text">{t("Безопасный пароль")}</h1> </div> {/* Карточка остается родителем для шестеренки */} <div className="card generator-card" style={{ position: 'relative' }}> {/* Меню ВНУТРИ карточки — оно НЕ будет блюриться */} <div className={`header-controls ${isMenuOpen ? 'open' : ''}`} ref={menuRef} > <button className="menu-toggle" onClick={() => setIsMenuOpen(!isMenuOpen)} aria-label="Settings" > <Settings/> </button> <div className="controls-content"> <div className="lang-switcher"> <button className={`lang-btn ${i18n.language.startsWith('ru') ? 'active' : ''}`} onClick={() => { changeLanguage('ru'); setIsMenuOpen(false); }} >RU</button> <button className={`lang-btn ${i18n.language.startsWith('en') ? 'active' : ''}`} onClick={() => { changeLanguage('en'); setIsMenuOpen(false); }} >EN</button> </div> <div style={{ position: 'relative' }}> {showThemeTip && ( <div className="custom-tooltip"> {theme === "dark" ? t("Светлая тема") : t("Темная тема")} </div> )} <button className="theme-toggle" onMouseEnter={() => setShowThemeTip(true)} onMouseLeave={() => setShowThemeTip(false)} onClick={() => { setTheme(theme === "dark" ? "light" : "dark"); setIsMenuOpen(false); }} > {theme === "dark" ? <Sun color="#ffc15e"/> : <Moon/>} </button> </div> </div> </div> {/* Оборачиваем САМ КОНТЕНТ карточек в блюр */} <div className={isMenuOpen ? 'blur-content' : ''}> {/* Передаем функцию управления */} <Generator setResetSignal={setResetSignal} /> </div> </div> <div className={`card ${isMenuOpen ? 'blur-content' : ''}`}> {/* Передаем само значение */} <Analyzer resetSignal={resetSignal} /> </div> </div> <Toaster position="top-center" containerStyle={{ top: "max(20px, env(safe-area-inset-top, 0px))" }} toastOptions={{ duration: 2500, style: { maxWidth: "min(92vw, 360px)", marginTop: "5px" }, }} /> {activeModal && <Modal type={activeModal} onClose={() => setActiveModal(null)} />} </> ); } export default App;