Removed MuiColorSchemeSync: it called MUI setMode which ALSO wrote data-theme, fighting ThemeContext (the other writer) → theme flip-flop loop. ThemeContext is now the sole owner; MUI's cssVariables CSS (scoped by [data-theme]) applies from the attribute, no JS bridge needed. New animated ThemeToggle kit button (sun/moon crossfade+rotate) replaces the static AppShell toggle. DashAttendanceToday avatars now solid-fill + white initials (matches StatCard/activity badges). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
58 lines
1.6 KiB
TypeScript
58 lines
1.6 KiB
TypeScript
import IconButton from "@mui/material/IconButton";
|
|
import { AnimatePresence, motion } from "framer-motion";
|
|
import { useTheme } from "../../context/ThemeContext";
|
|
|
|
const sun = (
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
>
|
|
<circle cx="12" cy="12" r="5" />
|
|
<path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42" />
|
|
</svg>
|
|
);
|
|
|
|
const moon = (
|
|
<svg
|
|
width="20"
|
|
height="20"
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
>
|
|
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
|
|
</svg>
|
|
);
|
|
|
|
/**
|
|
* Animated dark/light theme toggle: the sun/moon icon crossfades + rotates on
|
|
* switch. Reads ThemeContext (the single owner of the `data-theme` attribute).
|
|
*/
|
|
export default function ThemeToggle() {
|
|
const { theme, toggleTheme } = useTheme();
|
|
const isDark = theme === "dark";
|
|
const label = isDark ? "Světlý režim" : "Tmavý režim";
|
|
|
|
return (
|
|
<IconButton onClick={toggleTheme} aria-label={label} title={label}>
|
|
<AnimatePresence mode="wait" initial={false}>
|
|
<motion.span
|
|
key={theme}
|
|
initial={{ rotate: -90, opacity: 0, scale: 0.5 }}
|
|
animate={{ rotate: 0, opacity: 1, scale: 1 }}
|
|
exit={{ rotate: 90, opacity: 0, scale: 0.5 }}
|
|
transition={{ duration: 0.25, ease: [0.4, 0, 0.2, 1] }}
|
|
style={{ display: "inline-flex" }}
|
|
>
|
|
{isDark ? moon : sun}
|
|
</motion.span>
|
|
</AnimatePresence>
|
|
</IconButton>
|
|
);
|
|
}
|