Files
tkmind_go/ui/desktop/src/components/GooseSidebar/ThemeSelector.tsx
T
Zane 77ea27f5f5 UI update with sidebar and settings tabs (#3288)
Co-authored-by: Nahiyan Khan <nahiyan@squareup.com>
Co-authored-by: Taylor Ho <taylorkmho@gmail.com>
Co-authored-by: Lily Delalande <119957291+lily-de@users.noreply.github.com>
Co-authored-by: Spence <spencrmartin@gmail.com>
Co-authored-by: spencrmartin <spencermartin@squareup.com>
Co-authored-by: Judson Stephenson <Jud@users.noreply.github.com>
Co-authored-by: Max Novich <mnovich@squareup.com>
Co-authored-by: Best Codes <106822363+The-Best-Codes@users.noreply.github.com>
Co-authored-by: caroline-a-mckenzie <cmckenzie@squareup.com>
Co-authored-by: Michael Neale <michael.neale@gmail.com>
2025-07-15 17:24:41 -07:00

136 lines
4.7 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { Moon, Sliders, Sun } from 'lucide-react';
import { Button } from '../ui/button';
interface ThemeSelectorProps {
className?: string;
hideTitle?: boolean;
horizontal?: boolean;
}
const ThemeSelector: React.FC<ThemeSelectorProps> = ({
className = '',
hideTitle = false,
horizontal = false,
}) => {
const [themeMode, setThemeMode] = useState<'light' | 'dark' | 'system'>(() => {
const savedUseSystemTheme = localStorage.getItem('use_system_theme') === 'true';
if (savedUseSystemTheme) {
return 'system';
}
const savedTheme = localStorage.getItem('theme');
return savedTheme === 'dark' ? 'dark' : 'light';
});
const [isDarkMode, setDarkMode] = useState(() => {
// First check localStorage to determine the intended theme
const savedUseSystemTheme = localStorage.getItem('use_system_theme') === 'true';
const savedTheme = localStorage.getItem('theme');
if (savedUseSystemTheme) {
// Use system preference
const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
return systemPrefersDark;
} else if (savedTheme) {
// Use saved theme preference
return savedTheme === 'dark';
} else {
// Fallback: check current DOM state to maintain consistency
return document.documentElement.classList.contains('dark');
}
});
useEffect(() => {
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
const handleThemeChange = (e: { matches: boolean }) => {
if (themeMode === 'system') {
setDarkMode(e.matches);
}
};
mediaQuery.addEventListener('change', handleThemeChange);
if (themeMode === 'system') {
setDarkMode(mediaQuery.matches);
localStorage.setItem('use_system_theme', 'true');
} else {
setDarkMode(themeMode === 'dark');
localStorage.setItem('use_system_theme', 'false');
localStorage.setItem('theme', themeMode);
}
return () => mediaQuery.removeEventListener('change', handleThemeChange);
}, [themeMode]);
useEffect(() => {
if (isDarkMode) {
document.documentElement.classList.add('dark');
document.documentElement.classList.remove('light');
} else {
document.documentElement.classList.remove('dark');
document.documentElement.classList.add('light');
}
}, [isDarkMode]);
const handleThemeChange = (newTheme: 'light' | 'dark' | 'system') => {
setThemeMode(newTheme);
};
return (
<div className={`${!horizontal ? 'px-1 py-2 space-y-2' : ''} ${className}`}>
{!hideTitle && <div className="text-xs text-text-default px-3">Theme</div>}
<div
className={`${horizontal ? 'flex' : 'grid grid-cols-3'} gap-1 ${!horizontal ? 'px-3' : ''}`}
>
<Button
data-testid="light-mode-button"
onClick={() => handleThemeChange('light')}
className={`flex items-center justify-center gap-1 p-2 rounded-md border transition-colors text-xs ${
themeMode === 'light'
? 'bg-background-accent text-text-on-accent border-border-accent hover:!bg-background-accent hover:!text-text-on-accent'
: 'border-border-default hover:!bg-background-muted text-text-muted hover:text-text-default'
}`}
variant="ghost"
size="sm"
>
<Sun className="h-3 w-3" />
<span>Light</span>
</Button>
<Button
data-testid="dark-mode-button"
onClick={() => handleThemeChange('dark')}
className={`flex items-center justify-center gap-1 p-2 rounded-md border transition-colors text-xs ${
themeMode === 'dark'
? 'bg-background-accent text-text-on-accent border-border-accent hover:!bg-background-accent hover:!text-text-on-accent'
: 'border-border-default hover:!bg-background-muted text-text-muted hover:text-text-default'
}`}
variant="ghost"
size="sm"
>
<Moon className="h-3 w-3" />
<span>Dark</span>
</Button>
<Button
data-testid="system-mode-button"
onClick={() => handleThemeChange('system')}
className={`flex items-center justify-center gap-1 p-2 rounded-md border transition-colors text-xs ${
themeMode === 'system'
? 'bg-background-accent text-text-on-accent border-border-accent hover:!bg-background-accent hover:!text-text-on-accent'
: 'border-border-default hover:!bg-background-muted text-text-muted hover:text-text-default'
}`}
variant="ghost"
size="sm"
>
<Sliders className="h-3 w-3" />
<span>System</span>
</Button>
</div>
</div>
);
};
export default ThemeSelector;