|
@@ -0,0 +1,566 @@
|
|
|
|
|
+# Компонент ModalWindow - Полная документация
|
|
|
|
|
+
|
|
|
|
|
+## Назначение компонента
|
|
|
|
|
+
|
|
|
|
|
+`ModalWindow` - универсальный компонент для создания модальных окон (диалоговых окон) в приложении. Предоставляет функциональность для отображения контента поверх основного интерфейса с затемнением фона, анимациями и управлением через глобальную шину событий.
|
|
|
|
|
+
|
|
|
|
|
+## Архитектура компонента
|
|
|
|
|
+
|
|
|
|
|
+### Структура файлов
|
|
|
|
|
+```
|
|
|
|
|
+app/shared/ModalWindow/
|
|
|
|
|
+├── index.coffee # Логика компонента
|
|
|
|
|
+├── index.pug # Шаблон
|
|
|
|
|
+└── index.styl # Стили
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Импорт и регистрация
|
|
|
|
|
+
|
|
|
|
|
+```coffee
|
|
|
|
|
+# В основном файле приложения (app/temp.coffee)
|
|
|
|
|
+components:
|
|
|
|
|
+ 'modal-window': require 'app/shared/ModalWindow'
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Базовое использование
|
|
|
|
|
+
|
|
|
|
|
+### 1. Простое модальное окно
|
|
|
|
|
+```pug
|
|
|
|
|
+modal-window(
|
|
|
|
|
+ :is-visible="showModal"
|
|
|
|
|
+ @close="showModal = false"
|
|
|
|
|
+)
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ h2(class="text-xl font-bold mb-4") Заголовок
|
|
|
|
|
+ p Содержимое модального окна
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 2. С кнопками действий
|
|
|
|
|
+```pug
|
|
|
|
|
+modal-window(
|
|
|
|
|
+ :is-visible="showModal"
|
|
|
|
|
+ @close="showModal = false"
|
|
|
|
|
+ @confirm="handleConfirm"
|
|
|
|
|
+)
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ h2(class="text-xl font-bold mb-4") Подтверждение
|
|
|
|
|
+ p Вы уверены, что хотите выполнить это действие?
|
|
|
|
|
+
|
|
|
|
|
+ template([footer])
|
|
|
|
|
+ button(@click="$emit('close')" class="btn-secondary") Отмена
|
|
|
|
|
+ button(@click="$emit('confirm')" class="btn-primary") Подтвердить
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Полный список props
|
|
|
|
|
+
|
|
|
|
|
+### `isVisible` (обязательный)
|
|
|
|
|
+- **Тип:** `Boolean`
|
|
|
|
|
+- **Описание:** Управляет видимостью модального окна
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="isModalOpen")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `title` (опциональный)
|
|
|
|
|
+- **Тип:** `String`
|
|
|
|
|
+- **По умолчанию:** `''`
|
|
|
|
|
+- **Описание:** Заголовок модального окна
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" title="Заголовок окна")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `size` (опциональный)
|
|
|
|
|
+- **Тип:** `String`
|
|
|
|
|
+- **По умолчанию:** `'md'`
|
|
|
|
|
+- **Допустимые значения:** `'sm'`, `'md'`, `'lg'`, `'xl'`, `'full'`
|
|
|
|
|
+- **Описание:** Размер модального окна
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" size="lg")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `closeOnBackdrop` (опциональный)
|
|
|
|
|
+- **Тип:** `Boolean`
|
|
|
|
|
+- **По умолчанию:** `true`
|
|
|
|
|
+- **Описание:** Закрывать ли окно при клике на подложку
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" :close-on-backdrop="false")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `closeOnEscape` (опциональный)
|
|
|
|
|
+- **Тип:** `Boolean`
|
|
|
|
|
+- **По умолчанию:** `true`
|
|
|
|
|
+- **Описание:** Закрывать ли окно при нажатии Escape
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" :close-on-escape="false")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `showCloseButton` (опциональный)
|
|
|
|
|
+- **Тип:** `Boolean`
|
|
|
|
|
+- **По умолчанию:** `true`
|
|
|
|
|
+- **Описание:** Показывать ли кнопку закрытия
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" :show-close-button="false")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `overlayClass` (опциональный)
|
|
|
|
|
+- **Тип:** `String`
|
|
|
|
|
+- **По умолчанию:** `''`
|
|
|
|
|
+- **Описание:** Дополнительные классы для подложки
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" overlay-class="bg-opacity-80")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `contentClass` (опциональный)
|
|
|
|
|
+- **Тип:** `String`
|
|
|
|
|
+- **По умолчанию:** `''`
|
|
|
|
|
+- **Описание:** Дополнительные классы для контента
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" content-class="custom-modal-style")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+## События (Events)
|
|
|
|
|
+
|
|
|
|
|
+### `@close`
|
|
|
|
|
+- **Описание:** Событие закрытия модального окна
|
|
|
|
|
+- **Payload:** `null`
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" @close="handleModalClose")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `@confirm` (опциональный)
|
|
|
|
|
+- **Описание:** Событие подтверждения действия
|
|
|
|
|
+- **Payload:** `null`
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" @confirm="handleConfirm")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `@opened`
|
|
|
|
|
+- **Описание:** Событие полного открытия модального окна (после анимации)
|
|
|
|
|
+- **Payload:** `null`
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" @opened="handleModalOpened")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `@closed`
|
|
|
|
|
+- **Описание:** Событие полного закрытия модального окна (после анимации)
|
|
|
|
|
+- **Payload:** `null`
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal" @closed="handleModalClosed")
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+## Слоты (Slots)
|
|
|
|
|
+
|
|
|
|
|
+### `[body]` (основной слот)
|
|
|
|
|
+- **Описание:** Основное содержимое модального окна
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal")
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ h2 Заголовок
|
|
|
|
|
+ p Основной контент
|
|
|
|
|
+ form
|
|
|
|
|
+ input(type="text" placeholder="Введите данные")
|
|
|
|
|
+ button(type="submit") Отправить
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `[header]` (опциональный)
|
|
|
|
|
+- **Описание:** Заголовочная область модального окна
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal")
|
|
|
|
|
+ template([header])
|
|
|
|
|
+ div(class="flex items-center justify-between")
|
|
|
|
|
+ h2(class="text-xl font-bold") Пользовательский заголовок
|
|
|
|
|
+ custom-close-button(@click="$emit('close')")
|
|
|
|
|
+
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ p Содержимое модального окна
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+### `[footer]` (опциональный)
|
|
|
|
|
+- **Описание:** Нижняя область модального окна (обычно для кнопок)
|
|
|
|
|
+- **Пример:**
|
|
|
|
|
+ ```pug
|
|
|
|
|
+ modal-window(:is-visible="showModal")
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ p Вы уверены, что хотите удалить этот элемент?
|
|
|
|
|
+
|
|
|
|
|
+ template([footer])
|
|
|
|
|
+ div(class="flex justify-end space-x-3")
|
|
|
|
|
+ button(@click="$emit('close')" class="btn-secondary") Отмена
|
|
|
|
|
+ button(@click="$emit('confirm')" class="btn-danger") Удалить
|
|
|
|
|
+ ```
|
|
|
|
|
+
|
|
|
|
|
+## Размеры модальных окон
|
|
|
|
|
+
|
|
|
|
|
+### Доступные размеры
|
|
|
|
|
+```pug
|
|
|
|
|
+//- Маленькое (sm)
|
|
|
|
|
+modal-window(:is-visible="showModal" size="sm")
|
|
|
|
|
+
|
|
|
|
|
+//- Среднее (md) - по умолчанию
|
|
|
|
|
+modal-window(:is-visible="showModal" size="md")
|
|
|
|
|
+
|
|
|
|
|
+//- Большое (lg)
|
|
|
|
|
+modal-window(:is-visible="showModal" size="lg")
|
|
|
|
|
+
|
|
|
|
|
+//- Очень большое (xl)
|
|
|
|
|
+modal-window(:is-visible="showModal" size="xl")
|
|
|
|
|
+
|
|
|
|
|
+//- На весь экран (full)
|
|
|
|
|
+modal-window(:is-visible="showModal" size="full")
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Стили и CSS классы
|
|
|
|
|
+
|
|
|
|
|
+### Базовые классы
|
|
|
|
|
+- `.modal-overlay` - класс подложки
|
|
|
|
|
+- `.modal-container` - класс контейнера
|
|
|
|
|
+- `.modal-content` - класс контента
|
|
|
|
|
+
|
|
|
|
|
+### Модификаторы размера
|
|
|
|
|
+- `.modal--sm` - маленький размер
|
|
|
|
|
+- `.modal--md` - средний размер (по умолчанию)
|
|
|
|
|
+- `.modal--lg` - большой размер
|
|
|
|
|
+- `.modal--xl` - очень большой размер
|
|
|
|
|
+- `.modal--full` - на весь экран
|
|
|
|
|
+
|
|
|
|
|
+### Состояния анимации
|
|
|
|
|
+- `.modal-enter-active` - анимация появления
|
|
|
|
|
+- `.modal-leave-active` - анимация исчезновения
|
|
|
|
|
+
|
|
|
|
|
+## Примеры использования в различных сценариях
|
|
|
|
|
+
|
|
|
|
|
+### 1. Простая информационная модалка
|
|
|
|
|
+```pug
|
|
|
|
|
+modal-window(
|
|
|
|
|
+ :is-visible="showInfoModal"
|
|
|
|
|
+ @close="showInfoModal = false"
|
|
|
|
|
+ title="Информация"
|
|
|
|
|
+)
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ p(class="mb-4") Это информационное модальное окно с простым текстом.
|
|
|
|
|
+ p Вы можете закрыть его, нажав на крестик или кликнув вне окна.
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 2. Модалка подтверждения действия
|
|
|
|
|
+```pug
|
|
|
|
|
+modal-window(
|
|
|
|
|
+ :is-visible="showConfirmModal"
|
|
|
|
|
+ @close="showConfirmModal = false"
|
|
|
|
|
+ @confirm="handleDeleteConfirm"
|
|
|
|
|
+ title="Подтверждение удаления"
|
|
|
|
|
+ size="sm"
|
|
|
|
|
+)
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ p Вы уверены, что хотите удалить "{{ itemName }}"? Это действие нельзя отменить.
|
|
|
|
|
+
|
|
|
|
|
+ template([footer])
|
|
|
|
|
+ div(class="flex justify-end space-x-3")
|
|
|
|
|
+ button(
|
|
|
|
|
+ @click="$emit('close')"
|
|
|
|
|
+ class="px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors"
|
|
|
|
|
+ ) Отмена
|
|
|
|
|
+ button(
|
|
|
|
|
+ @click="$emit('confirm')"
|
|
|
|
|
+ class="px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 transition-colors"
|
|
|
|
|
+ ) Удалить
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 3. Форма в модальном окне
|
|
|
|
|
+```pug
|
|
|
|
|
+modal-window(
|
|
|
|
|
+ :is-visible="showFormModal"
|
|
|
|
|
+ @close="closeFormModal"
|
|
|
|
|
+ title="Добавить мероприятие"
|
|
|
|
|
+ size="lg"
|
|
|
|
|
+ :close-on-backdrop="false"
|
|
|
|
|
+)
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ form(@submit.prevent="submitEventForm")
|
|
|
|
|
+ div(class="space-y-4")
|
|
|
|
|
+ div
|
|
|
|
|
+ label(for="title" class="block text-sm font-medium text-gray-700") Название
|
|
|
|
|
+ input(
|
|
|
|
|
+ id="title"
|
|
|
|
|
+ v-model="form.title"
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
|
|
|
|
+ required
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ div
|
|
|
|
|
+ label(for="description" class="block text-sm font-medium text-gray-700") Описание
|
|
|
|
|
+ textarea(
|
|
|
|
|
+ id="description"
|
|
|
|
|
+ v-model="form.description"
|
|
|
|
|
+ rows="4"
|
|
|
|
|
+ class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500"
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ template([footer])
|
|
|
|
|
+ div(class="flex justify-end space-x-3")
|
|
|
|
|
+ button(
|
|
|
|
|
+ type="button"
|
|
|
|
|
+ @click="$emit('close')"
|
|
|
|
|
+ class="px-4 py-2 text-gray-600 hover:text-gray-800 transition-colors"
|
|
|
|
|
+ ) Отмена
|
|
|
|
|
+ button(
|
|
|
|
|
+ type="submit"
|
|
|
|
|
+ class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
|
|
|
|
|
+ ) Сохранить
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4. Модалка с кастомным заголовком
|
|
|
|
|
+```pug
|
|
|
|
|
+modal-window(
|
|
|
|
|
+ :is-visible="showCustomModal"
|
|
|
|
|
+ @close="showCustomModal = false"
|
|
|
|
|
+ :show-close-button="false"
|
|
|
|
|
+)
|
|
|
|
|
+ template([header])
|
|
|
|
|
+ div(class="bg-gradient-to-r from-blue-500 to-purple-600 text-white p-6")
|
|
|
|
|
+ div(class="flex items-center justify-between")
|
|
|
|
|
+ div
|
|
|
|
|
+ h2(class="text-2xl font-bold") Премиум функции
|
|
|
|
|
+ p(class="text-blue-100") Доступно только для премиум пользователей
|
|
|
|
|
+ button(
|
|
|
|
|
+ @click="$emit('close')"
|
|
|
|
|
+ class="text-white hover:text-blue-200 transition-colors"
|
|
|
|
|
+ )
|
|
|
|
|
+ icon(name="close" class="w-6 h-6")
|
|
|
|
|
+
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ div(class="p-6")
|
|
|
|
|
+ ul(class="space-y-3")
|
|
|
|
|
+ li(class="flex items-center")
|
|
|
|
|
+ icon(name="check" class="w-5 h-5 text-green-500 mr-3")
|
|
|
|
|
+ span Расширенная аналитика
|
|
|
|
|
+ li(class="flex items-center")
|
|
|
|
|
+ icon(name="check" class="w-5 h-5 text-green-500 mr-3")
|
|
|
|
|
+ span Приоритетная поддержка
|
|
|
|
|
+ li(class="flex items-center")
|
|
|
|
|
+ icon(name="check" class="w-5 h-5 text-green-500 mr-3")
|
|
|
|
|
+ span Кастомные домены
|
|
|
|
|
+
|
|
|
|
|
+ template([footer])
|
|
|
|
|
+ div(class="bg-gray-50 px-6 py-4 flex justify-between items-center")
|
|
|
|
|
+ span(class="text-gray-600") Всего 999 TJS/месяц
|
|
|
|
|
+ button(
|
|
|
|
|
+ @click="subscribePremium"
|
|
|
|
|
+ class="px-6 py-2 bg-gradient-to-r from-blue-500 to-purple-600 text-white rounded-lg hover:from-blue-600 hover:to-purple-700 transition-colors"
|
|
|
|
|
+ ) Подписаться
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 5. Модалка с загрузкой контента
|
|
|
|
|
+```pug
|
|
|
|
|
+modal-window(
|
|
|
|
|
+ :is-visible="showLoadingModal"
|
|
|
|
|
+ @close="cancelLoading"
|
|
|
|
|
+ title="Загрузка"
|
|
|
|
|
+ :close-on-escape="false"
|
|
|
|
|
+ :close-on-backdrop="false"
|
|
|
|
|
+ :show-close-button="false"
|
|
|
|
|
+)
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ div(class="text-center py-8")
|
|
|
|
|
+ div(class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600 mx-auto mb-4")
|
|
|
|
|
+ p Загрузка данных, пожалуйста подождите...
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 6. Модалка с вкладками
|
|
|
|
|
+```pug
|
|
|
|
|
+modal-window(
|
|
|
|
|
+ :is-visible="showTabsModal"
|
|
|
|
|
+ @close="showTabsModal = false"
|
|
|
|
|
+ title="Настройки"
|
|
|
|
|
+ size="lg"
|
|
|
|
|
+)
|
|
|
|
|
+ template([body])
|
|
|
|
|
+ div
|
|
|
|
|
+ div(class="border-b border-gray-200")
|
|
|
|
|
+ nav(class="-mb-px flex space-x-8")
|
|
|
|
|
+ button(
|
|
|
|
|
+ v-for="tab in tabs"
|
|
|
|
|
+ :key="tab.id"
|
|
|
|
|
+ @click="currentTab = tab.id"
|
|
|
|
|
+ :class="{
|
|
|
|
|
+ 'border-blue-500 text-blue-600': currentTab === tab.id,
|
|
|
|
|
+ 'border-transparent text-gray-500 hover:text-gray-700 hover:border-gray-300': currentTab !== tab.id
|
|
|
|
|
+ }"
|
|
|
|
|
+ class="whitespace-nowrap py-4 px-1 border-b-2 font-medium text-sm"
|
|
|
|
|
+ ) {{ tab.name }}
|
|
|
|
|
+
|
|
|
|
|
+ div(class="py-4")
|
|
|
|
|
+ div(v-if="currentTab === 'general'")
|
|
|
|
|
+ h3(class="text-lg font-medium mb-4") Основные настройки
|
|
|
|
|
+ //- Контент вкладки
|
|
|
|
|
+
|
|
|
|
|
+ div(v-else-if="currentTab === 'security'")
|
|
|
|
|
+ h3(class="text-lg font-medium mb-4") Безопасность
|
|
|
|
|
+ //- Контент вкладки
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Управление через глобальную шину событий
|
|
|
|
|
+
|
|
|
|
|
+### Открытие модального окна
|
|
|
|
|
+```coffee
|
|
|
|
|
+# В любом компоненте
|
|
|
|
|
+EventBus.emit('open_modal', {
|
|
|
|
|
+ component: require('app/components/CustomModal'),
|
|
|
|
|
+ props: {
|
|
|
|
|
+ title: 'Кастомное окно',
|
|
|
|
|
+ data: someData
|
|
|
|
|
+ }
|
|
|
|
|
+})
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Закрытие модального окна
|
|
|
|
|
+```coffee
|
|
|
|
|
+# Закрытие текущего модального окна
|
|
|
|
|
+EventBus.emit('close_modal')
|
|
|
|
|
+
|
|
|
|
|
+# Закрытие всех модальных окон
|
|
|
|
|
+EventBus.emit('close_all_modals')
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Интеграция с глобальным состоянием
|
|
|
|
|
+
|
|
|
|
|
+### Использование с appState
|
|
|
|
|
+```coffee
|
|
|
|
|
+# В основном приложении
|
|
|
|
|
+appState:
|
|
|
|
|
+ modalState:
|
|
|
|
|
+ isVisible: false
|
|
|
|
|
+ component: null
|
|
|
|
|
+ props: {}
|
|
|
|
|
+
|
|
|
|
|
+# Открытие модалки
|
|
|
|
|
+openModal: (component, props = {}) ->
|
|
|
|
|
+ @appState.modalState.component = component
|
|
|
|
|
+ @appState.modalState.props = props
|
|
|
|
|
+ @appState.modalState.isVisible = true
|
|
|
|
|
+
|
|
|
|
|
+# В шаблоне
|
|
|
|
|
+modal-window(
|
|
|
|
|
+ :is-visible="appState.modalState.isVisible"
|
|
|
|
|
+ @close="closeModal"
|
|
|
|
|
+ v-if="appState.modalState.component"
|
|
|
|
|
+)
|
|
|
|
|
+ component(
|
|
|
|
|
+ :is="appState.modalState.component"
|
|
|
|
|
+ v-bind="appState.modalState.props"
|
|
|
|
|
+ @close="closeModal"
|
|
|
|
|
+ )
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Анимации и переходы
|
|
|
|
|
+
|
|
|
|
|
+### Кастомные анимации
|
|
|
|
|
+```styl
|
|
|
|
|
+// В стилях компонента
|
|
|
|
|
+.modal-custom-enter-active
|
|
|
|
|
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)
|
|
|
|
|
+
|
|
|
|
|
+ .modal-container
|
|
|
|
|
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)
|
|
|
|
|
+ transform: scale(0.95) translateY(-10px)
|
|
|
|
|
+ opacity: 0
|
|
|
|
|
+
|
|
|
|
|
+.modal-custom-enter-to
|
|
|
|
|
+ .modal-container
|
|
|
|
|
+ transform: scale(1) translateY(0)
|
|
|
|
|
+ opacity: 1
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Best Practices
|
|
|
|
|
+
|
|
|
|
|
+### 1. Управление состоянием
|
|
|
|
|
+```coffee
|
|
|
|
|
+# Правильно - реактивное управление
|
|
|
|
|
+data: ->
|
|
|
|
|
+ return
|
|
|
|
|
+ showModal: false
|
|
|
|
|
+
|
|
|
|
|
+methods:
|
|
|
|
|
+ openModal: ->
|
|
|
|
|
+ @showModal = true
|
|
|
|
|
+
|
|
|
|
|
+ closeModal: ->
|
|
|
|
|
+ @showModal = false
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 2. Обработка подтверждений
|
|
|
|
|
+```coffee
|
|
|
|
|
+methods:
|
|
|
|
|
+ handleConfirm: ->
|
|
|
|
|
+ try
|
|
|
|
|
+ await this.performAction()
|
|
|
|
|
+ this.showModal = false
|
|
|
|
|
+ this.showSuccessMessage()
|
|
|
|
|
+ catch error
|
|
|
|
|
+ this.showErrorMessage(error)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 3. Доступность
|
|
|
|
|
+```pug
|
|
|
|
|
+//- Добавляйте aria-атрибуты для доступности
|
|
|
|
|
+modal-window(
|
|
|
|
|
+ :is-visible="showModal"
|
|
|
|
|
+ @close="closeModal"
|
|
|
|
|
+ role="dialog"
|
|
|
|
|
+ aria-labelledby="modal-title"
|
|
|
|
|
+)
|
|
|
|
|
+ template([header])
|
|
|
|
|
+ h2(id="modal-title") Заголовок модалки
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### 4. Управление фокусом
|
|
|
|
|
+```coffee
|
|
|
|
|
+# Автоматически фокусироваться на первом интерактивном элементе
|
|
|
|
|
+mounted: ->
|
|
|
|
|
+ this.$nextTick(() ->
|
|
|
|
|
+ const firstInput = this.$el.querySelector('input, button, textarea')
|
|
|
|
|
+ if firstInput
|
|
|
|
|
+ firstInput.focus()
|
|
|
|
|
+ )
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+## Отладка и разработка
|
|
|
|
|
+
|
|
|
|
|
+### Логирование событий
|
|
|
|
|
+```coffee
|
|
|
|
|
+# Отслеживание событий модального окна
|
|
|
|
|
+EventBus.on('modal_opened', (data) ->
|
|
|
|
|
+ debug.log "Модальное окно открыто: "+data.component?.name
|
|
|
|
|
+)
|
|
|
|
|
+
|
|
|
|
|
+EventBus.on('modal_closed', (data) ->
|
|
|
|
|
+ debug.log "Модальное окно закрыто"
|
|
|
|
|
+)
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+### Проверка доступности
|
|
|
|
|
+```coffee
|
|
|
|
|
+# Проверка управления фокусом
|
|
|
|
|
+checkFocusManagement: ->
|
|
|
|
|
+ if this.isVisible
|
|
|
|
|
+ const activeElement = document.activeElement
|
|
|
|
|
+ const modalContent = this.$el.querySelector('.modal-content')
|
|
|
|
|
+
|
|
|
|
|
+ if modalContent and !modalContent.contains(activeElement)
|
|
|
|
|
+ debug.log "Фокус вне модального окна!"
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+Компонент ModalWindow предоставляет мощный и гибкий инструмент для создания модальных окон с поддержкой анимаций, управления через события, кастомизацией и соблюдением лучших практик доступности.
|