This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
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<ThemeType, 'light' | 'dark'> = {
|
||||
[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<MutationObserver | null>(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,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user