209 lines
5.9 KiB
TypeScript
209 lines
5.9 KiB
TypeScript
"use client";
|
|
|
|
import {
|
|
PALETTES,
|
|
THEME_MODES,
|
|
FONT_PAIRS,
|
|
DENSITIES,
|
|
DASH_LAYOUTS,
|
|
CAL_VIEWS,
|
|
NAV_STYLES,
|
|
} from "@/modules/_core/themes";
|
|
import type {
|
|
Palette,
|
|
ThemeMode,
|
|
FontPair,
|
|
Density,
|
|
DashLayout,
|
|
CalView,
|
|
NavStyle,
|
|
} from "@/modules/_core/themes";
|
|
import { useTheme } from "@/hooks/use-theme";
|
|
import { Label } from "@/components/ui/label";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
import { Button } from "@/components/ui/button";
|
|
import { cn } from "@/lib/utils";
|
|
|
|
interface Props {
|
|
initialPalette?: Palette;
|
|
initialMode?: ThemeMode;
|
|
initialFontPair?: FontPair;
|
|
initialDensity?: Density;
|
|
initialDashLayout?: DashLayout;
|
|
initialCalView?: CalView;
|
|
initialNavStyle?: NavStyle;
|
|
signedIn?: boolean;
|
|
}
|
|
|
|
export function ThemePicker({
|
|
initialPalette = "clay",
|
|
initialMode = "system",
|
|
initialFontPair = "serif-sans",
|
|
initialDensity = "regular",
|
|
initialDashLayout = "classic",
|
|
initialCalView = "month",
|
|
initialNavStyle = "rail-desktop",
|
|
signedIn = false,
|
|
}: Props) {
|
|
const t = useTheme(
|
|
{
|
|
palette: initialPalette,
|
|
mode: initialMode,
|
|
fontPair: initialFontPair,
|
|
density: initialDensity,
|
|
dashLayout: initialDashLayout,
|
|
calView: initialCalView,
|
|
navStyle: initialNavStyle,
|
|
},
|
|
signedIn,
|
|
);
|
|
|
|
return (
|
|
<div className="space-y-5">
|
|
<div className="space-y-1.5">
|
|
<Label>Theme</Label>
|
|
<Select value={t.palette} onValueChange={(v) => t.setPalette(v as Palette)}>
|
|
<SelectTrigger className="w-56">
|
|
<div className="flex items-center gap-2 min-w-0">
|
|
<span
|
|
className="inline-block w-5 h-4 rounded-sm shrink-0 overflow-hidden border-[0.5px]"
|
|
style={{
|
|
background: `linear-gradient(to bottom, ${
|
|
PALETTES.find((p) => p.id === t.palette)?.paper ?? "#fff"
|
|
} 55%, ${PALETTES.find((p) => p.id === t.palette)?.hex ?? "#888"} 55%)`,
|
|
borderColor: "var(--hair-2)",
|
|
}}
|
|
/>
|
|
<SelectValue />
|
|
</div>
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{PALETTES.map((p) => (
|
|
<SelectItem key={p.id} value={p.id}>
|
|
<div className="flex items-center justify-between gap-6 w-40">
|
|
<span>{p.label}</span>
|
|
<span
|
|
className="inline-block w-5 h-4 rounded-sm shrink-0 overflow-hidden border-[0.5px]"
|
|
style={{
|
|
background: `linear-gradient(to bottom, ${p.paper} 55%, ${p.hex} 55%)`,
|
|
borderColor: "var(--hair-2)",
|
|
}}
|
|
/>
|
|
</div>
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="space-y-1.5">
|
|
<Label>Mode</Label>
|
|
<div className="flex gap-1">
|
|
{THEME_MODES.map((m) => (
|
|
<Button
|
|
key={m.id}
|
|
variant="outline"
|
|
size="sm"
|
|
className={cn(t.mode === m.id && "bg-primary text-primary-foreground")}
|
|
onClick={() => t.setMode(m.id)}
|
|
>
|
|
{m.label}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1.5">
|
|
<Label>Type pairing</Label>
|
|
<Select value={t.fontPair} onValueChange={(v) => t.setFontPair(v as FontPair)}>
|
|
<SelectTrigger className="w-72">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{FONT_PAIRS.map((f) => (
|
|
<SelectItem key={f.id} value={f.id}>
|
|
{f.label}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="space-y-1.5">
|
|
<Label>Density</Label>
|
|
<div className="flex gap-1">
|
|
{DENSITIES.map((d) => (
|
|
<Button
|
|
key={d.id}
|
|
variant="outline"
|
|
size="sm"
|
|
className={cn(t.density === d.id && "bg-primary text-primary-foreground")}
|
|
onClick={() => t.setDensity(d.id)}
|
|
>
|
|
{d.label}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1.5">
|
|
<Label>Dashboard layout</Label>
|
|
<Select value={t.dashLayout} onValueChange={(v) => t.setDashLayout(v as DashLayout)}>
|
|
<SelectTrigger className="w-72">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{DASH_LAYOUTS.map((d) => (
|
|
<SelectItem key={d.id} value={d.id}>
|
|
{d.label}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
|
|
<div className="space-y-1.5">
|
|
<Label>Calendar default</Label>
|
|
<div className="flex gap-1">
|
|
{CAL_VIEWS.map((c) => (
|
|
<Button
|
|
key={c.id}
|
|
variant="outline"
|
|
size="sm"
|
|
className={cn(t.calView === c.id && "bg-primary text-primary-foreground")}
|
|
onClick={() => t.setCalView(c.id)}
|
|
>
|
|
{c.label}
|
|
</Button>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="space-y-1.5">
|
|
<Label>Navigation</Label>
|
|
<Select value={t.navStyle} onValueChange={(v) => t.setNavStyle(v as NavStyle)}>
|
|
<SelectTrigger className="w-72">
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{NAV_STYLES.map((n) => (
|
|
<SelectItem key={n.id} value={n.id}>
|
|
{n.label}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
<p className="text-xs text-muted-foreground">
|
|
Mobile (under 760px) always uses bottom nav + FAB.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|