home page #1

Open
nikitaa_ts wants to merge 15 commits from dev into master
10 changed files with 333 additions and 2 deletions
Showing only changes of commit d421dc8f2c - Show all commits
+10
View File
@@ -12,6 +12,7 @@
"axios": "^1.17.0", "axios": "^1.17.0",
"react": "^19.2.6", "react": "^19.2.6",
"react-dom": "^19.2.6", "react-dom": "^19.2.6",
"react-icons": "^5.6.0",
"react-router-dom": "^7.17.0", "react-router-dom": "^7.17.0",
"tailwindcss": "^4.3.0" "tailwindcss": "^4.3.0"
}, },
@@ -2958,6 +2959,15 @@
"react": "^19.2.7" "react": "^19.2.7"
} }
}, },
"node_modules/react-icons": {
"version": "5.6.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz",
"integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==",
"license": "MIT",
"peerDependencies": {
"react": "*"
}
},
"node_modules/react-router": { "node_modules/react-router": {
"version": "7.17.0", "version": "7.17.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.17.0.tgz", "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.17.0.tgz",
+1
View File
@@ -14,6 +14,7 @@
"axios": "^1.17.0", "axios": "^1.17.0",
"react": "^19.2.6", "react": "^19.2.6",
"react-dom": "^19.2.6", "react-dom": "^19.2.6",
"react-icons": "^5.6.0",
"react-router-dom": "^7.17.0", "react-router-dom": "^7.17.0",
"tailwindcss": "^4.3.0" "tailwindcss": "^4.3.0"
}, },
+3
View File
@@ -1,9 +1,12 @@
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { BrowserRouter } from "react-router-dom"; import { BrowserRouter } from "react-router-dom";
import App from "./app/App.tsx"; import App from "./app/App.tsx";
import { ThemeInitialProvider } from "@/modules/theme-changer";
createRoot(document.getElementById("root")!).render( createRoot(document.getElementById("root")!).render(
<BrowserRouter> <BrowserRouter>
<ThemeInitialProvider>
<App /> <App />
</ThemeInitialProvider>
</BrowserRouter>, </BrowserRouter>,
); );
@@ -0,0 +1,106 @@
export const themes = [
{
id: "light",
name: "Светлая",
description: "Чистая светлая тема",
type: "light",
colors: {
primary: "#4f46e5",
background: "#ffffff",
surface: "#f8fafc",
text: "#1f2937",
border: "#e5e7eb",
},
},
{
id: "dark",
name: "Темная",
description: "Элегантная темная тема",
type: "dark",
colors: {
primary: "#6366f1",
background: "#0f172a",
surface: "#1e293b",
text: "#f1f5f9",
border: "#334155",
},
},
{
id: "nightowl",
name: "Night Owl",
description: "Тема вдохновленная редактором кода",
type: "dark",
colors: {
primary: "#7dd3fc",
background: "#011627",
surface: "#011e3c",
text: "#d6deeb",
border: "#1d3b53",
},
},
{
id: "sunset",
name: "Закат",
description: "Теплые оранжевые тона",
type: "dark",
colors: {
primary: "#f97316",
background: "#1c1917",
surface: "#292524",
text: "#fafaf9",
border: "#57534e",
},
},
{
id: "forest",
name: "Лес",
description: "Успокаивающая зеленая тема",
type: "dark",
colors: {
primary: "#22c55e",
background: "#052e16",
surface: "#14532d",
text: "#f0fdf4",
border: "#166534",
},
},
{
id: "ocean",
name: "Океан",
description: "Глубокие синие тона",
type: "dark",
colors: {
primary: "#06b6d4",
background: "#164e63",
surface: "#0e7490",
text: "#f0fdfd",
border: "#0891b2",
},
},
{
id: "lavender",
name: "Лаванда",
description: "Нежная фиолетовая тема",
type: "light",
colors: {
primary: "#a855f7",
background: "#faf5ff",
surface: "#f3e8ff",
text: "#581c87",
border: "#e9d5ff",
},
},
{
id: "coffee",
name: "Кофе",
description: "Уютная коричневая тема",
type: "dark",
colors: {
primary: "#d97706",
background: "#292524",
surface: "#44403c",
text: "#f5f5f4",
border: "#57534e",
},
},
];
+2
View File
@@ -0,0 +1,2 @@
export { ThemeInitialProvider } from "./provider/theme.initial.provider";
export { ThemeToggle } from "./ui/Theme.toggle";
@@ -0,0 +1,13 @@
import { useLayoutEffect } from "react";
import { applyTheme, initializeTheme } from "../utils/apply.theme";
export const ThemeInitialProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
useLayoutEffect(() => {
const theme = initializeTheme();
applyTheme(theme);
}, []);
return children;
};
@@ -0,0 +1,13 @@
export interface ITheme {
id: string;
name: string;
description: string;
type: string;
colors: {
primary: string;
background: string;
surface: string;
text: string;
border: string;
};
}
@@ -0,0 +1,68 @@
import React, { useState, useEffect } from "react";
import { FiSun, FiMoon } from "react-icons/fi";
import {
getCurrentTheme,
toggleDarkLight,
getSavedTheme,
} from "../../theme-changer/utils/apply.theme";
import { themes } from "../../theme-changer/config/theme.config";
export const ThemeToggle: React.FC = () => {
const [currentTheme, setCurrentTheme] = useState<string>(() =>
getSavedTheme(),
);
const currentThemeData = themes.find((t) => t.id === currentTheme);
const isDark = currentThemeData?.type === "dark";
const handleClick = () => {
const newTheme = toggleDarkLight();
setCurrentTheme(newTheme);
};
// Инициализация при монтировании
useEffect(() => {
const saved = getSavedTheme();
const current = getCurrentTheme() || saved;
setCurrentTheme(current);
}, []);
// Слушаем изменения темы из других компонентов
useEffect(() => {
const handleThemeChange = (e: Event) => {
const event = e as CustomEvent;
setCurrentTheme(event.detail.theme);
};
window.addEventListener("themechange", handleThemeChange);
return () => window.removeEventListener("themechange", handleThemeChange);
}, []);
return (
<button
onClick={handleClick}
className="p-2 rounded-lg transition-colors duration-200"
style={{
backgroundColor: "var(--bg-secondary)",
}}
onMouseEnter={(e) => {
e.currentTarget.style.backgroundColor = "var(--bg-tertiary)";
}}
onMouseLeave={(e) => {
e.currentTarget.style.backgroundColor = "var(--bg-secondary)";
}}
aria-label="Переключить тему"
title={
isDark ? "Переключить на светлую тему" : "Переключить на тёмную тему"
}
>
{isDark ? (
<FiSun className="w-5 h-5" style={{ color: "var(--accent)" }} />
) : (
<FiMoon
className="w-5 h-5"
style={{ color: "var(--text-secondary)" }}
/>
)}
</button>
);
};
@@ -0,0 +1,105 @@
import { themes } from "../config/theme.config";
export const applyTheme = (themeId: string) => {
const theme = themes.find((t) => t.id === themeId);
const root = document.documentElement;
if (theme) {
try {
root.setAttribute("data-theme", themeId);
localStorage.setItem("theme", themeId);
localStorage.setItem("theme-type", theme.type);
window.dispatchEvent(
new CustomEvent("themechange", {
detail: { theme: themeId, type: theme.type },
}),
);
} catch (error) {
console.error("❌ Error applying theme:", error);
}
} else {
console.warn(`⚠️ Theme not found: ${themeId}, falling back to light theme`);
applyTheme("light");
}
};
export const getSavedTheme = () => {
try {
return localStorage.getItem("theme") || "light";
} catch (error) {
console.error("Error reading theme from localStorage:", error);
return "light";
}
};
export const initializeTheme = () => {
const savedTheme = getSavedTheme();
const themeExists = themes.some((t) => t.id === savedTheme);
const themeToApply = themeExists ? savedTheme : "light";
applyTheme(themeToApply);
return themeToApply;
};
export const getCurrentTheme = () => {
return document.documentElement.getAttribute("data-theme") || "light";
};
export const getCurrentThemeType = () => {
const currentTheme = getCurrentTheme();
const theme = themes.find((t) => t.id === currentTheme);
return theme ? theme.type : "light";
};
export const toggleDarkLight = () => {
const currentTheme = getCurrentTheme();
const currentThemeData = themes.find((t) => t.id === currentTheme);
if (currentThemeData) {
const oppositeThemes = themes.filter(
(t) => t.type !== currentThemeData.type,
);
if (oppositeThemes.length > 0) {
applyTheme(oppositeThemes[0].id);
return oppositeThemes[0].id;
}
}
const newTheme = currentTheme === "light" ? "dark" : "light";
applyTheme(newTheme);
return newTheme;
};
export const getNextTheme = () => {
const currentTheme = getCurrentTheme();
const currentIndex = themes.findIndex((t) => t.id === currentTheme);
const nextIndex = (currentIndex + 1) % themes.length;
return themes[nextIndex].id;
};
export const applySystemTheme = () => {
if (
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches
) {
applyTheme("dark");
} else {
applyTheme("light");
}
};
export const watchSystemTheme = () => {
if (window.matchMedia) {
window
.matchMedia("(prefers-color-scheme: dark)")
.addEventListener("change", (e) => {
if (e.matches) {
applyTheme("dark");
} else {
applyTheme("light");
}
});
}
};
+11 -1
View File
@@ -1,3 +1,13 @@
import { ThemeToggle } from "@/modules/theme-changer/ui/Theme.toggle";
export const HomePage = () => { export const HomePage = () => {
return <div className="bg-red-500">Домашняя</div>; return (
<div
style={{ backgroundColor: "var(--bg-primary)" }}
className="min-h-screen flex items-center justify-center p-4"
>
<ThemeToggle />
Домашняя
</div>
);
}; };