Files
lobe-chat/.cursor/rules/modal-imperative.mdc
T

163 lines
4.0 KiB
Plaintext
Raw Normal View History

2026-01-23 14:41:07 +08:00
---
description: Modal 命令式调用指南
globs: "**/features/**/*.tsx"
alwaysApply: false
---
# Modal 命令式调用指南
当需要创建可命令式调用的 Modal 组件时,使用 `@lobehub/ui` 提供的 `createModal` API。
## 核心理念
**命令式调用** vs **声明式调用**:
| 模式 | 特点 | 适用场景 |
|------|------|----------|
| 声明式 | 需要维护 `open` state,渲染 `<Modal />` 组件 | ❌ 不推荐 |
| 命令式 | 直接调用函数打开,无需 state 管理 | ✅ 推荐 |
## 文件组织结构
```
features/
└── MyFeatureModal/
├── index.tsx # 导出 createXxxModal 函数
├── MyFeatureContent.tsx # Modal 内容组件
└── ...其他子组件
```
## createModal 用法(推荐)
### 1. 定义 Content 组件 (`MyFeatureContent.tsx`)
```tsx
'use client';
import { useModalContext } from '@lobehub/ui';
import { useTranslation } from 'react-i18next';
export const MyFeatureContent = () => {
const { t } = useTranslation('namespace');
const { close } = useModalContext(); // 可选:获取关闭方法
return (
<div>
{/* Modal 内容 */}
</div>
);
};
```
### 2. 导出 createModal 函数 (`index.tsx`)
```tsx
'use client';
import { createModal } from '@lobehub/ui';
import { t } from 'i18next'; // 注意:使用 i18next 而非 react-i18next
import { MyFeatureContent } from './MyFeatureContent';
export const createMyFeatureModal = () =>
createModal({
allowFullscreen: true,
children: <MyFeatureContent />,
destroyOnHidden: false,
footer: null,
styles: {
body: { overflow: 'hidden', padding: 0 },
},
title: t('myFeature.title', { ns: 'setting' }),
width: 'min(80%, 800px)',
});
```
### 3. 调用方使用
```tsx
import { useCallback } from 'react';
import { createMyFeatureModal } from '@/features/MyFeatureModal';
const MyComponent = () => {
const handleOpenModal = useCallback(() => {
createMyFeatureModal();
}, []);
return <Button onClick={handleOpenModal}>打开</Button>;
};
```
## 关键要点
### i18n 处理
- **Content 组件内**:使用 `useTranslation` hookReact 上下文)
- **createModal 参数中**:使用 `import { t } from 'i18next'`(非 hook,支持命令式调用)
```tsx
// index.tsx - 命令式上下文
import { t } from 'i18next';
title: t('key', { ns: 'namespace' })
// Content.tsx - React 组件上下文
import { useTranslation } from 'react-i18next';
const { t } = useTranslation('namespace');
```
### useModalContext Hook
在 Content 组件内可使用 `useModalContext` 获取 Modal 控制方法:
```tsx
const { close, setCanDismissByClickOutside } = useModalContext();
```
### ModalHost
`createModal` 依赖全局 `<ModalHost />` 组件。项目中已在 `src/layout/GlobalProvider/index.tsx` 配置,无需额外添加。
## 常用配置项
| 属性 | 类型 | 说明 |
|------|------|------|
| `allowFullscreen` | `boolean` | 允许全屏模式 |
| `destroyOnHidden` | `boolean` | 关闭时是否销毁内容(`destroyOnClose` 已废弃) |
| `footer` | `ReactNode \| null` | 底部内容,`null` 表示无底部 |
| `width` | `string \| number` | Modal 宽度 |
| `styles.body` | `CSSProperties` | body 区域样式 |
## 迁移指南
### Before(声明式)
```tsx
// 调用方需要维护 state
const [open, setOpen] = useState(false);
return (
<>
<Button onClick={() => setOpen(true)}>打开</Button>
<MyModal open={open} setOpen={setOpen} />
</>
);
```
### After(命令式)
```tsx
// 调用方无需 state,直接调用函数
const handleOpen = useCallback(() => {
createMyModal();
}, []);
return <Button onClick={handleOpen}>打开</Button>;
```
## 示例参考
- `src/features/SkillStore/index.tsx` - createModal 标准用法
- `src/features/SkillStore/SkillStoreContent.tsx` - Content 组件示例
- `src/features/LibraryModal/CreateNew/index.tsx` - 带回调的 createModal 用法
- `src/features/Electron/updater/UpdateModal.tsx` - 复杂 Modal 控制示例