Files
2026-06-06 15:33:01 +03:30

83 lines
2.8 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { SunIcon, MoonIcon } from '@heroicons/react/24/outline';
type Theme = 'light' | 'dark';
interface ThemeToggleProps {
/** When true, renders as a fixed floating pill (used on the app shell). */
floating?: boolean;
/** When true, shows a Persian text label next to the icon. */
withLabel?: boolean;
className?: string;
}
export default function ThemeToggle({ floating = false, withLabel, className = '' }: ThemeToggleProps) {
const [theme, setTheme] = useState<Theme>('light');
const [mounted, setMounted] = useState(false);
// Floating variant shows a label by default; inline variant is icon-only unless asked.
const showLabel = withLabel ?? floating;
useEffect(() => {
setMounted(true);
setTheme(document.documentElement.classList.contains('dark') ? 'dark' : 'light');
}, []);
const toggle = () => {
const next: Theme = theme === 'dark' ? 'light' : 'dark';
const root = document.documentElement;
if (next === 'dark') {
root.classList.add('dark');
} else {
root.classList.remove('dark');
}
try {
localStorage.setItem('theme', next);
} catch {
// ignore storage errors
}
setTheme(next);
};
const base =
'inline-flex items-center gap-2 border border-gray-200 bg-white text-gray-700 shadow-sm hover:bg-gray-50 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-offset-gray-900 transition-colors';
const shape = showLabel
? 'rounded-full px-4 py-2 text-sm font-medium'
: 'rounded-full justify-center h-10 w-10';
const floatingCls = floating ? 'fixed bottom-5 left-5 z-50' : '';
const isDark = theme === 'dark';
const label = isDark ? 'حالت روشن' : 'حالت تیره';
// Avoid hydration mismatch: render a neutral placeholder until mounted
if (!mounted) {
return (
<button
type="button"
aria-label="تغییر تم"
className={`${base} ${shape} ${floatingCls} ${className}`}
>
<SunIcon className="h-5 w-5" />
{showLabel && <span>تم</span>}
</button>
);
}
return (
<button
type="button"
onClick={toggle}
aria-label={label}
title={label}
className={`${base} ${shape} ${floatingCls} ${className}`}
>
{isDark ? <SunIcon className="h-5 w-5" /> : <MoonIcon className="h-5 w-5" />}
{showLabel && <span>{label}</span>}
</button>
);
}