Skip to content

Commit

Permalink
feat(ui): implement dropdown menu for theme selection
Browse files Browse the repository at this point in the history
  • Loading branch information
Yukaii committed Jan 29, 2025
1 parent 80dcba1 commit 9234ccd
Showing 1 changed file with 55 additions and 34 deletions.
89 changes: 55 additions & 34 deletions src/client/layouts/RootLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,51 +1,72 @@
import { Outlet } from "react-router-dom";
import { useTheme } from "../hooks/useTheme";
import { Moon, Sun, Desktop } from "@phosphor-icons/react";
import { Moon, Sun, Desktop, CaretDown } from "@phosphor-icons/react";
import { useState, useRef, useEffect } from "react";

export function RootLayout() {
const { theme, setTheme } = useTheme();
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);

useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);

const themeOptions = [
{ value: "system", icon: Desktop, label: "System" },
{ value: "light", icon: Sun, label: "Light" },
{ value: "dark", icon: Moon, label: "Dark" },
];

const currentThemeOption = themeOptions.find((option) => option.value === theme);
const Icon = currentThemeOption?.icon || Desktop;

return (
<div className="container p-4 mx-auto dark:bg-gray-900 min-h-screen">
<div className="flex justify-between items-center mb-4">
<h1 className="text-2xl font-bold dark:text-white">Gakuon Web</h1>
<div className="flex gap-2">
<button
type="button"
onClick={() => setTheme("system")}
className={`p-2 rounded-lg ${
theme === "system"
? "bg-blue-500 text-white"
: "bg-gray-200 dark:bg-gray-700 dark:text-gray-300"
}`}
title="System theme"
>
<Desktop size={20} weight="bold" />
</button>
<button
type="button"
onClick={() => setTheme("light")}
className={`p-2 rounded-lg ${
theme === "light"
? "bg-blue-500 text-white"
: "bg-gray-200 dark:bg-gray-700 dark:text-gray-300"
}`}
title="Light theme"
>
<Sun size={20} weight="bold" />
</button>
<div className="relative" ref={dropdownRef}>
<button
type="button"
onClick={() => setTheme("dark")}
className={`p-2 rounded-lg ${
theme === "dark"
? "bg-blue-500 text-white"
: "bg-gray-200 dark:bg-gray-700 dark:text-gray-300"
}`}
title="Dark theme"
onClick={() => setIsOpen(!isOpen)}
className="flex items-center gap-2 px-3 py-2 rounded-lg bg-gray-200 dark:bg-gray-700 dark:text-gray-300 hover:bg-gray-300 dark:hover:bg-gray-600"
>
<Moon size={20} weight="bold" />
<Icon size={20} weight="bold" />
<span>{currentThemeOption?.label}</span>
<CaretDown
size={16}
weight="bold"
className={`transform transition-transform ${isOpen ? "rotate-180" : ""}`}
/>
</button>

{isOpen && (
<div className="absolute right-0 mt-2 w-48 rounded-lg shadow-lg bg-white dark:bg-gray-800 ring-1 ring-black ring-opacity-5">
<div className="py-1">
{themeOptions.map((option) => (
<button
key={option.value}
onClick={() => {
setTheme(option.value as "system" | "light" | "dark");
setIsOpen(false);
}}
className={`flex items-center gap-2 px-4 py-2 w-full text-left hover:bg-gray-100 dark:hover:bg-gray-700
${theme === option.value ? "text-blue-500" : "text-gray-700 dark:text-gray-300"}`}
type="button"
>
<option.icon size={20} weight="bold" />
{option.label}
</button>
))}
</div>
</div>
)}
</div>
</div>
<Outlet />
Expand Down

0 comments on commit 9234ccd

Please sign in to comment.