npm package: @nnnb/prompt
Description: A factory method that supports various modal components in function call. Support asynchronous collection and function call.
Author: nnnb
Latest version: 0.0.2

# @nnnb/prompt

一个用于 React 应用中 各种组件的灵活弹层管理解决方案。

## 特性

- 🚀 简单易用的弹窗系统，支持 Modal、Drawer 等弹层组件
- 🎯 支持全局和局部上下文渲染
- ⚡ 高效的弹层管理，自动清理资源
- 🛠️ 使用 TypeScript 实现类型安全
- 🔄 可复用的弹层组件，支持自定义配置
- 📊 支持异步获取弹层组件的返回值

## 安装

```bash
npm install @nnnb/prompt
# 或
yarn add @nnnb/prompt
```

## 使用方法

### 基础用法

```typescript
import { createPrompt } from '@nnnb/prompt';
import { Modal } from 'antd';

// 创建一个带默认配置的 Modal 提示框
const ModalPrompt = createPrompt(Modal, {
  centered: true,
  destroyOnClose: true
});

// 使用 Modal 提示框
const showModal = async () => {
  const result = await ModalPrompt({
    props:{  
      title: '示例弹窗',
      content: '这是一个弹窗内容'
    }
  });
  console.log(result);
};
```

## 使用案例

### 1. 简单使用

创建并显示一个 Modal，支持自定义组件和获取返回值：

```typescript
import React from 'react';
import { createPrompt } from '@nnnb/prompt';
import { Button, Input, Modal } from 'antd';

// 创建一个 Modal 提示框
const ModalPrompt = createPrompt(Modal, {
  centered: true,
  destroyOnClose: true
});

// 自定义表单组件
const FormA = ({ A, B }) => {
  return (
    <div>
      <div>
        A: <Input defaultValue={A} />
      </div>
      <div>
        B: <Input defaultValue={B} />
      </div>
    </div>
  );
};

const Form = () => {
  const createModal = async () => {
    const res = await ModalPrompt({
      component: FormA,
      props: {
        title: 'Modal',
        onOk: (e) => {
          console.log('onOk', e);
          return {
            a: 'a'
          };
        },
        onCancel: () => {
          console.log('onCancel');
        }
      },
      data: {
        A: 'AAAA',
        B: 'BBBB'
      }
    });

    console.log(res); // 获取返回值
  };
  
  return (
    <div>
      <Button onClick={() => createModal()}>创建 Modal</Button>
    </div>
  );
};
```

### 2. 使用 Ant Design 的全局配置

在 ConfigProvider 中使用弹层，继承全局主题配置：

```typescript
import React from 'react';
import { LayerContextProvider, createPrompt } from '@nnnb/prompt';
import { Button, ConfigProvider, Input, Modal } from 'antd';

const ModalPrompt = createPrompt(Modal, {
  centered: true,
  destroyOnClose: true
});

// 自定义表单组件
const FormA = ({ A, B }) => {
  return (
    <div>
      <div>
        A: <Input defaultValue={A} />
      </div>
      <div>
        B: <Input defaultValue={B} />
      </div>
    </div>
  );
};

const Form = () => {
  const createModal = () => {
    ModalPrompt({
      component: FormA,
      props: {
        title: 'Modal'
      },
      data: {
        A: 'AAAA',
        B: 'BBBB'
      }
    });
  };
  
  return (
    <div>
      <Button onClick={() => createModal()}>创建 Modal</Button>
    </div>
  );
};

// 在应用中使用
function App() {
  const [open, setOpen] = React.useState(false);
  
  return (
    <ConfigProvider
      theme={{
        token: {
          colorPrimary: '#006603',
          borderRadius: 6
        },
        components: {
          Button: {
            colorPrimary: '#00B96B'
          }
        }
      }}
    >
      <div>
        <Button onClick={() => setOpen(!open)}>显示</Button>
        <div>{open && <LayerContextProvider />}</div>
      </div>
      <Form />
    </ConfigProvider>
  );
}
```

### 3. 自定义渲染位置

使用 `usePrompt` 钩子获取 holder 来控制弹层渲染位置：

