|
|
@ -1,6 +1,7 @@ |
|
|
import { useTheme } from "@core/hooks/useTheme.ts"; |
|
|
import { useTheme } from "@core/hooks/useTheme.ts"; |
|
|
import { cn } from "@core/utils/cn.ts"; |
|
|
import { cn } from "@core/utils/cn.ts"; |
|
|
import { Monitor, Moon, Sun } from "lucide-react"; |
|
|
import { Monitor, Moon, Sun } from "lucide-react"; |
|
|
|
|
|
import { useEffect, useState } from "react"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import { useTranslation } from "react-i18next"; |
|
|
import { Button } from "./UI/Button.tsx"; |
|
|
import { Button } from "./UI/Button.tsx"; |
|
|
import { Subtle } from "./UI/Typography/Subtle.tsx"; |
|
|
import { Subtle } from "./UI/Typography/Subtle.tsx"; |
|
|
@ -12,10 +13,14 @@ interface ThemeSwitcherProps { |
|
|
disableHover?: boolean; |
|
|
disableHover?: boolean; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const TOOLTIP_TIMEOUT = 2000; // 2 seconds
|
|
|
|
|
|
|
|
|
export default function ThemeSwitcher({ |
|
|
export default function ThemeSwitcher({ |
|
|
className: passedClassName = "", |
|
|
className: passedClassName = "", |
|
|
disableHover = false, |
|
|
disableHover = false, |
|
|
}: ThemeSwitcherProps) { |
|
|
}: ThemeSwitcherProps) { |
|
|
|
|
|
const [showTooltip, setShowTooltip] = useState(false); |
|
|
|
|
|
|
|
|
const { preference, setPreference } = useTheme(); |
|
|
const { preference, setPreference } = useTheme(); |
|
|
const { t } = useTranslation("ui"); |
|
|
const { t } = useTranslation("ui"); |
|
|
|
|
|
|
|
|
@ -35,8 +40,10 @@ export default function ThemeSwitcher({ |
|
|
const toggleTheme = () => { |
|
|
const toggleTheme = () => { |
|
|
const preferences: ThemePreference[] = ["light", "dark", "system"]; |
|
|
const preferences: ThemePreference[] = ["light", "dark", "system"]; |
|
|
const currentIndex = preferences.indexOf(preference); |
|
|
const currentIndex = preferences.indexOf(preference); |
|
|
const nextPreference = preferences[(currentIndex + 1) % preferences.length]; |
|
|
const nextPreference = |
|
|
|
|
|
preferences[(currentIndex + 1) % preferences.length] ?? "system"; |
|
|
setPreference(nextPreference); |
|
|
setPreference(nextPreference); |
|
|
|
|
|
setShowTooltip(true); |
|
|
}; |
|
|
}; |
|
|
|
|
|
|
|
|
const preferenceDisplayMap: Record<ThemePreference, string> = { |
|
|
const preferenceDisplayMap: Record<ThemePreference, string> = { |
|
|
@ -47,6 +54,18 @@ export default function ThemeSwitcher({ |
|
|
|
|
|
|
|
|
const currentDisplayPreference = preferenceDisplayMap[preference]; |
|
|
const currentDisplayPreference = preferenceDisplayMap[preference]; |
|
|
|
|
|
|
|
|
|
|
|
useEffect(() => { |
|
|
|
|
|
if (!showTooltip) { |
|
|
|
|
|
return; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const timeout = setTimeout(() => { |
|
|
|
|
|
setShowTooltip(false); |
|
|
|
|
|
}, TOOLTIP_TIMEOUT); |
|
|
|
|
|
|
|
|
|
|
|
return () => clearTimeout(timeout); |
|
|
|
|
|
}, [showTooltip]); |
|
|
|
|
|
|
|
|
return ( |
|
|
return ( |
|
|
<Button |
|
|
<Button |
|
|
variant="ghost" |
|
|
variant="ghost" |
|
|
@ -65,12 +84,12 @@ export default function ThemeSwitcher({ |
|
|
<span |
|
|
<span |
|
|
data-label="theme-preference-tooltip" |
|
|
data-label="theme-preference-tooltip" |
|
|
className={cn( |
|
|
className={cn( |
|
|
"transition-opacity duration-150", |
|
|
"transition-opacity duration-150 hidden", |
|
|
"block absolute w-max max-w-xs", |
|
|
"block absolute w-max max-w-xs", |
|
|
"p-1 text-xs text-white dark:text-black bg-black dark:bg-white", |
|
|
"p-1 text-xs text-white dark:text-black bg-black dark:bg-white", |
|
|
"rounded-md shadow-lg", |
|
|
"rounded-md shadow-lg", |
|
|
"left-1/2 -translate-x-1/2 -top-8", |
|
|
"left-1/2 -translate-x-1/2 -top-8", |
|
|
"opacity-0", |
|
|
showTooltip ? "visible" : "hidden opacity-0", |
|
|
)} |
|
|
)} |
|
|
> |
|
|
> |
|
|
{currentDisplayPreference} |
|
|
{currentDisplayPreference} |
|
|
|