Add theme_switcher and some theme (#2)

* Add theme_switcher and some theme

* Unify theme with both light & dark version

* Use anchor instead of window.open
This commit is contained in:
Yukko
2025-06-04 22:19:51 +08:00
committed by GitHub
parent 0e6ea060c5
commit e8c4d7f545
4 changed files with 320 additions and 32 deletions

View File

@@ -10,6 +10,7 @@ import {
} from "react-icons/md";
import { useTranslation } from "react-i18next";
import UploadingSideBar from "./uploading_side_bar.tsx";
import { ThemeSwitcher } from "./theme_switcher.tsx";
import { IoLogoGithub } from "react-icons/io";
import { useAppContext } from "./AppContext.tsx";
@@ -140,6 +141,7 @@ export default function Navigator() {
<div className="flex gap-2">
<SearchBar />
<UploadingSideBar />
<ThemeSwitcher />
{app.isLoggedIn() && (
<button
className={"btn btn-circle btn-ghost"}
@@ -150,14 +152,15 @@ export default function Navigator() {
<MdSettings size={24} />
</button>
)}
<button
className={"btn btn-circle btn-ghost"}
onClick={() => {
window.open("https://github.com/wgh136/nysoure", "_blank");
}}
<a
href="https://github.com/wgh136/nysoure"
target="_blank"
rel="noopener noreferrer"
>
<IoLogoGithub size={24} />
</button>
<button className={"btn btn-circle btn-ghost"}>
<IoLogoGithub size={24} />
</button>
</a>
{app.isLoggedIn() ? (
<UserButton />
) : (

View File

@@ -0,0 +1,93 @@
import { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { MdPalette } from "react-icons/md";
interface ThemeOption {
name: string;
displayName: string;
displayColor: string;
}
const themeOptions: ThemeOption[] = [
{
name: "pink",
displayName: "Nyne Pink",
displayColor: "oklch(65% 0.241 354.308)",
},
{
name: "ocean",
displayName: "Ocean Breeze",
displayColor: "oklch(70% 0.18 220)",
},
{
name: "mint",
displayName: "Mint Leaf",
displayColor: "oklch(65% 0.16 160)",
},
{
name: "glow",
displayName: "Golden Glow",
displayColor: "oklch(70% 0.16 70)",
},
];
export function ThemeSwitcher() {
const [currentTheme, setCurrentTheme] = useState("pink");
const { t } = useTranslation();
useEffect(() => {
const savedTheme = localStorage.getItem("theme") || "pink";
setCurrentTheme(savedTheme);
applyTheme(savedTheme);
}, []);
const applyTheme = (themeName: string) => {
const root = document.documentElement;
root.setAttribute("data-theme", themeName);
};
const handleThemeChange = (themeName: string) => {
setCurrentTheme(themeName);
applyTheme(themeName);
localStorage.setItem("theme", themeName);
const activeElement = document.activeElement as HTMLElement;
if (activeElement) {
activeElement.blur();
}
};
return (
<div className="dropdown dropdown-end">
<div tabIndex={0} role="button" className="btn btn-circle btn-ghost">
<MdPalette size={24} />
</div>
<ul
tabIndex={0}
className="dropdown-content menu bg-base-100 rounded-box z-[1] w-max min-w-35 p-2 shadow"
>
{themeOptions.map((theme) => (
<li key={theme.name}>
<a
className={`flex items-center justify-between${
currentTheme === theme.name ? "active" : ""
}`}
onClick={() => handleThemeChange(theme.name)}
>
<div className="flex items-center gap-3">
<div
className="w-4 h-4 rounded-full border-2 border-base-content/20"
style={{ backgroundColor: theme.displayColor }}
/>
<span className="mr-1">{t(theme.displayName)}</span>
</div>
{currentTheme === theme.name && (
<div className="w-2 h-2 bg-primary rounded-full" />
)}
</a>
</li>
))}
</ul>
</div>
);
}