```typescript
import React from 'react';
import { usePrompt } from '@nnnb/prompt';
import { Button, ConfigProvider, Drawer, Input, Modal } from 'antd';

// 自定义表单组件
const FormA = ({ A, B }) => {
  return (
    <div>
      <div>
        A: <Input defaultValue={A} />
      </div>
      <div>
        B: <Input defaultValue={B} />
      </div>
    </div>
  );
};

const Form = ({ ModalPrompt }) => {
  const createModal = () => {
    ModalPrompt({
      component: FormA,
      props: {
        title: 'Modal'
      },
      data: {
        A: 'AAAA',
        B: 'BBBB'
      }
    });
  };
  
  return (
    <div>
      <Button type="primary" onClick={() => createModal()}>创建 Modal</Button>
    </div>
  );
};

function App() {
  // 获取 Modal 的 holder 和 prompt 函数
  const [holder, prompt] = usePrompt(Modal);
  
  // 获取 Drawer 的 holder 和 prompt 函数
  const [holder2, prompt2] = usePrompt(Drawer);

  return (
    <div>
      {/* Modal 将在这里渲染 */}
      {holder}
      
      <ConfigProvider
        theme={{
          token: {
            colorPrimary: '#006603',
            borderRadius: 6
          }
        }}
      >
        {/* Drawer 将在这里渲染，会继承 ConfigProvider 的样式 */}
        {holder2}
        
        <Form ModalPrompt={prompt} />
        <Form ModalPrompt={prompt2} />
      </ConfigProvider>
    </div>
  );
}
```

### 4. 高级用法 - 异步获取子组件返回值

通过 `ref` 和 `useImperativeHandle` 实现异步获取子组件值：

```typescript
import React, { ForwardRefRenderFunction, useImperativeHandle } from 'react';
import { PromptModalRef, usePrompt } from '@nnnb/prompt';
import { Button, Input, Modal } from 'antd';

// 延迟函数，模拟异步操作
const delay = (time: number) => new Promise((resolve) => setTimeout(resolve, time));

// 自定义表单组件，通过 ref 暴露方法
const FormA: ForwardRefRenderFunction<PromptModalRef, any> = ({ A, B }, ref) => {
  useImperativeHandle(ref, () => ({
    // 定义 onOk 方法，将在点击确定按钮时调用
    onOk: async () => {
      await delay(2000); // 模拟异步操作，比如数据验证或提交
      return 'a'; // 返回值将作为 ModalPrompt 的结果
    }
  }));
  
  return (
    <div>
      <div>
        A: <Input defaultValue={A} />
      </div>
      <div>
        B: <Input defaultValue={B} />
      </div>
    </div>
  );
};

const Form = ({ ModalPrompt }) => {
  const createModal = async () => {
    // 等待弹窗操作完成，获取返回值
    const val = await ModalPrompt({
      component: FormA,
      props: {
        title: 'Modal',
        onOk: (e) => {
          console.log('Form', e);
        }
      },
      data: {
        A: 'AAAA',
        B: 'BBBB'
      }
    });
    
    console.log(val); // 输出 'a'
  };
  
  return (
    <div>
      <Button type="primary" onClick={() => createModal()}>创建 Modal</Button>
    </div>
  );
};

function App() {
  const [holder, prompt] = usePrompt(Modal);

  return (
    <div>
      {holder}
      <Form ModalPrompt={prompt} />
    </div>
  );
}
```

## API 文档

### createPrompt

```typescript
createPrompt<ComponentProps extends PromptBaseModalProps, ReturnType = any>(
  Component: ComponentType<ComponentProps>,
  defaultConfig?: Partial<ComponentProps>
) => PromptFunction
```

创建一个新的提示框函数，可以指定组件和默认配置。

### usePrompt

```typescript
usePrompt<P>(
  Component: ComponentType<P>,
  defaultConfig?: Partial<P>
): [ReactNode, PromptFunction]
```

返回一个元组，包含渲染占位符和提示函数。

### LayerContextProvider

```typescript
<LayerContextProvider />
```

全局上下文提供者，用于在特定位置渲染弹层组件。

### PromptModalRef 接口

```typescript
interface PromptModalRef {
  onOk?: () => Promise<any> | any;
  onCancel?: () => void;
}
```

用于在自定义组件中通过 ref 暴露方法。

## 类型定义

```typescript
interface PromptBaseModalProps {
  open?: boolean;
  confirmLoading?: boolean;
  onOk?: (e: any) => void | Promise<any>;
  onCancel?: (e: any) => void;
  children?: ReactNode;
}
```

## 贡献指南

欢迎提交 Pull Request 来帮助改进这个项目！

## 许可证

MIT