feat(settings): add theme preference selector replacing header ModeToggle
This commit is contained in:
@@ -1,7 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Sparkles, Zap } from "lucide-react";
|
import { Monitor, Moon, Sparkles, Sun, Zap } from "lucide-react";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { useIsMobile } from "@/hooks/use-mobile";
|
import { useIsMobile } from "@/hooks/use-mobile";
|
||||||
@@ -26,6 +28,7 @@ export function SettingsPanel({
|
|||||||
settings,
|
settings,
|
||||||
}: SettingsPanelProps) {
|
}: SettingsPanelProps) {
|
||||||
const isMobile = useIsMobile();
|
const isMobile = useIsMobile();
|
||||||
|
const { setTheme, theme } = useTheme();
|
||||||
const valuePrefix = hasLoadedSettings
|
const valuePrefix = hasLoadedSettings
|
||||||
? "Current preference"
|
? "Current preference"
|
||||||
: "Default value";
|
: "Default value";
|
||||||
@@ -156,6 +159,55 @@ export function SettingsPanel({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className={settingRowClasses}>
|
||||||
|
<div className="flex items-start gap-3">
|
||||||
|
<div className="mt-0.5 rounded-full bg-primary/10 p-2 text-primary">
|
||||||
|
<Sun className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
<div className="min-w-0 flex-1 space-y-3">
|
||||||
|
<div className="space-y-1">
|
||||||
|
<p className="text-sm font-medium text-foreground">
|
||||||
|
Theme preference
|
||||||
|
</p>
|
||||||
|
<p className="text-sm leading-relaxed text-muted-foreground">
|
||||||
|
Choose the appearance LocalCal should use on this device.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"grid gap-2",
|
||||||
|
isMobile ? "grid-cols-1" : "grid-cols-3",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={theme === "light" ? "default" : "outline"}
|
||||||
|
onClick={() => setTheme("light")}
|
||||||
|
>
|
||||||
|
<Sun className="h-4 w-4" />
|
||||||
|
Light
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={theme === "dark" ? "default" : "outline"}
|
||||||
|
onClick={() => setTheme("dark")}
|
||||||
|
>
|
||||||
|
<Moon className="h-4 w-4" />
|
||||||
|
Dark
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant={theme === "system" ? "default" : "outline"}
|
||||||
|
onClick={() => setTheme("system")}
|
||||||
|
>
|
||||||
|
<Monitor className="h-4 w-4" />
|
||||||
|
System
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={cn(settingRowClasses, "space-y-3")}>
|
<div className={cn(settingRowClasses, "space-y-3")}>
|
||||||
|
|||||||
11
tests/settings-panel.test.ts
Normal file
11
tests/settings-panel.test.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { describe, expect, test } from "bun:test";
|
||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
|
||||||
|
describe("settings panel", () => {
|
||||||
|
test("theme controls are available from settings after leaving the mobile header", () => {
|
||||||
|
const source = readFileSync("src/components/settings-panel.tsx", "utf8");
|
||||||
|
|
||||||
|
expect(source).toContain("Theme preference");
|
||||||
|
expect(source).toContain("setTheme(");
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user