Компонент меню — фундаментальный элемент пользовательского интерфейса, обеспечивающий навигацию, доступ к действиям и структурирование контента. В экосистеме SvelteKit UI-библиотек (например, Skeleton, Flowbite-Svelte, Carbon Components Svelte) меню реализуются как высокоуровневые абстракции с акцентом на реактивность, доступность и композиционность.
Menu может выступать в нескольких ролях:
Типичный компонент меню строится из следующих частей:
Пример базовой реализации:
<script>
let open = false;
</script>
<button on:click={() => open = !open}>
Меню
</button>
{#if open}
<ul class="menu">
<li>Профиль</li>
<li>Настройки</li>
<li>Выход</li>
</ul>
{/if}
<script>
import { Dropdown, DropdownItem } from '@skeletonlabs/skeleton';
</script>
<Dropdown>
<button slot="trigger">Открыть</button>
<DropdownItem>Профиль</DropdownItem>
<DropdownItem>Настройки</DropdownItem>
<DropdownItem>Выход</DropdownItem>
</Dropdown>
Особенности:
<script>
import { Dropdown, DropdownItem } from 'flowbite-svelte';
</script>
<Dropdown label="Меню">
<DropdownItem>Dashboard</DropdownItem>
<DropdownItem>Settings</DropdownItem>
<DropdownItem>Logout</DropdownItem>
</Dropdown>
Особенности:
Svelte позволяет управлять состоянием меню без сторонних стейт-менеджеров.
<script>
let isOpen = false;
function toggle() {
isOpen = !isOpen;
}
</script>
<button on:click={toggle}>Toggle</button>
{#if isOpen}
<div class="menu">...</div>
{/if}
Для более сложных сценариев используется store:
import { writable } from 'svelte/store';
export const menuState = writable(false);
Закрытие меню при клике вне области:
<script>
import { onMount } from 'svelte';
let open = false;
let ref;
function handleClickOutside(event) {
if (ref && !ref.contains(event.target)) {
open = false;
}
}
onMount(() => {
document.addEventListener('click', handleClickOutside);
return () => document.removeEventListener('click', handleClickOutside);
});
</script>
<div bind:this={ref}>
<button on:click={() => open = !open}>Меню</button>
{#if open}
<div class="dropdown">...</div>
{/if}
</div>
Качественная реализация Menu должна учитывать ARIA-атрибуты:
role="menu"role="menuitem"aria-expandedaria-controlsПример:
<button
aria-haspopup="true"
aria-expanded={open}
on:click={() => open = !open}
>
Меню
</button>
{#if open}
<ul role="menu">
<li role="menuitem">Пункт 1</li>
<li role="menuitem">Пункт 2</li>
</ul>
{/if}
Также важно:
Пример обработки стрелок:
<script>
let items = [];
let index = 0;
function handleKey(e) {
if (e.key === 'ArrowDown') {
index = (index + 1) % items.length;
items[index].focus();
}
}
</script>
<ul on:keydown={handleKey}>
{#each ['One', 'Two', 'Three'] as item, i}
<li tabindex="0" bind:this={el => items[i] = el}>
{item}
</li>
{/each}
</ul>
<li>
Настройки
<ul class="submenu">
<li>Профиль</li>
<li>Безопасность</li>
</ul>
</li>
Реализация требует:
<script>
let visible = false;
let x = 0, y = 0;
function openMenu(e) {
e.preventDefault();
x = e.clientX;
y = e.clientY;
visible = true;
}
</script>
<div on:contextmenu={openMenu}>
Правый клик
</div>
{#if visible}
<ul style="position: absolute; top: {y}px; left: {x}px;">
<li>Копировать</li>
<li>Вставить</li>
</ul>
{/if}
Svelte предоставляет встроенные transition-функции:
<script>
import { fade, scale } from 'svelte/transition';
</script>
{#if open}
<div transition:scale>
Меню
</div>
{/if}
Комбинации:
Для сложных сценариев используется Popper.js или Floating UI:
import { computePosition } from '@floating-ui/dom';
Задачи:
Меню обычно стилизуется через:
Пример:
.menu {
background: white;
border-radius: 8px;
box-shadow: 0 4px 10px rgba(0,0,0,0.1);
}
Compound Components
<Menu>
<Menu.Trigger />
<Menu.Content>
<Menu.Item />
</Menu.Content>
</Menu>
Render Props / Slots
<Dropdown let:open>
<button>{open ? 'Закрыть' : 'Открыть'}</button>
</Dropdown>
{#if open})Проверяются:
Инструменты:
Mega Menu
Command Palette
Adaptive Menu
Menu часто используется в:
+layout.svelte — глобальная навигация+page.svelte — локальные действияС учетом SSR:
window без проверкиonMount для DOM API