在 Svelte 中使用 Melt UI 构建可折叠组件
Source: Dev.to
请提供您希望翻译的正文内容,我将为您翻译成简体中文。
Melt UI 可折叠指南
Melt UI 为 Svelte 提供了遵循 WAI‑ARIA 指南的无 UI、可访问的组件构建器。Collapsible 构建器让您能够开箱即用地创建可展开/折叠的内容区域,并具备完整的键盘导航和屏幕阅读器支持。
本指南通过实际的、可用于生产环境的实现,演示如何使用 Melt UI 的 createCollapsible 构建器创建可折叠组件。重点在于真实的使用模式,而非抽象概念。
为什么在 @melt-ui/svelte(Melt UI)中使用 Svelte?
Svelte 应用经常需要可展开的内容区域,用于 FAQ、手风琴、导航菜单和详情视图。Melt UI 的 Collapsible 构建器自然契合此生态,因为它提供:
- 完整的 WAI‑ARIA 合规性,确保可访问性
- Headless 设计(由你自行控制所有样式)
- TypeScript 支持,具备出色的类型安全
- 简单、声明式的 API
- 内置键盘导航
- 除 Svelte 外无其他外部依赖
构建器模式允许你将可折叠行为附加到任意元素上,完全掌控标记和样式。
要求
- 一个 Svelte 项目(SvelteKit 或独立的 Svelte)
- Node.js 16+ 以及 npm/pnpm/yarn
- 对 Svelte 组件和 store 有基本了解
安装
使用您喜欢的包管理器安装 Melt UI:
npm install @melt-ui/svelte
# or
pnpm add @melt-ui/svelte
# or
yarn add @melt-ui/svelte
该包包括所有构建器及其 TypeScript 定义。
配置
无需额外配置。Melt UI 可直接在 Svelte 中使用。如果您使用 TypeScript,请确保您的 tsconfig.json 包含正确的模块解析:
{
"compilerOptions": {
"moduleResolution": "bundler",
"target": "ES2020"
}
}
基本用法
创建可折叠区域的最简方式是使用 createCollapsible 构建器:
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
const {
elements: { root, content, trigger },
states: { open }
} = createCollapsible();
</script>
<div use:melt={root}>
<button use:melt={trigger}>
{$open ? 'Close' : 'Open'}
</button>
<div use:melt={content}>
This content can be expanded and collapsed.
</div>
</div>
createCollapsible() 返回内容
- elements – 用于
root、content和trigger元素的 Svelte store。 - states – 如
open之类的响应式 store,用于跟踪折叠状态。
使用 melt 动作将构建器的行为附加到你的元素上。open store 是响应式的,当折叠状态变化时会自动更新。
高级功能
使用 Tailwind CSS 进行样式
Since Melt UI is headless, you have complete control over styling:
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
const {
elements: { root, content, trigger },
states: { open }
} = createCollapsible();
</script>
<div use:melt={root} class="border rounded">
<button use:melt={trigger} class="px-4 py-2 bg-gray-200">
Toggle Content <span class="ml-2">▼</span>
</button>
<div use:melt={content} class="p-4">
This is the collapsible content area.
</div>
</div>
受控状态
To control the collapsible state externally, pass a writable store:
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
import { writable } from 'svelte/store';
const isOpen = writable(false);
const {
elements: { root, content, trigger },
states: { open }
} = createCollapsible({ open: isOpen });
function toggle() {
isOpen.update(n => !n);
}
</script>
<div use:melt={root}>
<button use:melt={trigger} on:click={toggle}>
{$open ? 'Close' : 'Open'}
</button>
<div use:melt={content}>
Controlled content
</div>
</div>
创建手风琴
Build an accordion by managing multiple collapsible states:
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
import { writable } from 'svelte/store';
const openIndex = writable(null);
const items = [
{ id: 1, title: 'Item 1', content: 'Content for item 1' },
{ id: 2, title: 'Item 2', content: 'Content for item 2' },
{ id: 3, title: 'Item 3', content: 'Content for item 3' }
];
function createItemCollapsible(index) {
const isOpen = writable(openIndex.get() === index);
const unsubscribe = openIndex.subscribe(value => {
isOpen.set(value === index);
});
return {
collapsible: createCollapsible({ open: isOpen }),
unsubscribe
};
}
</script>
{#each items as item, index}
{@const { collapsible, unsubscribe } = createItemCollapsible(index)}
{@const { elements: { root, content, trigger }, states: { open } } = collapsible}
<div use:melt={root} class="border-b">
<button
use:melt={trigger}
on:click={() => openIndex.set($open ? null : index)}
class="w-full px-4 py-2 text-left"
>
{item.title}
</button>
{#if $open}
<div use:melt={content} class="p-4">
{item.content}
</div>
{/if}
</div>
{/each}
自定义动画
Add animations using Svelte’s transition directives:
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
import { slide } from 'svelte/transition';
const {
elements: { root, content, trigger },
states: { open }
} = createCollapsible();
</script>
<div use:melt={root}>
<button use:melt={trigger}>
{$open ? 'Hide' : 'Show'} details
</button>
{#if $open}
<div use:melt={content} transition:slide>
Animated collapsible content.
</div>
{/if}
</div>
结论
Melt UI 的无头、可访问的 Collapsible 构建器为您提供灵活性,以在 Svelte 中创建完全自定义、符合 ARIA 标准的可展开部分。通过利用构建器模式,您可以完全控制标记、样式和状态管理,同时受益于内置的可访问性和键盘支持。祝编码愉快!
<script>
import { createCollapsible, melt } from '@melt-ui/svelte';
import { slide } from 'svelte/transition';
const {
elements: { root, content, trigger },
states: { open }
} = createCollapsible();
</script>
<div use:melt={root}>
<button use:melt={trigger}>Toggle</button>
{#if $open}
<div use:melt={content} transition:slide>
This content slides in and out.
</div>
{/if}
</div>
全屏控制
- 进入全屏模式
- 退出全屏模式
常见问题 / 故障排除
折叠面板不切换
- 确保
melt动作已应用于 三个元素(root、trigger、content)。 - 检查存储是否使用
$前缀访问:$open。 - 确认构建器在组件初始化时 只调用一次(不要有条件调用)。
缺少可访问性属性
- 确保
melt动作已应用于元素,并且没有因条件渲染而被移除。 - 不要 手动添加 ARIA 属性——构建器会自动处理。
TypeScript 错误
- 需要时从
@melt-ui/svelte导入类型。 - 使用 Svelte v4+ 以获得最佳 TypeScript 支持。
内容未动画
- 使用
{#if}条件渲染时,确保melt动作仍然被应用。 - 将 Svelte 过渡(例如
transition:slide)应用于 content 元素,而不是根元素。
生产最佳实践
- 始终在 所有构建器元素 上使用
melt操作,以确保可访问性。 - 测试键盘导航(
Tab、Enter、Space、Escape)。 - 使用屏幕阅读器验证 ARIA 属性。
- 将样式与逻辑分离——Melt UI 负责行为,您负责外观。
- 为获得更佳性能,优先使用 CSS 过渡而非 JavaScript 动画。
- 对于手风琴组件,一次只允许 一个项目 打开,以提升用户体验。
- 在组件销毁时清理订阅,以避免内存泄漏。
最后说明
Melt UI 的 Collapsible 构建器为可展开内容提供了坚实的基础,设置简便。它的 headless 方式让您能够完全控制样式,同时保持可访问性合规。
一旦 collapsible 正常工作,常见的后续步骤包括:
- 使用共享状态构建 accordion 组件。
- 添加自定义动画和过渡效果。
- 与路由集成,实现可展开的导航菜单。
- 创建带有可折叠区段的详细视图。