|
|
3 veckor sedan | |
|---|---|---|
| .. | ||
| README.md | 3 veckor sedan | |
| index.coffee | 3 veckor sedan | |
| index.pug | 3 veckor sedan | |
| index.styl | 3 veckor sedan | |
ThemeToggle - компонент для переключения между светлой и темной темами в приложении. Сохраняет выбор пользователя в localStorage, синхронизирует состояние между вкладками и обеспечивает плавные переходы между темами.
https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/src/master/vue/app/shared/ThemeToggle/
# В основном файле приложения (app/temp.coffee)
components:
'themetoggle': require 'app/shared/ThemeToggle'
//- Минимальное использование
themetoggle
//- С кастомными классами
themetoggle(class="ml-4")
//- В навигационной панели
div(class="flex items-center space-x-4")
language-switcher
themetoggle
size (опциональный)String'md''sm', 'md', 'lg'Пример:
themetoggle(size="sm")
themetoggle(size="lg")
variant (опциональный)String'default''default', 'minimal', 'icon-only'Пример:
themetoggle(variant="minimal")
themetoggle(variant="icon-only")
showLabels (опциональный)BooleantrueПример:
themetoggle(:showLabels="false")
labels (опциональный)Object{ light: 'Светлая', dark: 'Тёмная', system: 'Системная' }Пример:
themetoggle(:labels="{ light: 'Light', dark: 'Dark', system: 'Auto' }")
storageKey (опциональный)String'borbad-theme'Пример:
themetoggle(storageKey="my-app-theme")
class (опциональный)String | Object | Array''Пример:
themetoggle(class="border border-gray-300 rounded-lg")
@change{ theme: String, previousTheme: String }Пример:
themetoggle(@change="handleThemeChange")
@init{ theme: String }Пример:
themetoggle(@init="handleThemeInit")
[light-icon]Пример:
themetoggle
template([light-icon])
svig(class="w-4 h-4" ...) ...
[dark-icon]Пример:
themetoggle
template([dark-icon])
svig(class="w-4 h-4" ...) ...
[system-icon]Пример:
themetoggle
template([system-icon])
svig(class="w-4 h-4" ...) ...
lightdarksystemprefers-color-scheme media query//- Стандартный переключатель
themetoggle
//- Только иконки
themetoggle(variant="icon-only")
//- Минималистичный вариант
themetoggle(variant="minimal")
//- Разные размеры
div(class="flex space-x-2")
themetoggle(size="sm")
themetoggle(size="md")
themetoggle(size="lg")
themetoggle
template([light-icon])
div(class="w-4 h-4 bg-yellow-400 rounded-full")
template([dark-icon])
div(class="w-4 h-4 bg-purple-600 rounded-full")
template([system-icon])
div(class="w-4 h-4 bg-gray-500 rounded-full")
themetoggle(
:labels="{
light: '☀️ Светлая',
dark: '🌙 Тёмная',
system: '💻 Система'
}"
)
header(class="bg-white dark:bg-gray-800 shadow-sm")
div(class="container mx-auto px-4")
div(class="flex justify-between items-center py-4")
//- Логотип
app-link(to="/" class="text-xl font-bold") Борбад
//- Навигация
nav(class="flex items-center space-x-6")
app-link(to="/events") Мероприятия
app-link(to="/blog") Блог
//- Переключатель темы
themetoggle(
variant="icon-only"
class="text-gray-600 dark:text-gray-300"
)
div(class="mobile-menu bg-white dark:bg-gray-800 p-4")
div(class="space-y-4")
app-link(to="/" class="block") Главная
app-link(to="/events" class="block") Мероприятия
app-link(to="/blog" class="block") Блог
//- Переключатель темы в мобильном меню
div(class="pt-4 border-t dark:border-gray-700")
themetoggle(
variant="minimal"
:showLabels="true"
class="w-full"
)
themetoggle(
@change="onThemeChange"
@init="onThemeInit"
)
//- В methods компонента
methods:
onThemeChange: ({ theme, previousTheme }) ->
debug.log "Тема изменена: "+previousTheme+" -> "+theme
# Можно добавить аналитику
analytics.track('theme_changed', {
new_theme: theme,
previous_theme: previousTheme
})
onThemeInit: ({ theme }) ->
debug.log "Тема инициализирована: "+theme
# Через глобальный объект
currentTheme = _.appState?.currentTheme
# Через EventBus
EventBus.on 'theme_changed', (theme) ->
debug.log "Текущая тема: "+theme
# Через глобальный объект
_.setTheme('dark')
# Через EventBus
EventBus.emit('set_theme', 'light')
# В компоненте
beforeMount: ->
EventBus.on 'theme_changed', @handleThemeChange
methods:
handleThemeChange: (theme) ->
# Обновляем стили компонента
@updateComponentStyles(theme)
Компонент использует стандартные CSS-переменные Tailwind:
:root {
--color-bg-primary: #ffffff;
--color-bg-secondary: #f7fafc;
--color-text-primary: #2d3748;
}
@media (prefers-color-scheme: dark) {
:root {
--color-bg-primary: #1a202c;
--color-bg-secondary: #2d3748;
--color-text-primary: #f7fafc;
}
}
// Переопределение стандартных стилей
.theme-toggle--custom
@apply bg-gradient-to-r from-blue-500 to-purple-600 text-white
.theme-toggle__button
@apply hover:from-blue-600 hover:to-purple-700
.theme-toggle__button--active
@apply from-blue-700 to-purple-800
// Плавные переходы
.theme-transition
transition: all 0.3s ease-in-out
// Анимация переключения
@keyframes theme-switch
0%
opacity: 0
transform: scale(0.8)
100%
opacity: 1
transform: scale(1)
.theme-toggle--animating
animation: theme-switch 0.3s ease-in-out
module.exports = {
darkMode: 'class', // или 'media' для системной темы
// ...
}
div(class="bg-white dark:bg-gray-800 text-gray-900 dark:text-white")
h1(class="text-2xl font-bold") Заголовок
p(class="text-gray-600 dark:text-gray-300") Текст
# Использует matchMedia для определения системных предпочтений
systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
# Слушает события storage для синхронизации
window.addEventListener('storage', (event) ->
if event.key == @storageKey
@applyTheme(event.newValue)
)
# Добавляет класс для анимации при переключении
document.documentElement.classList.add('theme-transition')
setTimeout(() ->
document.documentElement.classList.remove('theme-transition')
, 300)
setTheme(theme)theme - 'light', 'dark', или 'system'Пример:
@$refs.themeToggle.setTheme('dark')
getCurrentTheme()StringПример:
currentTheme = @$refs.themeToggle.getCurrentTheme()
toggle()Пример:
@$refs.themeToggle.toggle()
# Проверка допустимых значений
if !['light', 'dark', 'system'].includes(theme)
throw new Error("Недопустимое значение темы: "+theme)
try
localStorage.setItem(@storageKey, theme)
catch error
# Используем cookie как fallback
document.cookie = @storageKey+"="+theme+"; path=/; max-age=31536000"
methods:
handleThemeChange: ({ theme, previousTheme }) ->
# Отправка в аналитику
gtag('event', 'theme_change', {
event_category: 'ui',
event_label: theme,
value: theme == 'dark' ? 1 : 0
})
methods:
handleThemeChange: ({ theme }) ->
# Сохранение на сервере (если пользователь авторизован)
if @user
await @api.saveUserPreferences({ theme: theme })
methods:
handleThemeChange: ({ theme }) ->
# Изменение meta-тегов
const themeColor = theme == 'dark' ? '#1a202c' : '#ffffff'
document.querySelector('meta[name="theme-color"]').content = themeColor
# Изменение favicon
const favicon = theme == 'dark' ? '/favicon-dark.ico' : '/favicon-light.ico'
document.querySelector('link[rel="icon"]').href = favicon
//- Правильно
themetoggle
//- Неправильно (ограниченный выбор)
themetoggle(:themes="['light', 'dark']")
//- Позволяет следовать системным настройкам
themetoggle
//- Автоматически сохраняет в localStorage
themetoggle
//- Правильные ARIA-атрибуты
themetoggle(aria-label="Переключение темы")
# В тестах
describe('ThemeToggle', ->
it('should switch between themes', ->
toggle.setTheme('light')
expect(toggle.getCurrentTheme()).toBe('light')
toggle.setTheme('dark')
expect(toggle.getCurrentTheme()).toBe('dark')
)
)
Компонент ThemeToggle предоставляет полнофункциональное решение для управления темами в приложении с поддержкой доступности, сохранения состояния и плавных переходов.