feat: update appearance settings page
This commit is contained in:
parent
b489296b08
commit
04798b5adb
@ -1,14 +1,11 @@
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { useUserConfig, useUpdateAppearance } from "./hooks";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Switch } from "@/components/ui/switch";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
|
||||
const themes = [
|
||||
{ value: "light", label: "Light" },
|
||||
{ value: "dark", label: "Dark" },
|
||||
{ value: "system", label: "System" },
|
||||
];
|
||||
import { themePresets, applyTheme, getThemeById, getSavedThemeId, saveThemeId } from "@/lib/theme";
|
||||
import { Check } from "lucide-react";
|
||||
|
||||
const codeThemes = [
|
||||
{ value: "github-light", label: "GitHub Light" },
|
||||
@ -26,11 +23,62 @@ const densities = [
|
||||
{ value: "comfortable", label: "Comfortable" },
|
||||
];
|
||||
|
||||
function ThemeSwatch({ preset }: { preset: (typeof themePresets)[0] }) {
|
||||
const c = preset.colors;
|
||||
return (
|
||||
<div
|
||||
className="relative h-20 w-full overflow-hidden rounded-xl border"
|
||||
style={{
|
||||
background: c.background,
|
||||
borderColor: c.border,
|
||||
}}
|
||||
>
|
||||
<div className="absolute inset-0 flex flex-col p-2.5 gap-1.5">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div
|
||||
className="h-3.5 w-3.5 rounded-md"
|
||||
style={{ background: c.primary }}
|
||||
/>
|
||||
<div
|
||||
className="h-1.5 w-12 rounded-full"
|
||||
style={{ background: c.mutedForeground, opacity: 0.5 }}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
<div
|
||||
className="h-5 flex-1 rounded-md"
|
||||
style={{ background: c.card, border: `1px solid ${c.border}` }}
|
||||
/>
|
||||
<div
|
||||
className="h-5 flex-1 rounded-md"
|
||||
style={{ background: c.muted }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default function SettingsAppearancePage() {
|
||||
const { data: config, isLoading } = useUserConfig();
|
||||
const update = useUpdateAppearance();
|
||||
const appearance = config?.appearance;
|
||||
|
||||
const [activeTheme, setActiveTheme] = useState(getSavedThemeId());
|
||||
|
||||
useEffect(() => {
|
||||
setActiveTheme(getSavedThemeId());
|
||||
}, []);
|
||||
|
||||
const handleThemeChange = useCallback((id: string) => {
|
||||
setActiveTheme(id);
|
||||
const preset = getThemeById(id);
|
||||
if (preset) {
|
||||
applyTheme(preset);
|
||||
saveThemeId(id);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="px-8 py-10">
|
||||
<h1 className="text-xl font-heading font-bold text-foreground">Appearance</h1>
|
||||
@ -44,28 +92,57 @@ export default function SettingsAppearancePage() {
|
||||
</div>
|
||||
) : (
|
||||
<div className="mt-8 space-y-6">
|
||||
{/* Color Theme */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Theme</CardTitle>
|
||||
<CardDescription>Select your preferred color theme</CardDescription>
|
||||
<CardTitle>Color Theme</CardTitle>
|
||||
<CardDescription>Choose a preset color palette for the entire interface</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Select
|
||||
value={appearance?.theme ?? "system"}
|
||||
onValueChange={(v) => update.mutate({ theme: v })}
|
||||
>
|
||||
<SelectTrigger className="w-48">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{themes.map((t) => (
|
||||
<SelectItem key={t.value} value={t.value}>{t.label}</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3 lg:grid-cols-4">
|
||||
{themePresets.map((preset) => {
|
||||
const isActive = activeTheme === preset.id;
|
||||
return (
|
||||
<button
|
||||
key={preset.id}
|
||||
onClick={() => handleThemeChange(preset.id)}
|
||||
className="group relative flex flex-col gap-2 rounded-2xl border p-3 text-left transition-all hover:border-ring/40 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/30"
|
||||
style={{
|
||||
borderColor: isActive ? preset.colors.ring : preset.colors.border,
|
||||
background: isActive ? preset.colors.muted : preset.colors.card,
|
||||
}}
|
||||
>
|
||||
{isActive && (
|
||||
<div
|
||||
className="absolute top-2.5 right-2.5 grid size-5 place-items-center rounded-full"
|
||||
style={{ background: preset.colors.primary }}
|
||||
>
|
||||
<Check className="size-3" style={{ color: preset.colors.primaryForeground }} />
|
||||
</div>
|
||||
)}
|
||||
<ThemeSwatch preset={preset} />
|
||||
<div className="flex flex-col gap-0.5">
|
||||
<span
|
||||
className="text-[13px] font-semibold"
|
||||
style={{ color: preset.colors.foreground }}
|
||||
>
|
||||
{preset.name}
|
||||
</span>
|
||||
<span
|
||||
className="text-[11px] leading-tight"
|
||||
style={{ color: preset.colors.mutedForeground }}
|
||||
>
|
||||
{preset.description}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Code Theme */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Code Theme</CardTitle>
|
||||
@ -88,6 +165,7 @@ export default function SettingsAppearancePage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Layout Density */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Layout Density</CardTitle>
|
||||
@ -110,6 +188,7 @@ export default function SettingsAppearancePage() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Editor */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Editor</CardTitle>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user