mirror of
https://github.com/woodchen-ink/nezha-dash-v1.git
synced 2025-07-18 09:31:55 +08:00
移除主题切换相关组件,简化主题管理逻辑,默认设置为暗黑模式,提升代码整洁性和用户体验。
This commit is contained in:
parent
9090b407cc
commit
3958b3b35c
49
index.html
49
index.html
@ -3,29 +3,20 @@
|
|||||||
<head>
|
<head>
|
||||||
<script>
|
<script>
|
||||||
// 在页面渲染前就执行主题初始化
|
// 在页面渲染前就执行主题初始化
|
||||||
try {
|
document.documentElement.classList.add("dark")
|
||||||
const storageKey = "vite-ui-theme"
|
|
||||||
let theme = localStorage.getItem(storageKey)
|
|
||||||
if (theme === "system" || !theme) {
|
|
||||||
theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
|
|
||||||
}
|
|
||||||
document.documentElement.classList.add(theme)
|
|
||||||
} catch (e) {
|
|
||||||
document.documentElement.classList.add("light")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 全局配置变量
|
// 全局配置变量
|
||||||
window.ShowServerDetails = true; // 是否显示服务器详细信息
|
window.ShowServerDetails = true; // 是否显示服务器详细信息
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
/* Prevent FOUC in Safari */
|
/* Prevent FOUC in Safari */
|
||||||
html:not(.dark):not(.light) * {
|
html:not(.dark) * {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
color-scheme: light;
|
color-scheme: dark;
|
||||||
--bg: #ffffff;
|
--bg: #242424;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.dark {
|
html.dark {
|
||||||
@ -33,11 +24,6 @@
|
|||||||
--bg: #242424;
|
--bg: #242424;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.light {
|
|
||||||
color-scheme: light;
|
|
||||||
--bg: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
html {
|
html {
|
||||||
background-color: var(--bg) !important;
|
background-color: var(--bg) !important;
|
||||||
}
|
}
|
||||||
@ -67,31 +53,10 @@
|
|||||||
</style>
|
</style>
|
||||||
<script>
|
<script>
|
||||||
;(function () {
|
;(function () {
|
||||||
const storageKey = "vite-ui-theme"
|
|
||||||
const theme = localStorage.getItem(storageKey) || "system"
|
|
||||||
const root = document.documentElement
|
const root = document.documentElement
|
||||||
|
root.classList.remove("light")
|
||||||
function updateThemeColor(isDark) {
|
root.classList.add("dark")
|
||||||
const themeColor = isDark ? "#242424" : "#fafafa"
|
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", "#242424")
|
||||||
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", themeColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
function setTheme(newTheme) {
|
|
||||||
root.classList.remove("light", "dark")
|
|
||||||
root.classList.add(newTheme)
|
|
||||||
updateThemeColor(newTheme === "dark")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theme === "system") {
|
|
||||||
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
|
|
||||||
setTheme(systemTheme)
|
|
||||||
|
|
||||||
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", (e) => {
|
|
||||||
setTheme(e.matches ? "dark" : "light")
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
setTheme(theme)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add loaded class after React has mounted
|
// Add loaded class after React has mounted
|
||||||
window.addEventListener("load", () => {
|
window.addEventListener("load", () => {
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { ModeToggle } from "@/components/ThemeSwitcher"
|
|
||||||
import { Separator } from "@/components/ui/separator"
|
import { Separator } from "@/components/ui/separator"
|
||||||
import { Skeleton } from "@/components/ui/skeleton"
|
import { Skeleton } from "@/components/ui/skeleton"
|
||||||
import { useBackground } from "@/hooks/use-background"
|
import { useBackground } from "@/hooks/use-background"
|
||||||
@ -102,7 +101,6 @@ function Header() {
|
|||||||
<Links />
|
<Links />
|
||||||
<DashboardLink />
|
<DashboardLink />
|
||||||
</div>
|
</div>
|
||||||
<ModeToggle />
|
|
||||||
{(customBackgroundImage || sessionStorage.getItem("savedBackgroundImage")) && (
|
{(customBackgroundImage || sessionStorage.getItem("savedBackgroundImage")) && (
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { useTheme } from "@/hooks/use-theme"
|
|
||||||
import { useEffect } from "react"
|
|
||||||
|
|
||||||
export function ThemeColorManager() {
|
|
||||||
const { theme } = useTheme()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const updateThemeColor = () => {
|
|
||||||
const currentTheme = theme
|
|
||||||
const meta = document.querySelector('meta[name="theme-color"]')
|
|
||||||
|
|
||||||
if (!meta) {
|
|
||||||
const newMeta = document.createElement("meta")
|
|
||||||
newMeta.name = "theme-color"
|
|
||||||
document.head.appendChild(newMeta)
|
|
||||||
}
|
|
||||||
|
|
||||||
const themeColor =
|
|
||||||
currentTheme === "dark"
|
|
||||||
? "hsl(30 15% 8%)" // 深色模式背景色
|
|
||||||
: "hsl(0 0% 98%)" // 浅色模式背景色
|
|
||||||
|
|
||||||
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", themeColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update on mount and theme change
|
|
||||||
updateThemeColor()
|
|
||||||
|
|
||||||
// Listen for system theme changes
|
|
||||||
const mediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
|
|
||||||
mediaQuery.addEventListener("change", updateThemeColor)
|
|
||||||
|
|
||||||
return () => mediaQuery.removeEventListener("change", updateThemeColor)
|
|
||||||
}, [theme])
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
@ -1,11 +1,9 @@
|
|||||||
import { ReactNode, createContext, useEffect, useState } from "react"
|
import { ReactNode, createContext } from "react"
|
||||||
|
|
||||||
export type Theme = "dark" | "light" | "system"
|
export type Theme = "dark"
|
||||||
|
|
||||||
type ThemeProviderProps = {
|
type ThemeProviderProps = {
|
||||||
children: ReactNode
|
children: ReactNode
|
||||||
defaultTheme?: Theme
|
|
||||||
storageKey?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ThemeProviderState = {
|
type ThemeProviderState = {
|
||||||
@ -14,40 +12,22 @@ type ThemeProviderState = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const initialState: ThemeProviderState = {
|
const initialState: ThemeProviderState = {
|
||||||
theme: "system",
|
theme: "dark",
|
||||||
setTheme: () => null,
|
setTheme: () => null,
|
||||||
}
|
}
|
||||||
|
|
||||||
const ThemeProviderContext = createContext<ThemeProviderState>(initialState)
|
const ThemeProviderContext = createContext<ThemeProviderState>(initialState)
|
||||||
|
|
||||||
export function ThemeProvider({ children, storageKey = "vite-ui-theme" }: ThemeProviderProps) {
|
export function ThemeProvider({ children }: ThemeProviderProps) {
|
||||||
const [theme, setTheme] = useState<Theme>(() => (localStorage.getItem(storageKey) as Theme) || "system")
|
const root = window.document.documentElement
|
||||||
|
root.classList.remove("light")
|
||||||
|
root.classList.add("dark")
|
||||||
|
const themeColor = "hsl(30 15% 8%)"
|
||||||
|
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", themeColor)
|
||||||
|
|
||||||
useEffect(() => {
|
const value: ThemeProviderState = {
|
||||||
const root = window.document.documentElement
|
theme: "dark",
|
||||||
|
setTheme: () => null,
|
||||||
root.classList.remove("light", "dark")
|
|
||||||
|
|
||||||
if (theme === "system") {
|
|
||||||
const systemTheme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
|
|
||||||
|
|
||||||
root.classList.add(systemTheme)
|
|
||||||
const themeColor = systemTheme === "dark" ? "hsl(30 15% 8%)" : "hsl(0 0% 98%)"
|
|
||||||
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", themeColor)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
root.classList.add(theme)
|
|
||||||
const themeColor = theme === "dark" ? "hsl(30 15% 8%)" : "hsl(0 0% 98%)"
|
|
||||||
document.querySelector('meta[name="theme-color"]')?.setAttribute("content", themeColor)
|
|
||||||
}, [theme])
|
|
||||||
|
|
||||||
const value = {
|
|
||||||
theme,
|
|
||||||
setTheme: (theme: Theme) => {
|
|
||||||
localStorage.setItem(storageKey, theme)
|
|
||||||
setTheme(theme)
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ThemeProviderContext.Provider value={value}>{children}</ThemeProviderContext.Provider>
|
return <ThemeProviderContext.Provider value={value}>{children}</ThemeProviderContext.Provider>
|
||||||
|
@ -1,53 +0,0 @@
|
|||||||
import { Theme } from "@/components/ThemeProvider"
|
|
||||||
import { Button } from "@/components/ui/button"
|
|
||||||
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
|
|
||||||
import { cn } from "@/lib/utils"
|
|
||||||
import { CheckCircleIcon } from "@heroicons/react/20/solid"
|
|
||||||
import { Moon, Sun } from "lucide-react"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
|
|
||||||
import { useTheme } from "../hooks/use-theme"
|
|
||||||
|
|
||||||
export function ModeToggle() {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const { setTheme, theme } = useTheme()
|
|
||||||
|
|
||||||
const customBackgroundImage = (window.CustomBackgroundImage as string) !== "" ? window.CustomBackgroundImage : undefined
|
|
||||||
|
|
||||||
const handleSelect = (e: Event, newTheme: Theme) => {
|
|
||||||
e.preventDefault()
|
|
||||||
setTheme(newTheme)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
size="sm"
|
|
||||||
className={cn("rounded-full px-[9px] bg-white dark:bg-black", {
|
|
||||||
"bg-white/70 dark:bg-black/70": customBackgroundImage,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<Sun className="h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
|
||||||
<Moon className="absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
|
|
||||||
<span className="sr-only">Toggle theme</span>
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent className="flex flex-col gap-0.5" align="end">
|
|
||||||
<DropdownMenuItem className={cn({ "gap-3 bg-muted": theme === "light" })} onSelect={(e) => handleSelect(e, "light")}>
|
|
||||||
{t("theme.light")}
|
|
||||||
{theme === "light" && <CheckCircleIcon className="size-4" />}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem className={cn({ "gap-3 bg-muted": theme === "dark" })} onSelect={(e) => handleSelect(e, "dark")}>
|
|
||||||
{t("theme.dark")}
|
|
||||||
{theme === "dark" && <CheckCircleIcon className="size-4" />}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem className={cn({ "gap-3 bg-muted": theme === "system" })} onSelect={(e) => handleSelect(e, "system")}>
|
|
||||||
{t("theme.system")}
|
|
||||||
{theme === "system" && <CheckCircleIcon className="size-4" />}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
)
|
|
||||||
}
|
|
@ -4,7 +4,6 @@ import ReactDOM from "react-dom/client"
|
|||||||
import { Toaster } from "sonner"
|
import { Toaster } from "sonner"
|
||||||
|
|
||||||
import App from "./App"
|
import App from "./App"
|
||||||
import { ThemeColorManager } from "./components/ThemeColorManager"
|
|
||||||
import { ThemeProvider } from "./components/ThemeProvider"
|
import { ThemeProvider } from "./components/ThemeProvider"
|
||||||
import { MotionProvider } from "./components/motion/motion-provider"
|
import { MotionProvider } from "./components/motion/motion-provider"
|
||||||
import { SortProvider } from "./context/sort-provider"
|
import { SortProvider } from "./context/sort-provider"
|
||||||
@ -18,8 +17,7 @@ const queryClient = new QueryClient()
|
|||||||
|
|
||||||
ReactDOM.createRoot(document.getElementById("root")!).render(
|
ReactDOM.createRoot(document.getElementById("root")!).render(
|
||||||
<MotionProvider>
|
<MotionProvider>
|
||||||
<ThemeProvider storageKey="vite-ui-theme">
|
<ThemeProvider>
|
||||||
<ThemeColorManager />
|
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<WebSocketProvider url="/api/v1/ws/server">
|
<WebSocketProvider url="/api/v1/ws/server">
|
||||||
<StatusProvider>
|
<StatusProvider>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user