new theme switcher

This commit is contained in:
2025-09-28 23:51:51 +02:00
parent e33932c001
commit fc62ba5fad
5 changed files with 206 additions and 3 deletions

View File

@@ -22,12 +22,11 @@ export const ModeToggle = React.forwardRef<
ref={ref}
aria-label="Toogle theme"
{...props}
onClick={() => setTheme(theme === "light" ? "dark" : "light")}
>
onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
<SunIcon className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:rotate-90 dark:scale-0 text-foreground" />
<MoonIcon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100 text-foreground" />
</Button>
);
});
ModeToggle.displayName = "ModeToggle";
ModeToggle.displayName = "ModeToggle";

View File

@@ -0,0 +1,47 @@
// app/components/theme-switcher.tsx
"use client";
import { useState, useEffect } from "react";
import { useTheme } from "next-themes";
import { Sun, Moon } from "lucide-react";
import { Switch } from "@/components/ui/switch";
import { Tooltip, TooltipContent } from "./ui/tooltip";
import { TooltipTrigger } from "@radix-ui/react-tooltip";
export function ThemeSwitcher() {
const [mounted, setMounted] = useState(false);
const { theme, setTheme } = useTheme();
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return null;
}
const isDarkMode = theme === "dark";
const toggleTheme = (checked: boolean) => {
setTheme(checked ? "dark" : "light");
};
return (
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center space-x-2">
<Sun className="h-[1.2rem] w-[1.2rem] text-primary" />
<Switch
id="theme-switch"
checked={isDarkMode}
onCheckedChange={toggleTheme}
/>
<Moon className="h-[1.2rem] w-[1.2rem] text-primary" />
</div>
</TooltipTrigger>
<TooltipContent>
<p>Toggle Theme</p>
</TooltipContent>
</Tooltip>
);
}

31
components/ui/switch.tsx Normal file
View File

@@ -0,0 +1,31 @@
"use client"
import * as React from "react"
import * as SwitchPrimitive from "@radix-ui/react-switch"
import { cn } from "@/lib/utils"
function Switch({
className,
...props
}: React.ComponentProps<typeof SwitchPrimitive.Root>) {
return (
<SwitchPrimitive.Root
data-slot="switch"
className={cn(
"peer data-[state=checked]:bg-primary data-[state=unchecked]:bg-input focus-visible:border-ring focus-visible:ring-ring/50 dark:data-[state=unchecked]:bg-input/80 inline-flex h-[1.15rem] w-8 shrink-0 items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
className
)}
{...props}
>
<SwitchPrimitive.Thumb
data-slot="switch-thumb"
className={cn(
"bg-background dark:data-[state=unchecked]:bg-foreground dark:data-[state=checked]:bg-primary-foreground pointer-events-none block size-4 rounded-full ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0"
)}
/>
</SwitchPrimitive.Root>
)
}
export { Switch }

View File

@@ -11,8 +11,10 @@
"dependencies": {
"@radix-ui/react-avatar": "^1.1.10",
"@radix-ui/react-icons": "^1.3.2",
"@radix-ui/react-navigation-menu": "^1.2.14",
"@radix-ui/react-separator": "^1.1.7",
"@radix-ui/react-slot": "^1.2.3",
"@radix-ui/react-switch": "^1.2.6",
"@radix-ui/react-tooltip": "^1.2.8",
"@retorquere/bibtex-parser": "^9.0.21",
"@tailwindcss/typography": "^0.5.18",

124
pnpm-lock.yaml generated
View File

@@ -14,12 +14,18 @@ importers:
'@radix-ui/react-icons':
specifier: ^1.3.2
version: 1.3.2(react@19.1.1)
'@radix-ui/react-navigation-menu':
specifier: ^1.2.14
version: 1.2.14(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-separator':
specifier: ^1.1.7
version: 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-slot':
specifier: ^1.2.3
version: 1.2.3(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-switch':
specifier: ^1.2.6
version: 1.2.6(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-tooltip':
specifier: ^1.2.8
version: 1.2.8(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@@ -647,6 +653,19 @@ packages:
'@types/react-dom':
optional: true
'@radix-ui/react-collection@1.1.7':
resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-compose-refs@1.1.2':
resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==}
peerDependencies:
@@ -665,6 +684,15 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-direction@1.1.1':
resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-dismissable-layer@1.1.11':
resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==}
peerDependencies:
@@ -692,6 +720,19 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-navigation-menu@1.2.14':
resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-popper@1.2.8':
resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==}
peerDependencies:
@@ -766,6 +807,19 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-switch@1.2.6':
resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==}
peerDependencies:
'@types/react': '*'
'@types/react-dom': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@types/react-dom':
optional: true
'@radix-ui/react-tooltip@1.2.8':
resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==}
peerDependencies:
@@ -833,6 +887,15 @@ packages:
'@types/react':
optional: true
'@radix-ui/react-use-previous@1.1.1':
resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
peerDependencies:
'@types/react': '*'
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
peerDependenciesMeta:
'@types/react':
optional: true
'@radix-ui/react-use-rect@1.1.1':
resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==}
peerDependencies:
@@ -3692,6 +3755,18 @@ snapshots:
'@types/react': 19.1.13
'@types/react-dom': 19.1.9(@types/react@19.1.13)
'@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-slot': 1.2.3(@types/react@19.1.13)(react@19.1.1)
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
optionalDependencies:
'@types/react': 19.1.13
'@types/react-dom': 19.1.9(@types/react@19.1.13)
'@radix-ui/react-compose-refs@1.1.2(@types/react@19.1.13)(react@19.1.1)':
dependencies:
react: 19.1.1
@@ -3704,6 +3779,12 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.13
'@radix-ui/react-direction@1.1.1(@types/react@19.1.13)(react@19.1.1)':
dependencies:
react: 19.1.1
optionalDependencies:
'@types/react': 19.1.13
'@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
@@ -3728,6 +3809,28 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.13
'@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-collection': 1.1.7(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-direction': 1.1.1(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-id': 1.1.1(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
optionalDependencies:
'@types/react': 19.1.13
'@types/react-dom': 19.1.9(@types/react@19.1.13)
'@radix-ui/react-popper@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@floating-ui/react-dom': 2.1.6(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
@@ -3791,6 +3894,21 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.13
'@radix-ui/react-switch@1.2.6(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-context': 1.1.2(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.13)(react@19.1.1)
'@radix-ui/react-use-size': 1.1.1(@types/react@19.1.13)(react@19.1.1)
react: 19.1.1
react-dom: 19.1.1(react@19.1.1)
optionalDependencies:
'@types/react': 19.1.13
'@types/react-dom': 19.1.9(@types/react@19.1.13)
'@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.1.9(@types/react@19.1.13))(@types/react@19.1.13)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)':
dependencies:
'@radix-ui/primitive': 1.1.3
@@ -3852,6 +3970,12 @@ snapshots:
optionalDependencies:
'@types/react': 19.1.13
'@radix-ui/react-use-previous@1.1.1(@types/react@19.1.13)(react@19.1.1)':
dependencies:
react: 19.1.1
optionalDependencies:
'@types/react': 19.1.13
'@radix-ui/react-use-rect@1.1.1(@types/react@19.1.13)(react@19.1.1)':
dependencies:
'@radix-ui/rect': 1.1.1