|
|
3 minggu lalu | |
|---|---|---|
| .. | ||
| README.md | 3 minggu lalu | |
| index.coffee | 3 minggu lalu | |
| index.pug | 3 minggu lalu | |
| index.styl | 3 minggu lalu | |
ModalWindow - универсальный компонент для создания модальных окон (диалоговых окон) в приложении. Предоставляет функциональность для отображения контента поверх основного интерфейса с затемнением фона, анимациями и управлением через глобальную шину событий.
https://gogs.osvoj.ru/s5l.ru/borbad.s5l.ru/src/master/vue/app/shared/ModalWindow/
app/shared/ModalWindow/
├── index.coffee # Логика компонента
├── index.pug # Шаблон
└── index.styl # Стили
# В основном файле приложения (app/temp.coffee)
components:
'modal-window': require 'app/shared/ModalWindow'
modal-window(
:is-visible="showModal"
@close="showModal = false"
)
template([body])
h2(class="text-xl font-bold mb-4") Заголовок
p Содержимое модального окна
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") Подтвердить
isVisible (обязательный)BooleanПример:
modal-window(:is-visible="isModalOpen")
title (опциональный)String''Пример:
modal-window(:is-visible="showModal" title="Заголовок окна")
size (опциональный)String'md''sm', 'md', 'lg', 'xl', 'full'Пример:
modal-window(:is-visible="showModal" size="lg")
closeOnBackdrop (опциональный)BooleantrueПример:
modal-window(:is-visible="showModal" :close-on-backdrop="false")
closeOnEscape (опциональный)BooleantrueПример:
modal-window(:is-visible="showModal" :close-on-escape="false")
showCloseButton (опциональный)BooleantrueПример:
modal-window(:is-visible="showModal" :show-close-button="false")
overlayClass (опциональный)String''Пример:
modal-window(:is-visible="showModal" overlay-class="bg-opacity-80")
contentClass (опциональный)String''Пример:
modal-window(:is-visible="showModal" content-class="custom-modal-style")
@closenullПример:
modal-window(:is-visible="showModal" @close="handleModalClose")
@confirm (опциональный)nullПример:
modal-window(:is-visible="showModal" @confirm="handleConfirm")
@openednullПример:
modal-window(:is-visible="showModal" @opened="handleModalOpened")
@closednullПример:
modal-window(:is-visible="showModal" @closed="handleModalClosed")
[body] (основной слот)Пример:
modal-window(:is-visible="showModal")
template([body])
h2 Заголовок
p Основной контент
form
input(type="text" placeholder="Введите данные")
button(type="submit") Отправить
[header] (опциональный)Пример:
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] (опциональный)Пример:
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") Удалить
//- Маленькое (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")
.modal-overlay - класс подложки.modal-container - класс контейнера.modal-content - класс контента.modal--sm - маленький размер.modal--md - средний размер (по умолчанию).modal--lg - большой размер.modal--xl - очень большой размер.modal--full - на весь экран.modal-enter-active - анимация появления.modal-leave-active - анимация исчезновенияmodal-window(
:is-visible="showInfoModal"
@close="showInfoModal = false"
title="Информация"
)
template([body])
p(class="mb-4") Это информационное модальное окно с простым текстом.
p Вы можете закрыть его, нажав на крестик или кликнув вне окна.
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"
) Удалить
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"
) Сохранить
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"
) Подписаться
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 Загрузка данных, пожалуйста подождите...
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") Безопасность
//- Контент вкладки
# В любом компоненте
EventBus.emit('open_modal', {
component: require('app/components/CustomModal'),
props: {
title: 'Кастомное окно',
data: someData
}
})
# Закрытие текущего модального окна
EventBus.emit('close_modal')
# Закрытие всех модальных окон
EventBus.emit('close_all_modals')
# В основном приложении
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"
)
// В стилях компонента
.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
# Правильно - реактивное управление
data: ->
return
showModal: false
methods:
openModal: ->
@showModal = true
closeModal: ->
@showModal = false
methods:
handleConfirm: ->
try
await this.performAction()
this.showModal = false
this.showSuccessMessage()
catch error
this.showErrorMessage(error)
//- Добавляйте aria-атрибуты для доступности
modal-window(
:is-visible="showModal"
@close="closeModal"
role="dialog"
aria-labelledby="modal-title"
)
template([header])
h2(id="modal-title") Заголовок модалки
# Автоматически фокусироваться на первом интерактивном элементе
mounted: ->
this.$nextTick(() ->
const firstInput = this.$el.querySelector('input, button, textarea')
if firstInput
firstInput.focus()
)
# Отслеживание событий модального окна
EventBus.on('modal_opened', (data) ->
debug.log "Модальное окно открыто: "+data.component?.name
)
EventBus.on('modal_closed', (data) ->
debug.log "Модальное окно закрыто"
)
# Проверка управления фокусом
checkFocusManagement: ->
if this.isVisible
const activeElement = document.activeElement
const modalContent = this.$el.querySelector('.modal-content')
if modalContent and !modalContent.contains(activeElement)
debug.log "Фокус вне модального окна!"
Компонент ModalWindow предоставляет мощный и гибкий инструмент для создания модальных окон с поддержкой анимаций, управления через события, кастомизацией и соблюдением лучших практик доступности.