Skip to content

Все о работе модальных окон в проекте

Регистрация модального окна

Расположение модальных окон

js
.
└─ src/
   └─ components/
      └─ Modals/
         ├─ ConfirmDelete/
         ├─ UnsavedChanges/
         └─ YourNewModal/

Дополнительно

Важно называть будущую директорию модального окна в стиле CamelCase.

Минимальная вложенность директории модального окна

YourNewModal
        ├─ index.ts
        ├─ index.vue
        ├─ Loader.vue
        └─ types.ts

Важно

index.ts - Это главная точка входа в эту директорию
index.vue - Само модальное окно с контентом
Loader.vue - Лоадер модального окна
types.ts - Все используемые типы в контексте этого модального окна

Минимальная вложенность кода в каждом файле

Дополнительно

Данный код является не обязательным и может быть изменен по усмотрению разработчика

ts
import { default as Component } from './index.vue';

export * from './types';
export default Component;
ts
export interface IThe[`MyNewModalName`]ModalProps {
    // Здесь пишем пропсы, которые хотим принимать
}

export interface IThe[`MyNewModalName`]ModalEmits {
    (event: 'close'): void
    // Здесь пишем emit's, которые хотим принимать
}
vue
<template>
    <TheModal :id="id" title="Мое новое модальное окно">  <!-- Этот id(props) - мы получаем всегда -->
        <template #content>
            <!-- Тут наш будущий главный контент -->
        </template>
        <template #footer>
            <!-- Тут наш будущий контент футера -->
        </template>
    </TheModal>
</template>

<script async setup lang="ts">
// Types
import type { IModalDefaultProps } from "@/services/Modals";
import type { IThe[`MyNewModalName`]ModalEmits, IThe[`MyNewModalName`]ModalProps } from "./";

type ExtendedPropsT = IModalDefaultProps & IThe[`MyNewModalName`]ModalProps

// Modules
import { defineEmits, defineProps } from "vue";

// Components
import TheModal from "@/components/ui/TheModal";

// Inits
const props = defineProps<ExtendedPropsT>();
const emits = defineEmits<ITheConfirmDeleteModalEmits>();
</script>

<style scoped lang="scss">
@reference "tailwindcss/theme";

// Наши будущие стили модального окна
</style>
vue
<template>
    <!-- Тут пример использование skeleton -->
    <TheModal :id="id">
        <template #content>
            <div class="flex flex-col items-center justify-center py-5">
                <Skeletor class="mb-10" width="80" height="80" circle />
                <Skeletor height="20" width="100%" class="rounded-md mb-3 min-w-80" />
                <Skeletor height="20" width="100%" class="rounded-md mb-3" />
                <Skeletor height="20" width="100%" class="rounded-md" />
            </div>
        </template>
        <template #footer>
            <div class="flex justify-end gap-4">
                <Skeletor height="48" width="89" class="rounded-md" />
                <Skeletor height="48" width="89" class="rounded-md" />
            </div>
        </template>
    </TheModal>
</template>


<script setup lang="ts">
// Types
import type { IModalDefaultProps } from "@/services/Modals";
import type { IThe[`MyNewModalName`]ModalEmits, IThe[`MyNewModalName`]ModalProps } from "./";

type ExtendedPropsT = IModalDefaultProps & IThe[`MyNewModalName`]ModalProps

// Modules
import { defineProps, defineEmits } from 'vue';

// Components
import TheModal from "@/components/ui/TheModal";
import { Skeletor } from 'vue-skeletor';

// Inits
defineProps<ExtendedPropsT>();
defineEmits<IThe[`MyNewModalName`]ModalEmits>();
</script>

Добавление нового модального окна в словарь

ts
// Файл расположен по: ./src/components/Modals/index.ts

// Other
modals.set('myNewModal', { 
    content: defineAsyncComponent(() => import('@/components/Modals/MyNewModal')), 
    loader: (await import('@/components/Modals/MyNewModal/Loader.vue')).default, 
    title: 'Заголовок нового модального окна'
}); 

Регистрация props и emit's в логику сервиса модальных окон

ts
// Файл расположен по: ./src/services/Modals/types.ts

import type {
    IThe[`MyNewModalName`]ModalProps, 
    IThe[`MyNewModalName`]ModalEmits 
} from "@/components/Modals/MyNewModal"; 

/**
 * Тип, который отображает имена модальных окон на их соответствующие свойства.
 * Каждое имя модального окна соответствует конкретным параметрам, которые оно принимает.
 */
export type ModalsPropsMap = {
    'unsavedChanges': ITheUnsavedChangesModalProps,
    'confirmDelete': ITheConfirmDeleteModalProps,
    'myNewModal': IThe[`MyNewModalName`]ModalProps, // ВАЖНО: названия поля должен быть одинаковым как в названии словаря
}

/**
 * Тип, который отображает имена модальных окон на их соответствующие события.
 * Каждое имя модального окна соответствует конкретным параметрам, которые оно принимает.
 */
export type ModalsEmitsMap = {
    'unsavedChanges': ITheUnsavedChangesModalEmits,
    'confirmDelete': ITheConfirmDeleteModalEmits,
    'myNewModal': IThe[`MyNewModalName`]ModalEmits, // ВАЖНО: названия поля должен быть одинаковым как в названии словаря
}

Вызов и использование

То как вызывается

ts
// Modules
import ModalsService from '@/services/Modals';

// Inits
const modalsService = new ModalsService();

// Scenario
modalsService.addModal<'myNewModalName'>({
    name: 'myNewModalName',
    props: {
        // Это пример отправлямого пропса. 
        // Он блокирует закрытие модалки фоном.
        isProhibitBackClose: true,
    },
    emits: {
        submit: () => {}
    }
});

Совет

В данном случае generic нужен, чтобы при отправке props или emit's сразу определялся тип данных, которых можно отправить

Изменение входных props модального окна

ts
// Modules
import ModalsService from '@/services/Modals';

// Inits
const modalsService = new ModalsService();

// Scenario
// В данном случае modal.id мы принимаем props'ом 
// из родителя и переотравляем его.
modalsService.addPropsInModal<'myNewModalName'>(modal.id, {
    firstName: 'he-he',
    lastName: 'ha-ha'
});