|
|
@@ -0,0 +1,504 @@
|
|
|
+# Компонент ThemeToggle - Полная документация
|
|
|
+
|
|
|
+## Назначение компонента
|
|
|
+
|
|
|
+`ThemeToggle` - компонент для переключения между светлой и темной темами в приложении. Сохраняет выбор пользователя в localStorage, синхронизирует состояние между вкладками и обеспечивает плавные переходы между темами.
|
|
|
+
|
|
|
+## Импорт и регистрация
|
|
|
+
|
|
|
+```coffee
|
|
|
+# В основном файле приложения (app/temp.coffee)
|
|
|
+components:
|
|
|
+ 'themetoggle': require 'app/shared/ThemeToggle'
|
|
|
+```
|
|
|
+
|
|
|
+## Базовое использование
|
|
|
+
|
|
|
+```pug
|
|
|
+//- Минимальное использование
|
|
|
+themetoggle
|
|
|
+
|
|
|
+//- С кастомными классами
|
|
|
+themetoggle(class="ml-4")
|
|
|
+
|
|
|
+//- В навигационной панели
|
|
|
+div(class="flex items-center space-x-4")
|
|
|
+ language-switcher
|
|
|
+ themetoggle
|
|
|
+```
|
|
|
+
|
|
|
+## Полный список props
|
|
|
+
|
|
|
+### `size` (опциональный)
|
|
|
+- **Тип:** `String`
|
|
|
+- **По умолчанию:** `'md'`
|
|
|
+- **Допустимые значения:** `'sm'`, `'md'`, `'lg'`
|
|
|
+- **Описание:** Размер переключателя
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle(size="sm")
|
|
|
+ themetoggle(size="lg")
|
|
|
+ ```
|
|
|
+
|
|
|
+### `variant` (опциональный)
|
|
|
+- **Тип:** `String`
|
|
|
+- **По умолчанию:** `'default'`
|
|
|
+- **Допустимые значения:** `'default'`, `'minimal'`, `'icon-only'`
|
|
|
+- **Описание:** Вариант отображения переключателя
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle(variant="minimal")
|
|
|
+ themetoggle(variant="icon-only")
|
|
|
+ ```
|
|
|
+
|
|
|
+### `showLabels` (опциональный)
|
|
|
+- **Тип:** `Boolean`
|
|
|
+- **По умолчанию:** `true`
|
|
|
+- **Описание:** Показывать текстовые метки
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle(:showLabels="false")
|
|
|
+ ```
|
|
|
+
|
|
|
+### `labels` (опциональный)
|
|
|
+- **Тип:** `Object`
|
|
|
+- **По умолчанию:** `{ light: 'Светлая', dark: 'Тёмная', system: 'Системная' }`
|
|
|
+- **Описание:** Кастомные текстовые метки
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle(:labels="{ light: 'Light', dark: 'Dark', system: 'Auto' }")
|
|
|
+ ```
|
|
|
+
|
|
|
+### `storageKey` (опциональный)
|
|
|
+- **Тип:** `String`
|
|
|
+- **По умолчанию:** `'borbad-theme'`
|
|
|
+- **Описание:** Ключ для сохранения в localStorage
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle(storageKey="my-app-theme")
|
|
|
+ ```
|
|
|
+
|
|
|
+### `class` (опциональный)
|
|
|
+- **Тип:** `String | Object | Array`
|
|
|
+- **По умолчанию:** `''`
|
|
|
+- **Описание:** Дополнительные CSS классы
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle(class="border border-gray-300 rounded-lg")
|
|
|
+ ```
|
|
|
+
|
|
|
+## События (Events)
|
|
|
+
|
|
|
+### `@change`
|
|
|
+- **Описание:** Событие смены темы
|
|
|
+- **Payload:** `{ theme: String, previousTheme: String }`
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle(@change="handleThemeChange")
|
|
|
+ ```
|
|
|
+
|
|
|
+### `@init`
|
|
|
+- **Описание:** Событие инициализации темы
|
|
|
+- **Payload:** `{ theme: String }`
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle(@init="handleThemeInit")
|
|
|
+ ```
|
|
|
+
|
|
|
+## Слоты (Slots)
|
|
|
+
|
|
|
+### `[light-icon]`
|
|
|
+- **Описание:** Иконка для светлой темы
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle
|
|
|
+ template([light-icon])
|
|
|
+ svig(class="w-4 h-4" ...) ...
|
|
|
+ ```
|
|
|
+
|
|
|
+### `[dark-icon]`
|
|
|
+- **Описание:** Иконка для темной темы
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle
|
|
|
+ template([dark-icon])
|
|
|
+ svig(class="w-4 h-4" ...) ...
|
|
|
+ ```
|
|
|
+
|
|
|
+### `[system-icon]`
|
|
|
+- **Описание:** Иконка для системной темы
|
|
|
+- **Пример:**
|
|
|
+ ```pug
|
|
|
+ themetoggle
|
|
|
+ template([system-icon])
|
|
|
+ svig(class="w-4 h-4" ...) ...
|
|
|
+ ```
|
|
|
+
|
|
|
+## Доступные темы
|
|
|
+
|
|
|
+### `light`
|
|
|
+- Светлая тема
|
|
|
+- Принудительно устанавливает светлый режим
|
|
|
+
|
|
|
+### `dark`
|
|
|
+- Темная тема
|
|
|
+- Принудительно устанавливает темный режим
|
|
|
+
|
|
|
+### `system`
|
|
|
+- Системная тема
|
|
|
+- Следует за настройками операционной системы
|
|
|
+- Использует `prefers-color-scheme` media query
|
|
|
+
|
|
|
+## Примеры использования
|
|
|
+
|
|
|
+### 1. Базовые варианты
|
|
|
+```pug
|
|
|
+//- Стандартный переключатель
|
|
|
+themetoggle
|
|
|
+
|
|
|
+//- Только иконки
|
|
|
+themetoggle(variant="icon-only")
|
|
|
+
|
|
|
+//- Минималистичный вариант
|
|
|
+themetoggle(variant="minimal")
|
|
|
+
|
|
|
+//- Разные размеры
|
|
|
+div(class="flex space-x-2")
|
|
|
+ themetoggle(size="sm")
|
|
|
+ themetoggle(size="md")
|
|
|
+ themetoggle(size="lg")
|
|
|
+```
|
|
|
+
|
|
|
+### 2. Кастомные иконки
|
|
|
+```pug
|
|
|
+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")
|
|
|
+```
|
|
|
+
|
|
|
+### 3. Кастомные метки
|
|
|
+```pug
|
|
|
+themetoggle(
|
|
|
+ :labels="{
|
|
|
+ light: '☀️ Светлая',
|
|
|
+ dark: '🌙 Тёмная',
|
|
|
+ system: '💻 Система'
|
|
|
+ }"
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+### 4. Интеграция с навигацией
|
|
|
+```pug
|
|
|
+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"
|
|
|
+ )
|
|
|
+```
|
|
|
+
|
|
|
+### 5. В мобильном меню
|
|
|
+```pug
|
|
|
+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"
|
|
|
+ )
|
|
|
+```
|
|
|
+
|
|
|
+### 6. С обработкой событий
|
|
|
+```pug
|
|
|
+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
|
|
|
+```
|
|
|
+
|
|
|
+## Программное взаимодействие
|
|
|
+
|
|
|
+### Получение текущей темы
|
|
|
+```coffee
|
|
|
+# Через глобальный объект
|
|
|
+currentTheme = _.appState?.currentTheme
|
|
|
+
|
|
|
+# Через EventBus
|
|
|
+EventBus.on 'theme_changed', (theme) ->
|
|
|
+ debug.log "Текущая тема: "+theme
|
|
|
+```
|
|
|
+
|
|
|
+### Установка темы программно
|
|
|
+```coffee
|
|
|
+# Через глобальный объект
|
|
|
+_.setTheme('dark')
|
|
|
+
|
|
|
+# Через EventBus
|
|
|
+EventBus.emit('set_theme', 'light')
|
|
|
+```
|
|
|
+
|
|
|
+### Подписка на изменения темы
|
|
|
+```coffee
|
|
|
+# В компоненте
|
|
|
+beforeMount: ->
|
|
|
+ EventBus.on 'theme_changed', @handleThemeChange
|
|
|
+
|
|
|
+methods:
|
|
|
+ handleThemeChange: (theme) ->
|
|
|
+ # Обновляем стили компонента
|
|
|
+ @updateComponentStyles(theme)
|
|
|
+```
|
|
|
+
|
|
|
+## Стили и кастомизация
|
|
|
+
|
|
|
+### CSS-переменные
|
|
|
+Компонент использует стандартные CSS-переменные Tailwind:
|
|
|
+```css
|
|
|
+: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;
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Кастомные стили
|
|
|
+```styl
|
|
|
+// Переопределение стандартных стилей
|
|
|
+.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
|
|
|
+```
|
|
|
+
|
|
|
+### Анимации переходов
|
|
|
+```styl
|
|
|
+// Плавные переходы
|
|
|
+.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
|
|
|
+```
|
|
|
+
|
|
|
+## Интеграция с Tailwind CSS
|
|
|
+
|
|
|
+### Конфигурация tailwind.config.js
|
|
|
+```javascript
|
|
|
+module.exports = {
|
|
|
+ darkMode: 'class', // или 'media' для системной темы
|
|
|
+ // ...
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+### Использование в компонентах
|
|
|
+```pug
|
|
|
+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") Текст
|
|
|
+```
|
|
|
+
|
|
|
+## Особенности работы
|
|
|
+
|
|
|
+### Определение системной темы
|
|
|
+```coffee
|
|
|
+# Использует matchMedia для определения системных предпочтений
|
|
|
+systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
|
+```
|
|
|
+
|
|
|
+### Синхронизация между вкладками
|
|
|
+```coffee
|
|
|
+# Слушает события storage для синхронизации
|
|
|
+window.addEventListener('storage', (event) ->
|
|
|
+ if event.key == @storageKey
|
|
|
+ @applyTheme(event.newValue)
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+### Плавные переходы
|
|
|
+```coffee
|
|
|
+# Добавляет класс для анимации при переключении
|
|
|
+document.documentElement.classList.add('theme-transition')
|
|
|
+setTimeout(() ->
|
|
|
+ document.documentElement.classList.remove('theme-transition')
|
|
|
+, 300)
|
|
|
+```
|
|
|
+
|
|
|
+## Методы API
|
|
|
+
|
|
|
+### `setTheme(theme)`
|
|
|
+- **Описание:** Устанавливает указанную тему
|
|
|
+- **Параметры:** `theme` - 'light', 'dark', или 'system'
|
|
|
+- **Пример:**
|
|
|
+ ```coffee
|
|
|
+ @$refs.themeToggle.setTheme('dark')
|
|
|
+ ```
|
|
|
+
|
|
|
+### `getCurrentTheme()`
|
|
|
+- **Описание:** Возвращает текущую активную тему
|
|
|
+- **Возвращает:** `String`
|
|
|
+- **Пример:**
|
|
|
+ ```coffee
|
|
|
+ currentTheme = @$refs.themeToggle.getCurrentTheme()
|
|
|
+ ```
|
|
|
+
|
|
|
+### `toggle()`
|
|
|
+- **Описание:** Переключает между светлой и темной темами
|
|
|
+- **Пример:**
|
|
|
+ ```coffee
|
|
|
+ @$refs.themeToggle.toggle()
|
|
|
+ ```
|
|
|
+
|
|
|
+## Обработка ошибок
|
|
|
+
|
|
|
+### Валидация темы
|
|
|
+```coffee
|
|
|
+# Проверка допустимых значений
|
|
|
+if !['light', 'dark', 'system'].includes(theme)
|
|
|
+ throw new Error("Недопустимое значение темы: "+theme)
|
|
|
+```
|
|
|
+
|
|
|
+### Fallback для localStorage
|
|
|
+```coffee
|
|
|
+try
|
|
|
+ localStorage.setItem(@storageKey, theme)
|
|
|
+catch error
|
|
|
+ # Используем cookie как fallback
|
|
|
+ document.cookie = @storageKey+"="+theme+"; path=/; max-age=31536000"
|
|
|
+```
|
|
|
+
|
|
|
+## Примеры интеграции
|
|
|
+
|
|
|
+### 1. С аналитикой
|
|
|
+```coffee
|
|
|
+methods:
|
|
|
+ handleThemeChange: ({ theme, previousTheme }) ->
|
|
|
+ # Отправка в аналитику
|
|
|
+ gtag('event', 'theme_change', {
|
|
|
+ event_category: 'ui',
|
|
|
+ event_label: theme,
|
|
|
+ value: theme == 'dark' ? 1 : 0
|
|
|
+ })
|
|
|
+```
|
|
|
+
|
|
|
+### 2. С синхронизацией с сервером
|
|
|
+```coffee
|
|
|
+methods:
|
|
|
+ handleThemeChange: ({ theme }) ->
|
|
|
+ # Сохранение на сервере (если пользователь авторизован)
|
|
|
+ if @user
|
|
|
+ await @api.saveUserPreferences({ theme: theme })
|
|
|
+```
|
|
|
+
|
|
|
+### 3. С кастомной логикой
|
|
|
+```coffee
|
|
|
+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
|
|
|
+```
|
|
|
+
|
|
|
+## Best Practices
|
|
|
+
|
|
|
+### 1. Всегда предоставляйте все три варианта
|
|
|
+```pug
|
|
|
+//- Правильно
|
|
|
+themetoggle
|
|
|
+
|
|
|
+//- Неправильно (ограниченный выбор)
|
|
|
+themetoggle(:themes="['light', 'dark']")
|
|
|
+```
|
|
|
+
|
|
|
+### 2. Используйте системную тему по умолчанию
|
|
|
+```pug
|
|
|
+//- Позволяет следовать системным настройкам
|
|
|
+themetoggle
|
|
|
+```
|
|
|
+
|
|
|
+### 3. Сохраняйте состояние пользователя
|
|
|
+```pug
|
|
|
+//- Автоматически сохраняет в localStorage
|
|
|
+themetoggle
|
|
|
+```
|
|
|
+
|
|
|
+### 4. Обеспечивайте доступность
|
|
|
+```pug
|
|
|
+//- Правильные ARIA-атрибуты
|
|
|
+themetoggle(aria-label="Переключение темы")
|
|
|
+```
|
|
|
+
|
|
|
+### 5. Тестируйте все варианты
|
|
|
+```coffee
|
|
|
+# В тестах
|
|
|
+describe('ThemeToggle', ->
|
|
|
+ it('should switch between themes', ->
|
|
|
+ toggle.setTheme('light')
|
|
|
+ expect(toggle.getCurrentTheme()).toBe('light')
|
|
|
+
|
|
|
+ toggle.setTheme('dark')
|
|
|
+ expect(toggle.getCurrentTheme()).toBe('dark')
|
|
|
+ )
|
|
|
+)
|
|
|
+```
|
|
|
+
|
|
|
+Компонент ThemeToggle предоставляет полнофункциональное решение для управления темами в приложении с поддержкой доступности, сохранения состояния и плавных переходов.
|