refactor: simplify ThemeToggle with config-driven approach

Replace imperative theme toggling logic with declarative
THEME_CONFIG object for cleaner, more maintainable code.
Add type safety with Theme type and isTheme type guard.
This commit is contained in:
2025-10-23 13:00:22 -05:00
parent a51d21df83
commit 698328f445
2 changed files with 23 additions and 35 deletions

View File

@@ -17,6 +17,8 @@ export default [
// Base configuration with ignores // Base configuration with ignores
{ {
ignores: [ ignores: [
".media/**",
"coverage/**",
".next/**", ".next/**",
"node_modules/**", "node_modules/**",
"out/**", "out/**",

View File

@@ -3,7 +3,21 @@
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import { IconButton } from "@radix-ui/themes"; import { IconButton } from "@radix-ui/themes";
import { MoonIcon, SunIcon, DesktopIcon } from "@radix-ui/react-icons"; import { MoonIcon, SunIcon, DesktopIcon } from "@radix-ui/react-icons";
import { useEffect, useState } from "react"; import { useEffect, useState, type ReactElement } from "react";
type Theme = "light" | "dark" | "system";
const ICON_SIZE = 22;
const THEME_CONFIG: Record<Theme, { icon: ReactElement; next: Theme }> = {
light: { icon: <SunIcon width={ICON_SIZE} height={ICON_SIZE} />, next: "dark" },
dark: { icon: <MoonIcon width={ICON_SIZE} height={ICON_SIZE} />, next: "system" },
system: { icon: <DesktopIcon width={ICON_SIZE} height={ICON_SIZE} />, next: "light" },
};
const isTheme = (value: string | undefined): value is Theme => {
return value === "light" || value === "dark" || value === "system";
};
export const ThemeToggle = () => { export const ThemeToggle = () => {
const { theme, setTheme } = useTheme(); const { theme, setTheme } = useTheme();
@@ -18,44 +32,16 @@ export const ThemeToggle = () => {
return null; return null;
} }
const currentTheme: Theme = isTheme(theme) ? theme : "system";
const { icon, next } = THEME_CONFIG[currentTheme];
const toggleTheme = () => { const toggleTheme = () => {
if (theme === "light") { setTheme(next);
setTheme("dark");
} else if (theme === "dark") {
setTheme("system");
} else {
setTheme("light");
}
}; };
const getNextTheme = () => {
if (theme === "light") return "dark";
if (theme === "dark") return "system";
return "light";
};
const getIcon = () => {
if (theme === "light") {
return <SunIcon width="22" height="22" />;
} else if (theme === "dark") {
return <MoonIcon width="22" height="22" />;
} else {
return <DesktopIcon width="22" height="22" />;
}
};
const nextTheme = getNextTheme();
const themeLabel = theme === "system" ? "system" : theme === "light" ? "light" : "dark";
return ( return (
<IconButton <IconButton size="3" variant="ghost" onClick={toggleTheme} aria-label="Toggle theme">
size="3" {icon}
variant="ghost"
onClick={toggleTheme}
aria-label="Toggle theme"
title={`Current: ${themeLabel} mode. Click to switch to ${nextTheme} mode`}
>
{getIcon()}
</IconButton> </IconButton>
); );
}; };