SevdanurGenc's picture
Upload 300 files
9438a57 verified
Raw History Blame Contribute Delete
2.73 kB
import { createContext, useCallback, useContext, useEffect, useState, type ReactNode } from 'react';
import { useI18n } from './i18n';
/** Aydınlık / karanlık tema. İlk açılışta sistem ayarı izlenir; seçim yapılınca tarayıcıda hatırlanır. */
type Theme = 'light' | 'dark';
const KEY = 'testajani.theme';
const mq = () => window.matchMedia('(prefers-color-scheme: dark)');
function stored(): Theme | null {
try {
const v = window.localStorage.getItem(KEY);
return v === 'light' || v === 'dark' ? v : null;
} catch { return null; }
}
const Ctx = createContext<{ dark: boolean; toggle: () => void; choice: Theme | null; setChoice: (t: Theme) => void }>(
{ dark: false, toggle: () => {}, choice: null, setChoice: () => {} });
export function ThemeProvider({ children }: { children: ReactNode }) {
const [choice, setChoice] = useState<Theme | null>(stored);
const [sysDark, setSysDark] = useState(() => mq().matches);
useEffect(() => {
const m = mq();
const h = () => setSysDark(m.matches);
m.addEventListener('change', h);
return () => m.removeEventListener('change', h);
}, []);
const dark = choice ? choice === 'dark' : sysDark;
useEffect(() => {
// CSS, :root[data-theme] ile açık seçimi; yoksa sistem ayarını (prefers-color-scheme) kullanır
if (choice) document.documentElement.dataset.theme = choice;
else delete document.documentElement.dataset.theme;
}, [choice]);
const choose = useCallback((next: Theme) => {
setChoice(next);
try { window.localStorage.setItem(KEY, next); } catch { /* yoksay */ }
}, []);
const toggle = useCallback(() => choose(dark ? 'light' : 'dark'), [dark, choose]);
return <Ctx.Provider value={{ dark, toggle, choice, setChoice: choose }}>{children}</Ctx.Provider>;
}
export const useTheme = () => useContext(Ctx);
export function ThemeSwitch() {
const { dark, toggle } = useTheme();
const { t } = useI18n();
const label = dark ? t('theme.toLight') : t('theme.toDark');
return (
<button className="icon-btn theme-btn" onClick={toggle} title={label} aria-label={label}>
{dark ? (
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
<circle cx="12" cy="12" r="4.5" />
<path d="M12 2v2.5M12 19.5V22M2 12h2.5M19.5 12H22M4.9 4.9l1.8 1.8M17.3 17.3l1.8 1.8M4.9 19.1l1.8-1.8M17.3 6.7l1.8-1.8" />
</svg>
) : (
<svg width="16" height="16" viewBox="0 0 24 24" aria-hidden fill="none" stroke="currentColor" strokeWidth="2" strokeLinejoin="round">
<path d="M20.5 14.5A8.5 8.5 0 0 1 9.5 3.5a8.5 8.5 0 1 0 11 11z" />
</svg>
)}
</button>
);
}