Модальные окна и оверлеи

Модальные окна и оверлеи являются ключевыми элементами интерфейса для взаимодействия с пользователем. В контексте SvelteKit их реализация требует понимания реактивности, управления состоянием и правильного использования компонентов библиотек UI.

Создание модального окна

В SvelteKit модальное окно обычно реализуется как отдельный компонент. Основные элементы структуры:

<script>
  export let isOpen = false;
  export let onCl ose = () => {};
</script>

{#if isOpen}
  <div class="overlay" on:click={onClose}></div>
  <div class="modal">
    <slot></slot>
    <button on:click={onClose}>Закрыть</button>
  </div>
{/if}

<style>
  .overlay {
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0,0,0,0.5);
    z-index: 10;
  }

  .modal {
    position: fixed;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
    background: white;
    padding: 2rem;
    border-radius: 8px;
    z-index: 11;
    max-width: 500px;
    width: 90%;
  }
</style>

Ключевые моменты реализации:

  • isOpen управляет видимостью модального окна.
  • Оверлей закрывает окно при клике по затемнённой области.
  • slot позволяет вставлять произвольный контент внутрь модального окна.

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

В больших приложениях управление состоянием модальных окон часто централизуется через store:

// modalStore.js
import { writable } from 'svelte/store';

export const modalState = writable({
  isOpen: false,
  content: null
});

Компонент модального окна подписывается на store и отображает контент динамически:

<script>
  import { modalState } from './modalStore';
  import { onDestroy } from 'svelte';

  let unsubscribe;
  let isOpen = false;
  let content = null;

  unsubscribe = modalState.subscribe(value => {
    isOpen = value.isOpen;
    content = value.content;
  });

  onDestroy(() => unsubscribe());
</script>

{#if isOpen}
  <div class="overlay" on:click={() => modalState.set({ isOpen: false, content: null })}></div>
  <div class="modal">
    {#if content}
      {#await content()}
        <p>Загрузка...</p>
      {:then data}
        <div>{data}</div>
      {:catch error}
        <p>Ошибка: {error.message}</p>
      {/await}
    {/if}
    <button on:click={() => modalState.set({ isOpen: false, content: null })}>Закрыть</button>
  </div>
{/if}

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

Анимации и переходы

SvelteKit и Svelte предоставляют встроенные переходы и анимации, которые делают появление модальных окон плавным:

<script>
  import { fade, fly } from 'svelte/transition';
  export let isOpen = false;
</script>

{#if isOpen}
  <div class="overlay" on:click={() => isOpen = false} transition:fade></div>
  <div class="modal" transition:fly="{{ y: -50, duration: 300 }}">
    <slot></slot>
    <button on:click={() => isOpen = false}>Закрыть</button>
  </div>
{/if}

Примечания:

  • fade применяется к оверлею для плавного затемнения.
  • fly создаёт эффект вылета/появления окна с заданной координатой и длительностью.
  • Можно комбинировать несколько переходов для более сложных эффектов.

Модальные окна с динамическим контентом

Для отображения различных форм, сообщений или карточек внутри одного модального окна используется концепция dynamic content. Контент передаётся через функцию или компонент:

// modalContent.js
export function getUserForm() {
  return import('./UserForm.svelte').then(m => m.default);
}
// Где открывается модальное окно
import { modalState } from './modalStore';
import { getUserForm } from './modalContent';

function openUserForm() {
  modalState.set({
    isOpen: true,
    content: getUserForm
  });
}

Такой подход позволяет уменьшить количество отдельных компонентов модальных окон и управлять ими централизованно.

Оверлеи и блокировка фона

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

  • Использовать фиксированный overlay с высокой z-index.
  • При необходимости отключать скролл страницы через document.body.style.overflow = 'hidden' при открытии и восстанавливать после закрытия.
  • Добавлять обработку Escape клавиши для закрытия окна:
<script>
  import { onMount, onDestroy } from 'svelte';
  export let isOpen = false;
  export let onCl ose = () => {};

  function handleKey(event) {
    if (event.key === 'Escape') onClose();
  }

  onMount(() => window.addEventListener('keydown', handleKey));
  onDestroy(() => window.removeEventListener('keydown', handleKey));
</script>

Интеграция с UI-библиотеками

Популярные библиотеки для SvelteKit, такие как Svelte Material UI (SMUI), Skeleton, Flowbite Svelte, предоставляют готовые модальные компоненты с встроенными стилями и анимациями. Использование их упрощает разработку:

<script>
  import { Dialog, DialogTitle, DialogContent, DialogActions } from '@smui/dialog';
  let open = false;
</script>

<Dialog bind:open>
  <DialogTitle>Подтверждение</DialogTitle>
  <DialogContent>Вы уверены, что хотите удалить этот элемент?</DialogContent>
  <DialogActions>
    <button on:click={() => open = false}>Отмена</button>
    <button on:click={confirmDelete}>Удалить</button>
  </DialogActions>
</Dialog>

Преимущества использования UI-библиотек:

  • Единый визуальный стиль с остальными компонентами.
  • Поддержка анимаций и адаптивности.
  • Снижение объёма собственного CSS-кода.

Работа с несколькими модальными окнами

Для сложных интерфейсов с вложенными модальными окнами рекомендуется:

  • Использовать стек состояний (modalStack) через store.
  • Управлять z-index автоматически.
  • Закрывать только верхний элемент стека при клике на оверлей или Escape.
// modalStack.js
import { writable } from 'svelte/store';
export const modalStack = writable([]);

Добавление и удаление окна осуществляется через push/pop, что позволяет реализовать динамические цепочки модальных окон без конфликтов.

Доступность (Accessibility)

  • Использовать aria-modal="true" и role="dialog" на модальном окне.
  • Фокус должен перемещаться внутрь окна при открытии и возвращаться обратно при закрытии.
  • Кнопки закрытия и интерактивные элементы должны быть доступными для клавиатуры и экранных читалок.

Эта структура позволяет создавать модальные окна и оверлеи любого уровня сложности в SvelteKit, от простых диалогов до динамических форм с централизованным управлением состоянием, а также обеспечивает плавные анимации и соответствие стандартам доступности.