All checks were successful
platform/bro-js/journal.pl/pipeline/head This commit looks good
132 lines
4.8 KiB
TypeScript
132 lines
4.8 KiB
TypeScript
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,
|
||
};
|
||
};
|