Как сделать кастомные темы?
Написал hook useTheme
и у нас будет стандартные темы: dark и light. Но также я хочу добавить кнопка "Создать тему" и при клике на неё добавлять новую тему в custom-themes, по дефолту все темы которые мы добавляем - выключены. Если мы включили нашу custom-тему, нужно не добавлять атрибут data-theme
, а просто получить все то, что находится в details
и передать в setProperty
. Ключ - это у нас тип --background, --color
, а значение - цвет. Можете пожалуйста помочь с реализацией?))
Я предполагаю, что для каждой нашей темы нужно добавлять active: false
и при клике на отпрядённую тему менять состояние и проверять весь список наших тем, дабы определить какая включена и поменять.
use-theme.js
export const useTheme = () => {
// Получить системную тему пользователя
const getSystemTheme = () => window?.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
const [theme, setTheme] = useState(localStorage.getItem('app-theme') || getSystemTheme());
// Получить кастомные темы пользователя
const getCustomThemes = () => JSON.parse(localStorage.getItem('custom-themes')) || [];
// Добавить обьект темы в LocalStorage custom-themes
// Example: addCustomTheme({name: 'Название тема', details: [background: 'orange']})
const addCustomTheme = obj => {
if (getCustomThemes().find(t => t.name === obj.name)) return false;
localStorage.setItem(
'custom-themes',
JSON.stringify(
[
...getCustomThemes(),
{ ...obj, active: false }
]
)
);
}
useLayoutEffect(() => {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}, [theme]);
return { currentTheme: theme, setTheme, addCustomTheme };
}
Ответы (1 шт):
Вам нужно добавить функцию toggleCustomTheme:
import { useState, useLayoutEffect } from 'react';
export const useTheme = () => {
// Получить системную тему пользователя
const getSystemTheme = () =>
window?.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
const [theme, setTheme] = useState(localStorage.getItem('app-theme') || getSystemTheme());
const [customThemes, setCustomThemes] = useState(getCustomThemes());
// Получить кастомные темы пользователя
const getCustomThemes = () => JSON.parse(localStorage.getItem('custom-themes')) || [];
// Добавить обьект темы в LocalStorage custom-themes
const addCustomTheme = obj => {
if (customThemes.find(t => t.name === obj.name)) return false;
const newThemes = [
...customThemes,
{ ...obj, active: false }
];
localStorage.setItem('custom-themes', JSON.stringify(newThemes));
setCustomThemes(newThemes); // Обновить состояние на основе локального хранилища
};
// Включить кастомную тему
const toggleCustomTheme = (name) => {
const updatedThemes = customThemes.map(theme =>
theme.name === name ? { ...theme, active: !theme.active } : theme
);
localStorage.setItem('custom-themes', JSON.stringify(updatedThemes));
setCustomThemes(updatedThemes);
// Установить CSS-переменные для активной темы
const activeTheme = updatedThemes.find(t => t.active);
if (activeTheme) {
Object.entries(activeTheme.details).forEach(([key, value]) => {
document.documentElement.style.setProperty(key, value);
});
setTheme('custom'); // Устанавливаем специальное значение для темы
} else {
setTheme(getSystemTheme()); // Устанавливаем систему или прочую тему
}
};
useLayoutEffect(() => {
const defaultActiveTheme = customThemes.find(t => t.active);
if (defaultActiveTheme) {
Object.entries(defaultActiveTheme.details).forEach(([key, value]) => {
document.documentElement.style.setProperty(key, value);
});
setTheme('custom'); // Устанавливаем специальное значение для темы
} else {
document.documentElement.setAttribute('data-theme', theme);
localStorage.setItem('theme', theme);
}
}, [theme, customThemes]);
return { currentTheme: theme, setTheme, addCustomTheme, toggleCustomTheme, customThemes };
}