import { useColorMode } from '@chakra-ui/react'; import { useEffect, useRef } from 'react'; import { useDispatch } from 'react-redux'; import { ThemeType } from '../types/theme'; import { LIGHT_THEME, DARK_THEME, PINK_THEME, BLUE_THEME, GREEN_THEME, PURPLE_THEME, THEMES } from '../utils/themes'; import { useAppSelector } from '../__data__/store'; import { setTheme, cycleNextTheme as cycleTheme, selectCurrentTheme, selectIsLightVariant, selectIsDarkVariant } from '../__data__/slices/theme'; // Маппинг тем к базовым режимам Chakra UI const themeToColorMode: Record = { [LIGHT_THEME]: 'light', [DARK_THEME]: 'dark', [PINK_THEME]: 'light', [BLUE_THEME]: 'light', [GREEN_THEME]: 'light', [PURPLE_THEME]: 'dark' }; export const useThemeManager = () => { // Получаем базовый функционал переключения темы из Chakra UI const { colorMode, setColorMode } = useColorMode(); // Используем Redux для управления темой const dispatch = useDispatch(); const currentTheme = useAppSelector(selectCurrentTheme); const isLightVariant = useAppSelector(selectIsLightVariant); const isDarkVariant = useAppSelector(selectIsDarkVariant); // Ref для хранения observer const observerRef = useRef(null); // Функция для применения классов и атрибутов темы const applyThemeToDOM = () => { if (typeof document === 'undefined') return; // Удаляем все классы тем document.documentElement.classList.remove(...THEMES); // Добавляем класс текущей темы document.documentElement.classList.add(currentTheme); // Также устанавливаем data-theme атрибут для использования в CSS document.documentElement.setAttribute('data-theme', currentTheme); // Устанавливаем специальный флаг, что тема установлена нами document.documentElement.setAttribute('data-custom-theme-source', 'redux'); }; // Эффект для применения дополнительных классов к документу в зависимости от выбранной темы // и создания MutationObserver для отслеживания изменений useEffect(() => { if (typeof document === 'undefined' || typeof window === 'undefined') return; // Применяем тему к DOM applyThemeToDOM(); // Синхронизируем с Chakra UI для светлой/темной темы const requiredColorMode = themeToColorMode[currentTheme]; if (colorMode !== requiredColorMode) { setColorMode(requiredColorMode); } // Создаем MutationObserver для отслеживания изменений атрибута data-theme if (!observerRef.current) { observerRef.current = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if ( mutation.type === 'attributes' && mutation.attributeName === 'data-theme' && document.documentElement.getAttribute('data-theme') !== currentTheme ) { // Если атрибут был изменен не нами, восстанавливаем его applyThemeToDOM(); } }); }); // Начинаем наблюдение за изменениями атрибута data-theme observerRef.current.observe(document.documentElement, { attributes: true, attributeFilter: ['data-theme'] }); } // Для дополнительной защиты устанавливаем интервал для проверки и восстановления атрибута const intervalId = setInterval(() => { if (document.documentElement.getAttribute('data-theme') !== currentTheme) { applyThemeToDOM(); } }, 500); // Отключаем observer и интервал при размонтировании компонента return () => { if (observerRef.current) { observerRef.current.disconnect(); } clearInterval(intervalId); }; }, [currentTheme, setColorMode, colorMode]); // Функция для изменения темы const changeTheme = (theme: ThemeType) => { dispatch(setTheme(theme)); }; // Функция для последовательного циклического переключения тем const cycleNextTheme = () => { dispatch(cycleTheme()); }; return { currentTheme, changeTheme, cycleNextTheme, isLightVariant, isDarkVariant, }; };