关于 modal
modal 是一份使用 @lobehub/ui 实现命令式模态框的指南,主要采用 base-ui API。内容涵盖设置、模态框内容、确认对话框、国际化及回调行为,面向相关应用的开发者。
主要功能
- 01使用 base-ui 的 createModal 和 confirmModal
- 02要求挂载全局 ModalHost
- 03说明模态框内容与调用方式
- 04记录关闭回调行为
- 05介绍国际化与常用选项
适用场景
- 01在应用中添加命令式模态框
- 02配置模态框关闭后的清理逻辑
- 03创建确认对话框
- 04将用法迁移到 base-ui
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lobehub/ lobehub --skill modal安装后在 Claude Code 中输入/modal确认已加载。
详细介绍
Modal 命令式 API 指南
推荐:@lobehub/ui/base-ui
新代码应使用 base-ui 模态框体系(无头原语,而非 antd Modal):
- 从
@lobehub/ui/base-ui导入createModal、confirmModal、ModalHost - 在模态框内容中从
@lobehub/ui/base-ui导入useModalContext
主体插槽:传入 content(或 children;运行时使用 content ?? children)。
全局 ModalHost(必需)
Base-ui 的 createModal 通过与根包分开的宿主渲染。应用必须在靠近根节点的位置挂载一次来自 @lobehub/ui/base-ui 的 ModalHost(例如,与其他全局宿主并列)。若没有它,createModal 调用将不会显示。
如果项目只挂载了来自 @lobehub/ui 的 ModalHost,请额外挂载第二个来自 @lobehub/ui/base-ui 的懒加载 ModalHost,直到所有命令式模态框完成迁移。
为什么使用命令式方式?
| 模式 | 特点 | 推荐 |
|---|---|---|
| 声明式 | open 状态 + <Modal /> | ❌ |
| 命令式 | 调用 createModal(),无需本地状态 | ✅ |
文件结构
features/
└── MyFeatureModal/
├── index.tsx # export createXxxModal
└── MyFeatureContent.tsx # modal body
1. 内容(MyFeatureContent.tsx)
'use client';
import { useModalContext } from '@lobehub/ui/base-ui';
import { useTranslation } from 'react-i18next';
export const MyFeatureContent = () => {
const { t } = useTranslation('namespace');
const { close } = useModalContext();
return <div>{/* ... */}</div>;
};
2. createModal(index.tsx)
'use client';
import { createModal } from '@lobehub/ui/base-ui';
import { t } from 'i18next';
import { MyFeatureContent } from './MyFeatureContent';
export const createMyFeatureModal = () =>
createModal({
content: <MyFeatureContent />,
footer: null,
maskClosable: true,
styles: {
content: { overflow: 'hidden', padding: 0 },
},
title: t('myFeature.title', { ns: 'setting' }),
width: 'min(80%, 800px)',
});
3. 用法
import { createMyFeatureModal } from '@/features/MyFeatureModal';
const handleOpen = useCallback(() => {
createMyFeatureModal();
}, []);
return <Button onClick={handleOpen}>Open</Button>;
国际化
- 内容:在组件中使用
useTranslation。 createModal选项:在无法使用 Hook 的位置,执行import { t } from 'i18next'。
useModalContext
const { close, setCanDismissByClickOutside } = useModalContext();
关闭时:实际触发的回调
在内容中通过 useModalContext() 调用的 close(),或通过返回的
ModalInstance 调用的 close(),只会将堆栈条目的 open 设为 false。它不会经过 base-ui 的关闭处理流程,因此:
| 回调 | 用户关闭(Esc / 背景遮罩 / 标题栏 ✕) | 内容或实例调用 close() |
|---|---|---|
onOpenChange | 触发 | 不会触发 |
onOpenChangeComplete | 以 false 触发 | 以 false 触发 |
将调用方的清理逻辑(清除编辑标记、重置提供方的
open 状态)放在 onOpenChangeComplete 中。将其绑定到 onOpenChange 看起来没问题,直到页脚按钮关闭模态框时,调用方才会发现它已经关闭——通常会导致标记仍处于设置状态,从而无法重新打开模态框。
createModal 完成时只会传入 false(命令式渲染器会自行提供参数,并且绝不会将该属性转发给 base-ui),但仍应对此进行判断——其他 base-ui 原语(例如 DropdownMenu)会报告两个方向的变化;添加判断可避免调用处依赖这一差异:
onOpenChangeComplete: (open) => {
if (!open) onClosed?.();
},
常用选项(base-ui)
ImperativeModalProps 基于 BaseModalProps 构建:title、width、maskClosable、open、onOpenChange、footer、styles / classNames(键:backdrop、popup、header、title、close、content、……)。
| 属性 | 说明 |
|---|---|
content | 主体内容(相比 children,优先使用此名称) |
maskClosable | 点击外部区域关闭 |
styles.* | 语义区域,而非 antd 的 styles.body |
确认框
import { confirmModal } from '@lobehub/ui/base-ui';
confirmModal({
title: '…',
content: '…',
okText: '…',
cancelText: '…',
onOk: async () => {},
});
旧版:@lobehub/ui(根包)
根入口 @lobehub/ui 中的 createModal 类型为 antd Modal 属性(children、allowFullscreen、getContainer、destroyOnHidden、styles.body 等)。应用代码已不再导入它;请勿重新引入——应使用 @lobehub/ui/base-ui。
示例
- Base-ui(首选):遵循以上章节;确保已挂载 base-ui
ModalHost。 - Base-ui 调用位置:
src/features/SkillStore/index.tsx、src/features/LibraryModal/CreateNew/index.tsx