Restore the direct light/dark toggle instead of a dropdown
This commit is contained in:
@@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { Moon, Sun } from 'lucide-react';
|
||||||
|
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
|
import { useUIStore } from '@/stores/uiStore';
|
||||||
|
|
||||||
|
// Single-click light/dark toggle. Color themes and corner radius live on the
|
||||||
|
// Appearance settings page instead of a dropdown.
|
||||||
|
export function ModeToggle() {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
const theme = useUIStore((s) => s.theme);
|
||||||
|
const setTheme = useUIStore((s) => s.setTheme);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}
|
||||||
|
aria-label={
|
||||||
|
theme === 'light'
|
||||||
|
? t('appearance.switchToDark', 'Switch to dark mode')
|
||||||
|
: t('appearance.switchToLight', 'Switch to light mode')
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{theme === 'light' ? <Moon className="h-4 w-4" /> : <Sun className="h-4 w-4" />}
|
||||||
|
</Button>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next';
|
|
||||||
import { Moon, Palette, Sun, Square, Radius } from 'lucide-react';
|
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuRadioGroup,
|
|
||||||
DropdownMenuRadioItem,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from '@/components/ui/dropdown-menu';
|
|
||||||
import { COLOR_THEMES, useUIStore, type ColorTheme, type Radius as RadiusValue, type Theme } from '@/stores/uiStore';
|
|
||||||
|
|
||||||
export function ThemeSwitcher() {
|
|
||||||
const { t } = useTranslation();
|
|
||||||
const theme = useUIStore((s) => s.theme);
|
|
||||||
const setTheme = useUIStore((s) => s.setTheme);
|
|
||||||
const colorTheme = useUIStore((s) => s.colorTheme);
|
|
||||||
const setColorTheme = useUIStore((s) => s.setColorTheme);
|
|
||||||
const radius = useUIStore((s) => s.radius);
|
|
||||||
const setRadius = useUIStore((s) => s.setRadius);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<DropdownMenu>
|
|
||||||
<DropdownMenuTrigger asChild>
|
|
||||||
<Button variant="ghost" size="icon" aria-label={t('appearance.label', 'Appearance')}>
|
|
||||||
<Palette className="h-4 w-4" />
|
|
||||||
</Button>
|
|
||||||
</DropdownMenuTrigger>
|
|
||||||
<DropdownMenuContent align="end" className="w-52">
|
|
||||||
<DropdownMenuLabel>{t('appearance.mode', 'Mode')}</DropdownMenuLabel>
|
|
||||||
<DropdownMenuRadioGroup value={theme} onValueChange={(value) => setTheme(value as Theme)}>
|
|
||||||
<DropdownMenuRadioItem value="light">
|
|
||||||
<Sun className="mr-2 h-4 w-4" />
|
|
||||||
{t('appearance.light', 'Light')}
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="dark">
|
|
||||||
<Moon className="mr-2 h-4 w-4" />
|
|
||||||
{t('appearance.dark', 'Dark')}
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
</DropdownMenuRadioGroup>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuLabel>{t('appearance.colorTheme', 'Color theme')}</DropdownMenuLabel>
|
|
||||||
<DropdownMenuRadioGroup value={colorTheme} onValueChange={(value) => setColorTheme(value as ColorTheme)}>
|
|
||||||
{COLOR_THEMES.map((entry) => (
|
|
||||||
<DropdownMenuRadioItem key={entry.value} value={entry.value}>
|
|
||||||
<span
|
|
||||||
className="mr-2 h-3.5 w-3.5 shrink-0 rounded-full border border-border"
|
|
||||||
style={{ background: entry.swatch }}
|
|
||||||
/>
|
|
||||||
{t(entry.labelKey, entry.fallback)}
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
))}
|
|
||||||
</DropdownMenuRadioGroup>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
<DropdownMenuLabel>{t('appearance.corners', 'Corners')}</DropdownMenuLabel>
|
|
||||||
<DropdownMenuRadioGroup value={radius} onValueChange={(value) => setRadius(value as RadiusValue)}>
|
|
||||||
<DropdownMenuRadioItem value="rounded">
|
|
||||||
<Radius className="mr-2 h-4 w-4" />
|
|
||||||
{t('appearance.rounded', 'Rounded')}
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
<DropdownMenuRadioItem value="square">
|
|
||||||
<Square className="mr-2 h-4 w-4" />
|
|
||||||
{t('appearance.square', 'Square')}
|
|
||||||
</DropdownMenuRadioItem>
|
|
||||||
</DropdownMenuRadioGroup>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu';
|
} from '@/components/ui/dropdown-menu';
|
||||||
import Logo from '@/components/common/Logo';
|
import Logo from '@/components/common/Logo';
|
||||||
import { ThemeSwitcher } from '@/components/common/ThemeSwitcher';
|
import { ModeToggle } from '@/components/common/ModeToggle';
|
||||||
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
|
||||||
import { EnterpriseUpsell } from '@/components/common/EnterpriseUpsell';
|
import { EnterpriseUpsell } from '@/components/common/EnterpriseUpsell';
|
||||||
import { findFirstAccessibleLinkInLayout, findFirstVisibleLinkInLayout, visibleLayouts } from '@/lib/layout';
|
import { findFirstAccessibleLinkInLayout, findFirstVisibleLinkInLayout, visibleLayouts } from '@/lib/layout';
|
||||||
@@ -122,7 +122,7 @@ export function TopBar() {
|
|||||||
|
|
||||||
<CommandPalette open={paletteOpen} onOpenChange={setPaletteOpen} />
|
<CommandPalette open={paletteOpen} onOpenChange={setPaletteOpen} />
|
||||||
|
|
||||||
<ThemeSwitcher />
|
<ModeToggle />
|
||||||
|
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
"mode": "Mode",
|
"mode": "Mode",
|
||||||
"rounded": "Rounded",
|
"rounded": "Rounded",
|
||||||
"square": "Square",
|
"square": "Square",
|
||||||
|
"switchToDark": "Switch to dark mode",
|
||||||
|
"switchToLight": "Switch to light mode",
|
||||||
"theme": {
|
"theme": {
|
||||||
"forest": "Forest",
|
"forest": "Forest",
|
||||||
"ocean": "Ocean",
|
"ocean": "Ocean",
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { ArrowRight, Loader2 } from 'lucide-react';
|
import { ArrowRight, Loader2 } from 'lucide-react';
|
||||||
|
|
||||||
import Logo from '@/components/common/Logo';
|
import Logo from '@/components/common/Logo';
|
||||||
import { ThemeSwitcher } from '@/components/common/ThemeSwitcher';
|
import { ModeToggle } from '@/components/common/ModeToggle';
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Input } from '@/components/ui/input';
|
import { Input } from '@/components/ui/input';
|
||||||
@@ -46,7 +46,7 @@ export default function LoginPage() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-content-background px-4">
|
<div className="flex min-h-screen items-center justify-center bg-content-background px-4">
|
||||||
<div className="fixed right-4 top-4 z-50">
|
<div className="fixed right-4 top-4 z-50">
|
||||||
<ThemeSwitcher />
|
<ModeToggle />
|
||||||
</div>
|
</div>
|
||||||
<Card className="w-full max-w-sm shadow-sm">
|
<Card className="w-full max-w-sm shadow-sm">
|
||||||
<CardHeader className="items-center text-center">
|
<CardHeader className="items-center text-center">
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2 } from 'lucide-react';
|
||||||
|
|
||||||
import { useAuthStore } from '@/stores/authStore';
|
import { useAuthStore } from '@/stores/authStore';
|
||||||
import { ThemeSwitcher } from '@/components/common/ThemeSwitcher';
|
import { ModeToggle } from '@/components/common/ModeToggle';
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
import { getBasePath } from '@/lib/basePath';
|
import { getBasePath } from '@/lib/basePath';
|
||||||
import { exchangeCode, getStoredOAuthData, clearStoredOAuthData, getOAuthRedirectUri } from '@/services/auth/oauth';
|
import { exchangeCode, getStoredOAuthData, clearStoredOAuthData, getOAuthRedirectUri } from '@/services/auth/oauth';
|
||||||
@@ -87,7 +87,7 @@ export default function OAuthCallback() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-background px-4">
|
<div className="flex min-h-screen items-center justify-center bg-background px-4">
|
||||||
<div className="fixed right-4 top-4 z-50">
|
<div className="fixed right-4 top-4 z-50">
|
||||||
<ThemeSwitcher />
|
<ModeToggle />
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full max-w-sm space-y-4 text-center">
|
<div className="w-full max-w-sm space-y-4 text-center">
|
||||||
<p className="text-sm text-destructive" role="alert">
|
<p className="text-sm text-destructive" role="alert">
|
||||||
@@ -104,7 +104,7 @@ export default function OAuthCallback() {
|
|||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center bg-background">
|
<div className="flex min-h-screen items-center justify-center bg-background">
|
||||||
<div className="fixed right-4 top-4 z-50">
|
<div className="fixed right-4 top-4 z-50">
|
||||||
<ThemeSwitcher />
|
<ModeToggle />
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col items-center gap-3">
|
<div className="flex flex-col items-center gap-3">
|
||||||
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
||||||
|
|||||||
Reference in New Issue
Block a user